Skip to content

Commit

Permalink
Merge pull request #769 from googlefonts/no-convert-cubics
Browse files Browse the repository at this point in the history
add convertCubics to compileVariable; check no cubics if glyphDataFormat=0
  • Loading branch information
anthrotype authored Jul 28, 2023
2 parents 1ff885d + 376b042 commit fbd1c5b
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 40 deletions.
2 changes: 2 additions & 0 deletions Lib/ufo2ft/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ def compileTTF(ufo, **kwargs):
**dict(
preProcessorClass=TTFInterpolatablePreProcessor,
outlineCompilerClass=OutlineTTFCompiler,
convertCubics=True,
cubicConversionError=None,
reverseDirection=True,
flattenComponents=False,
Expand Down Expand Up @@ -564,6 +565,7 @@ def compileFeatures(
**dict(
preProcessorClass=TTFInterpolatablePreProcessor,
outlineCompilerClass=OutlineTTFCompiler,
convertCubics=True,
cubicConversionError=None,
reverseDirection=True,
flattenComponents=False,
Expand Down
13 changes: 12 additions & 1 deletion Lib/ufo2ft/outlineCompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from fontTools.pens.ttGlyphPen import TTGlyphPointPen
from fontTools.ttLib import TTFont, newTable
from fontTools.ttLib.standardGlyphOrder import standardGlyphOrder
from fontTools.ttLib.tables._g_l_y_f import Glyph
from fontTools.ttLib.tables._g_l_y_f import Glyph, flagCubic
from fontTools.ttLib.tables._h_e_a_d import mac_epoch_diff
from fontTools.ttLib.tables.O_S_2f_2 import Panose

Expand Down Expand Up @@ -1462,6 +1462,7 @@ def compileGlyphs(self):
allGlyphs = self.allGlyphs
ttGlyphs = {}
round = otRound if self.roundCoordinates else noRound
glyphDataFormat = self.glyphDataFormat
for name in self.glyphOrder:
glyph = allGlyphs[name]
pen = TTGlyphPointPen(allGlyphs)
Expand All @@ -1475,6 +1476,16 @@ def compileGlyphs(self):
dropImpliedOnCurves=self.dropImpliedOnCurves,
round=round,
)
if (
glyphDataFormat == 0
and ttGlyph.numberOfContours > 0
and any(f & flagCubic for f in ttGlyph.flags)
):
raise ValueError(
f"{name!r} has cubic Bezier curves, but glyphDataFormat=0; "
"either convert to quadratic (convertCubics=True) or use "
"allQuadratic=False so that glyphDataFormat=1."
)
ttGlyphs[name] = ttGlyph
return ttGlyphs

Expand Down
19 changes: 11 additions & 8 deletions Lib/ufo2ft/preProcessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ def __init__(
ufos,
inplace=False,
flattenComponents=False,
convertCubics=True,
conversionError=None,
reverseDirection=True,
rememberCurveType=True,
Expand Down Expand Up @@ -289,6 +290,7 @@ def __init__(
)
for ufo, layerName in zip(ufos, layerNames)
]
self.convertCubics = convertCubics
self._conversionErrors = [
(conversionError or DEFAULT_MAX_ERR)
* getAttrWithFallback(ufo.info, "unitsPerEm")
Expand Down Expand Up @@ -334,14 +336,15 @@ def process(self):
for func in funcs:
func(ufo, glyphSet)

fonts_to_quadratic(
self.glyphSets,
max_err=self._conversionErrors,
reverse_direction=self._reverseDirection,
dump_stats=True,
remember_curve_type=self._rememberCurveType and self.inplace,
all_quadratic=self.allQuadratic,
)
if self.convertCubics:
fonts_to_quadratic(
self.glyphSets,
max_err=self._conversionErrors,
reverse_direction=self._reverseDirection,
dump_stats=True,
remember_curve_type=self._rememberCurveType and self.inplace,
all_quadratic=self.allQuadratic,
)

# TrueType fonts cannot mix contours and components, so pick out all glyphs
# that have contours (`bool(len(g)) == True`) and decompose their
Expand Down
62 changes: 31 additions & 31 deletions tests/outlineCompiler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,22 @@ def emptyufo(FontClass):


class OutlineTTFCompilerTest:
def test_compile_with_gasp(self, testufo):
compiler = OutlineTTFCompiler(testufo)
def test_compile_with_gasp(self, quadufo):
compiler = OutlineTTFCompiler(quadufo)
compiler.compile()
assert "gasp" in compiler.otf
assert compiler.otf["gasp"].gaspRange == {7: 10, 65535: 15}

def test_compile_without_gasp(self, testufo):
testufo.info.openTypeGaspRangeRecords = None
compiler = OutlineTTFCompiler(testufo)
def test_compile_without_gasp(self, quadufo):
quadufo.info.openTypeGaspRangeRecords = None
compiler = OutlineTTFCompiler(quadufo)
compiler.compile()
assert "gasp" not in compiler.otf

def test_compile_empty_gasp(self, testufo):
def test_compile_empty_gasp(self, quadufo):
# ignore empty gasp
testufo.info.openTypeGaspRangeRecords = []
compiler = OutlineTTFCompiler(testufo)
quadufo.info.openTypeGaspRangeRecords = []
compiler = OutlineTTFCompiler(quadufo)
compiler.compile()
assert "gasp" not in compiler.otf

Expand Down Expand Up @@ -150,10 +150,10 @@ def test_no_contour_glyphs(self, testufo):
assert compiler.otf["hhea"].minRightSideBearing == 0
assert compiler.otf["hhea"].xMaxExtent == 0

def test_os2_no_widths(self, testufo):
for glyph in testufo:
def test_os2_no_widths(self, quadufo):
for glyph in quadufo:
glyph.width = 0
compiler = OutlineTTFCompiler(testufo)
compiler = OutlineTTFCompiler(quadufo)
compiler.compile()
assert compiler.otf["OS/2"].xAvgCharWidth == 0

Expand Down Expand Up @@ -220,8 +220,8 @@ def test_contour_starts_with_offcurve_point(self, emptyufo):
assert endPts == [4]
assert list(flags) == [0, 0, 0, 0, 1]

def test_setupTable_meta(self, testufo):
testufo.lib["public.openTypeMeta"] = {
def test_setupTable_meta(self, quadufo):
quadufo.lib["public.openTypeMeta"] = {
"appl": b"BEEF",
"bild": b"AAAA",
"dlng": ["en-Latn", "nl-Latn"],
Expand All @@ -231,7 +231,7 @@ def test_setupTable_meta(self, testufo):
"PRIU": "Some private unicode string…",
}

compiler = OutlineTTFCompiler(testufo)
compiler = OutlineTTFCompiler(quadufo)
ttFont = compiler.compile()
meta = ttFont["meta"]

Expand All @@ -243,13 +243,13 @@ def test_setupTable_meta(self, testufo):
assert meta.data["PRIA"] == b"Some private ascii string"
assert meta.data["PRIU"] == "Some private unicode string…".encode("utf-8")

def test_setupTable_name(self, testufo):
compiler = OutlineTTFCompiler(testufo)
def test_setupTable_name(self, quadufo):
compiler = OutlineTTFCompiler(quadufo)
compiler.compile()
actual = compiler.otf["name"].getName(1, 3, 1, 1033).string
assert actual == "Some Font Regular (Style Map Family Name)"

testufo.info.openTypeNameRecords.append(
quadufo.info.openTypeNameRecords.append(
{
"nameID": 1,
"platformID": 3,
Expand All @@ -258,20 +258,20 @@ def test_setupTable_name(self, testufo):
"string": "Custom Name for Windows",
}
)
compiler = OutlineTTFCompiler(testufo)
compiler = OutlineTTFCompiler(quadufo)
compiler.compile()
actual = compiler.otf["name"].getName(1, 3, 1, 1033).string
assert actual == "Custom Name for Windows"

def test_post_underline_without_public_key(self, testufo):
compiler = OutlineTTFCompiler(testufo)
def test_post_underline_without_public_key(self, quadufo):
compiler = OutlineTTFCompiler(quadufo)
compiler.compile()
actual = compiler.otf["post"].underlinePosition
assert actual == -200

def test_post_underline_with_public_key(self, testufo):
testufo.lib[OPENTYPE_POST_UNDERLINE_POSITION_KEY] = -485
compiler = OutlineTTFCompiler(testufo)
def test_post_underline_with_public_key(self, quadufo):
quadufo.lib[OPENTYPE_POST_UNDERLINE_POSITION_KEY] = -485
compiler = OutlineTTFCompiler(quadufo)
compiler.compile()
actual = compiler.otf["post"].underlinePosition
assert actual == -485
Expand Down Expand Up @@ -687,7 +687,7 @@ def test_underline_ps_rounding(self, testufo):


class GlyphOrderTest:
def test_compile_original_glyph_order(self, testufo):
def test_compile_original_glyph_order(self, quadufo):
DEFAULT_ORDER = [
".notdef",
"space",
Expand All @@ -704,11 +704,11 @@ def test_compile_original_glyph_order(self, testufo):
"k",
"l",
]
compiler = OutlineTTFCompiler(testufo)
compiler = OutlineTTFCompiler(quadufo)
compiler.compile()
assert compiler.otf.getGlyphOrder() == DEFAULT_ORDER

def test_compile_tweaked_glyph_order(self, testufo):
def test_compile_tweaked_glyph_order(self, quadufo):
NEW_ORDER = [
".notdef",
"space",
Expand All @@ -725,12 +725,12 @@ def test_compile_tweaked_glyph_order(self, testufo):
"k",
"l",
]
testufo.lib["public.glyphOrder"] = NEW_ORDER
compiler = OutlineTTFCompiler(testufo)
quadufo.lib["public.glyphOrder"] = NEW_ORDER
compiler = OutlineTTFCompiler(quadufo)
compiler.compile()
assert compiler.otf.getGlyphOrder() == NEW_ORDER

def test_compile_strange_glyph_order(self, testufo):
def test_compile_strange_glyph_order(self, quadufo):
"""Move space and .notdef to end of glyph ids
ufo2ft always puts .notdef first.
"""
Expand All @@ -751,8 +751,8 @@ def test_compile_strange_glyph_order(self, testufo):
"k",
"l",
]
testufo.lib["public.glyphOrder"] = NEW_ORDER
compiler = OutlineTTFCompiler(testufo)
quadufo.lib["public.glyphOrder"] = NEW_ORDER
compiler = OutlineTTFCompiler(quadufo)
compiler.compile()
assert compiler.otf.getGlyphOrder() == EXPECTED_ORDER

Expand Down

0 comments on commit fbd1c5b

Please sign in to comment.