From b56bb840bf90c41ff04ab7a50ac1dc9e0c6d2437 Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Mon, 2 Nov 2026 07:34:01 +0800 Subject: [PATCH] M2: a stroke colour and a shading in an Indexed space with no lookup table keep the upstream handling (ISO 32000-2 8.6.6.3) Tests for the mutation pass on c709dd99e: - a_fill_colour_in_a_space_with_no_table_keeps_the_fallback now covers CS/SC too; - a_shading_in_a_space_with_no_table_is_not_drawn checks that only images and the colour operators see a tableless space. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv --- crates/render/tests/it/indexed_lookup.rs | 29 +++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/render/tests/it/indexed_lookup.rs b/crates/render/tests/it/indexed_lookup.rs index 9fad023927..f02cf08de4 100644 --- a/crates/render/tests/it/indexed_lookup.rs +++ b/crates/render/tests/it/indexed_lookup.rs @@ -40,12 +40,17 @@ fn pdf(colour_space: &str, content: &str, extra: &[&[u8]]) -> Vec { /// As `pdf`, with the colour space resource named `resource` rather than `/P`. fn pdf_with_resource(resource: &str, colour_space: &str, content: &str, extra: &[&[u8]]) -> Vec { + pdf_with_resources(resource, colour_space, "", content, extra) +} + +/// As `pdf_with_resource`, with `more` entries in the page's resource dictionary. +fn pdf_with_resources(resource: &str, colour_space: &str, more: &str, content: &str, extra: &[&[u8]]) -> Vec { let mut objs: Vec> = vec![ b"<< /Type /Catalog /Pages 2 0 R >>".to_vec(), b"<< /Type /Pages /Kids [3 0 R] /Count 1 >>".to_vec(), format!( "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Contents 4 0 R \ - /Resources << /XObject << /Im 5 0 R >> /ColorSpace << /{resource} {colour_space} >> >> >>" + /Resources << /XObject << /Im 5 0 R >> /ColorSpace << /{resource} {colour_space} >> {more} >> >>" ) .into_bytes(), format!("<< /Length {} >>\nstream\n{content}\nendstream", content.len()).into_bytes(), @@ -427,5 +432,27 @@ fn a_fill_colour_in_a_space_with_no_table_keeps_the_fallback() { let page = render(pdf(&format!("[/Indexed /DeviceRGB 7 {lookup}]"), content, &[])); assert_eq!(columns(&page)[0], WHITE, "{lookup:?}: DeviceGray 1"); assert_eq!(page.warnings, vec![RenderWarning::ColourFallback], "{lookup:?}"); + // A stroke colour the same way. + let content = "1 0 0 rg 0 0 200 200 re f /P CS 1 SC 20 w 0 100 m 200 100 l S"; + let page = render(pdf(&format!("[/Indexed /DeviceRGB 7 {lookup}]"), content, &[])); + assert_eq!(columns(&page)[0], WHITE, "{lookup:?}: a DeviceGray 1 stroke"); + assert_eq!(page.warnings, vec![RenderWarning::ColourFallback], "{lookup:?}"); } } + +/// Only images and the colour operators handle a space with no table: anything else reads it as +/// upstream did, as a space that cannot be read. A shading in one is not drawn. +#[test] +fn a_shading_in_a_space_with_no_table_is_not_drawn() { + let shading = |cs: &str| { + format!( + "/Shading << /Sh << /ShadingType 2 /ColorSpace {cs} /Coords [0 0 200 0] \ + /Function << /FunctionType 2 /Domain [0 1] /C0 [0] /C1 [1] /N 1 >> /Extend [true true] >> >>" + ) + }; + let page = render(pdf_with_resources("P", "/DeviceRGB", &shading("[/Indexed /DeviceRGB 1 null]"), "/Sh sh", &[])); + assert_eq!(columns(&page), [WHITE; 8]); + // The same shading with a table draws. + let page = render(pdf_with_resources("P", "/DeviceRGB", &shading("[/Indexed /DeviceRGB 1 ]"), "/Sh sh", &[])); + assert_ne!(columns(&page), [WHITE; 8]); +}