blob: 6af5254924de238f17c0f68fa539d5df409fb9d3 [file] [log] [blame]
/***************************************************************************
Copyright (c) Microsoft Corporation 2012-2015.
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
Published at http://OpenXmlDeveloper.org
Resource Center and Documentation: http://openxmldeveloper.org/wiki/w/wiki/powertools-for-open-xml.aspx
Developer: Eric White
Blog: http://www.ericwhite.com
Twitter: @EricWhiteDev
Email: eric@ericwhite.com
Version: 2.6.00
***************************************************************************/
#define TestForUnsupportedDocuments
#define MergeStylesWithSameNames
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using DocumentFormat.OpenXml.Packaging;
namespace OpenXmlPowerTools
{
public partial class WmlDocument : OpenXmlPowerToolsDocument
{
public IEnumerable<WmlDocument> SplitOnSections()
{
return DocumentBuilder.SplitOnSections(this);
}
}
public class Source
{
public WmlDocument WmlDocument { get; set; }
public int Start { get; set; }
public int Count { get; set; }
public bool KeepSections { get; set; }
public bool DiscardHeadersAndFootersInKeptSections { get; set; }
public string InsertId { get; set; }
public Source(string fileName)
{
WmlDocument = new WmlDocument(fileName);
Start = 0;
Count = Int32.MaxValue;
KeepSections = false;
InsertId = null;
}
public Source(WmlDocument source)
{
WmlDocument = source;
Start = 0;
Count = Int32.MaxValue;
KeepSections = false;
InsertId = null;
}
public Source(string fileName, bool keepSections)
{
WmlDocument = new WmlDocument(fileName);
Start = 0;
Count = Int32.MaxValue;
KeepSections = keepSections;
InsertId = null;
}
public Source(WmlDocument source, bool keepSections)
{
WmlDocument = source;
Start = 0;
Count = Int32.MaxValue;
KeepSections = keepSections;
InsertId = null;
}
public Source(string fileName, string insertId)
{
WmlDocument = new WmlDocument(fileName);
Start = 0;
Count = Int32.MaxValue;
KeepSections = false;
InsertId = insertId;
}
public Source(WmlDocument source, string insertId)
{
WmlDocument = source;
Start = 0;
Count = Int32.MaxValue;
KeepSections = false;
InsertId = insertId;
}
public Source(string fileName, int start, bool keepSections)
{
WmlDocument = new WmlDocument(fileName);
Start = start;
Count = Int32.MaxValue;
KeepSections = keepSections;
InsertId = null;
}
public Source(WmlDocument source, int start, bool keepSections)
{
WmlDocument = source;
Start = start;
Count = Int32.MaxValue;
KeepSections = keepSections;
InsertId = null;
}
public Source(string fileName, int start, string insertId)
{
WmlDocument = new WmlDocument(fileName);
Start = start;
Count = Int32.MaxValue;
KeepSections = false;
InsertId = insertId;
}
public Source(WmlDocument source, int start, string insertId)
{
WmlDocument = source;
Start = start;
Count = Int32.MaxValue;
KeepSections = false;
InsertId = insertId;
}
public Source(string fileName, int start, int count, bool keepSections)
{
WmlDocument = new WmlDocument(fileName);
Start = start;
Count = count;
KeepSections = keepSections;
InsertId = null;
}
public Source(WmlDocument source, int start, int count, bool keepSections)
{
WmlDocument = source;
Start = start;
Count = count;
KeepSections = keepSections;
InsertId = null;
}
public Source(string fileName, int start, int count, string insertId)
{
WmlDocument = new WmlDocument(fileName);
Start = start;
Count = count;
KeepSections = false;
InsertId = insertId;
}
public Source(WmlDocument source, int start, int count, string insertId)
{
WmlDocument = source;
Start = start;
Count = count;
KeepSections = false;
InsertId = insertId;
}
}
public static class DocumentBuilder
{
public static void BuildDocument(List<Source> sources, string fileName)
{
using (OpenXmlMemoryStreamDocument streamDoc = OpenXmlMemoryStreamDocument.CreateWordprocessingDocument())
{
using (WordprocessingDocument output = streamDoc.GetWordprocessingDocument())
{
BuildDocument(sources, output);
output.Close();
}
streamDoc.GetModifiedDocument().SaveAs(fileName);
}
}
public static WmlDocument BuildDocument(List<Source> sources)
{
using (OpenXmlMemoryStreamDocument streamDoc = OpenXmlMemoryStreamDocument.CreateWordprocessingDocument())
{
using (WordprocessingDocument output = streamDoc.GetWordprocessingDocument())
{
BuildDocument(sources, output);
output.Close();
}
return streamDoc.GetModifiedWmlDocument();
}
}
private struct TempSource
{
public int Start;
public int Count;
};
private class Atbi
{
public XElement BlockLevelContent;
public int Index;
}
private class Atbid
{
public XElement BlockLevelContent;
public int Index;
public int Div;
}
public static IEnumerable<WmlDocument> SplitOnSections(WmlDocument doc)
{
List<TempSource> tempSourceList;
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
{
XDocument mainDocument = document.MainDocumentPart.GetXDocument();
var divs = mainDocument
.Root
.Element(W.body)
.Elements()
.Select((p, i) => new Atbi
{
BlockLevelContent = p,
Index = i,
})
.Rollup(new Atbid
{
BlockLevelContent = (XElement)null,
Index = -1,
Div = 0,
},
(b, p) =>
{
XElement elementBefore = b.BlockLevelContent
.SiblingsBeforeSelfReverseDocumentOrder()
.FirstOrDefault();
if (elementBefore != null && elementBefore.Descendants(W.sectPr).Any())
return new Atbid
{
BlockLevelContent = b.BlockLevelContent,
Index = b.Index,
Div = p.Div + 1,
};
return new Atbid
{
BlockLevelContent = b.BlockLevelContent,
Index = b.Index,
Div = p.Div,
};
});
var groups = divs
.GroupAdjacent(b => b.Div);
tempSourceList = groups
.Select(g => new TempSource
{
Start = g.First().Index,
Count = g.Count(),
})
.ToList();
foreach (var ts in tempSourceList)
{
List<Source> sources = new List<Source>()
{
new Source(doc, ts.Start, ts.Count, true)
};
WmlDocument newDoc = DocumentBuilder.BuildDocument(sources);
newDoc = AdjustSectionBreak(newDoc);
yield return newDoc;
}
}
}
private static WmlDocument AdjustSectionBreak(WmlDocument doc)
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc))
{
using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument())
{
XDocument mainXDoc = document.MainDocumentPart.GetXDocument();
XElement lastElement = mainXDoc.Root
.Element(W.body)
.Elements()
.LastOrDefault();
if (lastElement != null)
{
if (lastElement.Name != W.sectPr &&
lastElement.Descendants(W.sectPr).Any())
{
mainXDoc.Root.Element(W.body).Add(lastElement.Descendants(W.sectPr).First());
lastElement.Descendants(W.sectPr).Remove();
if (!lastElement.Elements()
.Where(e => e.Name != W.pPr)
.Any())
lastElement.Remove();
document.MainDocumentPart.PutXDocument();
}
}
}
return streamDoc.GetModifiedWmlDocument();
}
}
private static void BuildDocument(List<Source> sources, WordprocessingDocument output)
{
if (RelationshipMarkup == null)
RelationshipMarkup = new Dictionary<XName, XName[]>()
{
//{ button, new [] { image }},
{ A.blip, new [] { R.embed, R.link }},
{ A.hlinkClick, new [] { R.id }},
{ A.relIds, new [] { R.cs, R.dm, R.lo, R.qs }},
//{ a14:imgLayer, new [] { R.embed }},
//{ ax:ocx, new [] { R.id }},
{ C.chart, new [] { R.id }},
{ C.externalData, new [] { R.id }},
{ C.userShapes, new [] { R.id }},
{ DGM.relIds, new [] { R.cs, R.dm, R.lo, R.qs }},
{ O.OLEObject, new [] { R.id }},
{ VML.fill, new [] { R.id }},
{ VML.imagedata, new [] { R.href, R.id, R.pict }},
{ VML.stroke, new [] { R.id }},
{ W.altChunk, new [] { R.id }},
{ W.attachedTemplate, new [] { R.id }},
{ W.control, new [] { R.id }},
{ W.dataSource, new [] { R.id }},
{ W.embedBold, new [] { R.id }},
{ W.embedBoldItalic, new [] { R.id }},
{ W.embedItalic, new [] { R.id }},
{ W.embedRegular, new [] { R.id }},
{ W.footerReference, new [] { R.id }},
{ W.headerReference, new [] { R.id }},
{ W.headerSource, new [] { R.id }},
{ W.hyperlink, new [] { R.id }},
{ W.printerSettings, new [] { R.id }},
{ W.recipientData, new [] { R.id }}, // Mail merge, not required
{ W.saveThroughXslt, new [] { R.id }},
{ W.sourceFileName, new [] { R.id }}, // Framesets, not required
{ W.src, new [] { R.id }}, // Mail merge, not required
{ W.subDoc, new [] { R.id }}, // Sub documents, not required
//{ w14:contentPart, new [] { R.id }},
{ WNE.toolbarData, new [] { R.id }},
};
// This list is used to eliminate duplicate images
List<ImageData> images = new List<ImageData>();
XDocument mainPart = output.MainDocumentPart.GetXDocument();
mainPart.Declaration.Standalone = "yes";
mainPart.Declaration.Encoding = "UTF-8";
mainPart.Root.ReplaceWith(
new XElement(W.document, NamespaceAttributes,
new XElement(W.body)));
if (sources.Count > 0)
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(sources[0].WmlDocument))
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
{
CopyStartingParts(doc, output, images);
}
int sourceNum2 = 0;
foreach (Source source in sources)
{
if (source.InsertId != null)
{
while (true)
{
#if false
modify AppendDocument so that it can take a part.
for each in main document part, header parts, footer parts
are there any PtOpenXml.Insert elements in any of them?
if so, then open and process all.
#endif
bool foundInMainDocPart = false;
XDocument mainXDoc = output.MainDocumentPart.GetXDocument();
if (mainXDoc.Descendants(PtOpenXml.Insert).Any(d => (string)d.Attribute(PtOpenXml.Id) == source.InsertId))
foundInMainDocPart = true;
if (foundInMainDocPart)
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(source.WmlDocument))
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
{
#if TestForUnsupportedDocuments
// throws exceptions if a document contains unsupported content
TestForUnsupportedDocument(doc, sources.IndexOf(source));
#endif
if (foundInMainDocPart)
{
if (source.KeepSections && source.DiscardHeadersAndFootersInKeptSections)
RemoveHeadersAndFootersFromSections(doc);
else if (source.KeepSections)
ProcessSectionsForLinkToPreviousHeadersAndFooters(doc);
List<XElement> contents = doc.MainDocumentPart.GetXDocument()
.Root
.Element(W.body)
.Elements()
.Skip(source.Start)
.Take(source.Count)
.ToList();
try
{
AppendDocument(doc, output, contents, source.KeepSections, source.InsertId, images);
}
catch (DocumentBuilderInternalException dbie)
{
if (dbie.Message.Contains("{0}"))
throw new DocumentBuilderException(string.Format(dbie.Message, sourceNum2));
else
throw dbie;
}
}
}
}
else
break;
}
}
else
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(source.WmlDocument))
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
{
#if TestForUnsupportedDocuments
// throws exceptions if a document contains unsupported content
TestForUnsupportedDocument(doc, sources.IndexOf(source));
#endif
if (source.KeepSections && source.DiscardHeadersAndFootersInKeptSections)
RemoveHeadersAndFootersFromSections(doc);
else if (source.KeepSections)
ProcessSectionsForLinkToPreviousHeadersAndFooters(doc);
List<XElement> contents = doc.MainDocumentPart.GetXDocument()
.Root
.Element(W.body)
.Elements()
.Skip(source.Start)
.Take(source.Count)
.ToList();
try
{
AppendDocument(doc, output, contents, source.KeepSections, null, images);
}
catch (DocumentBuilderInternalException dbie)
{
if (dbie.Message.Contains("{0}"))
throw new DocumentBuilderException(string.Format(dbie.Message, sourceNum2));
else
throw dbie;
}
}
}
++sourceNum2;
}
if (!sources.Any(s => s.KeepSections))
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(sources[0].WmlDocument))
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
{
var sectPr = doc.MainDocumentPart.GetXDocument().Root.Element(W.body)
.Elements().Last();
if (sectPr.Name == W.sectPr)
{
AddSectionAndDependencies(doc, output, sectPr, images);
output.MainDocumentPart.GetXDocument().Root.Element(W.body).Add(sectPr);
}
}
}
else
{
FixUpSectionProperties(output);
// Any sectPr elements that do not have headers and footers should take their headers and footers from the *next* section,
// i.e. from the running section.
var mxd = output.MainDocumentPart.GetXDocument();
var sections = mxd.Descendants(W.sectPr).Reverse().ToList();
CachedHeaderFooter[] cachedHeaderFooter = new[]
{
new CachedHeaderFooter() { Ref = W.headerReference, Type = "first" },
new CachedHeaderFooter() { Ref = W.headerReference, Type = "even" },
new CachedHeaderFooter() { Ref = W.headerReference, Type = "default" },
new CachedHeaderFooter() { Ref = W.footerReference, Type = "first" },
new CachedHeaderFooter() { Ref = W.footerReference, Type = "even" },
new CachedHeaderFooter() { Ref = W.footerReference, Type = "default" },
};
bool firstSection = true;
foreach (var sect in sections)
{
if (firstSection)
{
foreach (var hf in cachedHeaderFooter)
{
var referenceElement = sect.Elements(hf.Ref).FirstOrDefault(z => (string)z.Attribute(W.type) == hf.Type);
if (referenceElement != null)
hf.CachedPartRid = (string)referenceElement.Attribute(R.id);
}
firstSection = false;
continue;
}
else
{
CopyOrCacheHeaderOrFooter(output, cachedHeaderFooter, sect, W.headerReference, "first");
CopyOrCacheHeaderOrFooter(output, cachedHeaderFooter, sect, W.headerReference, "even");
CopyOrCacheHeaderOrFooter(output, cachedHeaderFooter, sect, W.headerReference, "default");
CopyOrCacheHeaderOrFooter(output, cachedHeaderFooter, sect, W.footerReference, "first");
CopyOrCacheHeaderOrFooter(output, cachedHeaderFooter, sect, W.footerReference, "even");
CopyOrCacheHeaderOrFooter(output, cachedHeaderFooter, sect, W.footerReference, "default");
}
}
}
// Now can process PtOpenXml:Insert elements in headers / footers
int sourceNum = 0;
foreach (Source source in sources)
{
if (source.InsertId != null)
{
while (true)
{
#if false
this uses an overload of AppendDocument that takes a part.
for each in main document part, header parts, footer parts
are there any PtOpenXml.Insert elements in any of them?
if so, then open and process all.
#endif
bool foundInHeadersFooters = false;
if (output.MainDocumentPart.HeaderParts.Any(hp =>
{
var hpXDoc = hp.GetXDocument();
return hpXDoc.Descendants(PtOpenXml.Insert).Any(d => (string)d.Attribute(PtOpenXml.Id) == source.InsertId);
}))
foundInHeadersFooters = true;
if (output.MainDocumentPart.FooterParts.Any(fp =>
{
var hpXDoc = fp.GetXDocument();
return hpXDoc.Descendants(PtOpenXml.Insert).Any(d => (string)d.Attribute(PtOpenXml.Id) == source.InsertId);
}))
foundInHeadersFooters = true;
if (foundInHeadersFooters)
{
using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(source.WmlDocument))
using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument())
{
#if TestForUnsupportedDocuments
// throws exceptions if a document contains unsupported content
TestForUnsupportedDocument(doc, sources.IndexOf(source));
#endif
var partList = output.MainDocumentPart.HeaderParts.Cast<OpenXmlPart>().Concat(output.MainDocumentPart.FooterParts.Cast<OpenXmlPart>()).ToList();
foreach (var part in partList)
{
var partXDoc = part.GetXDocument();
if (!partXDoc.Descendants(PtOpenXml.Insert).Any(d => (string)d.Attribute(PtOpenXml.Id) == source.InsertId))
continue;
List<XElement> contents = doc.MainDocumentPart.GetXDocument()
.Root
.Element(W.body)
.Elements()
.Skip(source.Start)
.Take(source.Count)
.ToList();
try
{
AppendDocument(doc, output, part, contents, source.KeepSections, source.InsertId, images);
}
catch (DocumentBuilderInternalException dbie)
{
if (dbie.Message.Contains("{0}"))
throw new DocumentBuilderException(string.Format(dbie.Message, sourceNum));
else
throw dbie;
}
}
}
}
else
break;
}
}
++sourceNum;
}
AdjustDocPrIds(output);
}
foreach (var part in output.GetAllParts())
if (part.Annotation<XDocument>() != null)
part.PutXDocument();
}
private static void RemoveHeadersAndFootersFromSections(WordprocessingDocument doc)
{
var mdXDoc = doc.MainDocumentPart.GetXDocument();
var sections = mdXDoc.Descendants(W.sectPr).ToList();
foreach (var sect in sections)
{
sect.Elements(W.headerReference).Remove();
sect.Elements(W.footerReference).Remove();
}
doc.MainDocumentPart.PutXDocument();
}
private class CachedHeaderFooter
{
public XName Ref;
public string Type;
public string CachedPartRid;
};
private static void ProcessSectionsForLinkToPreviousHeadersAndFooters(WordprocessingDocument doc)
{
CachedHeaderFooter[] cachedHeaderFooter = new[]
{
new CachedHeaderFooter() { Ref = W.headerReference, Type = "first" },
new CachedHeaderFooter() { Ref = W.headerReference, Type = "even" },
new CachedHeaderFooter() { Ref = W.headerReference, Type = "default" },
new CachedHeaderFooter() { Ref = W.footerReference, Type = "first" },
new CachedHeaderFooter() { Ref = W.footerReference, Type = "even" },
new CachedHeaderFooter() { Ref = W.footerReference, Type = "default" },
};
var mdXDoc = doc.MainDocumentPart.GetXDocument();
var sections = mdXDoc.Descendants(W.sectPr).ToList();
var firstSection = true;
foreach (var sect in sections)
{
if (firstSection)
{
var headerFirst = FindReference(sect, W.headerReference, "first");
var headerDefault = FindReference(sect, W.headerReference, "default");
var headerEven = FindReference(sect, W.headerReference, "even");
var footerFirst = FindReference(sect, W.footerReference, "first");
var footerDefault = FindReference(sect, W.footerReference, "default");
var footerEven = FindReference(sect, W.footerReference, "even");
if (headerEven == null)
{
if (headerDefault != null)
AddReferenceToExistingHeaderOrFooter(doc.MainDocumentPart, sect, (string)headerDefault.Attribute(R.id), W.headerReference, "even");
else
InitEmptyHeaderOrFooter(doc.MainDocumentPart, sect, W.headerReference, "even");
}
if (headerFirst == null)
{
if (headerDefault != null)
AddReferenceToExistingHeaderOrFooter(doc.MainDocumentPart, sect, (string)headerDefault.Attribute(R.id), W.headerReference, "first");
else
InitEmptyHeaderOrFooter(doc.MainDocumentPart, sect, W.headerReference, "first");
}
if (footerEven == null)
{
if (footerDefault != null)
AddReferenceToExistingHeaderOrFooter(doc.MainDocumentPart, sect, (string)footerDefault.Attribute(R.id), W.footerReference, "even");
else
InitEmptyHeaderOrFooter(doc.MainDocumentPart, sect, W.footerReference, "even");
}
if (footerFirst == null)
{
if (footerDefault != null)
AddReferenceToExistingHeaderOrFooter(doc.MainDocumentPart, sect, (string)footerDefault.Attribute(R.id), W.footerReference, "first");
else
InitEmptyHeaderOrFooter(doc.MainDocumentPart, sect, W.footerReference, "first");
}
foreach (var hf in cachedHeaderFooter)
{
if (sect.Elements(hf.Ref).FirstOrDefault(z => (string)z.Attribute(W.type) == hf.Type) == null)
InitEmptyHeaderOrFooter(doc.MainDocumentPart, sect, hf.Ref, hf.Type);
var reference = sect.Elements(hf.Ref).FirstOrDefault(z => (string)z.Attribute(W.type) == hf.Type);
if (reference == null)
throw new OpenXmlPowerToolsException("Internal error");
hf.CachedPartRid = (string)reference.Attribute(R.id);
}
firstSection = false;
continue;
}
else
{
CopyOrCacheHeaderOrFooter(doc, cachedHeaderFooter, sect, W.headerReference, "first");
CopyOrCacheHeaderOrFooter(doc, cachedHeaderFooter, sect, W.headerReference, "even");
CopyOrCacheHeaderOrFooter(doc, cachedHeaderFooter, sect, W.headerReference, "default");
CopyOrCacheHeaderOrFooter(doc, cachedHeaderFooter, sect, W.footerReference, "first");
CopyOrCacheHeaderOrFooter(doc, cachedHeaderFooter, sect, W.footerReference, "even");
CopyOrCacheHeaderOrFooter(doc, cachedHeaderFooter, sect, W.footerReference, "default");
}
}
doc.MainDocumentPart.PutXDocument();
}
private static void CopyOrCacheHeaderOrFooter(WordprocessingDocument doc, CachedHeaderFooter[] cachedHeaderFooter, XElement sect, XName referenceXName, string type)
{
var referenceElement = FindReference(sect, referenceXName, type);
if (referenceElement == null)
{
var cachedPartRid = cachedHeaderFooter.FirstOrDefault(z => z.Ref == referenceXName && z.Type == type).CachedPartRid;
AddReferenceToExistingHeaderOrFooter(doc.MainDocumentPart, sect, cachedPartRid, referenceXName, type);
}
else
{
var cachedPart = cachedHeaderFooter.FirstOrDefault(z => z.Ref == referenceXName && z.Type == type);
cachedPart.CachedPartRid = (string)referenceElement.Attribute(R.id);
}
}
private static XElement FindReference(XElement sect, XName reference, string type)
{
return sect.Elements(reference).FirstOrDefault(z =>
{
return (string)z.Attribute(W.type) == type;
});
}
private static void AddReferenceToExistingHeaderOrFooter(MainDocumentPart mainDocPart, XElement sect, string rId, XName reference, string toType)
{
if (reference == W.headerReference)
{
var referenceToAdd = new XElement(W.headerReference,
new XAttribute(W.type, toType),
new XAttribute(R.id, rId));
sect.AddFirst(referenceToAdd);
}
else
{
var referenceToAdd = new XElement(W.footerReference,
new XAttribute(W.type, toType),
new XAttribute(R.id, rId));
sect.AddFirst(referenceToAdd);
}
}
private static void InitEmptyHeaderOrFooter(MainDocumentPart mainDocPart, XElement sect, XName referenceXName, string toType)
{
XDocument xDoc = null;
if (referenceXName == W.headerReference)
{
xDoc = XDocument.Parse(
@"<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<w:hdr xmlns:wpc='http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas'
xmlns:mc='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:wp14='http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing'
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:w14='http://schemas.microsoft.com/office/word/2010/wordml'
xmlns:w15='http://schemas.microsoft.com/office/word/2012/wordml'
xmlns:wpg='http://schemas.microsoft.com/office/word/2010/wordprocessingGroup'
xmlns:wpi='http://schemas.microsoft.com/office/word/2010/wordprocessingInk'
xmlns:wne='http://schemas.microsoft.com/office/word/2006/wordml'
xmlns:wps='http://schemas.microsoft.com/office/word/2010/wordprocessingShape'
mc:Ignorable='w14 w15 wp14'>
<w:p>
<w:pPr>
<w:pStyle w:val='Header' />
</w:pPr>
<w:r>
<w:t></w:t>
</w:r>
</w:p>
</w:hdr>");
var newHeaderPart = mainDocPart.AddNewPart<HeaderPart>();
newHeaderPart.PutXDocument(xDoc);
var referenceToAdd = new XElement(W.headerReference,
new XAttribute(W.type, toType),
new XAttribute(R.id, mainDocPart.GetIdOfPart(newHeaderPart)));
sect.AddFirst(referenceToAdd);
}
else
{
xDoc = XDocument.Parse(@"<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<w:ftr xmlns:wpc='http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas'
xmlns:mc='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:wp14='http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing'
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:w14='http://schemas.microsoft.com/office/word/2010/wordml'
xmlns:w15='http://schemas.microsoft.com/office/word/2012/wordml'
xmlns:wpg='http://schemas.microsoft.com/office/word/2010/wordprocessingGroup'
xmlns:wpi='http://schemas.microsoft.com/office/word/2010/wordprocessingInk'
xmlns:wne='http://schemas.microsoft.com/office/word/2006/wordml'
xmlns:wps='http://schemas.microsoft.com/office/word/2010/wordprocessingShape'
mc:Ignorable='w14 w15 wp14'>
<w:p>
<w:pPr>
<w:pStyle w:val='Footer' />
</w:pPr>
<w:r>
<w:t></w:t>
</w:r>
</w:p>
</w:ftr>");
var newFooterPart = mainDocPart.AddNewPart<FooterPart>();
newFooterPart.PutXDocument(xDoc);
var referenceToAdd = new XElement(W.footerReference,
new XAttribute(W.type, toType),
new XAttribute(R.id, mainDocPart.GetIdOfPart(newFooterPart)));
sect.AddFirst(referenceToAdd);
}
}
private static void TestPartForUnsupportedContent(OpenXmlPart part, int sourceNumber)
{
XNamespace[] obsoleteNamespaces = new[]
{
XNamespace.Get("http://schemas.microsoft.com/office/word/2007/5/30/wordml"),
XNamespace.Get("http://schemas.microsoft.com/office/word/2008/9/16/wordprocessingDrawing"),
XNamespace.Get("http://schemas.microsoft.com/office/word/2009/2/wordml"),
};
XDocument xDoc = part.GetXDocument();
XElement invalidElement = xDoc.Descendants()
.FirstOrDefault(d =>
{
bool b = d.Name == W.subDoc ||
d.Name == W.control ||
d.Name == W.altChunk ||
d.Name.LocalName == "contentPart" ||
obsoleteNamespaces.Contains(d.Name.Namespace);
bool b2 = b ||
d.Attributes().Any(a => obsoleteNamespaces.Contains(a.Name.Namespace));
return b2;
});
if (invalidElement != null)
{
if (invalidElement.Name == W.subDoc)
throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains sub document",
sourceNumber));
if (invalidElement.Name == W.control)
throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains ActiveX controls",
sourceNumber));
if (invalidElement.Name == W.altChunk)
throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains altChunk",
sourceNumber));
if (invalidElement.Name.LocalName == "contentPart")
throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains contentPart content",
sourceNumber));
if (obsoleteNamespaces.Contains(invalidElement.Name.Namespace) ||
invalidElement.Attributes().Any(a => obsoleteNamespaces.Contains(a.Name.Namespace)))
throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains obsolete namespace",
sourceNumber));
}
}
//What does not work:
//- sub docs
//- bidi text appears to work but has not been tested
//- languages other than en-us appear to work but have not been tested
//- documents with activex controls
//- mail merge source documents (look for dataSource in settings)
//- documents with ink
//- documents with frame sets and frames
private static void TestForUnsupportedDocument(WordprocessingDocument doc, int sourceNumber)
{
if ((string)doc.MainDocumentPart.GetXDocument().Root.Name.NamespaceName == "http://purl.oclc.org/ooxml/wordprocessingml/main")
throw new DocumentBuilderException(string.Format("Source {0} is saved in strict mode, not supported", sourceNumber));
// note: if ever want to support section changes, need to address the code that rationalizes headers and footers, propagating to sections that inherit headers/footers from prev section
foreach (var d in doc.MainDocumentPart.GetXDocument().Descendants())
{
if (d.Name == W.sectPrChange)
throw new DocumentBuilderException(string.Format("Source {0} contains section changes (w:sectPrChange), not supported", sourceNumber));
// note: if ever want to support Open-Xml-PowerTools attributes, need to make sure that all attributes are propagated in all cases
//if (d.Name.Namespace == PtOpenXml.ptOpenXml ||
// d.Name.Namespace == PtOpenXml.pt)
// throw new DocumentBuilderException(string.Format("Source {0} contains Open-Xml-PowerTools markup, not supported", sourceNumber));
//if (d.Attributes().Any(a => a.Name.Namespace == PtOpenXml.ptOpenXml || a.Name.Namespace == PtOpenXml.pt))
// throw new DocumentBuilderException(string.Format("Source {0} contains Open-Xml-PowerTools markup, not supported", sourceNumber));
}
TestPartForUnsupportedContent(doc.MainDocumentPart, sourceNumber);
foreach (var hdr in doc.MainDocumentPart.HeaderParts)
TestPartForUnsupportedContent(hdr, sourceNumber);
foreach (var ftr in doc.MainDocumentPart.FooterParts)
TestPartForUnsupportedContent(ftr, sourceNumber);
if (doc.MainDocumentPart.FootnotesPart != null)
TestPartForUnsupportedContent(doc.MainDocumentPart.FootnotesPart, sourceNumber);
if (doc.MainDocumentPart.EndnotesPart != null)
TestPartForUnsupportedContent(doc.MainDocumentPart.EndnotesPart, sourceNumber);
if (doc.MainDocumentPart.DocumentSettingsPart != null &&
doc.MainDocumentPart.DocumentSettingsPart.GetXDocument().Descendants().Any(d => d.Name == W.src ||
d.Name == W.recipientData || d.Name == W.mailMerge))
throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains Mail Merge content",
sourceNumber));
if (doc.MainDocumentPart.WebSettingsPart != null &&
doc.MainDocumentPart.WebSettingsPart.GetXDocument().Descendants().Any(d => d.Name == W.frameset))
throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains a frameset", sourceNumber));
var numberingElements = doc.MainDocumentPart
.GetXDocument()
.Descendants(W.numPr)
.Where(n =>
{
bool zeroId = (int?)n.Attribute(W.id) == 0;
bool hasChildInsId = n.Elements(W.ins).Any();
if (zeroId || hasChildInsId)
return false;
return true;
})
.ToList();
if (numberingElements.Any() &&
doc.MainDocumentPart.NumberingDefinitionsPart == null)
throw new DocumentBuilderException(String.Format(
"Source {0} is invalid document - contains numbering markup but no numbering part", sourceNumber));
}
private static void FixUpSectionProperties(WordprocessingDocument newDocument)
{
XDocument mainDocumentXDoc = newDocument.MainDocumentPart.GetXDocument();
mainDocumentXDoc.Declaration.Standalone = "yes";
mainDocumentXDoc.Declaration.Encoding = "UTF-8";
XElement body = mainDocumentXDoc.Root.Element(W.body);
var sectionPropertiesToMove = body
.Elements()
.Take(body.Elements().Count() - 1)
.Where(e => e.Name == W.sectPr)
.ToList();
foreach (var s in sectionPropertiesToMove)
{
var p = s.SiblingsBeforeSelfReverseDocumentOrder().First();
if (p.Element(W.pPr) == null)
p.AddFirst(new XElement(W.pPr));
p.Element(W.pPr).Add(s);
}
foreach (var s in sectionPropertiesToMove)
s.Remove();
}
private static void AddSectionAndDependencies(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument,
XElement sectionMarkup, List<ImageData> images)
{
var headerReferences = sectionMarkup.Elements(W.headerReference);
foreach (var headerReference in headerReferences)
{
string oldRid = headerReference.Attribute(R.id).Value;
HeaderPart oldHeaderPart = null;
try
{
oldHeaderPart = (HeaderPart)sourceDocument.MainDocumentPart.GetPartById(oldRid);
}
catch (ArgumentOutOfRangeException)
{
var message = string.Format("ArgumentOutOfRangeException, attempting to get header rId={0}", oldRid);
throw new OpenXmlPowerToolsException(message);
}
XDocument oldHeaderXDoc = oldHeaderPart.GetXDocument();
if (oldHeaderXDoc != null && oldHeaderXDoc.Root != null)
CopyNumbering(sourceDocument, newDocument, new[] { oldHeaderXDoc.Root }, images);
HeaderPart newHeaderPart = newDocument.MainDocumentPart.AddNewPart<HeaderPart>();
XDocument newHeaderXDoc = newHeaderPart.GetXDocument();
newHeaderXDoc.Declaration.Standalone = "yes";
newHeaderXDoc.Declaration.Encoding = "UTF-8";
newHeaderXDoc.Add(oldHeaderXDoc.Root);
headerReference.Attribute(R.id).Value = newDocument.MainDocumentPart.GetIdOfPart(newHeaderPart);
AddRelationships(oldHeaderPart, newHeaderPart, new[] { newHeaderXDoc.Root });
CopyRelatedPartsForContentParts(oldHeaderPart, newHeaderPart, new[] { newHeaderXDoc.Root }, images);
}
var footerReferences = sectionMarkup.Elements(W.footerReference);
foreach (var footerReference in footerReferences)
{
string oldRid = footerReference.Attribute(R.id).Value;
FooterPart oldFooterPart = (FooterPart)sourceDocument.MainDocumentPart.GetPartById(oldRid);
XDocument oldFooterXDoc = oldFooterPart.GetXDocument();
if (oldFooterXDoc != null && oldFooterXDoc.Root != null)
CopyNumbering(sourceDocument, newDocument, new[] { oldFooterXDoc.Root }, images);
FooterPart newFooterPart = newDocument.MainDocumentPart.AddNewPart<FooterPart>();
XDocument newFooterXDoc = newFooterPart.GetXDocument();
newFooterXDoc.Declaration.Standalone = "yes";
newFooterXDoc.Declaration.Encoding = "UTF-8";
newFooterXDoc.Add(oldFooterXDoc.Root);
footerReference.Attribute(R.id).Value = newDocument.MainDocumentPart.GetIdOfPart(newFooterPart);
AddRelationships(oldFooterPart, newFooterPart, new[] { newFooterXDoc.Root });
CopyRelatedPartsForContentParts(oldFooterPart, newFooterPart, new[] { newFooterXDoc.Root }, images);
}
}
private static void MergeStyles(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, XDocument fromStyles, XDocument toStyles, IEnumerable<XElement> newContent)
{
#if MergeStylesWithSameNames
var newIds = new Dictionary<string, string>();
#endif
foreach (XElement style in fromStyles.Root.Elements(W.style))
{
var fromId = (string)style.Attribute(W.styleId);
var fromName = (string)style.Elements(W.name).Attributes(W.val).FirstOrDefault();
var toStyle = toStyles
.Root
.Elements(W.style)
.FirstOrDefault(st => (string)st.Elements(W.name).Attributes(W.val).FirstOrDefault() == fromName);
if (toStyle == null)
{
#if MergeStylesWithSameNames
var linkElement = style.Element(W.link);
string linkedId;
if (linkElement != null && newIds.TryGetValue(linkElement.Attribute(W.val).Value, out linkedId))
{
var linkedStyle = toStyles.Root.Elements(W.style)
.First(o => o.Attribute(W.styleId).Value == linkedId);
if (linkedStyle.Element(W.link) != null)
newIds.Add(fromId, linkedStyle.Element(W.link).Attribute(W.val).Value);
continue;
}
//string name = (string)style.Elements(W.name).Attributes(W.val).FirstOrDefault();
//var namedStyle = toStyles
// .Root
// .Elements(W.style)
// .Where(st => st.Element(W.name) != null)
// .FirstOrDefault(o => (string)o.Element(W.name).Attribute(W.val) == name);
//if (namedStyle != null)
//{
// if (! newIds.ContainsKey(fromId))
// newIds.Add(fromId, namedStyle.Attribute(W.styleId).Value);
// continue;
//}
#endif
int number = 1;
int abstractNumber = 0;
XDocument oldNumbering = null;
XDocument newNumbering = null;
foreach (XElement numReference in style.Descendants(W.numPr))
{
XElement idElement = numReference.Descendants(W.numId).FirstOrDefault();
if (idElement != null)
{
if (oldNumbering == null)
{
if (sourceDocument.MainDocumentPart.NumberingDefinitionsPart != null)
oldNumbering = sourceDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument();
else
{
oldNumbering = new XDocument();
oldNumbering.Declaration = new XDeclaration("1.0", "UTF-8", "yes");
oldNumbering.Add(new XElement(W.numbering, NamespaceAttributes));
}
}
if (newNumbering == null)
{
if (newDocument.MainDocumentPart.NumberingDefinitionsPart != null)
{
newNumbering = newDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument();
newNumbering.Declaration.Standalone = "yes";
newNumbering.Declaration.Encoding = "UTF-8";
var numIds = newNumbering
.Root
.Elements(W.num)
.Select(f => (int)f.Attribute(W.numId));
if (numIds.Any())
number = numIds.Max() + 1;
numIds = newNumbering
.Root
.Elements(W.abstractNum)
.Select(f => (int)f.Attribute(W.abstractNumId));
if (numIds.Any())
abstractNumber = numIds.Max() + 1;
}
else
{
newDocument.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>();
newNumbering = newDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument();
newNumbering.Declaration.Standalone = "yes";
newNumbering.Declaration.Encoding = "UTF-8";
newNumbering.Add(new XElement(W.numbering, NamespaceAttributes));
}
}
string numId = idElement.Attribute(W.val).Value;
if (numId != "0")
{
XElement element = oldNumbering
.Descendants()
.Elements(W.num)
.Where(p => ((string)p.Attribute(W.numId)) == numId)
.FirstOrDefault();
// Copy abstract numbering element, if necessary (use matching NSID)
string abstractNumId = string.Empty;
if (element != null)
{
abstractNumId = element
.Elements(W.abstractNumId)
.First()
.Attribute(W.val)
.Value;
XElement abstractElement = oldNumbering
.Descendants()
.Elements(W.abstractNum)
.Where(p => ((string)p.Attribute(W.abstractNumId)) == abstractNumId)
.FirstOrDefault();
string abstractNSID = string.Empty;
if (abstractElement != null)
{
XElement nsidElement = abstractElement
.Element(W.nsid);
abstractNSID = null;
if (nsidElement != null)
abstractNSID = (string)nsidElement
.Attribute(W.val);
XElement newAbstractElement = newNumbering
.Descendants()
.Elements(W.abstractNum)
.Where(e => e.Annotation<FromPreviousSourceSemaphore>() == null)
.Where(p =>
{
var thisNsidElement = p.Element(W.nsid);
if (thisNsidElement == null)
return false;
return (string)thisNsidElement.Attribute(W.val) == abstractNSID;
})
.FirstOrDefault();
if (newAbstractElement == null)
{
newAbstractElement = new XElement(abstractElement);
newAbstractElement.Attribute(W.abstractNumId).Value = abstractNumber.ToString();
abstractNumber++;
if (newNumbering.Root.Elements(W.abstractNum).Any())
newNumbering.Root.Elements(W.abstractNum).Last().AddAfterSelf(newAbstractElement);
else
newNumbering.Root.Add(newAbstractElement);
foreach (XElement pictId in newAbstractElement.Descendants(W.lvlPicBulletId))
{
string bulletId = (string)pictId.Attribute(W.val);
XElement numPicBullet = oldNumbering
.Descendants(W.numPicBullet)
.FirstOrDefault(d => (string)d.Attribute(W.numPicBulletId) == bulletId);
int maxNumPicBulletId = new int[] { -1 }.Concat(
newNumbering.Descendants(W.numPicBullet)
.Attributes(W.numPicBulletId)
.Select(a => (int)a))
.Max() + 1;
XElement newNumPicBullet = new XElement(numPicBullet);
newNumPicBullet.Attribute(W.numPicBulletId).Value = maxNumPicBulletId.ToString();
pictId.Attribute(W.val).Value = maxNumPicBulletId.ToString();
newNumbering.Root.AddFirst(newNumPicBullet);
}
}
string newAbstractId = newAbstractElement.Attribute(W.abstractNumId).Value;
// Copy numbering element, if necessary (use matching element with no overrides)
XElement newElement = null;
if (!element.Elements(W.lvlOverride).Any())
newElement = newNumbering
.Descendants()
.Elements(W.num)
.Where(p => !p.Elements(W.lvlOverride).Any() &&
((string)p.Elements(W.abstractNumId).First().Attribute(W.val)) == newAbstractId)
.FirstOrDefault();
if (newElement == null)
{
newElement = new XElement(element);
newElement
.Elements(W.abstractNumId)
.First()
.Attribute(W.val).Value = newAbstractId;
newElement.Attribute(W.numId).Value = number.ToString();
number++;
newNumbering.Root.Add(newElement);
}
idElement.Attribute(W.val).Value = newElement.Attribute(W.numId).Value;
}
}
}
}
}
var newStyle = new XElement(style);
// get rid of anything not in the w: namespace
newStyle.Descendants().Where(d => d.Name.NamespaceName != W.w).Remove();
newStyle.Descendants().Attributes().Where(d => d.Name.NamespaceName != W.w).Remove();
toStyles.Root.Add(newStyle);
}
else
{
var toId = (string)toStyle.Attribute(W.styleId);
if (fromId != toId)
{
if (! newIds.ContainsKey(fromId))
newIds.Add(fromId, toId);
}
}
}
#if MergeStylesWithSameNames
if (newIds.Count > 0)
{
foreach (var style in toStyles
.Root
.Elements(W.style))
{
ConvertToNewId(style.Element(W.basedOn), newIds);
ConvertToNewId(style.Element(W.next), newIds);
}
foreach (var item in newContent.DescendantsAndSelf()
.Where(d => d.Name == W.pStyle ||
d.Name == W.rStyle ||
d.Name == W.tblStyle))
{
ConvertToNewId(item, newIds);
}
if (newDocument.MainDocumentPart.NumberingDefinitionsPart != null)
{
var newNumbering = newDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument();
ConvertNumberingPartToNewIds(newNumbering, newIds);
}
// Convert source document, since numberings will be copied over after styles.
if (sourceDocument.MainDocumentPart.NumberingDefinitionsPart != null)
{
var sourceNumbering = sourceDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument();
ConvertNumberingPartToNewIds(sourceNumbering, newIds);
}
}
#endif
}
#if MergeStylesWithSameNames
private static void ConvertToNewId(XElement element, Dictionary<string, string> newIds)
{
if (element == null)
return;
var valueAttribute = element.Attribute(W.val);
string newId;
if (newIds.TryGetValue(valueAttribute.Value, out newId))
{
valueAttribute.Value = newId;
}
}
private static void ConvertNumberingPartToNewIds(XDocument newNumbering, Dictionary<string, string> newIds)
{
foreach (var abstractNum in newNumbering
.Root
.Elements(W.abstractNum))
{
ConvertToNewId(abstractNum.Element(W.styleLink), newIds);
ConvertToNewId(abstractNum.Element(W.numStyleLink), newIds);
}
foreach (var item in newNumbering
.Descendants()
.Where(d => d.Name == W.pStyle ||
d.Name == W.rStyle ||
d.Name == W.tblStyle))
{
ConvertToNewId(item, newIds);
}
}
#endif
private static void MergeFontTables(XDocument fromFontTable, XDocument toFontTable)
{
foreach (XElement font in fromFontTable.Root.Elements(W.font))
{
string name = font.Attribute(W.name).Value;
if (toFontTable
.Root
.Elements(W.font)
.Where(o => o.Attribute(W.name).Value == name)
.Count() == 0)
toFontTable.Root.Add(new XElement(font));
}
}
private static void CopyStylesAndFonts(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument,
IEnumerable<XElement> newContent)
{
// Copy all styles to the new document
if (sourceDocument.MainDocumentPart.StyleDefinitionsPart != null)
{
XDocument oldStyles = sourceDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
if (newDocument.MainDocumentPart.StyleDefinitionsPart == null)
{
newDocument.MainDocumentPart.AddNewPart<StyleDefinitionsPart>();
XDocument newStyles = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
newStyles.Declaration.Standalone = "yes";
newStyles.Declaration.Encoding = "UTF-8";
newStyles.Add(oldStyles.Root);
}
else
{
XDocument newStyles = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
MergeStyles(sourceDocument, newDocument, oldStyles, newStyles, newContent);
}
}
// Copy all styles with effects to the new document
if (sourceDocument.MainDocumentPart.StylesWithEffectsPart != null)
{
XDocument oldStyles = sourceDocument.MainDocumentPart.StylesWithEffectsPart.GetXDocument();
if (newDocument.MainDocumentPart.StylesWithEffectsPart == null)
{
newDocument.MainDocumentPart.AddNewPart<StylesWithEffectsPart>();
XDocument newStyles = newDocument.MainDocumentPart.StylesWithEffectsPart.GetXDocument();
newStyles.Declaration.Standalone = "yes";
newStyles.Declaration.Encoding = "UTF-8";
newStyles.Add(oldStyles.Root);
}
else
{
XDocument newStyles = newDocument.MainDocumentPart.StylesWithEffectsPart.GetXDocument();
MergeStyles(sourceDocument, newDocument, oldStyles, newStyles, newContent);
}
}
// Copy fontTable to the new document
if (sourceDocument.MainDocumentPart.FontTablePart != null)
{
XDocument oldFontTable = sourceDocument.MainDocumentPart.FontTablePart.GetXDocument();
if (newDocument.MainDocumentPart.FontTablePart == null)
{
newDocument.MainDocumentPart.AddNewPart<FontTablePart>();
XDocument newFontTable = newDocument.MainDocumentPart.FontTablePart.GetXDocument();
newFontTable.Declaration.Standalone = "yes";
newFontTable.Declaration.Encoding = "UTF-8";
newFontTable.Add(oldFontTable.Root);
}
else
{
XDocument newFontTable = newDocument.MainDocumentPart.FontTablePart.GetXDocument();
MergeFontTables(oldFontTable, newFontTable);
}
}
}
private static void CopyComments(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument,
IEnumerable<XElement> newContent, List<ImageData> images)
{
Dictionary<int, int> commentIdMap = new Dictionary<int, int>();
int number = 0;
XDocument oldComments = null;
XDocument newComments = null;
foreach (XElement comment in newContent.DescendantsAndSelf(W.commentReference))
{
if (oldComments == null)
oldComments = sourceDocument.MainDocumentPart.WordprocessingCommentsPart.GetXDocument();
if (newComments == null)
{
if (newDocument.MainDocumentPart.WordprocessingCommentsPart != null)
{
newComments = newDocument.MainDocumentPart.WordprocessingCommentsPart.GetXDocument();
newComments.Declaration.Standalone = "yes";
newComments.Declaration.Encoding = "UTF-8";
var ids = newComments.Root.Elements(W.comment).Select(f => (int)f.Attribute(W.id));
if (ids.Any())
number = ids.Max() + 1;
}
else
{
newDocument.MainDocumentPart.AddNewPart<WordprocessingCommentsPart>();
newComments = newDocument.MainDocumentPart.WordprocessingCommentsPart.GetXDocument();
newComments.Declaration.Standalone = "yes";
newComments.Declaration.Encoding = "UTF-8";
newComments.Add(new XElement(W.comments, NamespaceAttributes));
}
}
int id = (int)comment.Attribute(W.id);
XElement element = oldComments
.Descendants()
.Elements(W.comment)
.Where(p => ((int)p.Attribute(W.id)) == id)
.First();
XElement newElement = new XElement(element);
newElement.Attribute(W.id).Value = number.ToString();
newComments.Root.Add(newElement);
if (! commentIdMap.ContainsKey(id))
commentIdMap.Add(id, number);
number++;
}
foreach (var item in newContent.DescendantsAndSelf()
.Where(d => d.Name == W.commentReference ||
d.Name == W.commentRangeStart ||
d.Name == W.commentRangeEnd)
.ToList())
{
if (commentIdMap.ContainsKey((int)item.Attribute(W.id)))
item.Attribute(W.id).Value = commentIdMap[(int)item.Attribute(W.id)].ToString();
}
if (sourceDocument.MainDocumentPart.WordprocessingCommentsPart != null &&
newDocument.MainDocumentPart.WordprocessingCommentsPart != null)
{
AddRelationships(sourceDocument.MainDocumentPart.WordprocessingCommentsPart,
newDocument.MainDocumentPart.WordprocessingCommentsPart,
new[] { newDocument.MainDocumentPart.WordprocessingCommentsPart.GetXDocument().Root });
CopyRelatedPartsForContentParts(sourceDocument.MainDocumentPart.WordprocessingCommentsPart,
newDocument.MainDocumentPart.WordprocessingCommentsPart,
new[] { newDocument.MainDocumentPart.WordprocessingCommentsPart.GetXDocument().Root },
images);
}
}
private static void AdjustUniqueIds(WordprocessingDocument sourceDocument,
WordprocessingDocument newDocument, IEnumerable<XElement> newContent)
{
// adjust bookmark unique ids
int maxId = 0;
if (newDocument.MainDocumentPart.GetXDocument().Descendants(W.bookmarkStart).Any())
maxId = newDocument.MainDocumentPart.GetXDocument().Descendants(W.bookmarkStart)
.Select(d => (int)d.Attribute(W.id)).Max();
Dictionary<int, int> bookmarkIdMap = new Dictionary<int, int>();
foreach (var item in newContent.DescendantsAndSelf().Where(bm => bm.Name == W.bookmarkStart ||
bm.Name == W.bookmarkEnd))
{
int id = (int)item.Attribute(W.id);
if (!bookmarkIdMap.ContainsKey(id))
bookmarkIdMap.Add(id, ++maxId);
}
foreach (var bookmarkElement in newContent.DescendantsAndSelf().Where(e => e.Name == W.bookmarkStart ||
e.Name == W.bookmarkEnd))
bookmarkElement.Attribute(W.id).Value = bookmarkIdMap[(int)bookmarkElement.Attribute(W.id)].ToString();
// adjust shape unique ids
// This doesn't work because OLEObjects refer to shapes by ID.
// Punting on this, because sooner or later, this will be a non-issue.
//foreach (var item in newContent.DescendantsAndSelf(VML.shape))
//{
// Guid g = Guid.NewGuid();
// string s = "R" + g.ToString().Replace("-", "");
// item.Attribute(NoNamespace.id).Value = s;
//}
}
private static void AdjustDocPrIds(WordprocessingDocument newDocument)
{
int docPrId = 0;
foreach (var item in newDocument.MainDocumentPart.GetXDocument().Descendants(WP.docPr))
item.Attribute(NoNamespace.id).Value = (++docPrId).ToString();
foreach (var header in newDocument.MainDocumentPart.HeaderParts)
foreach (var item in header.GetXDocument().Descendants(WP.docPr))
item.Attribute(NoNamespace.id).Value = (++docPrId).ToString();
foreach (var footer in newDocument.MainDocumentPart.FooterParts)
foreach (var item in footer.GetXDocument().Descendants(WP.docPr))
item.Attribute(NoNamespace.id).Value = (++docPrId).ToString();
if (newDocument.MainDocumentPart.FootnotesPart != null)
foreach (var item in newDocument.MainDocumentPart.FootnotesPart.GetXDocument().Descendants(WP.docPr))
item.Attribute(NoNamespace.id).Value = (++docPrId).ToString();
if (newDocument.MainDocumentPart.EndnotesPart != null)
foreach (var item in newDocument.MainDocumentPart.EndnotesPart.GetXDocument().Descendants(WP.docPr))
item.Attribute(NoNamespace.id).Value = (++docPrId).ToString();
}
// This probably doesn't need to be done, except that the Open XML SDK will not validate
// documents that contain the o:gfxdata attribute.
private static void RemoveGfxdata(IEnumerable<XElement> newContent)
{
newContent.DescendantsAndSelf().Attributes(O.gfxdata).Remove();
}
private static object InsertTransform(XNode node, List<XElement> newContent)
{
XElement element = node as XElement;
if (element != null)
{
if (element.Annotation<ReplaceSemaphore>() != null)
return newContent;
return new XElement(element.Name,
element.Attributes(),
element.Nodes().Select(n => InsertTransform(n, newContent)));
}
return node;
}
private class ReplaceSemaphore { }
// Rules for sections
// - if KeepSections for all documents in the source collection are false, then it takes the section
// from the first document.
// - if you specify true for any document, and if the last section is part of the specified content,
// then that section is copied. If any paragraph in the content has a section, then that section
// is copied.
// - if you specify true for any document, and there are no sections for any paragraphs, then no
// sections are copied.
private static void AppendDocument(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument,
List<XElement> newContent, bool keepSection, string insertId, List<ImageData> images)
{
FixRanges(sourceDocument.MainDocumentPart.GetXDocument(), newContent);
AddRelationships(sourceDocument.MainDocumentPart, newDocument.MainDocumentPart, newContent);
CopyRelatedPartsForContentParts(sourceDocument.MainDocumentPart, newDocument.MainDocumentPart,
newContent, images);
// Append contents
XDocument newMainXDoc = newDocument.MainDocumentPart.GetXDocument();
newMainXDoc.Declaration.Standalone = "yes";
newMainXDoc.Declaration.Encoding = "UTF-8";
if (keepSection == false)
{
List<XElement> adjustedContents = newContent.Where(e => e.Name != W.sectPr).ToList();
adjustedContents.DescendantsAndSelf(W.sectPr).Remove();
newContent = adjustedContents;
}
var listOfSectionProps = newContent.DescendantsAndSelf(W.sectPr).ToList();
foreach (var sectPr in listOfSectionProps)
AddSectionAndDependencies(sourceDocument, newDocument, sectPr, images);
CopyStylesAndFonts(sourceDocument, newDocument, newContent);
CopyNumbering(sourceDocument, newDocument, newContent, images);
CopyComments(sourceDocument, newDocument, newContent, images);
CopyFootnotes(sourceDocument, newDocument, newContent, images);
CopyEndnotes(sourceDocument, newDocument, newContent, images);
AdjustUniqueIds(sourceDocument, newDocument, newContent);
RemoveGfxdata(newContent);
CopyCustomXml(sourceDocument, newDocument, newContent);
CopyWebExtensions(sourceDocument, newDocument);
if (insertId != null)
{
XElement insertElementToReplace = newMainXDoc
.Descendants(PtOpenXml.Insert)
.FirstOrDefault(i => (string)i.Attribute(PtOpenXml.Id) == insertId);
if (insertElementToReplace != null)
insertElementToReplace.AddAnnotation(new ReplaceSemaphore());
newMainXDoc.Element(W.document).ReplaceWith((XElement)InsertTransform(newMainXDoc.Root, newContent));
}
else
newMainXDoc.Root.Element(W.body).Add(newContent);
if (newMainXDoc.Descendants().Any(d =>
{
if (d.Name.Namespace == PtOpenXml.pt || d.Name.Namespace == PtOpenXml.ptOpenXml)
return true;
if (d.Attributes().Any(att => att.Name.Namespace == PtOpenXml.pt || att.Name.Namespace == PtOpenXml.ptOpenXml))
return true;
return false;
}))
{
var root = newMainXDoc.Root;
if (!root.Attributes().Any(na => na.Value == PtOpenXml.pt.NamespaceName))
{
root.Add(new XAttribute(XNamespace.Xmlns + "pt", PtOpenXml.pt.NamespaceName));
AddToIgnorable(root, "pt");
}
if (!root.Attributes().Any(na => na.Value == PtOpenXml.ptOpenXml.NamespaceName))
{
root.Add(new XAttribute(XNamespace.Xmlns + "pt14", PtOpenXml.ptOpenXml.NamespaceName));
AddToIgnorable(root, "pt14");
}
}
}
private static void CopyWebExtensions(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument)
{
if (sourceDocument.WebExTaskpanesPart != null && newDocument.WebExTaskpanesPart == null)
{
newDocument.AddWebExTaskpanesPart();
newDocument.WebExTaskpanesPart.GetXDocument().Add(sourceDocument.WebExTaskpanesPart.GetXDocument().Root);
foreach (var sourceWebExtensionPart in sourceDocument.WebExTaskpanesPart.WebExtensionParts)
{
var newWebExtensionpart = newDocument.WebExTaskpanesPart.AddNewPart<WebExtensionPart>(
sourceDocument.WebExTaskpanesPart.GetIdOfPart(sourceWebExtensionPart));
newWebExtensionpart.GetXDocument().Add(sourceWebExtensionPart.GetXDocument().Root);
}
}
}
private static void AddToIgnorable(XElement root, string v)
{
var ignorable = root.Attribute(MC.Ignorable);
if (ignorable != null)
{
var val = (string)ignorable;
val = val + " " + v;
ignorable.Remove();
root.SetAttributeValue(MC.Ignorable, val);
}
}
/// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// New method to support new functionality
private static void AppendDocument(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, OpenXmlPart part,
List<XElement> newContent, bool keepSection, string insertId, List<ImageData> images)
{
// Append contents
XDocument partXDoc = part.GetXDocument();
partXDoc.Declaration.Standalone = "yes";
partXDoc.Declaration.Encoding = "UTF-8";
FixRanges(part.GetXDocument(), newContent);
AddRelationships(sourceDocument.MainDocumentPart, part, newContent);
CopyRelatedPartsForContentParts(sourceDocument.MainDocumentPart, part,
newContent, images);
// never keep sections for content to be inserted into a header/footer
List<XElement> adjustedContents = newContent.Where(e => e.Name != W.sectPr).ToList();
adjustedContents.DescendantsAndSelf(W.sectPr).Remove();
newContent = adjustedContents;
CopyNumbering(sourceDocument, newDocument, newContent, images);
CopyComments(sourceDocument, newDocument, newContent, images);
AdjustUniqueIds(sourceDocument, newDocument, newContent);
RemoveGfxdata(newContent);
if (insertId == null)
throw new OpenXmlPowerToolsException("Internal error");
XElement insertElementToReplace = partXDoc
.Descendants(PtOpenXml.Insert)
.FirstOrDefault(i => (string)i.Attribute(PtOpenXml.Id) == insertId);
if (insertElementToReplace != null)
insertElementToReplace.AddAnnotation(new ReplaceSemaphore());
partXDoc.Elements().First().ReplaceWith((XElement)InsertTransform(partXDoc.Root, newContent));
}
/// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private static void CopyCustomXml(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument,
IEnumerable<XElement> newContent)
{
List<string> itemList = new List<string>();
foreach (string itemId in newContent
.Descendants(W.dataBinding)
.Select(e => (string)e.Attribute(W.storeItemID)))
if (!itemList.Contains(itemId))
itemList.Add(itemId);
foreach (CustomXmlPart customXmlPart in sourceDocument.MainDocumentPart.CustomXmlParts)
{
OpenXmlPart propertyPart = customXmlPart
.Parts
.Select(p => p.OpenXmlPart)
.Where(p => p.ContentType == "application/vnd.openxmlformats-officedocument.customXmlProperties+xml")
.FirstOrDefault();
if (propertyPart != null)
{
XDocument propertyPartDoc = propertyPart.GetXDocument();
if (itemList.Contains(propertyPartDoc.Root.Attribute(DS.itemID).Value))
{
CustomXmlPart newPart = newDocument.MainDocumentPart.AddCustomXmlPart(customXmlPart.ContentType);
newPart.GetXDocument().Add(customXmlPart.GetXDocument().Root);
foreach (OpenXmlPart propPart in customXmlPart.Parts.Select(p => p.OpenXmlPart))
{
CustomXmlPropertiesPart newPropPart = newPart.AddNewPart<CustomXmlPropertiesPart>();
newPropPart.GetXDocument().Add(propPart.GetXDocument().Root);
}
}
}
}
}
private static Dictionary<XName, XName[]> RelationshipMarkup = null;
private static void UpdateContent(IEnumerable<XElement> newContent, XName elementToModify, string oldRid, string newRid)
{
foreach (var attributeName in RelationshipMarkup[elementToModify])
{
var elementsToUpdate = newContent
.Descendants(elementToModify)
.Where(e => (string)e.Attribute(attributeName) == oldRid);
foreach (var element in elementsToUpdate)
element.Attribute(attributeName).Value = newRid;
}
}
private static void AddRelationships(OpenXmlPart oldPart, OpenXmlPart newPart, IEnumerable<XElement> newContent)
{
var relevantElements = newContent.DescendantsAndSelf()
.Where(d => RelationshipMarkup.ContainsKey(d.Name) &&
d.Attributes().Any(a => RelationshipMarkup[d.Name].Contains(a.Name)))
.ToList();
foreach (var e in relevantElements)
{
if (e.Name == W.hyperlink)
{
string relId = (string)e.Attribute(R.id);
if (string.IsNullOrEmpty(relId))
continue;
var tempHyperlink = newPart.HyperlinkRelationships.FirstOrDefault(h => h.Id == relId);
if (tempHyperlink != null)
continue;
Guid g = Guid.NewGuid();
string newRid = "R" + g.ToString().Replace("-", "");
var oldHyperlink = oldPart.HyperlinkRelationships.FirstOrDefault(h => h.Id == relId);
if (oldHyperlink == null)
continue;
//throw new DocumentBuilderInternalException("Internal Error 0002");
newPart.AddHyperlinkRelationship(oldHyperlink.Uri, oldHyperlink.IsExternal, newRid);
UpdateContent(newContent, e.Name, relId, newRid);
}
if (e.Name == W.attachedTemplate || e.Name == W.saveThroughXslt)
{
string relId = (string)e.Attribute(R.id);
if (string.IsNullOrEmpty(relId))
continue;
var tempExternalRelationship = newPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId);
if (tempExternalRelationship != null)
continue;
Guid g = Guid.NewGuid();
string newRid = "R" + g.ToString().Replace("-", "");
var oldRel = oldPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId);
if (oldRel == null)
throw new DocumentBuilderInternalException("Source {0} is invalid document - hyperlink contains invalid references");
newPart.AddExternalRelationship(oldRel.RelationshipType, oldRel.Uri, newRid);
UpdateContent(newContent, e.Name, relId, newRid);
}
if (e.Name == A.hlinkClick || e.Name == A.hlinkHover || e.Name == A.hlinkMouseOver)
{
string relId = (string)e.Attribute(R.id);
if (string.IsNullOrEmpty(relId))
continue;
var tempHyperlink = newPart.HyperlinkRelationships.FirstOrDefault(h => h.Id == relId);
if (tempHyperlink != null)
continue;
Guid g = Guid.NewGuid();
string newRid = "R" + g.ToString().Replace("-", "");
var oldHyperlink = oldPart.HyperlinkRelationships.FirstOrDefault(h => h.Id == relId);
if (oldHyperlink == null)
continue;
newPart.AddHyperlinkRelationship(oldHyperlink.Uri, oldHyperlink.IsExternal, newRid);
UpdateContent(newContent, e.Name, relId, newRid);
}
if (e.Name == VML.imagedata)
{
string relId = (string)e.Attribute(R.href);
if (string.IsNullOrEmpty(relId))
continue;
var tempExternalRelationship = newPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId);
if (tempExternalRelationship != null)
continue;
Guid g = Guid.NewGuid();
string newRid = "R" + g.ToString().Replace("-", "");
var oldRel = oldPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId);
if (oldRel == null)
throw new DocumentBuilderInternalException("Internal Error 0006");
newPart.AddExternalRelationship(oldRel.RelationshipType, oldRel.Uri, newRid);
UpdateContent(newContent, e.Name, relId, newRid);
}
if (e.Name == A.blip)
{
// <a:blip r:embed="rId6" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" />
string relId = (string)e.Attribute(R.link);
//if (relId == null)
// relId = (string)e.Attribute(R.embed);
if (string.IsNullOrEmpty(relId))
continue;
var tempExternalRelationship = newPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId);
if (tempExternalRelationship != null)
continue;
Guid g = Guid.NewGuid();
string newRid = "R" + g.ToString().Replace("-", "");
var oldRel = oldPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId);
if (oldRel == null)
continue;
newPart.AddExternalRelationship(oldRel.RelationshipType, oldRel.Uri, newRid);
UpdateContent(newContent, e.Name, relId, newRid);
}
}
}
private class FromPreviousSourceSemaphore { };
private static void CopyNumbering(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument,
IEnumerable<XElement> newContent, List<ImageData> images)
{
Dictionary<int, int> numIdMap = new Dictionary<int, int>();
int number = 1;
int abstractNumber = 0;
XDocument oldNumbering = null;
XDocument newNumbering = null;
foreach (XElement numReference in newContent.DescendantsAndSelf(W.numPr))
{
XElement idElement = numReference.Descendants(W.numId).FirstOrDefault();
if (idElement != null)
{
if (oldNumbering == null)
oldNumbering = sourceDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument();
if (newNumbering == null)
{
if (newDocument.MainDocumentPart.NumberingDefinitionsPart != null)
{
newNumbering = newDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument();
var numIds = newNumbering
.Root
.Elements(W.num)
.Select(f => (int)f.Attribute(W.numId));
if (numIds.Any())
number = numIds.Max() + 1;
numIds = newNumbering
.Root
.Elements(W.abstractNum)
.Select(f => (int)f.Attribute(W.abstractNumId));
if (numIds.Any())
abstractNumber = numIds.Max() + 1;
}
else
{
newDocument.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>();
newNumbering = newDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument();
newNumbering.Declaration.Standalone = "yes";
newNumbering.Declaration.Encoding = "UTF-8";
newNumbering.Add(new XElement(W.numbering, NamespaceAttributes));
}
}
int numId = (int)idElement.Attribute(W.val);
if (numId != 0)
{
XElement element = oldNumbering
.Descendants(W.num)
.Where(p => ((int)p.Attribute(W.numId)) == numId)
.FirstOrDefault();
if (element == null)
continue;
// Copy abstract numbering element, if necessary (use matching NSID)
int abstractNumId = (int)element
.Elements(W.abstractNumId)
.First()
.Attribute(W.val);
XElement abstractElement = oldNumbering
.Descendants()
.Elements(W.abstractNum)
.Where(p => ((int)p.Attribute(W.abstractNumId)) == abstractNumId)
.First();
XElement nsidElement = abstractElement
.Element(W.nsid);
string abstractNSID = null;
if (nsidElement != null)
abstractNSID = (string)nsidElement
.Attribute(W.val);
XElement newAbstractElement = newNumbering
.Descendants()
.Elements(W.abstractNum)
.Where(e => e.Annotation<FromPreviousSourceSemaphore>() == null)
.Where(p =>
{
var thisNsidElement = p.Element(W.nsid);
if (thisNsidElement == null)
return false;
return (string)thisNsidElement.Attribute(W.val) == abstractNSID;
})
.FirstOrDefault();
if (newAbstractElement == null)
{
newAbstractElement = new XElement(abstractElement);
newAbstractElement.Attribute(W.abstractNumId).Value = abstractNumber.ToString();
abstractNumber++;
if (newNumbering.Root.Elements(W.abstractNum).Any())
newNumbering.Root.Elements(W.abstractNum).Last().AddAfterSelf(newAbstractElement);
else
newNumbering.Root.Add(newAbstractElement);
foreach (XElement pictId in newAbstractElement.Descendants(W.lvlPicBulletId))
{
string bulletId = (string)pictId.Attribute(W.val);
XElement numPicBullet = oldNumbering
.Descendants(W.numPicBullet)
.FirstOrDefault(d => (string)d.Attribute(W.numPicBulletId) == bulletId);
int maxNumPicBulletId = new int[] { -1 }.Concat(
newNumbering.Descendants(W.numPicBullet)
.Attributes(W.numPicBulletId)
.Select(a => (int)a))
.Max() + 1;
XElement newNumPicBullet = new XElement(numPicBullet);
newNumPicBullet.Attribute(W.numPicBulletId).Value = maxNumPicBulletId.ToString();
pictId.Attribute(W.val).Value = maxNumPicBulletId.ToString();
newNumbering.Root.AddFirst(newNumPicBullet);
}
}
string newAbstractId = newAbstractElement.Attribute(W.abstractNumId).Value;
// Copy numbering element, if necessary (use matching element with no overrides)
XElement newElement;
if (numIdMap.ContainsKey(numId))
{
newElement = newNumbering
.Descendants()
.Elements(W.num)
.Where(e => e.Annotation<FromPreviousSourceSemaphore>() == null)
.Where(p => ((int)p.Attribute(W.numId)) == numIdMap[numId])
.First();
}
else
{
newElement = new XElement(element);
newElement
.Elements(W.abstractNumId)
.First()
.Attribute(W.val).Value = newAbstractId;
newElement.Attribute(W.numId).Value = number.ToString();
numIdMap.Add(numId, number);
number++;
newNumbering.Root.Add(newElement);
}
idElement.Attribute(W.val).Value = newElement.Attribute(W.numId).Value;
}
}
}
if (newNumbering != null)
{
foreach (var abstractNum in newNumbering.Descendants(W.abstractNum))
abstractNum.AddAnnotation(new FromPreviousSourceSemaphore());
foreach (var num in newNumbering.Descendants(W.num))
num.AddAnnotation(new FromPreviousSourceSemaphore());
}
if (newDocument.MainDocumentPart.NumberingDefinitionsPart != null &&
sourceDocument.MainDocumentPart.NumberingDefinitionsPart != null)
{
AddRelationships(sourceDocument.MainDocumentPart.NumberingDefinitionsPart,
newDocument.MainDocumentPart.NumberingDefinitionsPart,
new[] { newDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument().Root });
CopyRelatedPartsForContentParts(sourceDocument.MainDocumentPart.NumberingDefinitionsPart,
newDocument.MainDocumentPart.NumberingDefinitionsPart,
new[] { newDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument().Root }, images);
}
}
private static void CopyRelatedImage(OpenXmlPart oldContentPart, OpenXmlPart newContentPart, XElement imageReference, XName attributeName,
List<ImageData> images)
{
string relId = (string)imageReference.Attribute(attributeName);
if (string.IsNullOrEmpty(relId))
return;
// First look to see if this relId has already been added to the new document.
// This is necessary for those parts that get processed with both old and new ids, such as the comments
// part. This is not necessary for parts such as the main document part, but this code won't malfunction
// in that case.
var tempPartIdPair5 = newContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId);
if (tempPartIdPair5 != null)
return;
ExternalRelationship tempEr5 = newContentPart.ExternalRelationships.FirstOrDefault(er => er.Id == relId);
if (tempEr5 != null)
return;
var ipp2 = oldContentPart.Parts.FirstOrDefault(ipp => ipp.RelationshipId == relId);
if (ipp2 != null)
{
ImagePart oldPart = (ImagePart)ipp2.OpenXmlPart;
ImageData temp = ManageImageCopy(oldPart, newContentPart, images);
if (temp.ImagePart == null)
{
ImagePart newPart = null;
if (newContentPart is MainDocumentPart)
newPart = ((MainDocumentPart)newContentPart).AddImagePart(oldPart.ContentType);
if (newContentPart is HeaderPart)
newPart = ((HeaderPart)newContentPart).AddImagePart(oldPart.ContentType);
if (newContentPart is FooterPart)
newPart = ((FooterPart)newContentPart).AddImagePart(oldPart.ContentType);
if (newContentPart is EndnotesPart)
newPart = ((EndnotesPart)newContentPart).AddImagePart(oldPart.ContentType);
if (newContentPart is FootnotesPart)
newPart = ((FootnotesPart)newContentPart).AddImagePart(oldPart.ContentType);
if (newContentPart is ThemePart)
newPart = ((ThemePart)newContentPart).AddImagePart(oldPart.ContentType);
if (newContentPart is WordprocessingCommentsPart)
newPart = ((WordprocessingCommentsPart)newContentPart).AddImagePart(oldPart.ContentType);
if (newContentPart is DocumentSettingsPart)
newPart = ((DocumentSettingsPart)newContentPart).AddImagePart(oldPart.ContentType);
if (newContentPart is ChartPart)
newPart = ((ChartPart)newContentPart).AddImagePart(oldPart.ContentType);
if (newContentPart is NumberingDefinitionsPart)
newPart = ((NumberingDefinitionsPart)newContentPart).AddImagePart(oldPart.ContentType);
if (newContentPart is DiagramDataPart)
newPart = ((DiagramDataPart)newContentPart).AddImagePart(oldPart.ContentType);
if (newContentPart is ChartDrawingPart)
newPart = ((ChartDrawingPart)newContentPart).AddImagePart(oldPart.ContentType);
temp.ImagePart = newPart;
var id = newContentPart.GetIdOfPart(newPart);
temp.AddContentPartRelTypeResourceIdTupple(newContentPart, newPart.RelationshipType, id);
imageReference.Attribute(attributeName).Value = id;
temp.WriteImage(newPart);
}
else
{
var refRel = newContentPart.Parts.FirstOrDefault(pip =>
{
var rel = temp.ContentPartRelTypeIdList.FirstOrDefault(cpr =>
{
var found = cpr.ContentPart == newContentPart;
return found;
});
return rel != null;
});
if (refRel != null)
{
imageReference.Attribute(attributeName).Value = temp.ContentPartRelTypeIdList.First(cpr =>
{
var found = cpr.ContentPart == newContentPart;
return found;
}).RelationshipId;
return;
}
var newId = "R" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16);
newContentPart.CreateRelationshipToPart(temp.ImagePart, newId);
imageReference.Attribute(R.id).Value = newId;
}
}
else
{
ExternalRelationship er = oldContentPart.ExternalRelationships.FirstOrDefault(er1 => er1.Id == relId);
if (er != null)
{
ExternalRelationship newEr = newContentPart.AddExternalRelationship(er.RelationshipType, er.Uri);
imageReference.Attribute(R.id).Value = newEr.Id;
}
throw new DocumentBuilderInternalException("Source {0} is unsupported document - contains reference to NULL image");
}
}
private static void CopyRelatedPartsForContentParts(OpenXmlPart oldContentPart, OpenXmlPart newContentPart,
IEnumerable<XElement> newContent, List<ImageData> images)
{
var relevantElements = newContent.DescendantsAndSelf()
.Where(d => d.Name == VML.imagedata || d.Name == VML.fill || d.Name == VML.stroke || d.Name == A.blip)
.ToList();
foreach (XElement imageReference in relevantElements)
{
CopyRelatedImage(oldContentPart, newContentPart, imageReference, R.embed, images);
CopyRelatedImage(oldContentPart, newContentPart, imageReference, R.pict, images);
CopyRelatedImage(oldContentPart, newContentPart, imageReference, R.id, images);
}
foreach (XElement diagramReference in newContent.DescendantsAndSelf().Where(d => d.Name == DGM.relIds || d.Name == A.relIds))
{
// dm attribute
string relId = diagramReference.Attribute(R.dm).Value;
var ipp = newContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId);
if (ipp != null)
{
OpenXmlPart tempPart = ipp.OpenXmlPart;
continue;
}
ExternalRelationship tempEr = newContentPart.ExternalRelationships.FirstOrDefault(er2 => er2.Id == relId);
if (tempEr != null)
continue;
OpenXmlPart oldPart = oldContentPart.GetPartById(relId);
OpenXmlPart newPart = newContentPart.AddNewPart<DiagramDataPart>();
newPart.GetXDocument().Add(oldPart.GetXDocument().Root);
diagramReference.Attribute(R.dm).Value = newContentPart.GetIdOfPart(newPart);
AddRelationships(oldPart, newPart, new[] { newPart.GetXDocument().Root });
CopyRelatedPartsForContentParts(oldPart, newPart, new[] { newPart.GetXDocument().Root }, images);
// lo attribute
relId = diagramReference.Attribute(R.lo).Value;
var ipp2 = newContentPart.Parts.FirstOrDefault(z => z.RelationshipId == relId);
if (ipp2 != null)
{
OpenXmlPart tempPart = ipp2.OpenXmlPart;
continue;
}
ExternalRelationship tempEr4 = newContentPart.ExternalRelationships.FirstOrDefault(er3 => er3.Id == relId);
if (tempEr4 != null)
continue;
oldPart = oldContentPart.GetPartById(relId);
newPart = newContentPart.AddNewPart<DiagramLayoutDefinitionPart>();
newPart.GetXDocument().Add(oldPart.GetXDocument().Root);
diagramReference.Attribute(R.lo).Value = newContentPart.GetIdOfPart(newPart);
AddRelationships(oldPart, newPart, new[] { newPart.GetXDocument().Root });
CopyRelatedPartsForContentParts(oldPart, newPart, new[] { newPart.GetXDocument().Root }, images);
// qs attribute
relId = diagramReference.Attribute(R.qs).Value;
var ipp5 = newContentPart.Parts.FirstOrDefault(z => z.RelationshipId == relId);
if (ipp5 != null)
{
OpenXmlPart tempPart = ipp5.OpenXmlPart;
continue;
}
ExternalRelationship tempEr5 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId);
if (tempEr5 != null)
continue;
oldPart = oldContentPart.GetPartById(relId);
newPart = newContentPart.AddNewPart<DiagramStylePart>();
newPart.GetXDocument().Add(oldPart.GetXDocument().Root);
diagramReference.Attribute(R.qs).Value = newContentPart.GetIdOfPart(newPart);
AddRelationships(oldPart, newPart, new[] { newPart.GetXDocument().Root });
CopyRelatedPartsForContentParts(oldPart, newPart, new[] { newPart.GetXDocument().Root }, images);
// cs attribute
relId = diagramReference.Attribute(R.cs).Value;
var ipp6 = newContentPart.Parts.FirstOrDefault(z => z.RelationshipId == relId);
if (ipp6 != null)
{
OpenXmlPart tempPart = ipp6.OpenXmlPart;
continue;
}
ExternalRelationship tempEr6 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId);
if (tempEr6 != null)
continue;
oldPart = oldContentPart.GetPartById(relId);
newPart = newContentPart.AddNewPart<DiagramColorsPart>();
newPart.GetXDocument().Add(oldPart.GetXDocument().Root);
diagramReference.Attribute(R.cs).Value = newContentPart.GetIdOfPart(newPart);
AddRelationships(oldPart, newPart, new[] { newPart.GetXDocument().Root });
CopyRelatedPartsForContentParts(oldPart, newPart, new[] { newPart.GetXDocument().Root }, images);
}
foreach (XElement oleReference in newContent.DescendantsAndSelf(O.OLEObject))
{
string relId = (string)oleReference.Attribute(R.id);
// First look to see if this relId has already been added to the new document.
// This is necessary for those parts that get processed with both old and new ids, such as the comments
// part. This is not necessary for parts such as the main document part, but this code won't malfunction
// in that case.
var ipp1 = newContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId);
if (ipp1 != null)
{
OpenXmlPart tempPart = ipp1.OpenXmlPart;
continue;
}
ExternalRelationship tempEr1 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId);
if (tempEr1 != null)
continue;
var ipp4 = oldContentPart.Parts.FirstOrDefault(z => z.RelationshipId == relId);
if (ipp4 != null)
{
OpenXmlPart oldPart = oldContentPart.GetPartById(relId);
OpenXmlPart newPart = null;
if (oldPart is EmbeddedObjectPart)
{
if (newContentPart is HeaderPart)
newPart = ((HeaderPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType);
if (newContentPart is FooterPart)
newPart = ((FooterPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType);
if (newContentPart is MainDocumentPart)
newPart = ((MainDocumentPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType);
if (newContentPart is FootnotesPart)
newPart = ((FootnotesPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType);
if (newContentPart is EndnotesPart)
newPart = ((EndnotesPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType);
if (newContentPart is WordprocessingCommentsPart)
newPart = ((WordprocessingCommentsPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType);
}
else if (oldPart is EmbeddedPackagePart)
{
if (newContentPart is HeaderPart)
newPart = ((HeaderPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType);
if (newContentPart is FooterPart)
newPart = ((FooterPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType);
if (newContentPart is MainDocumentPart)
newPart = ((MainDocumentPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType);
if (newContentPart is FootnotesPart)
newPart = ((FootnotesPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType);
if (newContentPart is EndnotesPart)
newPart = ((EndnotesPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType);
if (newContentPart is WordprocessingCommentsPart)
newPart = ((WordprocessingCommentsPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType);
if (newContentPart is ChartPart)
newPart = ((ChartPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType);
}
using (Stream oldObject = oldPart.GetStream(FileMode.Open, FileAccess.Read))
using (Stream newObject = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite))
{
int byteCount;
byte[] buffer = new byte[65536];
while ((byteCount = oldObject.Read(buffer, 0, 65536)) != 0)
newObject.Write(buffer, 0, byteCount);
}
oleReference.Attribute(R.id).Value = newContentPart.GetIdOfPart(newPart);
}
else
{
if (relId != null)
{
ExternalRelationship er = oldContentPart.GetExternalRelationship(relId);
ExternalRelationship newEr = newContentPart.AddExternalRelationship(er.RelationshipType, er.Uri);
oleReference.Attribute(R.id).Value = newEr.Id;
}
}
}
foreach (XElement chartReference in newContent.DescendantsAndSelf(C.chart))
{
string relId = (string)chartReference.Attribute(R.id);
if (string.IsNullOrEmpty(relId))
continue;
var ipp2 = newContentPart.Parts.FirstOrDefault(z => z.RelationshipId == relId);
if (ipp2 != null)
{
OpenXmlPart tempPart = ipp2.OpenXmlPart;
continue;
}
ExternalRelationship tempEr2 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId);
if (tempEr2 != null)
continue;
var ipp3 = oldContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId);
if (ipp3 == null)
continue;
ChartPart oldPart = (ChartPart)ipp3.OpenXmlPart;
XDocument oldChart = oldPart.GetXDocument();
ChartPart newPart = newContentPart.AddNewPart<ChartPart>();
XDocument newChart = newPart.GetXDocument();
newChart.Add(oldChart.Root);
chartReference.Attribute(R.id).Value = newContentPart.GetIdOfPart(newPart);
CopyChartObjects(oldPart, newPart);
CopyRelatedPartsForContentParts(oldPart, newPart, new[] { newChart.Root }, images);
}
foreach (XElement userShape in newContent.DescendantsAndSelf(C.userShapes))
{
string relId = (string)userShape.Attribute(R.id);
if (string.IsNullOrEmpty(relId))
continue;
var ipp4 = newContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId);
if (ipp4 != null)
{
OpenXmlPart tempPart = ipp4.OpenXmlPart;
continue;
}
ExternalRelationship tempEr4 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId);
if (tempEr4 != null)
continue;
var ipp5 = oldContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId);
if (ipp5 != null)
{
ChartDrawingPart oldPart = (ChartDrawingPart)ipp5.OpenXmlPart;
XDocument oldXDoc = oldPart.GetXDocument();
ChartDrawingPart newPart = newContentPart.AddNewPart<ChartDrawingPart>();
XDocument newXDoc = newPart.GetXDocument();
newXDoc.Add(oldXDoc.Root);
userShape.Attribute(R.id).Value = newContentPart.GetIdOfPart(newPart);
AddRelationships(oldPart, newPart, newContent);
CopyRelatedPartsForContentParts(oldPart, newPart, new[] { newXDoc.Root }, images);
}
}
}
private static void CopyFontTable(FontTablePart oldFontTablePart, FontTablePart newFontTablePart)
{
var relevantElements = oldFontTablePart.GetXDocument().Descendants().Where(d => d.Name == W.embedRegular ||
d.Name == W.embedBold || d.Name == W.embedItalic || d.Name == W.embedBoldItalic).ToList();
foreach (XElement fontReference in relevantElements)
{
string relId = (string)fontReference.Attribute(R.id);
if (string.IsNullOrEmpty(relId))
continue;
var ipp1 = newFontTablePart.Parts.FirstOrDefault(z => z.RelationshipId == relId);
if (ipp1 != null)
{
OpenXmlPart tempPart = ipp1.OpenXmlPart;
continue;
}
ExternalRelationship tempEr1 = newFontTablePart.ExternalRelationships.FirstOrDefault(z => z.Id == relId);
if (tempEr1 != null)
continue;
FontPart oldPart = (FontPart)oldFontTablePart.GetPartById(relId);
FontPart newPart = newFontTablePart.AddFontPart(oldPart.ContentType);
var ResourceID = newFontTablePart.GetIdOfPart(newPart);
using (Stream oldFont = oldPart.GetStream(FileMode.Open, FileAccess.Read))
using (Stream newFont = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite))
{
int byteCount;
byte[] buffer = new byte[65536];
while ((byteCount = oldFont.Read(buffer, 0, 65536)) != 0)
newFont.Write(buffer, 0, byteCount);
}
fontReference.Attribute(R.id).Value = ResourceID;
}
}
private static void CopyChartObjects(ChartPart oldChart, ChartPart newChart)
{
foreach (XElement dataReference in newChart.GetXDocument().Descendants(C.externalData))
{
string relId = dataReference.Attribute(R.id).Value;
var ipp1 = oldChart.Parts.FirstOrDefault(z => z.RelationshipId == relId);
if (ipp1 != null)
{
var oldRelatedPart = ipp1.OpenXmlPart;
if (oldRelatedPart is EmbeddedPackagePart)
{
EmbeddedPackagePart oldPart = (EmbeddedPackagePart)ipp1.OpenXmlPart;
EmbeddedPackagePart newPart = newChart.AddEmbeddedPackagePart(oldPart.ContentType);
using (Stream oldObject = oldPart.GetStream(FileMode.Open, FileAccess.Read))
using (Stream newObject = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite))
{
int byteCount;
byte[] buffer = new byte[65536];
while ((byteCount = oldObject.Read(buffer, 0, 65536)) != 0)
newObject.Write(buffer, 0, byteCount);
}
dataReference.Attribute(R.id).Value = newChart.GetIdOfPart(newPart);
}
else if (oldRelatedPart is EmbeddedObjectPart)
{
EmbeddedObjectPart oldPart = (EmbeddedObjectPart)ipp1.OpenXmlPart;
var relType = oldRelatedPart.RelationshipType;
var conType = oldRelatedPart.ContentType;
string id = "R" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 8);
var newPart = newChart.AddExtendedPart(relType, conType, ".bin", id);
using (Stream oldObject = oldPart.GetStream(FileMode.Open, FileAccess.Read))
using (Stream newObject = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite))
{
int byteCount;
byte[] buffer = new byte[65536];
while ((byteCount = oldObject.Read(buffer, 0, 65536)) != 0)
newObject.Write(buffer, 0, byteCount);
}
dataReference.Attribute(R.id).Value = newChart.GetIdOfPart(newPart);
}
}
else
{
ExternalRelationship oldRelationship = oldChart.GetExternalRelationship(relId);
Guid g = Guid.NewGuid();
string newRid = "R" + g.ToString().Replace("-", "");
var oldRel = oldChart.ExternalRelationships.FirstOrDefault(h => h.Id == relId);
if (oldRel == null)
throw new DocumentBuilderInternalException("Internal Error 0007");
newChart.AddExternalRelationship(oldRel.RelationshipType, oldRel.Uri, newRid);
dataReference.Attribute(R.id).Value = newRid;
}
}
}
private static void CopyStartingParts(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument,
List<ImageData> images)
{
// A Core File Properties part does not have implicit or explicit relationships to other parts.
CoreFilePropertiesPart corePart = sourceDocument.CoreFilePropertiesPart;
if (corePart != null && corePart.GetXDocument().Root != null)
{
newDocument.AddCoreFilePropertiesPart();
XDocument newXDoc = newDocument.CoreFilePropertiesPart.GetXDocument();
newXDoc.Declaration.Standalone = "yes";
newXDoc.Declaration.Encoding = "UTF-8";
XDocument sourceXDoc = corePart.GetXDocument();
newXDoc.Add(sourceXDoc.Root);
}
// An application attributes part does not have implicit or explicit relationships to other parts.
ExtendedFilePropertiesPart extPart = sourceDocument.ExtendedFilePropertiesPart;
if (extPart != null)
{
OpenXmlPart newPart = newDocument.AddExtendedFilePropertiesPart();
XDocument newXDoc = newDocument.ExtendedFilePropertiesPart.GetXDocument();
newXDoc.Declaration.Standalone = "yes";
newXDoc.Declaration.Encoding = "UTF-8";
newXDoc.Add(extPart.GetXDocument().Root);
}
// An custom file properties part does not have implicit or explicit relationships to other parts.
CustomFilePropertiesPart customPart = sourceDocument.CustomFilePropertiesPart;
if (customPart != null)
{
newDocument.AddCustomFilePropertiesPart();
XDocument newXDoc = newDocument.CustomFilePropertiesPart.GetXDocument();
newXDoc.Declaration.Standalone = "yes";
newXDoc.Declaration.Encoding = "UTF-8";
newXDoc.Add(customPart.GetXDocument().Root);
}
DocumentSettingsPart oldSettingsPart = sourceDocument.MainDocumentPart.DocumentSettingsPart;
if (oldSettingsPart != null)
{
DocumentSettingsPart newSettingsPart = newDocument.MainDocumentPart.AddNewPart<DocumentSettingsPart>();
XDocument settingsXDoc = oldSettingsPart.GetXDocument();
AddRelationships(oldSettingsPart, newSettingsPart, new[] { settingsXDoc.Root });
CopyFootnotesPart(sourceDocument, newDocument, settingsXDoc, images);
CopyEndnotesPart(sourceDocument, newDocument, settingsXDoc, images);
XDocument newXDoc = newDocument.MainDocumentPart.DocumentSettingsPart.GetXDocument();
newXDoc.Declaration.Standalone = "yes";
newXDoc.Declaration.Encoding = "UTF-8";
newXDoc.Add(settingsXDoc.Root);
CopyRelatedPartsForContentParts(oldSettingsPart, newSettingsPart, new[] { newXDoc.Root }, images);
}
WebSettingsPart oldWebSettingsPart = sourceDocument.MainDocumentPart.WebSettingsPart;
if (oldWebSettingsPart != null)
{
WebSettingsPart newWebSettingsPart = newDocument.MainDocumentPart.AddNewPart<WebSettingsPart>();
XDocument settingsXDoc = oldWebSettingsPart.GetXDocument();
AddRelationships(oldWebSettingsPart, newWebSettingsPart, new[] { settingsXDoc.Root });
XDocument newXDoc = newDocument.MainDocumentPart.WebSettingsPart.GetXDocument();
newXDoc.Declaration.Standalone = "yes";
newXDoc.Declaration.Encoding = "UTF-8";
newXDoc.Add(settingsXDoc.Root);
}
ThemePart themePart = sourceDocument.MainDocumentPart.ThemePart;
if (themePart != null)
{
ThemePart newThemePart = newDocument.MainDocumentPart.AddNewPart<ThemePart>();
XDocument newXDoc = newDocument.MainDocumentPart.ThemePart.GetXDocument();
newXDoc.Declaration.Standalone = "yes";
newXDoc.Declaration.Encoding = "UTF-8";
newXDoc.Add(themePart.GetXDocument().Root);
CopyRelatedPartsForContentParts(themePart, newThemePart, new[] { newThemePart.GetXDocument().Root }, images);
}
// If needed to handle GlossaryDocumentPart in the future, then
// this code should handle the following parts:
// MainDocumentPart.GlossaryDocumentPart.StyleDefinitionsPart
// MainDocumentPart.GlossaryDocumentPart.StylesWithEffectsPart
// A Style Definitions part shall not have implicit or explicit relationships to any other part.
StyleDefinitionsPart stylesPart = sourceDocument.MainDocumentPart.StyleDefinitionsPart;
if (stylesPart != null)
{
newDocument.MainDocumentPart.AddNewPart<StyleDefinitionsPart>();
XDocument newXDoc = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument();
newXDoc.Declaration.Standalone = "yes";
newXDoc.Declaration.Encoding = "UTF-8";
newXDoc.Add(new XElement(W.styles,
new XAttribute(XNamespace.Xmlns + "w", W.w),
stylesPart.GetXDocument().Descendants(W.docDefaults)));
MergeStyles(sourceDocument, newDocument, stylesPart.GetXDocument(), newXDoc, Enumerable.Empty<XElement>());
}
//// A StylesWithEffects part shall not have implicit or explicit relationships to any other part.
//StylesWithEffectsPart stylesWithEffectsPart = sourceDocument.MainDocumentPart.StylesWithEffectsPart;
//if (stylesWithEffectsPart != null)
//{
// newDocument.MainDocumentPart.AddNewPart<StylesWithEffectsPart>();
// XDocument newXDoc = newDocument.MainDocumentPart.StylesWithEffectsPart.GetXDocument();
// newXDoc.Declaration.Standalone = "yes";
// newXDoc.Declaration.Encoding = "UTF-8";
// newXDoc.Add(stylesWithEffectsPart.GetXDocument().Root);
//}
// Note: Do not copy the numbering part. For every source, create new numbering definitions from
// scratch.
//NumberingDefinitionsPart numberingPart = sourceDocument.MainDocumentPart.NumberingDefinitionsPart;
//if (numberingPart != null)
//{
// newDocument.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>();
// XDocument newXDoc = newDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument();
// newXDoc.Declaration.Standalone = "yes";
// newXDoc.Declaration.Encoding = "UTF-8";
// newXDoc.Add(numberingPart.GetXDocument().Root);
// newXDoc.Descendants(W.numIdMacAtCleanup).Remove();
//}
// A Font Table part shall not have any implicit or explicit relationships to any other part.
FontTablePart fontTablePart = sourceDocument.MainDocumentPart.FontTablePart;
if (fontTablePart != null)
{
newDocument.MainDocumentPart.AddNewPart<FontTablePart>();
XDocument newXDoc = newDocument.MainDocumentPart.FontTablePart.GetXDocument();
newXDoc.Declaration.Standalone = "yes";
newXDoc.Declaration.Encoding = "UTF-8";
CopyFontTable(sourceDocument.MainDocumentPart.FontTablePart, newDocument.MainDocumentPart.FontTablePart);
newXDoc.Add(fontTablePart.GetXDocument().Root);
}
}
private static void CopyFootnotesPart(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument,
XDocument settingsXDoc, List<ImageData> images)
{
int number = 0;
XDocument oldFootnotes = null;
XDocument newFootnotes = null;
XElement footnotePr = settingsXDoc.Root.Element(W.footnotePr);
if (footnotePr == null)
return;
foreach (XElement footnote in footnotePr.Elements(W.footnote))
{
if (oldFootnotes == null)
oldFootnotes = sourceDocument.MainDocumentPart.FootnotesPart.GetXDocument();
if (newFootnotes == null)
{
if (newDocument.MainDocumentPart.FootnotesPart != null)
{
newFootnotes = newDocument.MainDocumentPart.FootnotesPart.GetXDocument();
newFootnotes.Declaration.Standalone = "yes";
newFootnotes.Declaration.Encoding = "UTF-8";
var ids = newFootnotes.Root.Elements(W.footnote).Select(f => (int)f.Attribute(W.id));
if (ids.Any())
number = ids.Max() + 1;
}
else
{
newDocument.MainDocumentPart.AddNewPart<FootnotesPart>();
newFootnotes = newDocument.MainDocumentPart.FootnotesPart.GetXDocument();
newFootnotes.Declaration.Standalone = "yes";
newFootnotes.Declaration.Encoding = "UTF-8";
newFootnotes.Add(new XElement(W.footnotes, NamespaceAttributes));
}
}
string id = (string)footnote.Attribute(W.id);
XElement element = oldFootnotes.Descendants()
.Elements(W.footnote)
.Where(p => ((string)p.Attribute(W.id)) == id)
.FirstOrDefault();
if (element != null)
{
XElement newElement = new XElement(element);
// the following adds the footnote into the new settings part
newElement.Attribute(W.id).Value = number.ToString();
newFootnotes.Root.Add(newElement);
footnote.Attribute(W.id).Value = number.ToString();
number++;
}
}
}
private static void CopyEndnotesPart(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument,
XDocument settingsXDoc, List<ImageData> images)
{
int number = 0;
XDocument oldEndnotes = null;
XDocument newEndnotes = null;
XElement endnotePr = settingsXDoc.Root.Element(W.endnotePr);
if (endnotePr == null)
return;
foreach (XElement endnote in endnotePr.Elements(W.endnote))
{
if (oldEndnotes == null)
oldEndnotes = sourceDocument.MainDocumentPart.EndnotesPart.GetXDocument();
if (newEndnotes == null)
{
if (newDocument.MainDocumentPart.EndnotesPart != null)
{
newEndnotes = newDocument.MainDocumentPart.EndnotesPart.GetXDocument();
newEndnotes.Declaration.Standalone = "yes";
newEndnotes.Declaration.Encoding = "UTF-8";
var ids = newEndnotes.Root
.Elements(W.endnote)
.Select(f => (int)f.Attribute(W.id));
if (ids.Any())
number = ids.Max() + 1;
}
else
{
newDocument.MainDocumentPart.AddNewPart<EndnotesPart>();
newEndnotes = newDocument.MainDocumentPart.EndnotesPart.GetXDocument();
newEndnotes.Declaration.Standalone = "yes";
newEndnotes.Declaration.Encoding = "UTF-8";
newEndnotes.Add(new XElement(W.endnotes, NamespaceAttributes));
}
}
string id = (string)endnote.Attribute(W.id);
XElement element = oldEndnotes.Descendants()
.Elements(W.endnote)
.Where(p => ((string)p.Attribute(W.id)) == id)
.FirstOrDefault();
if (element != null)
{
XElement newElement = new XElement(element);
newElement.Attribute(W.id).Value = number.ToString();
newEndnotes.Root.Add(newElement);
endnote.Attribute(W.id).Value = number.ToString();
number++;
}
}
}
public static void FixRanges(XDocument sourceDocument, IEnumerable<XElement> newContent)
{
FixRange(sourceDocument,
newContent,
W.commentRangeStart,
W.commentRangeEnd,
W.id,
W.commentReference);
FixRange(sourceDocument,
newContent,
W.bookmarkStart,
W.bookmarkEnd,
W.id,
null);
FixRange(sourceDocument,
newContent,
W.permStart,
W.permEnd,
W.id,
null);
FixRange(sourceDocument,
newContent,
W.moveFromRangeStart,
W.moveFromRangeEnd,
W.id,
null);
FixRange(sourceDocument,
newContent,
W.moveToRangeStart,
W.moveToRangeEnd,
W.id,
null);
DeleteUnmatchedRange(sourceDocument,
newContent,
W.moveFromRangeStart,
W.moveFromRangeEnd,
W.moveToRangeStart,
W.name,
W.id);
DeleteUnmatchedRange(sourceDocument,
newContent,
W.moveToRangeStart,
W.moveToRangeEnd,
W.moveFromRangeStart,
W.name,
W.id);
}
private static void AddAtBeginning(IEnumerable<XElement> newContent, XElement contentToAdd)
{
if (newContent.First().Element(W.pPr) != null)
newContent.First().Element(W.pPr).AddAfterSelf(contentToAdd);
else
newContent.First().AddFirst(new XElement(contentToAdd));
}
private static void AddAtEnd(IEnumerable<XElement> newContent, XElement contentToAdd)
{
if (newContent.Last().Element(W.pPr) != null)
newContent.Last().Element(W.pPr).AddAfterSelf(new XElement(contentToAdd));
else
newContent.Last().Add(new XElement(contentToAdd));
}
// If the set of paragraphs from sourceDocument don't have a complete start/end for bookmarks,
// comments, etc., then this adds them to the paragraph. Note that this adds them to
// sourceDocument, and is impure.
private static void FixRange(XDocument sourceDocument, IEnumerable<XElement> newContent,
XName startElement, XName endElement, XName idAttribute, XName refElement)
{
foreach (XElement start in newContent.DescendantsAndSelf(startElement))
{
string rangeId = start.Attribute(idAttribute).Value;
if (newContent
.DescendantsAndSelf(endElement)
.Where(e => e.Attribute(idAttribute).Value == rangeId)
.Count() == 0)
{
XElement end = sourceDocument
.Descendants(endElement)
.Where(o => o.Attribute(idAttribute).Value == rangeId)
.FirstOrDefault();
if (end != null)
{
AddAtEnd(newContent, new XElement(end));
if (refElement != null)
{
XElement newRef = new XElement(refElement, new XAttribute(idAttribute, rangeId));
AddAtEnd(newContent, new XElement(newRef));
}
}
}
}
foreach (XElement end in newContent.Elements(endElement))
{
string rangeId = end.Attribute(idAttribute).Value;
if (newContent
.DescendantsAndSelf(startElement)
.Where(s => s.Attribute(idAttribute).Value == rangeId)
.Count() == 0)
{
XElement start = sourceDocument
.Descendants(startElement)
.Where(o => o.Attribute(idAttribute).Value == rangeId)
.FirstOrDefault();
if (start != null)
AddAtBeginning(newContent, new XElement(start));
}
}
}
private static void DeleteUnmatchedRange(XDocument sourceDocument, IEnumerable<XElement> newContent,
XName startElement, XName endElement, XName matchTo, XName matchAttr, XName idAttr)
{
List<string> deleteList = new List<string>();
foreach (XElement start in newContent.Elements(startElement))
{
string id = start.Attribute(matchAttr).Value;
if (!newContent.Elements(matchTo).Where(n => n.Attribute(matchAttr).Value == id).Any())
deleteList.Add(start.Attribute(idAttr).Value);
}
foreach (string item in deleteList)
{
newContent.Elements(startElement).Where(n => n.Attribute(idAttr).Value == item).Remove();
newContent.Elements(endElement).Where(n => n.Attribute(idAttr).Value == item).Remove();
newContent.Where(p => p.Name == startElement && p.Attribute(idAttr).Value == item).Remove();
newContent.Where(p => p.Name == endElement && p.Attribute(idAttr).Value == item).Remove();
}
}
private static void CopyFootnotes(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument,
IEnumerable<XElement> newContent, List<ImageData> images)
{
int number = 0;
XDocument oldFootnotes = null;
XDocument newFootnotes = null;
foreach (XElement footnote in newContent.DescendantsAndSelf(W.footnoteReference))
{
if (oldFootnotes == null)
oldFootnotes = sourceDocument.MainDocumentPart.FootnotesPart.GetXDocument();
if (newFootnotes == null)
{
if (newDocument.MainDocumentPart.FootnotesPart != null)
{
newFootnotes = newDocument.MainDocumentPart.FootnotesPart.GetXDocument();
var ids = newFootnotes
.Root
.Elements(W.footnote)
.Select(f => (int)f.Attribute(W.id));
if (ids.Any())
number = ids.Max() + 1;
}
else
{
newDocument.MainDocumentPart.AddNewPart<FootnotesPart>();
newFootnotes = newDocument.MainDocumentPart.FootnotesPart.GetXDocument();
newFootnotes.Declaration.Standalone = "yes";
newFootnotes.Declaration.Encoding = "UTF-8";
newFootnotes.Add(new XElement(W.footnotes, NamespaceAttributes));
}
}
string id = (string)footnote.Attribute(W.id);
XElement element = oldFootnotes
.Descendants()
.Elements(W.footnote)
.Where(p => ((string)p.Attribute(W.id)) == id)
.FirstOrDefault();
if (element != null)
{
XElement newElement = new XElement(element);
newElement.Attribute(W.id).Value = number.ToString();
newFootnotes.Root.Add(newElement);
footnote.Attribute(W.id).Value = number.ToString();
number++;
}
}
if (sourceDocument.MainDocumentPart.FootnotesPart != null &&
newDocument.MainDocumentPart.FootnotesPart != null)
{
AddRelationships(sourceDocument.MainDocumentPart.FootnotesPart,
newDocument.MainDocumentPart.FootnotesPart,
new[] { newDocument.MainDocumentPart.FootnotesPart.GetXDocument().Root });
CopyRelatedPartsForContentParts(sourceDocument.MainDocumentPart.FootnotesPart,
newDocument.MainDocumentPart.FootnotesPart,
new[] { newDocument.MainDocumentPart.FootnotesPart.GetXDocument().Root }, images);
}
}
private static void CopyEndnotes(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument,
IEnumerable<XElement> newContent, List<ImageData> images)
{
int number = 0;
XDocument oldEndnotes = null;
XDocument newEndnotes = null;
foreach (XElement endnote in newContent.DescendantsAndSelf(W.endnoteReference))
{
if (oldEndnotes == null)
oldEndnotes = sourceDocument.MainDocumentPart.EndnotesPart.GetXDocument();
if (newEndnotes == null)
{
if (newDocument.MainDocumentPart.EndnotesPart != null)
{
newEndnotes = newDocument
.MainDocumentPart
.EndnotesPart
.GetXDocument();
var ids = newEndnotes
.Root
.Elements(W.endnote)
.Select(f => (int)f.Attribute(W.id));
if (ids.Any())
number = ids.Max() + 1;
}
else
{
newDocument.MainDocumentPart.AddNewPart<EndnotesPart>();
newEndnotes = newDocument.MainDocumentPart.EndnotesPart.GetXDocument();
newEndnotes.Declaration.Standalone = "yes";
newEndnotes.Declaration.Encoding = "UTF-8";
newEndnotes.Add(new XElement(W.endnotes, NamespaceAttributes));
}
}
string id = (string)endnote.Attribute(W.id);
XElement element = oldEndnotes
.Descendants()
.Elements(W.endnote)
.Where(p => ((string)p.Attribute(W.id)) == id)
.First();
XElement newElement = new XElement(element);
newElement.Attribute(W.id).Value = number.ToString();
newEndnotes.Root.Add(newElement);
endnote.Attribute(W.id).Value = number.ToString();
number++;
}
if (sourceDocument.MainDocumentPart.EndnotesPart != null &&
newDocument.MainDocumentPart.EndnotesPart != null)
{
AddRelationships(sourceDocument.MainDocumentPart.EndnotesPart,
newDocument.MainDocumentPart.EndnotesPart,
new[] { newDocument.MainDocumentPart.EndnotesPart.GetXDocument().Root });
CopyRelatedPartsForContentParts(sourceDocument.MainDocumentPart.EndnotesPart,
newDocument.MainDocumentPart.EndnotesPart,
new[] { newDocument.MainDocumentPart.EndnotesPart.GetXDocument().Root }, images);
}
}
// General function for handling images that tries to use an existing image if they are the same
private static ImageData ManageImageCopy(ImagePart oldImage, OpenXmlPart newContentPart, List<ImageData> images)
{
ImageData oldImageData = new ImageData(oldImage);
foreach (ImageData item in images)
{
if (newContentPart != item.ImagePart)
continue;
if (item.Compare(oldImageData))
return item;
}
images.Add(oldImageData);
return oldImageData;
}
private static XAttribute[] NamespaceAttributes =
{
new XAttribute(XNamespace.Xmlns + "wpc", WPC.wpc),
new XAttribute(XNamespace.Xmlns + "mc", MC.mc),
new XAttribute(XNamespace.Xmlns + "o", O.o),
new XAttribute(XNamespace.Xmlns + "r", R.r),
new XAttribute(XNamespace.Xmlns + "m", M.m),
new XAttribute(XNamespace.Xmlns + "v", VML.vml),
new XAttribute(XNamespace.Xmlns + "wp14", WP14.wp14),
new XAttribute(XNamespace.Xmlns + "wp", WP.wp),
new XAttribute(XNamespace.Xmlns + "w10", W10.w10),
new XAttribute(XNamespace.Xmlns + "w", W.w),
new XAttribute(XNamespace.Xmlns + "w14", W14.w14),
new XAttribute(XNamespace.Xmlns + "wpg", WPG.wpg),
new XAttribute(XNamespace.Xmlns + "wpi", WPI.wpi),
new XAttribute(XNamespace.Xmlns + "wne", WNE.wne),
new XAttribute(XNamespace.Xmlns + "wps", WPS.wps),
new XAttribute(MC.Ignorable, "w14 wp14"),
};
}
public class DocumentBuilderException : Exception
{
public DocumentBuilderException(string message) : base(message) { }
}
public class DocumentBuilderInternalException : Exception
{
public DocumentBuilderInternalException(string message) : base(message) { }
}
}