// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using DocumentFormat.OpenXml.Packaging;
namespace OpenXmlPowerTools
{
public partial class WmlDocument : OpenXmlPowerToolsDocument
{
public WmlDocument AddToc(string xPath, string switches, string title, int? rightTabPos)
{
return (WmlDocument)ReferenceAdder.AddToc(this, xPath, switches, title, rightTabPos);
}
public WmlDocument AddTof(string xPath, string switches, int? rightTabPos)
{
return (WmlDocument)ReferenceAdder.AddTof(this, xPath, switches, rightTabPos);
}
public WmlDocument AddToa(string xPath, string switches, int? rightTabPos)
{
return (WmlDocument)ReferenceAdder.AddToa(this, xPath, switches, rightTabPos);
}
}
public class ReferenceAdder
{
public static WmlDocument AddToc(WmlDocument document, string xPath, string switches, string title, int? rightTabPos)
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
{
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
{
AddToc(doc, xPath, switches, title, rightTabPos);
}
return streamDoc.GetModifiedWmlDocument();
}
}
public static void AddToc(WordprocessingDocument doc, string xPath, string switches, string title, int? rightTabPos)
{
UpdateFontTablePart(doc);
UpdateStylesPartForToc(doc);
UpdateStylesWithEffectsPartForToc(doc);
if (title == null)
title = "Contents";
if (rightTabPos == null)
rightTabPos = 9350;
// {0} tocTitle (default = "Contents")
// {1} rightTabPosition (default = 9350)
// {2} switches
String xmlString =
@"
{0}
{2}
";
XmlReader sdtReader = XmlReader.Create(new StringReader(String.Format(xmlString, title, rightTabPos, switches)));
XElement sdt = XElement.Load(sdtReader);
XmlNamespaceManager namespaceManager;
XDocument mainXDoc = doc.MainDocumentPart.GetXDocument(out namespaceManager);
namespaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
XElement addBefore = mainXDoc.XPathSelectElement(xPath, namespaceManager);
if (addBefore == null)
throw new OpenXmlPowerToolsException("XPath expression did not select an element");
addBefore.AddBeforeSelf(sdt);
doc.MainDocumentPart.PutXDocument();
XDocument settingsXDoc = doc.MainDocumentPart.DocumentSettingsPart.GetXDocument();
XElement updateFields = settingsXDoc.Descendants(W.updateFields).FirstOrDefault();
if (updateFields != null)
updateFields.Attribute(W.val).Value = "true";
else
{
updateFields = new XElement(W.updateFields,
new XAttribute(W.val, "true"));
settingsXDoc.Root.Add(updateFields);
}
doc.MainDocumentPart.DocumentSettingsPart.PutXDocument();
}
public static WmlDocument AddTof(WmlDocument document, string xPath, string switches, int? rightTabPos)
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
{
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
{
AddTof(doc, xPath, switches, rightTabPos);
}
return streamDoc.GetModifiedWmlDocument();
}
}
public static void AddTof(WordprocessingDocument doc, string xPath, string switches, int? rightTabPos)
{
UpdateFontTablePart(doc);
UpdateStylesPartForTof(doc);
UpdateStylesWithEffectsPartForTof(doc);
if (rightTabPos == null)
rightTabPos = 9350;
// {0} rightTabPosition (default = 9350)
// {1} switches
string xmlString =
@"
{1}
";
XDocument mainXDoc = doc.MainDocumentPart.GetXDocument();
XmlReader paragraphReader = XmlReader.Create(new StringReader(String.Format(xmlString, rightTabPos, switches)));
XElement paragraph = XElement.Load(paragraphReader);
XmlNameTable nameTable = paragraphReader.NameTable;
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(nameTable);
namespaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
XElement addBefore = mainXDoc.XPathSelectElement(xPath, namespaceManager);
if (addBefore == null)
throw new OpenXmlPowerToolsException("XPath expression did not select an element");
addBefore.AddBeforeSelf(paragraph);
doc.MainDocumentPart.PutXDocument();
XDocument settingsXDoc = doc.MainDocumentPart.DocumentSettingsPart.GetXDocument();
XElement updateFields = settingsXDoc.Descendants(W.updateFields).FirstOrDefault();
if (updateFields != null)
updateFields.Attribute(W.val).Value = "true";
else
{
updateFields = new XElement(W.updateFields,
new XAttribute(W.val, "true"));
settingsXDoc.Root.Add(updateFields);
}
doc.MainDocumentPart.DocumentSettingsPart.PutXDocument();
}
public static WmlDocument AddToa(WmlDocument document, string xPath, string switches, int? rightTabPos)
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document))
{
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
{
AddToa(doc, xPath, switches, rightTabPos);
}
return streamDoc.GetModifiedWmlDocument();
}
}
public static void AddToa(WordprocessingDocument doc, string xPath, string switches, int? rightTabPos)
{
UpdateFontTablePart(doc);
UpdateStylesPartForToa(doc);
UpdateStylesWithEffectsPartForToa(doc);
if (rightTabPos == null)
rightTabPos = 9350;
// {0} rightTabPosition (default = 9350)
// {1} switches
string xmlString =
@"
{1}
";
XDocument mainXDoc = doc.MainDocumentPart.GetXDocument();
XmlReader paragraphReader = XmlReader.Create(new StringReader(String.Format(xmlString, rightTabPos, switches)));
XElement paragraph = XElement.Load(paragraphReader);
XmlNameTable nameTable = paragraphReader.NameTable;
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(nameTable);
namespaceManager.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
XElement addBefore = mainXDoc.XPathSelectElement(xPath, namespaceManager);
if (addBefore == null)
throw new OpenXmlPowerToolsException("XPath expression did not select an element");
addBefore.AddBeforeSelf(paragraph);
doc.MainDocumentPart.PutXDocument();
XDocument settingsXDoc = doc.MainDocumentPart.DocumentSettingsPart.GetXDocument();
XElement updateFields = settingsXDoc.Descendants(W.updateFields).FirstOrDefault();
if (updateFields != null)
updateFields.Attribute(W.val).Value = "true";
else
{
updateFields = new XElement(W.updateFields,
new XAttribute(W.val, "true"));
settingsXDoc.Root.Add(updateFields);
}
doc.MainDocumentPart.DocumentSettingsPart.PutXDocument();
}
private static void AddElementIfMissing(XDocument partXDoc, XElement existing, string newElement)
{
if (existing != null)
return;
XElement newXElement = XElement.Parse(newElement);
newXElement.Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
partXDoc.Root.Add(newXElement);
}
private static void UpdateFontTablePart(WordprocessingDocument doc)
{
FontTablePart fontTablePart = doc.MainDocumentPart.FontTablePart;
if (fontTablePart == null)
throw new Exception("Todo need to insert font table part");
XDocument fontTableXDoc = fontTablePart.GetXDocument();
AddElementIfMissing(fontTableXDoc,
fontTableXDoc
.Root
.Elements(W.font)
.Where(e => (string)e.Attribute(W.name) == "Tahoma")
.FirstOrDefault(),
@"
");
fontTablePart.PutXDocument();
}
private static void UpdatePartForToc(OpenXmlPart part)
{
XDocument xDoc = part.GetXDocument();
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOCHeading")
.FirstOrDefault(),
@"
");
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOC1")
.FirstOrDefault(),
@"
");
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOC2")
.FirstOrDefault(),
@"
");
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOC3")
.FirstOrDefault(),
@"
");
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOC4")
.FirstOrDefault(),
@"
");
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Hyperlink")
.FirstOrDefault(),
@"
");
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "BalloonText")
.FirstOrDefault(),
@"
");
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "character" &&
(bool?)e.Attribute(W.customStyle) == true && (string)e.Attribute(W.styleId) == "BalloonTextChar")
.FirstOrDefault(),
@"
");
part.PutXDocument();
}
private static void UpdateStylesPartForToc(WordprocessingDocument doc)
{
StylesPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
if (stylesPart == null)
return;
UpdatePartForToc(stylesPart);
}
private static void UpdateStylesWithEffectsPartForToc(WordprocessingDocument doc)
{
StylesWithEffectsPart stylesWithEffectsPart = doc.MainDocumentPart.StylesWithEffectsPart;
if (stylesWithEffectsPart == null)
return;
UpdatePartForToc(stylesWithEffectsPart);
}
private static void UpdatePartForTof(OpenXmlPart part)
{
XDocument xDoc = part.GetXDocument();
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TableofFigures")
.FirstOrDefault(),
@"
");
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Hyperlink")
.FirstOrDefault(),
@"
");
part.PutXDocument();
}
private static void UpdateStylesPartForTof(WordprocessingDocument doc)
{
StylesPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
if (stylesPart == null)
return;
UpdatePartForTof(stylesPart);
}
private static void UpdateStylesWithEffectsPartForTof(WordprocessingDocument doc)
{
StylesWithEffectsPart stylesWithEffectsPart = doc.MainDocumentPart.StylesWithEffectsPart;
if (stylesWithEffectsPart == null)
return;
UpdatePartForTof(stylesWithEffectsPart);
}
private static void UpdatePartForToa(OpenXmlPart part)
{
XDocument xDoc = part.GetXDocument();
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TableofAuthorities")
.FirstOrDefault(),
@"
");
AddElementIfMissing(
xDoc,
xDoc.Root.Elements(W.style)
.Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "TOAHeading")
.FirstOrDefault(),
@"
");
part.PutXDocument();
}
private static void UpdateStylesPartForToa(WordprocessingDocument doc)
{
StylesPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
if (stylesPart == null)
return;
UpdatePartForToa(stylesPart);
}
private static void UpdateStylesWithEffectsPartForToa(WordprocessingDocument doc)
{
StylesWithEffectsPart stylesWithEffectsPart = doc.MainDocumentPart.StylesWithEffectsPart;
if (stylesWithEffectsPart == null)
return;
UpdatePartForToa(stylesWithEffectsPart);
}
}
}