Click here to Skip to main content
Click here to Skip to main content
Go to top

Generate Word documents from Word templates using the Word 2007 XML object model

, 16 Dec 2007
Rate this:
Please Sign up or sign in to vote.
How to generate Word documents from Word templates using the Word 2007 XML format.

Introduction

In many projects, developers need to generate reports in Word documents in a certain format or from a template. The old way of generating this using server-side Word instantiations will consume a lot of server resources and is not recommended. An alternative to this is Word generation using the Word 2007 XML object. Basic information about the Word 2007 XML format can be found here.

This article covers the generation of contract documents from a template on the server by populating the bookmarks and their styles.

Using the Code

This code requires Word 2007, and .NET Framework 3.0 or above to work. .NET 3.0 ships with a special DLL called WindowsBase.dll and a namespace called System.IO.Packaging, which are used to generate the Word documents without instantiating a Word object on the server.

//Namespaces required
using System.IO;
using System.IO.Packaging;
using System.Xml;
using System.Collections.Generic;

//Variable and constants.
const string documentRelationshipType = 
  "http://schemas.openxmlformats.org/" + 
  "officeDocument/2006/relationships/officeDocument";
const string headerContentType = 
  "application/vnd.openxmlformats-" + 
  "officedocument.wordprocessingml.header+xml";
const string footerContentType = 
  "application/vnd.openxmlformats-" + 
  "officedocument.wordprocessingml.footer+xml";
XmlNamespaceManager nsManager;

//Method which will create the documents on the fly.

private void CreateWordDocument()
{
    Random RandomClass = new Random();
    int randomInt = RandomClass.Next();

    //Word template file
    string templateName = "Template.docx";

    //New file name to be generated from 
    string docFileName = "Doc_" + randomInt + ".docx";

    File.Copy(Server.MapPath(@"_Documents/" + templateName), 
              Server.MapPath(@"_Documents/" + docFileName),true);

    string fileName = Server.MapPath(@"_Documents/" + docFileName);

    PackagePart documentPart = null;
    Package package = 
      Package.Open(fileName, FileMode.Open, FileAccess.ReadWrite);

    //  Get the main document part (document.xml).
    foreach (System.IO.Packaging.PackageRelationship documentRelationship 
             in package.GetRelationshipsByType(documentRelationshipType))
    {
        NameTable nt = new NameTable();
        nsManager = new XmlNamespaceManager(nt);
        nsManager.AddNamespace("w", 
          "http://schemas.openxmlformats.org/wordprocessingml/2006/main");

        Uri documentUri = PackUriHelper.ResolvePartUri(
          new Uri("/", UriKind.Relative), documentRelationship.TargetUri);
        documentPart = package.GetPart(documentUri);

        #region Update Document Bookmarks
        //Get document xml
        XmlDocument xdoc = new XmlDocument();

        //xdoc.Load(documentPart.GetStream());
        xdoc.Load(documentPart.GetStream(FileMode.Open,FileAccess.Read));

        //Select all bookmark nodes
        XmlNodeList nodeList = 
          xdoc.SelectNodes("//w:bookmarkStart", nsManager);

        foreach (XmlNode node in nodeList)
        {
           // S_ADDRESS_V1 and S_ADDRESS_V2 are
           // the bookmarks defined in the template 
           if(this.SetBookmarkText(xdoc, node, "S_ADDRESS_V1", 
                  RandomClass.Next().ToString())) continue;

           if (this.SetBookmarkText(xdoc, node, "S_ADDRESS_V2", 
                  RandomClass.Next().ToString())) continue;
        }

        #endregion

        #region Update Header/Footer Bookmarks

        PackagePartCollection documentParts = package.GetParts();

        foreach (PackagePart part in documentParts)
        {
            //Update header bookmarks
            if (part.ContentType == headerContentType)
            {
                //Get document xml
                XmlDocument xheader = new XmlDocument();
                xheader.Load(part.GetStream(FileMode.Open, FileAccess.Read));

                //Select all bookmark nodes
                XmlNodeList headerNodeList = 
                  xheader.SelectNodes("//w:bookmarkStart", nsManager);
                foreach (XmlNode node in headerNodeList)
                {
                    //HEADER5 is the bookmark of header in template
                    if (this.SetBookmarkText(xheader, node, 
                          "HEADER5", "Test Header")) continue;
                }

                //Save 
                if (headerNodeList.Count > 0)
                {
                    StreamWriter streamHeader = 
                      new StreamWriter(part.GetStream(FileMode.Open, FileAccess.Write));
                    xheader.Save(streamHeader);
                    streamHeader.Close();
                }
            }

            //Update footer bookmarks
            if (part.ContentType == footerContentType)
            {
                //Get document xml
                XmlDocument xfooter = new XmlDocument();
                xfooter.Load(part.GetStream(FileMode.Open, FileAccess.Read));

 
                //Select all bookmark nodes
                XmlNodeList footerNodeList = 
                  xfooter.SelectNodes("//w:bookmarkStart", nsManager);

                foreach (XmlNode node in footerNodeList)
                {
                    //FOOTER_1_1
                    if (this.SetBookmarkText(xfooter, node, 
                          "FOOTER_1_1", "Number", true)) continue;
                    if (this.SetBookmarkText(xfooter, node, 
                          "FOOTER_1_2", "123456", true)) continue;
                }

                //Save 
                if (footerNodeList.Count > 0)
                {
                    StreamWriter streamFooter = 
                       new StreamWriter(part.GetStream(FileMode.Open, FileAccess.Write));
                    xfooter.Save(streamFooter);
                    streamFooter.Close();
                }
            }
        }

        #endregion

        StreamWriter streamPart = new StreamWriter(
           documentPart.GetStream(FileMode.Open, FileAccess.Write));
        xdoc.Save(streamPart);
        streamPart.Close();
    }

    package.Flush();
    package.Close();

    ////send response to browser
    /*string File_Name = "_Documents/" + docFileName;
    string popupScript = "<script language="'javascript'">" +
                     "window.open('" + File_Name + "', 'Document', " +
                     "'width=700, height=600, menubar=yes, resizable=yes')" +
                     "</script>";

    ClientScript.RegisterClientScriptBlock(this.GetType(), 
                  "PopupScriptOffer", popupScript);
     */
}

/// <summary>
/// 
/// </summary>
/// <param name="xdoc"></param>
/// <param name="node"></param>
/// <param name="bookmarkName"></param>
private bool SetBookmarkText(XmlDocument xdoc, XmlNode node, 
                             string bookmarkName, string bookmarkValue)
{
    if (node.NextSibling.Name.ToString() == "w:bookmarkEnd")
    {
        if (node.Attributes["w:name"].Value == bookmarkName)
        {
            //get the node previous sibling style
            //("w:rPr") to apply to the bookmark text
            XmlNode nodeStyle = node.PreviousSibling.CloneNode(true);

            //parent node "w:p"
            XmlNode bookmrkParent = node.ParentNode;

            XmlElement tagRun;
            tagRun = xdoc.CreateElement("w:r", 
                          nsManager.LookupNamespace("w"));
            bookmrkParent.AppendChild(tagRun);

            //if (nodeStyle != null && nodeStyle.FirstChild.Name == "w:rPr")
            //    tagRun.AppendChild(nodeStyle.FirstChild);

            if (nodeStyle.SelectSingleNode("//w:rPr", nsManager) != null)
                tagRun.AppendChild(nodeStyle.SelectSingleNode("//w:rPr", nsManager));

            XmlElement tagText;
            tagText = xdoc.CreateElement("w:t", 
                              nsManager.LookupNamespace("w"));
            tagRun.AppendChild(tagText);

            //*** insert text into part as a Text node 
            XmlNode nodeText;
            nodeText = xdoc.CreateNode(XmlNodeType.Text, 
                         "w:t", nsManager.LookupNamespace("w"));
            nodeText.Value = bookmarkValue;
            tagText.AppendChild(nodeText);

            return true;
        }
    }
    return false;
}

private bool SetBookmarkText(XmlDocument xdoc, XmlNode node, 
             string bookmarkName, string bookmarkValue, bool IsFooter)
{
    if (node.NextSibling.Name.ToString() == "w:bookmarkEnd")
    {
        if (node.Attributes["w:name"].Value == bookmarkName)
        {
            //get the node previous sibling style
            //("w:rPr") to apply to the bookmark text
            XmlNode nodeStyle = node.PreviousSibling.CloneNode(true);

            //parent node "w:p"
            XmlNode bookmrkParent = node.ParentNode;

            XmlElement tagRun;
            tagRun = xdoc.CreateElement("w:r", 
                           nsManager.LookupNamespace("w"));
            bookmrkParent.AppendChild(tagRun);

            if (nodeStyle.SelectSingleNode("//w:rPr", nsManager) != null)
            {
                XmlNode xfootStyle = 
                  nodeStyle.SelectSingleNode("//w:rPr", nsManager);

                //reduce font size for footer to 16.  <w:sz w:val="20" />
                /*if (IsFooter)
                {
                    xfootStyle.SelectSingleNode("//w:sz", 
                       nsManager).Attributes["w:val"].Value = "16";
                }*/
                tagRun.AppendChild(xfootStyle);
            }

            XmlElement tagText;
            tagText = xdoc.CreateElement("w:t", 
                            nsManager.LookupNamespace("w"));
            tagRun.AppendChild(tagText);

            //*** insert text into part as a Text node 
            XmlNode nodeText;
            nodeText = xdoc.CreateNode(XmlNodeType.Text, 
               "w:t", nsManager.LookupNamespace("w"));
            nodeText.Value = bookmarkValue;
            tagText.AppendChild(nodeText);

            return true;
        }
    }
    return false;
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Praveen Bonakurthi
Web Developer TCS Hyderabad
India India
I am a Software Engineer working for TCS.
 
I have an experience in ASP, ASP.NET, C#, SQL Server 2005.

Comments and Discussions

 
QuestionImport data to DOCX template PinmemberJohnny Glenn26-Mar-12 22:52 
Hi,
 
there is also another easy way to import data from any .NET object to C# DOCX file format, using the C# Word mail merge.
 
Here is a sample code:
// Use the component in free mode.
ComponentInfo.SetLicense("FREE-LIMITED-KEY");
 
// Define DataTable with two columns: 'Name' and 'Surname', and fill it with some data.
// You don't have to do this if you already have a DataTable instance.
var dataTable = new DataTable("People")
{
  Columns =
  {
    new DataColumn("Name", typeof(string)),
    new DataColumn("Surname", typeof(string))
  },
  Rows =
  {
    new object[] { "John", "Doe" },
    new object[] { "Fred", "Nurk" },
    new object[] { "Hans", "Meier" },
    new object[] { "Ivan", "Horvat" }
  }
};
 
// Create and save a template document. 
// You don't have to do this if you already have a template document.
// This code is only provided as a reference how template document should look like.
var document = new DocumentModel();
document.Sections.Add(
  new Section(document,
    new Table(document,
      new TableRow(document,
        new TableCell(document,
          new Paragraph(document, "Name")),
        new TableCell(document,
          new Paragraph(document, "Surname"))),
      new TableRow(document,
        new TableCell(document,
          new Paragraph(document,
            new Field(document, FieldType.MergeField, "RangeStart:People"),
            new Field(document, FieldType.MergeField, "Name"))),
        new TableCell(document,
          new Paragraph(document,
            new Field(document, FieldType.MergeField, "Surname"),
            new Field(document, FieldType.MergeField, "RangeEnd:People")))))));
document.Save("TemplateDocument.docx", SaveOptions.DocxDefault);
 
// Load a template document.
document = DocumentModel.Load("TemplateDocument.docx", LoadOptions.DocxDefault);
 
// Mail merge template document with DataTable.
// Important: DataTable.TableName and RangeStart/RangeEnd merge field names must match.
document.MailMerge.ExecuteRange(dataTable);
 
// Save the mail merged document.
document.Save("Document.docx", SaveOptions.DocxDefault);
 
This code uses this C# / VB.NET Word library.
AnswerRe: Import data to DOCX template Pinmemberhugoro6-Jun-14 14:43 
GeneralMy vote of 3 PinmemberHHPanos27-May-11 9:58 
Generalinserting tab's Pinmemberek45271-May-09 5:56 
GeneralCode changes PinmemberPaulo Vaz28-Jan-09 15:33 
QuestionPrinting PinmemberSnowdon-Darling28-Oct-08 22:00 
AnswerRe: Printing PinmemberPraveen Bonakurthi22-Nov-08 10:12 
Questionbookmark question Pinmemberrichardcruz29-May-08 1:57 
GeneralGenerate the word documents from word templates using word 2007 XML object model PinmemberMember 402700828-Apr-08 11:35 
GeneralRe: Generate the word documents from word templates using word 2007 XML object model PinmemberPredrag Tomasevic28-Jan-09 17:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 16 Dec 2007
Article Copyright 2007 by Praveen Bonakurthi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid