Concept · Optimization
Compress and optimize PDF files
in code
Last updated: 2026-06-29
A generated PDF often carries dead weight: objects nothing references, redundant copies of identical data, and streams left uncompressed. optimize() sweeps all three away without touching a single character or pixel. compact() then packs what remains into the modern, denser file structure. Same document. Less space. Nothing thrown away.
The simplest analogy: vacuum-packing luggage
Think of a PDF as a suitcase. Over time it accumulates items packed in "just in case" that were never used, duplicate copies of the same shirt folded twice, and air gaps between everything. optimize() is the vacuum bag: it removes what was never needed, collapses identical things into one, and compresses out the air. The clothes (your content) come out exactly the same. The bag just takes up less space.
compact() is swapping the rigid hard-shell case for a compressed carry-on. Same clothes, structurally more efficient container. Nothing is thrown away. Nothing is changed. Just less volume, less weight.
What optimize() does
Three lossless passes, applied in order. Each is independent but they compound.
Drop unused objects
Any object that nothing references is removed: fonts from deleted pages, images from merged documents, leftover form fields. If no other object points to it, it is gone.
Deduplicate identical objects
Every pair of byte-identical objects (the same embedded font, the same color profile, the same resource dictionary) is collapsed to one canonical copy. All references are remapped automatically.
Flate-compress streams
Content streams and other data that arrived uncompressed are wrapped in
FlateDecode (zlib). Already-compressed streams (JPEG data, PNG
data) are left untouched.
compact(): object streams and cross-reference streams
optimize() shrinks what is inside the objects. compact() changes how the objects themselves are stored on disk.
The modern PDF file structure
PDF 1.5 introduced object streams (/ObjStm): a way to pack
many non-stream objects into a single compressed stream. Instead of each
dictionary being written individually with its own header, they are concatenated
inside one FlateDecode stream. That compresses far better because
repeated keys (/Type, /Font, /Subtype)
appear together and the compressor can exploit the repetition.
Alongside it comes the cross-reference stream (/XRef), which
replaces the classic plain-text cross-reference table with a compact binary
stream. Together they reduce the per-file overhead of the structural skeleton.
compact(true) enables both; compact(false) switches
back to the classic layout.
Note: object streams and encryption do not mix in this implementation, so
compact() is automatically disabled when encryption is active.
Lossless, by design
This is not the kind of compression that asks "reduce image quality to 72 dpi". Everything that matters stays intact.
Text and vector graphics are not altered. The content streams that draw your text and shapes go through the Flate pass only if they were uncompressed to begin with; their operators and coordinates are never changed.
Embedded JPEGs are kept verbatim. A JPEG is already a compressed image. Wrapping it in another layer would add complexity without meaningful savings, and re-encoding it would degrade quality. rust-pdf passes the raw DCT stream straight through, byte for byte, untouched.
PNG-sourced images are stored with Flate compression inside the PDF already, so the Flate pass does not reprocess them.
When to use it
Any pipeline that produces or processes PDFs benefits from a structural optimization pass before the file leaves the system.
Generated reports
Reports assembled from many sub-documents often share fonts and resources. Deduplication alone can cut the size significantly.
Email and upload limits
Trim a PDF before attaching it to an email or uploading to a portal with a file-size cap, without losing a single word.
Normalizing third-party PDFs
PDFs from external tools, scanners, or automated systems often leave data uncompressed or include stale unreferenced objects. Optimize cleans them up.
Storage at scale
When you store millions of PDFs, structural optimization compounds fast. Smaller files transfer faster and cost less in cloud egress and storage fees.
Bandwidth reduction
PDFs served over HTTP load faster and consume less bandwidth when they start life as compact as possible.
Merge pipelines
After merging multiple PDFs, shared resources are often duplicated across the combined result. optimize() collapses them to a single copy automatically.
How to compress a PDF in code
Load, optimize, optionally compact, save.
# pip install rustpdf
import rustpdf
ed = rustpdf.EditableDoc.load(open("big.pdf", "rb").read())
ed.optimize() # drop unused + dedupe + Flate-compress streams
ed.compact(True) # pack into object + xref streams
ed.save("small.pdf")
// dotnet add package RustPdf
using RustPdf;
using var ed = EditableDoc.Load(File.ReadAllBytes("big.pdf"));
ed.Optimize().Compact(true);
ed.Save("small.pdf");
// go get github.com/rustpdf/rustpdf-go@latest
ed, _ := rustpdf.Load(mustRead("big.pdf"))
defer ed.Close()
ed.Optimize()
ed.Compact(true)
ed.Save("small.pdf")
// npm install rustpdf
const { EditableDoc } = require("rustpdf");
const fs = require("fs");
const ed = EditableDoc.load(fs.readFileSync("big.pdf"));
ed.optimize().compact(true);
ed.save("small.pdf");
rust-pdf covers eight language bindings from one Rust core: Python, C#/.NET, Go, Node.js, PHP, Ruby, Delphi, and Swift. Full details in the documentation.
PDF compression FAQ
How do I compress a PDF in code?
Load your PDF into an EditableDoc, call optimize() to drop unused objects, deduplicate byte-identical objects, and Flate-compress uncompressed streams, then optionally call compact(true) to pack objects into a /ObjStm and emit a /XRef stream. Save or export the result. rust-pdf provides this API in Python, C#, Go, Node, PHP, Ruby, Delphi, and Swift.
Is the compression lossless?
Yes. optimize() and compact() are purely structural operations. Text, vector graphics, and every pixel of embedded images come out identical to the original. The library does not perform image down-sampling, colour-depth reduction, or quality reduction of any kind.
What is the difference between optimize and compact?
optimize() removes unused objects, deduplicates byte-identical objects by remapping references to one canonical copy, and Flate-compresses uncompressed streams. compact(true) packs every eligible object into an object stream (/ObjStm) and emits a cross-reference stream (/XRef) instead of a classic cross-reference table, which is the denser file structure introduced in PDF 1.5. The two operations are independent and can be combined for maximum reduction.
Will it shrink images?
Structurally, yes: images benefit from the same unused-object and deduplication passes as any other content. However, the pixel data inside each image is not altered. JPEG images are kept verbatim (their DCT stream is not re-encoded, so there is no quality loss). PNG-sourced images are already Flate-compressed inside the PDF. The library does not perform lossy down-sampling or quality reduction.
Can I compress and encrypt at once?
Not in the same pass. When encryption is enabled, object streams and cross-reference streams are automatically disabled because they do not mix in this implementation. The recommended approach is to run optimize() and compact() first, save the result, and then apply encryption in a separate step.
Compress PDF files in your language
One Rust core. Eight language bindings. Lossless structural optimization in every one. Prototype for free; license the corporate features when you ship.