mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 10:08:00 -06:00
* fix(optimize): count what form fields and comments draw with by kind The space audit claimed the AcroForm and annotations before any page resources, so the fonts in /DR and in widget and comment appearance streams were counted as Forms or Comments. A fillable form with an embedded field font showed mostly "Forms" and almost no "Fonts". Resources are now sorted by kind first, wherever they are used: page resources, the form's default resources and every annotation appearance (one stream or a dictionary of states). A form XObject's other entries now count as X Object Forms instead of overhead. Sorting nested forms' resources recursed once per form, so a long chain of nested forms overflowed the stack. The recursion is now bounded at 64 levels; deeper forms are still counted as X Object Forms through the iterative walk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ui): line up the Space Audit columns and say what it shows The percentages were drawn at the dialog's far edge, away from their header and outside the striped rows: egui gives a grid's last column the rest of the window, and the right-to-left layout used to right-align the numbers filled all of it. Every cell is now laid out first and given exactly its column's width; numeric columns and their headers are right-aligned, and the description column takes the rest, so the stripes span the dialog. A caption under the title says the table is what takes up the file's space now (not what optimizing would save), translated in every catalogue. The parity notes no longer call the space audit missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Refresh catalog hashes after merging main --------- 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.