From 1037be2d534cf390ca44f46beb896f8a23c480b5 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Wed, 14 Oct 2026 12:32:09 +0300 Subject: [PATCH] Text: an installed font beats its LFF stand-in (#1764) ISOCPEUR, ISOCTEUR and the other aliased names are real fonts; with one installed, its face draws the text instead of the stroke substitute, which has no Cyrillic. Co-Authored-By: Claude Opus 5.5 --- src/scene/text/lff.rs | 17 +++++++++++++---- src/scene/text/sysfont.rs | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/scene/text/lff.rs b/src/scene/text/lff.rs index 1e1c5c73d..d942cfd52 100644 --- a/src/scene/text/lff.rs +++ b/src/scene/text/lff.rs @@ -518,10 +518,19 @@ fn fonts_map() -> &'static HashMap { pub fn is_builtin(name: &str) -> bool { let map = fonts_map(); let key = name.trim().to_ascii_uppercase(); - map.contains_key(&key) - || key - .rsplit_once('.') - .is_some_and(|(stem, _)| map.contains_key(stem)) + let stem = key.rsplit_once('.').map_or(key.as_str(), |(stem, _)| stem); + (map.contains_key(&key) || map.contains_key(stem)) && !installed_alias(stem, name) +} + +/// An alias names a real font (ISOCPEUR is a TrueType face): an installed +/// copy of exactly that font draws it, Cyrillic included, where the LFF +/// stand-in has no such glyphs (#1764). +fn installed_alias(stem: &str, name: &str) -> bool { + use crate::scene::text::sysfont; + ALIASES.iter().any(|(alias, _)| *alias == stem) + && (sysfont::family_for_file(name).is_some() + || sysfont::canonical_family_name(stem) + .is_some_and(|family| family.eq_ignore_ascii_case(stem))) } pub fn get_font(name: &str) -> &'static Font { diff --git a/src/scene/text/sysfont.rs b/src/scene/text/sysfont.rs index e66304e5f..0c2e25cae 100644 --- a/src/scene/text/sysfont.rs +++ b/src/scene/text/sysfont.rs @@ -316,7 +316,7 @@ pub fn with_face_data_weighted( /// The family of the installed face whose file is named `name` — a bare file /// name or any path to one, matched case-insensitively. -fn family_for_file(name: &str) -> Option { +pub(crate) fn family_for_file(name: &str) -> Option { let file = name.rsplit(['/', '\\']).next()?.trim(); if file.is_empty() { return None;