更新技术美术明亮教具室版:字体、指路、教学与庆祝效果
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"""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')
|
||||
Reference in New Issue
Block a user