- Fix for "Format matching in ReplaceText does not work properly" - New method: RemoveTextInGivenFormat - Added unit test: Test_Paragraph_ReplaceTextInGivenFormat - Added 'VariousTextFormatting.docx' file for unit testing purposes - Moved ContainsEveryChildOf method to HelperFunction becasue it is shared by Contaner and Paragraph - Added new feature in Container class: RemoveTextInGivenFormat - Added unit test: Test_Document_RemoveTextInGivenFormatmaster
| p.ReplaceText(oldValue, newValue, trackChanges, options, newFormatting, matchFormatting, fo); | p.ReplaceText(oldValue, newValue, trackChanges, options, newFormatting, matchFormatting, fo); | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Removes all items with required formatting | |||||
| /// </summary> | |||||
| /// <returns>Numer of texts removed</returns> | |||||
| public int RemoveTextInGivenFormat(Formatting matchFormatting, MatchFormattingOptions fo = MatchFormattingOptions.SubsetMatch) | |||||
| { | |||||
| var deletedCount = 0; | |||||
| foreach (var x in Xml.Elements()) | |||||
| { | |||||
| deletedCount += RemoveTextWithFormatRecursive(x, matchFormatting, fo); | |||||
| } | |||||
| return deletedCount; | |||||
| } | |||||
| internal int RemoveTextWithFormatRecursive(XElement element, Formatting matchFormatting, MatchFormattingOptions fo) | |||||
| { | |||||
| var deletedCount = 0; | |||||
| foreach (var x in element.Elements()) | |||||
| { | |||||
| if ("rPr".Equals(x.Name.LocalName)) | |||||
| { | |||||
| if (HelperFunctions.ContainsEveryChildOf(matchFormatting.Xml, x, fo)) | |||||
| { | |||||
| x.Parent.Remove(); | |||||
| ++deletedCount; | |||||
| } | |||||
| } | |||||
| deletedCount += RemoveTextWithFormatRecursive(x, matchFormatting, fo); | |||||
| } | |||||
| return deletedCount; | |||||
| } | |||||
| public virtual void InsertAtBookmark(string toInsert, string bookmarkName) | public virtual void InsertAtBookmark(string toInsert, string bookmarkName) | ||||
| { | { | ||||
| if (String.IsNullOrWhiteSpace(bookmarkName)) | if (String.IsNullOrWhiteSpace(bookmarkName)) |
| { | { | ||||
| internal static class HelperFunctions | internal static class HelperFunctions | ||||
| { | { | ||||
| /// <summary> | |||||
| /// Checks whether 'toCheck' has all children that 'desired' has and values of 'val' attributes are the same | |||||
| /// </summary> | |||||
| /// <param name="desired"></param> | |||||
| /// <param name="toCheck"></param> | |||||
| /// <param name="fo">Matching options whether check if desired attributes are inder a, or a has exactly and only these attributes as b has.</param> | |||||
| /// <returns></returns> | |||||
| internal static bool ContainsEveryChildOf(XElement desired, XElement toCheck, MatchFormattingOptions fo) | |||||
| { | |||||
| foreach (XElement e in desired.Elements()) | |||||
| { | |||||
| // If a formatting property has the same name and 'val' attribute's value, its considered to be equivalent. | |||||
| if (!toCheck.Elements(e.Name).Where(bElement => bElement.GetAttribute(XName.Get("val", DocX.w.NamespaceName)) == e.GetAttribute(XName.Get("val", DocX.w.NamespaceName))).Any()) | |||||
| return false; | |||||
| } | |||||
| // If the formatting has to be exact, no additionaly formatting must exist. | |||||
| if (fo == MatchFormattingOptions.ExactMatch) | |||||
| return desired.Elements().Count() == toCheck.Elements().Count(); | |||||
| return true; | |||||
| } | |||||
| internal static void CreateRelsPackagePart(DocX Document, Uri uri) | internal static void CreateRelsPackagePart(DocX Document, Uri uri) | ||||
| { | { | ||||
| PackagePart pp = Document.package.CreatePart(uri, "application/vnd.openxmlformats-package.relationships+xml", CompressionOption.Maximum); | PackagePart pp = Document.package.CreatePart(uri, "application/vnd.openxmlformats-package.relationships+xml", CompressionOption.Maximum); |
| * Make sure that every formatting element in f.xml is also in this run, | * Make sure that every formatting element in f.xml is also in this run, | ||||
| * if this is not true, then their formatting does not match. | * if this is not true, then their formatting does not match. | ||||
| */ | */ | ||||
| if (!ContainsEveryChildOf(matchFormatting.Xml, rPr, fo)) | |||||
| if (!HelperFunctions.ContainsEveryChildOf(matchFormatting.Xml, rPr, fo)) | |||||
| { | { | ||||
| formattingMatch = false; | formattingMatch = false; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| internal bool ContainsEveryChildOf(XElement a, XElement b, MatchFormattingOptions fo) | |||||
| { | |||||
| foreach (XElement e in a.Elements()) | |||||
| { | |||||
| // If a formatting property has the same name and value, its considered to be equivalent. | |||||
| if (!b.Elements(e.Name).Where(bElement => bElement.Value == e.Value).Any()) | |||||
| return false; | |||||
| } | |||||
| // If the formatting has to be exact, no additionaly formatting must exist. | |||||
| if (fo == MatchFormattingOptions.ExactMatch) | |||||
| return a.Elements().Count() == b.Elements().Count(); | |||||
| return true; | |||||
| } | |||||
| /// <summary> | /// <summary> | ||||
| /// Find all instances of a string in this paragraph and return their indexes in a List. | /// Find all instances of a string in this paragraph and return their indexes in a List. | ||||
| /// </summary> | /// </summary> |
| } | } | ||||
| } | } | ||||
| [TestMethod] | |||||
| public void Test_Paragraph_ReplaceTextInGivenFormat() | |||||
| { | |||||
| // Load a document. | |||||
| using (DocX document = DocX.Load(_directoryWithFiles + "VariousTextFormatting.docx")) | |||||
| { | |||||
| // Removing red text highlighted with yellow | |||||
| var formatting = new Formatting(); | |||||
| formatting.FontColor = Color.Blue; | |||||
| // IMPORTANT: default constructor of Formatting sets up language property - set it to NULL to be language independent | |||||
| var desiredFormat = new Formatting() { Language = null, FontColor = Color.FromArgb(255, 0, 0), Highlight = Highlight.yellow }; | |||||
| var replaced = string.Empty; | |||||
| foreach (var p in document.Paragraphs) | |||||
| { | |||||
| if (p.Text == "Text highlighted with yellow") | |||||
| { | |||||
| p.ReplaceText("Text highlighted with yellow", "New text highlighted with yellow", false, RegexOptions.None, null, desiredFormat, MatchFormattingOptions.ExactMatch); | |||||
| replaced += p.Text; | |||||
| } | |||||
| } | |||||
| Assert.AreEqual("New text highlighted with yellow", replaced); | |||||
| // Removing red text with no other formatting (ExactMatch) | |||||
| desiredFormat = new Formatting() { Language = null, FontColor = Color.FromArgb(255, 0, 0) }; | |||||
| var count = 0; | |||||
| foreach (var p in document.Paragraphs) | |||||
| { | |||||
| p.ReplaceText("Text", "Replaced text", false, RegexOptions.None, null, desiredFormat, MatchFormattingOptions.ExactMatch); | |||||
| if (p.Text.StartsWith("Replaced text")) | |||||
| { | |||||
| ++count; | |||||
| } | |||||
| } | |||||
| Assert.AreEqual(1, count); | |||||
| // Removing just red text with any other formatting (SubsetMatch) | |||||
| desiredFormat = new Formatting() { Language = null, FontColor = Color.FromArgb(255, 0, 0) }; | |||||
| count = 0; | |||||
| foreach (var p in document.Paragraphs) | |||||
| { | |||||
| p.ReplaceText("Text", "Replaced text", false, RegexOptions.None, null, desiredFormat, MatchFormattingOptions.SubsetMatch); | |||||
| if (p.Text.StartsWith("Replaced text")) | |||||
| { | |||||
| ++count; | |||||
| } | |||||
| } | |||||
| Assert.AreEqual(2, count); | |||||
| } | |||||
| } | |||||
| [TestMethod] | [TestMethod] | ||||
| public void Test_Paragraph_RemoveText() | public void Test_Paragraph_RemoveText() | ||||
| { | { | ||||
| } | } | ||||
| } | } | ||||
| [TestMethod] | |||||
| public void Test_Document_RemoveTextInGivenFormat() | |||||
| { | |||||
| // Load a document. | |||||
| using (DocX document = DocX.Load(_directoryWithFiles + "VariousTextFormatting.docx")) | |||||
| { | |||||
| var formatting = new Formatting(); | |||||
| formatting.FontColor = Color.Blue; | |||||
| // IMPORTANT: default constructor of Formatting sets up language property - set it to NULL to be language independent | |||||
| formatting.Language = null; | |||||
| var deletedCount = document.RemoveTextInGivenFormat(formatting); | |||||
| Assert.AreEqual(2, deletedCount); | |||||
| deletedCount = document.RemoveTextInGivenFormat(new Formatting() { Highlight = Highlight.yellow, Language = null }); | |||||
| Assert.AreEqual(2, deletedCount); | |||||
| deletedCount = document.RemoveTextInGivenFormat(new Formatting() { Highlight = Highlight.blue, Language = null, FontColor = Color.FromArgb(0, 255, 0) }); | |||||
| Assert.AreEqual(1, deletedCount); | |||||
| deletedCount = document.RemoveTextInGivenFormat(new Formatting() { Language = null, FontColor = Color.FromArgb(123, 123, 123) }, MatchFormattingOptions.ExactMatch); | |||||
| Assert.AreEqual(2, deletedCount); | |||||
| } | |||||
| } | |||||
| [TestMethod] | [TestMethod] | ||||
| public void Test_Paragraph_InsertText() | public void Test_Paragraph_InsertText() | ||||
| { | { |
| <None Include="documents\testdoc_SectionsWithSectionBreaksMultiParagraph.docx" /> | <None Include="documents\testdoc_SectionsWithSectionBreaksMultiParagraph.docx" /> | ||||
| <None Include="documents\testdoc_UnorderedList.docx" /> | <None Include="documents\testdoc_UnorderedList.docx" /> | ||||
| <None Include="documents\TestParent.docx" /> | <None Include="documents\TestParent.docx" /> | ||||
| <None Include="documents\VariousTextFormatting.docx" /> | |||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <Content Include="documents\green.jpg" /> | <Content Include="documents\green.jpg" /> |