From 0a0986483378b57d62103a36490bd66166d32c8f Mon Sep 17 00:00:00 2001 From: Tanish Manku Date: Mon, 12 Oct 2026 12:57:39 -0700 Subject: [PATCH] F1, F2: adapt to main and re-pin the Arabic layout golden Rebase onto 7163ba3 (396 commits on). Three adaptations, none of them behavioural: - `crates/fonts` renamed its `arabic` module to `shaping` and added Thai, so `Family::width`'s callers moved; the italic argument is re-applied to all six, including one new call site main added in `draw_laid_shaped`. - `Metrics::text_of` now returns `Cow`, so the F2 tests compare through `as_deref()`. - `unicode_map` writes into a struct field now, so `/Differences` inserts go to `out.encoding`. `SMALL_ITEM_FINGERPRINT` in added/arabic_tests.rs is re-pinned, and this is the part worth reviewing. That golden is the appearance-stream pin F1's acceptance criteria asked for, which landed from the Arabic work after F1 was written; it caught the change correctly. The item's Latin run is "Hi " in Helvetica 12pt. The old character-class guess called H 680, i 260 and space 260 (14.4pt); the published metrics are 722, 222 and 278 (14.664pt), verified against the table. The Arabic clusters that follow start 0.264pt further right, which is the intended effect: the glyphs now land where the font says they do. The Arabic path itself is untouched. Also dropped from the rebase: a re-added ATTRIBUTION entry for vendor/hayro-interpret/assets/LAB.icc, which main deleted in favour of CGATS001Compat-v2-micro.icc. Carrying it would have attributed a file that no longer exists. Co-Authored-By: Claude --- crates/edit/src/added.rs | 6 ++---- crates/edit/src/added/arabic_tests.rs | 7 ++++++- crates/edit/src/tests.rs | 6 +----- crates/fonts/src/pdf.rs | 8 ++++---- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/crates/edit/src/added.rs b/crates/edit/src/added.rs index ad3132fc..9f13047b 100644 --- a/crates/edit/src/added.rs +++ b/crates/edit/src/added.rs @@ -11,9 +11,7 @@ use std::collections::{HashMap, HashSet}; use pdfcraft_cos::{Dict, Document, ObjRef, Object, PdfString, Stream}; -use pdfcraft_fonts::{ - GlyphError, GlyphOutline, ShapedCluster, ShapedScript, Std14, literal, shape_arabic, shape_thai, shaped_glyph, win_ansi, -}; +use pdfcraft_fonts::{GlyphError, GlyphOutline, ShapedCluster, ShapedScript, Std14, literal, shape_arabic, shape_thai, shaped_glyph, win_ansi}; use unicode_bidi::{Level, ParagraphBidiInfo}; use crate::{EditError, MarkKind, check, contents, n, page_list, place_tagged}; @@ -286,7 +284,7 @@ fn thai_lines(t: &AddedText, para: &str, width: f64) -> Result, Edit Piece::Shaped(clusters) => atoms.extend(clusters.into_iter().map(|c| (c.text, c.advance * t.size))), Piece::Latin(s) => atoms.extend(s.chars().map(|c| { let text = c.to_string(); - let w = t.family.width(&text, t.size, t.bold); + let w = t.family.width(&text, t.size, t.bold, t.italic); (text, w) })), } diff --git a/crates/edit/src/added/arabic_tests.rs b/crates/edit/src/added/arabic_tests.rs index 4321412b..d4ebb73e 100644 --- a/crates/edit/src/added/arabic_tests.rs +++ b/crates/edit/src/added/arabic_tests.rs @@ -8,7 +8,12 @@ use super::*; /// Regression fingerprint for the small-item fixture: drawing bytes, font map and document /// objects, with generated streams decoded so Flate backend selection cannot change the hash. -const SMALL_ITEM_FINGERPRINT: u64 = 4_811_174_621_764_261_623; +// Re-pinned when the standard-14 metrics became exact (F1). The Latin run in the fingerprinted +// item is "Hi " in Helvetica 12pt: the old character-class guess called H 680, i 260 and space +// 260 (14.4pt), where the published metrics are 722, 222 and 278 (14.664pt). The Arabic clusters +// that follow therefore start 0.264pt further right, which is the whole point of F1 -- the glyphs +// now land where the font says they do. Nothing about the Arabic path changed. +const SMALL_ITEM_FINGERPRINT: u64 = 9_200_256_185_091_160_371; /// A cluster that shows one character with the single glyph `id`, at `advance` em. fn cluster(id: u32, advance: f64) -> ShapedCluster { diff --git a/crates/edit/src/tests.rs b/crates/edit/src/tests.rs index 2bc44b6d..4bcaef81 100644 --- a/crates/edit/src/tests.rs +++ b/crates/edit/src/tests.rs @@ -3251,11 +3251,7 @@ fn an_items_measured_face_is_the_face_it_is_written_as() { let measured = family.std14(bold, italic); assert_eq!(written, measured.base_font(), "{family:?} bold={bold} italic={italic}"); // ... and the public measure reaches that face, rather than an upright stand-in. - assert_eq!( - family.width("Anna", 12.0, bold, italic), - measured.text_width("Anna", 12.0), - "{family:?} bold={bold} italic={italic}" - ); + assert_eq!(family.width("Anna", 12.0, bold, italic), measured.text_width("Anna", 12.0), "{family:?} bold={bold} italic={italic}"); } } } diff --git a/crates/fonts/src/pdf.rs b/crates/fonts/src/pdf.rs index ef20f581..cd53ab0b 100644 --- a/crates/fonts/src/pdf.rs +++ b/crates/fonts/src/pdf.rs @@ -1729,7 +1729,7 @@ mod encoding_tests { let (doc, d) = simple("Custom", None, Some(differences(vec![Object::Int(65), Object::name("g7")]))); let m = Metrics::from_dict(&doc, &d); assert_eq!(m.text_of(65), None); - assert_eq!(m.text_of(66), Some("B"), "neighbouring codes keep their meaning"); + assert_eq!(m.text_of(66).as_deref(), Some("B"), "neighbouring codes keep their meaning"); } #[test] @@ -1751,7 +1751,7 @@ mod encoding_tests { // /Differences still applies on top of a built-in encoding we cannot read. let (doc, d) = simple("Symbol", Some(4), Some(differences(vec![Object::Int(0x61), Object::name("uni03B1")]))); let m = Metrics::from_dict(&doc, &d); - assert_eq!(m.text_of(0x61), Some("\u{3b1}"), "the font told us this one"); + assert_eq!(m.text_of(0x61).as_deref(), Some("\u{3b1}"), "the font told us this one"); assert_eq!(m.text_of(0x62), None, "and nothing about this one"); } @@ -1763,7 +1763,7 @@ mod encoding_tests { enc.set(b"BaseEncoding".to_vec(), Object::name(name)); let (doc, d) = simple("Symbol", Some(4), Some(Object::Dict(enc))); let m = Metrics::from_dict(&doc, &d); - assert_eq!(m.text_of(0x61), Some("a"), "{name} was asked for by name"); + assert_eq!(m.text_of(0x61).as_deref(), Some("a"), "{name} was asked for by name"); } } @@ -1775,7 +1775,7 @@ mod encoding_tests { fn the_symbolic_flag_alone_does_not_suppress_the_latin_default() { let (doc, d) = simple("SomeSubsetFont", Some(4), None); let m = Metrics::from_dict(&doc, &d); - assert_eq!(m.text_of(0x61), Some("a")); + assert_eq!(m.text_of(0x61).as_deref(), Some("a")); assert_eq!(m.decode(b"abc"), "abc"); } }