diff --git a/Lib/glyphsLib/builder/custom_params.py b/Lib/glyphsLib/builder/custom_params.py index 15a92eeda..c7cc850ae 100644 --- a/Lib/glyphsLib/builder/custom_params.py +++ b/Lib/glyphsLib/builder/custom_params.py @@ -1062,7 +1062,7 @@ def to_glyphs(self, glyphs, ufo): register(RenameGlyphsParamHandler()) -def to_ufo_custom_params(self, ufo, glyphs_object): +def to_ufo_custom_params(self, ufo, glyphs_object, set_default_params=True): # glyphs_module=None because we shouldn't instanciate any Glyphs classes glyphs_proxy = GlyphsObjectProxy(glyphs_object, glyphs_module=None) ufo_proxy = UFOProxy(ufo) @@ -1076,7 +1076,8 @@ def to_ufo_custom_params(self, ufo, glyphs_object): name = _normalize_custom_param_name(param.name) ufo.lib[CUSTOM_PARAM_PREFIX + glyphs_proxy.sub_key + name] = param.value - _set_default_params(ufo) + if set_default_params: + _set_default_params(ufo) def to_glyphs_custom_params(self, ufo, glyphs_object): diff --git a/Lib/glyphsLib/builder/instances.py b/Lib/glyphsLib/builder/instances.py index 6f8d3d0e3..3f05b9c38 100644 --- a/Lib/glyphsLib/builder/instances.py +++ b/Lib/glyphsLib/builder/instances.py @@ -56,13 +56,41 @@ def to_designspace_instances(self): """Write instance data from self.font to self.designspace.""" for instance in self.font.instances: - if instance.type == InstanceType.VARIABLE: - continue if self.minimize_glyphs_diffs or ( is_instance_active(instance) and _is_instance_included_in_family(self, instance) ): - _to_designspace_instance(self, instance) + if instance.type == InstanceType.VARIABLE: + _to_designspace_varfont(self, instance) + else: + _to_designspace_instance(self, instance) + + +def _to_designspace_varfont(self, instance): + from fontTools.designspaceLib import RangeAxisSubsetDescriptor + from fontTools.ufoLib import fontInfoAttributesVersion3 + + ds = self.designspace + ufo_varfont = self.designspace.addVariableFontDescriptor( + name=instance.name, + filename=instance.customParameters["fileName"], + axisSubsets=[RangeAxisSubsetDescriptor(name=axis.name) for axis in ds.axes], + ) + + # to_ufo_custom_params() wants a UFO, create a dummy one + ufo = self.ufo_module.Font() + to_ufo_custom_params(self, ufo, instance, set_default_params=False) + + info = {} + for attr in fontInfoAttributesVersion3: + if (value := getattr(ufo.info, attr, None)) is not None: + info[attr] = value + + if info: + ufo_varfont.lib["public.fontInfo"] = info + + for key in ufo.lib: + ufo_varfont.lib[key] = ufo.lib[key] def _to_designspace_instance(self, instance): diff --git a/Lib/glyphsLib/classes.py b/Lib/glyphsLib/classes.py index 8b8309da2..ccf167a10 100755 --- a/Lib/glyphsLib/classes.py +++ b/Lib/glyphsLib/classes.py @@ -3372,6 +3372,8 @@ def fullName(self, value): # v2 compatibility def _get_axis_value(self, index): + if self.type == InstanceType.VARIABLE: + return None if index < len(self.axes): return self.axes[index] if index < len(self._axis_defaults): diff --git a/tests/builder/axes_test.py b/tests/builder/axes_test.py index 582cc8cd8..d800af75a 100644 --- a/tests/builder/axes_test.py +++ b/tests/builder/axes_test.py @@ -667,10 +667,8 @@ def test_axis_with_no_mapping_does_not_error_in_roundtrip_with_2_axes(ufo_module def test_variable_instance(ufo_module): - """Glyphs 3 introduced a so-called "variable" instance which is a - pseudo-instance that holds various VF settings. - This messed with the instance_mapping creation as it would overwrite the designLoc - of a default instance of an axis back to 0. + """Glyphs 3 introduced a "variable" instance which is a special instance + that holds various VF settings. We export it to Design Space variable-font. """ source_path = os.path.join("tests", "data", "VariableInstance.glyphs") font = GSFont(source_path) @@ -681,6 +679,21 @@ def test_variable_instance(ufo_module): assert doc.axes[0].default == 400 assert len(doc.instances) == 27 # The VF setting should not be in the DS + assert len(doc.variableFonts) == 1 + + varfont = doc.variableFonts[0] + assert len(varfont.axisSubsets) == len(doc.axes) + assert "public.fontInfo" in varfont.lib + + info = varfont.lib["public.fontInfo"] + assert len(info.get("openTypeNameRecords")) == 1 + assert info["openTypeNameRecords"][0].string == "Variable" + assert info["openTypeNameRecords"][0].nameID == 1 + assert info["openTypeNameRecords"][0].platformID == 1 + assert info["openTypeNameRecords"][0].languageID == 0 + assert info["openTypeNameRecords"][0].encodingID == 0 + assert info.get("openTypeNamePreferredFamilyName") == "Cairo Variable" + def test_virtual_masters_extend_min_max_for_unmapped_axis(ufo_module, datadir): # https://github.com/googlefonts/glyphsLib/issues/859 diff --git a/tests/classes_test.py b/tests/classes_test.py index 83b44f2cb..76bf9ba7e 100755 --- a/tests/classes_test.py +++ b/tests/classes_test.py @@ -40,6 +40,7 @@ GSPath, GSSmartComponentAxis, GSBackgroundImage, + InstanceType, segment, LayerComponentsProxy, LayerGuideLinesProxy, @@ -187,6 +188,20 @@ def test_font_master_proxy(self): self.assertEqual(master.font, font) +class GSInstanceTest(unittest.TestCase): + def test_variable_instance(self): + instance = GSInstance() + instance.name = "Variable" + instance.type = InstanceType.VARIABLE + + assert instance.weightValue is None + assert instance.widthValue is None + assert instance.customValue is None + assert instance.customValue1 is None + assert instance.customValue2 is None + assert instance.customValue3 is None + + class GSObjectsTestCase(unittest.TestCase): def setUp(self): self.font = GSFont(TESTFILE_PATH) diff --git a/tests/data/VariableInstance.glyphs b/tests/data/VariableInstance.glyphs index 007ed9368..deac4f1ea 100644 --- a/tests/data/VariableInstance.glyphs +++ b/tests/data/VariableInstance.glyphs @@ -1,5 +1,5 @@ { -.appVersion = "3139"; +.appVersion = "3234"; .formatVersion = 3; axes = ( { @@ -777,14 +777,24 @@ name = "ExtraBlack Slant Left"; weightClass = 1000; }, { -axesValues = ( -0, -0 +customParameters = ( +{ +name = "Name Table Entry"; +value = "1 1 0 0; Variable"; +} +); +name = Variable; +properties = ( +{ +key = preferredFamilyNames; +values = ( +{ +language = dflt; +value = "Cairo Variable"; +} +); +} ); -instanceInterpolations = { -"459CA063-FA7D-4205-A795-7B6CE244EAAB" = 1; -}; -name = Regular; type = variable; } ); @@ -832,34 +842,34 @@ key = designerURL; value = "https://gaber.design"; }, { -key = manufacturers; +key = licenses; values = ( { language = dflt; -value = "Kief Type Foundry, Accademia di Belle Arti di Urbino"; +value = "This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is available with a FAQ at: http://scripts.sil.org/OFL"; } ); }, { -key = manufacturerURL; -value = "https://gaber.design"; +key = licenseURL; +value = "http://scripts.sil.org/OFL"; }, { -key = licenses; +key = manufacturers; values = ( { language = dflt; -value = "This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is available with a FAQ at: http://scripts.sil.org/OFL"; +value = "Kief Type Foundry, Accademia di Belle Arti di Urbino"; } ); }, { -key = licenseURL; -value = "http://scripts.sil.org/OFL"; +key = manufacturerURL; +value = "https://gaber.design"; }, { key = vendorID; -value = "1KTF"; +value = 1KTF; } ); settings = {