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

Fix issue with sparse masters when .notdef contain cubic curves #772

Merged
merged 2 commits into from
Aug 1, 2023
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
2 changes: 1 addition & 1 deletion Lib/ufo2ft/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ def compileInterpolatableTTFsFromDS(designSpaceDoc, **kwargs):
kwargs["skipExportGlyphs"] = designSpaceDoc.lib.get("public.skipExportGlyphs", [])

if kwargs["notdefGlyph"] is None:
kwargs["notdefGlyph"] = _getDefaultNotdefGlyph(designSpaceDoc)
kwargs["notdefGlyph"] = _getDefaultNotdefGlyph(designSpaceDoc, empty=True)

kwargs["extraSubstitutions"] = defaultdict(set)
for rule in designSpaceDoc.rules:
Expand Down
14 changes: 13 additions & 1 deletion Lib/ufo2ft/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ def getDefaultMasterFont(designSpaceDoc):
return defaultSource.font


def _getDefaultNotdefGlyph(designSpaceDoc):
def _getDefaultNotdefGlyph(designSpaceDoc, empty=False):
from ufo2ft.errors import InvalidDesignSpaceData

try:
Expand All @@ -488,6 +488,18 @@ def _getDefaultNotdefGlyph(designSpaceDoc):
notdefGlyph = baseUfo[".notdef"]
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
return notdefGlyph


Expand Down
40 changes: 34 additions & 6 deletions tests/integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

import pytest
from fontTools.pens.boundsPen import BoundsPen
from fontTools.ttLib.tables._g_l_y_f import OVERLAP_COMPOUND, flagOverlapSimple
from fontTools.ttLib.tables._g_l_y_f import (
OVERLAP_COMPOUND,
flagCubic,
flagOverlapSimple,
)

from ufo2ft import (
compileInterpolatableTTFs,
Expand Down Expand Up @@ -450,17 +454,21 @@ def test_compileTTF_glyf1_not_allQuadratic(self, testufo):

assert ttf["head"].glyphDataFormat == 1

@staticmethod
def drawCurvedContour(glyph):
pen = glyph.getPen()
pen.moveTo((500, 0))
pen.curveTo((500, 277.614), (388.072, 500), (250, 500))
pen.curveTo((111.928, 500), (0, 277.614), (0, 0))
pen.closePath()

def test_compileVariableTTF_glyf1_not_allQuadratic(self, designspace):
base_master = designspace.findDefault()
assert base_master is not None
# add a glyph with some curveTo to exercise the cu2qu codepath
glyph = base_master.font.newGlyph("curved")
glyph.width = 1000
pen = glyph.getPen()
pen.moveTo((500, 0))
pen.curveTo((500, 277.614), (388.072, 500), (250, 500))
pen.curveTo((111.928, 500), (0, 277.614), (0, 0))
pen.closePath()
self.drawCurvedContour(glyph)

vf = compileVariableTTF(designspace, allQuadratic=False)
expectTTX(vf, "TestVariableFont-TTF-not-allQuadratic.ttx", tables=["glyf"])
Expand All @@ -480,6 +488,26 @@ def test_compileTTF_overlap_simple_flag(self, testufo):
assert not ttf["glyf"]["g"].components[0].flags & OVERLAP_COMPOUND
assert ttf["glyf"]["h"].components[0].flags & OVERLAP_COMPOUND

def test_compileVariableTTF_notdefGlyph_with_curves(self, designspace):
# The test DS contains two full masters (Regular and Bold) and one intermediate
# 'sparse' (Medium) master, which does not contain a .notdef glyph and as such
# is supposed to inherit one from the default master. If the notdef contains
# any curves, an error occured because these are weren't been converted to
# quadratic: https://github.com/googlefonts/ufo2ft/issues/501

# First we draw an additional contour containing cubic curves in the Regular
# and Bold's .notdef glyphs
for src_idx in (0, 2):
notdef = designspace.sources[src_idx].font[".notdef"]
self.drawCurvedContour(notdef)
assert ".notdef" not in designspace.sources[1].font.layers["Medium"]

# this must NOT fail!
vf = compileVariableTTF(designspace, convertCubics=True, allQuadratic=True)

# and because allQuadratic=True, we expect .notdef contains no cubic curves
assert not any(f & flagCubic for f in vf["glyf"][".notdef"].flags)


if __name__ == "__main__":
sys.exit(pytest.main(sys.argv))
Loading