Files
buzz/mobile/scripts/prepare-inter.py
2026-09-23 17:43:07 -07:00

79 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""Remove Inter's heart and warning mappings for native emoji fallback.
Requires fonttools==4.60.1. See assets/fonts/README.md for source and commands.
"""
import argparse
import hashlib
from pathlib import Path
from fontTools.ttLib import TTFont
SOURCES = {
"InterVariable.ttf": "4989b125924991b90d05b2d16e0e388c48f7d5bb8b30539bbf9c755278d0ccaf",
"InterVariable-Italic.ttf": "d6f1f6a172d9e588438db9f986fd5cfad7b30f644374080a8a9d4d91e344586f",
}
NATIVE_EMOJI = (0x2764, 0x26A0)
def verify(source: Path, output: Path) -> None:
with TTFont(source, lazy=True) as before, TTFont(output, lazy=True) as after:
if set(before.reader.keys()) != set(after.reader.keys()):
raise ValueError("Font tables changed")
for tag in before.reader.keys():
original, modified = before.reader[tag], after.reader[tag]
if tag == "cmap":
continue
if tag == "head":
# The only allowed header change is the whole-font checksum.
original = original[:8] + original[12:]
modified = modified[:8] + modified[12:]
if original != modified:
raise ValueError(f"Unexpected change to {tag}")
old_tables, new_tables = before["cmap"].tables, after["cmap"].tables
if len(old_tables) != len(new_tables):
raise ValueError("Character-map table count changed")
for old, new in zip(old_tables, new_tables, strict=True):
identity = lambda t: (t.format, t.platformID, t.platEncID, t.language)
if identity(old) != identity(new):
raise ValueError("Character-map identity changed")
expected = dict(old.cmap)
if old.isUnicode():
for codepoint in NATIVE_EMOJI:
del expected[codepoint]
if new.cmap != expected:
raise ValueError("Character mappings changed beyond U+2764 and U+26A0")
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("source_dir", type=Path)
parser.add_argument("--output-dir", type=Path, default=Path(__file__).resolve().parents[1] / "assets/fonts")
parser.add_argument("--check", action="store_true", help="Verify existing output without writing")
args = parser.parse_args()
for name, digest in SOURCES.items():
source, output = args.source_dir / name, args.output_dir / name
if source.resolve() == output.resolve():
raise ValueError("Keep pristine sources separate from generated fonts")
if hashlib.sha256(source.read_bytes()).hexdigest() != digest:
raise ValueError(f"Unexpected source font: {name}")
if not args.check:
with TTFont(source, lazy=True, recalcTimestamp=False) as font:
tables = [t for t in font["cmap"].tables if t.isUnicode()]
if not tables or any(codepoint not in t.cmap for t in tables for codepoint in NATIVE_EMOJI):
raise ValueError(f"Expected heart and warning mappings in {name}")
# FontTools shares dictionaries between equivalent subtables.
for cmap in {id(t.cmap): t.cmap for t in tables}.values():
for codepoint in NATIVE_EMOJI:
del cmap[codepoint]
args.output_dir.mkdir(parents=True, exist_ok=True)
font.save(output, reorderTables=False)
verify(source, output)
print(f"{name}: only U+2764 and U+26A0 mappings removed; other mappings and glyph data unchanged")
if __name__ == "__main__":
main()