mirror of
https://github.com/storytold/cadcraft.git
synced 2026-11-09 20:10:12 -06:00
Merge branch 'pr-1971' into land/2016
This commit is contained in:
@@ -3,10 +3,10 @@
|
||||
//! empty, `CRAFT_FONTS` is empty and the build is unchanged. It only reads the local checkout
|
||||
//! (no network). A bad checkout is a warning, or an error with `CRAFT_FONTS_REQUIRED=1`.
|
||||
//!
|
||||
//! On wasm32 it embeds only BIZ UDPGothic Regular, the `Arab` faces and the `Thai` Regular faces
|
||||
//! (about 5.9 MB), to keep the web build within hosting limits (Cloudflare Pages: 25 MiB per
|
||||
//! file). The web build has no system fonts, so these are its only fallback for Japanese,
|
||||
//! Arabic-script and Thai text.
|
||||
//! On wasm32 it embeds only BIZ UDPGothic Regular, the `Arab` and `Geor` faces and the `Thai`
|
||||
//! Regular faces (about 6.1 MB), to keep the web build within hosting limits (Cloudflare Pages:
|
||||
//! 25 MiB per file). The web build has no system fonts, so these are its only fallback for
|
||||
//! Japanese, Arabic-script, Georgian and Thai text.
|
||||
|
||||
use std::fmt::Write as _;
|
||||
use std::path::PathBuf;
|
||||
@@ -56,7 +56,7 @@ fn craft_fonts(dir: &std::path::Path) -> Result<String, String> {
|
||||
continue;
|
||||
}
|
||||
let has = |script: &str| scripts.split(',').any(|s| s.trim() == script);
|
||||
let web_face = (*family == "BIZ UDPGothic" && *style == "Regular") || has("Arab") || (has("Thai") && *style == "Regular");
|
||||
let web_face = (*family == "BIZ UDPGothic" && *style == "Regular") || has("Arab") || has("Geor") || (has("Thai") && *style == "Regular");
|
||||
if wasm && !web_face {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -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>`, `build.rs` embeds the fonts listed in its
|
||||
//! `fonts/manifest.txt` (web builds: only BIZ UDPGothic Regular, the Arabic faces and the Thai
|
||||
//! Regular faces). Without it [`CRAFT_FONTS`] is empty and nothing changes. The font table ([`crate::ttf`])
|
||||
//! `fonts/manifest.txt` (web builds: only BIZ UDPGothic Regular, the Arabic and Georgian faces and
|
||||
//! the Thai Regular faces). Without it [`CRAFT_FONTS`] is empty and nothing changes. The font table ([`crate::ttf`])
|
||||
//! lists the embedded fonts with the installed ones, under [`CraftFont::stem`]. A text style
|
||||
//! can name one (`BIZ UDPGothic`), and they are fallbacks for characters a text's font lacks.
|
||||
//! They are copied out of the binary only when first used.
|
||||
@@ -85,6 +85,7 @@ mod tests {
|
||||
Some("Arab") => '\u{0628}',
|
||||
Some("Thai") => '\u{0E01}',
|
||||
Some("Hang" | "Kore") => '가',
|
||||
Some("Geor") => 'ა',
|
||||
_ => 'A',
|
||||
};
|
||||
assert!(face.is_some_and(|x| x.has_glyph(sample)), "{} lacks {sample}", f.family);
|
||||
|
||||
@@ -276,6 +276,7 @@ fn run_language_command(app: &mut CadApp, id: &str, params: &Value) -> Option<Re
|
||||
"No installed interface font has the Vietnamese letters, so the interface stays in English. Install one that does (Segoe UI, Arial, DejaVu Sans or Noto Sans) to use Vietnamese."
|
||||
}
|
||||
"ar" => "No Arabic font is installed, so the interface stays in English. Install one (Noto Sans Arabic, for example) to use Arabic.",
|
||||
"ka" => "This build has no Georgian font, so the interface stays in English. Build with craft-fonts (its Geor face) to use Georgian.",
|
||||
_ => "No Chinese font is installed, so the interface stays in English. Install one (Noto Sans CJK, for example) to use Chinese.",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -314,7 +314,8 @@ pub fn mono() -> FontId {
|
||||
/// language: file, layout and layer names can be Japanese or Chinese whatever the interface
|
||||
/// shows (#513). In a Chinese interface (`language` "zh…") the Chinese font comes first, else a
|
||||
/// Japanese one, so names take their own glyph forms; in an Arabic interface an installed
|
||||
/// Arabic font follows. A font embedded from craft-fonts comes
|
||||
/// Arabic font follows. The embedded Georgian face loads in every language, so the language
|
||||
/// picker can show ქართული. A font embedded from craft-fonts comes
|
||||
/// last, for systems without any (and the web build, which has no system fonts). A Korean
|
||||
/// interface adds a Hangul font only when the CJK font found doesn't cover Hangul
|
||||
/// ([`korean_font`]).
|
||||
@@ -482,7 +483,8 @@ pub fn shows_vietnamese(language: &str) -> bool {
|
||||
/// shows (#513), with the interface's own face leading. A sans-serif face reads best, and a
|
||||
/// Chinese interface takes the face with its own glyphs ahead of a Japanese one — a Japanese face
|
||||
/// shares most ideographs but lacks the simplified-only ones (图 园 测), which would be tofu.
|
||||
/// العربية adds its Arabic face after those, egui's bundled faces carrying no Arabic glyphs.
|
||||
/// The Georgian face follows in every language (egui's bundled faces carry no Georgian glyphs, and
|
||||
/// the picker names the language ქართული); العربية adds its Arabic face after those.
|
||||
fn embedded_ui_faces<'a>(faces: &[&'a cadcraft_fonts::CraftFont], language: &str) -> Vec<&'a cadcraft_fonts::CraftFont> {
|
||||
let cjk = |f: &&cadcraft_fonts::CraftFont| f.style == "Regular" && f.scripts.iter().any(|s| matches!(*s, "Jpan" | "Hans" | "Hant"));
|
||||
let mut out: Vec<_> = faces.iter().copied().filter(cjk).collect();
|
||||
@@ -490,6 +492,7 @@ fn embedded_ui_faces<'a>(faces: &[&'a cadcraft_fonts::CraftFont], language: &str
|
||||
if language.starts_with("zh") {
|
||||
out.sort_by_key(|f| !f.scripts.contains(&"Hans"));
|
||||
}
|
||||
out.extend(faces.iter().copied().filter(|f| f.style == "Regular" && f.scripts.contains(&"Geor")));
|
||||
if language == "ar" {
|
||||
out.extend(faces.iter().copied().filter(|f| f.style == "Regular" && f.scripts.contains(&"Arab")));
|
||||
}
|
||||
@@ -728,12 +731,14 @@ pub fn shows_hangul(language: &str) -> bool {
|
||||
/// Whether the interface can show `language` without missing-glyph boxes: Chinese needs a font
|
||||
/// with its glyphs (installed, or the CJK face this build embedded from craft-fonts), Korean a
|
||||
/// font with Hangul, العربية an installed Arabic font or the embedded Arabic face, Tiếng Việt a
|
||||
/// system interface font with its tone-marked letters ([`vietnamese_font`]).
|
||||
/// system interface font with its tone-marked letters ([`vietnamese_font`]), ქართული the
|
||||
/// embedded Georgian face.
|
||||
pub fn can_show(language: &str) -> bool {
|
||||
match language {
|
||||
"ko" => shows_hangul("ko"),
|
||||
"vi" => vietnamese_font().is_some(),
|
||||
"ar" => arabic_font().is_some() || embedded_script("Arab"),
|
||||
"ka" => embedded_script("Geor"),
|
||||
l if l.starts_with("zh") => cjk_font().is_some() || embedded_script("Hans") || embedded_script("Hant"),
|
||||
_ => true,
|
||||
}
|
||||
@@ -843,13 +848,16 @@ mod tests {
|
||||
let japanese = face("BIZ UDPGothic", &["Jpan", "Latn"]);
|
||||
let chinese = face("Han Test Face", &["Hans", "Latn"]);
|
||||
let arabic = face("Noto Sans Arabic", &["Arab"]);
|
||||
let faces = [&japanese, &chinese, &arabic];
|
||||
let georgian = face("Noto Sans Georgian", &["Geor"]);
|
||||
let faces = [&japanese, &chinese, &arabic, &georgian];
|
||||
// Every interface reads CJK names, so both CJK faces load, the sans-serif face first.
|
||||
assert_eq!(families(&super::embedded_ui_faces(&faces, "en")), ["BIZ UDPGothic", "Han Test Face"]);
|
||||
// The Georgian face loads in every interface too, after them: the picker shows ქართული.
|
||||
assert_eq!(families(&super::embedded_ui_faces(&faces, "en")), ["BIZ UDPGothic", "Han Test Face", "Noto Sans Georgian"]);
|
||||
// A Chinese interface takes the Han face first: a Japanese face lacks 图 园 测.
|
||||
assert_eq!(families(&super::embedded_ui_faces(&faces, "zh")), ["Han Test Face", "BIZ UDPGothic"]);
|
||||
assert_eq!(families(&super::embedded_ui_faces(&faces, "zh")), ["Han Test Face", "BIZ UDPGothic", "Noto Sans Georgian"]);
|
||||
// العربية adds the Arabic face, after the CJK ones egui's bundled faces have no glyphs for.
|
||||
assert_eq!(families(&super::embedded_ui_faces(&faces, "ar")), ["BIZ UDPGothic", "Han Test Face", "Noto Sans Arabic"]);
|
||||
assert_eq!(families(&super::embedded_ui_faces(&faces, "ar")), ["BIZ UDPGothic", "Han Test Face", "Noto Sans Georgian", "Noto Sans Arabic"]);
|
||||
assert_eq!(families(&super::embedded_ui_faces(&faces, "ka")), ["BIZ UDPGothic", "Han Test Face", "Noto Sans Georgian"]);
|
||||
// Another weight is a style of a face, not an interface face of its own.
|
||||
let bold = CraftFont { family: "BIZ UDPGothic", style: "Bold", scripts: &["Jpan"], bytes: &[] };
|
||||
assert!(super::embedded_ui_faces(&[&bold], "en").is_empty());
|
||||
@@ -860,13 +868,38 @@ mod tests {
|
||||
/// leaving the installed-font answer the gate had before.
|
||||
#[test]
|
||||
fn embedded_faces_answer_for_their_scripts() {
|
||||
let faces = [face("BIZ UDPGothic", &["Jpan", "Latn"]), face("Noto Sans Arabic", &["Arab"])];
|
||||
assert!(super::any_script(&faces, "Jpan") && super::any_script(&faces, "Arab"));
|
||||
let faces = [face("BIZ UDPGothic", &["Jpan", "Latn"]), face("Noto Sans Arabic", &["Arab"]), face("Noto Sans Georgian", &["Geor"])];
|
||||
assert!(super::any_script(&faces, "Jpan") && super::any_script(&faces, "Arab") && super::any_script(&faces, "Geor"));
|
||||
assert!(!super::any_script(&faces, "Hans") && !super::any_script(&faces, "Hant"));
|
||||
// A `Bold`-only face is not the interface's, and a script code is matched whole.
|
||||
let bold = CraftFont { family: "Some Han Bold", style: "Bold", scripts: &["Hans"], bytes: &[] };
|
||||
assert!(!super::any_script(&[bold], "Hans") && !super::any_script(&faces, "Ara"));
|
||||
assert!(!super::embedded_script("Hans") || super::can_show("zh"));
|
||||
// ქართული rides on the embedded Georgian face alone.
|
||||
assert_eq!(super::can_show("ka"), super::embedded_script("Geor"));
|
||||
assert!(super::can_show("en") && super::can_show("uk"));
|
||||
}
|
||||
|
||||
/// With the craft-fonts Georgian face (release and web builds), every Mkhedruli letter and the
|
||||
/// picker's ქართული render in both families, in a Georgian and in an English interface.
|
||||
#[test]
|
||||
fn embedded_georgian_face_covers_the_alphabet_in_every_interface() {
|
||||
if !super::embedded_script("Geor") {
|
||||
return;
|
||||
}
|
||||
let letters: Vec<char> = ('ა'..='ჰ').chain("ქართული".chars()).collect();
|
||||
for language in ["ka", "en"] {
|
||||
let ctx = egui::Context::default();
|
||||
super::install_fonts(&ctx, language);
|
||||
let mut output = ctx.run_ui(Default::default(), |_| {});
|
||||
output.textures_delta.clear();
|
||||
ctx.fonts_mut(|view| {
|
||||
for family in [egui::FontFamily::Proportional, egui::FontFamily::Monospace] {
|
||||
for c in &letters {
|
||||
assert!(view.has_glyph(&egui::FontId::new(12.0, family.clone()), *c), "{language}: missing Georgian glyph {c} in {family:?}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,12 @@ catalog, the saved `ko` preference is kept but the interface falls back to Engli
|
||||
command reports that a font is needed, and the picker uses the readable label **Korean** wherever
|
||||
Hangul can't be shown. This change does not fix platform IME issues or localize engine output.
|
||||
|
||||
The bundled egui fonts have no Georgian letters. Georgian is drawn with the Noto Sans Georgian face
|
||||
(`Geor`) of the craft-fonts build input, which release builds embed (`craft-fonts-ref` in the
|
||||
workflows, and `web-craft-fonts` for the web build, whose embedded faces include it). The face loads
|
||||
in every interface language, so Georgian text (file, layout and layer names, labels) draws. A build
|
||||
without craft-fonts has no Georgian face.
|
||||
|
||||
## Greek (`el`) interface
|
||||
|
||||
The Greek translation covers all **1,330** distinct source keys collected from the existing
|
||||
|
||||
Reference in New Issue
Block a user