From 39c9783bf88c75988eaa315fbfa2f0674304e489 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Tue, 2 May 2023 14:56:56 +0100 Subject: [PATCH] add filter pen that explicitly emits closing line when lastPt != movePt it can be useful when comparing two paths and testing whether then contain the same number/types of segments --- Lib/fontTools/pens/explicitClosingLinePen.py | 101 +++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 Lib/fontTools/pens/explicitClosingLinePen.py diff --git a/Lib/fontTools/pens/explicitClosingLinePen.py b/Lib/fontTools/pens/explicitClosingLinePen.py new file mode 100644 index 000000000..e3c9c943c --- /dev/null +++ b/Lib/fontTools/pens/explicitClosingLinePen.py @@ -0,0 +1,101 @@ +from fontTools.pens.filterPen import ContourFilterPen + + +class ExplicitClosingLinePen(ContourFilterPen): + """A filter pen that adds an explicit lineTo to the first point of each closed + contour if the end point of the last segment is not already the same as the first point. + Otherwise, it passes the contour through unchanged. + + >>> from pprint import pprint + >>> from fontTools.pens.recordingPen import RecordingPen + >>> rec = RecordingPen() + >>> pen = ExplicitClosingLinePen(rec) + >>> pen.moveTo((0, 0)) + >>> pen.lineTo((100, 0)) + >>> pen.lineTo((100, 100)) + >>> pen.closePath() + >>> pprint(rec.value) + [('moveTo', ((0, 0),)), + ('lineTo', ((100, 0),)), + ('lineTo', ((100, 100),)), + ('lineTo', ((0, 0),)), + ('closePath', ())] + >>> rec = RecordingPen() + >>> pen = ExplicitClosingLinePen(rec) + >>> pen.moveTo((0, 0)) + >>> pen.lineTo((100, 0)) + >>> pen.lineTo((100, 100)) + >>> pen.lineTo((0, 0)) + >>> pen.closePath() + >>> pprint(rec.value) + [('moveTo', ((0, 0),)), + ('lineTo', ((100, 0),)), + ('lineTo', ((100, 100),)), + ('lineTo', ((0, 0),)), + ('closePath', ())] + >>> rec = RecordingPen() + >>> pen = ExplicitClosingLinePen(rec) + >>> pen.moveTo((0, 0)) + >>> pen.curveTo((100, 0), (0, 100), (100, 100)) + >>> pen.closePath() + >>> pprint(rec.value) + [('moveTo', ((0, 0),)), + ('curveTo', ((100, 0), (0, 100), (100, 100))), + ('lineTo', ((0, 0),)), + ('closePath', ())] + >>> rec = RecordingPen() + >>> pen = ExplicitClosingLinePen(rec) + >>> pen.moveTo((0, 0)) + >>> pen.curveTo((100, 0), (0, 100), (100, 100)) + >>> pen.lineTo((0, 0)) + >>> pen.closePath() + >>> pprint(rec.value) + [('moveTo', ((0, 0),)), + ('curveTo', ((100, 0), (0, 100), (100, 100))), + ('lineTo', ((0, 0),)), + ('closePath', ())] + >>> rec = RecordingPen() + >>> pen = ExplicitClosingLinePen(rec) + >>> pen.moveTo((0, 0)) + >>> pen.curveTo((100, 0), (0, 100), (0, 0)) + >>> pen.closePath() + >>> pprint(rec.value) + [('moveTo', ((0, 0),)), + ('curveTo', ((100, 0), (0, 100), (0, 0))), + ('closePath', ())] + >>> rec = RecordingPen() + >>> pen = ExplicitClosingLinePen(rec) + >>> pen.moveTo((0, 0)) + >>> pen.closePath() + >>> pprint(rec.value) + [('moveTo', ((0, 0),)), ('closePath', ())] + >>> rec = RecordingPen() + >>> pen = ExplicitClosingLinePen(rec) + >>> pen.closePath() + >>> pprint(rec.value) + [('closePath', ())] + >>> rec = RecordingPen() + >>> pen = ExplicitClosingLinePen(rec) + >>> pen.moveTo((0, 0)) + >>> pen.lineTo((100, 0)) + >>> pen.lineTo((100, 100)) + >>> pen.endPath() + >>> pprint(rec.value) + [('moveTo', ((0, 0),)), + ('lineTo', ((100, 0),)), + ('lineTo', ((100, 100),)), + ('endPath', ())] + """ + + def filterContour(self, contour): + if ( + not contour + or contour[0][0] != "moveTo" + or contour[-1][0] != "closePath" + or len(contour) < 3 + ): + return + movePt = contour[0][1][0] + lastSeg = contour[-2][1] + if lastSeg and movePt != lastSeg[-1]: + contour[-1:] = [("lineTo", (movePt,)), ("closePath", ())]