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:
parent
0690703b86
commit
5b93100616
@ -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()
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user