Add reviewed fallback fonts and optional native Chinese face

This commit is contained in:
skylartaylor
2026-10-24 03:49:41 +02:00
parent fff559f5f0
commit b4ce3ee090
22 changed files with 2119 additions and 65 deletions
+14 -3
View File
@@ -1,8 +1,9 @@
# Font attribution
Every font in `fonts/`, with its source and licence. The files are byte-identical to the pinned
upstream sources (checked by `craft-fonts-tests`, which verifies each SHA-256 in
`fonts/manifest.txt`).
upstream sources (checked by `craft-fonts-tests`, which verifies each SHA-256 in the manifest that
lists it: `fonts/manifest.txt` for the default embedded set, and
`fonts/simplified-chinese-manifest.txt` for the opt-in native-only Simplified Chinese face).
| File | Family / style | Author | Source (pinned) | Licence |
|---|---|---|---|---|
@@ -14,7 +15,17 @@ upstream sources (checked by `craft-fonts-tests`, which verifies each SHA-256 in
| `fonts/noto-sans-arabic/NotoSansArabic.ttf` | Noto Sans Arabic (variable) | The Noto Project Authors | [google/fonts@7085eb8](https://github.com/google/fonts/tree/7085eb89a950e85db5b166b7a58d414544b4140c/ofl/notosansarabic) | OFL-1.1 (`fonts/noto-sans-arabic/OFL.txt`) |
| `fonts/noto-sans-thai/NotoSansThai.ttf` | Noto Sans Thai (variable) | The Noto Project Authors | [google/fonts@8b0a1d0](https://github.com/google/fonts/tree/8b0a1d0f5983c89bc2b93f1b5fb55f9e252744b5/ofl/notosansthai) | OFL-1.1 (`fonts/noto-sans-thai/OFL.txt`) |
| `fonts/noto-sans-georgian/NotoSansGeorgian.ttf` | Noto Sans Georgian (variable) | The Noto Project Authors | [google/fonts@8b0a1d0](https://github.com/google/fonts/tree/8b0a1d0f5983c89bc2b93f1b5fb55f9e252744b5/ofl/notosansgeorgian) | OFL-1.1 (`fonts/noto-sans-georgian/OFL.txt`) |
| `fonts/nanum-gothic/NanumGothic-Regular.ttf` | NanumGothic Regular | NHN Corporation; Sandoll Communications | [google/fonts@bd8f81d](https://github.com/google/fonts/tree/bd8f81ddb5c74d5c8897b36ad88b440266245103/ofl/nanumgothic) | OFL-1.1 (`fonts/nanum-gothic/OFL.txt`) |
| `fonts/noto-sans-symbols/NotoSansSymbols.ttf` | Noto Sans Symbols Regular | The Noto Project Authors | [google/fonts@47d54ca](https://github.com/google/fonts/tree/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols) | OFL-1.1 (`fonts/noto-sans-symbols/OFL.txt`) |
| `fonts/noto-sans-symbols-2/NotoSansSymbols2-Regular.ttf` | Noto Sans Symbols 2 Regular | The Noto Project Authors | [google/fonts@47d54ca](https://github.com/google/fonts/tree/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols2) | OFL-1.1 (`fonts/noto-sans-symbols-2/OFL.txt`) |
| `fonts/noto-sans-hebrew/NotoSansHebrew-Regular.ttf` | Noto Sans Hebrew Regular | The Noto Project Authors | [notofonts/hebrew NotoSansHebrew-v3.001](https://github.com/notofonts/hebrew/releases/tag/NotoSansHebrew-v3.001) | OFL-1.1 (`fonts/noto-sans-hebrew/OFL.txt`) |
| `fonts/lxgw-wenkai-lite/LXGWWenKaiLite-Regular.ttf` | LXGW WenKai Lite Regular | LXGW / Luo Xia Gu Wu; derived from Fontworks Klee One | [lxgw/LxgwWenKai-Lite v1.530](https://github.com/lxgw/LxgwWenKai-Lite/releases/tag/v1.530) | OFL-1.1 (`fonts/lxgw-wenkai-lite/OFL.txt`) |
The OFL allows these fonts to be bundled, embedded and redistributed with software, provided the
licence text travels with them and the fonts are not sold on their own. "Shippori Mincho",
"BIZ UD" and "Noto" are the authors' names for their fonts; we use the files unmodified.
"BIZ UD", "Noto" and "LXGW WenKai Lite" are the authors' names for their fonts; we use the files
unmodified. LXGW WenKai Lite's upstream OFL reserves the names 霞鹜, 霞鶩, 落霞孤鹜, 落霞孤鶩 and
LXGW; the bundled release member and Klee-One copyright notice stay byte-identical to upstream.
The Symbols variable font keeps its upstream bytes; only its filename changes from
`NotoSansSymbols[wght].ttf` to `NotoSansSymbols.ttf`.
+14 -5
View File
@@ -21,10 +21,11 @@ This library is meant to have the following:
| Path | What |
|---|---|
| `fonts/` | The font files, one directory per family, each with its licence. |
| `fonts/manifest.txt` | Machine-readable list of every font: family, style, file, scripts, licence, SHA-256, pinned source. |
| `fonts/manifest.txt` | Machine-readable list of the default fonts: family, style, file, scripts, licence, SHA-256, pinned source. |
| `fonts/simplified-chinese-manifest.txt` | Opt-in native-only Simplified Chinese row, same eight-field format. Not in `FONTS` or the embed payload. |
| `ATTRIBUTION.md` | Authors, pinned sources and licences. |
| `crates/craft-fonts` | Rust: the manifest as a static table, finding a checkout (`CRAFT_FONTS_DIR`), loading it, and an `embed` feature that compiles the fonts in. |
| `crates/craft-fonts-tests` | Tests asserted on these fonts: checksums, licences, family names, Japanese coverage and outlines, vertical-text (`vert`/`vrt2`) support. |
| `crates/craft-fonts-tests` | Tests asserted on these fonts: checksums, licences, family/style names, Japanese/Korean/Hebrew/symbol coverage and outlines, static-face metadata and vertical-text (`vert`/`vrt2`) support. |
| `docs/integration.md` | How an app uses craft-fonts as a fully optional build input. |
### Fonts
@@ -38,17 +39,25 @@ This library is meant to have the following:
| Noto Sans Arabic | Regular (variable: wdth, wght) | Arabic UI and document text (sans) | OFL-1.1 |
| Noto Sans Thai | Regular (variable: wdth, wght) | Thai UI and document text (sans) | OFL-1.1 |
| Noto Sans Georgian | Regular (variable: wdth, wght) | Georgian UI and document text (sans) | OFL-1.1 |
| NanumGothic | Regular | Korean UI and document text | OFL-1.1 |
| Noto Sans Symbols | Regular (variable: wght) | UI key/modifier symbols (`⌃`, arrows) | OFL-1.1 |
| Noto Sans Symbols 2 | Regular | UI symbols (`✕`, `◀`, `✓`) | OFL-1.1 |
| Noto Sans Hebrew | Regular (static) | Hebrew UI and document text | OFL-1.1 |
| LXGW WenKai Lite | Regular (static) | Native-only opt-in Simplified Chinese fallback (Kai) | OFL-1.1 |
## How the apps use it
**Fully optional, never required.** The apps don't list craft-fonts in `Cargo.toml` (Cargo would
fetch it for every build). Building with `CRAFT_FONTS_DIR=<path to this checkout>` makes an app
embed the fonts in `fonts/manifest.txt`; without it, the app builds and runs as before. Release
builds check out a pinned commit of this repo and set the option. See
[`docs/integration.md`](docs/integration.md).
builds check out a pinned commit of this repo and set the option. Apps that explicitly opt in may
also read `fonts/simplified-chinese-manifest.txt` on native builds only; it is never part of the
default set or a wasm allowlist. See [`docs/integration.md`](docs/integration.md).
**Font assets for the Crafting Apps belong here**, not in the app repos: add the file, its licence,
a manifest line and an attribution row, and the tests check it.
a manifest line and an attribution row, and the tests check it. Put a face that only some native
consumers should embed in the opt-in manifest described in `docs/integration.md`, not in the
default `fonts/manifest.txt`.
## Develop
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "craft-fonts-tests"
description = "Assertions on the fonts themselves: checksums, licences, names, script coverage and vertical-text support."
description = "Assertions on the fonts themselves: checksums, licences, names, static-face metadata, script coverage and vertical-text support."
version.workspace = true
edition.workspace = true
license.workspace = true
+533 -34
View File
@@ -1,8 +1,12 @@
//! Assertions on the font files themselves.
use craft_fonts::{FONTS, Font, for_script};
use craft_fonts::{Entry, FONTS, Font, for_script, parse_manifest};
use sha2::{Digest, Sha256};
use skrifa::raw::TableProvider;
use skrifa::attribute::{Style, Weight};
use skrifa::instance::{LocationRef, Size};
use skrifa::outline::{DrawSettings, OutlinePen};
use skrifa::raw::tables::os2::SelectionFlags;
use skrifa::raw::{ReadError, TableProvider};
use skrifa::{FontRef, MetadataProvider, string::StringId};
fn bytes(f: &Font) -> Vec<u8> {
@@ -60,6 +64,133 @@ fn every_font_is_the_family_the_manifest_says() {
}
}
#[test]
fn every_font_is_the_style_the_manifest_says() {
for f in FONTS {
let data = bytes(f);
let font = FontRef::new(&data).unwrap_or_else(|e| panic!("{}: {e}", f.file));
let names: Vec<String> = [
StringId::SUBFAMILY_NAME,
StringId::TYPOGRAPHIC_SUBFAMILY_NAME,
]
.into_iter()
.flat_map(|id| {
font.localized_strings(id)
.map(|s| s.to_string())
.collect::<Vec<_>>()
})
.collect();
assert!(
names.iter().any(|n| n == f.style),
"{}: name table styles are {names:?}, manifest says {:?}",
f.file,
f.style
);
}
}
/// The exact manifest rows for the faces this prerequisite adds. The rows are pinned to the
/// verified upstream hashes above; a missing row or a drifted path/script/licence fails here
/// rather than silently skipping the Korean and symbol coverage below.
#[test]
fn prerequisite_faces_have_the_documented_manifest_rows() {
fn exact(family: &str, style: &str, file: &str, scripts: &[&str]) -> &'static Font {
let found: Vec<&'static Font> = FONTS.iter().filter(|f| f.family == family).collect();
assert_eq!(
found.len(),
1,
"manifest lists {} rows for {family}; expected exactly one",
found.len()
);
let f = found[0];
assert_eq!(f.style, style, "{}: manifest style", f.file);
assert_eq!(f.file, file, "{family}: manifest file");
assert_eq!(f.scripts, scripts, "{family}: manifest scripts");
assert_eq!(f.licence, "OFL-1.1", "{family}: licence");
f
}
exact(
"NanumGothic",
"Regular",
"fonts/nanum-gothic/NanumGothic-Regular.ttf",
&["Hang", "Kore"],
);
exact(
"Noto Sans Symbols",
"Regular",
"fonts/noto-sans-symbols/NotoSansSymbols.ttf",
&["Zsym"],
);
// Symbols 2 is required, not optional: the pinned Symbols 1 bytes lack U+2715 `✕` and
// U+25C0 `◀`, which Symbols 2 provides.
exact(
"Noto Sans Symbols 2",
"Regular",
"fonts/noto-sans-symbols-2/NotoSansSymbols2-Regular.ttf",
&["Zsym"],
);
}
/// An outline pen that counts drawing commands, so a cmap entry with an empty outline cannot
/// pass as coverage.
struct OutlineCount(usize);
impl OutlinePen for OutlineCount {
fn move_to(&mut self, _: f32, _: f32) {}
fn line_to(&mut self, _: f32, _: f32) {
self.0 += 1;
}
fn quad_to(&mut self, _: f32, _: f32, _: f32, _: f32) {
self.0 += 1;
}
fn curve_to(&mut self, _: f32, _: f32, _: f32, _: f32, _: f32, _: f32) {
self.0 += 1;
}
fn close(&mut self) {}
}
/// Assert that `c` maps to a glyph that actually draws something in `font`.
fn assert_glyph_outline(font: &FontRef<'_>, file: &str, c: char) {
let gid = font
.charmap()
.map(c)
.unwrap_or_else(|| panic!("{file}: no glyph for {c}"));
let glyph = font
.outline_glyphs()
.get(gid)
.unwrap_or_else(|| panic!("{file}: no outline for {c}"));
let mut pen = OutlineCount(0);
glyph
.draw(
DrawSettings::unhinted(Size::new(64.0), LocationRef::default()),
&mut pen,
)
.unwrap_or_else(|e| panic!("{file}: drawing {c}: {e}"));
assert!(pen.0 > 0, "{file}: {c} maps to a glyph that draws nothing");
}
/// Assert that `f` itself (not merely some other face) maps `c` to a drawn outline.
fn assert_face_glyph_outline(f: &Font, c: char) {
let data = bytes(f);
let font = FontRef::new(&data).unwrap_or_else(|e| panic!("{}: {e}", f.file));
assert_glyph_outline(&font, f.file, c);
}
/// The one manifest row for `family`.
fn face(family: &str) -> &'static Font {
let mut found = FONTS.iter().filter(|f| f.family == family);
let f = found
.next()
.unwrap_or_else(|| panic!("the manifest lists no {family} row"));
assert!(
found.next().is_none(),
"the manifest lists more than one {family} row"
);
f
}
/// Text every Japanese font must cover: kana, common kanji, Japanese punctuation, full-width
/// forms and basic Latin (UI strings mix them).
const JAPANESE: &str = concat!(
@@ -94,42 +225,11 @@ fn japanese_fonts_cover_japanese_text() {
#[test]
fn japanese_glyphs_have_outlines() {
use skrifa::instance::{LocationRef, Size};
use skrifa::outline::{DrawSettings, OutlinePen};
struct Count(usize);
impl OutlinePen for Count {
fn move_to(&mut self, _: f32, _: f32) {}
fn line_to(&mut self, _: f32, _: f32) {
self.0 += 1;
}
fn quad_to(&mut self, _: f32, _: f32, _: f32, _: f32) {
self.0 += 1;
}
fn curve_to(&mut self, _: f32, _: f32, _: f32, _: f32, _: f32, _: f32) {
self.0 += 1;
}
fn close(&mut self) {}
}
for f in for_script("Jpan") {
let data = bytes(f);
let font = FontRef::new(&data).unwrap_or_else(|e| panic!("{}: {e}", f.file));
let outlines = font.outline_glyphs();
for c in ['漢', 'あ', 'ア', '。'] {
let gid = font
.charmap()
.map(c)
.unwrap_or_else(|| panic!("{}: no glyph for {c}", f.file));
let glyph = outlines
.get(gid)
.unwrap_or_else(|| panic!("{}: no outline for {c}", f.file));
let mut pen = Count(0);
glyph
.draw(
DrawSettings::unhinted(Size::new(64.0), LocationRef::default()),
&mut pen,
)
.unwrap_or_else(|e| panic!("{}: drawing {c}: {e}", f.file));
assert!(pen.0 > 0, "{} {}: {c} draws nothing", f.family, f.style);
assert_glyph_outline(&font, f.file, c);
}
}
}
@@ -227,3 +327,402 @@ fn georgian_fonts_cover_georgian_text() {
),
);
}
/// Korean interface labels used by the GridCraft UI, not a single sample character.
const KOREAN_UI: &str = concat!(
"데이터 한글 파일 편집 보기 삽입 서식 저장 열기 닫기 인쇄 취소 확인 ",
"가나다라마바사아자차카타파하",
);
#[test]
fn korean_fonts_cover_korean_ui_text() {
covers("Hang", KOREAN_UI);
covers("Kore", KOREAN_UI);
}
/// The Korean `Hang` face really covers all modern Hangul syllables and the 51 compatibility
/// Jamo. These exact counts come from the pinned NanumGothic cmap, not from a sample string.
#[test]
fn korean_fonts_cover_every_modern_syllable_and_compatibility_jamo() {
let syllables = '\u{ac00}'..='\u{d7a3}';
let jamo = '\u{3131}'..='\u{3163}';
assert_eq!(syllables.clone().count(), 11_172);
assert_eq!(jamo.clone().count(), 51);
let fonts: Vec<&Font> = for_script("Hang").collect();
assert!(!fonts.is_empty(), "the manifest lists no Hang font");
for f in fonts {
let data = bytes(f);
let font = FontRef::new(&data).unwrap_or_else(|e| panic!("{}: {e}", f.file));
let charmap = font.charmap();
let missing_syllables = syllables
.clone()
.filter(|c| charmap.map(*c).is_none())
.count();
let missing_jamo = jamo.clone().filter(|c| charmap.map(*c).is_none()).count();
assert_eq!(
missing_syllables, 0,
"{}: {missing_syllables} of 11,172 modern Hangul syllables are missing",
f.file
);
assert_eq!(
missing_jamo, 0,
"{}: {missing_jamo} of the 51 compatibility Jamo are missing",
f.file
);
}
}
#[test]
fn korean_glyphs_have_outlines() {
let fonts: Vec<&Font> = for_script("Hang").collect();
assert!(
!fonts.is_empty(),
"the manifest lists no Hang font; add NanumGothic Regular from the pinned source in docs/egui-037-font-coverage-plan.md"
);
for f in fonts {
let data = bytes(f);
let font = FontRef::new(&data).unwrap_or_else(|e| panic!("{}: {e}", f.file));
for c in ['한', '글', '각', '힣'] {
assert_glyph_outline(&font, f.file, c);
}
}
}
/// Exact face assignment for the observed symbol gaps. Symbols 1 covers `⌃` and two of the
/// arrows but not `✕`, `◀` or `✓`; Symbols 2 covers `✕`, `◀` and `✓` but not `⌃`. `📋` is
/// supplied by egui's emoji faces, so it is intentionally not asserted against craft-fonts.
#[test]
fn zsym_faces_cover_the_documented_symbol_gaps() {
let symbols = face("Noto Sans Symbols");
let symbols2 = face("Noto Sans Symbols 2");
assert_eq!(
symbols.scripts,
["Zsym"].as_slice(),
"{}: scripts",
symbols.file
);
assert_eq!(
symbols2.scripts,
["Zsym"].as_slice(),
"{}: scripts",
symbols2.file
);
for c in ['⌃', '↘', '↑'] {
assert_face_glyph_outline(symbols, c);
}
for c in ['✕', '◀', '✓'] {
assert_face_glyph_outline(symbols2, c);
}
}
/// The Hebrew face is the verified upstream static Regular release, not the old variable
/// fixture. It must remain one face/file: no `fvar` (and no named instances), no `avar`, no
/// `STAT`, OS/2 weight 400 and an upright `REGULAR` selection. A consumer registers this one
/// face once per family set that includes it, never the nine instances of the variable font.
#[test]
fn hebrew_face_is_a_single_static_regular_face() {
const HEBREW_FILE: &str = "fonts/noto-sans-hebrew/NotoSansHebrew-Regular.ttf";
const HEBREW_LICENCE_FILE: &str = "fonts/noto-sans-hebrew/OFL.txt";
const HEBREW_SOURCE: &str =
"https://github.com/notofonts/hebrew/releases/tag/NotoSansHebrew-v3.001";
const HEBREW_SHA256: &str = "671951828bd5c95db818e5bb12dcea2d0c0dda00311888522be061ee6835125e";
let f = face("Noto Sans Hebrew");
assert_eq!(for_script("Hebr").count(), 1);
assert_eq!(f.style, "Regular");
assert_eq!(f.file, HEBREW_FILE);
assert_eq!(f.scripts, ["Hebr"].as_slice());
assert_eq!(f.licence, "OFL-1.1");
assert_eq!(f.licence_file, HEBREW_LICENCE_FILE);
assert_eq!(f.source, HEBREW_SOURCE);
assert_eq!(f.sha256, HEBREW_SHA256);
let data = bytes(f);
// A TrueType sfnt, not a TrueType Collection: one face in the file.
assert_eq!(&data[..4], &[0x00, 0x01, 0x00, 0x00]);
let font = FontRef::new(&data).unwrap_or_else(|e| panic!("{}: {e}", f.file));
for (name, table) in [
("fvar", font.fvar().map(|_| ())),
("avar", font.avar().map(|_| ())),
("STAT", font.stat().map(|_| ())),
] {
assert!(
matches!(table, Err(ReadError::TableIsMissing(_))),
"{}: expected no {name} table; the Hebrew file must stay a single static face",
f.file
);
}
let os2 = font
.os2()
.unwrap_or_else(|e| panic!("{}: OS/2: {e}", f.file));
assert_eq!(os2.us_weight_class(), 400, "{}: OS/2 weight", f.file);
assert_eq!(os2.us_width_class(), 5, "{}: OS/2 width", f.file);
let selection = os2.fs_selection();
assert!(
selection.contains(SelectionFlags::REGULAR),
"{}: fsSelection lacks REGULAR",
f.file
);
for flag in [
SelectionFlags::ITALIC,
SelectionFlags::BOLD,
SelectionFlags::OBLIQUE,
] {
assert!(
!selection.contains(flag),
"{}: fsSelection unexpectedly contains {flag:?}",
f.file
);
}
let attributes = font.attributes();
assert_eq!(attributes.weight, Weight::NORMAL, "{}: weight", f.file);
assert_eq!(attributes.style, Style::Normal, "{}: style", f.file);
}
/// All 27 Hebrew letters `U+05D0..U+05EA` map to drawn outlines, not merely cmap entries.
#[test]
fn hebrew_letters_have_drawn_outlines() {
let f = face("Noto Sans Hebrew");
let letters = '\u{05d0}'..='\u{05ea}';
assert_eq!(letters.clone().count(), 27);
for c in letters {
assert_face_glyph_outline(f, c);
}
}
/// All 24 assigned points, marks and punctuation in `U+05B0..U+05C7` map to drawn outlines.
/// The combining marks require GPOS positioning in a shaping engine. This font-only test
/// asserts coverage, not application-level niqqud positioning or bidi order.
#[test]
fn hebrew_points_have_drawn_outlines() {
let f = face("Noto Sans Hebrew");
let points = '\u{05b0}'..='\u{05c7}';
assert_eq!(points.clone().count(), 24);
for c in points {
assert_face_glyph_outline(f, c);
}
}
/// The unpointed Hebrew sample used by mixed acceptance text such as `שלום deck 42`.
/// craft-fonts only claims the Hebrew part here, which must map to drawn outlines.
#[test]
fn hebrew_fonts_cover_unpointed_hebrew_text() {
const HEBREW_SAMPLE: &str = "שלום עברית אבגדהוזחטיכלמנסעפצקרשת";
covers("Hebr", HEBREW_SAMPLE);
let f = face("Noto Sans Hebrew");
for c in "שלום".chars() {
assert_face_glyph_outline(f, c);
}
}
/// The opt-in Simplified Chinese manifest. It is deliberately separate from
/// `fonts/manifest.txt`: consumers that do not opt in (including every wasm build) must not
/// embed this face, and it is not part of craft-fonts' `FONTS`/embed payload.
const SIMPLIFIED_CHINESE_MANIFEST: &str = "fonts/simplified-chinese-manifest.txt";
const LXGW_FILE: &str = "fonts/lxgw-wenkai-lite/LXGWWenKaiLite-Regular.ttf";
const LXGW_LICENCE_FILE: &str = "fonts/lxgw-wenkai-lite/OFL.txt";
const LXGW_SOURCE: &str = "https://github.com/lxgw/LxgwWenKai-Lite/releases/tag/v1.530";
const LXGW_SHA256: &str = "f799a538a3355877e597a0ae04050e897932c9ae7689eb9227c2953c5dd549ea";
/// The plan's hard ceiling for one added font file.
const LXGW_MAX_BYTES: usize = 24 * 1024 * 1024;
/// The DeckCraft UI sample whose missing `简` U+7B80 triggered this opt-in face.
const DECK_CHINESE_SAMPLE: &str = "简体中文演示文稿";
fn optional_entries(relative: &str) -> Vec<Entry> {
let path = craft_fonts_tests::repo_root().join(relative);
let text = std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("{}: {e}", path.display()));
parse_manifest(&text).unwrap_or_else(|e| panic!("{}: {e}", path.display()))
}
fn lxgw_entry() -> Entry {
let entries = optional_entries(SIMPLIFIED_CHINESE_MANIFEST);
assert_eq!(
entries.len(),
1,
"{SIMPLIFIED_CHINESE_MANIFEST} must hold exactly the one reviewed LXGW row"
);
entries.into_iter().next().unwrap()
}
#[test]
fn simplified_chinese_opt_in_row_is_exact_and_not_in_the_default_manifest() {
let e = lxgw_entry();
assert_eq!(e.family, "LXGW WenKai Lite");
assert_eq!(e.style, "Regular");
assert_eq!(e.file, LXGW_FILE);
assert_eq!(
e.scripts.iter().map(String::as_str).collect::<Vec<_>>(),
["Hani", "Hans"],
"DeckCraft's script classifier emits Hani; Hans names the Simplified Chinese face"
);
assert_eq!(e.licence, "OFL-1.1");
assert_eq!(e.licence_file, LXGW_LICENCE_FILE);
assert_eq!(e.sha256, LXGW_SHA256);
assert_eq!(e.source, LXGW_SOURCE);
assert!(
!FONTS.iter().any(|f| f.family == e.family.as_str()),
"the opt-in LXGW face must not be in the default FONTS table or embed payload"
);
let default =
std::fs::read_to_string(craft_fonts_tests::repo_root().join("fonts/manifest.txt")).unwrap();
assert!(
!default.contains("LXGW") && !default.contains("simplified-chinese"),
"the default font manifest must not list the opt-in Chinese face"
);
}
#[test]
fn simplified_chinese_face_matches_its_hash_licence_and_size_ceiling() {
let e = lxgw_entry();
let data = bytes_from_entry(&e);
let digest = Sha256::digest(&data);
let hex: String = digest.iter().map(|b| format!("{b:02x}")).collect();
assert_eq!(
hex, e.sha256,
"{}: the file changed; update the opt-in manifest only after checking the new upstream source",
e.file
);
assert!(
data.len() <= LXGW_MAX_BYTES,
"{}: {} bytes exceeds the 24 MiB single-face ceiling",
e.file,
data.len()
);
assert_eq!(
std::path::Path::new(&e.file).parent(),
std::path::Path::new(&e.licence_file).parent(),
"the licence must sit next to the font it covers"
);
let licence = std::fs::read_to_string(craft_fonts_tests::repo_root().join(&e.licence_file))
.unwrap_or_else(|err| panic!("{}: {err}", e.licence_file));
assert!(licence.contains("SIL OPEN FONT LICENSE Version 1.1"));
}
#[test]
fn simplified_chinese_licence_retains_klee_copyright_and_reserved_font_names() {
let e = lxgw_entry();
let licence = std::fs::read_to_string(craft_fonts_tests::repo_root().join(&e.licence_file))
.unwrap_or_else(|err| panic!("{}: {err}", e.licence_file));
assert!(licence.contains("Reserved Font Name"));
assert!(licence.contains("Copyright 2020 The Klee Project Authors"));
for name in ["霞鹜", "霞鶩", "落霞孤鹜", "落霞孤鶩", "LXGW"] {
assert!(
licence.contains(name),
"{}: the upstream Reserved Font Name {name} is missing",
e.licence_file
);
}
}
#[test]
fn simplified_chinese_face_is_one_static_regular_400_face() {
let e = lxgw_entry();
let data = bytes_from_entry(&e);
assert_eq!(
&data[..4],
&[0x00, 0x01, 0x00, 0x00],
"{}: expected a single TrueType sfnt, not a collection or variable font",
e.file
);
let font = FontRef::new(&data).unwrap_or_else(|err| panic!("{}: {err}", e.file));
for (name, table) in [
("fvar", font.fvar().map(|_| ())),
("avar", font.avar().map(|_| ())),
("STAT", font.stat().map(|_| ())),
] {
assert!(
matches!(table, Err(ReadError::TableIsMissing(_))),
"{}: expected no {name} table; the opt-in file must stay a single static face",
e.file
);
}
let families: Vec<String> = [StringId::TYPOGRAPHIC_FAMILY_NAME, StringId::FAMILY_NAME]
.into_iter()
.flat_map(|id| {
font.localized_strings(id)
.map(|s| s.to_string())
.collect::<Vec<_>>()
})
.collect();
assert!(
families.iter().any(|n| n.as_str() == e.family),
"{}: name table has {families:?}, manifest says {:?}",
e.file,
e.family
);
let styles: Vec<String> = [
StringId::SUBFAMILY_NAME,
StringId::TYPOGRAPHIC_SUBFAMILY_NAME,
]
.into_iter()
.flat_map(|id| {
font.localized_strings(id)
.map(|s| s.to_string())
.collect::<Vec<_>>()
})
.collect();
assert!(
styles.iter().any(|n| n.as_str() == e.style),
"{}: name table styles are {styles:?}, manifest says {:?}",
e.file,
e.style
);
let versions: Vec<String> = font
.localized_strings(StringId::VERSION_STRING)
.map(|s| s.to_string())
.collect();
assert!(
versions.iter().any(|v| v.contains("Version 1.530")),
"{}: expected Version 1.530, found {versions:?}",
e.file
);
let os2 = font
.os2()
.unwrap_or_else(|err| panic!("{}: OS/2: {err}", e.file));
assert_eq!(os2.us_weight_class(), 400, "{}: OS/2 weight", e.file);
let selection = os2.fs_selection();
assert!(
selection.contains(SelectionFlags::REGULAR),
"{}: fsSelection lacks REGULAR",
e.file
);
for flag in [
SelectionFlags::ITALIC,
SelectionFlags::BOLD,
SelectionFlags::OBLIQUE,
] {
assert!(
!selection.contains(flag),
"{}: fsSelection unexpectedly contains {flag:?}",
e.file
);
}
let attributes = font.attributes();
assert_eq!(attributes.weight, Weight::NORMAL, "{}: weight", e.file);
assert_eq!(attributes.style, Style::Normal, "{}: style", e.file);
}
#[test]
fn simplified_chinese_face_draws_the_deckcraft_sample_and_jian() {
let e = lxgw_entry();
let data = bytes_from_entry(&e);
let font = FontRef::new(&data).unwrap_or_else(|err| panic!("{}: {err}", e.file));
assert_eq!(DECK_CHINESE_SAMPLE.chars().count(), 8);
assert!(DECK_CHINESE_SAMPLE.contains('简'));
for c in DECK_CHINESE_SAMPLE.chars() {
assert_glyph_outline(&font, &e.file, c);
}
assert_glyph_outline(&font, &e.file, '简');
}
fn bytes_from_entry(e: &Entry) -> Vec<u8> {
std::fs::read(craft_fonts_tests::repo_root().join(&e.file))
.unwrap_or_else(|err| panic!("{}: {err}", e.file))
}
+1 -1
View File
@@ -9,7 +9,7 @@ rust-version.workspace = true
publish = false
[features]
# Compile every font in the manifest into the binary (about 24 MB).
# Compile every font in the manifest into the binary (about 43 MiB).
embed = []
[lints]
+3 -1
View File
@@ -3,7 +3,9 @@
//! The fonts live in this repository's `fonts/` directory and are listed in `fonts/manifest.txt`.
//! This crate exposes that manifest ([`FONTS`]), finds a craft-fonts checkout at run time
//! ([`dir_from_env`]), loads fonts from one ([`load`]), and with the `embed` feature compiles them
//! into the binary ([`embedded`]).
//! into the binary ([`embedded`]). `fonts/simplified-chinese-manifest.txt` is a separate opt-in
//! native-only manifest; it is not part of [`FONTS`] or the `embed` payload, and consumers or
//! tests parse it with [`parse_manifest`].
//!
//! The apps do **not** depend on this crate: Cargo resolves even optional git dependencies, so a
//! dependency would make craft-fonts required for every build. Apps read `fonts/manifest.txt`
+3 -2
View File
@@ -1,6 +1,7 @@
//! The `fonts/manifest.txt` format, shared by `build.rs` and the library (`#[path]`-included by
//! both, so it has no dependencies). Apps that read the manifest from their own `build.rs` can
//! copy this file.
//! both, so it has no dependencies). The same parser reads the opt-in
//! `fonts/simplified-chinese-manifest.txt`. Apps that read a manifest from their own `build.rs`
//! can copy this file.
/// One line of the manifest.
#[derive(Clone, Debug, PartialEq, Eq)]
+475
View File
@@ -0,0 +1,475 @@
# egui 0.37 DeckCraft font gaps: Chinese UI fallback and table-style fixture
Status: central font assets, the opt-in manifest, and DeckCraft's native build/UI/table-fixture
changes are implemented locally. Central managed job `egui037-fonts-candidate-20261023-chinese-05`
passed 4 embed tests, 25 font tests and strict all-features clippy. Deck's fresh review found and
repaired tofu-permissive assertions and a Chinese sample that was not visible at startup;
managed `egui037-deckcraft-candidate-20261023-font-input-14` passed the two targeted font
regressions but failed its full UI suite on seven other tests. Later jobs
`egui037-deckcraft-candidate-20261024-upstream-tests-24` and `-25` passed 307 UI
tests with 4 ignored (serial and parallel), and `-upstream-lints-26` passed strict
linting. The earlier parallel SIGSEGV remains an open diagnostic, not a fixed issue.
Central `egui037-fonts-candidate-20261024-final-06` also passed default/embed tests,
formatting and strict linting after the CI-only upstream fast-forward to `fff559f`.
Native GUI, web-size and performance qualification remain open. The planning observations below
are retained as history; the acquisition and implementation sections supersede the speculative GB choice.
## Observed failures
### 1. Chinese interface text
The managed candidate run
`/home/codex/work/ssd2/task-evidence/egui037-deckcraft-candidate-20261023/task-jobs/egui037-deckcraft-candidate-20261023-ime-tests-11/output.log`
reports:
```text
No glyph for "简" (U+7B80) in Proportional.
Installed fonts: ["craft-1-BIZ UDPGothic-Regular", "craft-4-Noto Sans Arabic-Regular",
"craft-7-NanumGothic-Regular", "craft-8-Noto Sans Symbols-Regular",
"craft-9-Noto Sans Symbols 2-Regular", "craft-10-Noto Sans Hebrew-Regular",
"Ubuntu-Light", "egui-icons", "NotoEmoji-Regular", "emoji-icon-font", "Hack"]
```
This is the DeckCraft UI test
`crates/ui-egui/src/ime_tests.rs::cjk_text_in_the_interface_is_drawn_with_real_glyphs` (lines
182-220). The test's Chinese sample is `简体中文演示文稿` (line 189) and the missing character is
`简` U+7B80. Noto Sans CJK is present in the central repository but DeckCraft's
`crates/fonts/build.rs` excludes `Noto Sans CJK SC` under its asset policy, so it is correctly
absent from the candidate's installed fonts.
The existing `system_has(text)` skip at lines 200-204 is not a safe fix. The test builds the app
with `harness(s)` before it reaches that loop, and the app paints a UI surface containing the
Chinese section name. Under egui 0.37's strict missing-glyph policy the paint panics before the
skip can run. Even if the skip were reachable, skipping Chinese would violate the requirement that
the real UI regression remain covered. Do not remove the Chinese assertion or compare against
tofu.
The current central plan says:
- `docs/egui-037-font-coverage-plan.md` lines 353-358: Chinese stays system-only in GridCraft
while Noto CJK is barred; a permitted Chinese face may be added later with the same provenance,
cmap/outline and measurement evidence.
- `docs/integration.md`, web allowlist section: the web allowlist is fixed and excludes Noto CJK. A new
native Chinese face must not be added to that allowlist implicitly.
### 2. Table style Regular/Bold fixture
The same managed run reports:
```text
tests::tables_with_a_file_defined_style_edit_and_resize
panicked at crates/engine/src/tests.rs:3070:
the header is laid out in the file style's (regular) weight:
(177.70600000000002, 114.4) vs (177.70600000000002, 114.4)
```
The test uses `Session::with_new()` (lines 5-7), whose default theme names `Inter`. It takes the
built-in header position with the built-in style's bold weight, then applies a file style with
`bold: Some(false)` and expects a different caret x. In the candidate checkout there is no
`fonts/latin-manifest.txt` and no `Inter` face in the central manifest. `FontDb::face` therefore
falls through its family/substitute list to the first loaded face for both Regular and Bold
(`crates/fonts/src/fontdb.rs` lines 1006-1025). Both requests resolve to the same static face, so
the two positions are identical.
This is not evidence that the table-style implementation is wrong. The fixture is asking for two
metrics from a family that is not present. Adding Inter solely to satisfy this test would solve
the symptom but would also add a new, unreviewed Latin asset and change the default document font
for every candidate. The smaller, honest fixture fix is to make the test use a family with a real
Regular and Bold face that is already part of the candidate.
## Decision summary
1. **Chinese UI:** add one native-only, permitted Simplified Chinese face, supplied by
craft-fonts. Preferred candidate is **LXGW WenKai GB Regular** because it targets Simplified
Chinese, is derived from Fontworks Klee One under OFL-1.1, and should be smaller than the full
LXGW WenKai family. Keep it out of every wasm allowlist. Do not use Noto CJK.
2. **Table fixture:** use the existing `BIZ UDPGothic` Regular/Bold pair in the test, not a new
Inter/Latin asset. A read-only hmtx check of the existing bytes gives 12,767 font units for
`Hello world` in Regular and 13,237 in Bold at 2,048 upem (112.21 pt versus 116.34 pt at 18 pt;
a 4.13 pt difference). That is sufficient to make the assertion meaningful. The test may
choose another installed family with a real Regular/Bold pair only when the candidate is
absent; under `CRAFT_FONTS_REQUIRED=1` a missing pair must fail, not skip.
3. **System-only Chinese with an owned test fixture:** retain as a fallback only if the LXGW
candidate cannot pass provenance, licence/RFN or byte gates. It is not the preferred answer:
it proves the fallback machinery with a fixture but leaves Linux users without a shipped
Chinese face. If used, the fixture must be contributor-original or openly licensed, attributed,
and the production limitation must be stated explicitly. It must never be presented as
production Chinese coverage.
No Chinese font addition or production fallback change should land until the provenance gate in this document is complete. The table fixture fix can be reviewed independently because it uses existing BIZ bytes and does not add an asset.
## Chinese candidate research
### Policy fit
DeckCraft's asset policy (repository `AGENTS.md` §1.1-1.4) bars Microsoft, Adobe, Avid and
Autodesk visual assets, including Adobe's Source Han and Noto CJK. It permits OFL-1.1 assets from
their original source with attribution. Fontworks Klee One is not one of the barred vendors, and
LXGW WenKai is a derivative of Klee One under OFL-1.1, so the family is a policy candidate. This is
a licence/policy finding, not a provenance or quality pass.
`LXGW` is the project abbreviation for the Pinyin `Luo Xia Gu Wu`; the Debian package description
also identifies the upstream homepage and the Klee One derivation. Record the derivative author
as **LXGW / Luo Xia Gu Wu**, and record the original design/copyright holder as **Fontworks Inc.
(Klee One)**. Do not attribute the derivative to Adobe or Google.
### Release and provenance evidence available locally
The server has no working DNS for `github.com`, `raw.githubusercontent.com` or
`api.github.com`; attempts to read the two official repositories failed before any content was
fetched. This document therefore does **not** claim an upstream release tag, commit or asset hash
that it has not inspected.
The strongest local evidence is Debian trixie's package metadata for the base family:
- Debian source package: `fonts-lxgw-wenkai` version `1.511+repack-1`.
- Upstream homepage: `https://github.com/lxgw/LxgwWenKai`.
- Description: an open-source Chinese font derived from Fontworks' Klee One; `LXGW` is the
abbreviation for `Luo Xia Gu Wu`.
- Package contains six faces: `LXGWWenKai-{Light,Regular,Bold}.ttf` and
`LXGWWenKaiMono-{Light,Regular,Bold}.ttf`.
- Binary package size: 60,097,836 bytes compressed; installed size: 116,871 KiB (119,675,904
bytes) for all six files. The average is 19,945,984 bytes (19.02 MiB) per face. These are Debian
package figures, not a measured upstream Regular file.
The Debian version is **evidence of a release number, not an acceptable pin for the font bytes**:
the archive is a Debian `repack`, and the exact upstream tag, commit, release archive SHA-256 and
member hash still need to be recorded. `LxgwWenkaiGB` has no local package metadata at all, so its
release must be verified independently before it can be selected.
### Candidate table
| Field | Base candidate | GB candidate | Required evidence before adoption |
|---|---|---|---|
| Project | `lxgw/LxgwWenKai` | `lxgw/LxgwWenkaiGB` | Official release page and immutable tag/commit |
| Candidate release | `1.511` (Debian-observed; upstream tag/commit unverified) | not locally verified | Release tag, commit, release date, archive URL and SHA-256 |
| Expected file | `LXGWWenKai-Regular.ttf` | `LXGWWenKaiGB-Regular.ttf` (exact name to verify) | Byte-identical upstream member; no rename/subset unless reviewed |
| Derivative author | LXGW / Luo Xia Gu Wu | LXGW / Luo Xia Gu Wu | `name` table and upstream licence header |
| Original face | Fontworks Klee One | Fontworks Klee One | Upstream OFL and copyright notice |
| Licence | OFL-1.1 | OFL-1.1 expected; verify | Byte-identical `OFL.txt`, hash and destination |
| RFN | Not proven locally | Not proven locally | Read the full OFL header and record every Reserved Font Name; ship unmodified bytes |
| Script | `Hans` (plus exact inherited coverage) | `Hans` expected | Actual cmap/script data, not a guess from the family name |
| Coverage | Full family expected to be broader | GB-oriented; verify exact set | Outline checks for `简体中文演示文稿` and `简` U+7B80, plus documented limits |
| Native-only | Yes | Yes | No wasm allowlist entry |
**RFN handling:** the plan must not state "no Reserved Font Name" until the upstream OFL text has
been read. Regardless of the result, ship the release member unmodified and retain the upstream
family name. Do not subset, rename or create a "DeckCraft Chinese" face. If the upstream licence
does contain an RFN, the unmodified redistribution is still the safe path; any later modified
face needs a separate OFL review.
### Coverage acceptance
The bundled face is accepted for the current regression only when it draws every character of
`简体中文演示文稿` and the exact failing codepoint `简` U+7B80 with non-empty outlines. Do not
infer coverage from `scripts.contains("Hans")`. Also record which GB repertoire the chosen release
claims (for example GB 2312/GBK/GB18030) and do not advertise broader coverage without a cmap
check.
For mixed UI text, assert both proportional and bold interface families, as the current test does.
Fallback shaping is not the hard part here: these are ordinary Han characters with no combining
marks. Still, a real UI screenshot and an edit/reopen workflow are required, not just a glyph
lookup.
## Native byte budget and WASM exclusion
### Current bytes
The central `fonts/manifest.txt` currently names 45,600,892 font bytes (43.49 MiB). The
16,437,364-byte `Noto Sans CJK SC` file is still in the repository for other consumers but is
excluded from DeckCraft native builds by `crates/fonts/build.rs`.
DeckCraft's web branch embeds only:
- `BIZ UDPGothic` Regular
- `Inter` Regular, Bold or Variable
The wasm branch skips every other manifest row (`crates/fonts/build.rs` lines 49-62). A new
Chinese row in a native-only manifest therefore has a 0-byte wasm delta by construction. Do not
edit that allowlist and do not add the Chinese face to GridCraft's `WEB_FONTS`.
### Budget gates
Use these as hard gates until exact upstream bytes are available:
| Gate | Limit | Measurement / action |
|---|---|---|
| One font file | <= 24 MiB | Upstream file size; abort if larger |
| Licence file | <= 8 KiB | Byte hash and destination under `fonts/<family>/` |
| Native executable/bundle growth | <= 26 MiB | Release build before/after, recorded per app |
| Additional runtime font storage | <= 50 MiB peak | Startup and first Chinese paint; egui keeps a copy of each face |
| Wasm raw and compressed delta | 0 bytes | Build with the unchanged allowlist and assert no LXGW family in `CRAFT_FONTS` |
| Number of added faces | exactly 1 | One static Regular row, no Light/Bold/Mono sibling files |
The 24 MiB font ceiling is intentionally above the 19.02 MiB Debian-package average. It gives room
for a single full base Regular while rejecting an accidental six-face package. If the verified GB
Regular is smaller, record its actual size; do not replace the measurement with an estimate.
`crates/ui-egui/src/uifonts.rs` already accepts non-colour faces up to 128 MiB and an aggregate of
384 MiB. One 20-24 MiB face is within that policy, but the user-visible startup and memory cost
must still be measured rather than inferred from that constant.
### Packaging choice
Keep the existing native consumers unchanged unless they explicitly opt in. Add the Chinese face
under a separate opt-in manifest, for example `fonts/simplified-chinese-manifest.txt`, and have
DeckCraft's native build read it after the Latin and general manifests. This avoids silently
adding ~20 MiB to GridCraft and every other native consumer, and preserves GridCraft's documented
system-only Chinese policy. If the maintainers instead decide that all native consumers want the
face, a single row in `fonts/manifest.txt` is simpler, but then every consumer's native growth
must be reviewed and documented.
The wasm build must ignore the opt-in manifest entirely. Add a build-time or test assertion that
the Chinese family/file is absent from the wasm `CRAFT_FONTS` slice even when
`CRAFT_FONTS_REQUIRED=1`.
## Table-style test fixture
### Minimal fix
Do not add Inter and do not change production default fonts for this test. Before taking
`built_in`, set the first-row text run's font to a family whose Regular and Bold faces are both
present. Prefer `BIZ UDPGothic`, because it is already in the central manifest and is required by
the current candidate.
The read-only hmtx check above proves the existing BIZ files are not metric clones:
- `BIZUDPGothic-Regular.ttf`: `Hello world` = 12,767 units / 2,048 upem
- `BIZUDPGothic-Bold.ttf`: `Hello world` = 13,237 units / 2,048 upem
- At 18 pt: 112.21 pt vs 116.34 pt (4.13 pt difference)
That is enough for the test's `> 0.5 pt` threshold. The check is a static hmtx measurement, not a
layout pass; the managed test still has to prove the caret actually moves.
For a no-candidate run, choose another available family with genuinely different Regular/Bold
faces (for example DejaVu Sans or Liberation Sans if installed), or skip only when
`CRAFT_FONTS_REQUIRED` is unset. Under `CRAFT_FONTS_REQUIRED=1`, missing BIZ Regular/Bold must be a
failure, not a skip. The test must not silently return to the current "both weights are the same
fallback face" condition.
### Separate Latin-manifest gap
The absence of `fonts/latin-manifest.txt` and `Inter` is still a real production gap for DeckCraft's
default theme and document metrics. It is not necessary to solve it inside this table fixture.
Do not hide the gap by making the test pass with a CJK face. Record it as a separate prerequisite:
pin and verify the Latin manifest, add the reviewed Inter faces, and measure native/web growth.
The table test can proceed with BIZ in the meantime.
## Alternatives considered
### A. Native-only LXGW WenKai GB Regular (recommended)
- Pros: one real permitted face; fixes the Chinese UI regression on Linux and any platform without
a CJK system font; deterministic test; no wasm growth; keeps Noto CJK banned.
- Cons: ~10-24 MiB native cost; exact GB release/provenance not yet verified; Kai style may be
less UI-neutral than a sans, so it should remain a fallback behind system CJK when present.
- Decision: adopt only after the provenance, licence/RFN, coverage and byte gates pass.
### B. Native-only full LXGW WenKai Regular
- Pros: broader coverage and the release number is corroborated by Debian 1.511; same licence
family.
- Cons: likely ~19 MiB or more per face; larger than the GB variant; more glyphs than the UI
regression needs.
- Decision: fallback if GB cannot be pinned or does not cover the sample. Do not ship both.
### C. System-only Chinese plus owned fixture
- Pros: no shipped Chinese font, no native size growth, no new font licence.
- Cons: Linux/bare CI still cannot render Chinese; the UI test uses a fixture rather than
production coverage; a strict-glyph paint can panic before a runtime skip. It is honest only if
the product explicitly accepts that Chinese requires an installed system font.
- Decision: contingency only. The fixture must be contributor-original or openly licensed,
attributed, and clearly separated from production acceptance. Do not call this "real Chinese
coverage".
### D. Noto Sans CJK SC
- Rejected by DeckCraft policy. It remains in the central repository for other consumers but is
filtered out of DeckCraft's native build and must stay out of its wasm allowlist.
### E. Remove or skip the Chinese assertion
- Rejected. It would hide a real missing-glyph panic and contradict the requirement to keep the
UI regression meaningful. Tofu comparison or `allow_missing_glyphs` is not acceptance.
## Required tests and evidence before implementation
### Acquisition and provenance (must run before code changes)
1. Read the official release page for the selected LXGW project. Record the release tag, commit
SHA, release date, archive URL, archive byte size and archive SHA-256.
2. Download only the reviewed release archive into the durable acquisition evidence directory.
Record every release asset name and size. Do not use a package mirror's repacked binary as the
font source.
3. Extract the single Regular TTF member without renaming, subsetting or regenerating it. Record
its byte size and SHA-256, and the member's byte identity against the archive.
4. Extract the upstream `OFL.txt`; record byte size, SHA-256 and the complete copyright/RFN
language. Confirm the original Fontworks Klee One notice is retained.
5. Read the font's `name` table, OS/2 weight/width, sfnt/collection status and `fvar`/`STAT`
presence. Require one static Regular face if that is what is selected.
6. Read cmap and outlines for every character of `简体中文演示文稿`, especially `简` U+7B80.
Record the claimed GB repertoire and do not generalize beyond it.
7. Store the acquisition record, licence, hashes and inspection output under the existing
`/home/codex/work/ssd2/task-evidence/egui037-migration-20261022/font-acquisition/` structure.
If any of these fail, stop. Do not add the bytes and do not switch to "system-only" without
recording the product limitation.
### craft-fonts tests
Add, but do not run outside the managed workflow:
- exact manifest row for family/style/file/script/licence/hash/source;
- SHA-256 and licence adjacency checks;
- one static face, exact `name` table family/style, weight 400, no accidental variable instances;
- exact script `Hans` (unless the verified metadata justifies an additional script tag);
- outline checks for the full sample and U+7B80;
- file-size ceiling;
- a test that the opt-in manifest is not read by a wasm consumer, or an equivalent build-time
assertion that the wasm `CRAFT_FONTS` slice has no LXGW entry.
### DeckCraft tests
Run with the exact candidate and `CRAFT_FONTS_REQUIRED=1`:
```text
cargo test --config <reviewed-local-config> -p deckcraft-ui-egui --lib \
ime_tests::cjk_text_in_the_interface_is_drawn_with_real_glyphs
cargo test --config <reviewed-local-config> -p deckcraft-engine --lib \
tests::tables_with_a_file_defined_style_edit_and_resize
cargo test --config <reviewed-local-config> -p deckcraft-ui-egui --lib
cargo test --config <reviewed-local-config> -p deckcraft-engine --lib
```
The Chinese test must assert all four samples are checked on every platform where the bundled
face is present, and must not reach the missing-glyph panic before the assertion. The table test
must use the BIZ pair and show a caret delta greater than 0.5 pt.
Also run the no-candidate path separately. It may skip the table-metric assertion only when
`CRAFT_FONTS_REQUIRED` is unset; it must not claim Chinese coverage without a real face.
### Wasm and size checks
- Run the repository's wasm gate with the unchanged allowlist.
- Assert the generated wasm `CRAFT_FONTS` contains no LXGW family and no Chinese font bytes.
- Record raw and compressed wasm sizes before/after; require a 0-byte delta for this font.
- Record release native binary/bundle size before/after and enforce the <=26 MiB gate.
- Record peak memory before/after first Chinese paint.
### Performance
Use the existing font-registration benchmark protocol rather than a new ad-hoc timer:
- five alternating before/after pairs, warm-up, at least 100 repeated registrations per sample;
- additional median registration time <= 1 ms;
- within-run CV <= 5%; if noisier, do not claim a pass;
- register one face per family set that includes it, not multiple font copies per character;
- record the exact font bytes, build profile, commit and command.
Also record startup-to-usable and first-Chinese-paint timings, plus the UI screenshot. A glyph
lookup or unit test alone is not a UI acceptance result.
## Implementation sequence (after this plan is approved)
1. Complete acquisition/provenance and freeze the exact release and hashes.
2. Add the single face, `OFL.txt`, opt-in manifest row, attribution/README rows and integration
documentation.
3. Add craft-fonts metadata/coverage/size tests.
4. Teach DeckCraft's native build to read the opt-in manifest; leave the wasm branch unchanged.
5. Add the exact family to DeckCraft's native interface fallback only if the existing dynamic
`UiFonts` path does not deterministically select it. Prefer the existing fallback path; do not
make the Chinese face a primary Latin/UI face.
6. Change the table-style fixture to use BIZ UDPGothic Regular/Bold. Do not add Inter here.
7. Run the managed checks above, capture screenshots and measurements, and preserve all evidence.
8. Update the central Chinese section to distinguish GridCraft's system-only policy from
DeckCraft's native bundled fallback. Do not add Chinese to any web allowlist.
## Open issues and limitations
- Official GitHub release metadata, exact archive hashes and the upstream `OFL.txt`/RFN text could
not be read in this environment because DNS/network access failed. The only local version
evidence is Debian's `1.511+repack-1` packaging of the base family; that is not a font-byte pin.
- The GB release, exact file name, size and coverage are therefore unverified. The plan must not
be treated as authorization to fetch or commit a font.
- No native or wasm size, startup, memory or registration benchmark was measured here. The table
BIZ advance check is a read-only hmtx measurement, not a layout or performance result.
- Native-only Chinese fixes the strict-glyph UI failure only when the build actually includes the
reviewed face. The default no-font path remains a documented limitation and must not claim
Chinese coverage.
- The table fixture change is test-scoped. The missing `latin-manifest.txt`/Inter issue remains a
separate production prerequisite; it must not be papered over by selecting a CJK family for
the default theme.
## Acquisition and v2 review dispositions (2026-10-23)
The independent v2 review is saved in the migration evidence. Implement these corrections with
the original plan; they supersede the speculative GB choice and any broader coverage claim.
- The GB v1.530 Regular release is 26,170,696 bytes, above the planned 24 MiB font ceiling.
Select the official **LXGW WenKai Lite Regular v1.530**, commit
`b712c83d98acf977bacfb7fb010207802808bda9`, instead. The unmodified release TTF is
13,949,472 bytes, SHA256 `f799a538a3355877e597a0ae04050e897932c9ae7689eb9227c2953c5dd549ea`.
The official release ZIP is 51,990,239 bytes, SHA256
`7fa084dfdb76f36a40c183dbb50cb5d2f92b2ea26553454988e08c889f8ed5fa`.
Its extracted OFL.txt is 5,176 bytes, SHA256
`0333a85fe077b6849f8e417acc44e8bd879442ceb681e271acf93e2d57fd4e2e`.
All bytes and metadata are retained under the migration evidence's
`font-acquisition/lxgw-wenkai-lite/`. Static one-face sfnt, weight400, no fvar, actual
family `LXGW WenKai Lite`, style `Regular`, version1.530; no renaming/subsetting.
Official source: https://github.com/lxgw/LxgwWenKai-Lite/releases/tag/v1.530 .
RFNs are 霞鹜, 霞鶩, 落霞孤鹜, 落霞孤鶩, LXGW; retain full licence and Klee copyright.
- Add only `fonts/simplified-chinese-manifest.txt`, with the existing eight-field parser format.
Use `Hani,Hans` because Deck's script classifier emits Hani, and restrict coverage claims to
actually checked text. Tests explicitly parse this optional manifest, then verify hashes,
licence, family/style, weight400, no fvar, outlines and <=24MiB size. Do not add it to FONTS
or the default embedded manifest. Update central attribution to cover both manifests.
- Accept the existing bundled-first, per-character fallback order. This fixes missing glyphs
for document-derived UI text and adds a native static-label fallback; it does not promise
uniform Chinese typography or system-first precedence. BIZ remains ahead for characters it
already covers. Add LXGW after existing native families in theme::font_definitions.
- The observed panic is kittest's strict MissingGlyphPolicy::Panic; production defaults to
Tofu. Describe production missing glyphs, not an unproven production crash.
- Deck reads the opt-in manifest only for native builds. Missing file is compatible with older
craft-fonts checkouts. If present but malformed/unreadable, required-font mode fails; optional
mode warns and retains the successfully parsed main fonts. Required-font mode with no
CRAFT_FONTS_DIR is an error. No Chinese font is added to any wasm allowlist.
- The initial-document regression must paint Chinese before any user event. With the LXGW face
bundled or required-font mode enabled, require all four CJK samples on Linux too; no skips.
Verify actual proportional/bold glyph ink, including 简, rather than only a cmap entry.
- The table fixture sets explicit BIZ UDPGothic on the first-row runs before either caret
measurement, leaves bold unset so table style controls it, and requires distinct actual
regular/bold faces. Required-font mode fails when the pair is absent. In optional mode the
metric-only assertion may skip with a visible reason; preserve independent edit/resize checks.
- Update Deck attribution, localization-parity and central integration docs. Native size,
startup/registration measurements, screenshot and wasm zero-byte delta remain acceptance
gates, not claims established by this acquisition.
The release metadata was obtained through GitHub's read-only connector. Because it cannot
return binary assets, the exact public release ZIP was downloaded through the normal host
network path and verified against GitHub's published SHA256. No repository asset has been
modified or published by acquisition alone.
### Central craft-fonts implementation status (2026-10-24)
The central side is implemented locally, with no DeckCraft source edited here:
- Copied the reviewed upstream `LXGWWenKaiLite-Regular.ttf` (13,949,472 bytes, SHA-256
`f799a538a3355877e597a0ae04050e897932c9ae7689eb9227c2953c5dd549ea`) and `OFL.txt`
(5,176 bytes, SHA-256 `0333a85fe077b6849f8e417acc44e8bd879442ceb681e271acf93e2d57fd4e2e`)
unmodified to `fonts/lxgw-wenkai-lite/`.
- Added one row to the opt-in `fonts/simplified-chinese-manifest.txt`: `LXGW WenKai Lite`,
`Regular`, `Hani,Hans`, OFL-1.1, adjacent licence path, the TTF hash and the v1.530 release
source. The default `fonts/manifest.txt`, `FONTS` and the `embed` payload are unchanged.
- Added central tests that parse the optional manifest with `parse_manifest` and check the exact
row, hash, adjacent licence and reserved-font-name/copyright text, one static weight-400 face
without `fvar`/`avar`/`STAT`, drawn outlines for `简体中文演示文稿` and `简`, the 24 MiB
ceiling, and absence from the default manifest/`FONTS`.
- Updated `ATTRIBUTION.md`, `README.md`, `docs/integration.md` and the Chinese section of
`docs/egui-037-font-coverage-plan.md` to distinguish GridCraft's system-only policy from
DeckCraft's native-only opt-in.
- DeckCraft build-script/UI integration and the BIZ Regular/Bold table fixture are implemented;
current automated and central checks are recorded above. Native/wasm size measurements,
visible edit/save/reopen screenshots and performance remain consumer acceptance gates.
This central font resource does not own the downstream app/VM resources.
+495
View File
@@ -0,0 +1,495 @@
# egui 0.37 fallback-font prerequisite
## Status: central prerequisite validated locally; consumer qualification continues
The central asset/library change is validated atop `fff559f`. Managed
`egui037-fonts-candidate-20261023-chinese-05` passed all 29 all-feature tests and
strict clippy. `egui037-fonts-candidate-20261024-final-06` passed formatting,
28 default workspace tests, 4 explicit embed tests and strict all-feature clippy.
Fresh implementation review `egui037-fonts-upstream-final-review-20261024-root-v3`
confirmed all 13 font hashes, the five added font/licence pairs, metadata,
provenance and default/opt-in behavior. Consumer native GUI, font registration,
web size and performance checks remain in their separate app resources.
## Follow-up prerequisite: Hebrew consumer coverage
DeckCraft needs an actual Hebrew fallback for its mixed-script editing paths. The first
amendment incorrectly called the migration fixture's default instance Regular: the variable
font at google/fonts 8b0a1d0 defaults to Thin (`wght=100`) and enumerates nine named instances.
Keep that original fixture at `fixtures/noto-sans-hebrew/NotoSansHebrew.ttf` unchanged; it is
not added to this repository. Instead use the upstream **static Regular** release, which needs no
per-app variation-coordinate exception and registers one face.
### Provenance (verified on disk)
- Official release: [NotoSansHebrew-v3.001](https://github.com/notofonts/hebrew/releases/tag/NotoSansHebrew-v3.001),
target commit `036f3206f67caac235cf8546a7751d3440771a7e` (2024-06-03).
- Archive `NotoSansHebrew-v3.001.zip`: 3,734,185 bytes, SHA-256
`df0a71814b4e63644cf40fcc4529111b61266b7a2dafbe95068b29a7520cc3cb`
(`archive-provenance.json`; internal timestamps `2024-06-03 13:44` match the release date).
- Archive member `NotoSansHebrew/full/ttf/NotoSansHebrew-Regular.ttf` is **byte-identical** to
the extracted `full.ttf`: no rename, subset, generated instance or internal name edit.
- Font: 66,156 bytes, SHA-256
`671951828bd5c95db818e5bb12dcea2d0c0dda00311888522be061ee6835125e`.
- Archive-root `OFL.txt`, extracted as `license.txt`, is byte-identical: 4,382 bytes, SHA-256
`9b9fe028b5ba74d231659a1bbaf0ed09b11e759d1ca6a070999e16d151616b47`.
- Acquisition and all three upstream static variants (`full.ttf`, `hinted.ttf`, `unhinted.ttf`)
are retained at
`/home/codex/work/ssd2/task-evidence/egui037-migration-20261022/font-acquisition/noto-sans-hebrew-static/`.
The directory's `archive-provenance.json` records the URL, release commit, zip bytes and zip
SHA-256. The top-level `acquisition.json` also records the Hebrew font and licence members,
including their release URL, release commit, sizes and hashes. The evidence owner added
those records before the fresh implementation review.
- Metadata: no `fvar`, `avar` or `STAT` table; sfnt version 1.0; 17 tables; 470 glyphs; OS/2
`usWeightClass=400`, `usWidthClass=5`, `fsSelection=REGULAR|USE_TYPO_METRICS`, `macStyle=0`,
`italicAngle=0`; names `Noto Sans Hebrew`, `Regular`, `Noto Sans Hebrew Regular`,
`NotoSansHebrew-Regular`, version `3.001`.
- Coverage: all 27 letters `U+05D0..U+05EA` and all 24 assigned points/punctuation
`U+05B0..U+05C7` map to non-empty outlines. 88 of the 112 code points in `U+0590..U+05FF` are
mapped and every mapped glyph draws. The hinted/unhinted variants are smaller subsets (147
cmap entries / 151 glyphs vs 464 / 470) with the same Hebrew coverage, so the full face is
chosen to avoid assuming coverage from a subset.
### Repository addition (applied locally)
- `fonts/noto-sans-hebrew/NotoSansHebrew-Regular.ttf`, 66,156 bytes, SHA-256
`67195182…5125e`, byte-identical to the verified upstream member.
- `fonts/noto-sans-hebrew/OFL.txt`, 4,382 bytes, SHA-256 `9b9fe028…616b47`, byte-identical to
the archive-root `OFL.txt`. OFL-1.1 declares no Reserved Font Name, so shipping the unmodified
file with the adjacent licence is compliant.
- Manifest row (eight fields; the `source` field names the release tag):
```text
Noto Sans Hebrew | Regular | fonts/noto-sans-hebrew/NotoSansHebrew-Regular.ttf | Hebr | OFL-1.1 | fonts/noto-sans-hebrew/OFL.txt | 671951828bd5c95db818e5bb12dcea2d0c0dda00311888522be061ee6835125e | https://github.com/notofonts/hebrew/releases/tag/NotoSansHebrew-v3.001
```
- Matching `ATTRIBUTION.md` and README rows: family `Noto Sans Hebrew`, style `Regular`, script
`Hebr`, author The Noto Project Authors, OFL-1.1, licence destination
`fonts/noto-sans-hebrew/OFL.txt`.
- `docs/integration.md` lists the `Hebr` face in the central fallback set and states that it is
native-only for this round.
- The `crates/craft-fonts/Cargo.toml` "about 43 MiB" embedded-size comment remains accurate:
the manifest's font bytes become 45,600,892 (43.49 MiB) after +66,156, so no comment edit is
needed. This is the documented "embedded-size comment" result, not a silent omission.
- Every existing font, manifest line, attribution row, README row and the original baseline
fixture stay byte-identical.
### Registration and size gates
- Exactly +66,156 font bytes and +4,382 licence bytes (70,538 total) in the central asset set.
- Exactly one additional native FontFace **file/face**, never nine instances. "One face" means
one manifest row and one static face with no `fvar`/named instances; a consumer registers that
face once per family set that includes it (egui's proportional family and, where present, its
`CELL`/monospace family), so two families may reference the same face without duplicating bytes
or creating instances.
- Native GridCraft embeds the new non-barred row under its existing policy; record its
+66,156-byte font growth and the separate executable/bundle growth. GridCraft's five-face web
allowlist stays unchanged, and DeckCraft's current web allowlist also excludes Hebrew. This
amendment claims **native** required-font acceptance only; web support remains an explicit
separate qualification gap.
- **Shaping qualification:** this range includes combining marks and punctuation; the marks
need GPOS positioning. The storytold fork uses HarfRust and bidi layout (epaint
`font_face.rs` shaper data and `text_layout.rs`), so the earlier review
statement that this path has no shaping was incorrect. Font coverage alone does not prove
correctly positioned or reordered vocalized Hebrew. The initial glyph acceptance uses
unpointed mixed text such as `שלום deck 42` (and unpointed `שלום`
alone); it must not claim correctly shaped niqqud.
- Benchmark identical native DeckCraft font-registration operations before/after, five
alternating pairs with warm-up and at least 100 repeated registrations per sample; require
additional median time <=1 ms per registration and within-run CV <=5%. No performance pass if
measurement is too noisy. Register the one static face, not nine instances.
### Consumer acceptance
- DeckCraft native required-font acceptance must assert exact family `Noto Sans Hebrew`, static
Regular weight (OS/2 400, no `fvar`), Hebrew coverage, and mixed Hebrew/Latin caret/edit
behavior for `שלום deck 42` with system font discovery isolated (`CRAFT_FONTS_DIR` set, no
system font provider). The no-font path must build/run without claiming bundled Hebrew. Strict
glyph/accessibility checks remain enabled; no `allow_missing_glyphs`, tofu comparison or
silent skip is acceptance.
- Run central `fmt`, `clippy`, default/embed tests and the hash/licence/metadata tests below,
then the named DeckCraft required/no-font tests and actual mixed-direction editing. This
revision received the fresh adversarial review in
`egui037-font-hebrew-plan-review-20261023-root-v2.md`; the fresh implementation review
and central validation are recorded above. Nothing is published without explicit approval.
GridCraft's strict egui_kittest run exposes missing UI symbols and Korean glyphs. This
prerequisite adds only the fonts that are actually missing, keeps every existing face and asset
byte-identical, and keeps `CRAFT_FONTS_DIR` optional. It does not copy font binaries into app
repositories, and it does not use the barred Adobe / Source Han / Noto CJK families in GridCraft.
Fresh remote state used for this plan:
- `craft-fonts` PR 23 is **closed, unmerged**, head
`64b29ef11d9bfb2f50517ef659d4cfb410ad1afc`. Reuse only its selected, verified NanumGothic
provenance below; do not merge the PR, its Latin manifest or its other faces.
- `craft-fonts` PR 46 is **open**, head `692413113844e5c621239acc429d621addfdb084`. Reuse only
the selected Noto Sans Symbols provenance below; do not merge the whole PR or its CJK, emoji,
Math and UI face set.
## Implementation status and verified assets
The exact upstream assets have been acquired, verified and installed locally. The acquisition
evidence is `/home/codex/work/ssd2/task-evidence/egui037-migration-20261022/font-acquisition/`
with `acquisition.json` recording every URL, path, byte count and SHA-256 for the three faces
below. Every recomputed hash matches the recorded value and the pins below. The later Hebrew
face's structured record is in the follow-up section above and in
`font-acquisition/noto-sans-hebrew-static/archive-provenance.json`.
Read-only cmap and `glyf` inspection of the downloaded bytes established the actual coverage:
- NanumGothic Regular covers all 11,172 modern Hangul syllables (`U+AC00..U+D7A3`) and all 51
compatibility Jamo (`U+3131..U+3163`), with drawn outlines for `한`, `글`, `각` and `힣`. It
does not cover `✕`, `⌃`, `✓` or `📋`.
- Noto Sans Symbols Regular (Symbols 1) covers `⌃` U+2303, `↘` U+2198 and `↑` U+2191 with drawn
outlines. It does **not** cover `✕` U+2715, `◀` U+25C0 or `✓` U+2713.
- Noto Sans Symbols 2 Regular covers `✕` U+2715, `◀` U+25C0 and `✓` U+2713 with drawn outlines.
It does **not** cover `⌃`, `↘` or `↑`, so it cannot replace Symbols 1.
Symbols 2 is therefore **required**, not conditional: Symbols 1 lacks `✕` and `◀`, and no
existing permitted face covers `✕`. `📋` U+1F4CB is supplied by egui's bundled emoji faces and
is deliberately not claimed as craft-fonts coverage.
**Upstream filename correction:** the Symbols 1 file at the pinned upstream commit is
`NotoSansSymbols[wght].ttf`, not `NotoSansSymbols.ttf` as an earlier draft said. The local
manifest row and destination use `NotoSansSymbols.ttf`; this is a filename-only rename of the
byte-identical download. The internal family, style, PostScript and full names are unchanged
(`Noto Sans Symbols`, `Regular`, `NotoSansSymbols-Regular`, `Noto Sans Symbols Regular`) and the
SHA-256 is unchanged.
## Verified upstream assets
All files are upstream, unmodified, unsubsetted and unrenamed except for the documented Symbols 1
filename-only rename. All three fonts are OFL-1.1; their `OFL.txt` files are installed next to
them and are not copied into app repositories.
| Face | Upstream pin and file | Local destination | Bytes | SHA-256 |
|---|---|---|---|---|
| NanumGothic | [google/fonts@bd8f81d](https://github.com/google/fonts/tree/bd8f81ddb5c74d5c8897b36ad88b440266245103/ofl/nanumgothic) / `NanumGothic-Regular.ttf` | `fonts/nanum-gothic/NanumGothic-Regular.ttf` | 2,054,744 | `76f45ef4a6bcff344c837c95a7dcc26e017e38b5846d5ae0cdcb5b86be2e2d31` |
| Noto Sans Symbols | [google/fonts@47d54ca](https://github.com/google/fonts/tree/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols) / `NotoSansSymbols[wght].ttf` | `fonts/noto-sans-symbols/NotoSansSymbols.ttf` | 372,684 | `f7e7e04b4a24b6c78893d50cbfd2b2f6cae49617ab047bfef668d252adb128f7` |
| Noto Sans Symbols 2 | [google/fonts@47d54ca](https://github.com/google/fonts/tree/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols2) / `NotoSansSymbols2-Regular.ttf` | `fonts/noto-sans-symbols-2/NotoSansSymbols2-Regular.ttf` | 1,233,128 | `7d5fb73b7ca67a6798101741f5d280a3d016a56a197afcd4199dbb57b4b82a21` |
The licence files are:
- `fonts/nanum-gothic/OFL.txt`, 4,534 bytes,
SHA-256 `eeacf16032901d0ed0456876ec77b8f0fda6b3fecec7d972f8543eb602e6c30f`.
- `fonts/noto-sans-symbols/OFL.txt` and `fonts/noto-sans-symbols-2/OFL.txt`, both 4,383 bytes,
SHA-256 `b118dd41337806a5d4797052c77caf3bd096aed783e5eb21b4d11154351e1ac0`.
Exact acquisition commands (recomputed hashes must match before a manifest edit):
```sh
mkdir -p fonts/nanum-gothic fonts/noto-sans-symbols fonts/noto-sans-symbols-2
curl -fL -o fonts/nanum-gothic/NanumGothic-Regular.ttf \
https://raw.githubusercontent.com/google/fonts/bd8f81ddb5c74d5c8897b36ad88b440266245103/ofl/nanumgothic/NanumGothic-Regular.ttf
curl -fL -o fonts/nanum-gothic/OFL.txt \
https://raw.githubusercontent.com/google/fonts/bd8f81ddb5c74d5c8897b36ad88b440266245103/ofl/nanumgothic/OFL.txt
curl -fL -o 'fonts/noto-sans-symbols/NotoSansSymbols.ttf' \
'https://raw.githubusercontent.com/google/fonts/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols/NotoSansSymbols%5Bwght%5D.ttf'
curl -fL -o fonts/noto-sans-symbols/OFL.txt \
https://raw.githubusercontent.com/google/fonts/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols/OFL.txt
curl -fL -o fonts/noto-sans-symbols-2/NotoSansSymbols2-Regular.ttf \
https://raw.githubusercontent.com/google/fonts/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols2/NotoSansSymbols2-Regular.ttf
curl -fL -o fonts/noto-sans-symbols-2/OFL.txt \
https://raw.githubusercontent.com/google/fonts/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols2/OFL.txt
sha256sum fonts/nanum-gothic/NanumGothic-Regular.ttf \
fonts/noto-sans-symbols/NotoSansSymbols.ttf \
fonts/noto-sans-symbols-2/NotoSansSymbols2-Regular.ttf
```
### NanumGothic Regular (required)
- Family **`NanumGothic`** (exact spelling, no space), style `Regular`, scripts `Hang,Kore`.
- Licence header: `Copyright (c) 2010, NHN Corporation (http://www.nhncorp.com), with Reserved
Font Name Nanum, Naver Nanum, NanumGothic, ...`. Authors recorded by the PR 23 evidence: NHN
Corporation; Sandoll Communications.
- `METADATA.pb` was read for author/licence confirmation but is not copied into `fonts/`: this
repository ships the font, `OFL.txt`, the manifest row and the attribution row only.
- Font: 13,297 glyphs (`maxp`), 11,827 cmap entries; 11,172/11,172 modern Hangul syllables; 51/51 compatibility Jamo
`U+3131..U+3163`; 42 of the 43 archaic compatibility Jamo `U+3164..U+318E`; no conjoining
Hangul Jamo blocks (`U+1100..U+11FF`, `U+A960..U+A97F`, `U+D7B0..U+D7FF`).
- Outlines: `한`, `글` and `각` are simple glyphs; `힣` is a composite whose two referenced
components both have non-empty outlines.
### Noto Sans Symbols Regular (required)
- Family **`Noto Sans Symbols`**, style `Regular`, script `Zsym`. This is a variable font with a
`wght` axis (`100..900`, default `400`); the name table's subfamily is `Regular` and the
PostScript name is `NotoSansSymbols-Regular`.
- The upstream filename is `NotoSansSymbols[wght].ttf`; the local file is the same bytes under
`NotoSansSymbols.ttf`.
- cmap/outline evidence: `⌃` U+2303, `↘` U+2198 and `↑` U+2191 map to non-empty outlines.
`✕` U+2715, `◀` U+25C0, `✓` U+2713 and `📋` U+1F4CB are absent.
### Noto Sans Symbols 2 Regular (required)
- Family **`Noto Sans Symbols 2`**, style `Regular`, script `Zsym`.
- cmap/outline evidence: `✕` U+2715, `◀` U+25C0 and `✓` U+2713 map to non-empty outlines.
`⌃`, `↘` and `↑` are absent, so Symbols 2 cannot replace Symbols 1. Symbols 2 also has
`📋` U+1F4CB, but that glyph is supplied to GridCraft by egui's emoji faces and is not
claimed as craft-fonts coverage.
- Added only because the actual cmap proves it is required, not because PR 46 proposed it.
### Faces that must not be added here
- `📋` U+1F4CB is supplied by egui's bundled emoji faces (`Noto Emoji`, `emoji-icon-font`). Do
not add a craft emoji face or claim craft-fonts covers `📋`.
- Do not add Noto CJK, Source Han, Adobe, color emoji or other large CJK/emoji assets for this
prerequisite.
## Actual cmap coverage, old and new faces
Independently checked with a read-only cmap reader against the current and downloaded files (not
a build). `yes` means the codepoint maps to a drawn outline at the default instance
(`glyf` for TrueType faces; CFF for Noto Sans CJK SC).
| Face | `↘` | `✕` | `◀` | `↑` | `📋` | `✓` | `⌃` |
|---|---|---|---|---|---|---|---|
| BIZ UDPGothic Regular / Bold | yes | no | yes | yes | no | yes | no |
| BIZ UDMincho Regular | yes | no | yes | yes | no | yes | no |
| BIZ Shippori Mincho Regular | no | no | no | yes | no | no | no |
| Noto Sans CJK SC Regular | yes | no | yes | yes | no | yes | no |
| egui Hack Regular | yes | no | yes | yes | no | no | no |
| egui Noto Emoji Regular | yes | no | yes | no | yes | no | no |
| egui emoji-icon-font | no | no | no | no | yes | no | no |
| egui Ubuntu Light / egui-icons | no | no | no | no | no | no | no |
| NanumGothic Regular | yes | no | yes | yes | no | no | no |
| Noto Sans Symbols Regular | yes | no | no | yes | no | no | yes |
| Noto Sans Symbols 2 Regular | no | yes | yes | no | yes | yes | no |
Consequences:
- The no-font path cannot promise all seven symbols. `✕` and `⌃` are real gaps, and `✓` is not
in the bundled egui faces even though BIZ and Noto Sans CJK SC have it.
- With the selected craft faces, `⌃` comes from Symbols 1; `✕`, `◀` and `✓` come from Symbols 2;
`↘` and `↑` come from Symbols 1 (NanumGothic also happens to have them); `📋` comes from egui.
- Noto Sans CJK SC happens to cover Korean and `↘ ◀ ↑ ✓`, but GridCraft bars it. It cannot be
used as the Korean or symbol answer.
## Repository changes (applied locally)
These three manifest lines were added by the earlier prerequisite; the Hebrew follow-up adds
one more row (above). Every pre-existing line is unchanged:
```text
NanumGothic | Regular | fonts/nanum-gothic/NanumGothic-Regular.ttf | Hang,Kore | OFL-1.1 | fonts/nanum-gothic/OFL.txt | 76f45ef4a6bcff344c837c95a7dcc26e017e38b5846d5ae0cdcb5b86be2e2d31 | https://github.com/google/fonts/tree/bd8f81ddb5c74d5c8897b36ad88b440266245103/ofl/nanumgothic
Noto Sans Symbols | Regular | fonts/noto-sans-symbols/NotoSansSymbols.ttf | Zsym | OFL-1.1 | fonts/noto-sans-symbols/OFL.txt | f7e7e04b4a24b6c78893d50cbfd2b2f6cae49617ab047bfef668d252adb128f7 | https://github.com/google/fonts/tree/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols
Noto Sans Symbols 2 | Regular | fonts/noto-sans-symbols-2/NotoSansSymbols2-Regular.ttf | Zsym | OFL-1.1 | fonts/noto-sans-symbols-2/OFL.txt | 7d5fb73b7ca67a6798101741f5d280a3d016a56a197afcd4199dbb57b4b82a21 | https://github.com/google/fonts/tree/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols2
```
Matching `ATTRIBUTION.md` rows and README `Fonts` rows were added for the same three files; the
Hebrew follow-up adds matching rows for its one file. Every pre-existing font, asset, manifest,
attribution and README row is unchanged. The eight-field manifest format is kept; PR 46's
optional ninth `role` field and its `ui` faces are not copied.
## Consumer behavior
### No-font/default mode (no `CRAFT_FONTS_DIR`)
- The app must still build, test and run.
- Paint an explicit unavailable-icon state or fall back for UI icons that bundled/system faces
do not provide (notably `✕` and `⌃`, and `✓` in an egui-only environment). Do not paint tofu,
and do not weaken strict glyph checks, accessibility or `MissingGlyphPolicy` to make a test
pass.
- Acceptance tests in this mode assert only glyphs that bundled/system faces really provide.
They document `✓`/`⌃`/`✕` as optional-input or platform gaps rather than claiming all seven.
- Choosing an unavailable script must produce deliberate, tested fallback behavior. A hidden
local fixture path is never a prerequisite.
### Extended acceptance mode (`CRAFT_FONTS_DIR` set, `CRAFT_FONTS_REQUIRED=1`)
- Require the exact union `↘ ✕ ◀ ↑ 📋 ✓ ⌃` in both proportional and `CELL` families.
`📋` comes from egui's emoji faces; the other faces must be present in the installed
fallback set.
- Require Korean UI strings such as `데이터` and `한글`.
- Fail, do not return early or skip, when a required face or codepoint is missing. The existing
`symbol_glyphs.rs` early return when craft-fonts is absent is not acceptable acceptance
behavior.
- No `allow_missing_glyphs()` / tofu comparison may substitute for the exact-codepoint
assertion.
### Fallback ordering in GridCraft
Preserve the actual order, not the generic integration-doc order:
1. app/bundled faces;
2. Hack/emoji faces already added by `font_definitions`;
3. system script fallbacks;
4. allowed craft-fonts faces last.
Append NanumGothic, Noto Sans Symbols and Noto Sans Symbols 2 only when allowed and successfully
parsed. They must not displace system faces that already cover a glyph. Match exact family and
style: `NanumGothic` / `Regular`, `Noto Sans Symbols` / `Regular` and
`Noto Sans Symbols 2` / `Regular`; not `Nanum Gothic` and not a Symbols 2 row standing in for
Symbols 1. The manifest parser accepts arbitrary script strings, so exact `Hang,Kore` and `Zsym`
assertions belong in tests.
### Barred families in GridCraft
Keep the central repository's barred faces for other consumers, but filter them in GridCraft:
- `Source Han*`
- `Source Sans*`
- `Source Serif*`
- `Noto Sans CJK*`
- `Noto Serif CJK*`
- any Adobe-family face
Do not use a broad `Noto Sans` prefix that would also reject the permitted `Noto Sans Symbols`
and `Noto Sans Symbols 2`.
## Web allowlist
Before this migration, GridCraft's wasm `build.rs` embedded no craft-fonts. The local candidate
now uses the following explicit allowlist, retaining regular and bold Japanese UI faces.
The five font files total 12,970,840 raw bytes; this is not a measured wasm size or budget pass:
- `BIZ UDPGothic Regular`
- `BIZ UDPGothic Bold`
- `NanumGothic Regular`
- `Noto Sans Symbols Regular`
- `Noto Sans Symbols 2 Regular` (required by the verified cmap above)
Do not include Noto CJK or any barred family. Measure and record:
- raw bytes of each allowlisted font;
- raw wasm size before and after;
- compressed (gzip/brotli as used by the consumer) wasm size before and after;
- the consumer's actual web size gate.
Name the budget and add or use a real measurement gate; do not say "existing budget" without a
number and a check. If Korean web support is not accepted, remove Korean from the web language
picker and restored web preferences and record the gap honestly rather than shipping a claim
that cannot render.
## Chinese
Chinese stays documented as **system-only** in GridCraft while Noto CJK is barred. Keep Chinese
out of GridCraft's strict multilingual sample. Do not claim craft-fonts Chinese coverage from a
filtered face.
DeckCraft has a separate **native-only opt-in**, not a change to the default manifest:
`fonts/simplified-chinese-manifest.txt` lists the unmodified official LXGW WenKai Lite Regular
v1.530 (`Hani,Hans`, OFL-1.1, upstream commit
`b712c83d98acf977bacfb7fb010207802808bda9`). It is not in `fonts/manifest.txt`, `FONTS` or the
`embed` payload, and it must stay out of every wasm allowlist. It fixes document-derived UI
text such as `简体中文演示文稿` and U+7B80 `简` through the consumer's existing per-character
fallback after existing native families. It does not promise uniform Chinese typography,
system-font-first precedence, or static localized UI-label coverage. The central tests parse the
optional manifest with `parse_manifest` and check the exact row, hash, adjacent licence and RFN
text, one static weight-400 face, outlines for the full sample and `简`, and the 24 MiB ceiling.
## Tests in craft-fonts
`crates/craft-fonts-tests/tests/fonts.rs` now covers:
- exact style from the font `name` table for every manifest row;
- exact manifest metadata for all three new faces: `NanumGothic` / `Regular` / `Hang,Kore` and
`Noto Sans Symbols` plus `Noto Sans Symbols 2`, both `Regular` / `Zsym`, all three OFL-1.1;
- Korean UI strings (`데이터`, `한글`, `파일`, `편집`, `보기`, `삽입`, `서식`, `저장`, `열기`,
`닫기`, `인쇄`, `취소`, `확인`);
- the observed NanumGothic cmap: all 11,172 modern Hangul syllables and all 51 compatibility
Jamo;
- outline checks (not cmap presence alone) for `한`, `글`, `각` and `힣`;
- exact per-face symbol assignment with outlines: Symbols 1 covers `⌃ ↘ ↑`; Symbols 2 covers
`✕ ◀ ✓`; `📋` is not asserted against craft-fonts.
- the exact Hebrew manifest row and source, one Hebr face, and the one-face/static invariant:
sfnt (not TTC), no `fvar`/`avar`/`STAT`, OS/2 weight 400/width 5 and upright `REGULAR`;
- all 27 Hebrew letters `U+05D0..U+05EA` and all 24 assigned points/punctuation
`U+05B0..U+05C7` with drawn outlines (coverage, not GPOS shaping), plus the unpointed
sample `שלום עברית …` used by the mixed acceptance string.
- the optional `fonts/simplified-chinese-manifest.txt` row for `LXGW WenKai Lite` / `Regular`
(`Hani,Hans`), parsed with `parse_manifest` and kept out of `FONTS`/`fonts/manifest.txt`;
exact SHA-256, adjacent OFL licence with the five upstream reserved names and Klee copyright,
one static OS/2 weight-400 face with no `fvar`/`avar`/`STAT`, drawn outlines for
`简体中文演示文稿` and U+7B80 `简`, and a 24 MiB file ceiling. The optional manifest is not
read by craft-fonts' default build or embed payload; consumers must keep it out of wasm.
Every existing checksum, licence, family, Japanese, Chinese, Arabic, Thai, Georgian,
vertical-text and embed test is preserved. No test uses an `if present` skip or a tofu
comparison in place of coverage. These assertions passed the managed 05/06 checks recorded above.
## Regression sources
- PR 23 evidence:
`/home/codex/work/task-evidence/ci-contributor-overlap-20261015/prs/craft-fonts/23/diff.txt`
(NanumGothic manifest row, OFL header, destination paths).
- PR 46 evidence:
`/home/codex/work/task-evidence/language-adversarial-review-20261021/sources/craft-fonts-46/fonts/manifest.txt`
and `craft-fonts-46-metadata.json` (Symbols 1/2 rows, hashes, sizes and coverage notes).
- Acquisition evidence:
`/home/codex/work/ssd2/task-evidence/egui037-migration-20261022/font-acquisition/`
(`acquisition.json`, `METADATA.pb`, `OFL.txt` and the downloaded fonts).
- Independent plan reviews:
`/home/codex/work/ssd2/task-evidence/egui037-migration-20261022/egui037-font-coverage-plan-review-20261023-root-v1.md`
(overall plan) and
`/home/codex/work/ssd2/task-evidence/egui037-migration-20261022/egui037-font-hebrew-plan-review-20261023-root-v1.md`
(which found the variable fixture's Thin default and nine named instances) and
`/home/codex/work/ssd2/task-evidence/egui037-migration-20261022/egui037-font-hebrew-plan-review-20261023-root-v2.md`
(approval of this static-face revision, with the provenance and shaping nits).
- Hebrew acquisition evidence:
`/home/codex/work/ssd2/task-evidence/egui037-migration-20261022/font-acquisition/noto-sans-hebrew-static/`
(`archive-provenance.json`, the release zip, `full.ttf`, `hinted.ttf`, `unhinted.ttf` and
`license.txt`). The existing variable fixture at
`/home/codex/work/CraftApps/egui037-migration-20261022/fixtures/noto-sans-hebrew/` is
retained unchanged and is not a regression input for the new static face.
- DeckCraft LXGW opt-in evidence:
`/home/codex/work/ssd2/task-evidence/egui037-migration-20261022/font-acquisition/lxgw-wenkai-lite/`
(`LXGWWenKaiLite-Regular.ttf`, `OFL.txt`, the public v1.530 release ZIP and
`metadata-inspection.json`). The reviewed TTF and OFL are copied unmodified into
`fonts/lxgw-wenkai-lite/` and listed only in the opt-in Simplified Chinese manifest.
## Managed validation commands (completed as recorded above)
```sh
python3 /home/codex/work/maintenance/task.py run \
--job-id <unique>-font-fmt-01 --kind build --task <task> \
--source /home/codex/work/CraftApps/egui037-migration-20261022/craft-fonts-implementation \
--evidence /home/codex/work/ssd2/task-evidence/<task> \
-- cargo fmt --all --check
python3 /home/codex/work/maintenance/task.py run \
--job-id <unique>-font-clippy-01 --kind build --task <task> \
--source /home/codex/work/CraftApps/egui037-migration-20261022/craft-fonts-implementation \
--evidence /home/codex/work/ssd2/task-evidence/<task> \
-- cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
python3 /home/codex/work/maintenance/task.py run \
--job-id <unique>-font-test-01 --kind build --task <task> \
--source /home/codex/work/CraftApps/egui037-migration-20261022/craft-fonts-implementation \
--evidence /home/codex/work/ssd2/task-evidence/<task> \
-- cargo test --workspace --locked
python3 /home/codex/work/maintenance/task.py run \
--job-id <unique>-font-embed-01 --kind build --task <task> \
--source /home/codex/work/CraftApps/egui037-migration-20261022/craft-fonts-implementation \
--evidence /home/codex/work/ssd2/task-evidence/<task> \
-- cargo test -p craft-fonts --features embed --locked
```
## Completion gate
Locally satisfied by this implementation:
1. the exact downloaded files match the recomputed SHA-256 and immutable upstream pins;
2. cmap and outline inspection records which face covers `✕`, `⌃`, the arrows and the Korean
strings, and the real Hangul/Jamo counts;
3. `OFL.txt`, manifest, `ATTRIBUTION.md`, README, `docs/integration.md` and the regression
tests are updated for exactly the files added;
4. existing fonts and assets are byte-identical, and the variable Hebrew fixture is untouched;
5. the Hebrew add is exactly one static 400-weight face (70,538 bytes with its licence), with
no `fvar`/`avar`/`STAT` and no nine-instance registration plan.
Central prerequisite complete:
6. managed fmt, clippy, workspace and embed checks passed in 05/06; fresh implementation
review verified the tested source and asset bytes.
Downstream acceptance remains open in the separately owned app resources:
7. DeckCraft's native required/no-font Hebrew acceptance and mixed-direction editing are run
with system discovery isolated;
8. GridCraft's no-font and extended-mode behavior, fallback order, barred-family filter, native
Hebrew growth measurements and web allowlist/measurements are validated separately in the
GridCraft checkout;
9. The fresh implementation review's consumer-acceptance gaps are closed. The Hebrew
acquisition records are complete; the asset review verified their hashes and provenance.
Keep all changes local. No push, tag or PR publication without fresh explicit user approval of
the concrete diff and validation.
+100 -18
View File
@@ -1,8 +1,10 @@
# Using craft-fonts in a Crafting App
craft-fonts is a **fully optional** source of fonts. An app must build, test and run without it;
with it, the app embeds the fonts listed in `fonts/manifest.txt` and uses them (today: Japanese
UI and document fallback).
with it, the app embeds the fonts listed in `fonts/manifest.txt` and uses them (today: Japanese,
Korean, Hebrew and UI-symbol fallback). A native consumer may additionally opt in to
`fonts/simplified-chinese-manifest.txt` for the LXGW WenKai Lite Simplified Chinese fallback; that
manifest is never part of the default set or a wasm build.
## Why not a Cargo dependency
@@ -42,14 +44,17 @@ fn main() {
println!("cargo::rerun-if-env-changed=CRAFT_FONTS_DIR");
println!("cargo::rerun-if-env-changed=CRAFT_FONTS_REQUIRED");
let mut src = String::from("pub static CRAFT_FONTS: &[CraftFont] = &[\n");
let required = std::env::var_os("CRAFT_FONTS_REQUIRED").is_some();
if let Some(dir) = std::env::var_os("CRAFT_FONTS_DIR").map(PathBuf::from) {
match craft_fonts(&dir) {
Ok(entries) => src.push_str(&entries),
Err(e) if std::env::var_os("CRAFT_FONTS_REQUIRED").is_some() => {
Err(e) if required => {
println!("cargo::error=CRAFT_FONTS_DIR={}: {e}", dir.display());
}
Err(e) => println!("cargo::warning=building without craft-fonts: CRAFT_FONTS_DIR={}: {e}", dir.display()),
}
} else if required {
println!("cargo::error=CRAFT_FONTS_REQUIRED is set but CRAFT_FONTS_DIR is missing");
}
src.push_str("];\n");
let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap_or_default()).join("craft_fonts.rs");
@@ -63,10 +68,20 @@ fn craft_fonts(dir: &std::path::Path) -> Result<String, String> {
let manifest = dir.join("fonts/manifest.txt");
println!("cargo::rerun-if-changed={}", manifest.display());
let text = std::fs::read_to_string(&manifest).map_err(|e| format!("{}: {e}", manifest.display()))?;
// Web builds have a size budget (e.g. Cloudflare's 25 MiB per file): embed only the UI font there.
const WEB_FONTS: &[(&str, &str)] = &[("BIZ UDPGothic", "Regular")];
// Web builds use an explicit allowlist. Never embed the whole manifest or Noto CJK.
// GridCraft's selected faces are BIZ UDPGothic Regular/Bold, NanumGothic Regular,
// Noto Sans Symbols Regular and Noto Sans Symbols 2 Regular (verified cmap coverage requires
// both symbol faces). Measure raw font bytes, raw wasm and compressed wasm before/after.
const WEB_FONTS: &[(&str, &str)] = &[
("BIZ UDPGothic", "Regular"),
("BIZ UDPGothic", "Bold"),
("NanumGothic", "Regular"),
("Noto Sans Symbols", "Regular"),
("Noto Sans Symbols 2", "Regular"),
];
let wasm = std::env::var("CARGO_CFG_TARGET_ARCH").is_ok_and(|a| a == "wasm32");
let mut out = String::new();
let mut included = Vec::new();
for line in text.lines().map(str::trim).filter(|l| !l.is_empty() && !l.starts_with('#')) {
let f: Vec<&str> = line.split(" | ").map(str::trim).collect();
let [family, style, file, scripts, ..] = f.as_slice() else {
@@ -76,6 +91,7 @@ fn craft_fonts(dir: &std::path::Path) -> Result<String, String> {
continue;
}
let path = dir.join(file).canonicalize().map_err(|e| format!("{file}: {e}"))?;
included.push((*family, *style));
println!("cargo::rerun-if-changed={}", path.display());
let scripts: Vec<String> = scripts.split(',').map(|s| format!("{:?}", s.trim())).collect();
let _ = writeln!(
@@ -85,6 +101,13 @@ fn craft_fonts(dir: &std::path::Path) -> Result<String, String> {
path.display().to_string(),
);
}
if std::env::var_os("CRAFT_FONTS_REQUIRED").is_some() {
for &(family, style) in WEB_FONTS {
if !included.contains(&(family, style)) {
return Err(format!("required fallback face is missing: {family} {style}"));
}
}
}
Ok(out)
}
```
@@ -107,20 +130,72 @@ include!(concat!(env!("OUT_DIR"), "/craft_fonts.rs"));
`CRAFT_FONTS` is always defined, so there is no `cfg` to thread through: an empty slice means the
app was built without craft-fonts. Code that uses it must work when it is empty.
## Optional native-only manifest
`fonts/simplified-chinese-manifest.txt` uses the same eight-field format and parser, but it is a
separate opt-in input. Read it only for non-wasm builds, after the main manifest has been parsed:
- **Native builds only.** Never read it from a wasm branch and never add its row to `WEB_FONTS`.
Native builds that do not opt in embed no added bytes.
- **Missing file is compatible.** An older craft-fonts checkout without the optional manifest is
not an error; continue with the main manifest's fonts.
- **Malformed or unreadable when present.** Under `CRAFT_FONTS_REQUIRED=1`, fail the build. In
optional mode, emit a `cargo::warning` and keep every successfully parsed font from the main
manifest. An optional-manifest error must never discard the main fonts.
- **Required mode still needs a directory.** `CRAFT_FONTS_REQUIRED=1` with no `CRAFT_FONTS_DIR`
is a build error, as in the recipe above.
- **Not in `FONTS`/embed.** `craft-fonts`' static table and `embed` feature cover only
`fonts/manifest.txt`; the optional row does not appear there. Apps supply their own static
initializer from the parsed optional file.
- **Native fallback order.** Append `LXGW WenKai Lite` / `Regular` after the existing native
families, so it fills characters they lack without taking precedence over BIZ for characters
BIZ already covers. It is a per-character fallback, not a promise of uniform Chinese
typography or of system-font-first precedence. It does not make static localized UI labels
render in Chinese unless the consumer installs the face for those labels too.
The central tests in `crates/craft-fonts-tests/tests/fonts.rs` parse the optional file with
`craft_fonts::parse_manifest` and verify the exact row, SHA-256, adjacent licence and RFN text,
one static weight-400 face without `fvar`/`avar`/`STAT`, outlines for `简体中文演示文稿` and
`简`, the 24 MiB ceiling, and that the face is absent from `FONTS`/`fonts/manifest.txt`.
## Using the fonts
- **UI (egui):** add each `Jpan` font as a fallback at the end of every font family, after the
app's own fonts and before system-font discovery, so Japanese text renders the same everywhere
the app was built with craft-fonts.
- **Document text engines:** register them as fallback faces for Japanese, after the document's
requested font and the app's bundled fonts.
- **Web (wasm32):** the recipe embeds only `BIZ UDPGothic Regular` (~4.5 MB); all four fonts are
~24 MB, over typical per-file hosting limits. If the app's web build has a size check, measure
with `CRAFT_FONTS_DIR` set and shrink `WEB_FONTS` (even to empty) if it fails.
- **UI (egui):** append permitted craft faces at the end of each interface font family, after
bundled fonts, system fonts and existing script fallbacks. Register Japanese (`Jpan`) faces,
`NanumGothic` / `Regular` for `Hang,Kore`, `Noto Sans Hebrew` / `Regular` for `Hebr`, and both
`Noto Sans Symbols` / `Regular` and `Noto Sans Symbols 2` / `Regular` for `Zsym`. The Hebrew
file is one static face: add it once per family set that includes it (for example the
proportional family and, where the consumer has one, the `CELL`/monospace family). Do not
expand it into the nine named instances of the old variable fixture. A native consumer that
opts in to the Simplified Chinese manifest adds `LXGW WenKai Lite` / `Regular` for `Hani,Hans`
after those existing families. Match the exact family/style spelling; in particular,
`NanumGothic` has no space. Verify the installed
production set with `CRAFT_FONTS_REQUIRED=1` and strict glyph assertions for each advertised
script and symbol. Hebrew coverage here means each codepoint has a drawn outline. The
storytold egui fork uses HarfRust shaping and bidi layout, but font-only tests do not
qualify correctly positioned vocalized (pointed) Hebrew in an app. The initial glyph
acceptance text is unpointed, e.g. `שלום deck 42`; shaping needs separate app-level checks.
- **Document text engines:** register the same permitted script and symbol faces after the
document's requested font and the app's existing fallback faces. Preserve the app's asset
exclusions, and do not treat a script tag as proof that every character in that script exists.
- **Web (wasm32):** the recipe uses a fixed allowlist. Never embed the whole manifest: the
current faces together are roughly 43 MiB (45.5 MB), over typical per-file hosting limits. Measure the
raw bytes of each allowlisted font, plus raw and compressed wasm before and after, against the
consumer's actual web gate. Do not call the result "within budget" without a named number and
a real check. GridCraft's decision is documented in
[`egui-037-font-coverage-plan.md`](egui-037-font-coverage-plan.md): BIZ UDPGothic Regular/Bold,
NanumGothic Regular, Noto Sans Symbols Regular and Noto Sans Symbols 2 Regular (the verified
cmap shows Symbols 1 lacks `✕`/`◀` and Symbols 2 supplies them), and no Noto CJK. Hebrew is
deliberately **not** in that web allowlist: `Noto Sans Hebrew` is a native-only addition in
this round, and web Hebrew remains an unqualified gap. `LXGW WenKai Lite` is likewise
native-only and must stay out of every wasm allowlist.
- Prefer `BIZ UDPGothic` for UI text and `Shippori Mincho` / `BIZ UDMincho` for serif document
text.
- Tests that assert on these fonts' glyphs must skip (not fail) when `CRAFT_FONTS` is empty, and
CI should run them with `CRAFT_FONTS_DIR` set so they are exercised.
- Optional-input diagnostics may skip when `CRAFT_FONTS` is empty. Acceptance tests that
validate advertised scripts must run with `CRAFT_FONTS_DIR` set and `CRAFT_FONTS_REQUIRED=1`,
and must fail (not skip) when a required face or codepoint is missing. Never weaken strict
glyph checks, substitute tofu comparisons, or use `allow_missing_glyphs()` to make acceptance
pass.
## Releases and licences
@@ -147,6 +222,13 @@ craft-fonts' `ATTRIBUTION.md`.
## Adding a font
1. Put the file and its licence under `fonts/<family>/`, from a pinned upstream commit.
2. Add a manifest line (with its SHA-256) and an `ATTRIBUTION.md` row.
3. `cargo test --workspace`: `craft-fonts-tests` checks the checksum, licence, family name and,
for Japanese fonts, coverage and vertical-text support. Add assertions for any new script.
2. Add a manifest line (with its SHA-256) and an `ATTRIBUTION.md` row. A face that only some
native consumers should embed belongs in `fonts/simplified-chinese-manifest.txt`, not the
default `fonts/manifest.txt`.
3. `cargo test --workspace`: `craft-fonts-tests` checks the checksum, licence, family and style
names, and, for Japanese fonts, coverage and vertical-text support. Add exact ISO 15924
script assertions for any new face. Coverage tests may claim only glyphs the face really
contains: check outlines as well as cmap entries, do not claim a codepoint supplied by
another face (egui's emoji faces supply `📋`), and do not claim full-script coverage from a
sample string. Static faces also get metadata assertions (no `fvar`/`STAT`, weight 400),
so a variable font with a Thin default cannot pass as a one-face Regular.
Binary file not shown.
+94
View File
@@ -0,0 +1,94 @@
Copyright 2021-2026 LXGW (https://github.com/lxgw/LxgwWenKai-Lite), with Reserved Font Name '霞鹜', '霞鶩', '落霞孤鹜', '落霞孤鶩' and 'LXGW'. [ADDITIONAL PERMISSION] The Reserved Font Names '霞鹜', '霞鶩', '落霞孤鹜', '落霞孤鶩' and 'LXGW' may continue to be used in Modified Versions recompiled from the Original Version, without modifications to the font source code; or in Modified Versions subsetted or converted to other formats (e.g., WOFF/WOFF2) solely for web font delivery, provided such Modified Versions are not made available as installable desktop fonts (e.g., on mainstream platforms like Google Fonts, or third-party non-commercial platforms recognized by the author @lxgw; other web font platforms please contact the author @lxgw for confirmation).
Copyright 2020 The Klee Project Authors (https://github.com/fontworks-fonts/Klee)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+4
View File
@@ -10,3 +10,7 @@ Noto Sans CJK SC | Regular | fonts/noto-sans-cjk-sc/NotoSansCJKsc-Regular.otf |
Noto Sans Arabic | Regular | fonts/noto-sans-arabic/NotoSansArabic.ttf | Arab | OFL-1.1 | fonts/noto-sans-arabic/OFL.txt | 63111b5b2e074dd48cc67692e0a2726d86ee94c1c37fe8598257b7b4e87e869e | https://github.com/google/fonts/tree/7085eb89a950e85db5b166b7a58d414544b4140c/ofl/notosansarabic
Noto Sans Thai | Regular | fonts/noto-sans-thai/NotoSansThai.ttf | Thai | OFL-1.1 | fonts/noto-sans-thai/OFL.txt | 5a1c559bb539583c8a1fd99d1c5b9491e5e14478c9cd2bd0970d5c3096cc9ef8 | https://github.com/google/fonts/tree/8b0a1d0f5983c89bc2b93f1b5fb55f9e252744b5/ofl/notosansthai
Noto Sans Georgian | Regular | fonts/noto-sans-georgian/NotoSansGeorgian.ttf | Geor | OFL-1.1 | fonts/noto-sans-georgian/OFL.txt | dc591156f36842d38996c4a7a17fee9bb58e45da3e2cac7a31b7d33de700adb9 | https://github.com/google/fonts/tree/8b0a1d0f5983c89bc2b93f1b5fb55f9e252744b5/ofl/notosansgeorgian
NanumGothic | Regular | fonts/nanum-gothic/NanumGothic-Regular.ttf | Hang,Kore | OFL-1.1 | fonts/nanum-gothic/OFL.txt | 76f45ef4a6bcff344c837c95a7dcc26e017e38b5846d5ae0cdcb5b86be2e2d31 | https://github.com/google/fonts/tree/bd8f81ddb5c74d5c8897b36ad88b440266245103/ofl/nanumgothic
Noto Sans Symbols | Regular | fonts/noto-sans-symbols/NotoSansSymbols.ttf | Zsym | OFL-1.1 | fonts/noto-sans-symbols/OFL.txt | f7e7e04b4a24b6c78893d50cbfd2b2f6cae49617ab047bfef668d252adb128f7 | https://github.com/google/fonts/tree/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols
Noto Sans Symbols 2 | Regular | fonts/noto-sans-symbols-2/NotoSansSymbols2-Regular.ttf | Zsym | OFL-1.1 | fonts/noto-sans-symbols-2/OFL.txt | 7d5fb73b7ca67a6798101741f5d280a3d016a56a197afcd4199dbb57b4b82a21 | https://github.com/google/fonts/tree/47d54ca1ebb14fd864695f1d39dc734f39017c33/ofl/notosanssymbols2
Noto Sans Hebrew | Regular | fonts/noto-sans-hebrew/NotoSansHebrew-Regular.ttf | Hebr | OFL-1.1 | fonts/noto-sans-hebrew/OFL.txt | 671951828bd5c95db818e5bb12dcea2d0c0dda00311888522be061ee6835125e | https://github.com/notofonts/hebrew/releases/tag/NotoSansHebrew-v3.001
Binary file not shown.
+96
View File
@@ -0,0 +1,96 @@
Copyright (c) 2010, NHN Corporation (http://www.nhncorp.com),
with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver
NanumGothic, NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver
NanumBrush, NanumPen, Naver NanumPen.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2022 The Noto Project Authors (https://github.com/notofonts/hebrew)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2022 The Noto Project Authors (https://github.com/notofonts/symbols)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2022 The Noto Project Authors (https://github.com/notofonts/symbols)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+7
View File
@@ -0,0 +1,7 @@
# Opt-in native-only Simplified Chinese fonts. Same 8-field format as fonts/manifest.txt:
# family | style | file (relative to the repo root) | scripts (ISO 15924, comma-separated) | licence | licence file | sha256 | source
# Apps read this file separately from fonts/manifest.txt, and native builds only. It is not part
# of craft-fonts' default FONTS table or embed payload and must never be added to a wasm allowlist.
# DeckCraft classifies Han characters as Hani, so the row carries Hani,Hans.
# Pinned release: v1.530, commit b712c83d98acf977bacfb7fb010207802808bda9.
LXGW WenKai Lite | Regular | fonts/lxgw-wenkai-lite/LXGWWenKaiLite-Regular.ttf | Hani,Hans | OFL-1.1 | fonts/lxgw-wenkai-lite/OFL.txt | f799a538a3355877e597a0ae04050e897932c9ae7689eb9227c2953c5dd549ea | https://github.com/lxgw/LxgwWenKai-Lite/releases/tag/v1.530