You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Ensure fully deterministic ZIP output across net48 and net10.0
- Replace framework ZipArchive writing with raw ZipStorer to guarantee
compression method 0 (Stored) on all frameworks (net48 ignores
CompressionLevel.NoCompression)
- Write raw zlib stored blocks in PngNormalizer to avoid DEFLATE
differences between framework ZLibStream implementations
- Sort ZIP entries by name using ordinal comparison
- Sort [Content_Types].xml elements deterministically (ContentTypesPatcher)
- Renumber all relationship IDs in .rels files to DeterministicId{n} and
remap corresponding r:id references in content XML
- Add verification tests for stored entries, deterministic relationship
IDs, and sorted content types
* .
* .
* .
Copy file name to clipboardExpand all lines: claude.md
+34-6Lines changed: 34 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -43,12 +43,37 @@ The codebase uses a patcher pattern for normalizing OOXML content:
43
43
- Each patcher implements `IPatcher` interface
44
44
-`IsMatch(Entry entry)` - determines which files the patcher applies to (e.g., "word/document.xml")
45
45
-`PatchXml(XDocument xml)` - modifies the XML in-place
46
-
- Register patchers in `DeterministicPackage.cs` patchers list
47
-
-**Order matters** - patchers run in sequence
46
+
- Register patchers via `CreatePatchers()` factory in `DeterministicPackage.cs` (fresh instance per conversion)
47
+
-**Order matters** - relationship patchers must run before their content patchers (e.g., `WorkbookRelationshipPatcher` before `WorkbookPatcher`)
48
+
49
+
#### Paired Patchers
50
+
51
+
Some patchers work in pairs: a relationship patcher renumbers IDs in `.rels` files and stores the mapping, then a content patcher remaps `r:id` references in the corresponding XML:
The content patcher receives the relationship patcher via constructor injection.
57
+
58
+
#### Relationship ID Renumbering
59
+
60
+
`RelationshipRenumber` (in IPatcher.cs) provides shared helpers:
61
+
-`RenumberAndSort(XDocument)` — sorts relationships by Type+Target, renumbers to `DeterministicId{n}`, returns old→new mapping
62
+
-`RemapIds(XDocument, mapping)` — replaces `r:id` attribute values in content XML using the mapping
63
+
64
+
#### Content Types Sorting
65
+
66
+
`ContentTypesPatcher` sorts `[Content_Types].xml` elements by local name, then Extension, then PartName to ensure deterministic order across frameworks.
67
+
68
+
### ZIP Output
69
+
70
+
-`ZipStorer` rewrites ZIP archives with all entries using compression method 0 (Stored), bypassing net48's `ZipArchive` which ignores `CompressionLevel.NoCompression`
71
+
- Entries are sorted by `FullName` using `StringComparer.Ordinal`
72
+
-`PngNormalizer` writes raw zlib stored blocks (CMF+FLG + DEFLATE stored blocks + Adler-32) instead of using `ZLibStream`, which produces different output on net48 vs net10.0
0 commit comments