diff --git a/Lib/fontmake/__main__.py b/Lib/fontmake/__main__.py index aa642131..a3bde8f3 100644 --- a/Lib/fontmake/__main__.py +++ b/Lib/fontmake/__main__.py @@ -380,6 +380,13 @@ def main(args=None): "interpolating. Use if you share feature files of masters in " "external files, as instances can end up elsewhere.", ) + outputGroup.add_argument( + "--fea-include-dir", + default=None, + help="Overrides the default directory where to search for included " + "feature files with relative paths. This only works when the input is a " + "Designspace or UFOs, not from Glyphs at the moment.", + ) outputGroup.add_argument( "--no-generate-GDEF", dest="generate_GDEF", @@ -641,6 +648,10 @@ def main(args=None): project = FontProject(validate_ufo=args.pop("validate_ufo")) if inputs.glyphs_path: + # we don't support customizing include directory for .glyphs input + # for Glyphs.app does not either. + exclude_args(parser, args, ["fea_include_dir"], inputs.format_name) + with _make_tempdirs(parser, args): project.run_from_glyphs(inputs.glyphs_path, **args) return diff --git a/Lib/fontmake/font_project.py b/Lib/fontmake/font_project.py index 2be5b0f2..30ce6a02 100644 --- a/Lib/fontmake/font_project.py +++ b/Lib/fontmake/font_project.py @@ -295,6 +295,7 @@ def _build_interpolatable_masters( feature_writers=None, cff_round_tolerance=None, debug_feature_file=None, + fea_include_dir=None, flatten_components=False, filters=None, **kwargs, @@ -307,6 +308,7 @@ def _build_interpolatable_masters( cubicConversionError=conversion_error, featureWriters=feature_writers, debugFeatureFile=debug_feature_file, + feaIncludeDir=fea_include_dir, filters=filters, flattenComponents=flatten_components, inplace=True, @@ -318,6 +320,7 @@ def _build_interpolatable_masters( roundTolerance=cff_round_tolerance, featureWriters=feature_writers, debugFeatureFile=debug_feature_file, + feaIncludeDir=fea_include_dir, filters=filters, inplace=True, ) @@ -349,6 +352,7 @@ def build_variable_fonts( feature_writers=None, cff_round_tolerance=None, debug_feature_file=None, + fea_include_dir=None, flatten_components=False, filters=None, **kwargs, @@ -408,6 +412,7 @@ def build_variable_fonts( optimizeGvar=optimize_gvar, flattenComponents=flatten_components, debugFeatureFile=debug_feature_file, + feaIncludeDir=fea_include_dir, filters=filters, inplace=True, variableFontNames=list(vf_name_to_output_path), @@ -419,6 +424,7 @@ def build_variable_fonts( useProductionNames=use_production_names, roundTolerance=cff_round_tolerance, debugFeatureFile=debug_feature_file, + feaIncludeDir=fea_include_dir, optimizeCFF=optimize_cff, filters=filters, inplace=True, @@ -486,6 +492,7 @@ def save_otfs( flatten_components=False, filters=None, generate_GDEF=True, + fea_include_dir=None, ): """Build OpenType binaries from UFOs. @@ -588,6 +595,7 @@ def save_otfs( cubicConversionError=conversion_error, featureWriters=feature_writers, debugFeatureFile=debug_feature_file, + feaIncludeDir=fea_include_dir, cffVersion=cff_version, subroutinizer=subroutinizer, flattenComponents=flatten_components, @@ -801,8 +809,14 @@ def run_from_glyphs( generate_GDEF=generate_GDEF, ufo_structure=kwargs.get("ufo_structure"), ) + # 'include' statements in features.fea should be resolved relative to + # the input .glyphs path, like Glyphs.app would do, and not relative + # to the UFOs that are generated by glyphsLib. + fea_include_dir = os.path.dirname(glyphs_path) try: - self.run_from_designspace(designspace_path, **kwargs) + self.run_from_designspace( + designspace_path, fea_include_dir=fea_include_dir, **kwargs + ) except FontmakeError as e: e.source_trail.append(glyphs_path) raise @@ -813,6 +827,7 @@ def interpolate_instance_ufos( include=None, round_instances=False, expand_features_to_instances=False, + fea_include_dir=None, ufo_structure="package", ): """Interpolate master UFOs with Instantiator and return instance UFOs. @@ -857,7 +872,9 @@ def interpolate_instance_ufos( if expand_features_to_instances: logger.debug("Expanding features to instance UFOs") - fea_txt = parseLayoutFeatures(subDoc.default.font).asFea() + fea_txt = parseLayoutFeatures( + subDoc.default.font, includeDir=fea_include_dir + ).asFea() generator = attr.evolve(generator, copy_feature_text=fea_txt) for instance in subDoc.instances: @@ -903,6 +920,7 @@ def interpolate_instance_ufos_mutatormath( include=None, round_instances=False, expand_features_to_instances=False, + fea_include_dir=None, ): """Interpolate master UFOs with MutatorMath and return instance UFOs. @@ -965,7 +983,9 @@ def interpolate_instance_ufos_mutatormath( raise ValueError("No source is designated as the master for features.") else: master_source_font = builder.sources[master_source.name][0] - master_source_features = parseLayoutFeatures(master_source_font).asFea() + master_source_features = parseLayoutFeatures( + master_source_font, includeDir=fea_include_dir + ).asFea() for instance_ufo in instance_ufos: instance_ufo.features.text = master_source_features instance_ufo.save() @@ -1111,6 +1131,7 @@ def _run_from_designspace_static( round_instances=False, feature_writers=None, expand_features_to_instances=False, + fea_include_dir=None, use_mutatormath=False, ufo_structure="package", **kwargs, @@ -1127,6 +1148,7 @@ def _run_from_designspace_static( include=pattern, round_instances=round_instances, expand_features_to_instances=expand_features_to_instances, + fea_include_dir=fea_include_dir, ) ) else: @@ -1136,6 +1158,7 @@ def _run_from_designspace_static( include=pattern, round_instances=round_instances, expand_features_to_instances=expand_features_to_instances, + fea_include_dir=fea_include_dir, ufo_structure=ufo_structure, ) ) @@ -1159,6 +1182,7 @@ def _run_from_designspace_static( interpolate_layout_from=interpolate_layout_from, interpolate_layout_dir=interpolate_layout_dir, feature_writers=feature_writers, + fea_include_dir=fea_include_dir, **kwargs, ) diff --git a/requirements.txt b/requirements.txt index b7fd4367..b1abe269 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ fonttools[unicode,ufo,lxml]==4.38.0; platform_python_implementation == 'CPython' fonttools[unicode,ufo]==4.38.0; platform_python_implementation != 'CPython' cu2qu==1.6.7.post1 glyphsLib==6.1.0 -ufo2ft==2.28.0 +ufo2ft==2.29.0 MutatorMath==3.0.1 fontMath==0.9.2 defcon[lxml]==0.10.2; platform_python_implementation == 'CPython' diff --git a/setup.py b/setup.py index c041f82e..d8b51664 100644 --- a/setup.py +++ b/setup.py @@ -58,7 +58,7 @@ "fonttools[ufo,lxml,unicode]>=4.34.0 ; implementation_name == 'cpython'", "fonttools[ufo,unicode]>=4.34.0 ; implementation_name != 'cpython'", "glyphsLib>=6.1.0", - "ufo2ft[compreffor]>=2.28.0", + "ufo2ft[compreffor]>=2.29.0", "fontMath>=0.9.1", "ufoLib2>=0.13.0", "attrs>=19", diff --git a/tests/test_main.py b/tests/test_main.py index 6ed5d6e4..ea0f5c16 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1065,7 +1065,7 @@ def test_timing_logger(data_dir, tmp_path): check=True, ) - assert re.match( + assert re.search( r"^DEBUG:fontmake.timer:Took [\.0-9]+s to run 'save_otfs'\r?$", result.stderr.decode(), flags=re.MULTILINE,