diff --git a/Lib/fontTools/misc/bezierTools.py b/Lib/fontTools/misc/bezierTools.py index 1b37ade8d..2021f2443 100644 --- a/Lib/fontTools/misc/bezierTools.py +++ b/Lib/fontTools/misc/bezierTools.py @@ -631,7 +631,14 @@ def splitCubicAtT(pt1, pt2, pt3, pt4, *ts): ((77.3438, 56.25), (85.9375, 43.75), (93.75, 25), (100, 0)) """ a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4) - return _splitCubicAtT(a, b, c, d, *ts) + split = _splitCubicAtT(a, b, c, d, *ts) + + # the split impl can introduce floating point errors; we know the first + # segment should always start at pt1 and the last segment should end at pt4, + # so we set those values directly before returning. + split[0] = (pt1, *split[0][1:]) + split[-1] = (*split[-1][:-1], pt4) + return split @cython.locals( diff --git a/Tests/misc/bezierTools_test.py b/Tests/misc/bezierTools_test.py index 6107a39df..fe7a3d214 100644 --- a/Tests/misc/bezierTools_test.py +++ b/Tests/misc/bezierTools_test.py @@ -144,6 +144,12 @@ def test_splitCubicAtT(): ] +def test_splitCubicAtT_robustness(): + segment = ((-103, -231), (-61, -240), (-31.009, -245), (6, -245)) + (_, tail) = splitCubicAtT(*segment, 0.386637) + assert tail[-1] == segment[-1] + + def test_solveCubic(): assert solveCubic(1, 1, -6, 0) == [-3.0, -0.0, 2.0] assert solveCubic(-10.0, -9.0, 48.0, -29.0) == [-2.9, 1.0, 1.0]