Files
client/build_iosevka_fonts.py
T

112 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""
Build web-ready Iosevka fonts for the client.
The system Iosevka TTFs are ~10MB each (43k+ glyphs, full Unicode coverage).
For web delivery we subset them to Latin + common punctuation/symbols and
convert to WOFF2, which is far smaller and supported by all modern browsers.
Usage:
python3 build_iosevka_fonts.py
Outputs (into www/css/):
Iosevka-Regular.woff2
Iosevka-Bold.woff2
Iosevka-Italic.woff2
Iosevka-BoldItalic.woff2
"""
import os
import sys
from fontTools import subset
from fontTools.ttLib import TTFont
# Source TTF directory (system-installed Iosevka).
SRC_DIR = "/usr/share/fonts/truetype/iosevka"
# Destination directory for the generated web fonts.
OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "www", "css")
# Variants we ship: (source filename, output filename)
VARIANTS = [
("Iosevka-Regular.ttf", "Iosevka-Regular.woff2"),
("Iosevka-Bold.ttf", "Iosevka-Bold.woff2"),
("Iosevka-Italic.ttf", "Iosevka-Italic.woff2"),
("Iosevka-BoldItalic.ttf", "Iosevka-BoldItalic.woff2"),
]
# Unicode ranges to keep. Latin + Latin-1 Supplement + Latin Extended-A,
# Greek, Cyrillic, general punctuation, currency, arrows, math operators,
# box drawing, geometric shapes, misc symbols, dingbats, and the
# replacement char. This keeps the web font around ~300 KB per variant
# while covering everything the UI realistically renders.
UNICODE_RANGES = [
"U+0000-00FF", # Basic Latin + Latin-1 Supplement
"U+0100-017F", # Latin Extended-A
"U+0370-03FF", # Greek and Coptic
"U+0400-04FF", # Cyrillic
"U+2000-206F", # General Punctuation
"U+20A0-20CF", # Currency Symbols
"U+2190-21FF", # Arrows
"U+2200-22FF", # Mathematical Operators
"U+2500-257F", # Box Drawing
"U+25A0-25FF", # Geometric Shapes
"U+2600-26FF", # Miscellaneous Symbols
"U+2700-27BF", # Dingbats
"U+FFFD", # Replacement character
]
def build_variant(src_name: str, out_name: str) -> None:
src_path = os.path.join(SRC_DIR, src_name)
out_path = os.path.join(OUT_DIR, out_name)
if not os.path.exists(src_path):
print(f" ! Missing source font: {src_path}", file=sys.stderr)
return
print(f" - {src_name} -> {out_name}")
font = TTFont(src_path)
options = subset.Options()
options.flavor = "woff2"
options.desubroutinize = True
options.layout_features = ["*"] # keep all OpenType features (ligatures, etc.)
options.name_IDs = ["*"]
options.name_legacy = True
options.notdef_outline = True
options.recalc_bounds = True
options.drop_tables = ["DSIG"]
subsetter = subset.Subsetter(options=options)
subsetter.populate(unicodes=subset.parse_unicodes(",".join(UNICODE_RANGES)))
subsetter.subset(font)
font.flavor = "woff2"
font.save(out_path)
font.close()
size_kb = os.path.getsize(out_path) / 1024
print(f" wrote {out_path} ({size_kb:.1f} KB)")
def main() -> int:
if not os.path.isdir(SRC_DIR):
print(f"Source font directory not found: {SRC_DIR}", file=sys.stderr)
return 1
os.makedirs(OUT_DIR, exist_ok=True)
print(f"Building Iosevka web fonts into {OUT_DIR}")
for src_name, out_name in VARIANTS:
build_variant(src_name, out_name)
print("Done.")
return 0
if __name__ == "__main__":
raise SystemExit(main())