Skip to content

PDF compression benchmarks

View as Markdown

What the optimizer saves on a public corpus that CI measures at every release, what it saved on a larger private corpus, and how to run the same measurement yourself.

On the public corpus of 77 openly licensed PDFs, release 1.1.0 saved 12.1% losslessly, 23.4% with the balanced preset and 29.5% with the lossy profile, with zero failures. On a mixed private corpus of 1,296 files (569 MiB) with default options, PdfNetStack optimized 1,003 files from 568.9 MiB to 469.0 MiB, 17.6% saved, with zero verification failures. Every optimized file rendered identically to its original with PDFium at 150 DPI. The run took about 65 seconds on an 8 core machine.

CI downloads the documents listed in eng/corpus/manifest.json: public domain and openly licensed PDFs such as NIST and NASA publications, arXiv papers, government gazettes and test files from other PDF projects. They are never committed. The run happens every week, on demand, and for every version tag, when the report is attached to the GitHub release.

Each file runs in its own process under three profiles:

  • Lossless. The default optimizer.
  • Balanced. The preset of that name: palettes for drawings and screenshots, JPEG quality 75 at full resolution, behind an SSIM gate of 0.95.
  • Lossy. JPEG quality 75, downsampling to 150 dpi, photographs to JPEG and progressive JPEG, behind the same gate.

Every kept rewrite is checked again with PdfEquivalence, the text of every page and every image are compared with the original, and PDFium renders the first pages and the last page of both. A lossless rewrite must render identically.

Release 1.1.0, 5 November 2026, on a 4 core Linux runner:

ProfileDocuments optimizedInputOutputSavedOptimizer time
Lossless65 of 77196.6 MiB172.9 MiB12.1%44.4 s
Balanced65 of 77196.6 MiB150.6 MiB23.4%154.5 s
Lossy65 of 77196.6 MiB138.6 MiB29.5%497.4 s

Zero failures across the 77 documents. The 12 documents kept as they are were signed or encrypted files, which the optimizer never rewrites. The report with every document, its time, memory and render result is attached to the 1.1.0 release as corpus-report.md and corpus-report.json.

To run it yourself:

Terminal window
pip install pypdfium2 numpy
python tools/fetch_corpus.py --cache artifacts/corpus/files
dotnet build tools/PdfNetStack.CorpusRunner -c Release
python tools/corpus_bench.py --cache artifacts/corpus/files --out artifacts/corpus/report
  • Corpus. 1,296 files, 569 MiB: invoices, reports, manuals, scanned documents, presentations and a 631 page spare parts catalogue. The corpus is not public.
  • Machine. An 8 core Windows machine.
  • Options. Defaults: lossless, CompressionEffort.Maximum, verification on, 1% minimum saving. A second run added AllowIncrementalUpdates and AllowRepairedFiles.
  • Lossless means a conforming reader cannot tell the output from the input: same pages, content, fonts, images, annotations, forms, bookmarks, structure, metadata and attachments.
  • Verified means the optimizer parsed its own output and proved it equivalent to the source, object by object, with every stream compared after decoding and JPEG images compared by DCT coefficient. A file that fails is returned unchanged and counted as a verification failure. See Fidelity and verification.
  • Render check. Independent of the verifier: both files rendered with PDFium at 150 DPI, compared pixel for pixel, with page count, geometry and extracted text per page.

The figures are those published with version 1.0.0, in October 2026, and cover the lossless passes only. The lossy profiles are measured on the public corpus above.

Optimized1,003 files, total size 568.9 MiB to 469.0 MiB (17.6% saved)
Returned unchanged38 below the 1% threshold, 130 refused by the safety rules, 125 not PDFs (empty or resource fork files)
Verification failures0
Throughputabout 65 s for the whole corpus, 8 to 9 MB/s, lossless JPEG re-encoding included
With AllowIncrementalUpdates and AllowRepairedFiles1,118 files optimized, 20.1% saved
Render check1,118 documents, 2,161 pages, zero differing pixels, identical extracted text

The 631 page catalogue, 72.6 MiB of CAD drawings already Flate compressed: 6,075 duplicate objects merged, 3.1% saved, about 5 seconds with all cores, peak working set about 690 MB. Image heavy files gain the most, often 20 to 40%, mostly from PNG prediction on raw images. Files from producers that already use object streams have little left to gain losslessly.

Editing was checked the same way: every document in the corpus was rebuilt by importing all of its pages into a new document and saving it. 1,152 documents, 2,233 pages, zero differing pixels against the originals at 150 DPI.

Measure without writing anything, then write the optimized files and a CSV report:

Terminal window
dotnet tool install -g PdfNetStack.Tool
pdfnetstack batch ./corpus # measure only
pdfnetstack batch ./corpus ./out --report report.csv # write and report

The summary prints documents optimized, retained and unsupported, the sizes, the time, the throughput and the peak memory. The CSV has one row per document. See Command line.

Then render both sets with PDFium and compare them. The script is tools/render_compare.py in the repository; PDFium is a development tool here, not a dependency of any package.

Terminal window
pip install pypdfium2 numpy
python tools/render_compare.py ./corpus ./out --dpi 150 --pages 6

Each release report is compared with the previous one, so a change in size, time or memory shows next to the release. This page shows no numbers for other tools.