diff --git a/docs/word/how-to-accept-all-revisions-in-a-word-processing-document.md b/docs/word/how-to-accept-all-revisions-in-a-word-processing-document.md index 18f3e914..d7e22c4f 100644 --- a/docs/word/how-to-accept-all-revisions-in-a-word-processing-document.md +++ b/docs/word/how-to-accept-all-revisions-in-a-word-processing-document.md @@ -46,7 +46,7 @@ Using the Open XML SDK, you can create document structure and content using stro ## ParagraphPropertiesChange Element -When you accept a revision mark, you change the properties of a paragraph either by deleting an existing text or inserting a new text. In the following sections, you read about three elements that are used in the code to change the paragraph contents, mainly, `` (Revision Information for Paragraph Properties), `` (Deleted Paragraph), and `` (Inserted Table Row) elements. +When you accept a revision mark, you change the properties of a paragraph either by deleting existing text or inserting new text. In the following sections, you read about three elements that are used in the code to change the paragraph contents, mainly, `` (Revision Information for Paragraph Properties), `` (Deleted Paragraph), and `` (Inserted Table Row) elements. The following information from the [!include[ISO/IEC 29500 URL](../includes/iso-iec-29500-link.md)] specification introduces the `ParagraphPropertiesChange` element (`pPrChange`). @@ -86,9 +86,12 @@ This element specifies that the paragraph mark delimiting the end of a paragraph Consider a document consisting of two paragraphs (with each paragraph delimited by a pilcrow ¶): -![Two paragraphs each delimited by a pilcrow](../media/w-delparagraphs01.gif) If the physical character delimiting the end of the first paragraph is deleted and this change is tracked as a revision, the following will result: +![Two paragraphs each delimited by a pilcrow](../media/w-delparagraphs01.gif) + +If the physical character delimiting the end of the first paragraph is deleted and this change is tracked as a revision, the following will result: ![Two paragraphs delimited by a single pilcrow](../media/w-delparagraphs02.gif) + This revision is represented using the following WordprocessingML: ```xml @@ -162,12 +165,87 @@ a revision. © [!include[ISO/IEC 29500 version](../includes/iso-iec-29500-version.md)] +## Move From Element + +The following information from the [!include[ISO/IEC 29500 URL](../includes/iso-iec-29500-link.md)] specification +introduces the Move From element (`moveFrom`). + +### moveFrom (Move Source Paragraph) + +This element indicates that the parent paragraph has been relocated +from this position and marked as a revision. This does not affect the revision +status of the paragraph's content and pertains solely to the paragraph's +existence as a distinct entity. + +Consider a WordprocessingML document where a paragraph of text is moved down +within the document. This relocated paragraph would be represented using the +following WordprocessingML markup: + +```xml + + + + + + + + … + +``` + +### moveFromRangeStart (Move Source Location Container - Start) + +This element marks the beginning of a region where the move source contents are part of a single named move. +The following information from the [!include[ISO/IEC 29500 URL](../includes/iso-iec-29500-link.md)] specification +introduces the Move From Range Start element (`moveFromRangeStart`). + +### moveFromRangeEnd (Move Source Location Container - End) + +This element marks the end of a region where the move source contents are part of a single named move. +The following information from the [!include[ISO/IEC 29500 URL](../includes/iso-iec-29500-link.md)] specification +introduces the Move From Range End element (`moveFromRangeEnd`). + +## The Moved To Element +The following information from the [!include[ISO/IEC 29500 URL](../includes/iso-iec-29500-link.md)] specification +introduces the MoveTo element (`moveTo`). + +### moveTo (Move Destination Paragraph) +This element specifies that the parent paragraph has been moved to this location and tracked as a revision. +This does not imply anything about the revision state of the contents of the paragraph, and applies only to the existence of the paragraph as its own unique paragraph. + +Consider a WordprocessingML document in which a paragraph of text is moved down in the document. +This moved paragraph would be represented using the following WordprocessingML markup: + +```xml + + + + + + + + … + +``` + +### moveToRangeStart (Move Destination Location Container - Start) + +This element specifies the start of the region whose move destination contents are part of a single named move. +The following information from the [!include[ISO/IEC 29500 URL](../includes/iso-iec-29500-link.md)] specification +introduces the Move To Range Start element (`moveToRangeStart`). + +### moveToRangeEnd (Move Destination Location Container - End) + +This element specifies the end of a region whose move destination contents are part of a single named move. +The following information from the [!include[ISO/IEC 29500 URL](../includes/iso-iec-29500-link.md)] specification +introduces the Move To Range End element (`moveToRangeEnd`). + ## Sample Code The following code example shows how to accept the entire revisions in a word processing document. -After you have run the program, open the word processing file to make +After you have run the program, open the word processing document to make sure that all revision marks have been accepted. ### [C#](#tab/cs) diff --git a/samples/word/accept_all_revisions/cs/Program.cs b/samples/word/accept_all_revisions/cs/Program.cs index 5cbc0f42..e08fe6e9 100644 --- a/samples/word/accept_all_revisions/cs/Program.cs +++ b/samples/word/accept_all_revisions/cs/Program.cs @@ -19,62 +19,88 @@ static void AcceptAllRevisions(string fileName, string authorName) Body body = wdDoc.MainDocumentPart.Document.Body; // Handle the formatting changes. - List changes = body.Descendants() - .Where(c => c.Author is not null && c.Author.Value == authorName).Cast().ToList(); - - foreach (OpenXmlElement change in changes) - { - change.Remove(); - } + RemoveElements(body.Descendants().Where(c => c.Author?.Value == authorName)); // Handle the deletions. - List deletions = body - .Descendants() - .Where(c => c.Author is not null && c.Author.Value == authorName) - .Cast().ToList(); - - deletions.AddRange(body.Descendants() - .Where(c => c.Author is not null && c.Author.Value == authorName).Cast().ToList()); + RemoveElements(body.Descendants().Where(c => c.Author?.Value == authorName)); + RemoveElements(body.Descendants().Where(c => c.Author?.Value == authorName)); + RemoveElements(body.Descendants().Where(c => c.Author?.Value == authorName)); - deletions.AddRange(body.Descendants() - .Where(c => c.Author is not null && c.Author.Value == authorName).Cast().ToList()); + // Handle the insertions. + HandleInsertions(body, authorName); - foreach (OpenXmlElement deletion in deletions) - { - deletion.Remove(); - } + // Handle move from elements. + RemoveElements(body.Descendants() + .Where(p => p.Descendants() + .Any(m => m.Author?.Value == authorName))); + RemoveElements(body.Descendants()); - // Handle the insertions. - List insertions = - body.Descendants() - .Where(c => c.Author is not null && c.Author.Value == authorName).Cast().ToList(); + // Handle move to elements. + HandleMoveToElements(body, authorName); + } +} - insertions.AddRange(body.Descendants() - .Where(c => c.Author is not null && c.Author.Value == authorName).Cast().ToList()); +// Method to remove elements from the document body +static void RemoveElements(IEnumerable elements) +{ + foreach (var element in elements.ToList()) + { + element.Remove(); + } +} - insertions.AddRange(body.Descendants() - .Where(c => c.Author is not null && c.Author.Value == authorName).Cast().ToList()); +// Method to handle insertions in the document body +static void HandleInsertions(Body body, string authorName) +{ + // Collect all insertion elements by the specified author + var insertions = body.Descendants().Cast().ToList(); + insertions.AddRange(body.Descendants().Where(c => c.Author?.Value == authorName)); + insertions.AddRange(body.Descendants().Where(c => c.Author?.Value == authorName)); - foreach (OpenXmlElement insertion in insertions) + foreach (var insertion in insertions) + { + // Promote new content to the same level as the node and then delete the node + foreach (var run in insertion.Elements()) { - // Found new content. - // Promote them to the same level as node, and then delete the node. - foreach (var run in insertion.Elements()) + + if (run == insertion.FirstChild) + { + insertion.InsertAfterSelf(new Run(run.OuterXml)); + } + else { - if (run == insertion.FirstChild) - { - insertion.InsertAfterSelf(new Run(run.OuterXml)); - } - else - { - OpenXmlElement nextSibling = insertion.NextSibling()!; - nextSibling.InsertAfterSelf(new Run(run.OuterXml)); - } + OpenXmlElement nextSibling = insertion.NextSibling()!; + nextSibling.InsertAfterSelf(new Run(run.OuterXml)); } + } + + // Remove specific attributes and the insertion element itself + insertion.RemoveAttribute("rsidR", "https://schemas.openxmlformats.org/wordprocessingml/2006/main"); + insertion.RemoveAttribute("rsidRPr", "https://schemas.openxmlformats.org/wordprocessingml/2006/main"); + insertion.Remove(); + } +} + +// Method to handle move-to elements in the document body +static void HandleMoveToElements(Body body, string authorName) +{ + // Collect all move-to elements by the specified author + var paragraphs = body.Descendants() + .Where(p => p.Descendants() + .Any(m => m.Author?.Value == authorName)); + var moveToRun = body.Descendants(); + var moveToRangeEnd = body.Descendants(); - insertion.RemoveAttribute("rsidR", "https://schemas.openxmlformats.org/wordprocessingml/2006/main"); - insertion.RemoveAttribute("rsidRPr", "https://schemas.openxmlformats.org/wordprocessingml/2006/main"); - insertion.Remove(); + List moveToElements = [.. paragraphs, .. moveToRun, .. moveToRangeEnd]; + + foreach (var toElement in moveToElements) + { + // Promote new content to the same level as the node and then delete the node + foreach (var run in toElement.Elements()) + { + toElement.InsertBeforeSelf(new Run(run.OuterXml)); } + // Remove the move-to element itself + toElement.Remove(); } -} \ No newline at end of file +} diff --git a/samples/word/accept_all_revisions/cs/accept_all_revisions_cs.sln b/samples/word/accept_all_revisions/cs/accept_all_revisions_cs.sln new file mode 100644 index 00000000..44167ec9 --- /dev/null +++ b/samples/word/accept_all_revisions/cs/accept_all_revisions_cs.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35728.132 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "accept_all_revisions_cs", "accept_all_revisions_cs.csproj", "{FDDDBEF7-7D8F-4623-95E9-5E30BB031414}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FDDDBEF7-7D8F-4623-95E9-5E30BB031414}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDDDBEF7-7D8F-4623-95E9-5E30BB031414}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDDDBEF7-7D8F-4623-95E9-5E30BB031414}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDDDBEF7-7D8F-4623-95E9-5E30BB031414}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/samples/word/accept_all_revisions/vb/Program.vb b/samples/word/accept_all_revisions/vb/Program.vb index 9056b765..5d954d68 100644 --- a/samples/word/accept_all_revisions/vb/Program.vb +++ b/samples/word/accept_all_revisions/vb/Program.vb @@ -4,63 +4,83 @@ Imports DocumentFormat.OpenXml.Wordprocessing Module Program Sub Main(args As String()) - Dim fileName = args(0) - Dim authorName = args(1) + AcceptAllRevisions(args(0), args(1)) + End Sub - 'Public Sub AcceptRevisions(ByVal fileName As String, ByVal authorName As String) - ' Given a document name and an author name, accept revisions. + Sub AcceptAllRevisions(fileName As String, authorName As String) Using wdDoc As WordprocessingDocument = WordprocessingDocument.Open(fileName, True) + If wdDoc.MainDocumentPart Is Nothing OrElse wdDoc.MainDocumentPart.Document.Body Is Nothing Then + Throw New ArgumentNullException("MainDocumentPart and/or Body is null.") + End If + Dim body As Body = wdDoc.MainDocumentPart.Document.Body ' Handle the formatting changes. - Dim changes As List(Of OpenXmlElement) = - body.Descendants(Of ParagraphPropertiesChange)() _ - .Where(Function(c) c.Author.Value = authorName).Cast(Of OpenXmlElement)().ToList() - - For Each change In changes - change.Remove() - Next + RemoveElements(body.Descendants(Of ParagraphPropertiesChange)().Where(Function(c) c.Author?.Value = authorName)) ' Handle the deletions. - Dim deletions As List(Of OpenXmlElement) = - body.Descendants(Of Deleted)() _ - .Where(Function(c) c.Author.Value = authorName).Cast(Of OpenXmlElement)().ToList() + RemoveElements(body.Descendants(Of Deleted)().Where(Function(c) c.Author?.Value = authorName)) + RemoveElements(body.Descendants(Of DeletedRun)().Where(Function(c) c.Author?.Value = authorName)) + RemoveElements(body.Descendants(Of DeletedMathControl)().Where(Function(c) c.Author?.Value = authorName)) - deletions.AddRange(body.Descendants(Of DeletedRun)() _ - .Where(Function(c) c.Author.Value = authorName).Cast(Of OpenXmlElement)().ToList()) + ' Handle the insertions. + HandleInsertions(body, authorName) - deletions.AddRange(body.Descendants(Of DeletedMathControl)() _ - .Where(Function(c) c.Author.Value = authorName).Cast(Of OpenXmlElement)().ToList()) + ' Handle move from elements. + RemoveElements(body.Descendants(Of Paragraph)().Where(Function(p) p.Descendants(Of MoveFrom)().Any(Function(m) m.Author?.Value = authorName))) + RemoveElements(body.Descendants(Of MoveFromRangeEnd)()) - For Each deletion In deletions - deletion.Remove() - Next + ' Handle move to elements. + HandleMoveToElements(body, authorName) + End Using + End Sub - ' Handle the insertions. - Dim insertions As List(Of OpenXmlElement) = - body.Descendants(Of Inserted)() _ - .Where(Function(c) c.Author.Value = authorName).Cast(Of OpenXmlElement)().ToList() + ' Method to remove elements from the document body + Sub RemoveElements(elements As IEnumerable(Of OpenXmlElement)) + For Each element In elements.ToList() + element.Remove() + Next + End Sub - insertions.AddRange(body.Descendants(Of InsertedRun)() _ - .Where(Function(c) c.Author.Value = authorName).Cast(Of OpenXmlElement)().ToList()) + ' Method to handle insertions in the document body + Sub HandleInsertions(body As Body, authorName As String) + ' Collect all insertion elements by the specified author + Dim insertions As List(Of OpenXmlElement) = body.Descendants(Of Inserted)().Cast(Of OpenXmlElement)().ToList() + insertions.AddRange(body.Descendants(Of InsertedRun)().Where(Function(c) c.Author?.Value = authorName)) + insertions.AddRange(body.Descendants(Of InsertedMathControl)().Where(Function(c) c.Author?.Value = authorName)) - insertions.AddRange(body.Descendants(Of InsertedMathControl)() _ - .Where(Function(c) c.Author.Value = authorName).Cast(Of OpenXmlElement)().ToList()) + For Each insertion In insertions + ' Promote new content to the same level as the node and then delete the node + For Each run In insertion.Elements(Of Run)() + If run Is insertion.FirstChild Then + insertion.InsertAfterSelf(New Run(run.OuterXml)) + Else + Dim nextSibling As OpenXmlElement = insertion.NextSibling() + nextSibling.InsertAfterSelf(New Run(run.OuterXml)) + End If + Next - For Each insertion In insertions - ' Found new content. Promote them to the same level as node, and then - ' delete the node. - For Each run In insertion.Elements(Of Run)() - If run Is insertion.FirstChild Then - insertion.InsertAfterSelf(New Run(run.OuterXml)) - Else - insertion.NextSibling().InsertAfterSelf(New Run(run.OuterXml)) - End If - Next - insertion.RemoveAttribute("rsidR", "https://schemas.openxmlformats.org/wordprocessingml/2006/main") - insertion.RemoveAttribute("rsidRPr", "https://schemas.openxmlformats.org/wordprocessingml/2006/main") - insertion.Remove() + ' Remove specific attributes and the insertion element itself + insertion.RemoveAttribute("rsidR", "https://schemas.openxmlformats.org/wordprocessingml/2006/main") + insertion.RemoveAttribute("rsidRPr", "https://schemas.openxmlformats.org/wordprocessingml/2006/main") + insertion.Remove() + Next + End Sub + + ' Method to handle move-to elements in the document body + Sub HandleMoveToElements(body As Body, authorName As String) + ' Collect all move-to elements by the specified author + Dim moveToElements As List(Of OpenXmlElement) = body.Descendants(Of MoveToRun)().Cast(Of OpenXmlElement)().ToList() + moveToElements.AddRange(body.Descendants(Of Paragraph)().Where(Function(p) p.Descendants(Of MoveFrom)().Any(Function(m) m.Author?.Value = authorName))) + moveToElements.AddRange(body.Descendants(Of MoveToRangeEnd)()) + + For Each toElement In moveToElements + ' Promote new content to the same level as the node and then delete the node + For Each run In toElement.Elements(Of Run)() + toElement.InsertBeforeSelf(New Run(run.OuterXml)) Next - End Using + ' Remove the move-to element itself + toElement.Remove() + Next End Sub -End Module +End Module \ No newline at end of file diff --git a/samples/word/accept_all_revisions/vb/accept_all_revisions_vb.sln b/samples/word/accept_all_revisions/vb/accept_all_revisions_vb.sln new file mode 100644 index 00000000..093d8db0 --- /dev/null +++ b/samples/word/accept_all_revisions/vb/accept_all_revisions_vb.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35728.132 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "accept_all_revisions_vb", "accept_all_revisions_vb.vbproj", "{9B669D97-F249-4437-9314-6A7ABAC50451}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9B669D97-F249-4437-9314-6A7ABAC50451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B669D97-F249-4437-9314-6A7ABAC50451}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B669D97-F249-4437-9314-6A7ABAC50451}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B669D97-F249-4437-9314-6A7ABAC50451}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal