Draw CJK interface text in a sans font, not a mincho (#125)

Fallback fonts are ordered sans first (BIZ UDPGothic, then Noto Sans CJK, then other sans faces), with mincho and serif faces last, so Japanese and Chinese interface text isn't set like a book page.
This commit is contained in:
Firefly
2026-11-02 03:10:52 +09:00
committed by GitHub
parent 9d3ea967fb
commit 376e8efcbd
+29
View File
@@ -237,6 +237,8 @@ pub fn install_fonts(ctx: &egui::Context) {
fallbacks.push(name);
}
}
// Sans first: a mincho/serif fallback makes Japanese and Chinese UI text look like prose.
fallbacks.sort_by_key(|name| fallback_rank(name));
fonts.families.entry(FontFamily::Proportional).or_default().insert(0, "DMSans".to_owned());
fonts.families.entry(FontFamily::Monospace).or_default().insert(0, "JetBrainsMono".to_owned());
for fam in [FontFamily::Proportional, FontFamily::Monospace] {
@@ -251,6 +253,23 @@ pub fn install_fonts(ctx: &egui::Context) {
ctx.set_fonts(fonts);
}
/// Fallback order: a Japanese UI sans first (BIZ UDPGothic), then other sans faces, then anything
/// else, with mincho/serif last.
fn fallback_rank(name: &str) -> u8 {
let n = name.to_ascii_lowercase();
if n.contains("udpgothic") {
0
} else if n.contains("sans cjk") {
1
} else if n.contains("gothic") || n.contains("sans") {
2
} else if n.contains("mincho") || n.contains("serif") {
4
} else {
3
}
}
pub fn medium(size: f32) -> FontId {
FontId::new(size, FontFamily::Name("medium".into()))
}
@@ -405,6 +424,16 @@ mod tests {
0.2126 * ch(c.r()) + 0.7152 * ch(c.g()) + 0.0722 * ch(c.b())
}
/// A Japanese sans comes before a serif, so interface text is not set like a book page.
#[test]
fn fallback_fonts_are_sans_first() {
let rank = |family: &str| fallback_rank(&format!("craft:{family}:Regular"));
assert!(rank("BIZ UDPGothic") < rank("Noto Sans CJK SC"));
assert!(rank("Noto Sans CJK SC") < rank("Shippori Mincho"));
assert!(rank("BIZ UDMincho") <= rank("Shippori Mincho"));
assert_eq!(rank("Shippori Mincho"), 4);
}
/// Releases are built with craft-fonts (#19), and so is CI; a build without them has only
/// DM Sans, and shows other scripts as boxes.
#[test]