PDF internals research

How PDF Structural Overhead Hides Inside Your File

PDF size is often discussed as though content decided it. It does not. A PDF carries an object graph, and that graph includes things a reader never sees: document information, embedded metadata, XMP packets, producer strings, and cross-reference overhead. We measured how much a file grows when only non-content objects are added, and the answer is more than the entire compressed content stream.

What this guide helps you decide

Two PDFs render identically and hold the same content, but one is seven times larger. Where did the extra size go?

This research measures how non-content objects contribute to PDF size using an intentionally padded corpus file. It does not describe a specific application's metadata behaviour and does not recommend deleting structural objects.

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

The controlled comparison

Our fourth corpus file is identical to the 40-line compressed file except that it carries forty additional metadata objects. No text was added, no page was added, and the rendered result is the same single page. The only change is structural.

The measured growth

The unpadded compressed file is 821 bytes and the padded one is 5930 bytes, a growth of 5109 bytes. The actual page content stream stayed at 250 bytes in both files. Forty empty objects therefore cost roughly 20 times the size of the content they accompany.

Why the object count matters

Our analyzer reports 5 objects for the unpadded file and 45 for the padded one. Each object carries its own header, dictionary, and cross-reference row. At small file sizes this overhead dominates completely, which is why the padded file reports a stream share of only 4.2 percent while its unpadded twin reports 30.5 percent.

The practical consequence

A small PDF is a structural-heavy file. Compressing it will disappoint, because the bytes are not content. This also means a PDF can grow between two saves with no visible change: an editor that writes additional metadata or re-serialises the object graph adds size that no reader will ever see.

What this does not justify

The measurement shows that structure costs bytes. It does not show that structure is safe to remove. Document information, accessibility tagging, and embedded fonts are structural, and deleting them can break reading order, text extraction, or appearance. The honest conclusion is that overhead is a cost, not a defect.

Where the ceiling actually sits

Across our four files the stream share ranges from 4.2 percent to 89.3 percent. That range is the real story: the same nominal format produces wildly different size behaviour depending on how much of each file is content. Any single expectation about PDF size is wrong for most files.

Why forty objects is a fair test size

Forty is large enough that the effect dominates the file and small enough that the result is easy to reason about. It is roughly the number of additional objects a document gains from a small revision history, a handful of embedded accessibility tags, or a few producer-specific information dictionaries. It is not an extreme value chosen to produce a dramatic number; it is a plausible value that happens to be visible in a file this small. On a multi-megabyte document the same forty objects would be invisible, which is itself part of the lesson.

The cross-reference table grows too

Each additional object adds a row to the cross-reference table and increases the trailer's size field. The analyzer reports 45 objects for the padded file against 5 for its twin, and the file also carries the larger cross-reference section that those 45 objects require. So the measured growth of 5109 bytes is not solely the forty dictionaries; it includes the index that addresses them. That is why structural overhead scales worse than linearly at very small sizes.

Why small files are the interesting case

At small sizes the fixed costs of the format are proportionally large. A PDF has a header, a trailer, a cross-reference section, a catalogue, a page tree, and at least one page object before any content exists at all. Those costs are unavoidable and constant, so their share falls as a file grows. The four-file corpus makes this visible: the stream share ranges from 4.2 percent to 89.3 percent purely because of how much content each file carries relative to its fixed structure.

The practical implication for size expectations

If you are compressing a small PDF and the result disappoints, structural overhead is the likely reason and no amount of compression effort will fix it. If you are compressing a large text-heavy PDF and the result disappoints, the likely reason is different: the content is already compressed, or it is image data. Distinguishing these two cases before choosing a tool is the practical value of measuring stream share first.

Where the 5109 bytes go

Forty objects cost 5109 bytes between the two files, which is roughly 128 bytes each. That per-object cost is the object header, the dictionary itself, the endobj marker, and the shared growth of the cross-reference table. In this corpus each padding object is deliberately tiny, a metadata dictionary with a producer string, so the per-object figure is close to the floor the format imposes. A real metadata object carrying a larger XML packet would cost considerably more than 128 bytes.

Why metadata is structurally different from content

Content streams affect what the page looks like and must be present for the document to render. Metadata objects affect what the document reports about itself and are generally invisible when rendering. Both cost bytes and both live in the same object graph. This is why a size comparison between two visually identical documents can differ by kilobytes with no visible cause, and why measuring structure separately from content is necessary before drawing any conclusion about compression.

The interaction with page count

Per-page overhead follows the same pattern as per-object overhead. A document with many short pages carries more page-tree nodes, more content stream dictionaries, and more cross-reference rows per unit of visible content. The padded corpus isolates the object-count effect on a single page, but the same reasoning applies to pages: a twenty-page document of short pages can contain less text than a two-page document of long pages while being structurally larger.

Why we did not strip the padding to demonstrate a saving

Removing the forty objects would return the file to 821 bytes and would look like a demonstration of size saving. It would also be dishonest as a general claim, because the padding exists only to make the effect visible and carries no information a real document would need. The measurement is a controlled comparison, and the guides present it as one rather than as a compression technique.

Why metadata is not the only structural cost

The padded corpus isolates metadata objects because they are easy to add without changing the page. A real file also carries a catalogue, a page tree, a cross-reference section, and its trailer, all of which are structural and all of which cost bytes. Those are not optional and cannot be removed, which is why they belong to the fixed cost of the format rather than to the removable category. The measured overhead here is one specific component of a larger structural total.

How the per-object figure was derived

The padded file is 5109 bytes larger than its twin and carries forty additional objects, which gives roughly 128 bytes per object. That figure includes the object's header, its dictionary, its endobj marker, and its share of the larger cross-reference section. It is a floor rather than a typical value, because the padding dictionaries were written as small as possible. A metadata object carrying a sizeable XML packet would cost substantially more than the measured average.

Why smaller files are disproportionately affected

The format imposes a fixed cost before any content exists: header, trailer, cross-reference section, catalogue, page tree, and at least one page object. On a large document that cost is a rounding error, and on a small one it can dominate. The corpus shows the effect clearly, with the stream share falling from 89.3 percent in the raw file to 4.2 percent in the padded file purely because the fixed and structural costs grew relative to the content.

What a reader should not conclude

The measurement does not imply that metadata is waste or that removing it is a size strategy. Document information, accessibility structure, and embedded tags serve real purposes, and deleting them can break text extraction, reading order, or appearance. The honest conclusion is that overhead is a cost of the format's flexibility, and that a size expectation must account for it rather than assume it away.

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 compared the padded corpus file against its unpadded twin, which are identical except for forty additional metadata objects, so the size difference isolates structural overhead from page content.

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