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


            }


        }


    }


}


 

Comments (6)

  1. deepak says:

    thanks for above post,it is very helpful for me. i want to know

    how we insert custom meta data in header/footer. I tried lot but error comes tht files is already modified by this user.plz give some suggestion

  2. Deep says:

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

  3. 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 ?

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

  5. Andy Burns says:

    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?

Skip to main content