Commit 1a54715d authored by Sebastian Friedl's avatar Sebastian Friedl
Browse files

Slice font instances from variable fonts

parent bc85d68a
# ignore files generated by scripts
out/
# ignore python venv
# Python Virtual Environment
venv/
# ignore macOS bloat
# Files generated by scripts
out/
# macOS
.DS_Store
# IDEs, Editors
/.idea
*.swp
......@@ -18,7 +18,7 @@ build:
- source venv/bin/activate
- pip install -r dep/dep.pip
script:
- echo "Hello World!"
- ./scripts/11-slice.py
after_script:
- echo "Git commit $CI_COMMIT_SHORT_SHA" > BUILD
- echo "Build finished on `date -R`" >> BUILD
......
#!/usr/bin/env python3
from multiprocessing import Pool
import os
from pathlib import Path
import sys
from time import time
from fontTools.ttLib import TTFont
from fontTools.varLib import instancer
_root_dir = Path(__file__).parent.parent.resolve(strict=True)
_timestamp = int(time())
_copyright = '© 2021 German Young Physicists’ Tournament, with reserved Font Name ‘GYPT’.'
_version = f'0.{_timestamp}'
STYLES = [
('Serif', 'Source Serif 4 Variable'),
('Sans', 'Source Sans 3 VF'),
('Mono', 'Source Code Variable'),
]
INSTANCES = {
100: (220, 'UltraLight'),
200: (300, 'ExtraLight'),
300: (340, 'Light'),
400: (400, 'Regular'),
500: (480, 'Book'),
600: (580, 'Semibold'),
700: (640, 'Bold'),
800: (720, 'ExtraBold'),
900: (900, 'Black'),
}
def update_name_table(instance, style, weight, verbose_weight, italic=False):
# Updates information given in the instance's 'name' table.
#
# Documentation regarding the 'name' table is available at:
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
table = instance['name']
family = f'GYPT {style}'
subfamily = f'{(weight != 400 or not italic)*verbose_weight} {italic*"Italic"}'.strip()
if weight not in (400, 700):
legacy_family = f'{family} {verbose_weight}'
legacy_subfamily = 'Regular' if not italic else 'Italic'
else:
legacy_family, legacy_subfamily = family, subfamily
name = f'{family} {subfamily}' if weight != 400 or italic else family
ps_name = f'{family}-{subfamily}'.replace('Italic', 'It').replace(' ', '')
old_copyright = table.getName(0, 1, 0, 0) or table.getName(0, 3, 1, 1033)
assert old_copyright is not None, f'No copyright information in source for {style} {weight}{italic*"i"}'
old_copyright = old_copyright.toStr()
values = {
0: old_copyright[:(-1 if old_copyright.endswith('.') else None)] + '; ' + _copyright,
1: legacy_family,
2: legacy_subfamily,
3: f'{_timestamp}:{style.lower()}:{weight}{italic*"i"}',
4: name,
5: _version,
6: ps_name,
16: family,
17: subfamily,
25: family.replace(' ', ''),
}
# assert that all entries which should be replaced will be replaced
for nr in [nr for nr in table.names if nr.nameID in values.keys()]:
assert (nr.platformID, nr.platEncID, nr.langID) in [(1, 0, 0), (3, 1, 1033)]
for platform_id, platform_enc_id, lang_id in [(1, 0, 0), (3, 1, 1033)]:
for name_id, string in values.items():
table.setName(string, name_id, platform_id, platform_enc_id, lang_id)
# remove manufacturer name and font vendor url
for name_id in [8, 11]:
table.removeNames(nameID=name_id, platformID=None, platEncID=None, langID=None)
def update_flags(instance, weight, italic=False):
# Adjust font selection flags and weight classes given in the 'OS/2' and 'head' tables.
#
# Documentation regarding the 'OS/2' and 'head' tables is available at:
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6OS2.html
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6head.html
fs_selection = 0
mac_style = 0
if weight == 400 and not italic:
fs_selection = 64
if weight == 700:
fs_selection += 32
mac_style += 1
if italic:
fs_selection += 1
mac_style += 2
instance['OS/2'].fsSelection = fs_selection
instance['OS/2'].usWeightClass = weight
instance['OS/2'].achVendID = 'GYPT'
instance['head'].macStyle = mac_style
def slice_font(style, source, weight, adobe_weight, verbose_weight, italic=False):
source_dir = _root_dir / 'adobe' / '-'.join(source.split(' ')[:2]).lower() / 'VAR'
source_fnt = f'{source.replace(" ", "")}-{"Roman" if not italic else "Italic"}.ttf'
var_font = TTFont(source_dir / source_fnt)
attrs = {'wght': adobe_weight, 'opsz': 20}
instance = instancer.instantiateVariableFont(var_font, {a.axisTag: attrs[a.axisTag] for a in var_font['fvar'].axes})
assert 'fvar' not in instance, 'Instantiated variable font still has variation axes'
update_name_table(instance, style, weight, verbose_weight, italic)
update_flags(instance, weight, italic)
# TODO: patch small caps
output_dir = _root_dir / 'out' / f'gypt-{style.lower()}' / 'TTF'
output_fnt = f'GYPT{style}-{(weight != 400 or not italic)*verbose_weight}{italic*"It"}.ttf'
os.makedirs(output_dir, exist_ok=True)
instance.save(output_dir / output_fnt)
def main():
exit_code = 0
def term(pool):
def handler(e):
print(e)
pool.terminate()
nonlocal exit_code
exit_code = 1
return handler
with Pool(processes=min(os.cpu_count(), 8), maxtasksperchild=1) as p:
for style, source in STYLES:
for weight, options in INSTANCES.items():
p.apply_async(slice_font, (style, source, weight, *options), {'italic': False}, error_callback=term(p))
p.apply_async(slice_font, (style, source, weight, *options), {'italic': True}, error_callback=term(p))
p.close()
p.join()
if exit_code:
sys.exit(exit_code)
if __name__ == '__main__':
main()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment