mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 10:08:00 -06:00
The effective-resolution walk skipped any form XObject already visited once when it was nested more than four deep, with one seen-set for the whole document. A later, larger use of the same form was never measured, so Reduce File Size and Optimize PDF resampled the image for its smallest use: a 1200 px image drawn 1 in and 8 in wide through six nested forms came out 150 px wide, about 19 ppi on the large copy. - Cycles are detected on the current path of forms, not document-wide. - A form with its own resources drawn again at the same scale (only moved) is skipped, as that can't change any image's resolution; forms without resources draw their caller's images and are always walked. - Repeat walks are capped at 100,000. If the cap or the 12-level depth limit leaves a use unmeasured, every image gets INFINITY: recompressed as the settings say, never resampled. Fixes #1811 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
pdfcraft-optimize
Layer L4: the PDF Optimizer (execution plan M11.1; Acrobat's Reduce File Size and Optimize PDF ▸ Advanced optimization).
let report = optimize(&mut doc, &Settings::default())?; // Reduce File Size's choices
let report = optimize(&mut doc, &Preset::Mobile.settings())?; // smaller, for screens
// Or with progress: called before each image and the clean-up; `false` cancels.
let report = optimize_with_progress(&mut doc, &settings, &mut |stage| keep_going)?;
- Presets: Standard (Reduce File Size's choices: images above 225 ppi to 150 ppi, JPEG medium) and Mobile (above 144 ppi to 96 ppi, JPEG high), as in Acrobat's Settings menu.
- Merge image fragments (
fragments, on by default): some PDF printers draw a page picture as thousands of images one pixel row high. Runs of upright 8-bit grey or RGB images in one colour space, sharing their left and right edges and stacked edge to edge (at least four, drawn with nothing else between them), become one lossless image at their finest resolution, which the image settings then resample; the strips are dropped by Clean Up. A real page printed this way went from 4.3 MB to 231 KB (Standard) and 161 KB (Mobile). Not merged yet: side-by-side tiles, rotated strips, masks. - Images: the effective resolution of each image is measured where pages draw it (the CTM through form XObjects, up to 12 deep; the smallest over all uses). If a use can't be measured (forms nested deeper, or drawn too many times), no image is resampled. Colour and grayscale images above a threshold are resampled with a bicubic (Catmull-Rom) filter to a target resolution and stored as JPEG (Acrobat's quality steps) or ZIP; soft masks are resampled with their image. An image is replaced only if the result is smaller. Left alone: CMYK and other colour spaces, image masks, decode arrays, more than 8 bits, JPEG 2000, JBIG2 and CCITT.
- Discard objects: thumbnails, alternate images, document tags, print settings.
- Clean up: Flate for streams with no filter; for streams with an outer ASCII85 or ASCIIHex
layer, that layer is removed when the result is smaller (the inner filters keep their encoded
bytes). Streams with
/DecodeParms, images and object streams are left alone.
The engine runs Remove Hidden Information (pdfcraft-redact) for the user-data categories,
then this, then merges identical objects and writes a full, compressed save
(pdfcraft_engine::optimizer::OptimizeJob, which the UI runs on a worker thread with a
progress bar). JPEG decoding and
encoding and resampling come from the image crate.