Skip to content

Commit ac25ea5

Browse files
Harden structured Word comparison coverage
1 parent f444fbb commit ac25ea5

3 files changed

Lines changed: 497 additions & 151 deletions

File tree

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
using System.IO;
2+
using System.Linq;
3+
using DocumentFormat.OpenXml.Packaging;
4+
using DocumentFormat.OpenXml.Wordprocessing;
5+
using OfficeIMO.Word;
6+
using Xunit;
7+
using V = DocumentFormat.OpenXml.Vml;
8+
9+
namespace OfficeIMO.Tests {
10+
public partial class Word {
11+
[Fact]
12+
public void CompareStructureTreatsMovedBodyParagraphToHeaderAsDeleteInsert() {
13+
string sourcePath = Path.Combine(_directoryWithFiles, "compare_structure_source_body_to_header.docx");
14+
using (WordDocument doc = WordDocument.Create(sourcePath)) {
15+
doc.AddParagraph("Body anchor");
16+
doc.AddParagraph("Classification: Confidential");
17+
doc.Save(false);
18+
}
19+
20+
string targetPath = Path.Combine(_directoryWithFiles, "compare_structure_target_body_to_header.docx");
21+
using (WordDocument doc = WordDocument.Create(targetPath)) {
22+
doc.AddParagraph("Body anchor");
23+
doc.AddHeadersAndFooters();
24+
doc.Header.Default!.AddParagraph("Classification: Confidential");
25+
doc.Save(false);
26+
}
27+
28+
WordComparisonResult result = WordDocumentComparer.CompareStructure(sourcePath, targetPath);
29+
30+
Assert.Contains(result.Findings, finding =>
31+
finding.Scope == WordComparisonScope.Paragraph &&
32+
finding.ChangeKind == WordComparisonChangeKind.Deleted &&
33+
finding.SourceText == "Classification: Confidential");
34+
Assert.Contains(result.Findings, finding =>
35+
finding.Scope == WordComparisonScope.Paragraph &&
36+
finding.ChangeKind == WordComparisonChangeKind.Inserted &&
37+
finding.TargetText == "Classification: Confidential");
38+
Assert.DoesNotContain(result.Findings, finding =>
39+
finding.Scope == WordComparisonScope.Paragraph &&
40+
finding.ChangeKind == WordComparisonChangeKind.Modified &&
41+
finding.SourceText == "Classification: Confidential" &&
42+
finding.TargetText == "Classification: Confidential");
43+
}
44+
45+
[Fact]
46+
public void CompareStructurePreservesPageAndColumnBreaksInParagraphText() {
47+
string sourcePath = Path.Combine(_directoryWithFiles, "compare_structure_source_page_break.docx");
48+
using (WordDocument doc = WordDocument.Create(sourcePath)) {
49+
AddBreakParagraph(doc, BreakValues.Page);
50+
doc.Save(false);
51+
}
52+
53+
string targetPath = Path.Combine(_directoryWithFiles, "compare_structure_target_column_break.docx");
54+
using (WordDocument doc = WordDocument.Create(targetPath)) {
55+
AddBreakParagraph(doc, BreakValues.Column);
56+
doc.Save(false);
57+
}
58+
59+
WordComparisonResult result = WordDocumentComparer.CompareStructure(sourcePath, targetPath);
60+
61+
WordComparisonFinding modified = Assert.Single(result.Findings, finding =>
62+
finding.Scope == WordComparisonScope.Paragraph &&
63+
finding.ChangeKind == WordComparisonChangeKind.Modified);
64+
Assert.Equal("Before[PageBreak]After", modified.SourceText);
65+
Assert.Equal("Before[ColumnBreak]After", modified.TargetText);
66+
}
67+
68+
[Fact]
69+
public void CompareStructureReportsVmlImageReplacement() {
70+
string sourcePath = Path.Combine(_directoryWithFiles, "compare_structure_source_vml_image.docx");
71+
using (WordDocument doc = WordDocument.Create(sourcePath)) {
72+
AddVmlImageParagraph(doc, Path.Combine(_directoryWithImages, "EvotecLogo.png"));
73+
doc.Save(false);
74+
}
75+
76+
string targetPath = Path.Combine(_directoryWithFiles, "compare_structure_target_vml_image.docx");
77+
using (WordDocument doc = WordDocument.Create(targetPath)) {
78+
AddVmlImageParagraph(doc, Path.Combine(_directoryWithImages, "Kulek.jpg"));
79+
doc.Save(false);
80+
}
81+
82+
WordComparisonResult result = WordDocumentComparer.CompareStructure(sourcePath, targetPath);
83+
84+
WordComparisonFinding image = Assert.Single(result.Findings, finding =>
85+
finding.Scope == WordComparisonScope.Image &&
86+
finding.ChangeKind == WordComparisonChangeKind.Modified);
87+
Assert.Equal("image[0]", image.Location);
88+
}
89+
90+
[Fact]
91+
public void CompareStructureReadsBlockContentControlTextInsideTableCells() {
92+
string sourcePath = Path.Combine(_directoryWithFiles, "compare_structure_source_cell_sdt.docx");
93+
using (WordDocument doc = WordDocument.Create(sourcePath)) {
94+
WordTable table = doc.AddTable(1, 1);
95+
ReplaceCellWithBlockContentControl(table.Rows[0].Cells[0], "Evidence pending");
96+
doc.Save(false);
97+
}
98+
99+
string targetPath = Path.Combine(_directoryWithFiles, "compare_structure_target_cell_sdt.docx");
100+
using (WordDocument doc = WordDocument.Create(targetPath)) {
101+
WordTable table = doc.AddTable(1, 1);
102+
ReplaceCellWithBlockContentControl(table.Rows[0].Cells[0], "Evidence approved");
103+
doc.Save(false);
104+
}
105+
106+
WordComparisonResult result = WordDocumentComparer.CompareStructure(sourcePath, targetPath);
107+
108+
WordComparisonFinding cell = Assert.Single(result.Findings, finding =>
109+
finding.Scope == WordComparisonScope.TableCell &&
110+
finding.ChangeKind == WordComparisonChangeKind.Modified);
111+
Assert.Equal("Evidence pending", cell.SourceText);
112+
Assert.Equal("Evidence approved", cell.TargetText);
113+
}
114+
115+
[Fact]
116+
public void CompareStructureAlignsInsertionBeforeModifiedParagraph() {
117+
string sourcePath = Path.Combine(_directoryWithFiles, "compare_structure_source_insert_before_modified.docx");
118+
using (WordDocument doc = WordDocument.Create(sourcePath)) {
119+
doc.AddParagraph("Terms and conditions apply.");
120+
doc.AddParagraph("Closing section");
121+
doc.Save(false);
122+
}
123+
124+
string targetPath = Path.Combine(_directoryWithFiles, "compare_structure_target_insert_before_modified.docx");
125+
using (WordDocument doc = WordDocument.Create(targetPath)) {
126+
doc.AddParagraph("New executive summary");
127+
doc.AddParagraph("Terms and conditions apply after approval.");
128+
doc.AddParagraph("Closing section");
129+
doc.Save(false);
130+
}
131+
132+
WordComparisonResult result = WordDocumentComparer.CompareStructure(sourcePath, targetPath);
133+
134+
Assert.Equal(2, result.Findings.Count);
135+
WordComparisonFinding inserted = Assert.Single(result.Findings, finding =>
136+
finding.Scope == WordComparisonScope.Paragraph &&
137+
finding.ChangeKind == WordComparisonChangeKind.Inserted);
138+
Assert.Equal("paragraph[0]", inserted.Location);
139+
Assert.Equal("New executive summary", inserted.TargetText);
140+
141+
WordComparisonFinding modified = Assert.Single(result.Findings, finding =>
142+
finding.Scope == WordComparisonScope.Paragraph &&
143+
finding.ChangeKind == WordComparisonChangeKind.Modified);
144+
Assert.Equal("paragraph[1]", modified.Location);
145+
Assert.Equal("Terms and conditions apply.", modified.SourceText);
146+
Assert.Equal("Terms and conditions apply after approval.", modified.TargetText);
147+
}
148+
149+
private static void AddBreakParagraph(WordDocument document, BreakValues breakType) {
150+
WordParagraph paragraph = document.AddParagraph("Before");
151+
paragraph._paragraph.Append(new Run(new Break { Type = breakType }));
152+
paragraph._paragraph.Append(new Run(new Text("After")));
153+
}
154+
155+
private static void AddVmlImageParagraph(WordDocument document, string imagePath) {
156+
MainDocumentPart mainPart = document._wordprocessingDocument.MainDocumentPart!;
157+
ImagePart imagePart = Path.GetExtension(imagePath).Equals(".jpg", System.StringComparison.OrdinalIgnoreCase) ||
158+
Path.GetExtension(imagePath).Equals(".jpeg", System.StringComparison.OrdinalIgnoreCase)
159+
? mainPart.AddImagePart(ImagePartType.Jpeg)
160+
: mainPart.AddImagePart(ImagePartType.Png);
161+
using (FileStream stream = File.OpenRead(imagePath)) {
162+
imagePart.FeedData(stream);
163+
}
164+
165+
string relationshipId = mainPart.GetIdOfPart(imagePart);
166+
var imageData = new V.ImageData {
167+
RelationshipId = relationshipId,
168+
Title = "Legacy image"
169+
};
170+
var shape = new V.Shape(imageData) {
171+
Id = "LegacyImage",
172+
Type = "#_x0000_t75",
173+
Style = "width:72pt;height:72pt",
174+
Filled = false,
175+
Stroked = false
176+
};
177+
178+
document._document.Body!.Append(new Paragraph(new Run(new Picture(shape))));
179+
}
180+
181+
private static void ReplaceCellWithBlockContentControl(WordTableCell cell, string text) {
182+
cell._tableCell.RemoveAllChildren<Paragraph>();
183+
cell._tableCell.Append(new SdtBlock(
184+
new SdtProperties(new SdtAlias { Val = "Evidence" }),
185+
new SdtContentBlock(new Paragraph(new Run(new Text(text))))));
186+
}
187+
}
188+
}

0 commit comments

Comments
 (0)