Fonts: refine monospace heuristic and preserve office_twin order (#1598)

- Avoid false-positive monospace match on 'code' in split_whitespace for
  proportional families like Code Pro.
- Check office_twin before is_monospace_family in document_face so alias
  families like Courier New CE resolve to Courier New when installed.
- Add negative tests for Code Pro and test Courier New CE mapping.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Madhasoeki
2026-11-01 07:19:41 +07:00
co-authored by Copilot
parent 2ed2ce18d8
commit 8ec4f29059
2 changed files with 13 additions and 11 deletions
+5 -7
View File
@@ -105,7 +105,7 @@ pub fn is_monospace_family(family: &str) -> bool {
|| s.ends_with(" console")
|| s.ends_with(" typewriter")
|| s.ends_with(" monospace")
|| s.split_whitespace().any(|w| w == "mono" || w == "code" || w == "terminal" || w == "monospace")
|| s.split_whitespace().any(|w| w == "mono" || w == "terminal" || w == "monospace")
}
/// Families whose open substitutes ([`substitutes`]) keep their metrics, so text drawn with
@@ -1149,17 +1149,15 @@ impl FontDb {
/// Monospaced families keep a monospaced substitute instead of falling back to Calibri.
/// DeckCraft's own default family keeps [`FontDb::face`]'s fallbacks.
pub fn document_face(&self, family: &str, style: &str) -> Arc<FontFace> {
if family.eq_ignore_ascii_case(FALLBACK_FAMILY)
|| has_metric_twin(family)
|| is_monospace_family(family)
|| is_office_cloud_font(family)
|| self.has_family(family)
{
if family.eq_ignore_ascii_case(FALLBACK_FAMILY) || has_metric_twin(family) || is_office_cloud_font(family) || self.has_family(family) {
return self.face(family, style);
}
if let Some(twin) = office_twin(family) {
return self.face(twin, style);
}
if is_monospace_family(family) {
return self.face(family, style);
}
self.face(MISSING_FONT_FAMILY, style)
}
+8 -4
View File
@@ -216,9 +216,13 @@ fn missing_document_fonts_are_drawn_in_calibri() {
assert_eq!(f.family, db.face(MISSING_FONT_FAMILY, "Bold").family, "{missing:?}");
}
// PowerPoint's own twins and Windows' substitutes, when the font itself is missing.
for (missing, twin) in
[("Liberation Sans", "Arial"), ("Liberation Serif", "Times New Roman"), ("Arial CE", "Arial"), ("Times New Roman CYR", "Times New Roman")]
{
for (missing, twin) in [
("Liberation Sans", "Arial"),
("Liberation Serif", "Times New Roman"),
("Arial CE", "Arial"),
("Times New Roman CYR", "Times New Roman"),
("Courier New CE", "Courier New"),
] {
if !db.has_family(missing) {
assert_eq!(db.document_face(missing, "Regular").family, db.face(twin, "Regular").family, "{missing}");
}
@@ -261,7 +265,7 @@ fn office_fonts_have_substitutes() {
assert!(is_monospace_family(mono), "{mono} should be recognized as monospace");
}
for prop in ["Calibri", "Arial", "Times New Roman", "Inter", "Monotype Corsiva", "Monotype Baskerville", "zzz"] {
for prop in ["Calibri", "Arial", "Times New Roman", "Inter", "Monotype Corsiva", "Monotype Baskerville", "Code Pro", "zzz"] {
assert!(!is_monospace_family(prop), "{prop} should not be recognized as monospace");
}
}