From 9fca1d44f0f164e9795aed5cd44079dd0113c76b Mon Sep 17 00:00:00 2001 From: Grzegorz Martens Date: Sun, 25 Oct 2026 10:20:23 +0100 Subject: [PATCH] M11: monochrome downsampling: say an indexed image's tie goes to entry 0, its palette unread The README said an indexed image's palette decides which entry is the dark one on a tie, but dark_is_one never reads the palette: entry 0 wins whatever its colour. The README, the function's comment and the parity note now say so (from FredApps' review). Co-Authored-By: Claude Opus 5.5 --- crates/optimize/README.md | 5 +++-- crates/optimize/src/images.rs | 3 ++- parity/acrobat-features.toml | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/optimize/README.md b/crates/optimize/README.md index a76fb54c04..ae6c749ef3 100644 --- a/crates/optimize/README.md +++ b/crates/optimize/README.md @@ -46,8 +46,9 @@ let report = optimize_with_progress(&mut doc, &settings, &mut |stage| keep_going `target_ppi`: each new pixel takes the sample covering more of its area, and the dark one on a tie, so a dark line half a new pixel wide stays: black in grey and the painted part of an image mask (a 0, or a 1 with an inverting decode array), full ink in a separation (a 1, or a 0), and - entry 0 of an indexed image, whose palette says which is darker. Not one with a soft mask, whose own size - would have to follow. On a 600 ppi text page, Group 4 at 300 ppi is half the size. + entry 0 of an indexed image, whatever colour its palette gives it (the palette isn't read yet). + Not one with a soft mask, whose own size would have to follow. On a 600 ppi text page, Group 4 + at 300 ppi is half the size. - **Compression:** CCITT Group 4 (`pdfcraft_filters::encode_g4`), or 1-bit Flate when that is smaller, which only a resampled image or one packed from eight bits may be: a 1-bit image that is only stored again becomes Group 4 or stays. A CCITT image is only decoded to be resampled, diff --git a/crates/optimize/src/images.rs b/crates/optimize/src/images.rs index dc78b109ac..4c20c1b3e7 100644 --- a/crates/optimize/src/images.rs +++ b/crates/optimize/src/images.rs @@ -624,7 +624,8 @@ fn one_bit(doc: &Document, s: &Stream, eight_bit: bool) -> Option<(Vec, u32, /// Whether sample 1 is the dark one of the 1-bit image `d` describes, after its decode array /// (`[0 1]` when it has none of two numbers): the painted part of an image mask and black in grey /// decode to 0, and full ink in a separation (or a DeviceN of one colourant) to 1. An indexed image -/// says 0, as its palette, not its decode array, says which entry is darker. +/// says 0: its palette, not its decode array, says which entry is darker, and the palette isn't +/// read yet, so a tie goes to entry 0 whatever its colour. fn dark_is_one(doc: &Document, d: &Dict) -> bool { let number = |v: Option<&Object>| v.and_then(|v| doc.resolve(v).as_f64()); let (d0, d1) = match d.get(b"Decode").map(|v| doc.resolve(v)).as_deref() { diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index 41266cb293..d2ef8f4f75 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -7361,7 +7361,7 @@ status = "partial" commands = ["optimize.advanced"] tools = ["doc_optimize"] evidence = ["crates/optimize/tests/optimize.rs::images_are_measured_where_drawn_and_downsampled", "crates/optimize/tests/optimize.rs::settings_choose_what_happens", "crates/automation/tests/it/automation.rs::optimizing_through_tools", "crates/optimize/tests/optimize.rs::a_page_picture_printed_as_one_pixel_strips_is_merged_into_one_image", "crates/optimize/tests/optimize.rs::images_that_do_not_tile_a_picture_are_left_alone", "crates/optimize/tests/optimize.rs::a_deeply_nested_form_is_measured_at_its_largest_use", "crates/optimize/tests/optimize.rs::images_are_not_resampled_when_some_uses_could_not_be_measured", "crates/optimize/tests/optimize.rs::image_resolution_uses_the_pages_physical_unit", "crates/optimize/tests/optimize.rs::optimization_retains_an_image_below_threshold_on_a_scaled_page", "crates/optimize/tests/optimize.rs::shared_forms_are_measured_in_each_pages_physical_units", "crates/optimize/tests/optimize.rs::black_and_white_images_above_450_ppi_are_averaged_down_to_300", "crates/optimize/src/images.rs::it_matches_the_slow_way", "crates/automation/tests/it/automation.rs::doc_optimize_averages_black_and_white_scans_down", "crates/ui-egui/tests/it/create.rs::the_pdf_optimizer_dialog_saves_an_optimized_copy"] -notes = "colour and grayscale, bicubic, effective resolution measured where drawn (every use, with page-local UserUnit scaling, through nested forms up to 12 deep; when a use can't be measured, nothing is resampled); image fragments stacked edge to edge are merged first (side-by-side tiles not yet); monochrome (1-bit, CCITT fax and eight-bit black-and-white grey) averaged by area to 300 ppi above 450 ppi by default, a tie to the dark sample (black in grey, painted in a mask, ink in a separation, after the decode array), then stored as Group 4 or 1-bit Flate (Monochrome Images ▸ Downsample to; doc_optimize mono); not monochrome images with a soft mask, and Acrobat's bicubic and subsampling choices for monochrome aren't offered" +notes = "colour and grayscale, bicubic, effective resolution measured where drawn (every use, with page-local UserUnit scaling, through nested forms up to 12 deep; when a use can't be measured, nothing is resampled); image fragments stacked edge to edge are merged first (side-by-side tiles not yet); monochrome (1-bit, CCITT fax and eight-bit black-and-white grey) averaged by area to 300 ppi above 450 ppi by default, a tie to the dark sample (black in grey, painted in a mask, ink in a separation, after the decode array; an indexed image's entry 0, its palette not read yet), then stored as Group 4 or 1-bit Flate (Monochrome Images ▸ Downsample to; doc_optimize mono); not monochrome images with a soft mask, and Acrobat's bicubic and subsampling choices for monochrome aren't offered" [[feature]] id = "optimize.recompress-images"