[bezierTools] Fix infinite-recursion in calcCubicArcLength

Fixes https://github.com/fonttools/fonttools/issues/3502
This commit is contained in:
Behdad Esfahbod 2024-05-07 08:54:42 -07:00
parent 9acbd12637
commit 2ce45c2861
2 changed files with 24 additions and 1 deletions

View File

@ -18,6 +18,9 @@ except (AttributeError, ImportError):
COMPILED = False COMPILED = False
EPSILON = 1e-9
Intersection = namedtuple("Intersection", ["pt", "t1", "t2"]) Intersection = namedtuple("Intersection", ["pt", "t1", "t2"])
@ -92,7 +95,7 @@ def _split_cubic_into_two(p0, p1, p2, p3):
def _calcCubicArcLengthCRecurse(mult, p0, p1, p2, p3): def _calcCubicArcLengthCRecurse(mult, p0, p1, p2, p3):
arch = abs(p0 - p3) arch = abs(p0 - p3)
box = abs(p0 - p1) + abs(p1 - p2) + abs(p2 - p3) box = abs(p0 - p1) + abs(p1 - p2) + abs(p2 - p3)
if arch * mult >= box: if arch * mult + EPSILON >= box:
return (arch + box) * 0.5 return (arch + box) * 0.5
else: else:
one, two = _split_cubic_into_two(p0, p1, p2, p3) one, two = _split_cubic_into_two(p0, p1, p2, p3)

View File

@ -3,6 +3,7 @@ from fontTools.misc.bezierTools import (
calcQuadraticBounds, calcQuadraticBounds,
calcQuadraticArcLength, calcQuadraticArcLength,
calcCubicBounds, calcCubicBounds,
calcCubicArcLength,
curveLineIntersections, curveLineIntersections,
curveCurveIntersections, curveCurveIntersections,
segmentPointAtT, segmentPointAtT,
@ -192,6 +193,25 @@ def test_calcQuadraticArcLength():
) == pytest.approx(127.9225) ) == pytest.approx(127.9225)
@pytest.mark.parametrize(
"segment, expectedLength",
[
(
# https://github.com/fonttools/fonttools/issues/3502
((377, 469), (377, 468), (377, 472), (377, 472)), # off by one unit
3.32098765445,
),
(
# https://github.com/fonttools/fonttools/issues/3502
((242, 402), (242, 403), (242, 399), (242, 399)), # off by one unit
3.32098765445,
),
],
)
def test_calcCubicArcLength(segment, expectedLength):
assert calcCubicArcLength(*segment) == pytest.approx(expectedLength)
def test_intersections_linelike(): def test_intersections_linelike():
seg1 = [(0.0, 0.0), (0.0, 0.25), (0.0, 0.75), (0.0, 1.0)] seg1 = [(0.0, 0.0), (0.0, 0.25), (0.0, 0.75), (0.0, 1.0)]
seg2 = [(0.0, 0.5), (0.25, 0.5), (0.75, 0.5), (1.0, 0.5)] seg2 = [(0.0, 0.5), (0.25, 0.5), (0.75, 0.5), (1.0, 0.5)]