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

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

Rate This
  • Comments 5

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);

            }

        }

    }

}

 

Leave a Comment
  • Please add 4 and 3 and type the answer here:
  • Post
  • PingBack from http://stevepietrek.com/2008/12/18/links-12182008/

  • 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?

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

  • What if versioning is enabled in the library ? I think it will create additional version when you update the stream in ItemAdded event ...Is there a way to suppress it ?

  • Hii,

    i used this solution, it works fine. But sometime i got error "File containrs corrupted data : Window Base " . So unable to add header and footer on document. Do you know why i am getting this error? how can i check file can have corrupted data? What is the solution for this?

    i got error here : using (Package wdPackage = Package.Open(fileContent, FileMode.Open, FileAccess.ReadWrite))

Page 1 of 1 (5 items)