From 6179cf795d2d863a89489889e3a05f8d812ba6e7 Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Mon, 2 Nov 2026 02:06:51 +0800 Subject: [PATCH] M2: two Indexed resources are cached apart, and a stroke colour in a recovered Indexed space is reported (ISO 32000-2 8.6.6.3) Mutation pass on patch (60): - keying the resource colour-space cache with one key for every space survived the earlier tests, which used one Indexed resource a page: raster::tests::indexed_lookup_cache::two_indexed_resources_are_cached_apart kills it; - the ColorFallback report for a recovered space set with CS had no test (only cs was tested): a stroke case in a_fill_or_stroke_colour_in_a_short_table_is_padded_and_reported fails with that report removed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv --- .../render/src/raster/tests/indexed_lookup_cache.rs | 12 ++++++++++++ crates/render/tests/it/indexed_lookup.rs | 8 ++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/render/src/raster/tests/indexed_lookup_cache.rs b/crates/render/src/raster/tests/indexed_lookup_cache.rs index af772b0126..25a3a8fa75 100644 --- a/crates/render/src/raster/tests/indexed_lookup_cache.rs +++ b/crates/render/src/raster/tests/indexed_lookup_cache.rs @@ -61,3 +61,15 @@ fn inline_images_naming_one_indexed_resource_read_its_lookup_once() { let short = page(20, 7, b"\xFF\x00\x00\x00\xFF\x00"); assert_eq!(lookup_reads(short), [1, 1], "a short table: read once, then cached"); } + +/// Two Indexed resources whose arrays differ (`/P` names object 5, `/Q` object 6) are two +/// spaces: each is built, and its table read, once, and neither stands in for the other. +#[test] +fn two_indexed_resources_are_cached_apart() { + let image = |name: &str| format!("q 20 0 0 20 0 0 cm BI /W 2 /H 1 /BPC 8 /CS /{name} /F /AHx ID 0001> EI Q"); + let content = [image("P"), image("Q"), image("P"), image("Q")].join("\n"); + let resources = "<< /ColorSpace << /P [/Indexed /DeviceRGB 1 5 0 R] /Q [/Indexed /DeviceRGB 1 6 0 R] >> >>"; + let (red_green, blue_white): (&[u8], &[u8]) = (b"\xFF\x00\x00\x00\xFF\x00", b"\x00\x00\xFF\xFF\xFF\xFF"); + let pdf = image_cache_pdf(&content, resources, &[("<< /Length 6 >>", red_green), ("<< /Length 6 >>", blue_white)]); + assert_eq!(lookup_reads(pdf), [2, 2], "each table read once"); +} diff --git a/crates/render/tests/it/indexed_lookup.rs b/crates/render/tests/it/indexed_lookup.rs index 6c89da629e..2195b60fd9 100644 --- a/crates/render/tests/it/indexed_lookup.rs +++ b/crates/render/tests/it/indexed_lookup.rs @@ -299,10 +299,10 @@ fn an_unreadable_base_keeps_its_fallback() { } } -/// A fill colour set in such a space (`cs`, `sc`) draws through the same recovered table, and +/// A fill or stroke colour set in such a space (`cs`, `sc`, `CS`, `SC`) draws through the same recovered table, and /// the recovery is reported as `ColourFallback`, as the upstream DeviceGray fallback was. #[test] -fn a_fill_colour_in_a_short_table_is_padded_and_reported() { +fn a_fill_or_stroke_colour_in_a_short_table_is_padded_and_reported() { let fill = |index: u8| format!("/P cs {index} sc 0 0 200 200 re f"); let short = format!("[/Indexed /DeviceRGB 7 <{}>]", entries(2)); let page = render(pdf(&short, &fill(1), &[])); @@ -310,6 +310,10 @@ fn a_fill_colour_in_a_short_table_is_padded_and_reported() { assert_eq!(page.warnings, vec![RenderWarning::ColourFallback]); let page = render(pdf(&short, &fill(5), &[])); assert_eq!(columns(&page)[0], BLACK, "entry 5 is padding"); + // A stroke colour (`CS`, `SC`) the same way. + let page = render(pdf(&short, "/P CS 1 SC 20 w 0 100 m 200 100 l S", &[])); + assert_eq!(columns(&page)[0], COLOURS[1], "the stroke is green"); + assert_eq!(page.warnings, vec![RenderWarning::ColourFallback]); let page = render(pdf(&format!("[/Indexed /DeviceRGB 7 <{TABLE}>]"), &fill(1), &[])); assert_eq!(columns(&page)[0], COLOURS[1]); assert!(page.warnings.is_empty(), "{:?}", page.warnings);