Optimizer Options
optimize.New turns an optimize.Options value into a chain of passes, and
PdfWriter.SetOptimizer attaches that chain to the writer. Nothing runs until Write;
the chain receives the assembled object list and rewrites it in place, so optimization
is a property of how the file is written rather than a separate step over the document.
Every option is off by default. Enabling one appends its pass to the chain.
| Option | Pass | Lossless |
|---|---|---|
CombineDuplicateDirectObjects | Replaces direct objects with the same data hash by a single shared object. | Yes |
CombineDuplicateStreams | Same, for streams with identical data. | Yes |
CombineIdenticalIndirectObjects | Collapses indirect objects that serialize identically. | Yes |
CompressStreams | Flate-encodes streams that carry no filter. | Yes |
UseObjectStreams | Packs eligible objects into object streams, with cross-reference streams. | Yes |
CleanUnusedResources | Drops resource entries no content stream references: images, form XObjects, fonts, ExtGState. | Yes |
CleanFonts | Rewrites embedded TrueType programs with only the tables that are needed. | Yes |
SubsetFonts | Rewrites embedded TrueType programs with only the glyphs the document uses. | Yes |
CleanContentstream | Removes marked-content operators and shortens some operands. | No, see below |
ImageQuality (1-100) | Re-encodes images as JPEG at that quality. | No |
ImageUpperPPI | Downsamples images drawn at a higher effective PPI than the limit. | No |
Only ImageQuality and ImageUpperPPI change what the page looks like. The rest are
structural. CleanContentstream does not change rendering either, but it does discard
structure information, which is a different kind of loss.
Doing it
pdfWriter, err := reader.ToWriter(nil)
if err != nil {
return err
}
pdfWriter.SetOptimizer(optimize.New(optimize.Options{
CombineDuplicateDirectObjects: true,
CombineIdenticalIndirectObjects: true,
CombineDuplicateStreams: true,
CompressStreams: true,
UseObjectStreams: true,
ImageQuality: 80,
ImageUpperPPI: 100,
CleanUnusedResources: true,
}))
return pdfWriter.WriteToFile(outputPath)The order of the fields in the struct literal is irrelevant. optimize.New fixes the
pass order: fonts, content streams, image PPI, image quality, the four deduplication
passes, object streams, stream compression, unused resources. Font work happens before
anything has been deduplicated, and compression happens last so it sees the final
streams.
Both image options can be set together. ImageUpperPPI downsamples first, then
ImageQuality re-encodes what is left, so the two compound.
optimize.Chain is exported and takes any model.Optimizer through Append, which is
the way to run a subset in a different order or add your own pass.
Limitations
A pass that fails does not fail the write. Chain.Optimize logs the error at debug level
and moves on with the objects it had, so a document that optimizes badly comes out
correct but larger, and nothing is returned to tell you. Turning on debug logging is the
only way to see which pass gave up.
CleanContentstream strips BDC, BMC and EMC from page and form XObject content
streams. Those operators are what associate page content with the structure tree, so this
option breaks tagging, and with it PDF/UA and the A conformance levels of PDF/A. It also
removes optional content membership markers. Leave it off for tagged output.
ImageQuality skips images that are used as a soft mask by another image, and skips
JBIG2 and CCITT fax images on the grounds that they are already well compressed. If the
JPEG re-encode comes out larger than the original stream, the original is kept.
ImageUpperPPI works out the effective resolution of each image by processing page
content streams and reading the current transformation matrix at every Do. An image
that is never drawn from a page content stream, for example one referenced only from a
form XObject or an annotation appearance stream, is never measured and is left at its
original size. Soft mask images are skipped here too.
CleanUnusedResources only touches page resource dictionaries, and it pools the names it
finds across the whole document. A resource name drawn on any one page is kept in the
resources of every page that lists it, and resources inside a form XObject are not
examined at all.
CleanFonts and SubsetFonts only handle TrueType-flavored sfnt programs. OpenType
fonts with PostScript outlines, the ones whose program starts with OTTO, are skipped,
and so is any font whose rewritten program would be larger than the original. See
font subsetting for the rest.
Optimization operates on the object list, not on the file layout. If you also want the output arranged for progressive download, see linearization.
Run the example
pdf_optimize.go reads a file, attaches the chain shown above and prints the size
before and after. Everything specific to optimization is the single SetOptimizer call
in main.
git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/compress
go run pdf_optimize.go <input.pdf> <output.pdf>If this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.
View the full source
The same directory holds pdf_remove_unused_resources.go, which runs
CleanUnusedResources on its own. That is the pass to reach for when a document has been
through several edits and carries resources nothing draws any more.
Sample output
Original file: input.pdf
Original size: 266845 bytes
Optimized file: output.pdf
Optimized size: 207347 bytes
Compression ratio: 22.30%
Processing time: 53.17 ms