"""Offline font subset builder. Sources and fonttools are build-only, not shipped. Run with fonttools on PYTHONPATH (e.g. .font-tools), from this directory. Source downloads: https://github.com/google/fonts/tree/main/ofl/{nunito,notosanssc} """ from pathlib import Path from hashlib import sha256 import json from fontTools.ttLib import TTFont from fontTools import subset from fontTools.varLib.instancer import instantiateVariableFont root = Path(__file__).resolve().parent corpus = ''.join(p.read_text(encoding='utf-8') for p in (root / 'src').rglob('*') if p.suffix in ('.mjs', '.json')) chars = ''.join(sorted(set(corpus + ''.join(chr(i) for i in range(32, 127)) + '✓→…!:·'))) manifest = {'generator': 'fonttools', 'sources': [], 'outputs': [], 'characters': chars} for family, weights in [('notosanssc', [500, 600, 700]), ('nunito', [800])]: source = root / '.font-sources' / (family + '.ttf') manifest['sources'].append({'family': family, 'sha256': sha256(source.read_bytes()).hexdigest(), 'url': 'https://github.com/google/fonts/tree/main/ofl/' + family}) for weight in weights: font = TTFont(source) options = subset.Options() options.name_IDs = ['*'] sub = subset.Subsetter(options=options) text = chars if family == 'notosanssc' else '0123456789' sub.populate(text=text) sub.subset(font) font = instantiateVariableFont(font, {'wght': weight}, inplace=True) name = ('NumberSwipeSans' if family == 'notosanssc' else 'NumberSwipeDigits') + str(weight) for record in font['name'].names: if record.nameID in (1, 2, 3, 4, 6, 16, 17): value = 'Regular' if record.nameID in (2, 17) else name record.string = value.encode(record.getEncoding(), errors='replace') target = root / 'fonts' / (name + '.ttf') font.save(target) cmap = font.getBestCmap() missing = sorted({c for c in text if not c.isspace() and ord(c) not in cmap}) # Source fonts don't contain code symbols like private-use/non-printing chars; # all player-visible Han characters and digits must be present. critical = [c for c in missing if '\u4e00' <= c <= '\u9fff' or c.isdigit()] if critical: raise ValueError('Missing glyphs: ' + ''.join(critical)) manifest['outputs'].append({'file': target.name, 'weight': weight, 'sha256': sha256(target.read_bytes()).hexdigest(), 'bytes': target.stat().st_size, 'glyphs': len(cmap)}) print(target.name, target.stat().st_size) source = root / '.font-sources' / 'zcoolkuaile.ttf' font = TTFont(source) options = subset.Options() options.name_IDs = ['*'] sub = subset.Subsetter(options=options) playful_chars = '重开撤销指路已走步还剩块第关0123456789 ·' sub.populate(text=playful_chars) sub.subset(font) for record in font['name'].names: if record.nameID in (1, 2, 3, 4, 6, 16, 17): record.string = ('Regular' if record.nameID in (2, 17) else 'NumberSwipePlayful').encode(record.getEncoding(), errors='replace') target = root / 'fonts' / 'NumberSwipePlayful.ttf' font.save(target) manifest['sources'].append({'family': 'zcoolkuaile', 'sha256': sha256(source.read_bytes()).hexdigest(), 'url': 'https://github.com/google/fonts/tree/main/ofl/zcoolkuaile'}) manifest['outputs'].append({'file': target.name, 'weight': 400, 'sha256': sha256(target.read_bytes()).hexdigest(), 'bytes': target.stat().st_size, 'glyphs': len(font.getBestCmap()), 'characters': playful_chars}) print(target.name, target.stat().st_size) (root / 'fonts' / 'manifest.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding='utf-8')