Replace the styles parts in a word processing document

This topic shows how to use the classes in the Open XML SDK for Office to programmatically replace the styles in a word processing document with the styles from another word processing document. It contains an example ReplaceStyles method to illustrate this task, as well as the ReplaceStylesPart and ExtractStylesPart supporting methods.


About Styles Storage

A word processing document package, such as a file that has a .docx extension, is in fact a .zip file that consists of several parts. You can think of each part as being similar to an external file. A part has a particular content type, and can contain content equal to the content of an external XML file, binary file, image file, and so on, depending on the type. The standard that defines how Open XML documents are stored in .zip files is called the Open Packaging Conventions. For more information about the Open Packaging Conventions, see ISO/IEC 29500-2.

Styles are stored in dedicated parts within a word processing document package. An Microsoft Word 2010 document contains a single styles part. Microsoft Word 2013 adds a second stylesWithEffects part. The following image from the Document Explorer in the Open XML SDK Productivity Tool for Microsoft Office shows the document parts in a sample Word 2013 document that contains styles.

Figure 1. Styles parts in a word processing document

Styles parts in a word processing document. In order to provide for "round-tripping" a document from Word 2013 to Word 2010 and back, Word 2013 maintains both the original styles part and the new styles part. (The Office Open XML File Formats specification requires that Microsoft Word ignore any parts that it does not recognize; Word 2010 does not notice the stylesWithEffects part that Word 2013 adds to the document.)

The code example provided in this topic can be used to replace these styles parts.


ReplaceStyles Method

You can use the ReplaceStyles sample method to replace the styles in a word processing document with the styles in another word processing document. The ReplaceStyles method accepts two parameters: the first parameter contains a string that indicates the path of the file that contains the styles to extract. The second parameter contains a string that indicates the path of the file to which to copy the styles, effectively completely replacing the styles.

    public static void ReplaceStyles(string fromDoc, string toDoc)

The complete code listing for the ReplaceStyles method and its supporting methods can be found in the Sample Code section.


Calling the Sample Method

To call the sample method, you pass a string for the first parameter that indicates the path of the file with the styles to extract, and a string for the second parameter that represents the path to the file in which to replace the styles. The following sample code shows an example. When the code finishes executing, the styles in the target document will have been replaced, and consequently the appearance of the text in the document will reflect the new styles.

    const string fromDoc = @"C:\Users\Public\Documents\StylesFrom.docx";
    const string toDoc = @"C:\Users\Public\Documents\StylesTo.docx";
    ReplaceStyles(fromDoc, toDoc);

How the Code Works

The code extracts and replaces the styles part first, and then the stylesWithEffects part second, and relies on two supporting methods to do most of the work. The ExtractStylesPart method has the job of extracting the content of the styles or stylesWithEffects part, and placing it in an XDocument object. The ReplaceStylesPart method takes the object created by ExtractStylesPart and uses its content to replace the styles or stylesWithEffects part in the target document.

    // Extract and replace the styles part.
    var node = ExtractStylesPart(fromDoc, false);
    if (node != null)
        ReplaceStylesPart(toDoc, node, false);

The final parameter in the signature for either the ExtractStylesPart or the ReplaceStylesPart method determines whether the styles part or the stylesWithEffects part is employed. A value of false indicates that you want to extract and replace the styles part. The absence of a value (the parameter is optional), or a value of true (the default), means that you want to extract and replace the stylesWithEffects part.

    // Extract and replace the stylesWithEffects part. To fully support 
    // round-tripping from Word 2013 to Word 2010, you should 
    // replace this part, as well.
    node = ExtractStylesPart(fromDoc);
    if (node != null)
        ReplaceStylesPart(toDoc, node);
    return;

For more information about the ExtractStylesPart method, see the associated sample. The following section explains the ReplaceStylesPart method.


ReplaceStylesPart Method

The ReplaceStylesPart method can be used to replace the styles or styleWithEffects part in a document, given an XDocument instance that contains the same part for a Word 2010 or Word 2013 document (as shown in the sample code earlier in this topic, the ExtractStylesPart method can be used to obtain that instance). The ReplaceStylesPart method accepts three parameters: the first parameter contains a string that indicates the path to the file that you want to modify. The second parameter contains an XDocument object that contains the styles or stylesWithEffect part from another word processing document, and the third indicates whether you want to replace the styles part, or the stylesWithEffects part (as shown in the sample code earlier in this topic, you will need to call this procedure twice for Word 2013 documents, replacing each part with the corresponding part from a source document).

    public static void ReplaceStylesPart(string fileName, XDocument newStyles,
      bool setStylesWithEffectsPart = true)

How the ReplaceStylesPart Code Works

The ReplaceStylesPart method examines the document you specify, looking for the styles or stylesWithEffects part. If the requested part exists, the method saves the supplied XDocument into the selected part.

The code starts by opening the document by using the Open method and indicating that the document should be open for read/write access (the final true parameter). Given the open document, the code uses the MainDocumentPart property to navigate to the main document part, and then prepares a variable named stylesPart to hold a reference to the styles part.

    // Open the document for write access and get a reference.
    using (var document = 
        WordprocessingDocument.Open(fileName, true))
    {
        // Get a reference to the main document part.
        var docPart = document.MainDocumentPart;

        // Assign a reference to the appropriate part to the
        // stylesPart variable.
        StylesPart stylesPart = null;

Find the Correct Styles Part

The code next retrieves a reference to the requested styles part, using the setStylesWithEffectsPart Boolean parameter. Based on this value, the code retrieves a reference to the requested styles part, and stores it in the stylesPart variable.

    if (setStylesWithEffectsPart)
        stylesPart = docPart.StylesWithEffectsPart;
    else
        stylesPart = docPart.StyleDefinitionsPart;

Save the Part Contents

Assuming that the requested part exists, the code must save the entire contents of the XDocument passed to the method to the part. Each part provides a GetStream method, which returns a Stream. The code passes the Stream instance to the constructor of the StreamWriter class, creating a stream writer around the stream of the part. Finally, the code calls the Save method of the XDocument, saving its contents into the styles part.

    // If the part exists, populate it with the new styles.
    if (stylesPart != null)
    {
        newStyles.Save(new StreamWriter(stylesPart.GetStream(
          FileMode.Create, FileAccess.Write)));
    }

Sample Code

The following is the complete ReplaceStyles, ReplaceStylesPart, and ExtractStylesPart methods in C# and Visual Basic.

using DocumentFormat.OpenXml.Packaging;
using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;

ReplaceStyles(args[0], args[1]);

// Replace the styles in the "to" document with the styles in
// the "from" document.
static void ReplaceStyles(string fromDoc, string toDoc)
{
    // Extract and replace the styles part.
    var node = ExtractStylesPart(fromDoc, false);
    if (node is not null)
        ReplaceStylesPart(toDoc, node, false);

    // Extract and replace the stylesWithEffects part. To fully support 
    // round-tripping from Word 2010 to Word 2007, you should 
    // replace this part, as well.
    node = ExtractStylesPart(fromDoc);
    if (node is not null)
        ReplaceStylesPart(toDoc, node);
    return;
}

// Given a file and an XDocument instance that contains the content of 
// a styles or stylesWithEffects part, replace the styles in the file 
// with the styles in the XDocument.
static void ReplaceStylesPart(string fileName, XDocument newStyles, bool setStylesWithEffectsPart = true)
{
    // Open the document for write access and get a reference.
    using (var document = WordprocessingDocument.Open(fileName, true))
    {
        if (document.MainDocumentPart is null || document.MainDocumentPart.StyleDefinitionsPart is null || document.MainDocumentPart.StylesWithEffectsPart is null)
        {
            throw new ArgumentNullException("MainDocumentPart and/or one or both of the Styles parts is null.");
        }

        // Get a reference to the main document part.
        var docPart = document.MainDocumentPart;

        // Assign a reference to the appropriate part to the
        // stylesPart variable.
        StylesPart? stylesPart = null;
        if (setStylesWithEffectsPart)
            stylesPart = docPart.StylesWithEffectsPart;
        else
            stylesPart = docPart.StyleDefinitionsPart;

        // If the part exists, populate it with the new styles.
        if (stylesPart is not null)
        {
            newStyles.Save(new StreamWriter(stylesPart.GetStream(
              FileMode.Create, FileAccess.Write)));
        }
    }
}

// Extract the styles or stylesWithEffects part from a 
// word processing document as an XDocument instance.
static XDocument ExtractStylesPart(string fileName, bool getStylesWithEffectsPart = true)
{
    // Declare a variable to hold the XDocument.
    XDocument? styles = null;

    // Open the document for read access and get a reference.
    using (var document = WordprocessingDocument.Open(fileName, false))
    {
        // Get a reference to the main document part.
        var docPart = document.MainDocumentPart;

        if (docPart is null || docPart.StyleDefinitionsPart is null || docPart.StylesWithEffectsPart is null)
        {
            throw new ArgumentNullException("MainDocumentPart and/or one or both of the Styles parts is null.");
        }

        // Assign a reference to the appropriate part to the
        // stylesPart variable.
        StylesPart? stylesPart = null;
        if (getStylesWithEffectsPart)
            stylesPart = docPart.StylesWithEffectsPart;
        else
            stylesPart = docPart.StyleDefinitionsPart;

        using (var reader = XmlNodeReader.Create(
          stylesPart.GetStream(FileMode.Open, FileAccess.Read)))
        {
            // Create the XDocument.
            styles = XDocument.Load(reader);
        }
    }
    // Return the XDocument instance.
    return styles;
}

See also