/***************************************************************************
Copyright (c) Microsoft Corporation 2011.
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
***************************************************************************/
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;
using DocumentFormat.OpenXml.Packaging;
namespace OpenXmlPowerTools
{
public enum FooterType
{
Default,
First,
Even
}
///
/// Provides access to footer operations
///
public class FooterAccessor
{
private static XNamespace ns;
private static XNamespace relationshipns;
static FooterAccessor()
{
ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
relationshipns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
}
///
/// Elements tagged as section properties
///
/// IEnumerable<XElement> containing all the section properties elements found it in the document
private static IEnumerable SectionPropertiesElements(WordprocessingDocument document)
{
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
IEnumerable results =
mainDocument
.Descendants(ns + "p")
.Elements(ns + "pPr")
.Elements(ns + "sectPr");
if (results.Count() == 0)
results = mainDocument.Root.Elements(ns + "body").Elements(ns + "sectPr");
return results;
}
///
/// Footer reference nodes inside the document
///
/// The footer part type
/// XElement containing the part reference in the document
private static XElement GetFooterReference(WordprocessingDocument document, FooterType type, int sectionIndex)
{
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
XName footerReferenceTag = ns + "footerReference";
XName typeTag = ns + "type";
string typeName = "";
switch (type)
{
case FooterType.First: typeName = "first";
break;
case FooterType.Even: typeName = "even";
break;
case FooterType.Default: typeName = "default";
break;
}
XElement sectionPropertyElement = SectionPropertiesElements(document).Skip(sectionIndex).FirstOrDefault();
if (sectionPropertyElement != null)
{
return sectionPropertyElement.Descendants().Where(tag => (tag.Name == footerReferenceTag) && (tag.Attribute(typeTag).Value == typeName)).FirstOrDefault();
}
return null;
}
///
/// Get the specified footer from the document
///
/// The footer part type
/// the XDocument containing the footer
public static XDocument GetFooter(WmlDocument doc, FooterType type, int sectionIndex)
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
{
OpenXmlPart footer = GetFooterPart(document, type, sectionIndex);
if (footer != null)
return footer.GetXDocument();
return null;
}
}
///
/// The specified footer part from the document
///
/// The footer part type
/// A OpenXmlPart containing the footer part
public static OpenXmlPart GetFooterPart(WordprocessingDocument document, FooterType type, int sectionIndex)
{
// look in the section properties of the main document part, the respective footer
// needed to extract
XElement footerReferenceElement = GetFooterReference(document, type, sectionIndex);
if (footerReferenceElement != null)
{
// get the relation id of the footer part to extract from the document
string relationshipId = footerReferenceElement.Attribute(relationshipns + "id").Value;
return document.MainDocumentPart.GetPartById(relationshipId);
}
return null;
}
///
/// Removes the specified footer in the document
///
/// The footer part type
public static void RemoveFooter(WmlDocument doc, FooterType type, int sectionIndex)
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
{
OpenXmlPart footerPart = GetFooterPart(document, type, sectionIndex);
footerPart.RemovePart();
}
}
///
/// Set a new footer in a document
///
/// XDocument containing the footer to add in the document
/// The footer part type
public static OpenXmlPowerToolsDocument SetFooter(WmlDocument doc, XDocument footer, FooterType type, int sectionIndex)
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
{
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
{
// Removes the reference in the document.xml and the footer part if those already
// exist
XElement footerReferenceElement = GetFooterReference(document, type, sectionIndex);
if (footerReferenceElement != null)
{
GetFooterPart(document, type, sectionIndex).RemovePart();
footerReferenceElement.Remove();
}
// Add the new footer
FooterPart footerPart = document.MainDocumentPart.AddNewPart();
footerPart.PutXDocument(footer);
// If the document does not have a property section a new one must be created
if (SectionPropertiesElements(document).Count() == 0)
{
AddDefaultSectionProperties(document);
}
// Creates the relationship of the footer inside the section properties in the document
string relID = document.MainDocumentPart.GetIdOfPart(footerPart);
string kindName = "";
switch ((FooterType)type)
{
case FooterType.First:
kindName = "first";
break;
case FooterType.Even:
kindName = "even";
break;
case FooterType.Default:
kindName = "default";
break;
}
XElement sectionPropertyElement = SectionPropertiesElements(document).Skip(sectionIndex).FirstOrDefault();
if (sectionPropertyElement != null)
{
sectionPropertyElement.Add(
new XElement(ns + "footerReference",
new XAttribute(ns + "type", kindName),
new XAttribute(relationshipns + "id", relID)));
if (sectionPropertyElement.Element(ns + "titlePg") == null)
sectionPropertyElement.Add(
new XElement(ns + "titlePg")
);
}
document.MainDocumentPart.PutXDocument();
// add in the settings part the EvendAndOddHeaders. this element
// allow to see the odd and even footers and headers in the document.
SettingAccessor.AddEvenAndOddHeadersElement(document);
}
return streamDoc.GetModifiedDocument();
}
}
///
/// Adds a default sectPr element into the main document
///
private static void AddDefaultSectionProperties(WordprocessingDocument document)
{
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
mainDocument.Descendants(ns + "body").First().Add(
new XElement(ns + "sectPr")
);
}
}
}