From aff389733660d27ed8c84ac680237c59200bfc2d Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Wed, 1 Jun 2022 13:55:42 +0100 Subject: [PATCH 01/37] WIP Ninja Builder --- Lib/gftools/builder/ninja.py | 214 +++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 Lib/gftools/builder/ninja.py diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py new file mode 100644 index 000000000..4b6333af3 --- /dev/null +++ b/Lib/gftools/builder/ninja.py @@ -0,0 +1,214 @@ +"""Ninja file writer for orchestrating font builds""" +from ninja_syntax import Writer +import glyphsLib +import sys +import ufoLib2 +import os +from gftools.builder import GFBuilder +from fontTools.designspaceLib import DesignSpaceDocument +from pathlib import Path + + +class NinjaBuilder(GFBuilder): + def build(self): + self.w = Writer(open("build.ninja", "w")) + self.setup_rules() + self.get_designspaces() + + if self.config["buildVariable"]: + self.build_variable() + # transfer vf vtt hints now in case static fonts are instantiated + if "vttSources" in self.config: + self.build_vtt(self.config['vfDir']) + if self.config["buildStatic"]: + self.build_static() + if "vttSources" in self.config: + self.build_vtt(self.config['ttDir']) + self.w.close() + + def setup_rules(self): + self.w.rule("glyphs2ufo", "fontmake -o ufo -g $in") + + if self.config["buildVariable"]: + self.w.rule("variable", "fontmake -o variable -m $in $fontmake_args") + + self.w.rule("instanceufo", "fontmake -i -o ufo -m $in $fontmake_args") + self.w.rule("instancettf", "fontmake -o ttf -u $in $fontmake_args") + self.w.rule("instanceotf", "fontmake -o otf -u $in $fontmake_args") + self.w.rule("genstat", "gftools-gen-stat.py --inplace $other_args --axis-order $axis_order -- $in ; touch genstat.stamp") + if self.config["includeSourceFixes"]: + fixargs = "--include-source-fixes" + else: + fixargs = "" + self.w.rule("fix", f"gftools-fix-font.py -o $in {fixargs} $in; touch $in.fixstamp") + self.w.newline() + + def get_designspaces(self): + self.designspaces = [] + for source in self.config["sources"]: + if source.endswith(".glyphs"): + # Do the conversion once, so we know what the instances and filenames are + designspace = glyphsLib.to_designspace( + glyphsLib.GSFont(source), + ufo_module=ufoLib2, + generate_GDEF=True, + store_editor_state=False, + minimal=True, + ) + designspace_path = os.path.join("master_ufo", designspace.filename) + os.makedirs(os.path.dirname(designspace_path), exist_ok=True) + designspace.write(designspace_path) + self.w.build(designspace_path, "glyphs2ufo", source) + else: + designspace_path = source + designspace = DesignSpaceDocument.fromfile(designspace_path) + self.designspaces.append((designspace_path, designspace)) + self.w.newline() + + def fontmake_args(self, args): + my_args = [] + my_args.append("--filter ...") + if self.config["flattenComponents"]: + my_args.append("--filter FlattenComponentsFilter") + if self.config["decomposeTransformedComponents"]: + my_args.append("--filter DecomposeTransformedComponentsFilter") + if "output_dir" in args: + my_args.append("--output-dir "+args["output_dir"]) + if "output_path" in args: + my_args.append("--output-path "+args["output_path"]) + return " ".join(my_args) + + def build_variable(self): + targets = [] + for (designspace_path, designspace) in self.designspaces: + axis_tags = sorted([ax.tag for ax in designspace.axes]) + axis_tags = ",".join(axis_tags) + target = os.path.join(self.config["vfDir"], Path(designspace_path).stem + "[%s].ttf" % axis_tags) + self.w.build(target, "variable", designspace_path, variables={"fontmake_args": self.fontmake_args({"output_path": target})}) + targets.append(target) + self.gen_stat(axis_tags, targets) + # We post process each variable font after generating the STAT tables + # because these tables are needed in order to fix the name tables. + for t in targets: + self.post_process(t) + + def gen_stat(self, axis_tags, targets): + if "axisOrder" not in self.config: + self.config["axisOrder"] = axis_tags.split(",") + # Janky "is-italic" test. To strengthen this up we should look inside + # the source files and check their stylenames. + if any("italic" in x[0].lower() for x in self.designspaces): + self.config["axisOrder"].append("ital") + other_args = "" + if "stat" in self.config: + other_args = f"--src {self.config['stat']}" + if "stylespaceFile" in self.config or "statFormat4" in self.config: + raise ValueError("Stylespace files / statFormat4 not supported in Ninja mode") + # Because gftools-gen-stat doesn't seem to support it? + self.w.build("genstat.stamp", "genstat", targets, variables={"axis_order": self.config["axisOrder"], "other_args": other_args}) + + def post_process(self, file): + self.w.build(file+".fixstamp", "fix", file) + + def build_static(self): + pass + # if self.config["buildOTF"]: + # self.build_a_static_format("otf", self.config["otDir"], self.post_process) + # if self.config["buildWebfont"]: + # self.mkdir(self.config["woffDir"], clean=True) + # if self.config["buildTTF"]: + # if "instances" in self.config: + # self.instantiate_static_fonts( + # self.config["ttDir"], self.post_process_ttf + # ) + # else: + # self.build_a_static_format( + # "ttf", self.config["ttDir"], self.post_process_ttf + # ) + + # def instantiate_static_fonts(self, directory, postprocessor): + # self.mkdir(directory, clean=True) + # for font in self.config["instances"]: + # varfont_path = os.path.join(self.config['vfDir'], font) + # varfont = TTFont(varfont_path) + # for font, instances in self.config["instances"].items(): + # for inst in instances: + # if 'familyName' in inst: + # family_name = inst['familyName'] + # else: + # family_name = self.config['familyName'] + # if "styleName" in inst: + # style_name = inst['styleName'] + # else: + # style_name = None + + # static_font = gen_static_font( + # varfont, + # axes=inst["coordinates"], + # family_name=family_name, + # style_name=style_name, + # ) + # family_name = font_familyname(static_font) + # style_name = font_stylename(static_font) + # dst = os.path.join( + # directory, f"{family_name}-{style_name}.ttf".replace(" ", "") + # ) + # static_font.save(dst) + # postprocessor(dst) + # self.outputs.add(dst) + + # def build_a_static_format(self, format, directory, postprocessor): + # self.mkdir(directory, clean=True) + # for source in self.config["sources"]: + # args = { + # "output": [format], + # "output_dir": directory, + # "optimize_cff": CFFOptimization.SUBROUTINIZE, + # } + # if not source.endswith("ufo"): + # args["interpolate"] = True + # self.logger.info("Creating static fonts from %s" % source) + # for fontfile in self.run_fontmake(source, args): + # self.logger.info("Created static font %s" % fontfile) + # postprocessor(fontfile) + # self.outputs.add(fontfile) + + # def post_process_ttf(self, filename): + # if self.config["autohintTTF"]: + # self.logger.debug("Autohinting") + # autohint(filename, filename, add_script=self.config["ttfaUseScript"]) + # self.post_process(filename) + # if self.config["buildWebfont"]: + # self.logger.debug("Building webfont") + # woff2_main(["compress", filename]) + # self.move_webfont(filename) + + def build_vtt(self, font_dir): + raise NotImplementedError + # for font, vtt_source in self.config['vttSources'].items(): + # if font not in os.listdir(font_dir): + # continue + # self.logger.debug(f"Compiling hint file {vtt_source} into {font}") + # font_path = os.path.join(font_dir, font) + # font = TTFont(font_path) + # merge_vtt_hinting(font, vtt_source, keep_cvar=True) + # compile_vtt_hinting(font, ship=True) + + # # Add a gasp table which is optimised for VTT hinting + # # https://googlefonts.github.io/how-to-hint-variable-fonts/ + # gasp_tbl = newTable("gasp") + # gasp_tbl.gaspRange = {8: 10, 65535: 15} + # gasp_tbl.version = 1 + # font['gasp'] = gasp_tbl + # font.save(font.reader.file.name) + + # def move_webfont(self, filename): + # wf_filename = filename.replace(".ttf", ".woff2") + # os.rename( + # wf_filename, + # wf_filename.replace(self.config["ttDir"], self.config["woffDir"]), + # ) + + +if __name__ == "__main__": + NinjaBuilder(sys.argv[1]).build() From 3bd07eecfc7e83539f8bae4f2274fd8666c9e7af Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Wed, 1 Jun 2022 15:11:31 +0100 Subject: [PATCH 02/37] Handle static targets and webfonts (no autohinting, no instances: config) --- Lib/gftools/builder/ninja.py | 254 ++++++++++++++++++++--------------- 1 file changed, 148 insertions(+), 106 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 4b6333af3..2e0aafeb1 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -1,5 +1,5 @@ """Ninja file writer for orchestrating font builds""" -from ninja_syntax import Writer +from ninja.ninja_syntax import Writer import glyphsLib import sys import ufoLib2 @@ -19,49 +19,72 @@ def build(self): self.build_variable() # transfer vf vtt hints now in case static fonts are instantiated if "vttSources" in self.config: - self.build_vtt(self.config['vfDir']) + self.build_vtt(self.config["vfDir"]) if self.config["buildStatic"]: self.build_static() if "vttSources" in self.config: - self.build_vtt(self.config['ttDir']) + self.build_vtt(self.config["ttDir"]) self.w.close() def setup_rules(self): + self.w.comment("Rules") + self.w.newline() + self.w.comment("Convert glyphs file to UFO") self.w.rule("glyphs2ufo", "fontmake -o ufo -g $in") if self.config["buildVariable"]: - self.w.rule("variable", "fontmake -o variable -m $in $fontmake_args") + self.w.comment("Build a variable font from Designspace") + self.w.rule("variable", "fontmake -o variable -m $in $fontmake_args") + self.w.comment("Build a set of instance UFOs from Designspace") self.w.rule("instanceufo", "fontmake -i -o ufo -m $in $fontmake_args") - self.w.rule("instancettf", "fontmake -o ttf -u $in $fontmake_args") - self.w.rule("instanceotf", "fontmake -o otf -u $in $fontmake_args") - self.w.rule("genstat", "gftools-gen-stat.py --inplace $other_args --axis-order $axis_order -- $in ; touch genstat.stamp") - if self.config["includeSourceFixes"]: - fixargs = "--include-source-fixes" - else: - fixargs = "" - self.w.rule("fix", f"gftools-fix-font.py -o $in {fixargs} $in; touch $in.fixstamp") + + self.w.comment("Build a TTF file from a UFO") + self.w.rule( + "buildttf", "fontmake -o ttf -u $in $fontmake_args --output-path $out" + ) + + self.w.comment("Build an OTF file from a UFO") + self.w.rule( + "buildotf", "fontmake -o otf -u $in $fontmake_args --output-path $out" + ) + + self.w.comment("Add a STAT table to a set of variable fonts") + self.w.rule( + "genstat", + "gftools-gen-stat.py --inplace $other_args --axis-order $axis_order -- $in ; touch $stampfile", + ) + + self.w.comment("Run the font fixer in-place and touch a stamp file") + self.w.rule( + "fix", "gftools-fix-font.py -o $in $fixargs $in; touch $in.fixstamp" + ) + + self.w.comment("Create a web font") + self.w.rule("webfont", f"fonttools ttLib.woff2 compress -o $out $in") + self.w.newline() def get_designspaces(self): self.designspaces = [] for source in self.config["sources"]: if source.endswith(".glyphs"): - # Do the conversion once, so we know what the instances and filenames are - designspace = glyphsLib.to_designspace( - glyphsLib.GSFont(source), - ufo_module=ufoLib2, - generate_GDEF=True, - store_editor_state=False, - minimal=True, - ) - designspace_path = os.path.join("master_ufo", designspace.filename) - os.makedirs(os.path.dirname(designspace_path), exist_ok=True) - designspace.write(designspace_path) - self.w.build(designspace_path, "glyphs2ufo", source) + # Do the conversion once, so we know what the instances and filenames are + designspace = glyphsLib.to_designspace( + glyphsLib.GSFont(source), + ufo_module=ufoLib2, + generate_GDEF=True, + store_editor_state=False, + minimal=True, + ) + designspace_path = os.path.join("master_ufo", designspace.filename) + os.makedirs(os.path.dirname(designspace_path), exist_ok=True) + designspace.write(designspace_path) + self.w.comment("Convert glyphs source to designspace") + self.w.build(designspace_path, "glyphs2ufo", source) else: - designspace_path = source - designspace = DesignSpaceDocument.fromfile(designspace_path) + designspace_path = source + designspace = DesignSpaceDocument.fromfile(designspace_path) self.designspaces.append((designspace_path, designspace)) self.w.newline() @@ -73,26 +96,42 @@ def fontmake_args(self, args): if self.config["decomposeTransformedComponents"]: my_args.append("--filter DecomposeTransformedComponentsFilter") if "output_dir" in args: - my_args.append("--output-dir "+args["output_dir"]) + my_args.append("--output-dir " + args["output_dir"]) if "output_path" in args: - my_args.append("--output-path "+args["output_path"]) + my_args.append("--output-path " + args["output_path"]) return " ".join(my_args) def build_variable(self): targets = [] + self.w.newline() + self.w.comment("VARIABLE FONTS") + self.w.newline() for (designspace_path, designspace) in self.designspaces: axis_tags = sorted([ax.tag for ax in designspace.axes]) axis_tags = ",".join(axis_tags) - target = os.path.join(self.config["vfDir"], Path(designspace_path).stem + "[%s].ttf" % axis_tags) - self.w.build(target, "variable", designspace_path, variables={"fontmake_args": self.fontmake_args({"output_path": target})}) + target = os.path.join( + self.config["vfDir"], + Path(designspace_path).stem + "[%s].ttf" % axis_tags, + ) + self.w.build( + target, + "variable", + designspace_path, + variables={ + "fontmake_args": self.fontmake_args({"output_path": target}) + }, + ) targets.append(target) - self.gen_stat(axis_tags, targets) + self.w.newline() + stampfile = self.gen_stat(axis_tags, targets) # We post process each variable font after generating the STAT tables # because these tables are needed in order to fix the name tables. + self.w.comment("Variable font post-processing") for t in targets: - self.post_process(t) + self.post_process(t, implicit=stampfile) def gen_stat(self, axis_tags, targets): + self.w.comment("Generate STAT tables") if "axisOrder" not in self.config: self.config["axisOrder"] = axis_tags.split(",") # Janky "is-italic" test. To strengthen this up we should look inside @@ -103,88 +142,91 @@ def gen_stat(self, axis_tags, targets): if "stat" in self.config: other_args = f"--src {self.config['stat']}" if "stylespaceFile" in self.config or "statFormat4" in self.config: - raise ValueError("Stylespace files / statFormat4 not supported in Ninja mode") + raise ValueError( + "Stylespace files / statFormat4 not supported in Ninja mode" + ) # Because gftools-gen-stat doesn't seem to support it? - self.w.build("genstat.stamp", "genstat", targets, variables={"axis_order": self.config["axisOrder"], "other_args": other_args}) + stampfile = targets[0] + ".statstamp" + self.w.build( + stampfile, + "genstat", + targets, + variables={ + "axis_order": self.config["axisOrder"], + "other_args": other_args, + "stampfile": stampfile, + }, + ) + self.w.newline() + return stampfile - def post_process(self, file): - self.w.build(file+".fixstamp", "fix", file) + def post_process(self, file, implicit=None): + variables = {} + if self.config["includeSourceFixes"]: + variables = ({"fixargs": "--include-source-fixes"},) + self.w.build( + file + ".fixstamp", "fix", file, implicit=implicit, variables=variables + ) def build_static(self): + # Let's make our interpolated UFOs. + self.w.newline() + self.w.comment("STATIC FONTS") + self.w.newline() + for (path, designspace) in self.designspaces: + self.w.comment(f" Interpolate UFOs for {os.path.basename(path)}") + self.w.build( + [ + instance.filename.replace("instance_ufos", "instance_ufo") + for instance in designspace.instances + ], + "instanceufo", + path, + ) + self.w.newline() + + return GFBuilder.build_static(self) + + def instantiate_static_fonts(self, directory, postprocessor): pass - # if self.config["buildOTF"]: - # self.build_a_static_format("otf", self.config["otDir"], self.post_process) - # if self.config["buildWebfont"]: - # self.mkdir(self.config["woffDir"], clean=True) - # if self.config["buildTTF"]: - # if "instances" in self.config: - # self.instantiate_static_fonts( - # self.config["ttDir"], self.post_process_ttf - # ) - # else: - # self.build_a_static_format( - # "ttf", self.config["ttDir"], self.post_process_ttf - # ) - - # def instantiate_static_fonts(self, directory, postprocessor): - # self.mkdir(directory, clean=True) - # for font in self.config["instances"]: - # varfont_path = os.path.join(self.config['vfDir'], font) - # varfont = TTFont(varfont_path) - # for font, instances in self.config["instances"].items(): - # for inst in instances: - # if 'familyName' in inst: - # family_name = inst['familyName'] - # else: - # family_name = self.config['familyName'] - # if "styleName" in inst: - # style_name = inst['styleName'] - # else: - # style_name = None - - # static_font = gen_static_font( - # varfont, - # axes=inst["coordinates"], - # family_name=family_name, - # style_name=style_name, - # ) - # family_name = font_familyname(static_font) - # style_name = font_stylename(static_font) - # dst = os.path.join( - # directory, f"{family_name}-{style_name}.ttf".replace(" ", "") - # ) - # static_font.save(dst) - # postprocessor(dst) - # self.outputs.add(dst) - - # def build_a_static_format(self, format, directory, postprocessor): - # self.mkdir(directory, clean=True) - # for source in self.config["sources"]: - # args = { - # "output": [format], - # "output_dir": directory, - # "optimize_cff": CFFOptimization.SUBROUTINIZE, - # } - # if not source.endswith("ufo"): - # args["interpolate"] = True - # self.logger.info("Creating static fonts from %s" % source) - # for fontfile in self.run_fontmake(source, args): - # self.logger.info("Created static font %s" % fontfile) - # postprocessor(fontfile) - # self.outputs.add(fontfile) - - # def post_process_ttf(self, filename): - # if self.config["autohintTTF"]: - # self.logger.debug("Autohinting") - # autohint(filename, filename, add_script=self.config["ttfaUseScript"]) - # self.post_process(filename) - # if self.config["buildWebfont"]: - # self.logger.debug("Building webfont") - # woff2_main(["compress", filename]) - # self.move_webfont(filename) + + def build_a_static_format(self, format, directory, postprocessor): + self.w.comment(f"Build {format} format") + self.w.newline() + if format == "ttf": + target_dir = self.config["ttDir"] + else: + target_dir = self.config["otDir"] + targets = [] + for (path, designspace) in self.designspaces: + self.w.comment(f" {path}") + for instance in designspace.instances: + ufo = Path(instance.filename.replace("instance_ufos", "instance_ufo")) + target = str(Path(target_dir) / (ufo.stem + "." + format)) + self.w.build(target, "build" + format, str(ufo)) + targets.append(target) + self.w.newline() + self.w.comment(f"Post-processing {format}s") + for t in targets: + postprocessor(t) + self.w.newline() + + def post_process_ttf(self, filename): + # if self.config["autohintTTF"]: + # self.logger.debug("Autohinting") + # autohint(filename, filename, add_script=self.config["ttfaUseScript"]) + self.post_process(filename) + if self.config["buildWebfont"]: + webfont_filename = filename.replace(".ttf", ".woff2").replace( + self.config["ttDir"], self.config["woffDir"] + ) + self.w.build( + webfont_filename, "webfont", filename, implicit=filename + ".fixstamp" + ) def build_vtt(self, font_dir): raise NotImplementedError + # for font, vtt_source in self.config['vttSources'].items(): # if font not in os.listdir(font_dir): # continue From 6bcbb607b7b041ec9a12af5bec01c53e59d799b0 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Wed, 1 Jun 2022 15:17:42 +0100 Subject: [PATCH 03/37] Call ninja and build it --- Lib/gftools/builder/ninja.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 2e0aafeb1..1c04c66cd 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -1,5 +1,6 @@ """Ninja file writer for orchestrating font builds""" from ninja.ninja_syntax import Writer +import ninja import glyphsLib import sys import ufoLib2 @@ -26,6 +27,8 @@ def build(self): self.build_vtt(self.config["ttDir"]) self.w.close() + ninja._program("ninja", []) + def setup_rules(self): self.w.comment("Rules") self.w.newline() From f7ec3f80adbf2b01261052f2308d6170224de89a Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Wed, 1 Jun 2022 15:26:04 +0100 Subject: [PATCH 04/37] Do autohinting too --- Lib/gftools/builder/ninja.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 1c04c66cd..8b810a46c 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -63,6 +63,12 @@ def setup_rules(self): "fix", "gftools-fix-font.py -o $in $fixargs $in; touch $in.fixstamp" ) + self.w.comment("Run the ttfautohint in-place and touch a stamp file") + self.w.rule( + "autohint", + "ttfautohint $in $in.autohinted; mv $in.autohinted $in; touch $in.autohintstamp", + ) + self.w.comment("Create a web font") self.w.rule("webfont", f"fonttools ttLib.woff2 compress -o $out $in") @@ -215,10 +221,13 @@ def build_a_static_format(self, format, directory, postprocessor): self.w.newline() def post_process_ttf(self, filename): - # if self.config["autohintTTF"]: - # self.logger.debug("Autohinting") - # autohint(filename, filename, add_script=self.config["ttfaUseScript"]) - self.post_process(filename) + if self.config["autohintTTF"]: + if self.config["ttfaUseScript"]: + raise NotImplementedError("ttaUseScript not supported in ninja mode") + self.w.build(filename + ".autohintstamp", "autohint", filename) + self.post_process(filename, implicit=filename + ".autohintstamp") + else: + self.post_process(filename) if self.config["buildWebfont"]: webfont_filename = filename.replace(".ttf", ".woff2").replace( self.config["ttDir"], self.config["woffDir"] @@ -247,13 +256,6 @@ def build_vtt(self, font_dir): # font['gasp'] = gasp_tbl # font.save(font.reader.file.name) - # def move_webfont(self, filename): - # wf_filename = filename.replace(".ttf", ".woff2") - # os.rename( - # wf_filename, - # wf_filename.replace(self.config["ttDir"], self.config["woffDir"]), - # ) - if __name__ == "__main__": NinjaBuilder(sys.argv[1]).build() From b7fefaa9d150a991357c1b449bbde9183fb26153 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Mon, 6 Jun 2022 12:15:00 +0100 Subject: [PATCH 05/37] Run ninja by default, fallback for unimplemented stuff --- Lib/gftools/builder/ninja.py | 29 ++++++++++++----------------- bin/gftools-builder.py | 20 +++++++++++++++++--- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 8b810a46c..9ba2b04a2 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -9,9 +9,20 @@ from fontTools.designspaceLib import DesignSpaceDocument from pathlib import Path +UNSUPPORTED = ["stylespaceFile", "statFormat4", "ttfaUseScript", "vttSources"] + class NinjaBuilder(GFBuilder): def build(self): + # In some cases we want to fall back to GFBuilder + for unsupported_key in UNSUPPORTED: + if self.config.get(unsupported_key): + self.logger.error( + "%s configuration parameter not supported by ninja builder, " + "falling back to classic GFBuilder" % unsupported_key + ) + raise NotImplementedError() + self.w = Writer(open("build.ninja", "w")) self.setup_rules() self.get_designspaces() @@ -237,25 +248,9 @@ def post_process_ttf(self, filename): ) def build_vtt(self, font_dir): + # This should be an external gftool raise NotImplementedError - # for font, vtt_source in self.config['vttSources'].items(): - # if font not in os.listdir(font_dir): - # continue - # self.logger.debug(f"Compiling hint file {vtt_source} into {font}") - # font_path = os.path.join(font_dir, font) - # font = TTFont(font_path) - # merge_vtt_hinting(font, vtt_source, keep_cvar=True) - # compile_vtt_hinting(font, ship=True) - - # # Add a gasp table which is optimised for VTT hinting - # # https://googlefonts.github.io/how-to-hint-variable-fonts/ - # gasp_tbl = newTable("gasp") - # gasp_tbl.gaspRange = {8: 10, 65535: 15} - # gasp_tbl.version = 1 - # font['gasp'] = gasp_tbl - # font.save(font.reader.file.name) - if __name__ == "__main__": NinjaBuilder(sys.argv[1]).build() diff --git a/bin/gftools-builder.py b/bin/gftools-builder.py index 9ea2fb690..3eae4bda0 100755 --- a/bin/gftools-builder.py +++ b/bin/gftools-builder.py @@ -20,6 +20,14 @@ from gftools.builder import GFBuilder from gftools.builder import __doc__ as GFBuilder_doc +try: + from gftools.builder.ninja import NinjaBuilder + + builder_class = NinjaBuilder +except ImportError as e: + builder_class = GFBuilder + + parser = argparse.ArgumentParser( description=("Build a font family"), @@ -79,14 +87,16 @@ if len(args.file) == 1 and ( args.file[0].endswith(".yaml") or args.file[0].endswith(".yml") ): - builder = GFBuilder(configfile=args.file[0]) + builder_args = dict(configfile=args.file[0]) else: config={"sources": args.file} if args.stylespace: config["stylespaceFile"] = args.stylespace if args.family_name: config["familyName"] = args.family_name - builder = GFBuilder(config=config) + builder_args = dict(config=config) + +builder = builder_class(**builder_args) if args.no_autohint: builder.config["autohintTTF"] = False @@ -106,4 +116,8 @@ fp.write(yaml.dump(config, Dumper=yaml.SafeDumper)) sys.exit() -builder.build() +try: + builder.build() +except NotImplementedError: + builder = GFBuilder(**builder_args) + builder.build() From 3896c35e8754be900e2c855429e66e795bc84773 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Mon, 6 Jun 2022 12:15:08 +0100 Subject: [PATCH 06/37] Add ninja extras_require --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 6227cf44b..c50463c25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,6 +66,7 @@ qa = [ "fontbakery", "diffenator2>=0.2.0" ] +ninja = [ "ninja" ] [project.scripts] gftools = "gftools.scripts:main" From b697b2fa0e6bfbe61600fcc74083a0dc0fbdec71 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Mon, 6 Jun 2022 12:16:22 +0100 Subject: [PATCH 07/37] madig nit --- Lib/gftools/builder/ninja.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 9ba2b04a2..ca8dc3c57 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -222,7 +222,7 @@ def build_a_static_format(self, format, directory, postprocessor): self.w.comment(f" {path}") for instance in designspace.instances: ufo = Path(instance.filename.replace("instance_ufos", "instance_ufo")) - target = str(Path(target_dir) / (ufo.stem + "." + format)) + target = str(Path(target_dir) / ufo.with_suffix(f".{format}").name) self.w.build(target, "build" + format, str(ufo)) targets.append(target) self.w.newline() From fb9ff7613841006f65f2fced20ca207738b3c45b Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Tue, 14 Jun 2022 22:03:39 +0100 Subject: [PATCH 08/37] Speed up initial ninja.build creation --- Lib/gftools/builder/ninja.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index ca8dc3c57..25a8d5e33 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -2,6 +2,7 @@ from ninja.ninja_syntax import Writer import ninja import glyphsLib +from glyphsLib.builder.builders import UFOBuilder import sys import ufoLib2 import os @@ -89,14 +90,13 @@ def get_designspaces(self): self.designspaces = [] for source in self.config["sources"]: if source.endswith(".glyphs"): - # Do the conversion once, so we know what the instances and filenames are - designspace = glyphsLib.to_designspace( - glyphsLib.GSFont(source), - ufo_module=ufoLib2, - generate_GDEF=True, - store_editor_state=False, - minimal=True, - ) + builder = UFOBuilder(glyphsLib.GSFont(source)) + # This is a sneaky way of skipping the hard work of + # converting all the glyphs and stuff, and just gettting + # a minimal designspace + builder.to_ufo_groups = builder.to_ufo_kerning = builder.to_ufo_layers = lambda: True + + designspace = builder.designspace designspace_path = os.path.join("master_ufo", designspace.filename) os.makedirs(os.path.dirname(designspace_path), exist_ok=True) designspace.write(designspace_path) From e90cd24d321d082fd75e80f94f5a7874b476137b Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Tue, 14 Jun 2022 22:45:56 +0100 Subject: [PATCH 09/37] Maintain instance UFO filenames (because fontmake does) --- Lib/gftools/builder/ninja.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 25a8d5e33..6ccf499e7 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -197,7 +197,7 @@ def build_static(self): self.w.comment(f" Interpolate UFOs for {os.path.basename(path)}") self.w.build( [ - instance.filename.replace("instance_ufos", "instance_ufo") + instance.filename for instance in designspace.instances ], "instanceufo", @@ -221,7 +221,7 @@ def build_a_static_format(self, format, directory, postprocessor): for (path, designspace) in self.designspaces: self.w.comment(f" {path}") for instance in designspace.instances: - ufo = Path(instance.filename.replace("instance_ufos", "instance_ufo")) + ufo = Path(path).parent / Path(instance.filename) target = str(Path(target_dir) / ufo.with_suffix(f".{format}").name) self.w.build(target, "build" + format, str(ufo)) targets.append(target) From fd4cd13e13c519157dd0083a172858643edb9688 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Wed, 15 Jun 2022 09:30:39 +0100 Subject: [PATCH 10/37] Skip a few more stages --- Lib/gftools/builder/ninja.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 6ccf499e7..be62e9d92 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -94,7 +94,9 @@ def get_designspaces(self): # This is a sneaky way of skipping the hard work of # converting all the glyphs and stuff, and just gettting # a minimal designspace - builder.to_ufo_groups = builder.to_ufo_kerning = builder.to_ufo_layers = lambda: True + builder.to_ufo_groups = ( + builder.to_ufo_kerning + ) = builder.to_ufo_layers = lambda: True designspace = builder.designspace designspace_path = os.path.join("master_ufo", designspace.filename) From 8463706774d825d769a05a6f2e5a823f34742526 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Wed, 15 Jun 2022 09:30:52 +0100 Subject: [PATCH 11/37] Munge UFO paths --- Lib/gftools/builder/ninja.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index be62e9d92..43eb7c940 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -103,7 +103,15 @@ def get_designspaces(self): os.makedirs(os.path.dirname(designspace_path), exist_ok=True) designspace.write(designspace_path) self.w.comment("Convert glyphs source to designspace") - self.w.build(designspace_path, "glyphs2ufo", source) + designspace_and_ufos = [designspace_path] + list( + set( + [ + os.path.join("master_ufo", m.filename) + for m in designspace.sources + ] + ) + ) + self.w.build(designspace_and_ufos, "glyphs2ufo", source) else: designspace_path = source designspace = DesignSpaceDocument.fromfile(designspace_path) @@ -190,6 +198,17 @@ def post_process(self, file, implicit=None): file + ".fixstamp", "fix", file, implicit=implicit, variables=variables ) + def _instance_ufo_filenames(self, path, designspace): + instance_filenames = [] + for instance in designspace.instances: + fn = instance.filename.replace("instance_ufos/", "instance_ufo/") + if "/" in fn: + ufo = Path(fn) + else: + ufo = Path(path).parent / fn + instance_filenames.append(ufo) + return instance_filenames + def build_static(self): # Let's make our interpolated UFOs. self.w.newline() @@ -197,11 +216,9 @@ def build_static(self): self.w.newline() for (path, designspace) in self.designspaces: self.w.comment(f" Interpolate UFOs for {os.path.basename(path)}") + self.w.build( - [ - instance.filename - for instance in designspace.instances - ], + [str(i) for i in self._instance_ufo_filenames(path, designspace)], "instanceufo", path, ) @@ -222,8 +239,7 @@ def build_a_static_format(self, format, directory, postprocessor): targets = [] for (path, designspace) in self.designspaces: self.w.comment(f" {path}") - for instance in designspace.instances: - ufo = Path(path).parent / Path(instance.filename) + for ufo in self._instance_ufo_filenames(path, designspace): target = str(Path(target_dir) / ufo.with_suffix(f".{format}").name) self.w.build(target, "build" + format, str(ufo)) targets.append(target) From 1e95e60bc4ce55ff834d8c39c23fcbf76e686ae6 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Wed, 15 Jun 2022 11:05:54 +0100 Subject: [PATCH 12/37] Clean up temporaries --- Lib/gftools/builder/ninja.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 43eb7c940..25e045bb2 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -25,6 +25,7 @@ def build(self): raise NotImplementedError() self.w = Writer(open("build.ninja", "w")) + self.temporaries = [] self.setup_rules() self.get_designspaces() @@ -41,6 +42,10 @@ def build(self): ninja._program("ninja", []) + # Tidy up stamp files + for temporary in self.temporaries: + os.remove(temporary) + def setup_rules(self): self.w.comment("Rules") self.w.newline() @@ -177,6 +182,7 @@ def gen_stat(self, axis_tags, targets): ) # Because gftools-gen-stat doesn't seem to support it? stampfile = targets[0] + ".statstamp" + self.temporaries.append(stampfile) self.w.build( stampfile, "genstat", @@ -194,6 +200,7 @@ def post_process(self, file, implicit=None): variables = {} if self.config["includeSourceFixes"]: variables = ({"fixargs": "--include-source-fixes"},) + self.temporaries.append(file + ".fixstamp") self.w.build( file + ".fixstamp", "fix", file, implicit=implicit, variables=variables ) @@ -254,6 +261,7 @@ def post_process_ttf(self, filename): if self.config["ttfaUseScript"]: raise NotImplementedError("ttaUseScript not supported in ninja mode") self.w.build(filename + ".autohintstamp", "autohint", filename) + self.temporaries.append(filename + ".autohintstamp") self.post_process(filename, implicit=filename + ".autohintstamp") else: self.post_process(filename) From bc02a78d96ad710e4c7fa647a2baf70bfe1a9920 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 16 Jun 2022 13:47:47 +0100 Subject: [PATCH 13/37] Fix weird syntax --- Lib/gftools/builder/ninja.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 25e045bb2..de2a55d66 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -199,7 +199,7 @@ def gen_stat(self, axis_tags, targets): def post_process(self, file, implicit=None): variables = {} if self.config["includeSourceFixes"]: - variables = ({"fixargs": "--include-source-fixes"},) + variables = {"fixargs": "--include-source-fixes"} self.temporaries.append(file + ".fixstamp") self.w.build( file + ".fixstamp", "fix", file, implicit=implicit, variables=variables From e672efcd9f6f952578f7b136ba3ffbda80be90b1 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 16 Jun 2022 14:21:56 +0100 Subject: [PATCH 14/37] OK, just take their word for it --- Lib/gftools/builder/ninja.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index de2a55d66..ecfaf94bb 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -208,7 +208,7 @@ def post_process(self, file, implicit=None): def _instance_ufo_filenames(self, path, designspace): instance_filenames = [] for instance in designspace.instances: - fn = instance.filename.replace("instance_ufos/", "instance_ufo/") + fn = instance.filename if "/" in fn: ufo = Path(fn) else: From 8cc95c1597e9d81a690e2deee1c3fc476d7e9489 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Fri, 17 Jun 2022 06:31:49 +0100 Subject: [PATCH 15/37] Pass fontmake args to fontmake! --- Lib/gftools/builder/ninja.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index ecfaf94bb..eadd754ec 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -248,7 +248,9 @@ def build_a_static_format(self, format, directory, postprocessor): self.w.comment(f" {path}") for ufo in self._instance_ufo_filenames(path, designspace): target = str(Path(target_dir) / ufo.with_suffix(f".{format}").name) - self.w.build(target, "build" + format, str(ufo)) + self.w.build(target, "build" + format, str(ufo), variables={ + "fontmake_args": self.fontmake_args({"output_path": target}) + }) targets.append(target) self.w.newline() self.w.comment(f"Post-processing {format}s") From 447ace284320fbb02daa69aca56c05d2f5d9452a Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Wed, 29 Jun 2022 15:13:56 +0100 Subject: [PATCH 16/37] Fail harder when fixing/autohinting --- Lib/gftools/builder/ninja.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index eadd754ec..02cf6886f 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -72,18 +72,18 @@ def setup_rules(self): self.w.comment("Add a STAT table to a set of variable fonts") self.w.rule( "genstat", - "gftools-gen-stat.py --inplace $other_args --axis-order $axis_order -- $in ; touch $stampfile", + "gftools-gen-stat.py --inplace $other_args --axis-order $axis_order -- $in && touch $stampfile", ) self.w.comment("Run the font fixer in-place and touch a stamp file") self.w.rule( - "fix", "gftools-fix-font.py -o $in $fixargs $in; touch $in.fixstamp" + "fix", "gftools-fix-font.py -o $in $fixargs $in && touch $in.fixstamp" ) self.w.comment("Run the ttfautohint in-place and touch a stamp file") self.w.rule( "autohint", - "ttfautohint $in $in.autohinted; mv $in.autohinted $in; touch $in.autohintstamp", + "ttfautohint $in $in.autohinted && mv $in.autohinted $in && touch $in.autohintstamp", ) self.w.comment("Create a web font") From cda2860531fe356ddf71eb84b3b4a70d416f9ff4 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 30 Jun 2022 16:58:56 +0100 Subject: [PATCH 17/37] Run in serial when debugging --- Lib/gftools/builder/ninja.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 02cf6886f..f2a54dd96 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -40,7 +40,11 @@ def build(self): self.build_vtt(self.config["ttDir"]) self.w.close() - ninja._program("ninja", []) + ninja_args = [] + if self.config["logLevel"] == "DEBUG": + ninja_args = ["-v", "-j", "1"] + + ninja._program("ninja", ninja_args) # Tidy up stamp files for temporary in self.temporaries: From 5205454987d5b79e3161fb3d55744ea2e1bb342e Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 30 Jun 2022 17:26:18 +0100 Subject: [PATCH 18/37] Only tidy up stamps if they actually got created --- Lib/gftools/builder/ninja.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index f2a54dd96..f2668a9a6 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -48,7 +48,8 @@ def build(self): # Tidy up stamp files for temporary in self.temporaries: - os.remove(temporary) + if os.path.exists(temporary): + os.remove(temporary) def setup_rules(self): self.w.comment("Rules") From 6763b3ad4a983e8ba5f63d35ad454f8ae99c800c Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Fri, 1 Jul 2022 12:18:18 +0100 Subject: [PATCH 19/37] Propagate exit code --- Lib/gftools/builder/ninja.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index f2668a9a6..0b8b1e01d 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -44,13 +44,16 @@ def build(self): if self.config["logLevel"] == "DEBUG": ninja_args = ["-v", "-j", "1"] - ninja._program("ninja", ninja_args) + errcode = ninja._program("ninja", ninja_args) # Tidy up stamp files for temporary in self.temporaries: if os.path.exists(temporary): os.remove(temporary) + if errcode: + sys.exit(errcode) + def setup_rules(self): self.w.comment("Rules") self.w.newline() From b1fb9947d8fe2359de82493ca64042f390eedd12 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Mon, 1 Aug 2022 17:48:01 +0100 Subject: [PATCH 20/37] Use the console pool when debugging --- Lib/gftools/builder/ninja.py | 39 +++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 0b8b1e01d..6597692ae 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -56,46 +56,60 @@ def build(self): def setup_rules(self): self.w.comment("Rules") + if self.config["logLevel"] == "DEBUG": + args = {"pool": "console"} + else: + args = {} self.w.newline() self.w.comment("Convert glyphs file to UFO") - self.w.rule("glyphs2ufo", "fontmake -o ufo -g $in") + self.w.rule("glyphs2ufo", "fontmake -o ufo -g $in", **args) if self.config["buildVariable"]: self.w.comment("Build a variable font from Designspace") - self.w.rule("variable", "fontmake -o variable -m $in $fontmake_args") + self.w.rule( + "variable", "fontmake -o variable -m $in $fontmake_args", **args + ) self.w.comment("Build a set of instance UFOs from Designspace") - self.w.rule("instanceufo", "fontmake -i -o ufo -m $in $fontmake_args") + self.w.rule("instanceufo", "fontmake -i -o ufo -m $in $fontmake_args", **args) self.w.comment("Build a TTF file from a UFO") self.w.rule( - "buildttf", "fontmake -o ttf -u $in $fontmake_args --output-path $out" + "buildttf", + "fontmake -o ttf -u $in $fontmake_args --output-path $out", + **args, ) self.w.comment("Build an OTF file from a UFO") self.w.rule( - "buildotf", "fontmake -o otf -u $in $fontmake_args --output-path $out" + "buildotf", + "fontmake -o otf -u $in $fontmake_args --output-path $out", + **args, ) self.w.comment("Add a STAT table to a set of variable fonts") self.w.rule( "genstat", "gftools-gen-stat.py --inplace $other_args --axis-order $axis_order -- $in && touch $stampfile", + **args, ) self.w.comment("Run the font fixer in-place and touch a stamp file") self.w.rule( - "fix", "gftools-fix-font.py -o $in $fixargs $in && touch $in.fixstamp" + "fix", + "gftools-fix-font.py -o $in $fixargs $in && touch $in.fixstamp", + **args, ) self.w.comment("Run the ttfautohint in-place and touch a stamp file") self.w.rule( "autohint", "ttfautohint $in $in.autohinted && mv $in.autohinted $in && touch $in.autohintstamp", + **args, ) self.w.comment("Create a web font") - self.w.rule("webfont", f"fonttools ttLib.woff2 compress -o $out $in") + self.w.rule("webfont", f"fonttools ttLib.woff2 compress -o $out $in", **args) self.w.newline() @@ -256,9 +270,14 @@ def build_a_static_format(self, format, directory, postprocessor): self.w.comment(f" {path}") for ufo in self._instance_ufo_filenames(path, designspace): target = str(Path(target_dir) / ufo.with_suffix(f".{format}").name) - self.w.build(target, "build" + format, str(ufo), variables={ - "fontmake_args": self.fontmake_args({"output_path": target}) - }) + self.w.build( + target, + "build" + format, + str(ufo), + variables={ + "fontmake_args": self.fontmake_args({"output_path": target}) + }, + ) targets.append(target) self.w.newline() self.w.comment(f"Post-processing {format}s") From 6cf36a222c606813c8e722a806e78daca3c6377e Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Mon, 10 Oct 2022 06:31:54 +0100 Subject: [PATCH 21/37] Axis order no long required for gen-stat --- Lib/gftools/builder/ninja.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 6597692ae..0c06c282c 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -90,7 +90,7 @@ def setup_rules(self): self.w.comment("Add a STAT table to a set of variable fonts") self.w.rule( "genstat", - "gftools-gen-stat.py --inplace $other_args --axis-order $axis_order -- $in && touch $stampfile", + "gftools-gen-stat.py --inplace $other_args -- $in && touch $stampfile", **args, ) @@ -210,7 +210,6 @@ def gen_stat(self, axis_tags, targets): "genstat", targets, variables={ - "axis_order": self.config["axisOrder"], "other_args": other_args, "stampfile": stampfile, }, From 08d7279585b00d8af8aa7d377027f40595d1bb96 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Fri, 30 Dec 2022 10:27:39 +0000 Subject: [PATCH 22/37] Don't explode if there are no instances defined --- Lib/gftools/builder/ninja.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 0c06c282c..d2a467ca2 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -244,9 +244,12 @@ def build_static(self): self.w.newline() for (path, designspace) in self.designspaces: self.w.comment(f" Interpolate UFOs for {os.path.basename(path)}") + instances = self._instance_ufo_filenames(path, designspace) + if not instances: + continue self.w.build( - [str(i) for i in self._instance_ufo_filenames(path, designspace)], + [str(i) for i in instances], "instanceufo", path, ) From e006f2bf4b68b6fa523ef92fede2effd77fd8701 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Fri, 20 Jan 2023 11:49:05 +0000 Subject: [PATCH 23/37] Fix instance UFO directory --- Lib/gftools/builder/ninja.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index d2a467ca2..17e4aa51d 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -62,7 +62,7 @@ def setup_rules(self): args = {} self.w.newline() self.w.comment("Convert glyphs file to UFO") - self.w.rule("glyphs2ufo", "fontmake -o ufo -g $in", **args) + self.w.rule("glyphs2ufo", "fontmake -o ufo --instance-dir instance_ufo -g $in", **args) if self.config["buildVariable"]: self.w.comment("Build a variable font from Designspace") From 1510d4f058e4b8e47a87f7db1f17302df2f596e7 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Fri, 20 Jan 2023 11:49:12 +0000 Subject: [PATCH 24/37] Support glyphs packages --- Lib/gftools/builder/ninja.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 17e4aa51d..69614edbf 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -116,8 +116,8 @@ def setup_rules(self): def get_designspaces(self): self.designspaces = [] for source in self.config["sources"]: - if source.endswith(".glyphs"): - builder = UFOBuilder(glyphsLib.GSFont(source)) + if source.endswith(".glyphs") or source.endswith(".glyphspackage"): + builder = UFOBuilder(glyphsLib.GSFont(source), instance_dir="instance_ufo") # This is a sneaky way of skipping the hard work of # converting all the glyphs and stuff, and just gettting # a minimal designspace From 51c014e265f5f1f323ad8ebdc569bf7d0c516cda Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Fri, 20 Jan 2023 11:49:20 +0000 Subject: [PATCH 25/37] Use new style post-process methods --- Lib/gftools/builder/ninja.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 69614edbf..9bafcb0fc 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -287,7 +287,7 @@ def build_a_static_format(self, format, directory, postprocessor): postprocessor(t) self.w.newline() - def post_process_ttf(self, filename): + def post_process_static_ttf(self, filename): if self.config["autohintTTF"]: if self.config["ttfaUseScript"]: raise NotImplementedError("ttaUseScript not supported in ninja mode") @@ -304,6 +304,16 @@ def post_process_ttf(self, filename): webfont_filename, "webfont", filename, implicit=filename + ".fixstamp" ) + def post_process_variable(self, filename): + self.post_process(filename) + if self.config["buildWebfont"]: + webfont_filename = filename.replace(".ttf", ".woff2").replace( + self.config["ttDir"], self.config["woffDir"] + ) + self.w.build( + webfont_filename, "webfont", filename, implicit=filename + ".fixstamp" + ) + def build_vtt(self, font_dir): # This should be an external gftool raise NotImplementedError From 9b4c0b0c27f467d06580ec4966c63ce78314b165 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Fri, 20 Jan 2023 11:35:23 +0000 Subject: [PATCH 26/37] Save STAT table config to yaml and pass as file to genstat --- Lib/gftools/builder/ninja.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 9bafcb0fc..4f37927f8 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -4,7 +4,7 @@ import glyphsLib from glyphsLib.builder.builders import UFOBuilder import sys -import ufoLib2 +import yaml import os from gftools.builder import GFBuilder from fontTools.designspaceLib import DesignSpaceDocument @@ -196,14 +196,16 @@ def gen_stat(self, axis_tags, targets): if any("italic" in x[0].lower() for x in self.designspaces): self.config["axisOrder"].append("ital") other_args = "" + stampfile = targets[0] + ".statstamp" if "stat" in self.config: - other_args = f"--src {self.config['stat']}" + statfile = targets[0] + ".stat.yaml" + open(statfile, "w").write(yaml.dump(self.config["stat"])) + other_args = f"--src {statfile}" if "stylespaceFile" in self.config or "statFormat4" in self.config: raise ValueError( "Stylespace files / statFormat4 not supported in Ninja mode" ) # Because gftools-gen-stat doesn't seem to support it? - stampfile = targets[0] + ".statstamp" self.temporaries.append(stampfile) self.w.build( stampfile, From b8f9772116003ad568b53b8054bb68dfd13ac232 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Fri, 20 Jan 2023 12:06:01 +0000 Subject: [PATCH 27/37] Make sure the directory where we write the stat file exists --- Lib/gftools/builder/ninja.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 4f37927f8..982acccfd 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -199,6 +199,7 @@ def gen_stat(self, axis_tags, targets): stampfile = targets[0] + ".statstamp" if "stat" in self.config: statfile = targets[0] + ".stat.yaml" + os.makedirs(os.path.dirname(statfile), exist_ok=True) open(statfile, "w").write(yaml.dump(self.config["stat"])) other_args = f"--src {statfile}" if "stylespaceFile" in self.config or "statFormat4" in self.config: From 378eae51d93ef075556ea90e65ac0e7a3cfd4194 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 27 Jul 2023 09:53:37 +0100 Subject: [PATCH 28/37] Save and restore cwd --- Lib/gftools/builder/ninja.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 982acccfd..0c94c17c8 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -14,6 +14,10 @@ class NinjaBuilder(GFBuilder): + def __init__(self, **kwargs): + self.old_cwd = os.getcwd() + super().__init__(**kwargs) + def build(self): # In some cases we want to fall back to GFBuilder for unsupported_key in UNSUPPORTED: @@ -22,6 +26,7 @@ def build(self): "%s configuration parameter not supported by ninja builder, " "falling back to classic GFBuilder" % unsupported_key ) + os.chdir(self.old_cwd) raise NotImplementedError() self.w = Writer(open("build.ninja", "w")) From 83a7a453407c4935145cfac15f0250462e7028a3 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 27 Jul 2023 09:53:53 +0100 Subject: [PATCH 29/37] Use new utility file names --- Lib/gftools/builder/ninja.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 0c94c17c8..09c2eeea4 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -95,14 +95,14 @@ def setup_rules(self): self.w.comment("Add a STAT table to a set of variable fonts") self.w.rule( "genstat", - "gftools-gen-stat.py --inplace $other_args -- $in && touch $stampfile", + "gftools-gen-stat --inplace $other_args -- $in && touch $stampfile", **args, ) self.w.comment("Run the font fixer in-place and touch a stamp file") self.w.rule( "fix", - "gftools-fix-font.py -o $in $fixargs $in && touch $in.fixstamp", + "gftools-fix-font -o $in $fixargs $in && touch $in.fixstamp", **args, ) From f6003a569bb7c7c5c57f9f9d9f18c24429ace47d Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 27 Jul 2023 09:54:30 +0100 Subject: [PATCH 30/37] =?UTF-8?q?GSFont=20doesn=E2=80=99t=20deal=20with=20?= =?UTF-8?q?glyphspackages=20yet,=20use=20load=20instead?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/gftools/builder/ninja.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/ninja.py index 09c2eeea4..e04dceacc 100644 --- a/Lib/gftools/builder/ninja.py +++ b/Lib/gftools/builder/ninja.py @@ -122,7 +122,7 @@ def get_designspaces(self): self.designspaces = [] for source in self.config["sources"]: if source.endswith(".glyphs") or source.endswith(".glyphspackage"): - builder = UFOBuilder(glyphsLib.GSFont(source), instance_dir="instance_ufo") + builder = UFOBuilder(glyphsLib.load(source), instance_dir="instance_ufo") # This is a sneaky way of skipping the hard work of # converting all the glyphs and stuff, and just gettting # a minimal designspace From ba4f7b88eb6f97f6e4f4cef9bb0359963c6c9e58 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 27 Jul 2023 10:09:23 +0100 Subject: [PATCH 31/37] Plug ninja build into builder Module needs renaming to stop "import ninja" importing itself, for some odd reason I don't understand --- Lib/gftools/builder/__init__.py | 19 ++++++++++++++++--- Lib/gftools/builder/{ninja.py => _ninja.py} | 0 2 files changed, 16 insertions(+), 3 deletions(-) rename Lib/gftools/builder/{ninja.py => _ninja.py} (100%) diff --git a/Lib/gftools/builder/__init__.py b/Lib/gftools/builder/__init__.py index 31492bd55..a275d6adb 100644 --- a/Lib/gftools/builder/__init__.py +++ b/Lib/gftools/builder/__init__.py @@ -642,17 +642,26 @@ def main(args=None): args = parser.parse_args(args) + from gftools.builder._ninja import NinjaBuilder + + try: + builder_class = NinjaBuilder + except ImportError as e: + builder_class = GFBuilder + if len(args.file) == 1 and ( args.file[0].endswith(".yaml") or args.file[0].endswith(".yml") ): - builder = GFBuilder(configfile=args.file[0]) + builder_args = dict(configfile=args.file[0]) else: config={"sources": args.file} if args.stylespace: config["stylespaceFile"] = args.stylespace if args.family_name: config["familyName"] = args.family_name - builder = GFBuilder(config=config) + builder_args = dict(config=config) + + builder = builder_class(**builder_args) if args.no_autohint: builder.config["autohintTTF"] = False @@ -672,7 +681,11 @@ def main(args=None): fp.write(yaml.dump(config, Dumper=yaml.SafeDumper)) sys.exit() - builder.build() + try: + builder.build() + except NotImplementedError: + builder = GFBuilder(**builder_args) + builder.build() if __name__ == "__main__": main() diff --git a/Lib/gftools/builder/ninja.py b/Lib/gftools/builder/_ninja.py similarity index 100% rename from Lib/gftools/builder/ninja.py rename to Lib/gftools/builder/_ninja.py From 4b37d5c12785737d46e880f93838a86b7fcb5e7d Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 27 Jul 2023 10:10:16 +0100 Subject: [PATCH 32/37] No longer needed, pyproject installs binary from lib --- bin/gftools-builder.py | 123 ----------------------------------------- 1 file changed, 123 deletions(-) delete mode 100755 bin/gftools-builder.py diff --git a/bin/gftools-builder.py b/bin/gftools-builder.py deleted file mode 100755 index 3eae4bda0..000000000 --- a/bin/gftools-builder.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2020 The Google Font Tools Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import argparse -import os -import sys - -from gftools.builder import GFBuilder -from gftools.builder import __doc__ as GFBuilder_doc - -try: - from gftools.builder.ninja import NinjaBuilder - - builder_class = NinjaBuilder -except ImportError as e: - builder_class = GFBuilder - - - -parser = argparse.ArgumentParser( - description=("Build a font family"), - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog="#"*79 + "\n" + GFBuilder_doc, -) -parser.add_argument( - "--debug", - action="store_true", - default=False, - help="Show extra debugging information", -) -parser.add_argument("--family-name", help="Font family name") -parser.add_argument( - "--no-autohint", - action="store_true", - default=False, - help="Don't run ttfautohint on static TTFs", -) -parser.add_argument("--stylespace", help="Path to a statmake stylespace file") - -parser.add_argument( - "--no-clean-up", - action="store_true", - default=False, - help="Do not remove temporary files (instance_ufos/)") - -parser.add_argument("file", nargs="*", help="YAML build config file *or* source files") - -parser.add_argument("--dump-config", type=str, help="Config file to generate") - -args = parser.parse_args() - -if len(args.file) == 0: - # Try a few places to find one. - possible_config_files = list(filter(os.path.exists, [ - "sources/builder.yaml", - "sources/builder.yml", - "sources/config.yaml", - "sources/config.yml", - "builder.yaml", - "builder.yml", - "config.yaml", - "config.yml", - ])) - if len(possible_config_files) > 0: - args.file = [possible_config_files[0]] - print(f""" -No config file specified; found a potential one in {args.file[0]}. -If this is not correct, please specify a config file manually. -""", file=sys.stderr) - else: - parser.print_usage() - print("No config file specified") - sys.exit(1) - -if len(args.file) == 1 and ( - args.file[0].endswith(".yaml") or args.file[0].endswith(".yml") -): - builder_args = dict(configfile=args.file[0]) -else: - config={"sources": args.file} - if args.stylespace: - config["stylespaceFile"] = args.stylespace - if args.family_name: - config["familyName"] = args.family_name - builder_args = dict(config=config) - -builder = builder_class(**builder_args) - -if args.no_autohint: - builder.config["autohintTTF"] = False - -if args.no_clean_up: - builder.config["cleanUp"] = False - -if args.debug: - builder.config["logLevel"] = "DEBUG" - -if args.dump_config: - import sys - import yaml - - with open(args.dump_config, "w") as fp: - config= {k: v for (k, v) in builder.config.items() if v is not None} - fp.write(yaml.dump(config, Dumper=yaml.SafeDumper)) - sys.exit() - -try: - builder.build() -except NotImplementedError: - builder = GFBuilder(**builder_args) - builder.build() From c8aa87f6252bd27c155d93e1585a422403dac549 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 27 Jul 2023 10:49:17 +0100 Subject: [PATCH 33/37] Post-process variable fonts, fix webfont directory --- Lib/gftools/builder/_ninja.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/gftools/builder/_ninja.py b/Lib/gftools/builder/_ninja.py index e04dceacc..9acb3db04 100644 --- a/Lib/gftools/builder/_ninja.py +++ b/Lib/gftools/builder/_ninja.py @@ -190,7 +190,7 @@ def build_variable(self): # because these tables are needed in order to fix the name tables. self.w.comment("Variable font post-processing") for t in targets: - self.post_process(t, implicit=stampfile) + self.post_process_variable(t, implicit=stampfile) def gen_stat(self, axis_tags, targets): self.w.comment("Generate STAT tables") @@ -312,11 +312,11 @@ def post_process_static_ttf(self, filename): webfont_filename, "webfont", filename, implicit=filename + ".fixstamp" ) - def post_process_variable(self, filename): - self.post_process(filename) + def post_process_variable(self, filename, implicit=None): + self.post_process(filename, implicit=implicit) if self.config["buildWebfont"]: webfont_filename = filename.replace(".ttf", ".woff2").replace( - self.config["ttDir"], self.config["woffDir"] + self.config["vfDir"], self.config["woffDir"] ) self.w.build( webfont_filename, "webfont", filename, implicit=filename + ".fixstamp" From 37e038ad8622e0adb630603df6c48d26bf1d863e Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 27 Jul 2023 10:49:26 +0100 Subject: [PATCH 34/37] Wrap bare UFOs in mock designspace --- Lib/gftools/builder/_ninja.py | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/Lib/gftools/builder/_ninja.py b/Lib/gftools/builder/_ninja.py index 9acb3db04..805300e29 100644 --- a/Lib/gftools/builder/_ninja.py +++ b/Lib/gftools/builder/_ninja.py @@ -4,10 +4,15 @@ import glyphsLib from glyphsLib.builder.builders import UFOBuilder import sys +import ufoLib2 import yaml import os from gftools.builder import GFBuilder -from fontTools.designspaceLib import DesignSpaceDocument +from fontTools.designspaceLib import ( + DesignSpaceDocument, + SourceDescriptor, + InstanceDescriptor, +) from pathlib import Path UNSUPPORTED = ["stylespaceFile", "statFormat4", "ttfaUseScript", "vttSources"] @@ -67,7 +72,9 @@ def setup_rules(self): args = {} self.w.newline() self.w.comment("Convert glyphs file to UFO") - self.w.rule("glyphs2ufo", "fontmake -o ufo --instance-dir instance_ufo -g $in", **args) + self.w.rule( + "glyphs2ufo", "fontmake -o ufo --instance-dir instance_ufo -g $in", **args + ) if self.config["buildVariable"]: self.w.comment("Build a variable font from Designspace") @@ -122,7 +129,9 @@ def get_designspaces(self): self.designspaces = [] for source in self.config["sources"]: if source.endswith(".glyphs") or source.endswith(".glyphspackage"): - builder = UFOBuilder(glyphsLib.load(source), instance_dir="instance_ufo") + builder = UFOBuilder( + glyphsLib.load(source), instance_dir="instance_ufo" + ) # This is a sneaky way of skipping the hard work of # converting all the glyphs and stuff, and just gettting # a minimal designspace @@ -144,6 +153,26 @@ def get_designspaces(self): ) ) self.w.build(designspace_and_ufos, "glyphs2ufo", source) + elif source.endswith(".ufo"): + # Wrap this in a basic designspace + designspace_path = source.replace(".ufo", ".designspace") + ufo = ufoLib2.Font.open(source) + + designspace = DesignSpaceDocument() + source_descriptor = SourceDescriptor() + source_descriptor.path = source + + instance = InstanceDescriptor() + instance.styleName = ufo.info.styleName + instance.familyName = ufo.info.familyName + instance.path = os.path.join( + "instance_ttf", + ufo.info.familyName + "-" + ufo.info.styleName + ".ufo", + ).replace(" ", "") + + designspace.addSource(source_descriptor) + designspace.addInstance(instance) + designspace.write(designspace_path) else: designspace_path = source designspace = DesignSpaceDocument.fromfile(designspace_path) From ed6ff51d266328281924b54c72cbf84fde46d039 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 27 Jul 2023 12:02:08 +0100 Subject: [PATCH 35/37] Don't try ninja builder on Windows --- Lib/gftools/builder/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/gftools/builder/__init__.py b/Lib/gftools/builder/__init__.py index a275d6adb..e82fd4791 100644 --- a/Lib/gftools/builder/__init__.py +++ b/Lib/gftools/builder/__init__.py @@ -139,6 +139,7 @@ import argparse import logging import os +import platform import re import shutil import statmake.classes @@ -643,11 +644,13 @@ def main(args=None): args = parser.parse_args(args) from gftools.builder._ninja import NinjaBuilder + builder_class = GFBuilder try: - builder_class = NinjaBuilder + if platform.system() != "Windows": + builder_class = NinjaBuilder except ImportError as e: - builder_class = GFBuilder + pass if len(args.file) == 1 and ( args.file[0].endswith(".yaml") or args.file[0].endswith(".yml") From 3f480e830e0f51db3fed2e1d4b49945cdc2ba4e6 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Fri, 28 Jul 2023 16:03:50 +0100 Subject: [PATCH 36/37] Only import NinjaBuilder on non-Windows --- Lib/gftools/builder/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/gftools/builder/__init__.py b/Lib/gftools/builder/__init__.py index e82fd4791..182dadea4 100644 --- a/Lib/gftools/builder/__init__.py +++ b/Lib/gftools/builder/__init__.py @@ -643,11 +643,12 @@ def main(args=None): args = parser.parse_args(args) - from gftools.builder._ninja import NinjaBuilder builder_class = GFBuilder try: if platform.system() != "Windows": + from gftools.builder._ninja import NinjaBuilder + builder_class = NinjaBuilder except ImportError as e: pass From 94a49689d4d8b78cf4f7a60235623a63b0dd06dc Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Thu, 31 Aug 2023 17:31:19 +0100 Subject: [PATCH 37/37] Use correct instance directory --- Lib/gftools/builder/_ninja.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Lib/gftools/builder/_ninja.py b/Lib/gftools/builder/_ninja.py index 805300e29..756e821df 100644 --- a/Lib/gftools/builder/_ninja.py +++ b/Lib/gftools/builder/_ninja.py @@ -130,7 +130,7 @@ def get_designspaces(self): for source in self.config["sources"]: if source.endswith(".glyphs") or source.endswith(".glyphspackage"): builder = UFOBuilder( - glyphsLib.load(source), instance_dir="instance_ufo" + glyphsLib.load(source), instance_dir="../instance_ufo" ) # This is a sneaky way of skipping the hard work of # converting all the glyphs and stuff, and just gettting @@ -267,10 +267,7 @@ def _instance_ufo_filenames(self, path, designspace): instance_filenames = [] for instance in designspace.instances: fn = instance.filename - if "/" in fn: - ufo = Path(fn) - else: - ufo = Path(path).parent / fn + ufo = Path(path).parent / fn instance_filenames.append(ufo) return instance_filenames