Skip to content

fix: read the /XRefStm cross-reference stream of hybrid-reference files - #100

Open
wleeaf wants to merge 1 commit into
ledongthuc:masterfrom
wleeaf:hybrid-xrefstm
Open

wleeaf wants to merge 1 commit into
ledongthuc:masterfrom
wleeaf:hybrid-xrefstm

Conversation

@wleeaf

@wleeaf wleeaf commented Oct 26, 2026

Copy link
Copy Markdown

A hybrid-reference file keeps a cross-reference table for readers that don't know cross-reference streams, and names a cross-reference stream with /XRefStm in the trailer for those that do (PDF 32000-1:2008, 7.5.8.4). The objects in its object streams are only in that stream. The table leaves them out or lists them as free. readXrefTable ignores /XRefStm, so none of those objects resolve, and anything that refers to them reads as null.

Word, Acrobat Distiller, the Adobe PDF Library, LiveCycle Designer and Prince all write such files. In qpdf's good17.pdf, for example, Outline() returns no entries, because the outline is in an object stream.

readXrefTable now reads the stream named by /XRefStm after each table section, the newest first, as it does with /Prev. Its entries fill the numbers the table didn't set. If the stream can't be read, the table is used as it is.

readXrefTableData now also checks whether an entry is already set by its ptr rather than its offset. A compressed entry for the first object in an object stream has offset 0, so an entry from an older table section would otherwise replace it.

Test plan:

  • Added TestHybridReferenceFile: a file whose page tree is in an object stream, with free table entries for it. On master, NumPage is 0; with this change it is 1.
  • Added TestHybridReferenceFileWithUnreadableXRefStm, which checks that the table still works when /XRefStm points at something that isn't a cross-reference stream.
  • go test ./... and go build ./... pass.
  • I ran 1,637 PDFs from the qpdf and pdf.js test suites. Only the 14 hybrid-reference files change, and only by gaining objects: 320 in all.
    • 299 of them match qpdf --json.
    • The rest are each test file's cross-reference stream object, whose own entry points at the wrong offset (good17.pdf, good18.pdf and hybrid-xref.pdf), and objects of issue9418.pdf, a damaged file that qpdf reconstructs.
    • Outline() of good17.pdf and hybrid-xref.pdf now returns its 2 top-level entries. The text of the other hybrid files is unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant