public void CanReplaceTextWithSymbolsAndTrackedChanges() { XDocument partDocument = XDocument.Parse(SymbolsAndTrackedChangesDocumentXmlString); XElement p = partDocument.Descendants(W.p).First(); string innerText = InnerText(p); Assert.Equal("We can also use symbols such as \uF021 or \uF028.", innerText); using (var stream = new MemoryStream()) using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) { MainDocumentPart part = wordDocument.AddMainDocumentPart(); part.PutXDocument(partDocument); IEnumerable <XElement> content = partDocument.Descendants(W.p); var regex = new Regex(@"[\uF021]"); int count = OpenXmlRegex.Replace(content, regex, "\uF028", null, true, "John Doe"); p = partDocument.Descendants(W.p).First(); innerText = InnerText(p); Assert.Equal(1, count); Assert.Equal("We can also use symbols such as \uF028 or \uF028.", innerText); Assert.Contains(p.Descendants(W.ins), ins => ins.Descendants(W.sym).Any( sym => sym.Attribute(W.font).Value == "Wingdings" && sym.Attribute(W._char).Value == "F028")); } }
public DocX AddToC(string title = null) { var tableOfContents = new TableOfContents(document); var lastParagraph = MainDocumentPart .GetXDocument() .Descendants(W.p) .LastOrDefault(); if (lastParagraph == null) { var xdoc = MainDocumentPart.GetXDocument(); xdoc.Add(XElement.Parse(Body.OuterXml)); MainDocumentPart.PutXDocument(); lastParagraph = MainDocumentPart .GetXDocument() .Descendants(W.p) .LastOrDefault(); } tableOfContents.AddToc(lastParagraph, @"TOC \o '1-3' \h \z \u", title, null); return(this); }
public void CanRemoveContentControls() { XDocument partDocument = XDocument.Parse(SdtDocumentXmlString); Assert.True(partDocument.Descendants(W.sdt).Any()); using (var stream = new MemoryStream()) using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) { MainDocumentPart part = wordDocument.AddMainDocumentPart(); part.PutXDocument(partDocument); var settings = new SimplifyMarkupSettings { RemoveContentControls = true }; MarkupSimplifier.SimplifyMarkup(wordDocument, settings); partDocument = part.GetXDocument(); XElement element = partDocument .Descendants(W.body) .Descendants() .First(); Assert.False(partDocument.Descendants(W.sdt).Any()); Assert.Equal(W.p, element.Name); } }
public void CanRemoveGoBackBookmarks() { XDocument partDocument = XDocument.Parse(GoBackBookmarkDocumentXmlString); Assert.Contains(partDocument .Descendants(W.bookmarkStart) , e => e.Attribute(W.name).Value == "_GoBack" && e.Attribute(W.id).Value == "0"); Assert.Contains(partDocument .Descendants(W.bookmarkEnd) , e => e.Attribute(W.id).Value == "0"); using (var stream = new MemoryStream()) using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) { MainDocumentPart part = wordDocument.AddMainDocumentPart(); part.PutXDocument(partDocument); var settings = new SimplifyMarkupSettings { RemoveGoBackBookmark = true }; MarkupSimplifier.SimplifyMarkup(wordDocument, settings); partDocument = part.GetXDocument(); Assert.False(partDocument.Descendants(W.bookmarkStart).Any()); Assert.False(partDocument.Descendants(W.bookmarkEnd).Any()); } }
public void CanReplaceTextWithQuotationMarksAndTrackedChanges() { XDocument partDocument = XDocument.Parse(QuotationMarksAndTrackedChangesDocumentXmlString); XElement p = partDocument.Descendants(W.p).First(); string innerText = InnerText(p); Assert.Equal( "Text can be enclosed in “normal double quotes” and in «double angle quotation marks».", innerText); using (var stream = new MemoryStream()) using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) { MainDocumentPart part = wordDocument.AddMainDocumentPart(); part.PutXDocument(partDocument); IEnumerable <XElement> content = partDocument.Descendants(W.p); var regex = new Regex(string.Format("{0}(?<words>{1}){2}", LeftDoubleQuotationMarks, Words, RightDoubleQuotationMarks)); int count = OpenXmlRegex.Replace(content, regex, "‘changed ${words}’", null, true, "John Doe"); p = partDocument.Descendants(W.p).First(); innerText = InnerText(p); Assert.Equal(2, count); Assert.Equal( "Text can be enclosed in ‘changed normal double quotes’ and in ‘changed double angle quotation marks’.", innerText); Assert.Contains(p.Elements(W.ins), e => InnerText(e) == "‘changed normal double quotes’"); Assert.Contains(p.Elements(W.ins), e => InnerText(e) == "‘changed double angle quotation marks’"); } }
public static void SetContent(WordprocessingDocument document, IEnumerable <XElement> contents) { MainDocumentPart mainDocumentPart = document.MainDocumentPart; if (mainDocumentPart == null) { mainDocumentPart = document.AddMainDocumentPart(); } XDocument mainDocumentPartContentToInsert = new XDocument( new XElement(W.document, new XAttribute(XNamespace.Xmlns + "w", W.w), new XAttribute(XNamespace.Xmlns + "r", R.r), new XElement(W.body, contents))); XDocument mainDocumentPartContent = mainDocumentPart.GetXDocument(); if (mainDocumentPartContent.Root == null) { mainDocumentPartContent.Add(mainDocumentPartContentToInsert.Root); } else { mainDocumentPartContent.Root.ReplaceWith(mainDocumentPartContentToInsert.Root); } mainDocumentPart.PutXDocument(); }
public void CanReplaceTextWithFields() { XDocument partDocument = XDocument.Parse(FieldsDocumentXmlString); XElement p = partDocument.Descendants(W.p).Last(); string innerText = InnerText(p); Assert.Equal("As stated in Article {__1} and this Section {__1.1}, this is described in Schedule C (Performance Framework).", innerText); using (var stream = new MemoryStream()) using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) { MainDocumentPart part = wordDocument.AddMainDocumentPart(); part.PutXDocument(partDocument); IEnumerable <XElement> content = partDocument.Descendants(W.p); var regex = new Regex(@"Schedule C \(Performance Framework\)"); int count = OpenXmlRegex.Replace(content, regex, "Exhibit 4", null, true, "John Doe"); p = partDocument.Descendants(W.p).Last(); innerText = InnerText(p); Assert.Equal(1, count); Assert.Equal("As stated in Article {__1} and this Section {__1.1}, this is described in Exhibit 4.", innerText); } }
private static void ChangeFootnoteEndnoteReferencesToUniqueRange( WordprocessingDocument wDoc, int startingIdForFootnotesEndnotes) { MainDocumentPart mainDocPart = wDoc.MainDocumentPart; FootnotesPart footnotesPart = wDoc.MainDocumentPart.FootnotesPart; EndnotesPart endnotesPart = wDoc.MainDocumentPart.EndnotesPart; XElement document = mainDocPart.GetXDocument().Root ?? throw new OpenXmlPowerToolsException("Invalid document."); XElement footnotes = footnotesPart?.GetXDocument().Root; XElement endnotes = endnotesPart?.GetXDocument().Root; IEnumerable <XElement> references = document .Descendants() .Where(d => d.Name == W.footnoteReference || d.Name == W.endnoteReference); foreach (XElement r in references) { var oldId = (string)r.Attribute(W.id); string newId = startingIdForFootnotesEndnotes.ToString(); startingIdForFootnotesEndnotes++; r.SetAttributeValue(W.id, newId); if (r.Name == W.footnoteReference) { XElement fn = footnotes? .Elements() .FirstOrDefault(e => (string)e.Attribute(W.id) == oldId); if (fn == null) { throw new OpenXmlPowerToolsException("Invalid document"); } fn.SetAttributeValue(W.id, newId); } else { XElement en = endnotes? .Elements() .FirstOrDefault(e => (string)e.Attribute(W.id) == oldId); if (en == null) { throw new OpenXmlPowerToolsException("Invalid document"); } en.SetAttributeValue(W.id, newId); } } mainDocPart.PutXDocument(); footnotesPart?.PutXDocument(); endnotesPart?.PutXDocument(); }
private static MemoryStream CreateWordprocessingDocument(IEnumerable <string> runTexts) { var stream = new MemoryStream(); const WordprocessingDocumentType type = WordprocessingDocumentType.Document; using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, type)) { MainDocumentPart mainDocumentPart = wordDocument.AddMainDocumentPart(); mainDocumentPart.PutXDocument(new XDocument(CreateDocument(runTexts))); } return(stream); }
/// <summary> /// Creates a <see cref="WordprocessingDocument"/> for on a <see cref="MemoryStream"/> /// testing purposes, using the given <paramref name="document"/> as the w:document /// root element of the main document part. /// </summary> /// <param name="document">The w:document root element.</param> /// <returns>The <see cref="MemoryStream"/> containing the <see cref="WordprocessingDocument"/>.</returns> private static MemoryStream CreateWordprocessingDocument(XElement document) { var stream = new MemoryStream(); const WordprocessingDocumentType type = WordprocessingDocumentType.Document; using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, type)) { MainDocumentPart part = wordDocument.AddMainDocumentPart(); part.PutXDocument(new XDocument(document)); } return(stream); }
/// <summary> /// Creates a new <see cref="WordprocessingDocument" /> on the given <see cref="Stream" />, /// having the given <see cref="XDocument" /> as the main document part's content and /// adding a default style definitions part. /// </summary> /// <param name="stream">The <see cref="Stream" />.</param> /// <param name="document">The <see cref="XDocument" />.</param> /// <returns>The new <see cref="WordprocessingDocument" />.</returns> protected static WordprocessingDocument CreateWordprocessingDocument(Stream stream, XDocument document) { WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType); MainDocumentPart mainDocumentPart = wordDocument.AddMainDocumentPart(); mainDocumentPart.PutXDocument(document); var styleDefinitionsPart = mainDocumentPart.AddNewPart <StyleDefinitionsPart>(); styleDefinitionsPart.PutXDocument(XDocument.Parse(StylesXml)); return(wordDocument); }
public void MustEndPowerToolsBlockToUseStronglyTypedClasses() { using (var stream = new MemoryStream()) { CreateEmptyWordprocessingDocument(stream); using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true)) { MainDocumentPart part = wordDocument.MainDocumentPart; // Add a paragraph through the SDK. Body body = part.Document.Body; body.AppendChild(new Paragraph(new Run(new Text("Added through SDK")))); // Begin the PowerTools Block, which saves any changes made through the strongly // typed SDK classes to the parts of the WordprocessingDocument. // In this case, this could also be done by invoking the Save method on the // WordprocessingDocument, which will save all parts that had changes, or by // invoking part.RootElement.Save() for the one part that was changed. wordDocument.BeginPowerToolsBlock(); // Add a paragraph through the PowerTools. XDocument content = part.GetXDocument(); XElement bodyElement = content.Descendants(W.body).First(); bodyElement.Add(new XElement(W.p, new XElement(W.r, new XElement(W.t, "Added through PowerTools")))); part.PutXDocument(); // Get the part's content through the SDK. However, we will only see what we // added through the SDK, not what we added through the PowerTools functionality. body = part.Document.Body; List <Paragraph> paragraphs = body.Elements <Paragraph>().ToList(); Assert.Equal(1, paragraphs.Count); Assert.Equal("Added through SDK", paragraphs[0].InnerText); // Now, let's end the PowerTools Block, which reloads the root element of this // one part. Reloading those root elements this way is fine if you know exactly // which parts had their content changed by the Open XML PowerTools. wordDocument.EndPowerToolsBlock(); // Get the part's content through the SDK. Having reloaded the root element, // we should now see both paragraphs. body = part.Document.Body; paragraphs = body.Elements <Paragraph>().ToList(); Assert.Equal(2, paragraphs.Count); Assert.Equal("Added through SDK", paragraphs[0].InnerText); Assert.Equal("Added through PowerTools", paragraphs[1].InnerText); } } }
private static void InitializeWordprocessingDocument( WordprocessingDocument wordDocument, XElement customXmlRoot, XElement sdtContent) { MainDocumentPart mainDocumentPart = wordDocument.AddMainDocumentPart(); string storeItemId = CreateCustomXmlPart(mainDocumentPart, customXmlRoot); mainDocumentPart.PutXDocument(new XDocument( new XElement(W.document, new XAttribute(XNamespace.Xmlns + "w", W.w.NamespaceName), new XElement(W.body, new XElement(W.sdt, new XElement(W.sdtPr, new XElement(W.tag, new XAttribute(W.val, "Node")), new XElement(W.dataBinding, new XAttribute(W.prefixMappings, $"xmlns:{NsPrefix}='{NsName}'"), new XAttribute(W.xpath, $"{NsPrefix}:Root[1]/{NsPrefix}:Node[1]"), new XAttribute(W.storeItemID, storeItemId))), sdtContent))))); }
public void CanUsePowerToolsBlockToDemarcateApis() { using (var stream = new MemoryStream()) { CreateEmptyWordprocessingDocument(stream); using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true)) { MainDocumentPart part = wordDocument.MainDocumentPart; // Add a paragraph through the SDK. Body body = part.Document.Body; body.AppendChild(new Paragraph(new Run(new Text("Added through SDK")))); // This demonstrates the use of the PowerToolsBlock in a using statement to // demarcate the intermittent use of the PowerTools. using (new PowerToolsBlock(wordDocument)) { // Assert that we can see the paragraph added through the strongly typed classes. XDocument content = part.GetXDocument(); List <XElement> paragraphElements = content.Descendants(W.p).ToList(); Assert.Single(paragraphElements); Assert.Equal("Added through SDK", paragraphElements[0].Value); // Add a paragraph through the PowerTools. XElement bodyElement = content.Descendants(W.body).First(); bodyElement.Add(new XElement(W.p, new XElement(W.r, new XElement(W.t, "Added through PowerTools")))); part.PutXDocument(); } // Get the part's content through the SDK. Having used the PowerToolsBlock, // we should see both paragraphs. body = part.Document.Body; List <Paragraph> paragraphs = body.Elements <Paragraph>().ToList(); Assert.Equal(2, paragraphs.Count); Assert.Equal("Added through SDK", paragraphs[0].InnerText); Assert.Equal("Added through PowerTools", paragraphs[1].InnerText); } } }
public void CanDataBindBlockLevelSdtToCustomXmlWithNsPrefixIfNsPrefixInPrefixMapping() { // The following root element has an explicitly created attribute // xmlns:ex="http://example.com": // // <ex:Root xmlns:ex="http://example.com"> // <ex:Node>VALUE1</ex:Node> // </ex:Root> // var customXmlRootElement = new XElement(Ns + "Root", new XAttribute(XNamespace.Xmlns + NsPrefix, NsName), new XElement(Ns + "Node", "VALUE1")); using WordprocessingDocument wordDocument = WordprocessingDocument.Create("SdtBlock_NsPrefix_WithNsPrefixInMapping.docx", Type); MainDocumentPart mainDocumentPart = wordDocument.AddMainDocumentPart(); string storeItemId = CreateCustomXmlPart(mainDocumentPart, customXmlRootElement); mainDocumentPart.PutXDocument(new XDocument( new XElement(W.document, new XAttribute(XNamespace.Xmlns + "w", W.w.NamespaceName), new XElement(W.body, new XElement(W.sdt, new XElement(W.sdtPr, new XElement(W.dataBinding, // Note the w:prefixMapping attribute WITH a namespace // prefix and the corresponding w:xpath atttibute. new XAttribute(W.prefixMappings, $"xmlns:{NsPrefix}='{NsName}'"), new XAttribute(W.xpath, $"{NsPrefix}:Root[1]/{NsPrefix}:Node[1]"), new XAttribute(W.storeItemID, storeItemId))), new XElement(W.sdtContent, new XElement(W.p))))))); // Note that we just added an empty w:p to the w:sdtContent element. // However, if you open the Word document created by the above code // in Microsoft Word, you should see a single paragraph saying // "VALUE1". }
public void CannotDataBindBlockLevelSdtToCustomXmlWithDefaultNsIfNotNsPrefixInPrefixMapping() { // The following root element has an implicitly created attribute // xmlns='http://example.com': // // <Root xmlns="http://example.com"> // <Node>VALUE1</Node> // </Root> // var customXmlRootElement = new XElement(Ns + "Root", new XElement(Ns + "Node", "VALUE1")); using WordprocessingDocument wordDocument = WordprocessingDocument.Create("SdtBlock_DefaultNs_WithoutNsPrefixInMapping.docx", Type); MainDocumentPart mainDocumentPart = wordDocument.AddMainDocumentPart(); string storeItemId = CreateCustomXmlPart(mainDocumentPart, customXmlRootElement); mainDocumentPart.PutXDocument(new XDocument( new XElement(W.document, new XAttribute(XNamespace.Xmlns + "w", W.w.NamespaceName), new XElement(W.body, new XElement(W.sdt, new XElement(W.sdtPr, new XElement(W.dataBinding, // Note the w:prefixMapping attribute WITHOUT a namespace // prefix and the corresponding w:xpath atttibute. new XAttribute(W.prefixMappings, $"xmlns='{NsName}'"), new XAttribute(W.xpath, "Root[1]/Node[1]"), new XAttribute(W.storeItemID, storeItemId))), new XElement(W.sdtContent, new XElement(W.p))))))); // Note that we just added an empty w:p to the w:sdtContent element. // If you open the Word document created by the above code in Microsoft // Microsoft Word, you will only see an EMPTY paragraph. }
public void CanRemoveSmartTags() { XDocument partDocument = XDocument.Parse(SmartTagDocumentXmlString); Assert.True(partDocument.Descendants(W.smartTag).Any()); using (var stream = new MemoryStream()) using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) { MainDocumentPart part = wordDocument.AddMainDocumentPart(); part.PutXDocument(partDocument); var settings = new SimplifyMarkupSettings { RemoveSmartTags = true }; MarkupSimplifier.SimplifyMarkup(wordDocument, settings); partDocument = part.GetXDocument(); XElement t = partDocument.Descendants(W.t).First(); Assert.False(partDocument.Descendants(W.smartTag).Any()); Assert.Equal(SmartTagDocumentTextValue, t.Value); } }
public void CanUpdateCustomXmlAndMainDocumentPart() { // Define the initial and updated values of our custom XML element and // the data-bound w:sdt element. const string initialValue = "VALUE1"; const string updatedValue = "value2"; // Create the root element of the custom XML part with the initial value. var customXmlRoot = new XElement(Ns + "Root", new XAttribute(XNamespace.Xmlns + NsPrefix, NsName), new XElement(Ns + "Node", initialValue)); // Create the w:sdtContent child element of our w:sdt with the initial value. var sdtContent = new XElement(W.sdtContent, new XElement(W.p, new XElement(W.r, new XElement(W.t, initialValue)))); // Create a WordprocessingDocument with the initial values. using var stream = new MemoryStream(); using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, Type)) { InitializeWordprocessingDocument(wordDocument, customXmlRoot, sdtContent); } // Assert the WordprocessingDocument has the expected, initial values. using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true)) { AssertValuesAreAsExpected(wordDocument, initialValue); } // Update the WordprocessingDocument, using the updated value. using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true)) { MainDocumentPart mainDocumentPart = wordDocument.MainDocumentPart; // Change custom XML element, again using the simplifying assumption // that we only have a single custom XML part and a single ex:Node // element. CustomXmlPart customXmlPart = mainDocumentPart.CustomXmlParts.Single(); XElement root = customXmlPart.GetXElement(); XElement node = root.Elements(Ns + "Node").Single(); node.Value = updatedValue; customXmlPart.PutXDocument(); // Change the w:sdt contained in the MainDocumentPart. XElement document = mainDocumentPart.GetXElement(); XElement sdt = FindSdtWithTag("Node", document); sdtContent = sdt.Elements(W.sdtContent).Single(); sdtContent.ReplaceAll( new XElement(W.p, new XElement(W.r, new XElement(W.t, updatedValue)))); mainDocumentPart.PutXDocument(); } // Assert the WordprocessingDocument has the expected, updated values. using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true)) { AssertValuesAreAsExpected(wordDocument, updatedValue); } }