dropImpliedOnCurvePoints: raise if incompatible, skip empty/composites
This commit is contained in:
parent
84cebca6a1
commit
6a276d9f6a
@ -1541,6 +1541,8 @@ def dropImpliedOnCurvePoints(*interpolatable_glyphs: Glyph) -> Set[int]:
|
||||
If more than one glyphs are passed, these are assumed to be interpolatable masters
|
||||
of the same glyph impliable, and thus only the on-curve points that are impliable
|
||||
for all of them will actually be implied.
|
||||
Composite glyphs or empty glyphs are skipped, only simple glyphs with 1 or more
|
||||
contours are considered.
|
||||
The input glyph(s) is/are modified in-place.
|
||||
|
||||
Args:
|
||||
@ -1549,16 +1551,40 @@ def dropImpliedOnCurvePoints(*interpolatable_glyphs: Glyph) -> Set[int]:
|
||||
Returns:
|
||||
The set of point indices that were dropped if any.
|
||||
|
||||
Raises:
|
||||
ValueError if simple glyphs are not in fact interpolatable because they have
|
||||
different point flags or number of contours.
|
||||
|
||||
Reference:
|
||||
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
|
||||
"""
|
||||
assert len(interpolatable_glyphs) > 0
|
||||
|
||||
numContours = None
|
||||
flags = None
|
||||
drop = None
|
||||
for glyph in interpolatable_glyphs:
|
||||
simple_glyphs = []
|
||||
for i, glyph in enumerate(interpolatable_glyphs):
|
||||
if glyph.numberOfContours < 1:
|
||||
# ignore composite or empty glyphs
|
||||
continue
|
||||
|
||||
if numContours is None:
|
||||
numContours = glyph.numberOfContours
|
||||
elif glyph.numberOfContours != numContours:
|
||||
raise ValueError(
|
||||
f"Incompatible number of contours for glyph at master index {i}: "
|
||||
f"expected {numContours}, found {glyph.numberOfContours}"
|
||||
)
|
||||
|
||||
if flags is None:
|
||||
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()
|
||||
start = 0
|
||||
flags = glyph.flags
|
||||
coords = glyph.coordinates
|
||||
for last in glyph.endPtsOfContours:
|
||||
for i in range(start, last + 1):
|
||||
@ -1583,9 +1609,11 @@ def dropImpliedOnCurvePoints(*interpolatable_glyphs: Glyph) -> Set[int]:
|
||||
else:
|
||||
drop.intersection_update(may_drop)
|
||||
|
||||
simple_glyphs.append(glyph)
|
||||
|
||||
if drop:
|
||||
# Do the actual dropping
|
||||
for glyph in interpolatable_glyphs:
|
||||
for glyph in simple_glyphs:
|
||||
coords = glyph.coordinates
|
||||
glyph.coordinates = GlyphCoordinates(
|
||||
coords[i] for i in range(len(coords)) if i not in drop
|
||||
@ -1608,7 +1636,7 @@ def dropImpliedOnCurvePoints(*interpolatable_glyphs: Glyph) -> Set[int]:
|
||||
i += 1
|
||||
glyph.endPtsOfContours = newEndPts
|
||||
|
||||
return drop
|
||||
return drop if drop is not None else set()
|
||||
|
||||
|
||||
class GlyphComponent(object):
|
||||
|
@ -860,13 +860,17 @@ def test_dropImpliedOnCurvePoints_all_quad_off_curves():
|
||||
],
|
||||
Transform().scale(2.0),
|
||||
)
|
||||
# also add an empty glyph (will be ignored); we use this trick for 'sparse' masters
|
||||
glyph3 = Glyph()
|
||||
glyph3.numberOfContours = 0
|
||||
|
||||
assert dropImpliedOnCurvePoints(glyph1, glyph2) == {0, 2, 4, 6}
|
||||
assert dropImpliedOnCurvePoints(glyph1, glyph2, glyph3) == {0, 2, 4, 6}
|
||||
|
||||
assert glyph1.flags == glyph2.flags == array.array("B", [0, 0, 0, 0])
|
||||
assert glyph1.coordinates == GlyphCoordinates([(1, 1), (1, -1), (-1, -1), (-1, 1)])
|
||||
assert glyph2.coordinates == GlyphCoordinates([(2, 2), (2, -2), (-2, -2), (-2, 2)])
|
||||
assert glyph1.endPtsOfContours == glyph2.endPtsOfContours == [3]
|
||||
assert glyph3.numberOfContours == 0
|
||||
|
||||
|
||||
def test_dropImpliedOnCurvePoints_all_cubic_off_curves():
|
||||
@ -890,8 +894,10 @@ def test_dropImpliedOnCurvePoints_all_cubic_off_curves():
|
||||
],
|
||||
Transform().translate(10.0),
|
||||
)
|
||||
glyph3 = Glyph()
|
||||
glyph3.numberOfContours = 0
|
||||
|
||||
assert dropImpliedOnCurvePoints(glyph1, glyph2) == {0, 3, 6, 9}
|
||||
assert dropImpliedOnCurvePoints(glyph1, glyph2, glyph3) == {0, 3, 6, 9}
|
||||
|
||||
assert glyph1.flags == glyph2.flags == array.array("B", [flagCubic] * 8)
|
||||
assert glyph1.coordinates == GlyphCoordinates(
|
||||
@ -901,6 +907,7 @@ def test_dropImpliedOnCurvePoints_all_cubic_off_curves():
|
||||
[(11, 1), (11, 1), (11, -1), (11, -1), (9, -1), (9, -1), (9, 1), (9, 1)]
|
||||
)
|
||||
assert glyph1.endPtsOfContours == glyph2.endPtsOfContours == [7]
|
||||
assert glyph3.numberOfContours == 0
|
||||
|
||||
|
||||
def test_dropImpliedOnCurvePoints_not_all_impliable():
|
||||
@ -936,6 +943,49 @@ def test_dropImpliedOnCurvePoints_not_all_impliable():
|
||||
assert glyph2.flags == array.array("B", [0, flagOnCurve, 0, 0, 0])
|
||||
|
||||
|
||||
def test_dropImpliedOnCurvePoints_all_empty_glyphs():
|
||||
glyph1 = Glyph()
|
||||
glyph1.numberOfContours = 0
|
||||
glyph2 = Glyph()
|
||||
glyph2.numberOfContours = 0
|
||||
|
||||
assert dropImpliedOnCurvePoints(glyph1, glyph2) == set()
|
||||
|
||||
|
||||
def test_dropImpliedOnCurvePoints_incompatible_number_of_contours():
|
||||
glyph1 = Glyph()
|
||||
glyph1.numberOfContours = 1
|
||||
glyph1.endPtsOfContours = [3]
|
||||
glyph1.flags = array.array("B", [1, 1, 1, 1])
|
||||
glyph1.coordinates = GlyphCoordinates([(0, 0), (1, 1), (2, 2), (3, 3)])
|
||||
|
||||
glyph2 = Glyph()
|
||||
glyph2.numberOfContours = 2
|
||||
glyph2.endPtsOfContours = [1, 3]
|
||||
glyph2.flags = array.array("B", [1, 1, 1, 1])
|
||||
glyph2.coordinates = GlyphCoordinates([(0, 0), (1, 1), (2, 2), (3, 3)])
|
||||
|
||||
with pytest.raises(ValueError, match="Incompatible number of contours"):
|
||||
dropImpliedOnCurvePoints(glyph1, glyph2)
|
||||
|
||||
|
||||
def test_dropImpliedOnCurvePoints_incompatible_flags():
|
||||
glyph1 = Glyph()
|
||||
glyph1.numberOfContours = 1
|
||||
glyph1.endPtsOfContours = [3]
|
||||
glyph1.flags = array.array("B", [1, 1, 1, 1])
|
||||
glyph1.coordinates = GlyphCoordinates([(0, 0), (1, 1), (2, 2), (3, 3)])
|
||||
|
||||
glyph2 = Glyph()
|
||||
glyph2.numberOfContours = 1
|
||||
glyph2.endPtsOfContours = [3]
|
||||
glyph2.flags = array.array("B", [0, 0, 0, 0])
|
||||
glyph2.coordinates = GlyphCoordinates([(0, 0), (1, 1), (2, 2), (3, 3)])
|
||||
|
||||
with pytest.raises(ValueError, match="Incompatible flags"):
|
||||
dropImpliedOnCurvePoints(glyph1, glyph2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user