diff --git a/Lib/fontTools/pens/reverseContourPen.py b/Lib/fontTools/pens/reverseContourPen.py index 9b3241b6b..ee6dedde2 100644 --- a/Lib/fontTools/pens/reverseContourPen.py +++ b/Lib/fontTools/pens/reverseContourPen.py @@ -14,11 +14,15 @@ class ReverseContourPen(ContourFilterPen): the first point. """ + def __init__(self, outPen, outputImpliedClosingLine=False): + super().__init__(outPen) + self.outputImpliedClosingLine = outputImpliedClosingLine + def filterContour(self, contour): - return reversedContour(contour) + return reversedContour(contour, self.outputImpliedClosingLine) -def reversedContour(contour): +def reversedContour(contour, outputImpliedClosingLine=False): """ Generator that takes a list of pen's (operator, operands) tuples, and yields them with the winding direction reversed. """ @@ -60,7 +64,7 @@ def reversedContour(contour): if closed: # for closed paths, we keep the starting point yield firstType, firstPts - if firstOnCurve != lastOnCurve: + if outputImpliedClosingLine or firstOnCurve != lastOnCurve: # emit an implied line between the last and first points yield "lineTo", (lastOnCurve,) contour[-1] = (lastType, @@ -71,15 +75,17 @@ def reversedContour(contour): else: # contour has only two points, the second and last are the same secondType, secondPts = lastType, lastPts - # if a lineTo follows the initial moveTo, after reversing it - # will be implied by the closePath, so we don't emit one; - # unless the lineTo and moveTo overlap, in which case we keep the - # duplicate points - if secondType == "lineTo" and firstPts != secondPts: - del contour[0] - if contour: - contour[-1] = (lastType, - tuple(lastPts[:-1]) + secondPts) + + if not outputImpliedClosingLine: + # if a lineTo follows the initial moveTo, after reversing it + # will be implied by the closePath, so we don't emit one; + # unless the lineTo and moveTo overlap, in which case we keep the + # duplicate points + if secondType == "lineTo" and firstPts != secondPts: + del contour[0] + if contour: + contour[-1] = (lastType, + tuple(lastPts[:-1]) + secondPts) else: # for open paths, the last point will become the first yield firstType, (lastOnCurve,) diff --git a/Tests/pens/reverseContourPen_test.py b/Tests/pens/reverseContourPen_test.py index 9c715404c..912fa9b4c 100644 --- a/Tests/pens/reverseContourPen_test.py +++ b/Tests/pens/reverseContourPen_test.py @@ -310,6 +310,24 @@ def test_reverse_pen(contour, expected): getattr(revpen, operator)(*operands) assert recpen.value == expected +def test_reverse_pen_outputImpliedClosingLine(): + recpen = RecordingPen() + revpen = ReverseContourPen(recpen) + revpen.moveTo((0, 0)) + revpen.lineTo((10, 0)) + revpen.lineTo((0, 10)) + revpen.lineTo((0, 0)) + revpen.closePath() + assert len(recpen.value) == 4 + + recpen = RecordingPen() + revpen = ReverseContourPen(recpen, outputImpliedClosingLine=True) + revpen.moveTo((0, 0)) + revpen.lineTo((10, 0)) + revpen.lineTo((0, 10)) + revpen.lineTo((0, 0)) + revpen.closePath() + assert len(recpen.value) == 6 @pytest.mark.parametrize("contour, expected", TEST_DATA) def test_reverse_point_pen(contour, expected):