Skip to content

Repository files navigation

GlyphRoute

Trace every character in your real copy through the font fallback chain—before users see □.

中文 · Live report · Rule reference · Troubleshooting

CI Release Python License

GlyphRoute maps real localization copy through ordered font faces, then emits terminal, HTML, JSON, SARIF, and subset artifacts.

Your font file can open correctly and still ship:

  • a character absent from every configured font;
  • a mapped glyph with no drawable outline;
  • an unexpected fallback that makes one language look visually inconsistent;
  • a huge “fallback” font that is actually serving most of the product.

GlyphRoute scans the strings your product really uses—not an abstract Unicode block. It routes every unique scalar through the same ordered font chain you configure, preserves source positions without copying full messages into reports, and turns the result into a deterministic CI gate.

What is real in v0.1.0

  • Format-aware extraction from JSON/ARB, TOML, CSV/TSV, PO/POT, Java .properties, Apple .strings, YAML, Markdown, and plain UTF-8 text.
  • NFC/NFD/NFKC/NFKD normalization and explicit handling of variation selectors, joiners, bidi controls, and other default-ignorable scalars.
  • Ordered TTF, OTF, WOFF, WOFF2, TTC, and OTC face inspection through real OpenType cmap tables.
  • Blank-outline detection for mapped visible characters, not just “is this codepoint in cmap?”
  • Terminal summary plus self-contained HTML, stable JSON, and SARIF 2.1.0 reports.
  • CI policies for missing glyphs, blank glyphs, and occurrence-weighted fallback budgets.
  • Per-route TTF/WOFF/WOFF2 font subsetting with unicode-range CSS and SHA-256 manifests.
  • A composite GitHub Action, generated fixtures, 48+ tests, release packaging, and a deliberately broken acceptance sample.

No cloud service, account, API key, AI model, telemetry, or source-text upload is used.

Quick start

Python 3.11+ is required.

git clone https://github.com/KanadeK/glyphroute.git
cd glyphroute
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
python -m pip install -e .
glyphroute init

Edit glyphroute.toml, keeping fonts in the same order as your application:

version = 1

[scan]
sources = ["locales/**/*.json", "content/**/*.md"]
normalization = "NFC"
include_keys = false

[policy]
fail_on = "missing-or-blank"
max_fallback_percent = 35

[[fonts]]
path = "fonts/Inter-Regular.ttf"
label = "Primary"

[[fonts]]
path = "fonts/NotoSansCJK-Regular.otf"
label = "CJK fallback"

Run one scan and write all report formats:

glyphroute scan \
  --config glyphroute.toml \
  --json artifacts/glyphroute.json \
  --html artifacts/glyphroute.html \
  --sarif artifacts/glyphroute.sarif

Exit code 0 means policy passed, 1 means real coverage findings exceeded policy, and 2 means configuration/input/tooling failed.

See both acceptance paths

The repository includes tiny original fonts generated from source. They are fixtures, not fonts for application use.

python scripts/generate_demo_assets.py

# Must pass: Latin → CJK → symbol fallback.
glyphroute scan --config examples/glyphroute.toml

# Must exit 1: 🧭 is missing and ☠ maps to an empty outline.
glyphroute scan --config examples/glyphroute-risk.toml

Expected risky output:

GlyphRoute FAIL
3 files · 3 fonts · 41 unique characters · 63 occurrences
primary 20 · fallback 19 (31.75%) · missing 1 · blank 1

Blocking findings
  BLANK   ☠ U+2620 SKULL AND CROSSBONES
  MISSING 🧭 U+1F9ED COMPASS

Reports designed for different jobs

Output Use
Terminal Fast local feedback and CI logs
HTML Searchable, dependency-free review artifact with font and source evidence
JSON Stable schema for build systems, dashboards, or custom checks
SARIF Inline GitHub code-scanning annotations at localization source positions
Subsets One font file per selected route, CSS unicode-range, hashes, and license reminder

Reports include codepoints, counts, selected font, and up to five relative source positions. They do not include complete source strings, absolute project roots, or font bytes.

GitHub Actions

Use the repository action:

name: Glyph coverage
on: [push, pull_request]

jobs:
  glyphroute:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v7
      - uses: KanadeK/glyphroute@v0.1.0
        with:
          config: glyphroute.toml
          sarif: glyphroute.sarif
      - uses: github/codeql-action/upload-sarif@v4
        if: always()
        with:
          sarif_file: glyphroute.sarif

The action fails on the same policy as the CLI. It never uploads your fonts or localization files to GlyphRoute; standard GitHub-hosted runner behavior still applies.

Build minimal font payloads

After a passing scan:

python -m pip install -e ".[woff]"
glyphroute subset \
  --config glyphroute.toml \
  --flavor woff2 \
  --output-dir dist/fonts \
  --css dist/fonts/fonts.css

Each output contains only the characters routed to that face. glyphroute-subsets.json records source font identity, output hash, byte count, and Unicode range.

A subset remains governed by the source font license. GlyphRoute does not grant redistribution rights.

Why another font tool?

The adjacent tools are useful, but solve different layers:

Tool Primary job Gap GlyphRoute targets
Google Fonts FontReport Inventory one font's coverage and features Does not evaluate real product strings through an ordered fallback chain
Font Glyph Checker Browse glyphs contained in one uploaded font No source locations, chain routing, policy, SARIF, or subsetting workflow
glyphhanger Crawl web pages and subset web fonts Web-page oriented; the original repository is archived and does not provide this localization-file CI model
GlyphRoute Route actual copy across multiple font faces Coverage evidence, blank glyphs, fallback budgets, CI annotations, and routed subsets

GlyphRoute builds subsetting on the maintained fontTools subset engine rather than reimplementing OpenType layout closure.

Supported scope

See input formats and architecture for exact behavior.

Important v0.1.0 boundaries:

  • Coverage is scalar/cmap based. It does not claim that shaping, ligatures, mark positioning, or platform-specific fallback will render correctly.
  • A blank-outline warning means a visible scalar maps to a glyph with no vector bounds. Color bitmap/SVG edge cases may require manual review.
  • YAML extraction is intentionally conservative unless the optional yaml dependency is installed in a future release.
  • GlyphRoute reads local files. Treat untrusted fonts as untrusted input and scan them in an isolated environment.

Development and full acceptance

python -m pip install -e ".[dev,woff]"
python scripts/check.py

scripts/check.py runs formatting, lint, strict typing, fixture regeneration, 48+ tests with branch coverage, clean and known-bad CLI acceptance, subset generation, wheel/sdist creation, and package metadata validation.

If any step fails, follow the command-specific repair table in docs/troubleshooting.md; rerun the failed command first, then the full check. Do not bypass a red risky-fixture assertion—the expected exit code is part of the product contract.

Community

License

GlyphRoute code and generated demo fonts are available under the MIT License. User-supplied fonts retain their own licenses.

About

Trace real localization copy through an ordered font fallback chain before users see □.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages