Keeps font.ttf and per-language font_*.ttf out of Graphics so texture/atlas tooling doesn't need to skip non-image files. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
84 lines
4.5 KiB
Markdown
84 lines
4.5 KiB
Markdown
# `Fonts/font.ttf` — how it is made, and how the cabinet actually uses its fonts
|
||
|
||
## What the cabinet ships
|
||
|
||
The cabinet has **no vector font**. `Data\x64\font\` holds pre-rasterised BC7 glyph atlases,
|
||
one family per language and one atlas per size/weight bucket, each with an XML sidecar
|
||
(`codepoint → rect`; no bearings, no kerning):
|
||
|
||
| language | buckets on disk | distinct codepoints | face (as drawn in the atlas) |
|
||
|---|---|---|---|
|
||
| `jp` | `30`, `32_B`, `32_DB`, `32_EB`, `64` | 7 532 | 30/32: rounded gothic in three weights; 64: brush/勘亭流-style display face |
|
||
| `tw` | `30`, `32`, `64` | 17 678 | same split |
|
||
| `cn` | `30`, `32`, `64` | 7 608 | same split (the 64 face is a heavier brush) |
|
||
| `kr` | `30`, `32`, `64` | 8 407 | same split |
|
||
| `en` | `30`, `32`, `64` | 299 | same split |
|
||
|
||
(The JP 39.06 dump has the same files minus `cn_*`; the CN 05 dump adds `cn_*`. The
|
||
`jp/tw/kr/en` XMLs are byte-identical between the two.)
|
||
|
||
## How a text record picks its atlas
|
||
|
||
* **Language** comes from the cabinet language setting: wordlist rows are looked up in the
|
||
language's column and rendered from that language's atlas family. Rows flagged
|
||
`fontType 0` (untranslated) always render from `jp`.
|
||
* **Size/weight bucket** is authored per text record in the `.nulm` (`DefineFont` entry
|
||
symbol `"0".."4"` → `64`, `30`, `32_EB`, `32_B`, `32_DB`). It is not language dependent.
|
||
For `en/tw/kr/cn` the three weighted `32_*` buckets are registered onto the plain `32`
|
||
atlas, so a record that asks for `32_EB` on a `cn` cabinet is drawn from `cn_32`.
|
||
* The only fallback is `jp_64` when the language itself is unknown; a codepoint missing from
|
||
the chosen atlas is simply not drawn. Of the 692 text records in the shipped movies, 449
|
||
use the 30/32 (rounded gothic) buckets and 168 use the 64 (brush) bucket.
|
||
|
||
YataiDON draws every string through a single `.ttf`, so one face has to serve all of it.
|
||
This skin's `font.ttf` is built from the **`_64` (brush) atlases**, which is the face used for
|
||
headings, song titles and most of the large text.
|
||
|
||
## Building `font.ttf`
|
||
|
||
`tools/font/build_font.py` merges the five `_64` atlases into one TrueType font:
|
||
|
||
1. Decode each `<lang>_64.nutexb` to a DDS (BC7, `nutexb_dump -o`) next to its XML.
|
||
2. For each language in priority order `jp, tw, cn, kr, en`, every codepoint that does not
|
||
yet have an inked glyph takes its bitmap from that atlas (blank placeholder cells are
|
||
skipped so a later atlas can supply the character).
|
||
3. Each bitmap is upscaled 4× (bicubic), thresholded at alpha ≥ 128, traced with potrace
|
||
and converted to quadratic outlines (`cu2qu`).
|
||
4. Metrics: `unitsPerEm 1024`, `scale = 1024 / fontPoint` of the atlas (16 units per pixel
|
||
for `jp_64`), `advance = cell width × scale`, baseline = the atlas's median bottom ink row
|
||
of its Han/Hangul glyphs, so the five families sit on one baseline with equal Han height.
|
||
`ascender 880 / descender −144`, as the earlier build.
|
||
|
||
```
|
||
pip install potracer fonttools pillow numpy
|
||
python build_font.py <dir with *_64.dds + *_64.xml> font.ttf --order jp,tw,cn,kr,en
|
||
```
|
||
|
||
Result (version 3.000): 22 382 glyphs — 7 499 from `jp`, 10 107 from `tw`, 1 956 from `cn`,
|
||
2 737 from `kr`, 82 from `en`. Coverage of each atlas's repertoire: jp 7521/7532,
|
||
tw 17651/17678, cn 7589/7608, kr 8070/8407, en 268/295 (the remainder are blank cells in
|
||
the atlases themselves). Version 2.000 had no `cn` or `kr` glyphs, which is why Simplified
|
||
Chinese and Korean text showed `?`.
|
||
|
||
## Per-language fonts (`Fonts/font_<family>.ttf`)
|
||
|
||
The engine loads `Fonts/font_<language code>.ttf` if present, else `Fonts/font_<cabinet family>.ttf`
|
||
(zh → cn, ko → kr, ja → jp), else `font.ttf`. The settings screen reloads the skin, so switching
|
||
language switches the font. This mirrors the cabinet, which draws everything from the language's own
|
||
atlas family and only lacks what that family does not have.
|
||
|
||
| file | atlas priority | used for |
|
||
|---|---|---|
|
||
| `font.ttf` | `jp, tw, cn, kr, en` | Japanese, and any language without its own file (unchanged 3.000) |
|
||
| `font_cn.ttf` | `cn, tw, jp, en, kr` | Simplified Chinese (`zh`) |
|
||
| `font_tw.ttf` | `tw, jp, cn, en, kr` | Traditional Chinese (`zh_tw` / `tw`) |
|
||
| `font_en.ttf` | `en, jp, tw, cn, kr` | English |
|
||
| `font_kr.ttf` | `kr, jp, en, tw, cn` | Korean (`ko`) |
|
||
|
||
```
|
||
python build_font.py <dir> font_cn.ttf --order cn,tw,jp,en,kr
|
||
python build_font.py <dir> font_tw.ttf --order tw,jp,cn,en,kr
|
||
python build_font.py <dir> font_en.ttf --order en,jp,tw,cn,kr
|
||
python build_font.py <dir> font_kr.ttf --order kr,jp,en,tw,cn
|
||
```
|