diff --git a/Lib/ufo2ft/__init__.py b/Lib/ufo2ft/__init__.py index aad7e05c9..72102292c 100644 --- a/Lib/ufo2ft/__init__.py +++ b/Lib/ufo2ft/__init__.py @@ -24,7 +24,7 @@ TTFPreProcessor, ) from ufo2ft.util import ( - _getDefaultNotdefGlyph, + _notdefGlyphFallback, colrClipBoxQuantization, ensure_all_sources_have_names, init_kwargs, @@ -408,7 +408,7 @@ def compileInterpolatableTTFsFromDS(designSpaceDoc, **kwargs): kwargs["skipExportGlyphs"] = designSpaceDoc.lib.get("public.skipExportGlyphs", []) if kwargs["notdefGlyph"] is None: - kwargs["notdefGlyph"] = _getDefaultNotdefGlyph(designSpaceDoc, empty=True) + kwargs["notdefGlyph"] = _notdefGlyphFallback(designSpaceDoc) kwargs["extraSubstitutions"] = defaultdict(set) for rule in designSpaceDoc.rules: @@ -479,7 +479,7 @@ def compileInterpolatableOTFsFromDS(designSpaceDoc, **kwargs): kwargs["skipExportGlyphs"] = designSpaceDoc.lib.get("public.skipExportGlyphs", []) if kwargs["notdefGlyph"] is None: - kwargs["notdefGlyph"] = _getDefaultNotdefGlyph(designSpaceDoc) + kwargs["notdefGlyph"] = _notdefGlyphFallback(designSpaceDoc) kwargs["extraSubstitutions"] = defaultdict(set) for rule in designSpaceDoc.rules: diff --git a/Lib/ufo2ft/util.py b/Lib/ufo2ft/util.py index 08aacc7a4..48d56a79c 100644 --- a/Lib/ufo2ft/util.py +++ b/Lib/ufo2ft/util.py @@ -475,7 +475,17 @@ def getDefaultMasterFont(designSpaceDoc): return defaultSource.font -def _getDefaultNotdefGlyph(designSpaceDoc, empty=False): +def _notdefGlyphFallback(designSpaceDoc): + """Return an empty glyph to be used as .notdef for sparse layer masters. + + Sparse layers usually do not contain a .notdef glyph, however in order to + compile valid TTFs to be used as master in varLib.build, a .notdef at index 0 is + required. We can't use the auto-generated .notdef glyph because it may be + incompatible with the one already present in the other masters. So we make + an empty glyph which will be ignored when building gvar or HVAR. + If the default master does not contain a .notdef either, return None since + the auto-generated .notdef can be used. + """ from ufo2ft.errors import InvalidDesignSpaceData try: @@ -489,17 +499,11 @@ def _getDefaultNotdefGlyph(designSpaceDoc, empty=False): except KeyError: notdefGlyph = None else: - if empty: - # create a new empty .notdef glyph with the same width/height - # as the default master's .notdef glyph to be use for sparse layer - # master TTFs, so that it won't participate in gvar interpolation - # TODO(anthrotype): Use sentinel values for width/height to - # mark the glyph as non-participating for HVAR if/when fonttools - # supports that, https://github.com/googlefonts/ufo2ft/issues/501 - emptyGlyph = _getNewGlyphFactory(notdefGlyph)(".notdef") - emptyGlyph.width = notdefGlyph.width - emptyGlyph.height = notdefGlyph.height - notdefGlyph = emptyGlyph + notdefGlyph = _getNewGlyphFactory(notdefGlyph)(".notdef") + # sentinel value for varLib that means this advance does not participate + # https://github.com/fonttools/fonttools/pull/3235 + notdefGlyph.width = 0xFFFF + notdefGlyph.height = 0xFFFF return notdefGlyph diff --git a/requirements.txt b/requirements.txt index 03761ea5c..f905028bf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -fonttools[lxml,ufo]==4.40.0 +fonttools[lxml,ufo]==4.42.0 defcon==0.10.2 compreffor==0.5.3 booleanOperations==0.9.0 diff --git a/setup.py b/setup.py index 494f7ac48..6376893a7 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ setup_requires=pytest_runner + wheel + ["setuptools_scm"], tests_require=["pytest>=2.8"], install_requires=[ - "fonttools[ufo]>=4.40.0", + "fonttools[ufo]>=4.42.0", "cffsubr>=0.2.8", "booleanOperations>=0.9.0", ], diff --git a/tests/data/TestVariableFont-CFF2-cffsubr.ttx b/tests/data/TestVariableFont-CFF2-cffsubr.ttx index b55692286..229119048 100644 --- a/tests/data/TestVariableFont-CFF2-cffsubr.ttx +++ b/tests/data/TestVariableFont-CFF2-cffsubr.ttx @@ -471,20 +471,20 @@ - + - - + + - + diff --git a/tests/data/TestVariableFont-CFF2-post3.ttx b/tests/data/TestVariableFont-CFF2-post3.ttx index f80c8a030..c34b0e365 100644 --- a/tests/data/TestVariableFont-CFF2-post3.ttx +++ b/tests/data/TestVariableFont-CFF2-post3.ttx @@ -448,20 +448,20 @@ - + - - + + - + diff --git a/tests/data/TestVariableFont-CFF2-sparse-notdefGlyph.ttx b/tests/data/TestVariableFont-CFF2-sparse-notdefGlyph.ttx new file mode 100644 index 000000000..5cfd91194 --- /dev/null +++ b/tests/data/TestVariableFont-CFF2-sparse-notdefGlyph.ttx @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + 50 -250 rmoveto + 400 1000 -400 hlineto + 50 -950 rmoveto + 900 300 -900 vlineto + 100 500 1 blend + 200 rmoveto + 278 -112 222 -138 -138 -112 -222 -278 277 -112 223 -138 -138 -112 -223 -277 8 blend + vhcurveto + + + 468 -1 rmoveto + -21 435 -233 70 -205 -76 27 -91 -56 1 blend + 172 60 155 -40 -59 2 2 blend + 3 -360 56 1 blend + rlineto + 12 266 59 -2 2 blend + rmoveto + -352 -23 3 -218 139 -34 221 83 -6 63 -222 -60 -75 52 15 40 13 37 -21 5 blend + 2 46 294 35 -78 -30 2 blend + rlineto + + + -21 597 -8 28 2 blend + rmoveto + -16 -94 78 -2 9 88 -19 -48 44 7 -4 29 6 blend + rlineto + + + 1 vsindex + 127 228 -1 70 -25 1 2 blend + rmoveto + 449 -2 1 -45 -2 -2 2 blend + -5 79 -255 208 -276 -252 148 -279 338 63 -17 84 -280 -54 -82 188 170 153 163 -124 -355 6 27 0 0 -27 0 36 0 -29 0 -34 0 31 0 -1 0 2 0 -45 -2 13 28 100 37 0 13 0 -2 55 -40 -54 -32 -86 -30 -57 -85 -60 34 57 84 146 -5 0 21 blend + rlineto + + + 127 228 70 1 2 blend + rmoveto + 449 -2 -45 -2 2 blend + -5 79 -255 208 -276 -252 148 -279 338 63 -17 84 -27 36 -29 -34 31 -1 2 -45 13 100 10 blend + -280 -54 -82 188 170 153 163 -124 -355 55 -54 -86 -57 -60 57 146 7 blend + 6 rlineto + 167 395 -84 118 2 blend + rmoveto + -16 -94 78 -2 9 88 -19 -48 44 7 -4 29 6 blend + rlineto + + + 559 459 rmoveto + -235 71 -286 -187 389 -188 -145 -79 -229 98 -28 -91 279 -96 278 187 -369 192 113 76 -22 55 -58 -61 19 49 34 9 9 -56 -2 -41 46 12 29 24 -57 -31 18 blend + 213 -66 rlineto + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/TestVariableFont-CFF2-useProductionNames.ttx b/tests/data/TestVariableFont-CFF2-useProductionNames.ttx index e3d3b540f..5e6b95900 100644 --- a/tests/data/TestVariableFont-CFF2-useProductionNames.ttx +++ b/tests/data/TestVariableFont-CFF2-useProductionNames.ttx @@ -465,20 +465,20 @@ - + - - + + - + diff --git a/tests/data/TestVariableFont-CFF2.ttx b/tests/data/TestVariableFont-CFF2.ttx index d82ba84f9..735005abd 100644 --- a/tests/data/TestVariableFont-CFF2.ttx +++ b/tests/data/TestVariableFont-CFF2.ttx @@ -462,20 +462,20 @@ - + - - + + - + diff --git a/tests/data/TestVariableFont-TTF-post3.ttx b/tests/data/TestVariableFont-TTF-post3.ttx index 4f83e6bd1..eedec8ee2 100644 --- a/tests/data/TestVariableFont-TTF-post3.ttx +++ b/tests/data/TestVariableFont-TTF-post3.ttx @@ -451,20 +451,20 @@ - + - - + + - + diff --git a/tests/data/TestVariableFont-TTF-useProductionNames.ttx b/tests/data/TestVariableFont-TTF-useProductionNames.ttx index 5adc73e7b..1042f99e9 100644 --- a/tests/data/TestVariableFont-TTF-useProductionNames.ttx +++ b/tests/data/TestVariableFont-TTF-useProductionNames.ttx @@ -468,20 +468,20 @@ - + - - + + - + diff --git a/tests/data/TestVariableFont-TTF.ttx b/tests/data/TestVariableFont-TTF.ttx index e6fab0507..4749600e2 100644 --- a/tests/data/TestVariableFont-TTF.ttx +++ b/tests/data/TestVariableFont-TTF.ttx @@ -465,20 +465,20 @@ - + - - + + - + diff --git a/tests/integration_test.py b/tests/integration_test.py index d8a71d6f3..05c2ba0dd 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -515,6 +515,23 @@ def test_compileVariableTTF_notdefGlyph_with_curves(self, designspace): # varLib only warns: https://github.com/fonttools/fonttools/issues/2572 assert ".notdef" in vf["gvar"].variations + def test_compileVariableCFF2_sparse_notdefGlyph(self, designspace): + # test that sparse layer without .notdef does not participate in computation + # of CFF2 and HVAR deltas for the .notdef glypht + for src_idx, transform in ((0, (1, 0, 0, 1, 0, 0)), (2, (2, 0, 0, 2, 0, 0))): + notdef = designspace.sources[src_idx].font[".notdef"] + self.drawCurvedContour(notdef, transform) + designspace.sources[2].font[".notdef"].width *= 2 + assert ".notdef" not in designspace.sources[1].font.layers["Medium"] + + vf = compileVariableCFF2(designspace) + + expectTTX( + vf, + "TestVariableFont-CFF2-sparse-notdefGlyph.ttx", + tables=["CFF2", "hmtx", "HVAR"], + ) + if __name__ == "__main__": sys.exit(pytest.main(sys.argv))