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
|
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
|
of the same glyph impliable, and thus only the on-curve points that are impliable
|
||||||
for all of them will actually be implied.
|
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.
|
The input glyph(s) is/are modified in-place.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -1549,16 +1551,40 @@ def dropImpliedOnCurvePoints(*interpolatable_glyphs: Glyph) -> Set[int]:
|
|||||||
Returns:
|
Returns:
|
||||||
The set of point indices that were dropped if any.
|
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:
|
Reference:
|
||||||
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
|
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
|
||||||
"""
|
"""
|
||||||
assert len(interpolatable_glyphs) > 0
|
numContours = None
|
||||||
|
flags = None
|
||||||
drop = 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()
|
may_drop = set()
|
||||||
start = 0
|
start = 0
|
||||||
flags = glyph.flags
|
|
||||||
coords = glyph.coordinates
|
coords = glyph.coordinates
|
||||||
for last in glyph.endPtsOfContours:
|
for last in glyph.endPtsOfContours:
|
||||||
for i in range(start, last + 1):
|
for i in range(start, last + 1):
|
||||||
@ -1583,9 +1609,11 @@ def dropImpliedOnCurvePoints(*interpolatable_glyphs: Glyph) -> Set[int]:
|
|||||||
else:
|
else:
|
||||||
drop.intersection_update(may_drop)
|
drop.intersection_update(may_drop)
|
||||||
|
|
||||||
|
simple_glyphs.append(glyph)
|
||||||
|
|
||||||
if drop:
|
if drop:
|
||||||
# Do the actual dropping
|
# Do the actual dropping
|
||||||
for glyph in interpolatable_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
|
||||||
@ -1608,7 +1636,7 @@ def dropImpliedOnCurvePoints(*interpolatable_glyphs: Glyph) -> Set[int]:
|
|||||||
i += 1
|
i += 1
|
||||||
glyph.endPtsOfContours = newEndPts
|
glyph.endPtsOfContours = newEndPts
|
||||||
|
|
||||||
return drop
|
return drop if drop is not None else set()
|
||||||
|
|
||||||
|
|
||||||
class GlyphComponent(object):
|
class GlyphComponent(object):
|
||||||
|
@ -860,13 +860,17 @@ def test_dropImpliedOnCurvePoints_all_quad_off_curves():
|
|||||||
],
|
],
|
||||||
Transform().scale(2.0),
|
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.flags == glyph2.flags == array.array("B", [0, 0, 0, 0])
|
||||||
assert glyph1.coordinates == GlyphCoordinates([(1, 1), (1, -1), (-1, -1), (-1, 1)])
|
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 glyph2.coordinates == GlyphCoordinates([(2, 2), (2, -2), (-2, -2), (-2, 2)])
|
||||||
assert glyph1.endPtsOfContours == glyph2.endPtsOfContours == [3]
|
assert glyph1.endPtsOfContours == glyph2.endPtsOfContours == [3]
|
||||||
|
assert glyph3.numberOfContours == 0
|
||||||
|
|
||||||
|
|
||||||
def test_dropImpliedOnCurvePoints_all_cubic_off_curves():
|
def test_dropImpliedOnCurvePoints_all_cubic_off_curves():
|
||||||
@ -890,8 +894,10 @@ def test_dropImpliedOnCurvePoints_all_cubic_off_curves():
|
|||||||
],
|
],
|
||||||
Transform().translate(10.0),
|
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.flags == glyph2.flags == array.array("B", [flagCubic] * 8)
|
||||||
assert glyph1.coordinates == GlyphCoordinates(
|
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)]
|
[(11, 1), (11, 1), (11, -1), (11, -1), (9, -1), (9, -1), (9, 1), (9, 1)]
|
||||||
)
|
)
|
||||||
assert glyph1.endPtsOfContours == glyph2.endPtsOfContours == [7]
|
assert glyph1.endPtsOfContours == glyph2.endPtsOfContours == [7]
|
||||||
|
assert glyph3.numberOfContours == 0
|
||||||
|
|
||||||
|
|
||||||
def test_dropImpliedOnCurvePoints_not_all_impliable():
|
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])
|
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__":
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user