mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-10 06:31:41 +00:00
M2: a font without a /Subtype is drawn by its /BaseFont (view.render-pages)
A font dictionary whose /Subtype is missing or the empty name (`/Subtype /`) was refused, and its text drew in the Helvetica fallback: a `/Courier-Bold` lost its fixed pitch and its columns shifted (PDFBOX-1988 Test1.pdf). /Subtype is required (ISO 32000-2 §9.6.2.1, Table 109), but other readers read such a font as Type 1 by its /BaseFont, and so does this. Any other unknown subtype is still refused. Vendored hayro-interpret patch 94. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pq7HyCHD1R3rywQBPCHXZA
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f42fda7342
commit
8da439e320
@@ -5503,6 +5503,22 @@ fn a_font_whose_post_version_has_swapped_halves_is_still_used() {
|
||||
assert_eq!(alternate([0, 0, 0, 2]), alternate([0, 2, 0, 0]), "and also with swapped halves");
|
||||
}
|
||||
|
||||
/// A font dictionary whose `/Subtype` is missing or the empty name is read as a Type 1 font,
|
||||
/// so its `/BaseFont` picks the face (vendored hayro-interpret patch 94). `/Subtype` is required
|
||||
/// (ISO 32000-2 Table 109), but such a `/Courier-Bold` was drawn in the Helvetica fallback,
|
||||
/// shifting its columns (PDFBOX-1988).
|
||||
#[test]
|
||||
fn a_font_without_a_subtype_is_drawn_by_its_base_font() {
|
||||
let render = |subtype: &str, base_font: &str| {
|
||||
let resources = format!("<< /Font << /F1 << /Type /Font {subtype} /BaseFont /{base_font} >> >> >>");
|
||||
render_first_page(image_cache_pdf("BT /F1 20 Tf 5 25 Td (iiiiii) Tj ET", &resources, &[])).rgba.to_vec()
|
||||
};
|
||||
let courier = render("/Subtype /Type1", "Courier-Bold");
|
||||
assert_ne!(courier, render("/Subtype /Type1", "Helvetica"), "Courier Bold and Helvetica differ");
|
||||
assert_eq!(render("/Subtype /", "Courier-Bold"), courier, "an empty /Subtype");
|
||||
assert_eq!(render("", "Courier-Bold"), courier, "no /Subtype");
|
||||
}
|
||||
|
||||
/// A page whose content expands far beyond its compressed stream stops at the expansion budget,
|
||||
/// which is 64 times the compressed size and at least `MAX_DECODED_STREAM`, and the page says so.
|
||||
#[test]
|
||||
|
||||
@@ -734,8 +734,8 @@ milestone = "M2"
|
||||
status = "partial"
|
||||
commands = []
|
||||
tools = ["page_render"]
|
||||
evidence = ["crates/render/src/raster/tests.rs::renders_a_blue_rectangle", "crates/automation/tests/it/automation.rs::open_inspect_render_and_find", "crates/render/src/raster/tests.rs::user_unit_scales_the_page_and_everything_on_it", "crates/render/src/raster/tests.rs::non_interpolated_minification_keeps_thin_strokes", "crates/render/src/raster/tests.rs::a_shrunk_stencil_mask_keeps_thin_strokes", "crates/render/src/raster/tests.rs::minified_image_edges_snap_to_whole_pixels", "crates/render/src/raster/tests.rs::large_non_interpolated_images_are_smoothed_when_enlarged", "crates/render/src/raster/tests.rs::small_non_interpolated_images_keep_hard_pixels_when_enlarged"]
|
||||
notes = "hayro bootstrap renderer; own DisplayList devices in M2.6. Pages with /UserUnit are shown at their size in points, as Acrobat reads the value. An image drawn below its resolution is shrunk once to whole device pixels: by area averaging unless it asks for /Interpolate (then Catmull-Rom), so the text of a 300 dpi scan at 100% keeps its strokes, a 1-bit /ImageMask text layer included (it was nearest-neighbour, which dropped them). Enlarged by more than 1%, an image of page-scan size (512 samples or more on its shorter side) is smoothed bilinearly whatever its /Interpolate, so a zoomed scan has no staircases; smaller images keep their hard pixels. Not measured against Acrobat"
|
||||
evidence = ["crates/render/src/raster/tests.rs::renders_a_blue_rectangle", "crates/automation/tests/it/automation.rs::open_inspect_render_and_find", "crates/render/src/raster/tests.rs::user_unit_scales_the_page_and_everything_on_it", "crates/render/src/raster/tests.rs::non_interpolated_minification_keeps_thin_strokes", "crates/render/src/raster/tests.rs::a_shrunk_stencil_mask_keeps_thin_strokes", "crates/render/src/raster/tests.rs::minified_image_edges_snap_to_whole_pixels", "crates/render/src/raster/tests.rs::large_non_interpolated_images_are_smoothed_when_enlarged", "crates/render/src/raster/tests.rs::small_non_interpolated_images_keep_hard_pixels_when_enlarged", "crates/render/src/raster/tests.rs::a_font_without_a_subtype_is_drawn_by_its_base_font"]
|
||||
notes = "hayro bootstrap renderer; own DisplayList devices in M2.6. Pages with /UserUnit are shown at their size in points, as Acrobat reads the value. An image drawn below its resolution is shrunk once to whole device pixels: by area averaging unless it asks for /Interpolate (then Catmull-Rom), so the text of a 300 dpi scan at 100% keeps its strokes, a 1-bit /ImageMask text layer included (it was nearest-neighbour, which dropped them). Enlarged by more than 1%, an image of page-scan size (512 samples or more on its shorter side) is smoothed bilinearly whatever its /Interpolate, so a zoomed scan has no staircases; smaller images keep their hard pixels. A font dictionary with a missing or empty `/Subtype` is drawn as a Type 1 font by its `/BaseFont`. Not measured against Acrobat"
|
||||
|
||||
[[feature]]
|
||||
id = "view.render-shadings-patterns"
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+8
-2
@@ -244,8 +244,14 @@ impl<'a> Font<'a> {
|
||||
font_resolver: &FontResolverFn,
|
||||
cmap_resolver: &CMapResolverFn,
|
||||
) -> Option<Self> {
|
||||
let f_type = match dict.get::<Name<'_>>(SUBTYPE)?.deref() {
|
||||
TYPE1 | MM_TYPE1 => {
|
||||
// PdfCraft patch (94): a font dictionary whose `/Subtype` is missing or the empty name
|
||||
// (`/Subtype /`) is read as a Type 1 font, so its `/BaseFont` still picks the font: a
|
||||
// `/Courier-Bold` was drawn in the Helvetica fallback (PDFBOX-1988). `/Subtype` is
|
||||
// required (ISO 32000-2 §9.6.2.1, Table 109); other readers fall back to Type 1 this way.
|
||||
let subtype = dict.get::<Name<'_>>(SUBTYPE);
|
||||
let subtype: &[u8] = subtype.as_deref().unwrap_or(b"");
|
||||
let f_type = match subtype {
|
||||
b"" | TYPE1 | MM_TYPE1 => {
|
||||
FontType::Type1(Rc::new(Type1Font::new(dict, font_resolver, cmap_resolver)?))
|
||||
}
|
||||
// PDFBOX-5463: PDF viewers seem to accept OpenType as well.
|
||||
|
||||
Reference in New Issue
Block a user