Welcome to MSDN Blogs Sign in | Join | Help

Pranab Paul's Blog - Development Tips on SharePoint, Office and Web

------------------------------------------Web Parts, Workflow, InfoPath Form Services, Features, Site Definition, Event Receivers, Excel Services, Business Data Catalog (BDC), Search

News

SharePoint 2007 (MOSS/WSS) - Adding header and footer in Word Document (.docx) with ItemAdded Event Receiver using OpenXML

I used OpenXML to add the two metadata column values - document name as the header and modified date as footer while someone upload a document in document library. Please include a reference of  WindowsBase and DocumentFormat.OpenXml in the project. WindowsBase is included in .Net framework 3.0/3.5 and you have to download DocumentFormat.OpenXml separately.

Here is the code:

using System;

using System.Security.Permissions;

using System.Runtime.InteropServices;

using Microsoft.SharePoint;

using System.IO;

using System.IO.Packaging;

using DocumentFormat.OpenXml.Packaging;

using System.Xml;

using System.Collections.Generic;

 

namespace AddHeaderFooterReceiver

{

    public class AddHeaderFooterEventReceiver : SPItemEventReceiver

    {

        public string GetFooter()

        {

            string footerVal = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><w:ftr xmlns:ve=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" xmlns:w10=\"urn:schemas-microsoft-com:office:word\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:wne=\"http://schemas.microsoft.com/office/word/2006/wordml\"><w:p w:rsidR=\"00C24C70\" w:rsidRDefault=\"00C24C70\"><w:pPr><w:pStyle w:val=\"Footer\" /></w:pPr><w:r><w:t>Hi</w:t></w:r></w:p><w:p w:rsidR=\"00C24C70\" w:rsidRDefault=\"00C24C70\"><w:pPr><w:pStyle w:val=\"Footer\" /></w:pPr></w:p></w:ftr>";

            return footerVal;

        }

        public string GetHeader()

        {

            string headerVal = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><w:hdr xmlns:ve=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" xmlns:w10=\"urn:schemas-microsoft-com:office:word\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:wne=\"http://schemas.microsoft.com/office/word/2006/wordml\"><w:p w:rsidR=\"00C8737A\" w:rsidRDefault=\"00C8737A\"><w:pPr><w:pStyle w:val=\"Header\" /></w:pPr><w:r><w:t>hello</w:t></w:r></w:p><w:p w:rsidR=\"00C8737A\" w:rsidRDefault=\"00C8737A\"><w:pPr><w:pStyle w:val=\"Header\" /> </w:pPr></w:p></w:hdr>";           

            return headerVal;

        }

        public void WDAddHeader(Stream headerContent, Stream fileContent)

        {

            //  Given a document name, and a stream containing valid header content,

            //  add the stream content as a header in the document.

 

            const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";

            const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";

            const string headerContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml";

            const string headerRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header";

            const string relationshipNamespace = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";

 

            PackagePart documentPart = null;

 

            using (Package wdPackage = Package.Open(fileContent, FileMode.Open, FileAccess.ReadWrite))

            {

                //  Get the main document part (document.xml).

                foreach (System.IO.Packaging.PackageRelationship relationship in wdPackage.GetRelationshipsByType(documentRelationshipType))

                {

                    Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri);

                    documentPart = wdPackage.GetPart(documentUri);

                    //  There is only one officeDocument.

                    break;

                }

 

                Uri uriHeader = new Uri("/word/header1.xml", UriKind.Relative);

                if (wdPackage.PartExists(uriHeader))

                {

                    //  Although you can delete the relationship

                    //  to the existing node, the next time you save

                    //  the document after making changes, Word

                    //  will delete the relationship.

                    wdPackage.DeletePart(uriHeader);

                }

                //  Create the header part.

                PackagePart headerPart = wdPackage.CreatePart(uriHeader, headerContentType);

 

                //  Load the content from the input stream.

                //  This may seem redundant, but you must read it at some point.

                //  If you ever need to analyze the contents of the header,

                //  at least it is already in an XmlDocument.

                //  This code uses the XmlDocument object only as

                //  a "pass-through" -- giving it a place to hold as

                //  it moves from the input stream to the output stream.

                //  The code could read each byte from the input stream, and

                //  write each byte to the output stream, but this seems

                //  simpler...

                XmlDocument headerDoc = new XmlDocument();

                headerContent.Position = 0;

                headerDoc.Load(headerContent);

 

                //  Write the header out to its part.

                headerDoc.Save(headerPart.GetStream());

 

                //  Create the document's relationship to the new part.

                PackageRelationship rel = documentPart.CreateRelationship(uriHeader, TargetMode.Internal, headerRelationshipType);

                string relID = rel.Id;

 

                //  Manage namespaces to perform Xml XPath queries.

                NameTable nt = new NameTable();

                XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);

                nsManager.AddNamespace("w", wordmlNamespace);

 

                //  Get the document part from the package.

                //  Load the XML in the part into an XmlDocument instance.

                XmlDocument xdoc = new XmlDocument(nt);

                xdoc.Load(documentPart.GetStream());

 

                //  Find the node containing the document layout.

                XmlNode targetNode = xdoc.SelectSingleNode("//w:sectPr", nsManager);

                if (targetNode != null)

                {

                    //  Delete any existing references to headers.

                    XmlNodeList headerNodes = targetNode.SelectNodes("./w:headerReference", nsManager);

                    foreach (System.Xml.XmlNode headerNode in headerNodes)

                    {

                        targetNode.RemoveChild(headerNode);

                    }

 

                    //  Create the new header reference node.

                    XmlElement node = xdoc.CreateElement("w:headerReference", wordmlNamespace);

                    XmlAttribute attr = node.Attributes.Append(xdoc.CreateAttribute("r:id", relationshipNamespace));

                    attr.Value = relID;

                    node.Attributes.Append(attr);

                    targetNode.InsertBefore(node, targetNode.FirstChild);

                }

 

                //  Save the document XML back to its part.

                xdoc.Save(documentPart.GetStream(FileMode.Create, FileAccess.Write));

            }

        }

 

        public void WDAddFooter(Stream footerContent, Stream fileContent)

        {

            //  Given a document name, and a stream containing valid footer content,

            //  add the stream content as a footer in the document.

 

            const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";

            const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";

            const string footerContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml";

            const string footerRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer";

            const string relationshipNamespace = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";

 

            PackagePart documentPart = null;

 

            using (Package wdPackage = Package.Open(fileContent, FileMode.Open, FileAccess.ReadWrite))

            {

                //  Get the main document part (document.xml).

                foreach (System.IO.Packaging.PackageRelationship relationship in wdPackage.GetRelationshipsByType(documentRelationshipType))

                {

                    Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri);

                    documentPart = wdPackage.GetPart(documentUri);

                    //  There is only one officeDocument.

                    break;

                }

 

                Uri uriFooter = new Uri("/word/footer1.xml", UriKind.Relative);

                if (wdPackage.PartExists(uriFooter))

                {

                    //  Although you can delete the relationship

                    //  to the existing node, the next time you save

                    //  the document after making changes, Word

                    //  will delete the relationship.

                    wdPackage.DeletePart(uriFooter);

                }

                //  Create the footer part.

                PackagePart footerPart = wdPackage.CreatePart(uriFooter, footerContentType);

 

                //  Load the content from the input stream.

                //  This may seem redundant, but you must read it at some point.

                //  If you ever need to analyze the contents of the footer,

                //  at least it is already in an XmlDocument.

                //  This code uses the XmlDocument object only as

                //  a "pass-through" -- giving it a place to hold as

                //  it moves from the input stream to the output stream.

                //  The code could read each byte from the input stream, and

                //  write each byte to the output stream, but this seems

                //  simpler...

                XmlDocument footerDoc = new XmlDocument();

                footerContent.Position = 0;

                footerDoc.Load(footerContent);

 

                //  Write the footer out to its part.

                footerDoc.Save(footerPart.GetStream());

 

                //  Create the document's relationship to the new part.

                PackageRelationship rel = documentPart.CreateRelationship(uriFooter, TargetMode.Internal, footerRelationshipType);

                string relID = rel.Id;

 

                //  Manage namespaces to perform Xml XPath queries.

                NameTable nt = new NameTable();

                XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);

                nsManager.AddNamespace("w", wordmlNamespace);

 

                //  Get the document part from the package.

                //  Load the XML in the part into an XmlDocument instance.

                XmlDocument xdoc = new XmlDocument(nt);

                xdoc.Load(documentPart.GetStream());

 

                //  Find the node containing the document layout.

                XmlNode targetNode = xdoc.SelectSingleNode("//w:sectPr", nsManager);

                if (targetNode != null)

                {

                    //  Delete any existing references to footers.

                    XmlNodeList footerNodes = targetNode.SelectNodes("./w:footerReference", nsManager);

                    foreach (System.Xml.XmlNode footerNode in footerNodes)

                    {

                        targetNode.RemoveChild(footerNode);

                    }

 

                    //  Create the new footer reference node.

                    XmlElement node = xdoc.CreateElement("w:footerReference", wordmlNamespace);

                    XmlAttribute attr = node.Attributes.Append(xdoc.CreateAttribute("r:id", relationshipNamespace));

                    attr.Value = relID;

                    node.Attributes.Append(attr);

                    targetNode.InsertBefore(node, targetNode.FirstChild);

                }

 

                //  Save the document XML back to its part.

                xdoc.Save(documentPart.GetStream(FileMode.Create, FileAccess.Write));

            }

        }

 

        public override void ItemAdded(SPItemEventProperties properties)

        {

            string extension = properties.ListItem.Url.Substring(properties.ListItem.Url.LastIndexOf(".") + 1);

            if (extension == "docx")

            {

                string headerContent = GetHeader().Replace("hello", properties.ListItem["Name"].ToString());

                string footerContent = GetFooter().Replace("Hi", properties.ListItem["Modified"].ToString());

                Stream headerStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(headerContent));

                Stream footerStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(footerContent));

                MemoryStream fileStream = new MemoryStream();

                fileStream.Write(properties.ListItem.File.OpenBinary(), 0, (int)properties.ListItem.File.TotalLength);

                WDAddHeader(headerStream, fileStream);

                WDAddFooter(footerStream, fileStream);

                properties.ListItem.File.SaveBinary(fileStream);

            }

        }

    }

}

 

Posted: Thursday, December 18, 2008 8:28 PM by pranab

Comments

Andy Burns said:

Why not just use Document Properties Quick Parts (Word 2007 > Insert > Quick Part) in the header/footer? No code required, and it'll sync data in the document with the list.

Ah! Unless you're just Uploading documents, rather than creating them from a template attached to you library?

# December 24, 2008 4:30 AM

Gunnar Peipman's ASP.NET blog said:

SharePoint Create your own customized usage report solution step by step SharePoint WebPart Property

# May 15, 2009 6:35 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

  
Enter Code Here: Required

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Page view tracker