Last active
September 13, 2023 13:52
-
-
Save simoncozens/5ddafef074dfb47df8a68f8e2b2191b8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/opt/homebrew/bin/python3 | |
import sys | |
sys.path.insert(0, "/Users/simon/others-repos/ufo2ft/Lib") | |
from glyphsLib.classes import GSFont | |
from glyphsLib.builder import UFOBuilder | |
from glyphsLib.builder.font import fill_ufo_metadata | |
from fontmake.font_project import FontProject | |
from timeit import default_timer as timer | |
from pathlib import Path | |
from watchdog.observers import Observer | |
from watchdog.events import FileSystemEventHandler | |
class FileModifiedHandler(FileSystemEventHandler): | |
def __init__(self, file_paths, callback): | |
self.paths = file_paths | |
self.callback = callback | |
# set observer to watch for changes in the directory | |
self.observer = Observer() | |
for directory in set([Path(x).parent for x in self.paths]): | |
self.observer.schedule(self, directory, recursive=False) | |
self.observer.start() | |
self.observer.join() | |
def on_modified(self, event): | |
# only act on the change that we're looking for | |
if not event.is_directory and any( | |
event.src_path.endswith(file_name) for file_name in self.paths | |
): | |
self.observer.stop() # stop watching | |
self.callback(event.src_path) # call callback | |
def build_file(file): | |
start = timer() | |
font = GSFont(file) | |
gsfont_time = timer() | |
regular_master = [ m for m in font.masters if m.name == "Regular" ][0] | |
builder = UFOBuilder( | |
font, | |
minimal=True | |
) | |
for index, master in enumerate(font.masters): | |
ufo = builder.ufo_module.Font() | |
if master.id == regular_master.id: | |
fill_ufo_metadata(master, ufo) | |
builder.to_ufo_names(ufo, master, builder.family_name) # .names | |
builder.to_ufo_family_user_data(ufo) # .user_data | |
builder.to_ufo_custom_params(ufo, font) # .custom_params | |
builder.to_ufo_master_attributes(ufo, master) # .masters | |
source = builder._designspace.newSourceDescriptor() | |
source.font = ufo | |
builder._designspace.addSource(source) | |
builder._sources[master.id] = source | |
master_layer_ids = {m.id for m in builder.font.masters} | |
# Generate the main (master) layers first. | |
for glyph in builder.font.glyphs: | |
for layer in glyph.layers.values(): | |
if layer.associatedMasterId != layer.layerId: | |
continue | |
if layer.associatedMasterId != regular_master.id: | |
continue | |
ufo_layer = builder.to_ufo_layer(glyph, layer) # .layers | |
ufo_glyph = ufo_layer.newGlyph(glyph.name) | |
builder.to_ufo_glyph(ufo_glyph, layer, glyph) # .glyph | |
for master_id, source in builder._sources.items(): | |
ufo = source.font | |
master = builder.font.masters[master_id] | |
if builder.propagate_anchors: | |
builder.to_ufo_propagate_font_anchors(ufo) # .anchor_propagation | |
if not builder.minimal: | |
for layer in list(ufo.layers): | |
builder.to_ufo_layer_lib(master, ufo, layer) # .user_data | |
builder.to_ufo_master_features(ufo, master) # .features | |
builder.to_ufo_custom_params(ufo, master) # .custom_params | |
if builder.write_skipexportglyphs: | |
# Sanitize skip list and write it to both Designspace- and UFO-level lib | |
# keys. The latter is unnecessary when using e.g. the ufo2ft.compile*FromDS` | |
# functions, but the data may take a different path. Writing it everywhere | |
# can save on surprises/logic in other software. | |
skip_export_glyphs = builder._designspace.lib.get("public.skipExportGlyphs") | |
if skip_export_glyphs is not None: | |
skip_export_glyphs = sorted(set(skip_export_glyphs)) | |
builder._designspace.lib["public.skipExportGlyphs"] = skip_export_glyphs | |
for source in builder._sources.values(): | |
source.font.lib["public.skipExportGlyphs"] = skip_export_glyphs | |
builder.to_ufo_groups() # .groups | |
builder.to_ufo_kerning() # .kerning | |
ufo = builder._sources[regular_master.id].font | |
ufo_time = timer() | |
FontProject().save_otfs( | |
[ufo], | |
ttf=True, | |
autohint=False, | |
remove_overlaps=False, | |
reverse_direction=False, | |
output_dir="master_ttf/", | |
flatten_components=False | |
) | |
endtime = timer() | |
print(f"Done in: {endtime-start}s") | |
print(f" (Glyphs parsing: {gsfont_time-start}s)") | |
print(f" (Glyphs to UFO: {ufo_time-gsfont_time}s)") | |
print(f" (UFO to TTF: {endtime-ufo_time}s)") | |
import argparse | |
parser = argparse.ArgumentParser(description='Build fonts really fast.') | |
parser.add_argument('paths', metavar='N', nargs='+', | |
help='filenames') | |
parser.add_argument('--watch', action="store_true", | |
help='stay on and watch the files') | |
args = parser.parse_args() | |
for path in args.paths: | |
build_file(path) | |
if args.watch: | |
while True: | |
FileModifiedHandler(args.paths, build_file) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment