diff --git a/crates/compose/src/shape.rs b/crates/compose/src/shape.rs index 3c78a1392..1de4b823c 100644 --- a/crates/compose/src/shape.rs +++ b/crates/compose/src/shape.rs @@ -1169,8 +1169,9 @@ fn shape_segment( if let Some(label) = replacement.filter(|_| auto_leading.glyph_fallback) { let lang = designcraft_doc::language_tag(&p.language); let mut segments: Vec<(usize, usize, Arc)> = Vec::new(); + // The same for every character of the label: worked out once, not per character. + let prefer_sans = designcraft_fonts::sans_by_name(&p.font_family).or_else(|| style_of_substitute(&face.family)); for (at, c) in label.char_indices() { - let prefer_sans = designcraft_fonts::sans_by_name(&p.font_family).or_else(|| style_of_substitute(&face.family)); let selected = if face.covers(c) { face.clone() } else { db.fallback_for_styled(c, face.id(), lang, prefer_sans).unwrap_or_else(|| face.clone()) }; if let Some((_, end, previous)) = segments.last_mut().filter(|(_, _, previous)| previous.id() == selected.id()) { diff --git a/crates/fonts/src/fontdb/fallback.rs b/crates/fonts/src/fontdb/fallback.rs index 0b68f9c7c..73a2e0a25 100644 --- a/crates/fonts/src/fontdb/fallback.rs +++ b/crates/fonts/src/fontdb/fallback.rs @@ -120,12 +120,12 @@ pub fn sans_by_name(family: &str) -> Option { let has = |needles: &[&str]| needles.iter().any(|n| lower.contains(n)); // A Korean serif name comes first: "Myungjo Gothic" does not exist, but a name holding both // reads as the serif it names, and "Sans" inside "Noto Sans Myungjo" would otherwise win. - if has(&["batang", "myungjo", "myeongjo", "gungsuh", "\u{BC14}\u{D5D5}", "\u{BA85}\u{C870}", "\u{AD81}\u{C11C}"]) { + if has(&["batang", "myungjo", "myeongjo", "gungsuh", "\u{BC14}\u{D0D5}", "\u{BA85}\u{C870}", "\u{AD81}\u{C11C}"]) { return Some(false); } // Then the word itself, which outranks every sans indicator: "Sweet Gothic Serif" is a serif // family whatever "Gothic" suggests. "Sans serif" and "sans-serif" end with the word and do - // not mean it, so the phrase goes before the word is looked for (both noted in review). + // not mean it, so the phrase goes before the word is looked for. if lower.replace("sans-serif", "sans").replace("sans serif", "sans").contains("serif") { return Some(false); } diff --git a/crates/fonts/src/fontdb/tests/fallback.rs b/crates/fonts/src/fontdb/tests/fallback.rs index 798443524..a80730754 100644 --- a/crates/fonts/src/fontdb/tests/fallback.rs +++ b/crates/fonts/src/fontdb/tests/fallback.rs @@ -178,7 +178,7 @@ fn a_korean_family_name_says_sans_or_serif() { /// "sans serif" ends with the word it is not. The bare "serif" needle matched first, so /// "Microsoft Sans Serif" -- and the CSS generic -- classified as serif and a missing Hangul -/// glyph took the serif-first chain (noted in review of #1692). +/// glyph took the serif-first chain (#1692). #[test] fn a_sans_serif_name_is_not_read_as_serif() { for sans in ["sans-serif", "Microsoft Sans Serif", "MS Sans Serif", "Lucida Sans Serif"] { @@ -192,10 +192,15 @@ fn a_sans_serif_name_is_not_read_as_serif() { assert_eq!(sans_by_name("Noto Sans Myungjo"), Some(false)); assert_eq!(sans_by_name("Myungjo Gothic"), Some(false)); // And the word outranks every sans indicator: Sweet Gothic Serif is a serif family - // whatever "Gothic" suggests (noted in review). + // whatever "Gothic" suggests. for serif in ["Sweet Gothic Serif", "Gothic Serif", "Sans Forgetica Serif"] { assert_eq!(sans_by_name(serif), Some(false), "{serif} names itself serif"); } + // Korean serif names written in Hangul: 바탕 (Batang), 명조 (Myungjo), 궁서 (Gungsuh). 바탕 was + // spelled with the wrong second syllable, so a Hangul Batang name was not recognised. + for serif in ["\u{BC14}\u{D0D5}\u{CCB4}", "HCR \u{BC14}\u{D0D5}", "\u{D55C}\u{CEF4} \u{BA85}\u{C870}", "\u{AD81}\u{C11C}\u{CCB4}"] { + assert_eq!(sans_by_name(serif), Some(false), "{serif} is serif"); + } // Neither word present, so the caller still decides. assert_eq!(sans_by_name("monospace"), None); assert_eq!(sans_by_name("system-ui"), None);