mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-14 19:27:07 -04:00
Merge branch 'pr/639' into batch1
This commit is contained in:
commit
9bd099c0ae
3 files changed
+75
-5
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
Reference in new issue
Block a user