From 0e5e83cf5c20da4fcab3c6ac8b4db38bf87a9cad Mon Sep 17 00:00:00 2001 From: Hiroshi Inukai Date: Sun, 11 Oct 2026 14:07:54 +0900 Subject: [PATCH] EPS/.ai import: a font is found by its exact PostScript name before the name is split The private data a .ai file opens with names fonts by PostScript name, and the importer split the name at its first hyphen: rounded-x-mplus-1c-black became the family "rounded", style "x-mplus-1c-black", which no font has, so the type fell back to Source Sans 3 although Rounded-X M+ 1c is installed. An installed or bundled face of that exact PostScript name now gives its own family and style; other names split as before. Co-Authored-By: Claude Opus 5.5 --- crates/eps/src/import/tests_fontnames.rs | 7 +++++++ crates/eps/src/import/text.rs | 11 ++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/eps/src/import/tests_fontnames.rs b/crates/eps/src/import/tests_fontnames.rs index f0dd9c6c..d925905e 100644 --- a/crates/eps/src/import/tests_fontnames.rs +++ b/crates/eps/src/import/tests_fontnames.rs @@ -56,3 +56,10 @@ fn abbreviated_styles_in_postscript_names_are_spelled_out() { // Whole words stay as they were. assert_eq!(crate::family_style("Helvetica-BoldOblique").1, "Bold Oblique"); } + +/// A face of that exact PostScript name gives its own family and style, however the name splits +/// (a family can hold hyphens, as Rounded-X M+ 1c's `rounded-x-mplus-1c-black` does). +#[test] +fn an_installed_face_of_the_postscript_name_names_the_font() { + assert_eq!(crate::family_style("ABCDEF+SourceSans3-Semibold"), ("Source Sans 3".to_string(), "Semibold".to_string())); +} diff --git a/crates/eps/src/import/text.rs b/crates/eps/src/import/text.rs index 702cc9f1..716e3b40 100644 --- a/crates/eps/src/import/text.rs +++ b/crates/eps/src/import/text.rs @@ -39,9 +39,11 @@ fn style_word(w: &str) -> String { .to_string() } -/// A font's family and style from its PostScript name: `Helvetica-BoldOblique` → (`Helvetica`, -/// `Bold Oblique`), `TimesNewRomanPS-BoldMT` → (`Times New Roman`, `Bold`), without a subset -/// prefix (`ABCDEF+`) or the `*1` of a re-encoded copy (`ABCDEF+RollerBabyBV*1`). +/// A font's family and style from its PostScript name, without a subset prefix (`ABCDEF+`) or the +/// `*1` of a re-encoded copy (`ABCDEF+RollerBabyBV*1`): an installed or bundled face of that +/// exact name gives its own (a family can hold hyphens: `rounded-x-mplus-1c-black` is Rounded-X +/// M+ 1c, black); else the name is split, `Helvetica-BoldOblique` → (`Helvetica`, `Bold +/// Oblique`), `TimesNewRomanPS-BoldMT` → (`Times New Roman`, `Bold`). pub fn family_style(name: &str) -> (String, String) { let name = match name.split_once('+') { Some((prefix, rest)) if prefix.len() == 6 && prefix.bytes().all(|b| b.is_ascii_uppercase()) => rest, @@ -51,6 +53,9 @@ pub fn family_style(name: &str) -> (String, String) { Some((base, n)) if !base.is_empty() && !n.is_empty() && n.bytes().all(|b| b.is_ascii_digit()) => base, _ => name, }; + if let Some(face) = vectorcraft_text::FontDb::global().find_postscript(name) { + return (face.family.clone(), face.style.clone()); + } let (family, style) = name.split_once('-').unwrap_or((name, "")); let trim = |s: &str| -> String { let s = ["PSMT", "MT", "PS"].iter().find_map(|suffix| s.strip_suffix(suffix)).unwrap_or(s);