Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export variable instances as DSv5 variable-fonts #977

Merged
merged 3 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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],
anthrotype marked this conversation as resolved.
Show resolved Hide resolved
)

# 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]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder, what could end up here? Any params for which there isn't a corresponding fontinfo key basically? I guess it's fine to store them here, even though they won't be used right know by anybody.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. They might be useful for round-tripping, though this PR does not handle it right now.



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
Loading