fonts: Georgian UI text from craft-fonts' Noto Sans Georgian (#1270)

* fonts: Georgian UI text from craft-fonts' Noto Sans Georgian

font_definitions registers the Geor face and appends it to every family, like
Arabic (and to the Arabic families after the CJK faces); the wasm filter in
crates/fonts/build.rs keeps the face. The installed-font probe for Geor stays
skipped whenever craft-fonts covers the script. Bump craft-fonts-ref to a71c20e
(storytold/craft-fonts#27).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF

* Release: build the web version with craft-fonts too (web-craft-fonts)

The shared release workflow builds the web release without CRAFT_FONTS_DIR unless web-craft-fonts
is set, so the web version would still have no Georgian face (CodeRabbit on vectorcraft#2934).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF

* test: the Arabic New Document test scrolls to Height like the other fields

With craft-fonts' Noto Sans Arabic (from a71c20e3) the rows above Height are taller, so at
392x271 the field starts partly below the dialog body's clip and the test's unscrolled
visible_label check failed (FreeBSD jobs). It stays reachable: scroll to it as the test
already does for Gutter and the margins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF

* CI: same craft-fonts pin as release (a71c20e3), so the gate checks the Georgian face

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Davit Matchakhelidze
2026-10-23 19:47:45 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 348a8a4dc9
commit 80d13911b2
6 changed files with 21 additions and 11 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
uses: storytold/craft-common-actions/.github/workflows/gate.yml@main
with:
# Same craft-fonts commit as release.yml.
craft-fonts-ref: abb83316d96aa59c1cf64784289e378fe9fa5695
craft-fonts-ref: a71c20e30af613287c504f7e685ca8fad77752bc
timeout-minutes: 90
packages: >-
pkg-config libxkbcommon-dev libwayland-dev libx11-dev libxcb1-dev libxcursor-dev
+1 -1
View File
@@ -27,4 +27,4 @@ jobs:
# Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos.
uses: storytold/craft-common-actions/.github/workflows/freebsd.yml@main
with:
craft-fonts-ref: abb83316d96aa59c1cf64784289e378fe9fa5695
craft-fonts-ref: a71c20e30af613287c504f7e685ca8fad77752bc
+2 -1
View File
@@ -24,7 +24,8 @@ jobs:
with:
app-name: designcraft
app-title: DesignCraft
craft-fonts-ref: abb83316d96aa59c1cf64784289e378fe9fa5695
craft-fonts-ref: a71c20e30af613287c504f7e685ca8fad77752bc
web-craft-fonts: true
version: ${{ inputs.version }}
secrets:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
+2 -2
View File
@@ -1,7 +1,7 @@
//! Optional craft-fonts build input (https://github.com/storytold/craft-fonts, recipe from its
//! `docs/integration.md`). With `CRAFT_FONTS_DIR=<checkout>` the fonts in its
//! `fonts/manifest.txt` are embedded as `CRAFT_FONTS`; unset, `CRAFT_FONTS` is empty. Web
//! (wasm32) builds embed only the UI face, BIZ UDPGothic Regular, to stay within the web size
//! (wasm32) builds embed only the UI faces, BIZ UDPGothic Regular and Noto Sans Georgian Regular, to stay within the web size
//! budget. It only reads the local checkout: no network.
use std::fmt::Write as _;
use std::path::PathBuf;
@@ -38,7 +38,7 @@ fn craft_fonts(dir: &std::path::Path) -> Result<String, String> {
let [family, style, file, scripts, ..] = f.as_slice() else {
return Err(format!("malformed manifest line: {line}"));
};
if wasm && !(*family == "BIZ UDPGothic" && *style == "Regular") {
if wasm && !matches!((*family, *style), ("BIZ UDPGothic" | "Noto Sans Georgian", "Regular")) {
continue;
}
let path = dir.join(file).canonicalize().map_err(|e| format!("{file}: {e}"))?;
+5 -1
View File
@@ -9149,7 +9149,11 @@ mod tests {
for _ in 0..3 {
dialog_frame(&mut app, &ctx, screen, vec![]);
}
let height = visible_label(&dialog_frame(&mut app, &ctx, screen, vec![]), screen, "66p1");
// With craft-fonts' Noto Sans Arabic the rows above are taller, so at the smallest size the
// Height field starts partly below the body's clip: scroll to it like the other fields.
let labels = dialog_frame(&mut app, &ctx, screen, vec![]);
let (_, _, body) = labels.iter().find(|(text, _, _)| text.trim() == "66p1").expect("66p1 is painted");
let height = scroll_to_label(&mut app, &ctx, screen, body.center(), "66p1");
click_dialog(&mut app, &ctx, screen, height.center());
dialog_frame(
&mut app,
+10 -5
View File
@@ -446,9 +446,9 @@ pub(crate) fn add_system_fallbacks(fonts: &mut FontDefinitions, system: &[System
/// The UI fonts: the app's own, then the craft-fonts faces from `craft` (empty without
/// `CRAFT_FONTS_DIR`) as fallbacks at the end of every family: Japanese (BIZ UDPGothic first) and
/// Simplified Chinese, in the interface language's order, then Arabic. The `arabic` families
/// Simplified Chinese, in the interface language's order, then Arabic and Georgian. The `arabic` families
/// put the Arabic face first for right-to-left runs. egui has no system-font discovery, so
/// without craft-fonts CJK and Arabic UI text has no glyphs.
/// without craft-fonts CJK, Arabic and Georgian UI text has no glyphs.
pub(crate) fn font_definitions(craft: &'static [designcraft_fonts::CraftFont], lang: &str) -> FontDefinitions {
let mut fonts = FontDefinitions::default();
let add = |fonts: &mut FontDefinitions, name: &str, data: &'static [u8]| {
@@ -466,12 +466,13 @@ pub(crate) fn font_definitions(craft: &'static [designcraft_fonts::CraftFont], l
japanese.sort_by_key(|f| (f.family != "BIZ UDPGothic", f.style != "Regular"));
let chinese = pick("Hans");
let arabic = pick("Arab");
let georgian = pick("Geor");
let name = |f: &designcraft_fonts::CraftFont| format!("craft-{}-{}", f.family, f.style);
for f in japanese.iter().chain(&chinese) {
// CJK faces sit on the Latin baseline (see `baseline_tweak`).
fonts.font_data.insert(name(f), Arc::new(FontData { tweak: baseline_tweak(f.bytes, 0), ..FontData::from_static(f.bytes) }));
}
for f in &arabic {
for f in arabic.iter().chain(&georgian) {
add(&mut fonts, &name(f), f.bytes);
}
// Han characters take the forms of the interface language: Chinese faces first for Chinese.
@@ -483,12 +484,16 @@ pub(crate) fn font_definitions(craft: &'static [designcraft_fonts::CraftFont], l
ja.sort_by_key(|f| (f.family != "BIZ UDPGothic", f.style != "Bold"));
}
let (first, second) = if zh { (&chinese, &ja) } else { (&ja, &chinese) };
stack.extend(first.iter().chain(second).chain(&arabic).map(|f| name(f)));
stack.extend(first.iter().chain(second).chain(&arabic).chain(&georgian).map(|f| name(f)));
}
// Arabic runs (rtl.rs): the Arabic face first, so letters and spaces shape as one run.
for family in ["arabic", "arabic-semibold"] {
let ui = if family == "arabic" { "ui" } else { "ui-semibold" };
let stack = arabic.iter().map(|f| name(f)).chain([ui.to_string(), "ui".to_string()]).chain(japanese.iter().chain(&chinese).map(|f| name(f)));
let stack = arabic
.iter()
.map(|f| name(f))
.chain([ui.to_string(), "ui".to_string()])
.chain(japanese.iter().chain(&chinese).chain(&georgian).map(|f| name(f)));
let mut seen = std::collections::HashSet::new();
fonts.families.insert(FontFamily::Name(family.into()), stack.filter(|n| seen.insert(n.clone())).collect());
}