PDF internals research

Measuring Real PDF Compression Instead of Trusting the Label

A PDF stream carries a filter name in its dictionary, and it is easy to treat that name as proof that compression happened. It is not proof. A filter name says what transformation was applied to the bytes, not how much redundancy those bytes contained. The only honest test is to inflate the stream and compare the inflated size to the compressed size. We did that on our own corpus and recorded both results.

What this guide helps you decide

A PDF says its content stream uses FlateDecode. Does that mean the content is actually compressed, and how would I know?

This research measures whether a declared compression filter corresponds to real reducible data. It does not evaluate the quality of any compressor and does not claim a compression guarantee.

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

A filter name describes a transformation, not its effectiveness

FlateDecode in a stream dictionary says that Deflate was applied to the stored bytes. It does not state how much smaller those bytes became. The analyzer therefore attempts a real inflation and records compressed size, inflated size, object number, and ratio for a successful sample.

The corpus contains two measured inflation ratios

In text-40-flate.pdf, object 4 stores 250 compressed bytes that inflate to 4582 bytes, a ratio of 18.328x. In text-200-flate.pdf, the 715-byte stream inflates to 22923 bytes, a ratio of 32.06x. Those are direct measurements on the generated repetitive-text fixtures.

The raw and compressed pair gives a separate file-size result

The forty-line raw fixture is 5135 bytes and its compressed counterpart is 821 bytes, about an 84 percent reduction for that pair. That file-size comparison and the 18.328x stream inflation ratio answer related but different questions, so neither should be presented as a universal compression percentage.

Object 4 makes the inflation sample locatable

The analysis artifact identifies object 4 for the successful samples. A reader can locate that stream in the generated file, extract the stored bytes, run a Deflate decompressor, and compare the result with the recorded inflated byte count. The object number makes the measurement checkable rather than an unattributed ratio.

One sample is sufficient only for these single-stream fixtures

Each generated corpus file has one stream, so the successful sample covers the only stream in that fixture. A multi-stream document would require more samples before making a statement about all of its streams; the current analyzer and corpus do not provide that broader evidence.

Untested compression cases stay unmeasured

The corpus does not contain a near-one inflation example, an image-heavy document, encrypted content, or a deliberately corrupt Flate stream. The guide therefore does not assign measured ratios to those cases. They require separate fixtures if they become part of a future claim.

A reproducible compression claim needs the input and method

For this corpus, the evidence names the generated file, the stream object, compressed bytes, inflated bytes, and ratio. Keeping those fields together prevents a filter label or isolated percentage from being repeated without the fixture that produced it.

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 inflated a real FlateDecode stream from each compressed corpus file and compared the compressed size to the inflated size, so the compression claim rests on a decompressed result rather than on a filter name.

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