| /*************************************************************************** |
| |
| 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 |