From b75bd3c56a15eda4cc396e664928e0e2ad4cb1e5 Mon Sep 17 00:00:00 2001 From: Valentin Klein Date: Mon, 12 Oct 2026 04:42:05 +0200 Subject: [PATCH] Fonts: Palatino Linotype and Book Antiqua prefer Palatino look-alikes (#670) (#677) Co-authored-by: Claude Sonnet 5.5 Co-authored-by: Brandon Thomas --- ROADMAP.md | 1 + crates/fonts/src/word.rs | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index d9e5430..d7c31a5 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -130,6 +130,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm ## Progress log | Date | What landed | +| 2026-10-11 | Fonts: Palatino Linotype and Book Antiqua fall back to Palatino (macOS) or TeX Gyre Pagella / P052 / URW Palladio (Linux) before generic serif (#670) | |---|---| | 2026-10-11 | Word XML files (#548): Word XML Document (Flat OPC `.xml`) opens and saves through the DOCX reader and writer; Word 2003 XML opens (styles, lists, tables, sections, notes, comments, pictures, hyperlinks) | | 2026-10-11 | Font fallback indexes which characters each installed font maps, reading each font file once (#605): text in a script no installed font covers no longer re-reads every system font per distinct character (200 such characters: 13 s → 0.2 s) | diff --git a/crates/fonts/src/word.rs b/crates/fonts/src/word.rs index b4461c0..5cb79f5 100644 --- a/crates/fonts/src/word.rs +++ b/crates/fonts/src/word.rs @@ -36,6 +36,24 @@ fn substitutes(family: &str) -> &'static [&'static str] { // headings and screens. Open fonts with full Persian coverage first. const NASKH: &[&str] = &["Noto Naskh Arabic", "XB Zar", "XB Niloofar", "Amiri", "Vazirmatn", "Noto Sans Arabic", "DejaVu Sans", "Source Serif 4"]; const ARABIC_SANS: &[&str] = &["Vazirmatn", "Sahel", "Shabnam", "Samim", "Noto Sans Arabic", "Noto Naskh Arabic", "DejaVu Sans", "Source Sans 3"]; + // Palatino Linotype / Book Antiqua: macOS ships the same design as "Palatino"; open + // look-alikes with matching metrics (TeX Gyre Pagella, URW P052 / Palladio) on Linux. + const PALATINO: &[&str] = &[ + "Palatino Linotype", + "Palatino", + "Book Antiqua", + "TeX Gyre Pagella", + "P052", + "URW Palladio L", + "Palladio", + "Caladea", + "Times New Roman", + "Liberation Serif", + "Georgia", + "Noto Serif", + "DejaVu Serif", + "Source Serif 4", + ]; let f = family.to_ascii_lowercase(); if is_persian_naskh(&f) { return NASKH; @@ -44,6 +62,7 @@ fn substitutes(family: &str) -> &'static [&'static str] { return ARABIC_SANS; } match f.as_str() { + "palatino linotype" | "book antiqua" | "palatino" => PALATINO, "arial" | "helvetica" | "arial nova" => ARIAL, "times new roman" | "times" => TIMES, "courier new" | "courier" | "consolas" | "lucida console" | "cascadia code" | "cascadia mono" => MONO, @@ -52,8 +71,6 @@ fn substitutes(family: &str) -> &'static [&'static str] { | "cambria math" | "georgia" | "garamond" - | "book antiqua" - | "palatino linotype" | "constantia" | "baskerville old face" | "century" @@ -250,6 +267,19 @@ mod tests { assert!(a > 0.0 && d >= 0.0); } + #[test] + fn palatino_family_prefers_palatino_look_alikes_over_generic_serif() { + for name in ["Palatino Linotype", "Book Antiqua", "palatino"] { + let list = substitutes(name); + let pos = |n: &str| list.iter().position(|x| *x == n); + assert!(pos("Palatino").is_some() && pos("TeX Gyre Pagella").is_some() && pos("P052").is_some(), "{name}"); + // All of them come before the generic serif fallbacks (Caladea, Times New Roman). + assert!(pos("Palatino") < pos("Caladea") && pos("TeX Gyre Pagella") < pos("Times New Roman"), "{name}"); + } + // Other serif families are unchanged. + assert_eq!(substitutes("Georgia").first(), Some(&"Caladea")); + } + #[test] fn symbol_font_chars_map_to_unicode() { assert_eq!(symbol_font_char("Symbol", '\u{F0B7}'), Some('\u{2022}'));