From 2dff28d5f25c76c39dc1299f97c68f401f116a4e Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 8 Nov 2023 11:36:32 +0000 Subject: [PATCH 1/7] [builder/axes] take into account Virtual Master params when defining axis min/max but only for axes that are un-mapped, since virtual master coordinates have no means of mapping {user:design} so are assumed un-mapped (whereas axis min/def/max must be in user-space). --- Lib/glyphsLib/builder/axes.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/Lib/glyphsLib/builder/axes.py b/Lib/glyphsLib/builder/axes.py index cb3b7f2da..10ea222fc 100644 --- a/Lib/glyphsLib/builder/axes.py +++ b/Lib/glyphsLib/builder/axes.py @@ -159,6 +159,11 @@ def to_designspace_axes(self): assert isinstance(regular_master, classes.GSFontMaster) custom_mapping = self.font.customParameters["Axis Mappings"] + virtual_masters = [ + {v["Axis"]: v["Location"] for v in cp.value} + for cp in self.font.customParameters + if cp.name == "Virtual Master" + ] for axis_def in get_axis_definitions(self.font): axis = self.designspace.newAxisDescriptor() @@ -194,9 +199,7 @@ def to_designspace_axes(self): elif font_uses_axis_locations(self.font): # If all masters have an "Axis Location" custom parameter, only the values # from this parameter will be used to build the mapping of the masters and - # instances - # TODO: (jany) use Virtual Masters as well? - # (jenskutilek) virtual masters can't have an Axis Location parameter. + # instances. mapping = {} for master in self.font.masters: designLoc = axis_def.get_design_loc(master) @@ -248,11 +251,27 @@ def to_designspace_axes(self): regularUserLoc = piecewiseLinearMap(regularDesignLoc, reverse_mapping) # TODO make sure that the default is in mapping? + is_identity_map = all(uloc == dloc for uloc, dloc in mapping.items()) + + # Virtual Masters can't have an Axis Location parameter; their coordinates + # can either be mapped via Axis Mappings, or implicitly by neighbouring non-virtual + # masters' Axis Location params at least for existing axes; for newly defined + # axes the virtual master coordinates are assumed to be un-mapped (user==design). + # Only if the {user:design} mapping so far is an identity map (because it + # has not been 'bent' by one of the above mechanisms), the virtual masters + # contribute to extend the current axis' min/max range. + # https://github.com/googlefonts/glyphsLib/issues/859 + if is_identity_map: + for vm in virtual_masters: + for axis_name, axis_coord in vm.items(): + if axis_name != axis.name: + continue + mapping[axis_coord] = axis_coord + minimum = min(mapping) maximum = max(mapping) default = min(maximum, max(minimum, regularUserLoc)) # clamp - is_identity_map = all(uloc == dloc for uloc, dloc in mapping.items()) if ( minimum < maximum or minimum != axis_def.default_user_loc From 1442867566fea23631073250bbecb232947e4d3c Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 8 Nov 2023 18:07:24 +0000 Subject: [PATCH 2/7] [custom_params] ensure we store multiple 'Virtual Master' params in UFO with default glyphs_multivalued=False, we end up only storing one 'Virtual Master' param; also we want to read/write these only when dealing with GSFont, not also GSFontMaster --- Lib/glyphsLib/builder/custom_params.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Lib/glyphsLib/builder/custom_params.py b/Lib/glyphsLib/builder/custom_params.py index 22bf6a9f1..dc2128dcb 100644 --- a/Lib/glyphsLib/builder/custom_params.py +++ b/Lib/glyphsLib/builder/custom_params.py @@ -1153,3 +1153,21 @@ def _unset_default_params(glyphs): and glyphs.customParameters[glyphs_name] == default_value ): del glyphs.customParameters[glyphs_name] + + +class GSFontParamHandler(ParamHandler): + def to_glyphs(self, glyphs, ufo): + if not glyphs.is_font(): + return + super().to_glyphs(glyphs, ufo) + + def to_ufo(self, builder, glyphs, ufo): + if not glyphs.is_font(): + return + super().to_ufo(builder, glyphs, ufo) + + +# 'Virtual Master' params are GSFont-only and multi-valued (i.e. there can be multiple +# custom parameters named 'Virtual Master'); we know we want them stored in lib.plist +# hence ufo_info=False +register(GSFontParamHandler("Virtual Master", ufo_info=False, glyphs_multivalued=True)) From 069d2583c0fd1183fa4ef4f1ed77edb1c402cd09 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 8 Nov 2023 18:41:03 +0000 Subject: [PATCH 3/7] [axes_test] test that axis min/max are influenced by virtual masters params The test font 'IntermediateLayer.glyphs' has a couple of glyphs that use virtual masters to control the cap height via a 'CPHT' axis. It contains no Axis Mappings nor Axis Location parameters, the axis coordinates are intented to be un-mapped (user==design). The virtual master custom parameters are expected to be taken into account when computing the CPHT min and max values. --- tests/builder/axes_test.py | 45 ++ tests/data/IntermediateLayer.glyphs | 781 ++++++++++++++++++++++++++++ 2 files changed, 826 insertions(+) create mode 100644 tests/data/IntermediateLayer.glyphs diff --git a/tests/builder/axes_test.py b/tests/builder/axes_test.py index b928e506c..582cc8cd8 100644 --- a/tests/builder/axes_test.py +++ b/tests/builder/axes_test.py @@ -680,3 +680,48 @@ def test_variable_instance(ufo_module): assert doc.axes[0].map[2] == (400, 80) assert doc.axes[0].default == 400 assert len(doc.instances) == 27 # The VF setting should not be in the DS + + +def test_virtual_masters_extend_min_max_for_unmapped_axis(ufo_module, datadir): + # https://github.com/googlefonts/glyphsLib/issues/859 + font = GSFont(datadir.join("IntermediateLayer.glyphs")) + assert ["Cap Height", "Weight"] == [a.name for a in font.axes] + + assert "Axis Mappings" not in font.customParameters + for master in font.masters: + assert "Axis Location" not in master.customParameters + # all non-virtual masters are at the default Cap Height location + assert master.axes[0] == 700 + + virtual_masters = [ + cp.value for cp in font.customParameters if cp.name == "Virtual Master" + ] + assert virtual_masters[0] == [ + {"Axis": "Cap Height", "Location": 600}, + {"Axis": "Weight", "Location": 400}, + ] + assert virtual_masters[1] == [ + {"Axis": "Cap Height", "Location": 800}, + {"Axis": "Weight", "Location": 400}, + ] + + ds = to_designspace(font, ufo_module=ufo_module) + + # the min/max for this axis are taken from the virtual masters + assert ds.axes[0].name == "Cap Height" + assert ds.axes[0].minimum == 600 + assert ds.axes[0].default == 700 + assert ds.axes[0].maximum == 800 + assert not ds.axes[0].map + + assert ds.axes[1].name == "Weight" + assert ds.axes[1].minimum == 400 + assert ds.axes[1].default == 400 + assert ds.axes[1].maximum == 900 + assert not ds.axes[1].map + + font2 = to_glyphs(ds) + + assert [ + cp.value for cp in font2.customParameters if cp.name == "Virtual Master" + ] == virtual_masters diff --git a/tests/data/IntermediateLayer.glyphs b/tests/data/IntermediateLayer.glyphs new file mode 100644 index 000000000..fbeeea4db --- /dev/null +++ b/tests/data/IntermediateLayer.glyphs @@ -0,0 +1,781 @@ +{ +.appVersion = "3226"; +.formatVersion = 3; +axes = ( +{ +name = "Cap Height"; +tag = CPHT; +}, +{ +name = Weight; +tag = wght; +} +); +customParameters = ( +{ +name = "Write lastChange"; +value = 0; +}, +{ +name = glyphOrder; +value = ( +.notdef, +space, +H, +I, +i, +idotless, +acutecomb, +iacute +); +}, +{ +name = "Virtual Master"; +value = ( +{ +Axis = "Cap Height"; +Location = 600; +}, +{ +Axis = Weight; +Location = 400; +} +); +}, +{ +name = "Virtual Master"; +value = ( +{ +Axis = "Cap Height"; +Location = 800; +}, +{ +Axis = Weight; +Location = 400; +} +); +} +); +date = "2023-07-20 13:49:39 +0000"; +familyName = "Intermediate Layer"; +fontMaster = ( +{ +axesValues = ( +700, +400 +); +id = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +metricValues = ( +{ +over = 16; +pos = 800; +}, +{ +over = 16; +pos = 700; +}, +{ +over = 16; +pos = 500; +}, +{ +over = -16; +}, +{ +over = -16; +pos = -200; +}, +{ +} +); +name = Regular; +}, +{ +axesValues = ( +700, +900 +); +id = m01; +metricValues = ( +{ +over = 16; +pos = 800; +}, +{ +over = 16; +pos = 700; +}, +{ +over = 16; +pos = 500; +}, +{ +over = -16; +}, +{ +over = -16; +pos = -200; +}, +{ +} +); +name = Black; +} +); +glyphs = ( +{ +glyphname = .notdef; +layers = ( +{ +layerId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +shapes = ( +{ +closed = 1; +nodes = ( +(487,0,l), +(487,700,l), +(129,700,l), +(129,0,l) +); +}, +{ +closed = 1; +nodes = ( +(158,672,l), +(458,672,l), +(458,29,l), +(158,29,l) +); +} +); +width = 600; +}, +{ +layerId = m01; +shapes = ( +{ +closed = 1; +nodes = ( +(487,0,l), +(487,700,l), +(129,700,l), +(129,0,l) +); +}, +{ +closed = 1; +nodes = ( +(158,672,l), +(458,672,l), +(458,29,l), +(158,29,l) +); +} +); +width = 600; +} +); +}, +{ +glyphname = space; +layers = ( +{ +layerId = m01; +width = 600; +}, +{ +layerId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +width = 600; +} +); +unicode = 32; +}, +{ +glyphname = H; +layers = ( +{ +layerId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +shapes = ( +{ +closed = 1; +nodes = ( +(31,0,l), +(164,0,l), +(164,700,l), +(31,700,l) +); +}, +{ +closed = 1; +nodes = ( +(431,0,l), +(564,0,l), +(564,700,l), +(431,700,l) +); +}, +{ +closed = 1; +nodes = ( +(89,324,l), +(508,324,l), +(508,439,l), +(89,439,l) +); +} +); +width = 600; +}, +{ +associatedMasterId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +attr = { +coordinates = ( +600, +400 +); +}; +layerId = "674E3FAD-1F7C-4698-A483-3B70C8F3D3AE"; +name = "8 Nov 23 at 15:24"; +shapes = ( +{ +closed = 1; +nodes = ( +(31,0,l), +(164,0,l), +(164,600,l), +(31,600,l) +); +}, +{ +closed = 1; +nodes = ( +(431,0,l), +(564,0,l), +(564,600,l), +(431,600,l) +); +}, +{ +closed = 1; +nodes = ( +(89,274,l), +(508,274,l), +(508,389,l), +(89,389,l) +); +} +); +width = 600; +}, +{ +associatedMasterId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +attr = { +coordinates = ( +800, +400 +); +}; +layerId = "1FD5C09D-F887-4ED3-BF54-15FEA26CDF14"; +name = "8 Nov 23 at 15:14"; +shapes = ( +{ +closed = 1; +nodes = ( +(31,0,l), +(164,0,l), +(164,800,l), +(31,800,l) +); +}, +{ +closed = 1; +nodes = ( +(431,0,l), +(564,0,l), +(564,800,l), +(431,800,l) +); +}, +{ +closed = 1; +nodes = ( +(89,379,l), +(508,379,l), +(508,494,l), +(89,494,l) +); +} +); +width = 600; +}, +{ +layerId = m01; +shapes = ( +{ +closed = 1; +nodes = ( +(31,0,l), +(214,0,l), +(214,700,l), +(31,700,l) +); +}, +{ +closed = 1; +nodes = ( +(381,0,l), +(564,0,l), +(564,700,l), +(381,700,l) +); +}, +{ +closed = 1; +nodes = ( +(99,304,l), +(508,304,l), +(508,459,l), +(99,459,l) +); +} +); +width = 600; +} +); +unicode = 72; +}, +{ +glyphname = I; +layers = ( +{ +layerId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +shapes = ( +{ +closed = 1; +nodes = ( +(231,0,l), +(364,0,l), +(364,700,l), +(231,700,l) +); +} +); +width = 600; +}, +{ +associatedMasterId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +attr = { +coordinates = ( +600, +400 +); +}; +layerId = "1DD0DB79-DD15-42DF-A361-B703688AC628"; +name = "8 Nov 23 at 15:24"; +shapes = ( +{ +closed = 1; +nodes = ( +(231,0,l), +(364,0,l), +(364,600,l), +(231,600,l) +); +} +); +width = 600; +}, +{ +associatedMasterId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +attr = { +coordinates = ( +800, +400 +); +}; +layerId = "6550B861-E396-4850-9BB2-CA4B38E05F2F"; +name = "8 Nov 23 at 15:21"; +shapes = ( +{ +closed = 1; +nodes = ( +(231,0,l), +(364,0,l), +(364,800,l), +(231,800,l) +); +} +); +width = 600; +}, +{ +layerId = m01; +shapes = ( +{ +closed = 1; +nodes = ( +(171,0,l), +(424,0,l), +(424,700,l), +(171,700,l) +); +} +); +width = 600; +} +); +unicode = 73; +}, +{ +glyphname = i; +layers = ( +{ +layerId = m01; +shapes = ( +{ +closed = 1; +nodes = ( +(362,546,o), +(410,594,o), +(410,654,cs), +(410,713,o), +(362,761,o), +(303,761,cs), +(243,761,o), +(195,713,o), +(195,654,cs), +(195,594,o), +(243,546,o), +(303,546,cs) +); +}, +{ +ref = idotless; +} +); +width = 600; +}, +{ +layerId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +shapes = ( +{ +closed = 1; +nodes = ( +(336.667,584,o), +(364,611.667,o), +(364,645,cs), +(364,678.333,o), +(336.667,705,o), +(302,705,cs), +(267.333,705,o), +(239,678.333,o), +(239,645,cs), +(239,611.667,o), +(267.333,584,o), +(302,584,cs) +); +}, +{ +ref = idotless; +} +); +width = 600; +}, +{ +associatedMasterId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +attr = { +coordinates = ( +700, +700 +); +}; +layerId = "5807B081-E05E-4C8E-94C2-CB3F0EF9A1F4"; +name = "17 Aug 23 at 11:51"; +shapes = ( +{ +closed = 1; +nodes = ( +(349,556,o), +(388,597,o), +(388,647,cs), +(388,696,o), +(349,737,o), +(301,737,cs), +(252,737,o), +(213,696,o), +(213,647,cs), +(213,597,o), +(252,556,o), +(301,556,cs) +); +}, +{ +ref = idotless; +} +); +width = 600; +} +); +unicode = 105; +}, +{ +glyphname = idotless; +layers = ( +{ +layerId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +shapes = ( +{ +closed = 1; +nodes = ( +(354,0,l), +(354,500,l), +(241,500,l), +(241,0,l) +); +} +); +width = 600; +}, +{ +layerId = m01; +shapes = ( +{ +closed = 1; +nodes = ( +(404,0,l), +(404,500,l), +(191,500,l), +(191,0,l) +); +} +); +width = 600; +}, +{ +associatedMasterId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +attr = { +coordinates = ( +700, +700 +); +}; +layerId = "5AB430A1-B6BB-4175-8EC8-C9DE3FBA6DF5"; +name = "8 Nov 23 at 13:00"; +shapes = ( +{ +closed = 1; +nodes = ( +(374,0,l), +(296,500,l), +(296,500,l), +(221,0,l) +); +} +); +width = 600; +} +); +unicode = 305; +}, +{ +glyphname = acutecomb; +layers = ( +{ +layerId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +shapes = ( +{ +closed = 1; +nodes = ( +(68,637,l), +(149,637,l), +(328,800,l), +(206,802,l) +); +} +); +width = 300; +}, +{ +layerId = m01; +shapes = ( +{ +closed = 1; +nodes = ( +(68,637,l), +(206,637,l), +(427,814,l), +(225,814,l) +); +} +); +width = 300; +} +); +unicode = 769; +}, +{ +glyphname = iacute; +layers = ( +{ +layerId = "F969B945-4602-464F-B67C-A69BED6E6A4A"; +shapes = ( +{ +ref = idotless; +}, +{ +pos = (184,-26); +ref = acutecomb; +} +); +width = 600; +}, +{ +layerId = m01; +shapes = ( +{ +ref = idotless; +}, +{ +pos = (159,-38); +ref = acutecomb; +} +); +width = 600; +} +); +unicode = 237; +} +); +instances = ( +{ +name = Regular; +type = variable; +}, +{ +axesValues = ( +700, +400 +); +instanceInterpolations = { +"F969B945-4602-464F-B67C-A69BED6E6A4A" = 1; +}; +name = Regular; +}, +{ +axesValues = ( +700, +700 +); +instanceInterpolations = { +"F969B945-4602-464F-B67C-A69BED6E6A4A" = 0.4; +m01 = 0.6; +}; +isBold = 1; +name = Bold; +weightClass = 700; +}, +{ +axesValues = ( +700, +900 +); +instanceInterpolations = { +m01 = 1; +}; +name = Black; +weightClass = 900; +}, +{ +axesValues = ( +600, +400 +); +instanceInterpolations = { +"F969B945-4602-464F-B67C-A69BED6E6A4A" = 1; +}; +name = "Short Caps"; +}, +{ +axesValues = ( +600, +700 +); +instanceInterpolations = { +"F969B945-4602-464F-B67C-A69BED6E6A4A" = 0.4; +m01 = 0.6; +}; +name = "Short Caps Bold"; +weightClass = 700; +}, +{ +axesValues = ( +600, +900 +); +instanceInterpolations = { +m01 = 1; +}; +name = "Short Caps Black"; +weightClass = 900; +}, +{ +axesValues = ( +800, +400 +); +instanceInterpolations = { +"F969B945-4602-464F-B67C-A69BED6E6A4A" = 1; +}; +name = "Tall Caps"; +}, +{ +axesValues = ( +800, +700 +); +instanceInterpolations = { +"F969B945-4602-464F-B67C-A69BED6E6A4A" = 0.4; +m01 = 0.6; +}; +name = "Tall Caps Bold"; +weightClass = 700; +}, +{ +axesValues = ( +800, +900 +); +instanceInterpolations = { +m01 = 1; +}; +name = "Tall Caps Black"; +weightClass = 900; +} +); +metrics = ( +{ +type = ascender; +}, +{ +type = "cap height"; +}, +{ +type = "x-height"; +}, +{ +type = baseline; +}, +{ +type = descender; +}, +{ +type = "italic angle"; +} +); +unitsPerEm = 1000; +userData = { +GSDontShowVersionAlert = 1; +}; +versionMajor = 1; +versionMinor = 0; +} From 8082f994f4b934e19f3626b518a71dca1726c53e Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 8 Nov 2023 18:52:17 +0000 Subject: [PATCH 4/7] [ci] run lint job on python3.11 for now instead of latest 3.12 somehow it fails while installing skia-pathops, trying to build from source. Probably we only need to bump requirements-dev.txt but I want to do that separately --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ad7df8d7..006079073 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.x" + python-version: "3.11" - name: Install dependencies run: pip install tox - name: Run style and typing checks From 49ca65d2d06bb864b47b84d6fbc42a92e8f32b7f Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 8 Nov 2023 19:10:30 +0000 Subject: [PATCH 5/7] don't write empty 'Virtual Master' parameters to UFO --- Lib/glyphsLib/builder/custom_params.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/glyphsLib/builder/custom_params.py b/Lib/glyphsLib/builder/custom_params.py index dc2128dcb..15a92eeda 100644 --- a/Lib/glyphsLib/builder/custom_params.py +++ b/Lib/glyphsLib/builder/custom_params.py @@ -1170,4 +1170,8 @@ def to_ufo(self, builder, glyphs, ufo): # 'Virtual Master' params are GSFont-only and multi-valued (i.e. there can be multiple # custom parameters named 'Virtual Master'); we know we want them stored in lib.plist # hence ufo_info=False -register(GSFontParamHandler("Virtual Master", ufo_info=False, glyphs_multivalued=True)) +register( + GSFontParamHandler( + "Virtual Master", ufo_info=False, ufo_default=[], glyphs_multivalued=True + ) +) From 14506273674cdf946e1e7a52f773ae442e7d4e5e Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 8 Nov 2023 19:12:28 +0000 Subject: [PATCH 6/7] fix linter complaining line too long.. --- Lib/glyphsLib/builder/axes.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/glyphsLib/builder/axes.py b/Lib/glyphsLib/builder/axes.py index 10ea222fc..17982d3e4 100644 --- a/Lib/glyphsLib/builder/axes.py +++ b/Lib/glyphsLib/builder/axes.py @@ -254,9 +254,10 @@ def to_designspace_axes(self): is_identity_map = all(uloc == dloc for uloc, dloc in mapping.items()) # Virtual Masters can't have an Axis Location parameter; their coordinates - # can either be mapped via Axis Mappings, or implicitly by neighbouring non-virtual - # masters' Axis Location params at least for existing axes; for newly defined - # axes the virtual master coordinates are assumed to be un-mapped (user==design). + # can either be mapped via Axis Mappings, or implicitly by neighbouring + # non-virtual masters' Axis Location params at least for existing axes; for + # newly defined axes the virtual master coordinates are assumed to be un-mapped + # (user==design). # Only if the {user:design} mapping so far is an identity map (because it # has not been 'bent' by one of the above mechanisms), the virtual masters # contribute to extend the current axis' min/max range. From b54d99deff15f6f98f5d75f02d36d5a6eb5a5d6c Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Mon, 13 Nov 2023 16:54:59 +0000 Subject: [PATCH 7/7] prefer instance-based axis mapping over master's only when non-identity in absence of explicit Axis Mappings or Axis Location, we infer wght/wdth user-spacelocations from the instances; however if the axis is un-mapped or resolves in an identity (no-op) mapping, there's no reason to prefer the instance-based mapping over the similarly identity/no-op mapping associated with the masters; actually it's possible that instances do not cover all the masters and thus by preferring the instances' mapping over the masters' we might produce incorrect axis min/max. So, only use the inferred instance-based mapping when it does something interesting, otherwise keep using the master (identity) mapping. --- Lib/glyphsLib/builder/axes.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Lib/glyphsLib/builder/axes.py b/Lib/glyphsLib/builder/axes.py index 17982d3e4..602e9279c 100644 --- a/Lib/glyphsLib/builder/axes.py +++ b/Lib/glyphsLib/builder/axes.py @@ -152,6 +152,11 @@ def update_mapping_from_instances( mapping[userLoc] = designLoc +def is_identity(mapping): + """Return whether the mapping is an identity mapping.""" + return all(userLoc == designLoc for userLoc, designLoc in mapping.items()) + + def to_designspace_axes(self): if not self.font.masters: return @@ -241,8 +246,12 @@ def to_designspace_axes(self): userLoc = designLoc = axis_def.get_design_loc(master) master_mapping[userLoc] = designLoc - # Prefer the instance-based mapping - mapping = instance_mapping or master_mapping + # Prefer the instance-based mapping (but only if interesting) + mapping = ( + instance_mapping + if (instance_mapping and not is_identity(instance_mapping)) + else master_mapping + ) regularDesignLoc = axis_def.get_design_loc(regular_master) # Glyphs masters don't have a user location, so we compute it by @@ -251,7 +260,7 @@ def to_designspace_axes(self): regularUserLoc = piecewiseLinearMap(regularDesignLoc, reverse_mapping) # TODO make sure that the default is in mapping? - is_identity_map = all(uloc == dloc for uloc, dloc in mapping.items()) + is_identity_map = is_identity(mapping) # Virtual Masters can't have an Axis Location parameter; their coordinates # can either be mapped via Axis Mappings, or implicitly by neighbouring