Drop the source /XRefStm from a written trailer - #726
Conversation
Writer copies the source trailer dictionary wholesale, managing /Prev
itself and dropping /ID on a full rewrite, but it never looked at
/XRefStm. Since writeXRefInfo only ever emits a classic cross-reference
table, an inherited /XRefStm advertises a cross-reference stream for a
section the writer did not write: on a full rewrite the offset lands on
arbitrary bytes, so the file veraPDF just wrote cannot be reopened
("can not locate xref table"); on an incremental update it advertises
the previous revision's stream for the appended section.
Hybrid-reference files (ISO 32000-1, 7.5.8.4) are what word processors
commonly produce, so any such document was affected.
Output for documents without /XRefStm is byte for byte unchanged.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthrough
ChangesHybrid reference trailer handling
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The PR prevents written PDF trailers from advertising an unrelated cross-reference stream while preserving existing save behavior and document contents. No actionable merge-blocking risk remains after normal checks and review. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Problem
Writer.setTrailer(COSTrailer, long)copies the source trailer dictionary into the output and then adjusts only the keys it manages: it sets/Previtself and removes/IDon a full rewrite./XRefStmis not handled. Since the writer only ever emits a classic cross-reference table, an inherited/XRefStmadvertises a cross-reference stream for a section the writer did not write.On a full rewrite the offset lands on arbitrary bytes, so a file veraPDF has just written cannot be reopened:
Hybrid-reference files (ISO 32000-1, 7.5.8.4) are what word processors commonly produce — the last classic section is empty (
xref 0 0) and its trailer delegates through/XRefStm— so any such document is affected. qpdf 12.3.2 reports such an output asfile is damagedwithxref not foundat exactly that offset; tolerant readers still reconstruct it, strict ones do not.Fix
One unconditional
removeKey(ASAtom.XREF_STM)insetTrailer./Prevand/XRefStmare the only byte-offset keys a trailer carries, and/Previs already managed here, so this fills in the missing one. The removal happens while the source trailer is copied, so a deliberatesetXRefStmaftersetTrailerwould still take effect.The removal is not conditional on
prev, unlike the/IDremoval above it, because the reason differs:/IDcarries document identity, which an incremental update has to preserve, while/XRefStmdescribes the section being written, and that section is a classic table on both paths.To be precise about what is demonstrated: on the incremental path I could not observe a reader failure. With the stale key present, both veraPDF and qpdf still resolve incrementally changed objects correctly, so the appended section merely advertises the previous revision's stream rather than corrupting anything. That resolution depends on how a reader orders a section's table against the stream the trailer points at, which the specification describes for a stream that belongs to that section — not for an inherited one.
Verification
WriterTest, 3 tests. Reverting the one-line change fails 2 of them (can not locate xref tableon the rewrite, stale offset on the incremental update); the fixture guard still passes.mvn verifygreen on JDK 25 and 21: 378 tests, 0 failures./XRefStmproduce byte-identical output./XRefStmis untouched.Summary by CodeRabbit
Bug Fixes
/XRefStmentries.Tests