xref: /aosp_15_r20/external/fonttools/Tests/pens/pointPen_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesimport unittest
2*e1fe3e4aSElliott Hughes
3*e1fe3e4aSElliott Hughesfrom fontTools.pens.basePen import AbstractPen
4*e1fe3e4aSElliott Hughesfrom fontTools.pens.pointPen import (
5*e1fe3e4aSElliott Hughes    AbstractPointPen,
6*e1fe3e4aSElliott Hughes    PointToSegmentPen,
7*e1fe3e4aSElliott Hughes    SegmentToPointPen,
8*e1fe3e4aSElliott Hughes    GuessSmoothPointPen,
9*e1fe3e4aSElliott Hughes    ReverseContourPointPen,
10*e1fe3e4aSElliott Hughes)
11*e1fe3e4aSElliott Hughes
12*e1fe3e4aSElliott Hughes
13*e1fe3e4aSElliott Hughesclass _TestSegmentPen(AbstractPen):
14*e1fe3e4aSElliott Hughes    def __init__(self):
15*e1fe3e4aSElliott Hughes        self._commands = []
16*e1fe3e4aSElliott Hughes
17*e1fe3e4aSElliott Hughes    def __repr__(self):
18*e1fe3e4aSElliott Hughes        return " ".join(self._commands)
19*e1fe3e4aSElliott Hughes
20*e1fe3e4aSElliott Hughes    def moveTo(self, pt):
21*e1fe3e4aSElliott Hughes        self._commands.append("%s %s moveto" % (pt[0], pt[1]))
22*e1fe3e4aSElliott Hughes
23*e1fe3e4aSElliott Hughes    def lineTo(self, pt):
24*e1fe3e4aSElliott Hughes        self._commands.append("%s %s lineto" % (pt[0], pt[1]))
25*e1fe3e4aSElliott Hughes
26*e1fe3e4aSElliott Hughes    def curveTo(self, *pts):
27*e1fe3e4aSElliott Hughes        pts = ["%s %s" % pt for pt in pts]
28*e1fe3e4aSElliott Hughes        self._commands.append("%s curveto" % " ".join(pts))
29*e1fe3e4aSElliott Hughes
30*e1fe3e4aSElliott Hughes    def qCurveTo(self, *pts):
31*e1fe3e4aSElliott Hughes        pts = ["%s %s" % pt if pt is not None else "None" for pt in pts]
32*e1fe3e4aSElliott Hughes        self._commands.append("%s qcurveto" % " ".join(pts))
33*e1fe3e4aSElliott Hughes
34*e1fe3e4aSElliott Hughes    def closePath(self):
35*e1fe3e4aSElliott Hughes        self._commands.append("closepath")
36*e1fe3e4aSElliott Hughes
37*e1fe3e4aSElliott Hughes    def endPath(self):
38*e1fe3e4aSElliott Hughes        self._commands.append("endpath")
39*e1fe3e4aSElliott Hughes
40*e1fe3e4aSElliott Hughes    def addComponent(self, glyphName, transformation):
41*e1fe3e4aSElliott Hughes        self._commands.append("'%s' %s addcomponent" % (glyphName, transformation))
42*e1fe3e4aSElliott Hughes
43*e1fe3e4aSElliott Hughes
44*e1fe3e4aSElliott Hughesdef _reprKwargs(kwargs):
45*e1fe3e4aSElliott Hughes    items = []
46*e1fe3e4aSElliott Hughes    for key in sorted(kwargs):
47*e1fe3e4aSElliott Hughes        value = kwargs[key]
48*e1fe3e4aSElliott Hughes        if isinstance(value, str):
49*e1fe3e4aSElliott Hughes            items.append("%s='%s'" % (key, value))
50*e1fe3e4aSElliott Hughes        else:
51*e1fe3e4aSElliott Hughes            items.append("%s=%s" % (key, value))
52*e1fe3e4aSElliott Hughes    return items
53*e1fe3e4aSElliott Hughes
54*e1fe3e4aSElliott Hughes
55*e1fe3e4aSElliott Hughesclass _TestPointPen(AbstractPointPen):
56*e1fe3e4aSElliott Hughes    def __init__(self):
57*e1fe3e4aSElliott Hughes        self._commands = []
58*e1fe3e4aSElliott Hughes
59*e1fe3e4aSElliott Hughes    def __repr__(self):
60*e1fe3e4aSElliott Hughes        return " ".join(self._commands)
61*e1fe3e4aSElliott Hughes
62*e1fe3e4aSElliott Hughes    def beginPath(self, identifier=None, **kwargs):
63*e1fe3e4aSElliott Hughes        items = []
64*e1fe3e4aSElliott Hughes        if identifier is not None:
65*e1fe3e4aSElliott Hughes            items.append("identifier='%s'" % identifier)
66*e1fe3e4aSElliott Hughes        items.extend(_reprKwargs(kwargs))
67*e1fe3e4aSElliott Hughes        self._commands.append("beginPath(%s)" % ", ".join(items))
68*e1fe3e4aSElliott Hughes
69*e1fe3e4aSElliott Hughes    def addPoint(
70*e1fe3e4aSElliott Hughes        self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs
71*e1fe3e4aSElliott Hughes    ):
72*e1fe3e4aSElliott Hughes        items = ["%s" % (pt,)]
73*e1fe3e4aSElliott Hughes        if segmentType is not None:
74*e1fe3e4aSElliott Hughes            items.append("segmentType='%s'" % segmentType)
75*e1fe3e4aSElliott Hughes        if smooth:
76*e1fe3e4aSElliott Hughes            items.append("smooth=True")
77*e1fe3e4aSElliott Hughes        if name is not None:
78*e1fe3e4aSElliott Hughes            items.append("name='%s'" % name)
79*e1fe3e4aSElliott Hughes        if identifier is not None:
80*e1fe3e4aSElliott Hughes            items.append("identifier='%s'" % identifier)
81*e1fe3e4aSElliott Hughes        items.extend(_reprKwargs(kwargs))
82*e1fe3e4aSElliott Hughes        self._commands.append("addPoint(%s)" % ", ".join(items))
83*e1fe3e4aSElliott Hughes
84*e1fe3e4aSElliott Hughes    def endPath(self):
85*e1fe3e4aSElliott Hughes        self._commands.append("endPath()")
86*e1fe3e4aSElliott Hughes
87*e1fe3e4aSElliott Hughes    def addComponent(self, glyphName, transform, identifier=None, **kwargs):
88*e1fe3e4aSElliott Hughes        items = ["'%s'" % glyphName, "%s" % transform]
89*e1fe3e4aSElliott Hughes        if identifier is not None:
90*e1fe3e4aSElliott Hughes            items.append("identifier='%s'" % identifier)
91*e1fe3e4aSElliott Hughes        items.extend(_reprKwargs(kwargs))
92*e1fe3e4aSElliott Hughes        self._commands.append("addComponent(%s)" % ", ".join(items))
93*e1fe3e4aSElliott Hughes
94*e1fe3e4aSElliott Hughes
95*e1fe3e4aSElliott Hughesclass PointToSegmentPenTest(unittest.TestCase):
96*e1fe3e4aSElliott Hughes    def test_open(self):
97*e1fe3e4aSElliott Hughes        pen = _TestSegmentPen()
98*e1fe3e4aSElliott Hughes        ppen = PointToSegmentPen(pen)
99*e1fe3e4aSElliott Hughes        ppen.beginPath()
100*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 10), "move")
101*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 20), "line")
102*e1fe3e4aSElliott Hughes        ppen.endPath()
103*e1fe3e4aSElliott Hughes        self.assertEqual("10 10 moveto 10 20 lineto endpath", repr(pen))
104*e1fe3e4aSElliott Hughes
105*e1fe3e4aSElliott Hughes    def test_closed(self):
106*e1fe3e4aSElliott Hughes        pen = _TestSegmentPen()
107*e1fe3e4aSElliott Hughes        ppen = PointToSegmentPen(pen)
108*e1fe3e4aSElliott Hughes        ppen.beginPath()
109*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 10), "line")
110*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 20), "line")
111*e1fe3e4aSElliott Hughes        ppen.addPoint((20, 20), "line")
112*e1fe3e4aSElliott Hughes        ppen.endPath()
113*e1fe3e4aSElliott Hughes        self.assertEqual("10 10 moveto 10 20 lineto 20 20 lineto closepath", repr(pen))
114*e1fe3e4aSElliott Hughes
115*e1fe3e4aSElliott Hughes    def test_cubic(self):
116*e1fe3e4aSElliott Hughes        pen = _TestSegmentPen()
117*e1fe3e4aSElliott Hughes        ppen = PointToSegmentPen(pen)
118*e1fe3e4aSElliott Hughes        ppen.beginPath()
119*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 10), "line")
120*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 20))
121*e1fe3e4aSElliott Hughes        ppen.addPoint((20, 20))
122*e1fe3e4aSElliott Hughes        ppen.addPoint((20, 40), "curve")
123*e1fe3e4aSElliott Hughes        ppen.endPath()
124*e1fe3e4aSElliott Hughes        self.assertEqual("10 10 moveto 10 20 20 20 20 40 curveto closepath", repr(pen))
125*e1fe3e4aSElliott Hughes
126*e1fe3e4aSElliott Hughes    def test_quad(self):
127*e1fe3e4aSElliott Hughes        pen = _TestSegmentPen()
128*e1fe3e4aSElliott Hughes        ppen = PointToSegmentPen(pen)
129*e1fe3e4aSElliott Hughes        ppen.beginPath(identifier="foo")
130*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 10), "line")
131*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 40))
132*e1fe3e4aSElliott Hughes        ppen.addPoint((40, 40))
133*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 40), "qcurve")
134*e1fe3e4aSElliott Hughes        ppen.endPath()
135*e1fe3e4aSElliott Hughes        self.assertEqual("10 10 moveto 10 40 40 40 10 40 qcurveto closepath", repr(pen))
136*e1fe3e4aSElliott Hughes
137*e1fe3e4aSElliott Hughes    def test_quad_onlyOffCurvePoints(self):
138*e1fe3e4aSElliott Hughes        pen = _TestSegmentPen()
139*e1fe3e4aSElliott Hughes        ppen = PointToSegmentPen(pen)
140*e1fe3e4aSElliott Hughes        ppen.beginPath()
141*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 10))
142*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 40))
143*e1fe3e4aSElliott Hughes        ppen.addPoint((40, 40))
144*e1fe3e4aSElliott Hughes        ppen.endPath()
145*e1fe3e4aSElliott Hughes        self.assertEqual("10 10 10 40 40 40 None qcurveto closepath", repr(pen))
146*e1fe3e4aSElliott Hughes
147*e1fe3e4aSElliott Hughes    def test_roundTrip1(self):
148*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
149*e1fe3e4aSElliott Hughes        ppen = PointToSegmentPen(SegmentToPointPen(tpen))
150*e1fe3e4aSElliott Hughes        ppen.beginPath()
151*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 10), "line")
152*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 20))
153*e1fe3e4aSElliott Hughes        ppen.addPoint((20, 20))
154*e1fe3e4aSElliott Hughes        ppen.addPoint((20, 40), "curve")
155*e1fe3e4aSElliott Hughes        ppen.endPath()
156*e1fe3e4aSElliott Hughes        self.assertEqual(
157*e1fe3e4aSElliott Hughes            "beginPath() addPoint((10, 10), segmentType='line') addPoint((10, 20)) "
158*e1fe3e4aSElliott Hughes            "addPoint((20, 20)) addPoint((20, 40), segmentType='curve') endPath()",
159*e1fe3e4aSElliott Hughes            repr(tpen),
160*e1fe3e4aSElliott Hughes        )
161*e1fe3e4aSElliott Hughes
162*e1fe3e4aSElliott Hughes    def test_closed_outputImpliedClosingLine(self):
163*e1fe3e4aSElliott Hughes        tpen = _TestSegmentPen()
164*e1fe3e4aSElliott Hughes        ppen = PointToSegmentPen(tpen, outputImpliedClosingLine=True)
165*e1fe3e4aSElliott Hughes        ppen.beginPath()
166*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 10), "line")
167*e1fe3e4aSElliott Hughes        ppen.addPoint((10, 20), "line")
168*e1fe3e4aSElliott Hughes        ppen.addPoint((20, 20), "line")
169*e1fe3e4aSElliott Hughes        ppen.endPath()
170*e1fe3e4aSElliott Hughes        self.assertEqual(
171*e1fe3e4aSElliott Hughes            "10 10 moveto "
172*e1fe3e4aSElliott Hughes            "10 20 lineto "
173*e1fe3e4aSElliott Hughes            "20 20 lineto "
174*e1fe3e4aSElliott Hughes            "10 10 lineto "  # explicit closing line
175*e1fe3e4aSElliott Hughes            "closepath",
176*e1fe3e4aSElliott Hughes            repr(tpen),
177*e1fe3e4aSElliott Hughes        )
178*e1fe3e4aSElliott Hughes
179*e1fe3e4aSElliott Hughes    def test_closed_line_overlapping_start_end_points(self):
180*e1fe3e4aSElliott Hughes        # Test case from https://github.com/googlefonts/fontmake/issues/572.
181*e1fe3e4aSElliott Hughes        tpen = _TestSegmentPen()
182*e1fe3e4aSElliott Hughes        ppen = PointToSegmentPen(tpen, outputImpliedClosingLine=False)
183*e1fe3e4aSElliott Hughes        # The last oncurve point on this closed contour is a "line" segment and has
184*e1fe3e4aSElliott Hughes        # same coordinates as the starting point.
185*e1fe3e4aSElliott Hughes        ppen.beginPath()
186*e1fe3e4aSElliott Hughes        ppen.addPoint((0, 651), segmentType="line")
187*e1fe3e4aSElliott Hughes        ppen.addPoint((0, 101), segmentType="line")
188*e1fe3e4aSElliott Hughes        ppen.addPoint((0, 101), segmentType="line")
189*e1fe3e4aSElliott Hughes        ppen.addPoint((0, 651), segmentType="line")
190*e1fe3e4aSElliott Hughes        ppen.endPath()
191*e1fe3e4aSElliott Hughes        # Check that we always output an explicit 'lineTo' segment at the end,
192*e1fe3e4aSElliott Hughes        # regardless of the value of 'outputImpliedClosingLine', to disambiguate
193*e1fe3e4aSElliott Hughes        # the duplicate point from the implied closing line.
194*e1fe3e4aSElliott Hughes        self.assertEqual(
195*e1fe3e4aSElliott Hughes            "0 651 moveto "
196*e1fe3e4aSElliott Hughes            "0 101 lineto "
197*e1fe3e4aSElliott Hughes            "0 101 lineto "
198*e1fe3e4aSElliott Hughes            "0 651 lineto "
199*e1fe3e4aSElliott Hughes            "0 651 lineto "
200*e1fe3e4aSElliott Hughes            "closepath",
201*e1fe3e4aSElliott Hughes            repr(tpen),
202*e1fe3e4aSElliott Hughes        )
203*e1fe3e4aSElliott Hughes
204*e1fe3e4aSElliott Hughes    def test_roundTrip2(self):
205*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
206*e1fe3e4aSElliott Hughes        ppen = PointToSegmentPen(SegmentToPointPen(tpen))
207*e1fe3e4aSElliott Hughes        ppen.beginPath()
208*e1fe3e4aSElliott Hughes        ppen.addPoint((0, 651), segmentType="line")
209*e1fe3e4aSElliott Hughes        ppen.addPoint((0, 101), segmentType="line")
210*e1fe3e4aSElliott Hughes        ppen.addPoint((0, 101), segmentType="line")
211*e1fe3e4aSElliott Hughes        ppen.addPoint((0, 651), segmentType="line")
212*e1fe3e4aSElliott Hughes        ppen.endPath()
213*e1fe3e4aSElliott Hughes        self.assertEqual(
214*e1fe3e4aSElliott Hughes            "beginPath() "
215*e1fe3e4aSElliott Hughes            "addPoint((0, 651), segmentType='line') "
216*e1fe3e4aSElliott Hughes            "addPoint((0, 101), segmentType='line') "
217*e1fe3e4aSElliott Hughes            "addPoint((0, 101), segmentType='line') "
218*e1fe3e4aSElliott Hughes            "addPoint((0, 651), segmentType='line') "
219*e1fe3e4aSElliott Hughes            "endPath()",
220*e1fe3e4aSElliott Hughes            repr(tpen),
221*e1fe3e4aSElliott Hughes        )
222*e1fe3e4aSElliott Hughes
223*e1fe3e4aSElliott Hughes
224*e1fe3e4aSElliott Hughesclass TestSegmentToPointPen(unittest.TestCase):
225*e1fe3e4aSElliott Hughes    def test_move(self):
226*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
227*e1fe3e4aSElliott Hughes        pen = SegmentToPointPen(tpen)
228*e1fe3e4aSElliott Hughes        pen.moveTo((10, 10))
229*e1fe3e4aSElliott Hughes        pen.endPath()
230*e1fe3e4aSElliott Hughes        self.assertEqual(
231*e1fe3e4aSElliott Hughes            "beginPath() addPoint((10, 10), segmentType='move') endPath()", repr(tpen)
232*e1fe3e4aSElliott Hughes        )
233*e1fe3e4aSElliott Hughes
234*e1fe3e4aSElliott Hughes    def test_poly(self):
235*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
236*e1fe3e4aSElliott Hughes        pen = SegmentToPointPen(tpen)
237*e1fe3e4aSElliott Hughes        pen.moveTo((10, 10))
238*e1fe3e4aSElliott Hughes        pen.lineTo((10, 20))
239*e1fe3e4aSElliott Hughes        pen.lineTo((20, 20))
240*e1fe3e4aSElliott Hughes        pen.closePath()
241*e1fe3e4aSElliott Hughes        self.assertEqual(
242*e1fe3e4aSElliott Hughes            "beginPath() addPoint((10, 10), segmentType='line') "
243*e1fe3e4aSElliott Hughes            "addPoint((10, 20), segmentType='line') "
244*e1fe3e4aSElliott Hughes            "addPoint((20, 20), segmentType='line') endPath()",
245*e1fe3e4aSElliott Hughes            repr(tpen),
246*e1fe3e4aSElliott Hughes        )
247*e1fe3e4aSElliott Hughes
248*e1fe3e4aSElliott Hughes    def test_cubic(self):
249*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
250*e1fe3e4aSElliott Hughes        pen = SegmentToPointPen(tpen)
251*e1fe3e4aSElliott Hughes        pen.moveTo((10, 10))
252*e1fe3e4aSElliott Hughes        pen.curveTo((10, 20), (20, 20), (20, 10))
253*e1fe3e4aSElliott Hughes        pen.closePath()
254*e1fe3e4aSElliott Hughes        self.assertEqual(
255*e1fe3e4aSElliott Hughes            "beginPath() addPoint((10, 10), segmentType='line') "
256*e1fe3e4aSElliott Hughes            "addPoint((10, 20)) addPoint((20, 20)) addPoint((20, 10), "
257*e1fe3e4aSElliott Hughes            "segmentType='curve') endPath()",
258*e1fe3e4aSElliott Hughes            repr(tpen),
259*e1fe3e4aSElliott Hughes        )
260*e1fe3e4aSElliott Hughes
261*e1fe3e4aSElliott Hughes    def test_quad(self):
262*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
263*e1fe3e4aSElliott Hughes        pen = SegmentToPointPen(tpen)
264*e1fe3e4aSElliott Hughes        pen.moveTo((10, 10))
265*e1fe3e4aSElliott Hughes        pen.qCurveTo((10, 20), (20, 20), (20, 10))
266*e1fe3e4aSElliott Hughes        pen.closePath()
267*e1fe3e4aSElliott Hughes        self.assertEqual(
268*e1fe3e4aSElliott Hughes            "beginPath() addPoint((10, 10), segmentType='line') "
269*e1fe3e4aSElliott Hughes            "addPoint((10, 20)) addPoint((20, 20)) "
270*e1fe3e4aSElliott Hughes            "addPoint((20, 10), segmentType='qcurve') endPath()",
271*e1fe3e4aSElliott Hughes            repr(tpen),
272*e1fe3e4aSElliott Hughes        )
273*e1fe3e4aSElliott Hughes
274*e1fe3e4aSElliott Hughes    def test_quad2(self):
275*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
276*e1fe3e4aSElliott Hughes        pen = SegmentToPointPen(tpen)
277*e1fe3e4aSElliott Hughes        pen.qCurveTo((10, 20), (20, 20), (20, 10), (10, 10), None)
278*e1fe3e4aSElliott Hughes        pen.closePath()
279*e1fe3e4aSElliott Hughes        self.assertEqual(
280*e1fe3e4aSElliott Hughes            "beginPath() addPoint((10, 20)) addPoint((20, 20)) "
281*e1fe3e4aSElliott Hughes            "addPoint((20, 10)) addPoint((10, 10)) endPath()",
282*e1fe3e4aSElliott Hughes            repr(tpen),
283*e1fe3e4aSElliott Hughes        )
284*e1fe3e4aSElliott Hughes
285*e1fe3e4aSElliott Hughes    def test_roundTrip1(self):
286*e1fe3e4aSElliott Hughes        spen = _TestSegmentPen()
287*e1fe3e4aSElliott Hughes        pen = SegmentToPointPen(PointToSegmentPen(spen))
288*e1fe3e4aSElliott Hughes        pen.moveTo((10, 10))
289*e1fe3e4aSElliott Hughes        pen.lineTo((10, 20))
290*e1fe3e4aSElliott Hughes        pen.lineTo((20, 20))
291*e1fe3e4aSElliott Hughes        pen.closePath()
292*e1fe3e4aSElliott Hughes        self.assertEqual("10 10 moveto 10 20 lineto 20 20 lineto closepath", repr(spen))
293*e1fe3e4aSElliott Hughes
294*e1fe3e4aSElliott Hughes    def test_roundTrip2(self):
295*e1fe3e4aSElliott Hughes        spen = _TestSegmentPen()
296*e1fe3e4aSElliott Hughes        pen = SegmentToPointPen(PointToSegmentPen(spen))
297*e1fe3e4aSElliott Hughes        pen.qCurveTo((10, 20), (20, 20), (20, 10), (10, 10), None)
298*e1fe3e4aSElliott Hughes        pen.closePath()
299*e1fe3e4aSElliott Hughes        pen.addComponent("base", [1, 0, 0, 1, 0, 0])
300*e1fe3e4aSElliott Hughes        self.assertEqual(
301*e1fe3e4aSElliott Hughes            "10 20 20 20 20 10 10 10 None qcurveto closepath "
302*e1fe3e4aSElliott Hughes            "'base' [1, 0, 0, 1, 0, 0] addcomponent",
303*e1fe3e4aSElliott Hughes            repr(spen),
304*e1fe3e4aSElliott Hughes        )
305*e1fe3e4aSElliott Hughes
306*e1fe3e4aSElliott Hughes
307*e1fe3e4aSElliott Hughesclass TestGuessSmoothPointPen(unittest.TestCase):
308*e1fe3e4aSElliott Hughes    def test_guessSmooth_exact(self):
309*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
310*e1fe3e4aSElliott Hughes        pen = GuessSmoothPointPen(tpen)
311*e1fe3e4aSElliott Hughes        pen.beginPath(identifier="foo")
312*e1fe3e4aSElliott Hughes        pen.addPoint((0, 100), segmentType="curve")
313*e1fe3e4aSElliott Hughes        pen.addPoint((0, 200))
314*e1fe3e4aSElliott Hughes        pen.addPoint((400, 200), identifier="bar")
315*e1fe3e4aSElliott Hughes        pen.addPoint((400, 100), segmentType="curve")
316*e1fe3e4aSElliott Hughes        pen.addPoint((400, 0))
317*e1fe3e4aSElliott Hughes        pen.addPoint((0, 0))
318*e1fe3e4aSElliott Hughes        pen.endPath()
319*e1fe3e4aSElliott Hughes        self.assertEqual(
320*e1fe3e4aSElliott Hughes            "beginPath(identifier='foo') "
321*e1fe3e4aSElliott Hughes            "addPoint((0, 100), segmentType='curve', smooth=True) "
322*e1fe3e4aSElliott Hughes            "addPoint((0, 200)) addPoint((400, 200), identifier='bar') "
323*e1fe3e4aSElliott Hughes            "addPoint((400, 100), segmentType='curve', smooth=True) "
324*e1fe3e4aSElliott Hughes            "addPoint((400, 0)) addPoint((0, 0)) endPath()",
325*e1fe3e4aSElliott Hughes            repr(tpen),
326*e1fe3e4aSElliott Hughes        )
327*e1fe3e4aSElliott Hughes
328*e1fe3e4aSElliott Hughes    def test_guessSmooth_almost(self):
329*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
330*e1fe3e4aSElliott Hughes        pen = GuessSmoothPointPen(tpen)
331*e1fe3e4aSElliott Hughes        pen.beginPath()
332*e1fe3e4aSElliott Hughes        pen.addPoint((0, 100), segmentType="curve")
333*e1fe3e4aSElliott Hughes        pen.addPoint((1, 200))
334*e1fe3e4aSElliott Hughes        pen.addPoint((395, 200))
335*e1fe3e4aSElliott Hughes        pen.addPoint((400, 100), segmentType="curve")
336*e1fe3e4aSElliott Hughes        pen.addPoint((400, 0))
337*e1fe3e4aSElliott Hughes        pen.addPoint((0, 0))
338*e1fe3e4aSElliott Hughes        pen.endPath()
339*e1fe3e4aSElliott Hughes        self.assertEqual(
340*e1fe3e4aSElliott Hughes            "beginPath() addPoint((0, 100), segmentType='curve', smooth=True) "
341*e1fe3e4aSElliott Hughes            "addPoint((1, 200)) addPoint((395, 200)) "
342*e1fe3e4aSElliott Hughes            "addPoint((400, 100), segmentType='curve', smooth=True) "
343*e1fe3e4aSElliott Hughes            "addPoint((400, 0)) addPoint((0, 0)) endPath()",
344*e1fe3e4aSElliott Hughes            repr(tpen),
345*e1fe3e4aSElliott Hughes        )
346*e1fe3e4aSElliott Hughes
347*e1fe3e4aSElliott Hughes    def test_guessSmooth_tangent(self):
348*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
349*e1fe3e4aSElliott Hughes        pen = GuessSmoothPointPen(tpen)
350*e1fe3e4aSElliott Hughes        pen.beginPath()
351*e1fe3e4aSElliott Hughes        pen.addPoint((0, 0), segmentType="move")
352*e1fe3e4aSElliott Hughes        pen.addPoint((0, 100), segmentType="line")
353*e1fe3e4aSElliott Hughes        pen.addPoint((3, 200))
354*e1fe3e4aSElliott Hughes        pen.addPoint((300, 200))
355*e1fe3e4aSElliott Hughes        pen.addPoint((400, 200), segmentType="curve")
356*e1fe3e4aSElliott Hughes        pen.endPath()
357*e1fe3e4aSElliott Hughes        self.assertEqual(
358*e1fe3e4aSElliott Hughes            "beginPath() addPoint((0, 0), segmentType='move') "
359*e1fe3e4aSElliott Hughes            "addPoint((0, 100), segmentType='line', smooth=True) "
360*e1fe3e4aSElliott Hughes            "addPoint((3, 200)) addPoint((300, 200)) "
361*e1fe3e4aSElliott Hughes            "addPoint((400, 200), segmentType='curve') endPath()",
362*e1fe3e4aSElliott Hughes            repr(tpen),
363*e1fe3e4aSElliott Hughes        )
364*e1fe3e4aSElliott Hughes
365*e1fe3e4aSElliott Hughes
366*e1fe3e4aSElliott Hughesclass TestReverseContourPointPen(unittest.TestCase):
367*e1fe3e4aSElliott Hughes    def test_singlePoint(self):
368*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
369*e1fe3e4aSElliott Hughes        pen = ReverseContourPointPen(tpen)
370*e1fe3e4aSElliott Hughes        pen.beginPath()
371*e1fe3e4aSElliott Hughes        pen.addPoint((0, 0), segmentType="move")
372*e1fe3e4aSElliott Hughes        pen.endPath()
373*e1fe3e4aSElliott Hughes        self.assertEqual(
374*e1fe3e4aSElliott Hughes            "beginPath() " "addPoint((0, 0), segmentType='move') " "endPath()",
375*e1fe3e4aSElliott Hughes            repr(tpen),
376*e1fe3e4aSElliott Hughes        )
377*e1fe3e4aSElliott Hughes
378*e1fe3e4aSElliott Hughes    def test_line(self):
379*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
380*e1fe3e4aSElliott Hughes        pen = ReverseContourPointPen(tpen)
381*e1fe3e4aSElliott Hughes        pen.beginPath()
382*e1fe3e4aSElliott Hughes        pen.addPoint((0, 0), segmentType="move")
383*e1fe3e4aSElliott Hughes        pen.addPoint((0, 100), segmentType="line")
384*e1fe3e4aSElliott Hughes        pen.endPath()
385*e1fe3e4aSElliott Hughes        self.assertEqual(
386*e1fe3e4aSElliott Hughes            "beginPath() "
387*e1fe3e4aSElliott Hughes            "addPoint((0, 100), segmentType='move') "
388*e1fe3e4aSElliott Hughes            "addPoint((0, 0), segmentType='line') "
389*e1fe3e4aSElliott Hughes            "endPath()",
390*e1fe3e4aSElliott Hughes            repr(tpen),
391*e1fe3e4aSElliott Hughes        )
392*e1fe3e4aSElliott Hughes
393*e1fe3e4aSElliott Hughes    def test_triangle(self):
394*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
395*e1fe3e4aSElliott Hughes        pen = ReverseContourPointPen(tpen)
396*e1fe3e4aSElliott Hughes        pen.beginPath()
397*e1fe3e4aSElliott Hughes        pen.addPoint((0, 0), segmentType="line")
398*e1fe3e4aSElliott Hughes        pen.addPoint((0, 100), segmentType="line")
399*e1fe3e4aSElliott Hughes        pen.addPoint((100, 100), segmentType="line")
400*e1fe3e4aSElliott Hughes        pen.endPath()
401*e1fe3e4aSElliott Hughes        self.assertEqual(
402*e1fe3e4aSElliott Hughes            "beginPath() "
403*e1fe3e4aSElliott Hughes            "addPoint((0, 0), segmentType='line') "
404*e1fe3e4aSElliott Hughes            "addPoint((100, 100), segmentType='line') "
405*e1fe3e4aSElliott Hughes            "addPoint((0, 100), segmentType='line') "
406*e1fe3e4aSElliott Hughes            "endPath()",
407*e1fe3e4aSElliott Hughes            repr(tpen),
408*e1fe3e4aSElliott Hughes        )
409*e1fe3e4aSElliott Hughes
410*e1fe3e4aSElliott Hughes    def test_cubicOpen(self):
411*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
412*e1fe3e4aSElliott Hughes        pen = ReverseContourPointPen(tpen)
413*e1fe3e4aSElliott Hughes        pen.beginPath()
414*e1fe3e4aSElliott Hughes        pen.addPoint((0, 0), segmentType="move")
415*e1fe3e4aSElliott Hughes        pen.addPoint((0, 100))
416*e1fe3e4aSElliott Hughes        pen.addPoint((100, 200))
417*e1fe3e4aSElliott Hughes        pen.addPoint((200, 200), segmentType="curve")
418*e1fe3e4aSElliott Hughes        pen.endPath()
419*e1fe3e4aSElliott Hughes        self.assertEqual(
420*e1fe3e4aSElliott Hughes            "beginPath() "
421*e1fe3e4aSElliott Hughes            "addPoint((200, 200), segmentType='move') "
422*e1fe3e4aSElliott Hughes            "addPoint((100, 200)) "
423*e1fe3e4aSElliott Hughes            "addPoint((0, 100)) "
424*e1fe3e4aSElliott Hughes            "addPoint((0, 0), segmentType='curve') "
425*e1fe3e4aSElliott Hughes            "endPath()",
426*e1fe3e4aSElliott Hughes            repr(tpen),
427*e1fe3e4aSElliott Hughes        )
428*e1fe3e4aSElliott Hughes
429*e1fe3e4aSElliott Hughes    def test_quadOpen(self):
430*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
431*e1fe3e4aSElliott Hughes        pen = ReverseContourPointPen(tpen)
432*e1fe3e4aSElliott Hughes        pen.beginPath()
433*e1fe3e4aSElliott Hughes        pen.addPoint((0, 0), segmentType="move")
434*e1fe3e4aSElliott Hughes        pen.addPoint((0, 100))
435*e1fe3e4aSElliott Hughes        pen.addPoint((100, 200))
436*e1fe3e4aSElliott Hughes        pen.addPoint((200, 200), segmentType="qcurve")
437*e1fe3e4aSElliott Hughes        pen.endPath()
438*e1fe3e4aSElliott Hughes        self.assertEqual(
439*e1fe3e4aSElliott Hughes            "beginPath() "
440*e1fe3e4aSElliott Hughes            "addPoint((200, 200), segmentType='move') "
441*e1fe3e4aSElliott Hughes            "addPoint((100, 200)) "
442*e1fe3e4aSElliott Hughes            "addPoint((0, 100)) "
443*e1fe3e4aSElliott Hughes            "addPoint((0, 0), segmentType='qcurve') "
444*e1fe3e4aSElliott Hughes            "endPath()",
445*e1fe3e4aSElliott Hughes            repr(tpen),
446*e1fe3e4aSElliott Hughes        )
447*e1fe3e4aSElliott Hughes
448*e1fe3e4aSElliott Hughes    def test_cubicClosed(self):
449*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
450*e1fe3e4aSElliott Hughes        pen = ReverseContourPointPen(tpen)
451*e1fe3e4aSElliott Hughes        pen.beginPath()
452*e1fe3e4aSElliott Hughes        pen.addPoint((0, 0), segmentType="line")
453*e1fe3e4aSElliott Hughes        pen.addPoint((0, 100))
454*e1fe3e4aSElliott Hughes        pen.addPoint((100, 200))
455*e1fe3e4aSElliott Hughes        pen.addPoint((200, 200), segmentType="curve")
456*e1fe3e4aSElliott Hughes        pen.endPath()
457*e1fe3e4aSElliott Hughes        self.assertEqual(
458*e1fe3e4aSElliott Hughes            "beginPath() "
459*e1fe3e4aSElliott Hughes            "addPoint((0, 0), segmentType='curve') "
460*e1fe3e4aSElliott Hughes            "addPoint((200, 200), segmentType='line') "
461*e1fe3e4aSElliott Hughes            "addPoint((100, 200)) "
462*e1fe3e4aSElliott Hughes            "addPoint((0, 100)) "
463*e1fe3e4aSElliott Hughes            "endPath()",
464*e1fe3e4aSElliott Hughes            repr(tpen),
465*e1fe3e4aSElliott Hughes        )
466*e1fe3e4aSElliott Hughes
467*e1fe3e4aSElliott Hughes    def test_quadClosedOffCurveStart(self):
468*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
469*e1fe3e4aSElliott Hughes        pen = ReverseContourPointPen(tpen)
470*e1fe3e4aSElliott Hughes        pen.beginPath()
471*e1fe3e4aSElliott Hughes        pen.addPoint((100, 200))
472*e1fe3e4aSElliott Hughes        pen.addPoint((200, 200), segmentType="qcurve")
473*e1fe3e4aSElliott Hughes        pen.addPoint((0, 0), segmentType="line")
474*e1fe3e4aSElliott Hughes        pen.addPoint((0, 100))
475*e1fe3e4aSElliott Hughes        pen.endPath()
476*e1fe3e4aSElliott Hughes        self.assertEqual(
477*e1fe3e4aSElliott Hughes            "beginPath() "
478*e1fe3e4aSElliott Hughes            "addPoint((100, 200)) "
479*e1fe3e4aSElliott Hughes            "addPoint((0, 100)) "
480*e1fe3e4aSElliott Hughes            "addPoint((0, 0), segmentType='qcurve') "
481*e1fe3e4aSElliott Hughes            "addPoint((200, 200), segmentType='line') "
482*e1fe3e4aSElliott Hughes            "endPath()",
483*e1fe3e4aSElliott Hughes            repr(tpen),
484*e1fe3e4aSElliott Hughes        )
485*e1fe3e4aSElliott Hughes
486*e1fe3e4aSElliott Hughes    def test_quadNoOnCurve(self):
487*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
488*e1fe3e4aSElliott Hughes        pen = ReverseContourPointPen(tpen)
489*e1fe3e4aSElliott Hughes        pen.beginPath(identifier="bar")
490*e1fe3e4aSElliott Hughes        pen.addPoint((0, 0))
491*e1fe3e4aSElliott Hughes        pen.addPoint((0, 100), identifier="foo", arbitrary="foo")
492*e1fe3e4aSElliott Hughes        pen.addPoint((100, 200), arbitrary=123)
493*e1fe3e4aSElliott Hughes        pen.addPoint((200, 200))
494*e1fe3e4aSElliott Hughes        pen.endPath()
495*e1fe3e4aSElliott Hughes        pen.addComponent("base", [1, 0, 0, 1, 0, 0], identifier="foo")
496*e1fe3e4aSElliott Hughes        self.assertEqual(
497*e1fe3e4aSElliott Hughes            "beginPath(identifier='bar') "
498*e1fe3e4aSElliott Hughes            "addPoint((0, 0)) "
499*e1fe3e4aSElliott Hughes            "addPoint((200, 200)) "
500*e1fe3e4aSElliott Hughes            "addPoint((100, 200), arbitrary=123) "
501*e1fe3e4aSElliott Hughes            "addPoint((0, 100), identifier='foo', arbitrary='foo') "
502*e1fe3e4aSElliott Hughes            "endPath() "
503*e1fe3e4aSElliott Hughes            "addComponent('base', [1, 0, 0, 1, 0, 0], identifier='foo')",
504*e1fe3e4aSElliott Hughes            repr(tpen),
505*e1fe3e4aSElliott Hughes        )
506*e1fe3e4aSElliott Hughes
507*e1fe3e4aSElliott Hughes    def test_closed_line_overlapping_start_end_points(self):
508*e1fe3e4aSElliott Hughes        # Test case from https://github.com/googlefonts/fontmake/issues/572
509*e1fe3e4aSElliott Hughes        tpen = _TestPointPen()
510*e1fe3e4aSElliott Hughes        pen = ReverseContourPointPen(tpen)
511*e1fe3e4aSElliott Hughes        pen.beginPath()
512*e1fe3e4aSElliott Hughes        pen.addPoint((0, 651), segmentType="line")
513*e1fe3e4aSElliott Hughes        pen.addPoint((0, 101), segmentType="line")
514*e1fe3e4aSElliott Hughes        pen.addPoint((0, 101), segmentType="line")
515*e1fe3e4aSElliott Hughes        pen.addPoint((0, 651), segmentType="line")
516*e1fe3e4aSElliott Hughes        pen.endPath()
517*e1fe3e4aSElliott Hughes        self.assertEqual(
518*e1fe3e4aSElliott Hughes            "beginPath() "
519*e1fe3e4aSElliott Hughes            "addPoint((0, 651), segmentType='line') "
520*e1fe3e4aSElliott Hughes            "addPoint((0, 651), segmentType='line') "
521*e1fe3e4aSElliott Hughes            "addPoint((0, 101), segmentType='line') "
522*e1fe3e4aSElliott Hughes            "addPoint((0, 101), segmentType='line') "
523*e1fe3e4aSElliott Hughes            "endPath()",
524*e1fe3e4aSElliott Hughes            repr(tpen),
525*e1fe3e4aSElliott Hughes        )
526