fix(render): Type 3 text ignores rendering modes other than invisible (vendored hayro-interpret patch 27) (fixes #1469) (#1478)

This commit is contained in:
Marco Arnulfo 2026-10-13 06:03:47 +02:00 committed by GitHub
parent c6128c039a
commit 0d300c6a41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 53 additions and 2 deletions

View File

@ -5885,4 +5885,46 @@ trailer << /Root 1 0 R >>
assert!([(5, 5), (50, 50), (95, 95)].iter().all(|(x, y)| px(&p, *x, *y) == red), "{content}: no stray clip");
}
}
/// ISO 32000-1 §9.3.6: only text rendering mode 3 has any effect on a Type 3 font (vendored
/// hayro-interpret patch in `show_glyph`). Print-stream PDFs set `2 Tr` for Type 3 bitmap
/// fonts, and the glyph procedure ran twice (fill, then stroke), so its anti-aliased edges
/// doubled up and the text looked bold; under `1 Tr` an uncoloured glyph took the stroking
/// colour, and under `7 Tr` it disappeared. Both glyph kinds have edges between pixels.
#[test]
fn text_rendering_modes_other_than_invisible_draw_type3_glyphs_as_filled() {
let render = |glyph: &str, mode: u8| {
let content = format!("BT 1 0 0 RG 6 w 0 g /F1 20 Tf {mode} Tr 10.3 10.3 Td (A) Tj ET");
let pdf = format!(
"%PDF-1.7
1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj
3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 40 40] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >> endobj
4 0 obj << /Length {} >> stream
{content}
endstream endobj
5 0 obj << /Type /Font /Subtype /Type3 /FontBBox [0 0 1 1] /FontMatrix [1 0 0 1 0 0] /FirstChar 65 /LastChar 65 /Widths [1]
/Encoding << /Differences [65 /a] >> /CharProcs << /a 6 0 R >> >> endobj
6 0 obj << /Length {} >> stream
{glyph}
endstream endobj
trailer << /Root 1 0 R >>
%%EOF",
content.len(),
glyph.len()
);
let mut r = PageRenderer::new(Arc::new(pdf.into_bytes()), RenderConfig::default());
let p = r.render(RenderRequest { page: 0, kind: RequestKind::Pixels, tile: None, scale: 1.0, tag: 0, ..Default::default() });
assert!(p.error.is_none(), "{glyph} {mode} Tr: {:?}", p.error);
p.rgba
};
for glyph in ["1 0 0 0 1 1 d1 0.12 0.12 0.71 0.71 re f", "1 0 d0 0.12 0.12 0.71 0.71 re f"] {
let filled = render(glyph, 0);
assert!(filled.as_chunks::<4>().0.iter().any(|p| p[0] < 128), "{glyph}: the glyph paints");
for mode in [1, 2, 4, 5, 6, 7] {
assert!(render(glyph, mode) == filled, "{glyph}: {mode} Tr draws as 0 Tr");
}
assert!(render(glyph, 3).as_chunks::<4>().0.iter().all(|p| p[..3] == [255, 255, 255]), "{glyph}: 3 Tr hides the glyph");
}
}
}

2
vendor/README.md vendored

File diff suppressed because one or more lines are too long

View File

@ -69,7 +69,16 @@ pub(crate) fn show_glyph<'a>(
device.set_blend_mode(ctx.get().graphics_state.blend_mode);
let stroke_props = ctx.stroke_props();
match ctx.get().text_state.render_mode {
// PdfCraft patch: only mode 3 has any effect on text in a Type 3 font (ISO 32000-1
// 9.3.6). Stroking ran the glyph procedure a second time, so `2 Tr` made bitmap fonts
// look bold, and the clip modes have no outline to clip with.
let render_mode = match (glyph, ctx.get().text_state.render_mode) {
(Glyph::Type3(_), TextRenderingMode::Invisible) => TextRenderingMode::Invisible,
(Glyph::Type3(_), _) => TextRenderingMode::Fill,
(_, mode) => mode,
};
match render_mode {
TextRenderingMode::Fill => {
device.draw_glyph(
glyph,