BeanNest

PDF internals research

Reading PDF Objects with a Byte Scan Instead of the Cross-Reference Table

The repository's analyzer counts PDF object markers by scanning raw bytes instead of using the cross-reference table as its lookup index. The four generated fixtures give that scan a known expected answer, so this guide can explain what the method verifies on the corpus and what it does not establish about damaged or more complex PDFs.

What this guide helps you decide

A PDF has a cross-reference table that is supposed to make object lookup fast. Why would an analyzer ignore it, and what does it find instead?

This research explains the byte-level object-location approach used by this repository's analyzer and the reasoning behind it. It is not a general PDF specification guide and does not claim to handle every malformed file.

Try it on the anchor product: BeanNest Tools /pdf/.

The scan looks for object markers in raw bytes

For each fixture, the analyzer records object number, generation, and byte position from the object-header pattern it finds in the file. This path does not consult xref offsets to decide which markers to count.

The controlled corpus gives the scan a known answer

The three unpadded fixtures were generated with five objects and the padded fixture with forty-five. The analysis artifact reports exactly those counts: 5, 5, 5, and 45. Agreement with the constructed fixture is the validation this corpus provides.

Ignoring xref is a design choice, not damaged-file proof

Because the scan does not depend on xref lookup, its object count is independent of the xref table for these fixtures. The corpus contains no deliberately truncated, stale, repaired, or otherwise damaged xref case, so this guide does not claim that such a file is handled correctly.

Raw scanning can overcount bytes inside a stream

A stream payload is arbitrary data and can contain bytes that resemble an object header. The current scan does not first exclude every stream payload while searching, so a coincidental marker could be counted as an object. The generated corpus does not contain that collision.

Incremental-update behavior is also unmeasured here

The four fixtures do not contain incremental revisions with older and newer bodies for the same logical object. A raw byte scan could see more than one body in such a file, so the current object count should not be presented as an authoritative live-object count for an untested incremental document.

A hybrid parser would answer a different question

A stricter future implementation could compare xref-addressed objects with additional raw markers and report disagreements as anomalies. That would combine an indexed logical view with byte-level evidence. The current analyzer deliberately stops at the simpler scan needed for this controlled size corpus.

First-party evidence and provenance

How we checked this

We generated four single-page PDFs with this repository's own writer, then parsed each file's raw byte structure with this repository's own analyzer. Both tools are dependency-free and deterministic, so the same commands reproduce the same numbers on any machine. For this guide we ran the byte-level object scan over all four corpus files and checked that the reported object counts match how the corpus was constructed, which is what demonstrates the scan finds real structure.

Method
We generated four single-page PDFs with this repository's own writer, then parsed each file's raw byte structure with this repository's own analyzer. Both tools are dependency-free and deterministic, so the same commands reproduce the same numbers on any machine.
Environment
Node.js on a desktop workstation. No PDF library, no network access, and no third-party file was used. The corpus is produced from text this repository owns.
Captured
Reviewed by
BeanNest Studio

Repository evidence artifacts:

  • tools/research-evidence/pdf/analysis-2026-09-20.json
  • tools/research-evidence/pdf/README.md