import html import os import re import shutil import subprocess import sys from pathlib import Path from fontTools.subset import Options, Subsetter from fontTools.ttLib import TTFont ROOT = Path(__file__).resolve().parents[1] SITE = ROOT / "site" FONTS = SITE / "kit" / "ui" / "fonts" MASTER = ROOT / "files" / "fonts" / "symbols.ttf" SHIPPED = FONTS / "symbols.woff2" CSS = FONTS / "fonts.css" CONFIG = SITE / "site.json" DIST = Path(os.environ.get("MRLY_DIST") or SITE / "dist") SKIP = {"git", "raw"} FAMILY = "Noto Sans Symbols 2" DROP = re.compile(r"<(script|style)\b[^>]*>.*?", re.S | re.I) TAG = re.compile(r"<[^>]*>", re.S) def pages(dist): out = [] for here, dirs, files in os.walk(dist): if Path(here) == dist: dirs[:] = [d for d in dirs if d not in SKIP] out += [Path(here) / name for name in files if name.endswith(".html")] return sorted(out) def used(dist): seen = set() found = pages(dist) for file in found: text = html.unescape(TAG.sub(" ", DROP.sub(" ", file.read_text(encoding="utf8", errors="replace")))) seen |= {ord(ch) for ch in set(text)} return seen, len(found) def source(): here = FONTS / "symbols.ttf" if here.exists(): MASTER.parent.mkdir(parents=True, exist_ok=True) if subprocess.run(["git", "mv", str(here), str(MASTER)], cwd=ROOT, capture_output=True).returncode: shutil.move(str(here), str(MASTER)) print(f"symbols: master kept at {MASTER.relative_to(ROOT)}") if MASTER.exists(): return MASTER if SHIPPED.exists(): print("symbols: no master ttf, narrowing the shipped woff2") return SHIPPED raise SystemExit("symbols: nothing to subset") def ranges(points): out = [] for cp in points: if out and cp == out[-1][1] + 1: out[-1][1] = cp else: out.append([cp, cp]) return out def spell(spans): return ", ".join(f"U+{a:04x}" if a == b else f"U+{a:04x}-{b:04x}" for a, b in spans) def face(text, ranged): block = re.compile(r'@font-face \{\n font-family: "' + FAMILY + r'";.*?\n\}', re.S) found = block.search(text) if not found: raise SystemExit(f"symbols: no @font-face for {FAMILY} in {CSS}") body = re.sub( r" src: url\([^)]*\) format\([^)]*\);\n(?: unicode-range: [^\n]*\n)?", f' src: url(symbols.woff2) format("woff2");\n unicode-range: {ranged};\n', found.group(0), ) return text[: found.start()] + body + text[found.end() :] def listed(text): return text.replace('"fonts/symbols.ttf"', '"fonts/symbols.woff2"') def main(): if not DIST.exists(): raise SystemExit(f"symbols: no dist at {DIST}; build the site first") seen, count = used(DIST) file = source() font = TTFont(file, recalcTimestamp=False) keep = sorted(cp for cp in seen & set(font.getBestCmap()) if cp > 0x7F) if not keep: raise SystemExit("symbols: no page needs a symbol glyph") options = Options() options.layout_features = ["*"] options.name_IDs = ["*"] options.hinting = False options.flavor = "woff2" cut = Subsetter(options=options) cut.populate(unicodes=keep) cut.subset(font) font.flavor = "woff2" font.save(SHIPPED) font.close() spans = ranges(keep) ranged = spell(spans) CSS.write_text(face(CSS.read_text(encoding="utf8"), ranged), encoding="utf8") CONFIG.write_text(listed(CONFIG.read_text(encoding="utf8")), encoding="utf8") was = file.stat().st_size now = SHIPPED.stat().st_size print(f"symbols: {count} pages, {len(keep)} glyphs, {len(spans)} ranges, {was:,} -> {now:,} bytes") print(f"symbols: unicode-range: {ranged}") return 0 if __name__ == "__main__": sys.exit(main())