Merge branch 'pr/639' into batch1

This commit is contained in:
Brandon Thomas committed 2026-10-13 02:23:11 -04:00
commit 9bd099c0ae
3 files changed
+75 -5

No files matched your search

+17
View File
@@ -1959,6 +1959,23 @@ fn cjk_fallback_follows_the_language() {
assert_eq!(Some(han.family.clone()), db.fallback_for('直', primary.id(), None).map(|f| f.family.clone()));
}
#[test]
fn malayalam_falls_back_to_a_font_that_forms_its_conjuncts() {
// Run against the installed fonts: on macOS the first file mapping Malayalam is Arial
// Unicode MS, which leaves ക്ഷ as three glyphs.
let db = designcraft_fonts::FontDb::global();
if !["Malayalam Sangam MN", "Malayalam MN", "Nirmala UI", "Noto Sans Malayalam"].iter().any(|f| db.has_family(f)) {
return;
}
let text = "ക്ഷ";
let (mut d, sid, _) = doc_with(text, Rect::new(0.0, 0.0, 300.0, 100.0), ParaAttrs::default());
d.settings.glyph_fallback = true;
let cs = compose_story(&d, sid, &ComposeOptions::default());
let glyphs: Vec<_> = all_lines(&cs).iter().flat_map(|l| l.glyphs.iter()).map(|g| (g.face.family.clone(), g.gid)).collect();
assert_eq!(glyphs.len(), 1, "one conjunct: {glyphs:?}");
assert_ne!(glyphs[0].1, 0);
}
#[test]
fn missing_glyphs_are_the_fonts_box_unless_fallback_is_on() {
use designcraft_fonts::testing::font_with;
+28 -5
View File
@@ -435,6 +435,29 @@ const KOREAN_FALLBACKS: &[&str] = &[
"Source Han Sans K",
];
/// Fonts made for Malayalam (macOS, Windows, Noto, Linux). A font that only maps the characters
/// (Arial Unicode MS, first by path on macOS) sets them without conjuncts or vowel signs.
const MALAYALAM_FALLBACKS: &[&str] = &[
"Malayalam Sangam MN",
"Malayalam MN",
"Nirmala UI",
"Kartika",
"Noto Sans Malayalam",
"Noto Serif Malayalam",
"Manjari",
"Rachana",
"Meera",
"Lohit Malayalam",
];
/// The fallback chain of a script that only fonts made for it set well, whatever the language.
fn script_chain(c: char) -> Option<&'static [&'static str]> {
match c as u32 {
0x0D00..=0x0D7F => Some(MALAYALAM_FALLBACKS),
_ => None,
}
}
/// The Japanese fallback chain: the craft-fonts Japanese faces' families in document order
/// (Mincho first; none without craft-fonts), then [`JAPANESE_FALLBACKS`].
fn japanese_chain() -> &'static [&'static str] {
@@ -1375,12 +1398,12 @@ impl ScopedFonts<'_> {
}
/// A face other than `exclude` that covers `c` in `language` (a BCP 47 tag, `None` when
/// unknown). CJK characters try their language's chain first (see [`cjk_chain`]); then the
/// first face (fallback family first, then load order, the document's fonts last) that covers
/// `c`. On native, system fonts are cataloged and loaded lazily the first time no loaded face
/// covers a character.
/// unknown). CJK characters try their language's chain first (see [`cjk_chain`]), Malayalam
/// the fonts made for it (see [`script_chain`]); then the first face (fallback family first,
/// then load order, the document's fonts last) that covers `c`. On native, system fonts are
/// cataloged and loaded lazily the first time no loaded face covers a character.
pub fn fallback_for(&self, c: char, exclude: u32, language: Option<&str>) -> Option<Arc<FontFace>> {
if let Some(chain) = cjk_chain(c, language)
if let Some(chain) = cjk_chain(c, language).or_else(|| script_chain(c))
&& let Some(f) = chain.iter().find_map(|family| self.chain_face(family, c, exclude))
{
return Some(f);
+30
View File
@@ -127,3 +127,33 @@ fn chain_fonts_load_from_the_font_folders_when_first_needed() {
assert_eq!(family(&db, HAN, Some("ko")).as_deref(), Some(first_loaded("Other CJK")));
assert!(!db.is_loaded("AppleMyungjo"));
}
const KA: char = 'ക';
const VIRAMA: char = '\u{0D4D}';
const AA: char = '\u{0D3E}';
#[test]
fn malayalam_takes_a_font_made_for_it() {
// Arial Unicode MS maps Malayalam but can't shape it (no conjuncts); loaded first, it won.
let db = db_with(&[("Arial Unicode MS", &[KA, VIRAMA, AA]), ("Malayalam Sangam MN", &[KA, VIRAMA, AA])]);
for c in [KA, VIRAMA, AA] {
for language in [None, Some("en-US"), Some("ml")] {
assert_eq!(family(&db, c, language).as_deref(), Some("Malayalam Sangam MN"), "{c:?} {language:?}");
}
}
// Past the Malayalam fonts, the language-blind search as before.
let sangam = db.face("Malayalam Sangam MN", "Regular");
assert_eq!(db.fallback_for(KA, sangam.id(), None).map(|f| f.family.clone()).as_deref(), Some("Arial Unicode MS"));
}
#[test]
fn malayalam_fonts_load_from_the_font_folders_when_first_needed() {
let dir = std::env::temp_dir().join(format!("dc-malayalam-fallback-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("noto.ttf"), font_with("Noto Sans Malayalam", &[KA]).unwrap()).unwrap();
let db = FontDb::with_font_dirs(vec![dir]);
db.add_font(font_with("Arial Unicode MS", &[KA]).unwrap());
assert!(!db.is_loaded("Noto Sans Malayalam"));
assert_eq!(family(&db, KA, None).as_deref(), Some("Noto Sans Malayalam"));
}