Export (0) Print
Expand All
Expand Minimize

How to: Accept All Revisions in a Word Processing Document

Last modified: October 14, 2010

Applies to: Excel 2010 | Office 2010 | PowerPoint 2010 | Word 2010

In this article
Open the Existing Document for Editing
Structure of a WordProcessingML Document
ParagraphPropertiesChange Element
Deleted Element
The Inserted Element
How the Sample Code Works
Sample Code

This topic shows how to use the Open XML SDK 2.0 for Microsoft Office to accept all revisions in a word processing document programmatically.

The following assembly directives are required to compile the code in this topic.

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System.Linq;
using System.Collections.Generic;

To open an existing document, you can instantiate the WordprocessingDocument class as shown in the following using statement. To do so, you open the word processing file with the specified fileName by using the Open(String, Boolean) method, with the Boolean parameter set to true in order to enable editing the document.

using (WordprocessingDocument wdDoc = WordprocessingDocument.Open(fileName, true))
{
    // Insert other code here.
}

The using statement provides a recommended alternative to the typical .Open, .Save, .Close sequence. It ensures that the Dispose method (internal method used by the Open XML SDK to clean up resources) is automatically called when the closing brace is reached. The block that follows the using statement establishes a scope for the object that is created or named in the using statement, in this case wdDoc. Because the WordprocessingDocument class in the Open XML SDK automatically saves and closes the object as part of its System.IDisposable implementation, and because Dispose is automatically called when you exit the block, you do not have to explicitly call Save and Close as long as you use using.

The basic document structure of a WordProcessingML document consists of the document and body elements, followed by one or more block level elements such as p, which represents a paragraph. A paragraph contains one or more r elements. The r stands for run, which is a region of text with a common set of properties, such as formatting. A run contains one or more t elements. The t element contains a range of text. The following code example shows the WordprocessingML markup for a document that contains the text "Example text."

<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:body>
    <w:p>
      <w:r>
        <w:t>Example text.</w:t>
      </w:r>
    </w:p>
  </w:body>
</w:document>

Using the Open XML SDK 2.0, you can create document structure and content using strongly-typed classes that correspond to WordprocessingML elements. You will find these classes in the DocumentFormat.OpenXml.Wordprocessing namespace. The following table lists the class names of the classes that correspond to the document, body, p, r, and t elements.

WordprocessingML Element

Open XML SDK 2.0 Class

Description

document

Document

The root element for the main document part.

body

Body

The container for the block level structures such as paragraphs, tables, annotations and others specified in the ISO/IEC 29500 specification.

p

Paragraph

A paragraph.

r

Run

A run.

t

Text

A range of text.

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, <w: pPrChange> (Revision Information for Paragraph Properties), <w:del> (Deleted Paragraph), and <w:ins> (Inserted Table Row) elements.

The following information from the ISO/IEC 29500 specification introduces the ParagraphPropertiesChange element (pPrChange).

pPrChange (Revision Information for Paragraph Properties)

This element specifies the details about a single revision to a set of paragraph properties in a WordprocessingML document.

This element stores this revision as follows:

  • The child element of this element contains the complete set of paragraph properties which were applied to this paragraph before this revision.

  • The attributes of this element contain information about when this revision took place (in other words, when these properties became a "former" set of paragraph properties).

Consider a paragraph in a WordprocessingML document which is centered, and this change in the paragraph properties is tracked as a revision. This revision would be specified using the following WordprocessingML markup.

<w:pPr>
  <w:jc w:val="center"/>
  <w:pPrChange w:id="0" w:date="01-01-2006T12:00:00" w:author="Samantha Smith">
    <w:pPr/>
  </w:pPrChange>
</w:pPr>

The element specifies that there was a revision to the paragraph properties at 01-01-2006 by Samantha Smith, and the previous set of paragraph properties on the paragraph was the null set (in other words, no paragraph properties explicitly present under the element).pPrpPrChange

© ISO/IEC29500: 2008.

The following information from the ISO/IEC 29500 specification introduces the Deleted element (del).

del (Deleted Paragraph)

This element specifies that the paragraph mark delimiting the end of a paragraph within a WordprocessingML document shall be treated as deleted (in other words, the contents of this paragraph are no longer delimited by this paragraph mark, and are combined with the following paragraph—but those contents shall not automatically be marked as deleted) as part of a tracked revision.

Consider a document consisting of two paragraphs (with each paragraph delimited by a pilcrow ¶):

Two paragraphs each delimited by a pilcrow

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

This revision is represented using the following WordprocessingML:

<w:p>
  <w:pPr>
    <w:rPr>
      <w:del w:id="0" … />
    </w:rPr>
  </w:pPr>
  <w:r>
    <w:t>This is paragraph one.</w:t>
  </w:r>
</w:p>
<w:p>
  <w:r>
    <w:t>This is paragraph two.</w:t>
  </w:r>
</w:p>

The del element on the run properties for the first paragraph mark specifies that this paragraph mark was deleted, and this deletion was tracked as a revision.

© ISO/IEC29500: 2008.

The following information from the ISO/IEC 29500 specification introduces the Inserted element (ins).

ins (Inserted Table Row)

This element specifies that the parent table row shall be treated as an inserted row whose insertion has been tracked as a revision. This setting shall not imply any revision state about the table cells in this row or their contents (which must be revision marked independently), and shall only affect the table row itself.

Consider a two row by two column table in which the second row has been marked as inserted using a revision. This requirement would be specified using the following WordprocessingML:

<w:tbl>
  <w:tr>
    <w:tc>
      <w:p/>
    </w:tc>
    <w:tc>
      <w:p/>
    </w:tc>
  </w:tr>
  <w:tr>
    <w:trPr>
      <w:ins w:id="0" … />
    </w:trPr>
    <w:tc>
      <w:p/>
    </w:tc>
    <w:tc>
      <w:p/>
    </w:tc>
  </w:tr>
</w:tbl>

The ins element on the table row properties for the second table row specifies that this row was inserted, and this insertion was tracked as a revision.

© ISO/IEC29500: 2008.

After you have opened the document in the using statement, you instantiate the Body class, and then handle the formatting changes by creating the changesList, and removing each change (the w:pPrChange element) from the List, which is the same as accepting changes.

Body body = wdDoc.MainDocumentPart.Document.Body;

// Handle the formatting changes.
List<OpenXmlElement> changes =
    body.Descendants<ParagraphPropertiesChange>()
    .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList();

foreach (OpenXmlElement change in changes)
{
    change.Remove();
}

You then handle the deletions by constructing the deletionsList, and removing each deletion element (w:del) from the List, which is similar to the process of accepting deletion changes.

// Handle the deletions.
List<OpenXmlElement> deletions =
    body.Descendants<Deleted>()
    .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList();

deletions.AddRange(body.Descendants<DeletedRun>()
    .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList());

deletions.AddRange(body.Descendants<DeletedMathControl>()
    .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList());

foreach (OpenXmlElement deletion in deletions)
{
    deletion.Remove();
}

Finally, you handle the insertions by constructing the insertionsList and inserting the new text by removing the insertion element (w:ins), which is the same as accepting the inserted text.

// Handle the insertions.
List<OpenXmlElement> insertions =
    body.Descendants<Inserted>()
    .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList();

insertions.AddRange(body.Descendants<InsertedRun>()
    .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList());

insertions.AddRange(body.Descendants<InsertedMathControl>()
    .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList());

foreach (OpenXmlElement insertion in insertions)
{
    // Found new content.
    // Promote them to the same level as node, and then delete the node.
    foreach (var run in insertion.Elements<Run>())
    {
        if (run == insertion.FirstChild)
        {
            insertion.InsertAfterSelf(new Run(run.OuterXml));
        }
        else
        {
            insertion.NextSibling().InsertAfterSelf(new Run(run.OuterXml));
        }
    }
    insertion.RemoveAttribute("rsidR",
        "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
    insertion.RemoveAttribute("rsidRPr",
        "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
    insertion.Remove();
}

The following code example shows how to accept the entire revisions in a word processing document. To run the program, you can call the method AcceptRevisions to accept revisions in the file "word1.docx" as in the following example.

string docName = @"C:\Users\Public\Documents\word1.docx";
string authorName = "Katie Jordan";
AcceptRevisions(docName, authorName);

After you have run the program, open the word processing file to make sure that all revision marks have been accepted.

The following is the complete sample code in both C# and Visual Basic.

public static void AcceptRevisions(string fileName, string authorName)
{
    // Given a document name and an author name, accept revisions. 
    using (WordprocessingDocument wdDoc = 
        WordprocessingDocument.Open(fileName, true))
    {
        Body body = wdDoc.MainDocumentPart.Document.Body;

        // Handle the formatting changes.
        List<OpenXmlElement> changes = 
            body.Descendants<ParagraphPropertiesChange>()
            .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList();

        foreach (OpenXmlElement change in changes)
        {
            change.Remove();
        }

        // Handle the deletions.
        List<OpenXmlElement> deletions = 
            body.Descendants<Deleted>()
            .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList();
        
        deletions.AddRange(body.Descendants<DeletedRun>()
            .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList());
        
        deletions.AddRange(body.Descendants<DeletedMathControl>()
            .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList());
        
        foreach (OpenXmlElement deletion in deletions)
        {
            deletion.Remove();
        }

        // Handle the insertions.
        List<OpenXmlElement> insertions = 
            body.Descendants<Inserted>()
            .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList();

        insertions.AddRange(body.Descendants<InsertedRun>()
            .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList());

        insertions.AddRange(body.Descendants<InsertedMathControl>()
            .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList());

        foreach (OpenXmlElement insertion in insertions)
        {
            // Found new content.
            // Promote them to the same level as node, and then delete the node.
            foreach (var run in insertion.Elements<Run>())
            {
                if (run == insertion.FirstChild)
                {
                    insertion.InsertAfterSelf(new Run(run.OuterXml));
                }
                else
                {
                    insertion.NextSibling().InsertAfterSelf(new Run(run.OuterXml));
                }
            }
            insertion.RemoveAttribute("rsidR", 
                "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
            insertion.RemoveAttribute("rsidRPr", 
                "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
            insertion.Remove();
        }
    }
}
Show:
© 2015 Microsoft