Changing the Print Orientation of Word 2010 Documents by Using the Open XML SDK 2.0

Office Visual How To

Summary:  Use the strongly typed classes in the Open XML SDK 2.0 to specify the print orientation for a Word document, without loading the document into Microsoft Word.

Applies to: Excel 2010 | Office 2007 | Office 2010 | Open XML | PowerPoint 2010 | VBA | Word 2010

Published:  November 2010

Provided by:  Ken Getz, MCW Technologies, LLC

Overview

The Open XML file formats make it possible to specify print orientation in Microsoft Word documents. The Open XML SDK 2.0 adds strongly typed classes that simplify access to the Open XML file formats: The SDK simplifies the tasks of working with print orientation, because it makes it easier to interact with the elements within the Open XML content. The code sample that is included with this Visual How To describes how to the use the SDK to achieve this goal.

Code It

The sample, provided with this Visual How To, includes the code that is required to specify print orientation in a Word 2007 or Word 2010 document. The following sections describe the code.

Setting Up References

To use the code from the Open XML SDK 2.0, you must add some references to your project. The sample project already includes these references, but in your own code, you would have to explicitly reference the following assemblies:

  • WindowsBase─This reference may be set for you, depending on the kind of project that you create.

  • DocumentFormat.OpenXml─This assembly is installed by the Open XML SDK 2.0.

Also, you should add the following using/Imports statements to the top of your code file.

Imports DocumentFormat.OpenXml.Wordprocessing
Imports DocumentFormat.OpenXml.Packaging
Imports DocumentFormat.OpenXml
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

Examining the Procedure

The WDSetPrintOrientation procedure accepts two parameters: to indicate the name of the document to modify (string), and to describe the new print orientation (DocumentFormat.OpenXml.Wordprocessing.PageOrientationValues).

Public Sub WDSetPrintOrientation(
  ByVal fileName As String, 
  ByVal newOrientation As PageOrientationValues)
public static void WDSetPrintOrientation(
  string fileName, PageOrientationValues newOrientation)

For each section in the document, if the new orientation is different than the print orientation of the current section, the procedure modifies the print orientation for the section. In addition, the procedure must update the width and height and the margins for each section.

To call the procedure, pass the parameter values, as shown in the example code. Verify that you provide a document named C:\temp\Orientation.docx, which (for demonstration) contains multiple sections, before you run the sample code.

WDSetPrintOrientation(
  "C:\temp\Orientation.docx", PageOrientationValues.Portrait)
WDSetPrintOrientation(
  @"C:\temp\Orientation.docx", PageOrientationValues.Portrait);

Accessing the Document

The code starts by opening the document by using the WordprocessingDocument.Open method and indicating that the document should be open for read-write access (the final true parameter). The code maintains a Boolean variable tracking whether the document has changed (so it can save the document later, if so). The code retrieves a reference to the main document part, and then uses that reference to retrieve a collection of all the SectionProperties descendants within the content of the document—later code will use this collection to set the orientation for each section in turn.

    Using document = WordprocessingDocument.Open(fileName, True)
      Dim documentChanged As Boolean = False

      Dim docPart = document.MainDocumentPart
      Dim sections = docPart.Document.Descendants(
        Of SectionProperties)()
  ' Code removed here…
End Using
using (var document = WordprocessingDocument.Open(fileName, true))
{
  bool documentChanged = false;

  var docPart = document.MainDocumentPart;
  var sections = docPart.Document.Descendants<SectionProperties>();
  // Code removed here…
}

Iterating Through the Sections

The next block of code iterates through all the sections in the collection of SectionProperties elements. For each section, the code initializes a variable that tracks whether the page orientation for the section was changed (it is possible that the new orientation matches the original orientation, with no change required), so the code can update the page size and margins. The code continues by retrieving a reference to the first PageSize descendant of the SectionProperties element. If the reference is not null , the code will fix the orientation as required.

For Each sectPr As SectionProperties In sections
  Dim pageOrientationChanged As Boolean = False

  Dim pgSz As PageSize = sectPr.Descendants(Of PageSize).FirstOrDefault
  If pgSz IsNot Nothing Then
    ' Code removed here…
  End If
Next
foreach (SectionProperties sectPr in sections)
{
  bool pageOrientationChanged = false;

  PageSize pgSz = sectPr.Descendants<PageSize>().FirstOrDefault();
  if (pgSz != null)
  {
    // Code removed here…
  }
}

Setting the Section Orientation

The next block of code starts by verifying that the Orient property of the PageSize element already exists. As with many properties of Microsoft Word Open XML elements, it is possible that the property/attribute does not exist yet. In that case, retrieving the property returns a null reference using the Open XML SDK 2.0. If the property does not exist, and the new orientation is Portrait, there is nothing to do—that is the default and there is no reason to modify the document. If the Orient property already exists, and its value differs from the new orientation value supplied as a parameter to the method, the code sets the Value property of the Orient property, and sets both the pageOrientationChanged and the documentChanged flags. (The code uses the pageOrientationChanged flag to determine whether it must update the page size and margins. It uses the documentChanged flag to determine whether it must save the document at the end). Be aware that if the code must create the Orient property, it must also create the value to store in the property, as a new EnumValue instance, supplying the new orientation in the EnumValue constructor.

If pgSz.Orient Is Nothing Then
  If newOrientation <> PageOrientationValues.Portrait Then
    pageOrientationChanged = True
    documentChanged = True
    pgSz.Orient = New EnumValue(
      Of PageOrientationValues)(newOrientation)
  End If
Else
  If pgSz.Orient.Value <> newOrientation Then
    pgSz.Orient.Value = newOrientation
    pageOrientationChanged = True
    documentChanged = True
  End If
if (pgSz.Orient == null)
{
  if (newOrientation != PageOrientationValues.Portrait)
  {
    pageOrientationChanged = true;
    documentChanged = true;
    pgSz.Orient = new EnumValue<PageOrientationValues>(newOrientation);
  }
}
else
{
  if (pgSz.Orient.Value != newOrientation)
  {
    pgSz.Orient.Value = newOrientation;
    pageOrientationChanged = true;
    documentChanged = true;
  }
}

Fixing the Page Size

At this point in the code, the page orientation may have changed. If so, the code must perform two more tasks: It must fix the page size, and fix the page margins for the section. The code swaps the page height and width, storing the values in the PageSize element.

If pageOrientationChanged Then
  Dim width = pgSz.Width
  Dim height = pgSz.Height
  pgSz.Width = height
  pgSz.Height = width
  ' Code removed here…
End If
if (pageOrientationChanged)
{
  var width = pgSz.Width;
  var height = pgSz.Height;
  pgSz.Width = height;
  pgSz.Height = width;
  // Code removed here…
}

Fixing the Margins

The next step in the sample procedure handles margins for the section. If the page orientation has changed, the code must rotate the margins to match. To do so, the code retrieves a reference to the PageMargin element for the section and, if it exists, rotates the margins. Be aware that the code rotates the margins by 90 degrees—some printers rotate the margins by 270 degrees instead, and you could modify the code to account for that. Also, be aware that the Top and Bottom properties of the PageMargin object are signed values, and the Left and Right properties are unsigned values. The code must convert between the two kinds of values as it rotates the margin settings.

Dim pgMar As PageMargin =
  sectPr.Descendants(Of PageMargin).FirstOrDefault()
If pgMar IsNot Nothing Then

  ' Rotate margins. Printer settings control how far you 
  ' rotate when switching to landscape mode. Not having those
  ' settings, this code rotates 90 degrees. You could easily
  ' modify this behavior, or make it a parameter for the 
  ' procedure.

  Dim top = pgMar.Top.Value
  Dim bottom = pgMar.Bottom.Value
  Dim left = pgMar.Left.Value
  Dim right = pgMar.Right.Value

  pgMar.Top = CType(left, Int32Value)
  pgMar.Bottom = CType(right, Int32Value)
  pgMar.Left = CType(System.Math.Max(
    0, CType(bottom, Int32Value)), UInt32Value)
  pgMar.Right = CType(System.Math.Max(
    0, CType(top, Int32Value)), UInt32Value)
End If
PageMargin pgMar = sectPr.Descendants<PageMargin>().FirstOrDefault();
if (pgMar != null)
{

  // Rotate margins. Printer settings control how far you 
  // rotate when switching to landscape mode. Not having those
  // settings, this code rotates 90 degrees. You could easily
  // modify this behavior, or make it a parameter for the 
  // procedure.

  var top = pgMar.Top.Value;
  var bottom = pgMar.Bottom.Value;
  var left = pgMar.Left.Value;
  var right = pgMar.Right.Value;

  pgMar.Top = new Int32Value((int)left);
  pgMar.Bottom = new Int32Value((int)right);
  pgMar.Left = new UInt32Value((uint)System.Math.Max(0, bottom));
  pgMar.Right = new UInt32Value((uint)System.Math.Max(0, top));
}

Saving the Document

After modification, the code determines whether the document has changed; if the document has changed, the code then saves it.

If documentChanged Then
  docPart.Document.Save()
End If
if (documentChanged)
{
  docPart.Document.Save();
}

Sample Procedure

The following code example contains the complete sample procedure.

Public Sub WDSetPrintOrientation(
  ByVal fileName As String, ByVal newOrientation As PageOrientationValues)
  Using document = WordprocessingDocument.Open(fileName, True)
    Dim documentChanged As Boolean = False

    Dim docPart = document.MainDocumentPart
    Dim sections = docPart.Document.Descendants(Of SectionProperties)()

    For Each sectPr As SectionProperties In sections

      Dim pageOrientationChanged As Boolean = False

      Dim pgSz As PageSize = sectPr.Descendants(Of PageSize).FirstOrDefault
      If pgSz IsNot Nothing Then
        ' No Orient property? Create it now. Otherwise, just set its value.
        ' Assume that the default orientation is Portrait.
        If pgSz.Orient Is Nothing Then
          ' Need to create the attribute. You do not need to 
          ' create the Orient property if the property doesn't already
          ' exist and you are setting it to Portrait. That's the default value.
          If newOrientation <> PageOrientationValues.Portrait Then
            pageOrientationChanged = True
            documentChanged = True
            pgSz.Orient = New EnumValue(Of PageOrientationValues)(newOrientation)
          End If
        Else
          ' The Orient property exists, but its value
          ' is different than the new value.
          If pgSz.Orient.Value <> newOrientation Then
            pgSz.Orient.Value = newOrientation
            pageOrientationChanged = True
            documentChanged = True
          End If
        End If

        If pageOrientationChanged Then
          ' Changing the orientation isn't enough. You must also change the page size.
          Dim width = pgSz.Width
          Dim height = pgSz.Height
          pgSz.Width = height
          pgSz.Height = width

          Dim pgMar As PageMargin =
            sectPr.Descendants(Of PageMargin).FirstOrDefault()
          If pgMar IsNot Nothing Then

            ' Rotate margins. Printer settings control how far you 
            ' rotate when switching to landscape mode. Not having those
            ' settings, this code rotates 90 degrees. You could easily
            ' modify this behavior, or make it a parameter for the 
            ' procedure.

            Dim top = pgMar.Top.Value
            Dim bottom = pgMar.Bottom.Value
            Dim left = pgMar.Left.Value
            Dim right = pgMar.Right.Value

            pgMar.Top = CType(left, Int32Value)
            pgMar.Bottom = CType(right, Int32Value)
            pgMar.Left = CType(System.Math.Max(
              0, CType(bottom, Int32Value)), UInt32Value)
            pgMar.Right = CType(System.Math.Max(
              0, CType(top, Int32Value)), UInt32Value)
          End If
        End If
      End If
    Next

    If documentChanged Then
      docPart.Document.Save()
    End If
  End Using
End Sub
public static void WDSetPrintOrientation(
  string fileName, PageOrientationValues newOrientation)
{
  using (var document = WordprocessingDocument.Open(fileName, true))
  {
    bool documentChanged = false;

    var docPart = document.MainDocumentPart;
    var sections = docPart.Document.Descendants<SectionProperties>();

    foreach (SectionProperties sectPr in sections)
    {
      bool pageOrientationChanged = false;

      PageSize pgSz = sectPr.Descendants<PageSize>().FirstOrDefault();
      if (pgSz != null)
      {
        if (pgSz.Orient == null)
        {
          if (newOrientation != PageOrientationValues.Portrait)
          {
            pageOrientationChanged = true;
            documentChanged = true;
            pgSz.Orient = 
              new EnumValue<PageOrientationValues>(newOrientation);
          }
        }
        else
        {
          // The Orient property exists, but its value
          // is different than the new value.
          if (pgSz.Orient.Value != newOrientation)
          {
            pgSz.Orient.Value = newOrientation;
            pageOrientationChanged = true;
            documentChanged = true;
          }
        }

        if (pageOrientationChanged)
        {
          var width = pgSz.Width;
          var height = pgSz.Height;
          pgSz.Width = height;
          pgSz.Height = width;

          PageMargin pgMar = 
            sectPr.Descendants<PageMargin>().FirstOrDefault();
          if (pgMar != null)
          {
            var top = pgMar.Top.Value;
            var bottom = pgMar.Bottom.Value;
            var left = pgMar.Left.Value;
            var right = pgMar.Right.Value;

            pgMar.Top = new Int32Value((int)left);
            pgMar.Bottom = new Int32Value((int)right);
            pgMar.Left = new UInt32Value(
              (uint)System.Math.Max(0, bottom));
            pgMar.Right = new UInt32Value(
              (uint)System.Math.Max(0, top));
          }
        }
      }
    }
    if (documentChanged)
    {
      docPart.Document.Save();
    }
  }
}
Read It

The sample that is included with this Visual How To describes code that sets print orientation in a Word document. In order to use the sample, you must install the Open XML SDK 2.0, available from the link listed in the Explore It section. The sample also uses a modified version of code included as part of a set of code examples for the Open XML SDK 2.0. The Explore It section also includes a link to the full set of code examples, although you can use the sample without downloading and installing the code examples.

The sample application demonstrates only some the available properties and methods that are provided by the Open XML SDK 2.0 that you can use to modify document structure. For more information, see the documentation that is included with the Open XML SDK 2.0 Productivity Tool. Click the Open XML SDK Documentation tab in the lower-left corner of the application window, and search for the class that you want to study. Although the documentation does currently not include code examples, given the sample shown here and the documentation, you should be able to successfully modify the sample application.

See It

Watch the video

> [!VIDEO https://www.microsoft.com/en-us/videoplayer/embed/5b8b0111-18da-4aff-a570-f9bad7e33bd5]

Length: 00:09:24

Click to grab code

Grab the Code

Explore It

About the Author
Ken Getz is a senior consultant with MCW Technologies. He is coauthor of ASP.NET Developers Jumpstart (Addison-Wesley, 2002), Access Developer's Handbook (Sybex, 2001), and VBA Developer's Handbook, 2nd Edition (Sybex, 2001).