Merge pull request #1191 from storytold/fix/thai-craft-font

Type: Noto Sans Thai from craft-fonts is the Thai fallback, on the web too
This commit is contained in:
BFlat 2026-10-11 19:52:51 +07:00 committed by GitHub
commit d17c07f343
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 32 additions and 7 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,8 @@
//! Fonts from the optional craft-fonts build input (<https://github.com/storytold/craft-fonts>).
//!
//! Built with `CRAFT_FONTS_DIR=<craft-fonts checkout>`, [`CRAFT_FONTS`] holds every font in its
//! manifest (Japanese UI and document fonts, and Arabic fonts); built without it, it is empty and Japanese
//! text falls back to the installed system fonts. Release builds always set it. See `build.rs`
//! manifest (Japanese UI and document fonts, Arabic fonts and Noto Sans Thai); built without it,
//! it is empty and that text falls back to the installed system fonts. Release builds always set it. See `build.rs`
//! and craftrules `standards/fonts.md`.
/// A font from the optional craft-fonts build input (empty unless built with `CRAFT_FONTS_DIR`).
@ -39,6 +39,10 @@ impl CraftFont {
pub fn is_arabic(&self) -> bool {
self.scripts.contains(&"Arab")
}
/// Is the font for Thai text?
pub fn is_thai(&self) -> bool {
self.scripts.contains(&"Thai")
}
/// A serif (Mincho) family, for document text.
pub fn is_mincho(&self) -> bool {
self.family.contains("Mincho")
@ -63,11 +67,13 @@ pub fn japanese_document_fonts() -> Vec<&'static CraftFont> {
/// The craft-fonts faces the document font database loads: the Japanese ones in
/// [`japanese_document_fonts`] order, then the Arabic ones in manifest order (Noto Sans Arabic
/// first: the fallback for Arabic text; the other families are picked by name). Empty when built
/// without craft-fonts.
/// first: the fallback for Arabic text; the other families are picked by name), then the Thai ones
/// (Noto Sans Thai: the fallback for Thai text where no Thai font is installed, and on the web).
/// Empty when built without craft-fonts.
pub fn document_fonts() -> Vec<&'static CraftFont> {
let mut v = japanese_document_fonts();
v.extend(CRAFT_FONTS.iter().filter(|f| f.is_arabic() && !f.is_japanese()));
v.extend(CRAFT_FONTS.iter().filter(|f| f.is_thai() && !f.is_japanese() && !f.is_arabic()));
v
}
@ -104,8 +110,10 @@ mod tests {
assert!(name.ends_with(".ttf") || name.ends_with(".otf"), "{}", w.url);
assert!(w.integrity.starts_with("sha256-") && w.integrity.len() == "sha256-".len() + 44, "{}", w.integrity);
}
// The fallbacks the first frame may need: Noto Sans Arabic and Noto Sans Thai, where the
// checkout has them.
let startup: Vec<_> = WEB_FONTS.iter().filter(|w| w.startup).map(|w| w.family).collect();
let noto = CRAFT_FONTS.iter().any(|f| f.family == "Noto Sans Arabic");
assert_eq!(startup, if noto { vec!["Noto Sans Arabic"] } else { vec![] });
let expected: Vec<_> = ["Noto Sans Arabic", "Noto Sans Thai"].into_iter().filter(|n| CRAFT_FONTS.iter().any(|f| f.family == *n)).collect();
assert_eq!(startup, expected);
}
}

View File

@ -729,6 +729,20 @@ fn burasagari_leaves_latin_commas_and_full_stops_inside_the_line() {
}
}
/// With craft-fonts and no installed fonts (the web), Thai is set in Noto Sans Thai, not as empty
/// boxes. Skipped when built without craft-fonts or with a checkout that has no Thai font.
#[test]
fn thai_without_installed_fonts_takes_the_craft_fonts_thai_font() {
if !crate::CRAFT_FONTS.iter().any(|f| f.is_thai()) {
eprintln!("skipping: no Thai font in craft-fonts (set CRAFT_FONTS_DIR to a checkout that has one)");
return;
}
let l = layout(db(), &point("ภาษาไทย", style(20.0)));
assert!(!l.glyphs.is_empty() && l.glyphs.iter().all(|g| g.gid != 0), "no .notdef");
let families: Vec<String> = l.glyphs.iter().filter_map(|g| db().face_by_id(g.font_id)).map(|f| f.family.clone()).collect();
assert!(families.iter().all(|f| f == "Noto Sans Thai"), "{families:?}");
}
/// Thai is written without spaces between words: area type breaks its lines between words (as a
/// model of Thai words finds them), never inside a word nor before a vowel or tone mark.
#[test]

View File

@ -4,6 +4,7 @@
# background = fetched after the app starts (fonts users pick by name)
# Read by build.rs (the WEB_FONTS table) and `cargo xtask web-fonts` (copies the files, a Trunk hook).
startup | Noto Sans Arabic | Regular
startup | Noto Sans Thai | Regular
background | Cairo | Regular
background | Tajawal | ExtraLight
background | Tajawal | Light

View File

@ -133,6 +133,8 @@ CRAFT_FONTS_DIR="$PWD/../craft-fonts" cargo xtask ci # also runs the Japanese-
embed everywhere, was 8.7 MB, so the web build is still smaller than before.
- Document text: the Japanese faces join the font database after the bundled fonts (Mincho first),
so they are the fallback for Japanese after the requested font; installed system fonts come after.
Noto Sans Thai (when the checkout has it) is the fallback for Thai where no Thai font is
installed, and the web build fetches it before the app starts (`crates/text/web-fonts.txt`).
- UI: `theme::install_fonts` adds them at the end of every egui family (BIZ UDPGothic first), after
the UI fonts and before the installed fonts `ui_fonts.rs` discovers for other scripts.
- Tests that need Japanese glyphs skip with a message when built without craft-fonts. The FreeBSD