also check endPtsOfContours in dropImpliedOnCurvePoints

as per review https://github.com/fonttools/fonttools/pull/3147#discussion_r1214708207

Also, don't do same work multiple times when dropping points from flags/endPtsOfContours arrays since these are supposed to be the same for all interpolatable glyphs
This commit is contained in:
Cosimo Lupo 2023-06-05 12:11:17 +01:00
parent 0690703b86
commit 5b93100616
No known key found for this signature in database
GPG Key ID: DF65A8A5A119C9A8
2 changed files with 56 additions and 35 deletions

View File

@ -28,6 +28,7 @@ from fontTools.misc import xmlWriter
from fontTools.misc.filenames import userNameToFileName from fontTools.misc.filenames import userNameToFileName
from fontTools.misc.loggingTools import deprecateFunction from fontTools.misc.loggingTools import deprecateFunction
from enum import IntFlag from enum import IntFlag
from types import SimpleNamespace
from typing import Set from typing import Set
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -1558,8 +1559,9 @@ def dropImpliedOnCurvePoints(*interpolatable_glyphs: Glyph) -> Set[int]:
Reference: Reference:
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
""" """
numContours = None staticAttributes = SimpleNamespace(
flags = None numberOfContours=None, flags=None, endPtsOfContours=None
)
drop = None drop = None
simple_glyphs = [] simple_glyphs = []
for i, glyph in enumerate(interpolatable_glyphs): for i, glyph in enumerate(interpolatable_glyphs):
@ -1567,26 +1569,23 @@ def dropImpliedOnCurvePoints(*interpolatable_glyphs: Glyph) -> Set[int]:
# ignore composite or empty glyphs # ignore composite or empty glyphs
continue continue
if numContours is None: for attr in staticAttributes.__dict__:
numContours = glyph.numberOfContours expected = getattr(staticAttributes, attr)
elif glyph.numberOfContours != numContours: found = getattr(glyph, attr)
raise ValueError( if expected is None:
f"Incompatible number of contours for glyph at master index {i}: " setattr(staticAttributes, attr, found)
f"expected {numContours}, found {glyph.numberOfContours}" elif expected != found:
) raise ValueError(
f"Incompatible {attr} for glyph at master index {i}: "
if flags is None: f"expected {expected}, found {found}"
flags = glyph.flags )
elif glyph.flags != flags:
raise ValueError(
f"Incompatible flags for simple glyph at master index {i}: "
f"expected {flags}, found {glyph.flags}"
)
may_drop = set() may_drop = set()
start = 0 start = 0
coords = glyph.coordinates coords = glyph.coordinates
for last in glyph.endPtsOfContours: flags = staticAttributes.flags
endPtsOfContours = staticAttributes.endPtsOfContours
for last in endPtsOfContours:
for i in range(start, last + 1): for i in range(start, last + 1):
if not (flags[i] & flagOnCurve): if not (flags[i] & flagOnCurve):
continue continue
@ -1613,27 +1612,32 @@ def dropImpliedOnCurvePoints(*interpolatable_glyphs: Glyph) -> Set[int]:
if drop: if drop:
# Do the actual dropping # Do the actual dropping
flags = staticAttributes.flags
assert flags is not None
newFlags = array.array(
"B", (flags[i] for i in range(len(flags)) if i not in drop)
)
endPts = staticAttributes.endPtsOfContours
assert endPts is not None
newEndPts = []
i = 0
delta = 0
for d in sorted(drop):
while d > endPts[i]:
newEndPts.append(endPts[i] - delta)
i += 1
delta += 1
while i < len(endPts):
newEndPts.append(endPts[i] - delta)
i += 1
for glyph in simple_glyphs: for glyph in simple_glyphs:
coords = glyph.coordinates coords = glyph.coordinates
glyph.coordinates = GlyphCoordinates( glyph.coordinates = GlyphCoordinates(
coords[i] for i in range(len(coords)) if i not in drop coords[i] for i in range(len(coords)) if i not in drop
) )
glyph.flags = array.array( glyph.flags = newFlags
"B", (flags[i] for i in range(len(flags)) if i not in drop)
)
endPts = glyph.endPtsOfContours
newEndPts = []
i = 0
delta = 0
for d in sorted(drop):
while d > endPts[i]:
newEndPts.append(endPts[i] - delta)
i += 1
delta += 1
while i < len(endPts):
newEndPts.append(endPts[i] - delta)
i += 1
glyph.endPtsOfContours = newEndPts glyph.endPtsOfContours = newEndPts
return drop if drop is not None else set() return drop if drop is not None else set()

View File

@ -965,7 +965,7 @@ def test_dropImpliedOnCurvePoints_incompatible_number_of_contours():
glyph2.flags = array.array("B", [1, 1, 1, 1]) glyph2.flags = array.array("B", [1, 1, 1, 1])
glyph2.coordinates = GlyphCoordinates([(0, 0), (1, 1), (2, 2), (3, 3)]) glyph2.coordinates = GlyphCoordinates([(0, 0), (1, 1), (2, 2), (3, 3)])
with pytest.raises(ValueError, match="Incompatible number of contours"): with pytest.raises(ValueError, match="Incompatible numberOfContours"):
dropImpliedOnCurvePoints(glyph1, glyph2) dropImpliedOnCurvePoints(glyph1, glyph2)
@ -986,6 +986,23 @@ def test_dropImpliedOnCurvePoints_incompatible_flags():
dropImpliedOnCurvePoints(glyph1, glyph2) dropImpliedOnCurvePoints(glyph1, glyph2)
def test_dropImpliedOnCurvePoints_incompatible_endPtsOfContours():
glyph1 = Glyph()
glyph1.numberOfContours = 2
glyph1.endPtsOfContours = [2, 6]
glyph1.flags = array.array("B", [1, 1, 1, 1, 1, 1, 1])
glyph1.coordinates = GlyphCoordinates([(i, i) for i in range(7)])
glyph2 = Glyph()
glyph2.numberOfContours = 2
glyph2.endPtsOfContours = [3, 6]
glyph2.flags = array.array("B", [1, 1, 1, 1, 1, 1, 1])
glyph2.coordinates = GlyphCoordinates([(i, i) for i in range(7)])
with pytest.raises(ValueError, match="Incompatible endPtsOfContours"):
dropImpliedOnCurvePoints(glyph1, glyph2)
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys