diff --git a/crates/render/src/raster/tests.rs b/crates/render/src/raster/tests.rs index af535721ad..1c61511d1d 100644 --- a/crates/render/src/raster/tests.rs +++ b/crates/render/src/raster/tests.rs @@ -3457,11 +3457,19 @@ trailer << /Root 1 0 R >> /// PDFBOX-771: a fax image whose `/Rows` says more rows than its `/Height` (1,084,975,104 on a /// 2,592-column scan) is decoded to its height, as the image is (ISO 32000-2 Table 87): the /// decoder was asked for `/Rows` rows, which the size check refused, so every page drew blank. -/// Also through an `/Indexed` colour space, the filter's other output path. +/// Also through an `/Indexed` colour space, the filter's other output path. Samples past the +/// decoded data read as black there, so a second image striped black, white, black, white shows +/// that every row up to the last is decoded. #[test] fn group4_fax_with_rows_past_its_height_decodes_to_the_image_height() { const ALL_BLACK_8X4: [u8; 6] = [0x26, 0xa2, 0xfe, 0x00, 0x20, 0x02]; - for space in ["/DeviceGray", "[/Indexed /DeviceRGB 1 <000000FFFFFF>]"] { + // Each row in horizontal mode: black rows as white 0 + black 8, white rows as white 8 + + // black 0 (ITU-T T.6 ยง2.2.3.3, codes from Table 2/T.6), then EOFB. + const STRIPED_8X4: [u8; 12] = [0x26, 0xa2, 0x99, 0x86, 0xe4, 0xd4, 0x53, 0x30, 0xdc, 0x00, 0x40, 0x04]; + const BLACK: [u8; 4] = [0, 0, 0, 255]; + const WHITE: [u8; 4] = [255, 255, 255, 255]; + let images: [(&[u8], [[u8; 4]; 4]); 2] = [(&ALL_BLACK_8X4, [BLACK; 4]), (&STRIPED_8X4, [BLACK, WHITE, BLACK, WHITE])]; + for ((data, expected), space) in images.iter().flat_map(|i| ["/DeviceGray", "[/Indexed /DeviceRGB 1 <000000FFFFFF>]"].map(|s| (i, s))) { for rows in ["/Rows 5", "/Rows 1000000000", "/Rows 4294967295"] { let content = "q 20 0 0 20 5 5 cm /Im1 Do Q"; let mut pdf = format!( @@ -3475,10 +3483,10 @@ endstream endobj 5 0 obj << /Type /XObject /Subtype /Image /Width 8 /Height 4 /ColorSpace {space} /BitsPerComponent 1 /Filter /CCITTFaxDecode /DecodeParms << /K -1 /Columns 8 {rows} >> /Length {} >> stream ", content.len(), - ALL_BLACK_8X4.len() + data.len() ) .into_bytes(); - pdf.extend_from_slice(&ALL_BLACK_8X4); + pdf.extend_from_slice(data); pdf.extend_from_slice( b" endstream endobj @@ -3488,8 +3496,8 @@ trailer << /Root 1 0 R >> let mut renderer = PageRenderer::new(Arc::new(pdf), RenderConfig::default()); let page = renderer.render(RenderRequest { page: 0, kind: RequestKind::Pixels, tile: None, scale: 1.0, tag: 0, ..Default::default() }); assert!(page.error.is_none(), "{space} {rows}: {:?}", page.error); - for y in [17, 22, 27, 32] { - assert_eq!(&page.rgba[((y * 40 + 15) * 4)..][..4], &[0, 0, 0, 255], "{space} {rows}: image row at y {y} paints black"); + for (y, colour) in [17, 22, 27, 32].into_iter().zip(expected) { + assert_eq!(&page.rgba[((y * 40 + 15) * 4)..][..4], colour, "{space} {rows}: image row at y {y}"); } assert_eq!(&page.rgba[((38 * 40 + 1) * 4)..][..4], &[255, 255, 255, 255], "{space} {rows}: outside the image stays white"); }