112 lines
3.3 KiB
Python
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())
|