/// Accept deleted paragraphs. /// /// Group together all paragraphs that contain w:p/w:pPr/w:rPr/w:del elements. Make a /// second group for the content element immediately following a paragraph that contains /// a w:del element. The code uses the approach of dealing with paragraph content at /// 'levels', ignoring paragraph content at other levels. Form a new paragraph that /// contains the content of the grouped paragraphs with deleted paragraph marks, and the /// content of the paragraph immediately following a paragraph that contains a deleted /// paragraph mark. Include in the new paragraph the paragraph properties from the /// paragraph following. When assembling the new paragraph, use a transform that collapses /// the paragraph nodes when adding content, thereby preserving custom XML and content /// controls. private static void AnnotateBlockContentElements(XElement contentContainer) { // For convenience, there is a ParagraphInfo annotation on the contentContainer. // It contains the same information as the ParagraphInfo annotation on the first // paragraph. if (contentContainer.Annotation<BlockContentInfo>() != null) return; XElement firstContentElement = contentContainer .Elements() .DescendantsAndSelf() .FirstOrDefault(e => e.Name == W.p || e.Name == W.tbl); if (firstContentElement == null) return; // Add the annotation on the contentContainer. BlockContentInfo currentContentInfo = new BlockContentInfo() { PreviousBlockContentElement = null, ThisBlockContentElement = firstContentElement, NextBlockContentElement = null }; // Add as annotation even though NextParagraph is not set yet. contentContainer.AddAnnotation(currentContentInfo); while (true) { currentContentInfo.ThisBlockContentElement.AddAnnotation(currentContentInfo); // Find next sibling content element. XElement nextContentElement = null; XElement current = currentContentInfo.ThisBlockContentElement; while (true) { nextContentElement = current .ElementsAfterSelf() .DescendantsAndSelf() .FirstOrDefault(e => e.Name == W.p || e.Name == W.tbl); if (nextContentElement != null) { currentContentInfo.NextBlockContentElement = nextContentElement; break; } current = current.Parent; // When we've backed up the tree to the contentContainer, we're done. if (current == contentContainer) return; } currentContentInfo = new BlockContentInfo() { PreviousBlockContentElement = currentContentInfo.ThisBlockContentElement, ThisBlockContentElement = nextContentElement, NextBlockContentElement = null }; } }
private static void InitializeParagraphInfo(XElement contentContext) { if (!(W.BlockLevelContentContainers.Contains(contentContext.Name))) throw new ArgumentException( "GetParagraphInfo called for element that is not child of content container"); XElement prev = null; foreach (var content in contentContext.Elements()) { // This may return null, indicating that there is no descendant paragraph. For // example, comment elements have no descendant elements. XElement paragraph = content .DescendantsAndSelf() .Where(e => e.Name == W.p || e.Name == W.tc || e.Name == W.txbxContent) .FirstOrDefault(); if (paragraph != null && (paragraph.Name == W.tc || paragraph.Name == W.txbxContent)) paragraph = null; BlockContentInfo pi = new BlockContentInfo() { PreviousBlockContentElement = prev, ThisBlockContentElement = paragraph }; content.AddAnnotation(pi); prev = content; } }