Files
tactility_apps/Apps/EpubReader/gen_fonts.py
Shadowtrance dbf850c434 Fixes and new apps (#30)
- M5 Unit Modules library + M5 Unit Test app
- Minor fixes for TodoList, TwoEleven, Snake, Brainfuck and Breakout
- Fixed SerialConsole to use the uart controller as it was broken in one of the many updates
- Fixed keyboard input in TwoEleven, Breakout, Magic8Ball and Snake
- Bluetooth Media Keys app, supports pressing physical keys to trigger the corresponding buttonmatrix button
- Epub Reader app
2026-06-07 15:56:32 +02:00

130 lines
5.3 KiB
Python
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.
"""
gen_fonts.py - regenerate all LVGL binary font files for EpubReader.
Prerequisites:
node / npm must be on PATH
npm install -g lv_font_conv
Usage (from any directory):
python Apps/EpubReader/gen_fonts.py
Output: .bin files written to Apps/EpubReader/assets/
Output font filenames are font-family-agnostic: font_<size>pt_<variant>.bin
The app loader expects exactly these names - swap in any 4-weight typeface by
editing the FONTS dict and placing the .ttf files in Apps/EpubReader/fonts/.
Default: Noto Serif (4 weights) https://fonts.google.com/noto/specimen/Noto+Serif
NotoSerif-Medium.ttf → regular
NotoSerif-MediumItalic.ttf → italic
NotoSerif-Bold.ttf → bold
NotoSerif-BoldItalic.ttf → bold italic
"""
import shutil
import subprocess
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
# ── Latin content fonts (4 weights) ───────────────────────────────────────────
FONTS = {
"regular": SCRIPT_DIR / "fonts" / "NotoSerif-Medium.ttf",
"italic": SCRIPT_DIR / "fonts" / "NotoSerif-MediumItalic.ttf",
"bold": SCRIPT_DIR / "fonts" / "NotoSerif-Bold.ttf",
"bold_italic": SCRIPT_DIR / "fonts" / "NotoSerif-BoldItalic.ttf",
}
# ── Unicode ranges ─────────────────────────────────────────────────────────────
# Unicode coverage - adjust if your chosen font supports different blocks:
LATIN_TEXT_RANGES = [
"-r", "0x20-0x7E", # Basic Latin
"-r", "0xA0-0x024F", # Latin-1 Supplement + Latin Extended-A/B
"-r", "0x0370-0x03FF", # Greek (π, Δ, Σ, Ω etc.)
"-r", "0x1E9E", # ẞ (capital sharp-s, German)
"-r", "0x2010-0x2026", # General Punctuation (hyphens, dashes, ellipsis)
"-r", "0x2070-0x209F", # Superscripts and Subscripts (⁰⁴⁵⁶⁷⁸⁹ ₀₁₂₃₄₅₆₇₈₉)
"-r", "0x20A0-0x20CF", # Currency Symbols (€ ₹ ₽ ₩ ₪ ₴ etc.)
"-r", "0x2100-0x214F", # Letterlike Symbols (™ ℃ ℉ etc.)
"-r", "0x2150-0x218F", # Number Forms (⅓ ⅔ ⅛ ⅜ ⅝ ⅞ etc.)
]
# ── Sizes to generate ──────────────────────────────────────────────────────────
SIZES = [16, 20, 28, 36]
# ── Rendering options ──────────────────────────────────────────────────────────
BPP = 2 # 2-bit anti-aliasing; use 4 for smoother rendering (~2× larger)
FORMAT = "bin" # "lvgl" = C struct output; "bin" = binary blob
COMPRESS = False # True = enable RLE compression (smaller binary, slightly slower)
# ── Output directory ───────────────────────────────────────────────────────────
OUT_DIR = SCRIPT_DIR / "assets"
OUT_DIR.mkdir(parents=True, exist_ok=True)
# ── Locate lv_font_conv (handles Windows .cmd npm wrappers) ───────────────────
def _find_tool() -> str:
for name in ("lv_font_conv", "lv_font_conv.cmd"):
found = shutil.which(name)
if found:
return found
print("ERROR: lv_font_conv not found. Install with: npm install -g lv_font_conv", file=sys.stderr)
sys.exit(1)
LV_FONT_CONV = _find_tool()
USE_SHELL = sys.platform == "win32"
def _run(cmd: list) -> bool:
if USE_SHELL:
result = subprocess.run(subprocess.list2cmdline(cmd), capture_output=True, text=True, shell=True)
else:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f" ERROR: {result.stderr.strip()}", file=sys.stderr)
return False
return True
def generate_font(font_path: Path, ranges: list, size: int, symbol: str) -> bool:
"""Convert a single .ttf to an LVGL binary font file."""
if not font_path.exists():
print(f" SKIP {symbol} (font not found: {font_path.name})")
return False
out = OUT_DIR / f"{symbol}.bin"
cmd = [LV_FONT_CONV,
"--font", str(font_path)] + ranges + [
"--size", str(size),
"--bpp", str(BPP),
"--format", FORMAT,
"--lv-font-name", symbol,
"-o", str(out),
]
if not COMPRESS:
cmd.append("--no-compress")
print(f" {symbol:<36} size={size}{font_path.name}")
return _run(cmd)
def main():
print(f"lv_font_conv → {OUT_DIR}\n")
generated = 0
skipped = 0
# ── Content font variants (Latin text + symbols) ───────────────────────────
for size in SIZES:
print(f"── {size}pt {'' * 52}")
for font_key in FONTS.keys():
symbol = f"font_{size}pt_{font_key}"
ok = generate_font(FONTS[font_key], LATIN_TEXT_RANGES, size, symbol)
if ok: generated += 1
else: skipped += 1
print(f"\nDone. {generated} generated, {skipped} skipped.")
if __name__ == "__main__":
main()