blob: 7d91e0961b1757eaf4615f369762992715383d59 [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
***************************************************************************/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Validation;
using DocumentFormat.OpenXml.Wordprocessing;
using OpenXmlPowerTools;
using Xunit;
#if !ELIDE_XUNIT_TESTS
namespace OxPt
{
public class DbTests
{
[Fact]
public void DB001_DocumentBuilderKeepSections()
{
string name = "DB001-Sections.docx";
FileInfo sourceDocx = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, name));
List<Source> sources = null;
sources = new List<Source>()
{
new Source(new WmlDocument(sourceDocx.FullName), true),
};
var processedDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-processed-by-DocumentBuilder.docx")));
DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName);
}
[Fact]
public void DB002_DocumentBuilderKeepSectionsDiscardHeaders()
{
FileInfo source1Docx = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB002-Sections-With-Headers.docx"));
FileInfo source2Docx = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB002-Landscape-Section.docx"));
List<Source> sources = null;
sources = new List<Source>()
{
new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true },
new Source(new WmlDocument(source2Docx.FullName)) { KeepSections = true, DiscardHeadersAndFootersInKeptSections = true },
new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true },
};
var processedDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB002-Keep-Sections-Discard-Headers-And-Footers.docx"));
DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName);
}
[Fact]
public void DB003_DocumentBuilderOnlyDefaultHeader()
{
FileInfo source1Docx = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB003-Only-Default-Header.docx"));
FileInfo source2Docx = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB002-Landscape-Section.docx"));
List<Source> sources = null;
sources = new List<Source>()
{
new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true },
new Source(new WmlDocument(source2Docx.FullName)) { KeepSections = true, DiscardHeadersAndFootersInKeptSections = true },
new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true },
};
var processedDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB003-Only-Default-Header.docx"));
DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName);
}
[Fact]
public void DB004_DocumentBuilderNoHeaders()
{
FileInfo source1Docx = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB004-No-Headers.docx"));
FileInfo source2Docx = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB002-Landscape-Section.docx"));
List<Source> sources = null;
sources = new List<Source>()
{
new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true },
new Source(new WmlDocument(source2Docx.FullName)) { KeepSections = true, DiscardHeadersAndFootersInKeptSections = true },
new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true },
};
var processedDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB003-Only-Default-Header.docx"));
DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName);
}
[Fact]
public void DB005_HeadersWithRefsToImages()
{
FileInfo source1Docx = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB005-Headers-With-Images.docx"));
FileInfo source2Docx = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB002-Landscape-Section.docx"));
List<Source> sources = null;
sources = new List<Source>()
{
new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true },
new Source(new WmlDocument(source2Docx.FullName)) { KeepSections = true, DiscardHeadersAndFootersInKeptSections = true },
new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true },
};
var processedDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB005.docx"));
DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName);
}
[Fact]
public void DB006_Example_DocumentBuilder01()
{
FileInfo source1 = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB006-Source1.docx"));
FileInfo source2 = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB006-Source2.docx"));
FileInfo source3 = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB006-Source3.docx"));
List<Source> sources = null;
// Create new document from 10 paragraphs starting at paragraph 5 of Source1.docx
sources = new List<Source>()
{
new Source(new WmlDocument(source1.FullName), 5, 10, true),
};
var out1 = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB006-Out1.docx"));
DocumentBuilder.BuildDocument(sources, out1.FullName);
Validate(out1);
// Create new document from paragraph 1, and paragraphs 5 through end of Source3.docx.
// This effectively 'deletes' paragraphs 2-4
sources = new List<Source>()
{
new Source(new WmlDocument(source3.FullName), 0, 1, false),
new Source(new WmlDocument(source3.FullName), 4, false),
};
var out2 = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB006-Out2.docx"));
DocumentBuilder.BuildDocument(sources, out2.FullName);
Validate(out2);
// Create a new document that consists of the entirety of Source1.docx and Source2.docx. Use
// the section information (headings and footers) from source1.
sources = new List<Source>()
{
new Source(new WmlDocument(source1.FullName), true),
new Source(new WmlDocument(source2.FullName), false),
};
var out3 = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB006-Out3.docx"));
DocumentBuilder.BuildDocument(sources, out3.FullName);
Validate(out3);
// Create a new document that consists of the entirety of Source1.docx and Source2.docx. Use
// the section information (headings and footers) from source2.
sources = new List<Source>()
{
new Source(new WmlDocument(source1.FullName), false),
new Source(new WmlDocument(source2.FullName), true),
};
var out4 = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB006-Out4.docx"));
DocumentBuilder.BuildDocument(sources, out4.FullName);
Validate(out4);
// Create a new document that consists of the first 5 paragraphs of Source1.docx and the first
// five paragraphs of Source2.docx. This example returns a new WmlDocument, when you then can
// serialize to a SharePoint document library, or use in some other interesting scenario.
sources = new List<Source>()
{
new Source(new WmlDocument(source1.FullName), 0, 5, false),
new Source(new WmlDocument(source2.FullName), 0, 5, true),
};
WmlDocument wmlOut5 = DocumentBuilder.BuildDocument(sources);
var out5 = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB006-Out5.docx"));
wmlOut5.SaveAs(out5.FullName); // save it to the file system, but we could just as easily done something
// else with it.
Validate(out5);
}
[Fact]
public void DB007_Example_DocumentBuilder02_WhitePaper()
{
FileInfo spec = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB007-Spec.docx"));
FileInfo whitePaper = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB007-WhitePaper.docx"));
FileInfo paperAbstract = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB007-Abstract.docx"));
FileInfo authorBio = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB007-AuthorBiography.docx"));
List<Source> sources = null;
sources = new List<Source>()
{
new Source(new WmlDocument(whitePaper.FullName), 0, 1, true),
new Source(new WmlDocument(paperAbstract.FullName), false),
new Source(new WmlDocument(authorBio.FullName), false),
new Source(new WmlDocument(whitePaper.FullName), 1, false),
};
var assembledPaper = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB007-AssembledPaper.docx"));
DocumentBuilder.BuildDocument(sources, assembledPaper.FullName);
Validate(assembledPaper);
}
[Fact]
public void DB008_DeleteParasWithGivenStyle()
{
FileInfo notes = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB007-Notes.docx"));
List<Source> sources = null;
// Delete all paragraphs with a specific style.
using (WordprocessingDocument doc = WordprocessingDocument.Open(notes.FullName, false))
{
sources = doc
.MainDocumentPart
.GetXDocument()
.Root
.Element(W.body)
.Elements()
.Select((p, i) => new
{
Paragraph = p,
Index = i,
})
.GroupAdjacent(pi => (string)pi.Paragraph
.Elements(W.pPr)
.Elements(W.pStyle)
.Attributes(W.val)
.FirstOrDefault() != "Note")
.Where(g => g.Key == true)
.Select(g => new Source(
new WmlDocument(notes.FullName), g.First().Index,
g.Last().Index - g.First().Index + 1, true))
.ToList();
}
var newNotes = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB008-NewNotes.docx"));
DocumentBuilder.BuildDocument(sources, newNotes.FullName);
Validate(newNotes);
}
[Theory]
[InlineData("DB009-00010", "DB/HeadersFooters/Src/Content-Controls.docx", "DB/HeadersFooters/Dest/Fax.docx", "Templafy")]
[InlineData("DB009-00020", "DB/HeadersFooters/Src/Letterhead.docx", "DB/HeadersFooters/Dest/Fax.docx", "Templafy")]
[InlineData("DB009-00030", "DB/HeadersFooters/Src/Letterhead-with-Watermark.docx", "DB/HeadersFooters/Dest/Fax.docx", "Templafy")]
[InlineData("DB009-00040", "DB/HeadersFooters/Src/Logo.docx", "DB/HeadersFooters/Dest/Fax.docx", "Templafy")]
[InlineData("DB009-00050", "DB/HeadersFooters/Src/Watermark-1.docx", "DB/HeadersFooters/Dest/Fax.docx", "Templafy")]
[InlineData("DB009-00060", "DB/HeadersFooters/Src/Watermark-2.docx", "DB/HeadersFooters/Dest/Fax.docx", "Templafy")]
[InlineData("DB009-00070", "DB/HeadersFooters/Src/Disclaimer.docx", "DB/HeadersFooters/Dest/Fax.docx", "Templafy")]
[InlineData("DB009-00080", "DB/HeadersFooters/Src/Footer.docx", "DB/HeadersFooters/Dest/Fax.docx", "Templafy")]
[InlineData("DB009-00110", "DB/HeadersFooters/Src/Content-Controls.docx", "DB/HeadersFooters/Dest/Letter.docx", "Templafy")]
[InlineData("DB009-00120", "DB/HeadersFooters/Src/Letterhead.docx", "DB/HeadersFooters/Dest/Letter.docx", "Templafy")]
[InlineData("DB009-00130", "DB/HeadersFooters/Src/Letterhead-with-Watermark.docx", "DB/HeadersFooters/Dest/Letter.docx", "Templafy")]
[InlineData("DB009-00140", "DB/HeadersFooters/Src/Logo.docx", "DB/HeadersFooters/Dest/Letter.docx", "Templafy")]
[InlineData("DB009-00150", "DB/HeadersFooters/Src/Watermark-1.docx", "DB/HeadersFooters/Dest/Letter.docx", "Templafy")]
[InlineData("DB009-00160", "DB/HeadersFooters/Src/Watermark-2.docx", "DB/HeadersFooters/Dest/Letter.docx", "Templafy")]
[InlineData("DB009-00170", "DB/HeadersFooters/Src/Disclaimer.docx", "DB/HeadersFooters/Dest/Letter.docx", "Templafy")]
[InlineData("DB009-00180", "DB/HeadersFooters/Src/Footer.docx", "DB/HeadersFooters/Dest/Letter.docx", "Templafy")]
public void DB009_ImportIntoHeadersFooters(string testId, string src, string dest, string insertId)
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Load the source document
FileInfo sourceDocxFi = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, src));
WmlDocument wmlSourceDocument = new WmlDocument(sourceDocxFi.FullName);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Load the dest document
FileInfo destDocxFi = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, dest));
WmlDocument wmlDestDocument = new WmlDocument(destDocxFi.FullName);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Create the dir for the test
var rootTempDir = TestUtil.TempDir;
var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId));
if (thisTestTempDir.Exists)
Assert.True(false, "Duplicate test id: " + testId);
else
thisTestTempDir.Create();
var tempDirFullName = thisTestTempDir.FullName;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copy src DOCX to temp directory, for ease of review
while (true)
{
try
{
////////// CODE TO REPEAT UNTIL SUCCESS //////////
var sourceDocxCopiedToDestFileName = new FileInfo(Path.Combine(tempDirFullName, sourceDocxFi.Name));
if (!sourceDocxCopiedToDestFileName.Exists)
wmlSourceDocument.SaveAs(sourceDocxCopiedToDestFileName.FullName);
//////////////////////////////////////////////////
break;
}
catch (IOException)
{
System.Threading.Thread.Sleep(50);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copy dest DOCX to temp directory, for ease of review
while (true)
{
try
{
////////// CODE TO REPEAT UNTIL SUCCESS //////////
var destDocxCopiedToDestFileName = new FileInfo(Path.Combine(tempDirFullName, destDocxFi.Name));
if (!destDocxCopiedToDestFileName.Exists)
wmlDestDocument.SaveAs(destDocxCopiedToDestFileName.FullName);
//////////////////////////////////////////////////
break;
}
catch (IOException)
{
System.Threading.Thread.Sleep(50);
}
}
List<Source> sources = new List<Source>()
{
new Source(wmlDestDocument),
new Source(wmlSourceDocument, insertId),
};
var outFi = new FileInfo(Path.Combine(tempDirFullName, "Output.docx"));
DocumentBuilder.BuildDocument(sources, outFi.FullName);
Validate(outFi);
}
#if false
[Theory]
[InlineData("DB999-00010", "DBTEMP/03DE57384B87AA6C2A3BDE87DDDD7F880DC55E.docx", true)]
[InlineData("DB999-00020", "DBTEMP/0D3DEB27ED036116466BED616B2056CDD2783A.docx", false)]
[InlineData("DB999-00030", "DBTEMP/421628B3F4B03B123CA8EDDA5009E449F5F47D.docx", false)]
[InlineData("DB999-00040", "DBTEMP/58D4E8661C7F44FE33392B89B0A3CB0AF1684F.docx", false)]
[InlineData("DB999-00050", "DBTEMP/67EBCA627D6D584CAB3EB1DF2E4C3982023DEE.docx", true)]
[InlineData("DB999-00060", "DBTEMP/A529643E2FC3E2C682FA86DEE0A1B3064DCEE0.docx", false)]
[InlineData("DB999-00070", "DBTEMP/E794032F0422B440D3C564F0E09E395519127D.docx", false)]
[InlineData("DB999-00080", "DBTEMP/1FF1ADF30B24978E9449754459C743D3BC67ED.docx", false)]
[InlineData("DB999-00090", "DBTEMP/5E685927DA2FECB88DE9CAF0BECEC88BC118A7.docx", false)]
[InlineData("DB999-00100", "DBTEMP/6427BCF5C18B55D627B95F3E14924050628C5B.docx", false)]
[InlineData("DB999-00110", "DBTEMP/91691E0D3AB89E9927A2BAC5D385BB6277648F.docx", false)]
[InlineData("DB999-00120", "DBTEMP/9533BC5710190EA01DA86D29CD06880395C4AF.docx", false)]
[InlineData("DB999-00130", "DBTEMP/E9CD8C556AA52CA7D31DADB51A201EEF580AA8.docx", false)]
[InlineData("DB999-00140", "DBTEMP/21D3CE149C30B791F9A8BE092828E1469A9047.docx", false)]
[InlineData("DB999-00150", "DBTEMP/AC0CB8CE43A7ECAE995BB542D4FB1060FB835B.docx", false)]
[InlineData("DB999-00160", "DBTEMP/C61F69B52EC8B0E2C784C932B26F3C613AE671.docx", false)]
[InlineData("DB999-00170", "DBTEMP/1DF04A9130B3EF858ACA6837A706A429904973.dotm", false)]
[InlineData("DB999-00180", "DBTEMP/6E9F26B708DE6076B2C731B97AAA5288D839AB.docm", false)]
[InlineData("DB999-00190", "DBTEMP/A6649726EA0BD7545932DDD51403D83E4D5917.docx", false)]
[InlineData("DB999-00200", "DBTEMP/C8AE8AD0A73F24B7CFCFD11918B337CF2B90C9.docx", false)]
[InlineData("DB999-00210", "DBTEMP/BC46A7FBB212EFD10878A39D91AE3ECAADDAB0.docx", false)]
[InlineData("DB999-00220", "DBTEMP/B6F0E938B508676B322C47F3E0E29C8D786DB2.docm", false)]
[InlineData("DB999-00230", "DBTEMP/D4D8694A51DECA243AF748B3232BE565EEE19D.docx", false)]
[InlineData("DB999-00240", "DBTEMP/F20B3CE72BF635462E22BA3CA81CA9D57F6FEB.docx", false)]
[InlineData("DB999-00250", "DBTEMP/74ED106FF88C1B195D97C466E00BECCB636A03.docx", false)]
[InlineData("DB999-00260", "DBTEMP/4421A4B7B6ECC2813070309AA2D86C4BCA4AEF.docx", false)]
[InlineData("DB999-00270", "DBTEMP/BC7D91B993807518F3D430B7C6592AFD6BD91C.docx", false)]
[InlineData("DB999-00280", "DBTEMP/3006E76FE65E8A25A91ED204EEBEE6D6D62A44.docx", false)]
[InlineData("DB999-00290", "DBTEMP/6254B74778BFFCD1799F4F2B3B01C2025AABB2.docx", false)]
[InlineData("DB999-00300", "DBTEMP/5AD0A0BD99676B268D8E7C1F69238FB9B6149E.docx", false)]
[InlineData("DB999-00310", "DBTEMP/2D58495ECCF30ED9507B707C689CA9C9D4B049.docx", false)]
public void DB999_DocumentBuilder(string testId, string src, bool shouldThrow)
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Load the source document
FileInfo sourceDocxFi = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, src));
WmlDocument wmlSourceDocument = new WmlDocument(sourceDocxFi.FullName);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Create the dir for the test
var rootTempDir = TestUtil.TempDir;
var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId));
if (thisTestTempDir.Exists)
Assert.True(false, "Duplicate test id: " + testId);
else
thisTestTempDir.Create();
var tempDirFullName = thisTestTempDir.FullName;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copy src DOCX to temp directory, for ease of review
while (true)
{
try
{
////////// CODE TO REPEAT UNTIL SUCCESS //////////
var sourceDocxCopiedToDestFileName = new FileInfo(Path.Combine(tempDirFullName, sourceDocxFi.Name));
if (!sourceDocxCopiedToDestFileName.Exists)
wmlSourceDocument.SaveAs(sourceDocxCopiedToDestFileName.FullName);
//////////////////////////////////////////////////
break;
}
catch (IOException)
{
System.Threading.Thread.Sleep(50);
}
}
List<string> expectedErrors;
using (MemoryStream ms = new MemoryStream())
{
ms.Write(wmlSourceDocument.DocumentByteArray, 0, wmlSourceDocument.DocumentByteArray.Length);
using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, false))
{
OpenXmlValidator validator = new OpenXmlValidator();
expectedErrors = validator.Validate(wDoc)
.Select(e => e.Description)
.Distinct()
.ToList();
}
}
foreach (var item in s_ExpectedErrors)
expectedErrors.Add(item);
List<Source> sources = new List<Source>()
{
new Source(wmlSourceDocument, true),
};
var outFi = new FileInfo(Path.Combine(tempDirFullName, "Output.docx"));
if (shouldThrow)
{
Assert.Throws<DocumentBuilderException>(() => DocumentBuilder.BuildDocument(sources, outFi.FullName));
}
else
{
var outWml = DocumentBuilder.BuildDocument(sources);
outWml.SaveAs(outFi.FullName);
using (MemoryStream ms = new MemoryStream())
{
ms.Write(outWml.DocumentByteArray, 0, outWml.DocumentByteArray.Length);
using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, false))
{
OpenXmlValidator validator = new OpenXmlValidator();
var errors = validator.Validate(wDoc).Where(e =>
{
var str = e.Description;
foreach (var ee in expectedErrors)
{
if (str.Contains(ee))
return false;
}
return true;
});
if (errors.Count() != 0)
{
var message = errors.Select(e => e.Description + Environment.NewLine).StringConcatenate();
Assert.True(false, message);
}
}
}
}
}
#endif
private class DocumentInfo
{
public int DocumentNumber;
public int Start;
public int Count;
}
[Fact]
public void DB009_ShredDocument()
{
FileInfo spec = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB007-Spec.docx"));
// Shred a document into multiple parts for each section
List<DocumentInfo> documentList;
using (WordprocessingDocument doc = WordprocessingDocument.Open(spec.FullName, false))
{
var sectionCounts = doc
.MainDocumentPart
.GetXDocument()
.Root
.Element(W.body)
.Elements()
.Rollup(0, (pi, last) => (string)pi
.Elements(W.pPr)
.Elements(W.pStyle)
.Attributes(W.val)
.FirstOrDefault() == "Heading1" ? last + 1 : last);
var beforeZipped = doc
.MainDocumentPart
.GetXDocument()
.Root
.Element(W.body)
.Elements()
.Select((p, i) => new
{
Paragraph = p,
Index = i,
});
var zipped = PtExtensions.PtZip(beforeZipped, sectionCounts, (pi, sc) => new
{
Paragraph = pi.Paragraph,
Index = pi.Index,
SectionIndex = sc,
});
documentList = zipped
.GroupAdjacent(p => p.SectionIndex)
.Select(g => new DocumentInfo
{
DocumentNumber = g.Key,
Start = g.First().Index,
Count = g.Last().Index - g.First().Index + 1,
})
.ToList();
}
foreach (var doc in documentList)
{
string fileName = String.Format("DB009-Section{0:000}.docx", doc.DocumentNumber);
var fiSection = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, fileName));
List<Source> documentSource = new List<Source> {
new Source(new WmlDocument(spec.FullName), doc.Start, doc.Count, true)
};
DocumentBuilder.BuildDocument(documentSource, fiSection.FullName);
Validate(fiSection);
}
// Re-assemble the parts into a single document.
List<Source> sources = TestUtil.TempDir
.GetFiles("DB009-Section*.docx")
.Select(d => new Source(new WmlDocument(d.FullName), true))
.ToList();
var fiReassembled = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB009-Reassembled.docx"));
DocumentBuilder.BuildDocument(sources, fiReassembled.FullName);
using (WordprocessingDocument doc = WordprocessingDocument.Open(fiReassembled.FullName, true))
{
ReferenceAdder.AddToc(doc, "/w:document/w:body/w:p[1]",
@"TOC \o '1-3' \h \z \u", null, null);
}
Validate(fiReassembled);
}
[Fact]
public void DB010_InsertUsingInsertId()
{
FileInfo front = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB010-FrontMatter.docx"));
FileInfo insert01 = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB010-Insert-01.docx"));
FileInfo insert02 = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB010-Insert-02.docx"));
FileInfo template = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB010-Template.docx"));
WmlDocument doc1 = new WmlDocument(template.FullName);
using (MemoryStream mem = new MemoryStream())
{
mem.Write(doc1.DocumentByteArray, 0, doc1.DocumentByteArray.Length);
using (WordprocessingDocument doc = WordprocessingDocument.Open(mem, true))
{
XDocument xDoc = doc.MainDocumentPart.GetXDocument();
XElement frontMatterPara = xDoc.Root.Descendants(W.txbxContent).Elements(W.p).FirstOrDefault();
frontMatterPara.ReplaceWith(
new XElement(PtOpenXml.Insert,
new XAttribute("Id", "Front")));
XElement tbl = xDoc.Root.Element(W.body).Elements(W.tbl).FirstOrDefault();
XElement firstCell = tbl.Descendants(W.tr).First().Descendants(W.p).First();
firstCell.ReplaceWith(
new XElement(PtOpenXml.Insert,
new XAttribute("Id", "Liz")));
XElement secondCell = tbl.Descendants(W.tr).Skip(1).First().Descendants(W.p).First();
secondCell.ReplaceWith(
new XElement(PtOpenXml.Insert,
new XAttribute("Id", "Eric")));
doc.MainDocumentPart.PutXDocument();
}
doc1.DocumentByteArray = mem.ToArray();
}
List<Source> sources = new List<Source>()
{
new Source(doc1, true),
new Source(new WmlDocument(insert01.FullName), "Liz"),
new Source(new WmlDocument(insert02.FullName), "Eric"),
new Source(new WmlDocument(front.FullName), "Front"),
};
var out1 = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB010-Inserted.docx"));
DocumentBuilder.BuildDocument(sources, out1.FullName);
Validate(out1);
}
[Fact]
public void DB011_BodyAndHeaderWithShapes()
{
// Both of theses documents have a shape with a DocProperties ID of 1.
FileInfo source1 = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB011-Header-With-Shape.docx"));
FileInfo source2 = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB011-Body-With-Shape.docx"));
List<Source> sources = null;
sources = new List<Source>()
{
new Source(new WmlDocument(source1.FullName)),
new Source(new WmlDocument(source2.FullName)),
};
var processedDestDocx =
new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB011-Body-And-Header-With-Shapes.docx"));
DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName);
Validate(processedDestDocx);
ValidateUniqueDocPrIds(processedDestDocx);
}
[Fact]
public void DB012_NumberingsWithSameAbstractNumbering()
{
// This document has three numbering definitions that use the same abstract numbering definition.
string name = "DB012-Lists-With-Different-Numberings.docx";
FileInfo sourceDocx = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, name));
List<Source> sources = null;
sources = new List<Source>()
{
new Source(new WmlDocument(sourceDocx.FullName)),
};
var processedDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName,
sourceDocx.Name.Replace(".docx", "-processed-by-DocumentBuilder.docx")));
DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName);
using (WordprocessingDocument wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false))
{
var numberingRoot = wDoc.MainDocumentPart.NumberingDefinitionsPart.GetXDocument().Root;
Assert.Equal(3, numberingRoot.Elements(W.num).Count());
}
}
[Fact]
public void DB013a_LocalizedStyleIds_Heading()
{
// Each of these documents have changed the font color of the Heading 1 style, one to red, the other to green.
// One of the documents were created with English as the Word display language, the other with Danish as the language.
FileInfo source1 =
new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB013a-Red-Heading1-English.docx"));
FileInfo source2 = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName,
"DB013a-Green-Heading1-Danish.docx"));
List<Source> sources = null;
sources = new List<Source>()
{
new Source(new WmlDocument(source1.FullName)),
new Source(new WmlDocument(source2.FullName)),
};
var processedDestDocx =
new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB013a-Colored-Heading1.docx"));
DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName);
using (WordprocessingDocument wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false))
{
var styles = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument().Root.Elements(W.style).ToArray();
Assert.Equal(1, styles.Count(s => s.Element(W.name).Attribute(W.val).Value == "heading 1"));
var styleIds = new HashSet<string>(styles.Select(s => s.Attribute(W.styleId).Value));
var paragraphStylesIds = new HashSet<string>(wDoc.MainDocumentPart.GetXDocument()
.Descendants(W.pStyle)
.Select(p => p.Attribute(W.val).Value));
Assert.Subset(styleIds, paragraphStylesIds);
}
}
[Fact]
public void DB013b_LocalizedStyleIds_List()
{
// Each of these documents have changed the font color of the List Paragraph style, one to orange, the other to blue.
// One of the documents were created with English as the Word display language, the other with Danish as the language.
FileInfo source1 =
new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB013b-Orange-List-Danish.docx"));
FileInfo source2 = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName,
"DB013b-Blue-List-English.docx"));
List<Source> sources = null;
sources = new List<Source>()
{
new Source(new WmlDocument(source1.FullName)),
new Source(new WmlDocument(source2.FullName)),
};
var processedDestDocx =
new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB013b-Colored-List.docx"));
DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName);
using (WordprocessingDocument wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false))
{
var styles = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument().Root.Elements(W.style).ToArray();
Assert.Equal(1, styles.Count(s => s.Element(W.name).Attribute(W.val).Value == "List Paragraph"));
var styleIds = new HashSet<string>(styles.Select(s => s.Attribute(W.styleId).Value));
var paragraphStylesIds = new HashSet<string>(wDoc.MainDocumentPart.GetXDocument()
.Descendants(W.pStyle)
.Select(p => p.Attribute(W.val).Value));
Assert.Subset(styleIds, paragraphStylesIds);
}
}
[Fact]
public void DB014_KeepWebExtensions()
{
FileInfo source = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, "DB014-WebExtensions.docx"));
List<Source> sources = null;
sources = new List<Source>()
{
new Source(new WmlDocument(source.FullName)),
};
var processedDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB014-WebExtensions.docx"));
DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName);
Validate(processedDestDocx);
using (WordprocessingDocument wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false))
{
Assert.NotNull(wDoc.WebExTaskpanesPart);
Assert.Equal(2, wDoc.WebExTaskpanesPart.Taskpanes.ChildElements.Count);
Assert.Equal(2, wDoc.WebExTaskpanesPart.WebExtensionParts.Count());
}
}
private void Validate(FileInfo fi)
{
using (WordprocessingDocument wDoc = WordprocessingDocument.Open(fi.FullName, true))
{
OpenXmlValidator v = new OpenXmlValidator();
var errors = v.Validate(wDoc).Where(ve =>
{
var found = s_ExpectedErrors.Any(xe => ve.Description.Contains(xe));
return !found;
});
if (errors.Count() != 0)
{
StringBuilder sb = new StringBuilder();
foreach (var item in errors)
{
sb.Append(item.Description).Append(Environment.NewLine);
}
var s = sb.ToString();
Assert.True(false, s);
}
}
}
private static List<string> s_ExpectedErrors = new List<string>()
{
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:evenHBand' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:evenVBand' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:firstColumn' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:firstRow' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:firstRowFirstColumn' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:firstRowLastColumn' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:lastColumn' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:lastRow' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:lastRowFirstColumn' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:lastRowLastColumn' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:noHBand' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:noVBand' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:oddHBand' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:oddVBand' attribute is not declared.",
"The element has unexpected child element 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:updateFields'.",
"The attribute 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:name' has invalid value 'useWord2013TrackBottomHyphenation'. The Enumeration constraint failed.",
"The 'http://schemas.microsoft.com/office/word/2012/wordml:restartNumberingAfterBreak' attribute is not declared.",
"Attribute 'id' should have unique value. Its current value '",
"The 'urn:schemas-microsoft-com:mac:vml:blur' attribute is not declared.",
"Attribute 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:id' should have unique value. Its current value '",
"The element has unexpected child element 'http://schemas.microsoft.com/office/word/2012/wordml:",
"The element has invalid child element 'http://schemas.microsoft.com/office/word/2012/wordml:",
"The 'urn:schemas-microsoft-com:mac:vml:complextextbox' attribute is not declared.",
"http://schemas.microsoft.com/office/word/2010/wordml:",
"http://schemas.microsoft.com/office/word/2008/9/12/wordml:",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:allStyles' attribute is not declared.",
"The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:customStyles' attribute is not declared.",
};
private void ValidateUniqueDocPrIds(FileInfo fi)
{
using (WordprocessingDocument doc = WordprocessingDocument.Open(fi.FullName, false))
{
var docPrIds = new HashSet<string>();
foreach (var item in doc.MainDocumentPart.GetXDocument().Descendants(WP.docPr))
Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value));
foreach (var header in doc.MainDocumentPart.HeaderParts)
foreach (var item in header.GetXDocument().Descendants(WP.docPr))
Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value));
foreach (var footer in doc.MainDocumentPart.FooterParts)
foreach (var item in footer.GetXDocument().Descendants(WP.docPr))
Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value));
if (doc.MainDocumentPart.FootnotesPart != null)
foreach (var item in doc.MainDocumentPart.FootnotesPart.GetXDocument().Descendants(WP.docPr))
Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value));
if (doc.MainDocumentPart.EndnotesPart != null)
foreach (var item in doc.MainDocumentPart.EndnotesPart.GetXDocument().Descendants(WP.docPr))
Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value));
}
}
}
}
#endif