Skip to content

Commit b78d3e9

Browse files
committed
fix svg
1 parent b3ad984 commit b78d3e9

7 files changed

Lines changed: 142 additions & 11 deletions

src/DeterministicIoPackaging/Patching/RelationshipRenumber.cs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ static class RelationshipRenumber
22
{
33
static XNamespace r = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
44
static XName rId = r + "id";
5+
static XName rEmbed = r + "embed";
6+
static XName rLink = r + "link";
57

68
public static Dictionary<string, string> RenumberAndSort(XDocument xml, string? entryName = null)
79
{
@@ -64,22 +66,27 @@ static void NormalizeTargets(XElement root, string entryName)
6466
{
6567
targetAttr.SetValue(target[basePath.Length..]);
6668
}
67-
else
68-
{
69-
// Absolute path doesn't match base — just strip the leading /
70-
targetAttr.SetValue(target[1..]);
71-
}
69+
// else: absolute path points outside base directory — keep as-is
70+
// (stripping the leading / would create a broken relative path)
7271
}
7372
}
7473

7574
public static void RemapIds(XDocument xml, Dictionary<string, string> mapping)
7675
{
77-
foreach (var attr in xml.Descendants().Attributes(rId))
76+
foreach (var descendant in xml.Descendants())
7877
{
79-
if (mapping.TryGetValue(attr.Value, out var newId))
80-
{
81-
attr.SetValue(newId);
82-
}
78+
RemapAttribute(descendant, rId, mapping);
79+
RemapAttribute(descendant, rEmbed, mapping);
80+
RemapAttribute(descendant, rLink, mapping);
81+
}
82+
}
83+
84+
static void RemapAttribute(XElement element, XName name, Dictionary<string, string> mapping)
85+
{
86+
var attr = element.Attribute(name);
87+
if (attr != null && mapping.TryGetValue(attr.Value, out var newId))
88+
{
89+
attr.SetValue(newId);
8390
}
8491
}
8592
}

src/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Project>
33
<PropertyGroup>
44
<NoWarn>CS1591;CS0649;CA1416;NU1608;NU1109;NU1510</NoWarn>
5-
<Version>0.24.1</Version>
5+
<Version>0.24.2</Version>
66
<LangVersion>preview</LangVersion>
77
<AssemblyVersion>1.0.0</AssemblyVersion>
88
<Description>Modify System.IO.Packaging (https://learn.microsoft.com/en-us/dotnet/api/system.io.packaging) files to ensure they are deterministic. Helpful for testing, build reproducibility, security verification, and ensuring package integrity across different build environments.</Description>
2.01 KB
Binary file not shown.
1.97 KB
Binary file not shown.

src/Tests/OpenXmlTests.cs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using DocumentFormat.OpenXml;
22
using DocumentFormat.OpenXml.Packaging;
33
using DocumentFormat.OpenXml.Spreadsheet;
4+
using W = DocumentFormat.OpenXml.Wordprocessing;
5+
using A = DocumentFormat.OpenXml.Drawing;
6+
using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing;
7+
using PIC = DocumentFormat.OpenXml.Drawing.Pictures;
48

59
[TestFixture]
610
public class OpenXmlTests
@@ -14,6 +18,126 @@ public Task FixesPrefixedNamespace()
1418
return VerifyZip(result);
1519
}
1620

21+
[Test]
22+
public void SvgBlipEmbedIdsAreRemapped()
23+
{
24+
var docxStream = CreateDocxWithSvg();
25+
var result = DeterministicPackage.Convert(docxStream);
26+
27+
result.Position = 0;
28+
using var archive = new Archive(result, ZipArchiveMode.Read);
29+
30+
// Collect relationship IDs from .rels
31+
var relsEntry = archive.GetEntry("word/_rels/document.xml.rels")!;
32+
using var relsStream = relsEntry.Open();
33+
var relsXml = XDocument.Load(relsStream);
34+
var relIds = relsXml.Root!.Elements()
35+
.Select(_ => _.Attribute("Id")!.Value)
36+
.ToList();
37+
38+
// Collect r:embed references from document.xml
39+
var docEntry = archive.GetEntry("word/document.xml")!;
40+
using var docStream = docEntry.Open();
41+
var docXml = XDocument.Load(docStream);
42+
XNamespace r = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
43+
var embedRefs = docXml.Descendants().Attributes(r + "embed")
44+
.Select(_ => _.Value)
45+
.ToList();
46+
47+
// Every r:embed reference must exist in the .rels file
48+
foreach (var embedRef in embedRefs)
49+
{
50+
Assert.That(relIds, Does.Contain(embedRef),
51+
$"r:embed=\"{embedRef}\" in document.xml has no matching relationship ID in .rels");
52+
}
53+
}
54+
55+
[Test]
56+
public Task SvgBlipDocx()
57+
{
58+
var docxStream = CreateDocxWithSvg();
59+
var result = DeterministicPackage.Convert(docxStream);
60+
61+
return Verify(result, extension: "docx")
62+
.UniqueForRuntime();
63+
}
64+
65+
internal static MemoryStream CreateDocxWithSvg()
66+
{
67+
var stream = new MemoryStream();
68+
using (var document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
69+
{
70+
var mainPart = document.AddMainDocumentPart();
71+
72+
// Add PNG fallback
73+
var pngPart = mainPart.AddImagePart("image/png", "rPng1");
74+
var pngBytes = Convert.FromBase64String(
75+
"iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAEElEQVR4nGP4z8AARAwQCgAf7gP9i18U1AAAAABJRU5ErkJggg==");
76+
using (var ms = new MemoryStream(pngBytes))
77+
{
78+
pngPart.FeedData(ms);
79+
}
80+
81+
// Add SVG
82+
var svgPart = mainPart.AddImagePart("image/svg+xml", "rSvg1");
83+
var svgBytes = System.Text.Encoding.UTF8.GetBytes(
84+
"""<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><circle cx="50" cy="50" r="40" fill="red" /></svg>""");
85+
using (var ms = new MemoryStream(svgBytes))
86+
{
87+
svgPart.FeedData(ms);
88+
}
89+
90+
// Build blip with SVG extension
91+
var blip = new A.Blip { Embed = "rPng1" };
92+
var svgBlipElement = new OpenXmlUnknownElement("asvg", "svgBlip", "http://schemas.microsoft.com/office/drawing/2016/SVG/main");
93+
svgBlipElement.SetAttribute(new OpenXmlAttribute("r", "embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships", "rSvg1"));
94+
var ext = new OpenXmlUnknownElement("a", "ext", "http://schemas.openxmlformats.org/drawingml/2006/main");
95+
ext.SetAttribute(new OpenXmlAttribute("", "uri", "", "{96DAC541-7B7A-43D3-8B79-37D633B846F1}"));
96+
ext.Append(svgBlipElement);
97+
var extList = new OpenXmlUnknownElement("a", "extLst", "http://schemas.openxmlformats.org/drawingml/2006/main");
98+
extList.Append(ext);
99+
blip.Append(extList);
100+
101+
var drawing = new W.Drawing(
102+
new DW.Inline(
103+
new DW.Extent { Cx = 952500, Cy = 952500 },
104+
new DW.DocProperties { Id = 1U, Name = "Image" },
105+
new A.Graphic(
106+
new A.GraphicData(
107+
new PIC.Picture(
108+
new PIC.NonVisualPictureProperties(
109+
new PIC.NonVisualDrawingProperties { Id = 0U, Name = "Image" },
110+
new PIC.NonVisualPictureDrawingProperties()),
111+
new PIC.BlipFill(blip, new A.Stretch(new A.FillRectangle())),
112+
new PIC.ShapeProperties(
113+
new A.Transform2D(
114+
new A.Offset { X = 0, Y = 0 },
115+
new A.Extents { Cx = 952500, Cy = 952500 }),
116+
new A.PresetGeometry(new A.AdjustValueList()) { Preset = A.ShapeTypeValues.Rectangle })))
117+
{ Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" })
118+
)
119+
{
120+
DistanceFromTop = 0U,
121+
DistanceFromBottom = 0U,
122+
DistanceFromLeft = 0U,
123+
DistanceFromRight = 0U
124+
});
125+
126+
var body = new W.Body(
127+
new W.Paragraph(
128+
new W.Run(
129+
new W.Text("Before SVG") { Space = SpaceProcessingModeValues.Preserve })),
130+
new W.Paragraph(new W.Run(drawing)),
131+
new W.Paragraph(
132+
new W.Run(
133+
new W.Text("After SVG") { Space = SpaceProcessingModeValues.Preserve })));
134+
mainPart.Document = new W.Document(body);
135+
}
136+
137+
stream.Position = 0;
138+
return stream;
139+
}
140+
17141
static MemoryStream CreateSpreadsheet()
18142
{
19143
var stream = new MemoryStream();
8 Bytes
Binary file not shown.
8 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)