Files
YataiDONNijiiro/tools/font/FONT.md
YonokidandClaude Sonnet 5 4c770bf913 Move fonts to dedicated Fonts folder
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>
2026-09-23 12:15:58 -04:00

84 lines
4.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `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
```