Skip to content

Commit

Permalink
Merge pull request #977 from googlefonts/variable-instance
Browse files Browse the repository at this point in the history
Export variable instances as DSv5 variable-fonts
  • Loading branch information
khaledhosny authored Feb 1, 2024
2 parents ce69aad + 257ced9 commit 0275c9d
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 26 deletions.
5 changes: 3 additions & 2 deletions Lib/glyphsLib/builder/custom_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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):
Expand Down
34 changes: 31 additions & 3 deletions Lib/glyphsLib/builder/instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 2 additions & 0 deletions Lib/glyphsLib/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
21 changes: 17 additions & 4 deletions tests/builder/axes_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
15 changes: 15 additions & 0 deletions tests/classes_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
GSPath,
GSSmartComponentAxis,
GSBackgroundImage,
InstanceType,
segment,
LayerComponentsProxy,
LayerGuideLinesProxy,
Expand Down Expand Up @@ -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)
Expand Down
44 changes: 27 additions & 17 deletions tests/data/VariableInstance.glyphs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
.appVersion = "3139";
.appVersion = "3234";
.formatVersion = 3;
axes = (
{
Expand Down Expand Up @@ -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;
}
);
Expand Down Expand Up @@ -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 = {
Expand Down

0 comments on commit 0275c9d

Please sign in to comment.