diff --git a/Lib/fontmake/__main__.py b/Lib/fontmake/__main__.py index 0ce9dc13..f45a5f64 100644 --- a/Lib/fontmake/__main__.py +++ b/Lib/fontmake/__main__.py @@ -539,7 +539,19 @@ def main(args=None): "--no-auto-use-my-metrics", dest="auto_use_my_metrics", action="store_false", - help="Don't automatically set USE_MY_METRICS glyf component flags (0x0200).", + default=None, + ) + contourGroup.add_argument( + "--auto-use-my-metrics", + dest="auto_use_my_metrics", + action="store_true", + default=None, + help=( + "Automatically set (or not) USE_MY_METRICS glyf component flags (0x0200). " + "By default, fontmake only enables this for static fonts, though it can " + "also be enabled for variable fonts. This is not needed unless the font " + "is going to have hinted horizontal glyph metrics." + ), ) contourGroup.add_argument( "--drop-implied-oncurves", @@ -605,8 +617,7 @@ def main(args=None): "--production-names", dest="use_production_names", action="store_true", - help="Rename glyphs with production names if available otherwise use " - "uninames.", + help="Rename glyphs with production names if available otherwise use uninames.", ) glyphnamesGroup.add_argument( "--no-production-names", dest="use_production_names", action="store_false" @@ -743,6 +754,9 @@ def main(args=None): ) args.pop("ufo_structure", None) # unused for UFO output args.pop("indent_json", None) + # keep the old default for statics, where it doesn't harm + if args["auto_use_my_metrics"] is None: + args["auto_use_my_metrics"] = True project.run_from_ufos( inputs.ufo_paths, is_instance=args.pop("masters_as_instances"), **args ) diff --git a/Lib/fontmake/font_project.py b/Lib/fontmake/font_project.py index 606839b8..ee88f682 100644 --- a/Lib/fontmake/font_project.py +++ b/Lib/fontmake/font_project.py @@ -332,7 +332,7 @@ def _build_interpolatable_masters( fea_include_dir=None, flatten_components=False, filters=None, - auto_use_my_metrics=True, + auto_use_my_metrics=False, **kwargs, ): if ttf: @@ -395,7 +395,7 @@ def build_variable_fonts( fea_include_dir=None, flatten_components=False, filters=None, - auto_use_my_metrics=True, + auto_use_my_metrics=False, drop_implied_oncurves=False, variable_features=True, **kwargs, @@ -1098,6 +1098,7 @@ def run_from_designspace( filters=None, expand_features_to_instances=False, check_compatibility=None, + auto_use_my_metrics=None, **kwargs, ): """Run toolchain from a DesignSpace document to produce either static @@ -1194,6 +1195,9 @@ def run_from_designspace( try: if static_outputs: + # keep setting the USE_MY_METRICS flag for the static outputs only + if auto_use_my_metrics is None: + auto_use_my_metrics = True self._run_from_designspace_static( designspace, outputs=static_outputs, @@ -1204,9 +1208,19 @@ def run_from_designspace( feature_writers=feature_writers, expand_features_to_instances=expand_features_to_instances, filters=filters, + auto_use_my_metrics=auto_use_my_metrics, **kwargs, ) if interp_outputs: + # for interpolatable outputs, prefer not to set the flag automatically + # and let the user decide. For VFs in particular this is either useless + # (if the VF isn't going to be hinted) or wrong (if the composite and + # component metrics are not the same throughout the variation space, + # leading to potential mismatch between the metrics computed from HVAR + # vs glyf+gvar phantom points, depending on whether the flag is honored + # by the renderer). + if auto_use_my_metrics is None: + auto_use_my_metrics = False self._run_from_designspace_interpolatable( designspace, outputs=interp_outputs, @@ -1214,6 +1228,7 @@ def run_from_designspace( feature_writers=feature_writers, filters=filters, variable_features=variable_features, + auto_use_my_metrics=auto_use_my_metrics, **kwargs, ) except FontmakeError as e: diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/fontinfo.plist b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/fontinfo.plist new file mode 100644 index 00000000..6109b22a --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/fontinfo.plist @@ -0,0 +1,28 @@ + + + + + ascender + 800 + capHeight + 700 + descender + -200 + familyName + WghtVar Composite + styleMapFamilyName + WghtVar Composite + styleMapStyleName + bold + styleName + Bold + unitsPerEm + 1000 + versionMajor + 42 + versionMinor + 42 + xHeight + 500 + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/glyphs/contents.plist b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/glyphs/contents.plist new file mode 100644 index 00000000..4d791006 --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/glyphs/contents.plist @@ -0,0 +1,10 @@ + + + + + equal + equal.glif + hyphen + hyphen.glif + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/glyphs/equal.glif b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/glyphs/equal.glif new file mode 100644 index 00000000..c9236297 --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/glyphs/equal.glif @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/glyphs/hyphen.glif b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/glyphs/hyphen.glif new file mode 100644 index 00000000..4bc2c734 --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/glyphs/hyphen.glif @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/layercontents.plist b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/layercontents.plist new file mode 100644 index 00000000..b9c1a4f2 --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/layercontents.plist @@ -0,0 +1,10 @@ + + + + + + public.default + glyphs + + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/lib.plist b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/lib.plist new file mode 100644 index 00000000..33362f3d --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/lib.plist @@ -0,0 +1,11 @@ + + + + + public.glyphOrder + + hyphen + equal + + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/metainfo.plist b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/metainfo.plist new file mode 100644 index 00000000..7b8b34ac --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Bold.ufo/metainfo.plist @@ -0,0 +1,10 @@ + + + + + creator + com.github.fonttools.ufoLib + formatVersion + 3 + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/fontinfo.plist b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/fontinfo.plist new file mode 100644 index 00000000..5a5b8469 --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/fontinfo.plist @@ -0,0 +1,28 @@ + + + + + ascender + 737 + capHeight + 702 + descender + -42 + familyName + WghtVar Composite + styleMapFamilyName + WghtVar Composite + styleMapStyleName + regular + styleName + Regular + unitsPerEm + 1000 + versionMajor + 42 + versionMinor + 42 + xHeight + 501 + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/glyphs/contents.plist b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/glyphs/contents.plist new file mode 100644 index 00000000..4d791006 --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/glyphs/contents.plist @@ -0,0 +1,10 @@ + + + + + equal + equal.glif + hyphen + hyphen.glif + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/glyphs/equal.glif b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/glyphs/equal.glif new file mode 100644 index 00000000..ef8f9741 --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/glyphs/equal.glif @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/glyphs/hyphen.glif b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/glyphs/hyphen.glif new file mode 100644 index 00000000..78fe1378 --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/glyphs/hyphen.glif @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/layercontents.plist b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/layercontents.plist new file mode 100644 index 00000000..b9c1a4f2 --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/layercontents.plist @@ -0,0 +1,10 @@ + + + + + + public.default + glyphs + + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/lib.plist b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/lib.plist new file mode 100644 index 00000000..33362f3d --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/lib.plist @@ -0,0 +1,11 @@ + + + + + public.glyphOrder + + hyphen + equal + + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/metainfo.plist b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/metainfo.plist new file mode 100644 index 00000000..7b8b34ac --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite-Regular.ufo/metainfo.plist @@ -0,0 +1,10 @@ + + + + + creator + com.github.fonttools.ufoLib + formatVersion + 3 + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite.designspace b/tests/data/AutoUseMyMetrics/WghtVarComposite.designspace new file mode 100644 index 00000000..e306b9f5 --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite.designspace @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/AutoUseMyMetrics/WghtVarComposite.glyphs b/tests/data/AutoUseMyMetrics/WghtVarComposite.glyphs new file mode 100644 index 00000000..d69e2531 --- /dev/null +++ b/tests/data/AutoUseMyMetrics/WghtVarComposite.glyphs @@ -0,0 +1,183 @@ +{ +.appVersion = "3419"; +.formatVersion = 3; +DisplayStrings = ( +"=" +); +axes = ( +{ +name = Weight; +tag = wght; +} +); +date = "2025-08-27 10:48:24 +0000"; +familyName = "WghtVar Composite"; +fontMaster = ( +{ +axesValues = ( +400 +); +id = m01; +metricValues = ( +{ +over = 16; +pos = 737; +}, +{ +over = -16; +}, +{ +over = -16; +pos = -42; +}, +{ +pos = 702; +}, +{ +pos = 501; +} +); +name = Regular; +}, +{ +axesValues = ( +700 +); +iconName = Bold; +id = "E09E0C54-128D-4FEA-B209-1B70BEFE300B"; +metricValues = ( +{ +pos = 800; +}, +{ +}, +{ +pos = -200; +}, +{ +pos = 700; +}, +{ +pos = 500; +} +); +name = Bold; +} +); +glyphs = ( +{ +glyphname = hyphen; +layers = ( +{ +layerId = m01; +shapes = ( +{ +closed = 1; +nodes = ( +(131,250,l,{ +name = hr00; +}), +(470,250,l), +(470,330,l), +(131,330,l) +); +} +); +width = 600; +}, +{ +layerId = "E09E0C54-128D-4FEA-B209-1B70BEFE300B"; +shapes = ( +{ +closed = 1; +nodes = ( +(92,224,l), +(508,224,l), +(508,356,l), +(92,356,l) +); +} +); +width = 600; +} +); +unicode = 45; +}, +{ +glyphname = equal; +layers = ( +{ +layerId = m01; +shapes = ( +{ +pos = (0,50); +ref = hyphen; +}, +{ +pos = (0,-50); +ref = hyphen; +} +); +width = 600; +}, +{ +layerId = "E09E0C54-128D-4FEA-B209-1B70BEFE300B"; +shapes = ( +{ +pos = (0,70); +ref = hyphen; +}, +{ +pos = (0,-100); +ref = hyphen; +} +); +width = 600; +} +); +unicode = 61; +} +); +instances = ( +{ +axesValues = ( +400 +); +instanceInterpolations = { +m01 = 1; +}; +name = Regular; +}, +{ +axesValues = ( +700 +); +instanceInterpolations = { +"E09E0C54-128D-4FEA-B209-1B70BEFE300B" = 1; +}; +isBold = 1; +name = Bold; +weightClass = 700; +} +); +metrics = ( +{ +type = ascender; +}, +{ +type = baseline; +}, +{ +type = descender; +}, +{ +type = "cap height"; +}, +{ +type = "x-height"; +} +); +unitsPerEm = 1000; +versionMajor = 42; +versionMinor = 42; +} diff --git a/tests/test_main.py b/tests/test_main.py index 22d53333..fa874d79 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -11,6 +11,7 @@ import pytest import ufoLib2 from fontTools.misc.testTools import getXML +from fontTools.ttLib.tables._g_l_y_f import USE_MY_METRICS from ufo2ft.util import zip_strict import fontmake.__main__ @@ -1478,3 +1479,217 @@ def assert_charstring_regions(charstring, expected_regions): {"wght": (0.8, 1.0, 1.0)}, ], ) + + +def has_use_my_metrics_flag(font_file, composite_glyph_name) -> bool: + font = fontTools.ttLib.TTFont(font_file) + glyf = font["glyf"] + + glyph = glyf[composite_glyph_name] + assert glyph.isComposite() + assert len(glyph.components) > 0 + return any(comp.flags & USE_MY_METRICS != 0 for comp in glyph.components) + + +@pytest.mark.parametrize( + "source_type, source_name", + [ + pytest.param("-g", "WghtVarComposite.glyphs", id="glyphs"), + pytest.param("-m", "WghtVarComposite.designspace", id="designspace"), + ], +) +def test_auto_use_my_metrics_disabled_by_default_for_variable( + data_dir, tmp_path, source_type, source_name +): + fontmake.__main__.main( + [ + source_type, + str(data_dir / "AutoUseMyMetrics" / source_name), + "-o", + "variable", + "--output-path", + str(tmp_path / "WghtVarComposite-VF.ttf"), + ] + ) + + assert not has_use_my_metrics_flag(tmp_path / "WghtVarComposite-VF.ttf", "equal") + + +@pytest.mark.parametrize("auto_use_my_metrics", [True, False]) +@pytest.mark.parametrize( + "source_type, source_name", + [ + pytest.param("-g", "WghtVarComposite.glyphs", id="glyphs"), + pytest.param("-m", "WghtVarComposite.designspace", id="designspace"), + ], +) +def test_auto_use_my_metrics_flag_for_variable( + data_dir, tmp_path, source_type, source_name, auto_use_my_metrics +): + fontmake.__main__.main( + [ + source_type, + str(data_dir / "AutoUseMyMetrics" / source_name), + "-o", + "variable", + f"--{'no-' if not auto_use_my_metrics else ''}auto-use-my-metrics", + "--output-path", + str(tmp_path / "WghtVarComposite-VF.ttf"), + ] + ) + + assert ( + has_use_my_metrics_flag(tmp_path / "WghtVarComposite-VF.ttf", "equal") + == auto_use_my_metrics + ) + + +@pytest.mark.parametrize( + "source_type, source_name", + [ + pytest.param("-g", "WghtVarComposite.glyphs", id="glyphs"), + pytest.param("-m", "WghtVarComposite.designspace", id="designspace"), + ], +) +def test_auto_use_my_metrics_disabled_by_default_for_interpolatable_ttfs( + data_dir, tmp_path, source_type, source_name +): + fontmake.__main__.main( + [ + source_type, + str(data_dir / "AutoUseMyMetrics" / source_name), + "-o", + "ttf-interpolatable", + "--output-dir", + str(tmp_path), + ] + ) + + font_files = list(tmp_path.glob("*.ttf")) + assert len(list(font_files)) == 2 + for font_file in font_files: + assert not has_use_my_metrics_flag(font_file, "equal") + + +@pytest.mark.parametrize("auto_use_my_metrics", [True, False]) +@pytest.mark.parametrize( + "source_type, source_name", + [ + pytest.param("-g", "WghtVarComposite.glyphs", id="glyphs"), + pytest.param("-m", "WghtVarComposite.designspace", id="designspace"), + ], +) +def test_auto_use_my_metrics_flag_for_interpolatable_ttfs( + data_dir, tmp_path, source_type, source_name, auto_use_my_metrics +): + fontmake.__main__.main( + [ + source_type, + str(data_dir / "AutoUseMyMetrics" / source_name), + "-o", + "ttf-interpolatable", + f"--{'no-' if not auto_use_my_metrics else ''}auto-use-my-metrics", + "--output-dir", + str(tmp_path), + ] + ) + + font_files = list(tmp_path.glob("*.ttf")) + assert len(list(font_files)) == 2 + for font_file in font_files: + assert has_use_my_metrics_flag(font_file, "equal") == auto_use_my_metrics + + +@pytest.mark.parametrize( + "source_type, source_name", + [ + pytest.param("-g", "WghtVarComposite.glyphs", id="glyphs"), + pytest.param("-m", "WghtVarComposite.designspace", id="designspace"), + ], +) +def test_auto_use_my_metrics_enabled_by_default_for_interpolated_statics( + data_dir, tmp_path, source_type, source_name +): + fontmake.__main__.main( + [ + source_type, + str(data_dir / "AutoUseMyMetrics" / source_name), + "--interpolate", + "-o", + "ttf", + "--output-dir", + str(tmp_path), + ] + ) + + font_files = list(tmp_path.glob("*.ttf")) + assert len(list(font_files)) == 2 + for font_file in font_files: + assert has_use_my_metrics_flag(font_file, "equal") + + +@pytest.mark.parametrize("auto_use_my_metrics", [True, False]) +@pytest.mark.parametrize( + "source_type, source_name", + [ + pytest.param("-g", "WghtVarComposite.glyphs", id="glyphs"), + pytest.param("-m", "WghtVarComposite.designspace", id="designspace"), + ], +) +def test_auto_use_my_metrics_flag_for_interpolated_statics( + data_dir, tmp_path, source_type, source_name, auto_use_my_metrics +): + fontmake.__main__.main( + [ + source_type, + str(data_dir / "AutoUseMyMetrics" / source_name), + "--interpolate", + "-o", + "ttf", + f"--{'no-' if not auto_use_my_metrics else ''}auto-use-my-metrics", + "--output-dir", + str(tmp_path), + ] + ) + + font_files = list(tmp_path.glob("*.ttf")) + assert len(list(font_files)) == 2 + for font_file in font_files: + assert has_use_my_metrics_flag(font_file, "equal") == auto_use_my_metrics + + +def test_auto_use_my_metrics_enabled_by_default_for_single_ufo(data_dir, tmp_path): + fontmake.__main__.main( + [ + "-u", + str(data_dir / "AutoUseMyMetrics" / "WghtVarComposite-Regular.ufo"), + "-o", + "ttf", + "--output-path", + str(tmp_path / "WghtVarComposite-Regular.ttf"), + ] + ) + + assert has_use_my_metrics_flag(tmp_path / "WghtVarComposite-Regular.ttf", "equal") + + +@pytest.mark.parametrize("auto_use_my_metrics", [True, False]) +def test_auto_use_my_metrics_flag_for_single_ufo( + data_dir, tmp_path, auto_use_my_metrics +): + fontmake.__main__.main( + [ + "-u", + str(data_dir / "AutoUseMyMetrics" / "WghtVarComposite-Regular.ufo"), + "-o", + "ttf", + f"--{'no-' if not auto_use_my_metrics else ''}auto-use-my-metrics", + "--output-path", + str(tmp_path / "WghtVarComposite-Regular.ttf"), + ] + ) + + assert ( + has_use_my_metrics_flag(tmp_path / "WghtVarComposite-Regular.ttf", "equal") + == auto_use_my_metrics + )