mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
79 lines
3.6 KiB
Python
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()
|