Skip to content

chunlaw/wing-font-generator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

166 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Wing Font

Bake pronunciation annotations directly into Chinese fonts — Cantonese Jyutping, Mandarin Pinyin, Taiwanese Tâi-lô, and more — so they render in plain text anywhere a custom font goes.

License: MIT Deploy to GitHub Pages Demo Build Live site GitHub Sponsors

Wing Font generates OpenType fonts whose glyphs carry their own romanization above them. Type 行 and hang4 appears above it; type 銀行 and the same character renders with hong4 because the word-context disambiguation rides on OpenType ccmp. No HTML <ruby> markup, no separate text layers, no special apps — Word, Pages, Canva, Adobe Creative Cloud, Chrome / Firefox / Safari all just work.

The pipeline is a small Python program that adds GSUB rules to a base CJK font. The same code runs in the browser via Pyodide, so non-developers can compose their own variants without installing anything.

Documentation

The live site hosts the showcase, the in-browser generator, and per-platform install instructions for non-developers.

For development docs:

  • python/README.md — CLI reference, GSUB pipeline architecture, mapping data sources, local test rig.
  • web/README.md — React + Vite app, Pyodide worker, sync-python script, deployment notes.

Pre-built fonts

CI builds the font set on every push and publishes it across two surfaces — WOFF2 lives on the Pages site (used by the in-page previews), TTF lives in rolling GitHub Releases (heavier file, off-loaded so the Pages bandwidth budget covers visitor traffic). Browse and download from the showcase page, or use a direct URL like:

https://wing-font.chunlaw.io/fonts/NotoSansHK-Noto-lshk.woff2
https://github.com/chunlaw/wing-font-generator/releases/latest/download/NotoSansTC-Huninn-tailo.ttf

The releases/latest/download/ redirect always tracks the most recent build-<sha> release; CI keeps the 3 most recent releases for rollback headroom and prunes older ones, so the TTF URL is stable but the bytes are not guaranteed to be byte-identical across deploys (this is a generator, not a versioned font archive).

Coverage spans:

  • Cantonese (廣東話) — LSHK Jyutping, Yale, Cangjie, Lau, Guangdong, Chishima; cross-script transliteration into Katakana, Hangul, Thai, Devanagari, and Urdu Nastaliq (Arabic script).
  • Taiwanese / Southern Min (台語 / 河洛話) — Tâi-lô, POJ, Bopomofo (TPS), Taiwanese Kana.
  • Teochew / Min Nan (潮州話) — Peng'im (GDPI), Pe̍h-ūe-jī.
  • Mandarin (普通話 / 國語) — Hanyu Pinyin with phrase-level disambiguation across the full CJK Unified Ideograph range.
  • Japanese (日本語) — hiragana furigana from KanjiDic2 (on'yomi folded to hiragana so the whole annotation layer is one kana set), plus 熟語 word-context rules aligned from JMdict so contextual readings win (行 reads こう in 行 alone, ぎょう in 行政; 日本人 → に ほん じん). Kana and Japanese symbols carry a blank scale-only annotation so mixed kanji+kana text renders at one body size. Note that a font cannot disambiguate a kanji's reading from the following kana (kanji and kana are shaped as separate script runs in every text engine), so okurigana-context readings on polysemous kanji fall back to their most frequent reading — see python/mappings/japanese/NOTES.md for the limitation and its rationale.
  • Word-unit base scripts — experimental pipeline — beyond CJK, the pipeline can use a non-Han script (Arabic, Thai, Devanagari, …) as the base: each mapped word is composed into a single annotated glyph with cursive shaping / mark stacking / conjunct formation intact, reached via GSUB ligation (boundary-guarded for spaced scripts, longest-match for unspaced Thai) with GDEF ligature carets for in-word cursor movement and per-script variant override (trailing tatweel for Arabic, digit suffix for Thai/Hindi). Per-script behaviour is a data registry (csv_parser.WORD_SCRIPTS) so adding a script is a small code change. No pre-built font using this path ships yet — the Thai / Devanagari / Urdu builds above are CJK base fonts with those scripts as annotations, not the other way round. Power users who want a true non-CJK-base build can author a mapping (see python/mappings/thai/thai-paiboon.csv for the format) and invoke wing-font.py directly. See python/README.md for the walkthrough.

Pairings include ChironSung HK, ChironHei HK, Noto Sans HK / TC / SC, Source Han Serif, Xiaolai, and others. See Acknowledgements for upstream data + font sources.

Quick start

Web (no install needed)

Visit wing-font.chunlaw.io/generate, pick a base font + annotation font + mapping CSV (or upload your own), click Generate, and download the resulting .woff / .ttf. First generation takes ~10-15 s for Pyodide to boot; subsequent runs are warm.

CLI

cd python
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

python wing-font.py \
  -i input_fonts/ChironSungHK-R.ttf \
  -a input_fonts/NotoSerif-Regular.ttf \
  -m mappings/cantonese/canto-lshk.csv \
  -o ChironSungHK-Noto-lshk \
  -opt -as 0.13

Input fonts aren't tracked in this repo — python/init_fonts.py fetches them from the wing-font-hub CDN. See python/README.md for the full CLI reference and pipeline walkthrough.

Pairing tall annotations (Urdu Nastaliq, Thai with vowel + tone, Hangul jamo) with a low-ascent base font (e.g. Xiaolai at 880u): pass --out-ascent so apps that strictly clip at winAscent (Word, Pages, Keynote, Canva) don't truncate the top of the tallest glyphs. Suggested values: 1200 for Thai / Katakana / Korean on Xiaolai, 1300 for Urdu. The same lever is exposed in the in-browser pipeline at /generate → Step 3 → Advanced → "Output ascent" — handy for visually dialling in a value before committing to a CLI invocation.

Web dev

cd web
yarn install
yarn dev

yarn dev first runs scripts/sync-python.mjs to copy the Python sources from ../python/ into web/public/wingfont/, then starts Vite.

Repository layout

wing-font-generator/
├── python/        # Source of truth — font-generation pipeline (CLI + library)
├── web/           # React + Vite app that runs the pipeline in-browser via Pyodide
└── .github/workflows/
    ├── deploy-pages.yml   # Matrix builds 59 fonts + web app, publishes to gh-pages
    └── build-demo.yml     # Validation-only web build on pushes

python/ is the single source of truth. The Python files under web/public/wingfont/ are generated by web/scripts/sync-python.mjs on every web build — don't edit them directly.

A sibling repo, chunlaw/wing-font-hub, hosts the source .ttf files used as input by the pipeline (Noto Sans HK, ChironSung HK, Huninn, Source Han Serif, etc.). Keeping them out of this repo keeps clones lightweight and avoids LFS quota concerns.

Examples

  • Showcase — every pre-built font with a rotating sample of dialect-appropriate lyrics, plus direct .ttf / .woff2 download buttons. Shareable URLs via ?fonts= query string.
  • Specimen pages — single-font large-size preview with a custom-text field.
  • Generate — the 5-step in-browser pipeline (fonts → mappings → parameters → generate → download + CSS snippet).

Contributing

Wing Font is never finished. Three areas where help has the highest impact:

  • Code — bug reports, fixes, and improvements via GitHub issues and pull requests.
  • Design — annotation placement, font choices, proportions. Conversations happen in the Telegram group.
  • Data — additional dialect mappings, or improvements to default readings for polyphonic characters. Telegram group is the right place to start.

The About page on the live site explains each contribution area in more detail.

Changelog

Notable changes ship via GitHub Releases and the commit history on main.

License

This project is licensed under the terms of the MIT license — see LICENSE.

Input fonts and mapping data carry their own licenses (the fonts are mostly SIL OFL 1.1). See LICENSES.md and the Acknowledgements page for per-font and per-dataset attribution.

Acknowledgements

Wing Font stands on the shoulders of excellent open-source data and fonts. Full credits live at https://wing-font.chunlaw.io/credits; the headline contributions:

Fonts

Romanization data

Contact

Instagram · Telegram · Sponsor on GitHub

Sponsor this project

  •  

Packages

 
 
 

Contributors