mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 10:08:00 -06:00
* M11: merge image fragments, and the Standard and Mobile optimizer presets Some PDF printers draw a page picture as thousands of images one pixel row high (one real page: 3,300 strips, 4.3 MB). The optimizer works image by image, a one-row image can't be resampled, and every JPEG strip carries its own headers, so Reduce File Size saved 2 bytes on it. - pdfcraft-optimize `fragments` (Settings::merge_fragments, on by default): runs of `q … cm /Im Do Q` drawing 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, nothing else drawn between them; or, failing that, the rows drawn one under the other up to a gap) become one lossless image at their finest resolution. The image settings then resample it, and Clean Up drops the strips. A page whose content can't be read back exactly is left alone. - Presets as in Acrobat's Settings menu: Standard (Reduce File Size's choices) and Mobile (images above 144 ppi to 96 ppi, JPEG high). The PDF Optimizer dialog has a Settings menu (Custom once an option changes) and a Merge image fragments box; doc_optimize takes preset and merge_fragments and reports merged_images and fragments_merged. - A progress stage for the merge; four new strings in all 21 catalogs. On the real page: Standard/Reduce 4.3 MB → 231 KB, Mobile → 161 KB, with a rendering closer to the original than Acrobat's Mobile result (147 KB). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refresh catalog hashes after merging main --------- Co-authored-by: magdale76 <16279778+magdale76@users.noreply.github.com> 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; the smallest over all uses). 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.