Pure-Go PDF Compression Cuts Out Ghostscript and Re-Encodes Images One by One
Desktop tools can now ship PDF compression as a self-contained, zero-CGO binary without bundling Ghostscript or taking on AGPL obligations; the cost is a conservative subset that only touches images it is sure about, leaving unsupported encodings byte-for-byte.
Image Workshop 0.2.0 ships PDF compression built entirely in Go. pdfcpu handles parsing and structure optimization, while a custom image layer re-encodes embedded JPEGs and downsamples scanned pages. Ghostscript's /screen, /ebook, and /printer presets solve the same problem but come as an external AGPL process that must be packaged and maintained on macOS, Windows, and Kylin; the new path keeps everything inside the installer and never uploads documents.
The recompression step only touches images that match a narrow predicate: a single DCTDecode filter, DeviceRGB or DeviceGray, 8 bits per component, no Decode array. Each one is decoded, re-encoded at JPEG quality 75, and written back only if the output is smaller. Full-page scans are detected from structural signals rather than content-stream parsing, and anything above 150 DPI is downsampled to 150 DPI; text and vector content remain byte-identical, while encrypted and signed files are rejected. A spike on a 2400x2400 DCT image cut the whole file by 42.9% with a rendered pixel difference of 0.41/765. The implementation is in the open-source file-processor repository, 363 lines including comments, with installers for macOS, Windows, and Kylin amd64/arm64.
Ghostscript's external-process cost is bigger than its command-line simplicity suggests; for a distributed desktop tool, AGPL assessment and per-platform packaging can outweigh the effort of reimplementing image compression.
Preset-based compression treats every image the same, which is why online tools often make files worse; comparing each re-encoded image against the original turns compression into a monotonic operation.
Avoiding the content stream is a deliberate trade: structural signals identify scanned pages safely, but the 15% aspect-ratio rule means some compressible pages are left alone, accepting less gain for zero layout risk.
Fixing JPEG quality at 75 and DPI at 150 is a product decision as much as a technical one: exposing more knobs would offload engineering judgment onto users.
I ran it through Ghostscript following the article's approach; QPS went from 1200 to 1900, at the cost of an extra 300 MB of memory. How would you weigh that trade-off?
The reasons the article gives for excluding Ghostscript, such as AGPL, an external process, packaging a separate build for each of the three platforms, and a self-contained installer, none of them are touched by a QPS number. In a single-user scenario, the fair comparison axis is per-file latency and peak RSS, not server QPS. On that axis, an in-process pure Go implementation naturally avoids process startup and a resident interpreter, which is a structural advantage. QPS is a server-side benchmark, and using it to evaluate a desktop tool is grabbing the wrong yardstick.