xref: /aosp_15_r20/external/fonttools/Tests/pens/basePen_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1from fontTools.pens.basePen import (
2    AbstractPen,
3    BasePen,
4    decomposeSuperBezierSegment,
5    decomposeQuadraticSegment,
6)
7from fontTools.pens.pointPen import AbstractPointPen
8from fontTools.misc.loggingTools import CapturingLogHandler
9import unittest
10
11
12class _TestPen(BasePen):
13    def __init__(self):
14        BasePen.__init__(self, glyphSet={})
15        self._commands = []
16
17    def __repr__(self):
18        return " ".join(self._commands)
19
20    def getCurrentPoint(self):
21        return self._getCurrentPoint()
22
23    def _moveTo(self, pt):
24        self._commands.append("%s %s moveto" % (pt[0], pt[1]))
25
26    def _lineTo(self, pt):
27        self._commands.append("%s %s lineto" % (pt[0], pt[1]))
28
29    def _curveToOne(self, bcp1, bcp2, pt):
30        self._commands.append(
31            "%s %s %s %s %s %s curveto"
32            % (bcp1[0], bcp1[1], bcp2[0], bcp2[1], pt[0], pt[1])
33        )
34
35    def _closePath(self):
36        self._commands.append("closepath")
37
38    def _endPath(self):
39        self._commands.append("endpath")
40
41
42class _TestGlyph:
43    def draw(self, pen):
44        pen.moveTo((0.0, 0.0))
45        pen.lineTo((0.0, 100.0))
46        pen.curveTo((50.0, 75.0), (60.0, 50.0), (50.0, 25.0), (0.0, 0.0))
47        pen.closePath()
48
49
50class BasePenTest(unittest.TestCase):
51    def test_moveTo(self):
52        pen = _TestPen()
53        pen.moveTo((0.5, -4.3))
54        self.assertEqual("0.5 -4.3 moveto", repr(pen))
55        self.assertEqual((0.5, -4.3), pen.getCurrentPoint())
56
57    def test_lineTo(self):
58        pen = _TestPen()
59        pen.moveTo((4, 5))
60        pen.lineTo((7, 8))
61        self.assertEqual("4 5 moveto 7 8 lineto", repr(pen))
62        self.assertEqual((7, 8), pen.getCurrentPoint())
63
64    def test_curveTo_zeroPoints(self):
65        pen = _TestPen()
66        pen.moveTo((0.0, 0.0))
67        self.assertRaises(AssertionError, pen.curveTo)
68
69    def test_curveTo_onePoint(self):
70        pen = _TestPen()
71        pen.moveTo((0.0, 0.0))
72        pen.curveTo((1.0, 1.1))
73        self.assertEqual("0.0 0.0 moveto 1.0 1.1 lineto", repr(pen))
74        self.assertEqual((1.0, 1.1), pen.getCurrentPoint())
75
76    def test_curveTo_twoPoints(self):
77        pen = _TestPen()
78        pen.moveTo((0.0, 0.0))
79        pen.curveTo((6.0, 3.0), (3.0, 6.0))
80        self.assertEqual("0.0 0.0 moveto 4.0 2.0 5.0 4.0 3.0 6.0 curveto", repr(pen))
81        self.assertEqual((3.0, 6.0), pen.getCurrentPoint())
82
83    def test_curveTo_manyPoints(self):
84        pen = _TestPen()
85        pen.moveTo((0.0, 0.0))
86        pen.curveTo((1.0, 1.1), (2.0, 2.1), (3.0, 3.1), (4.0, 4.1))
87        self.assertEqual(
88            "0.0 0.0 moveto "
89            "1.0 1.1 1.5 1.6 2.0 2.1 curveto "
90            "2.5 2.6 3.0 3.1 4.0 4.1 curveto",
91            repr(pen),
92        )
93        self.assertEqual((4.0, 4.1), pen.getCurrentPoint())
94
95    def test_qCurveTo_zeroPoints(self):
96        pen = _TestPen()
97        pen.moveTo((0.0, 0.0))
98        self.assertRaises(AssertionError, pen.qCurveTo)
99
100    def test_qCurveTo_onePoint(self):
101        pen = _TestPen()
102        pen.moveTo((0.0, 0.0))
103        pen.qCurveTo((77.7, 99.9))
104        self.assertEqual("0.0 0.0 moveto 77.7 99.9 lineto", repr(pen))
105        self.assertEqual((77.7, 99.9), pen.getCurrentPoint())
106
107    def test_qCurveTo_manyPoints(self):
108        pen = _TestPen()
109        pen.moveTo((0.0, 0.0))
110        pen.qCurveTo((6.0, 3.0), (3.0, 6.0))
111        self.assertEqual("0.0 0.0 moveto 4.0 2.0 5.0 4.0 3.0 6.0 curveto", repr(pen))
112        self.assertEqual((3.0, 6.0), pen.getCurrentPoint())
113
114    def test_qCurveTo_onlyOffCurvePoints(self):
115        pen = _TestPen()
116        pen.moveTo((0.0, 0.0))
117        pen.qCurveTo((6.0, -6.0), (12.0, 12.0), (18.0, -18.0), None)
118        self.assertEqual(
119            "0.0 0.0 moveto "
120            "12.0 -12.0 moveto "
121            "8.0 -8.0 7.0 -3.0 9.0 3.0 curveto "
122            "11.0 9.0 13.0 7.0 15.0 -3.0 curveto "
123            "17.0 -13.0 16.0 -16.0 12.0 -12.0 curveto",
124            repr(pen),
125        )
126        self.assertEqual((12.0, -12.0), pen.getCurrentPoint())
127
128    def test_closePath(self):
129        pen = _TestPen()
130        pen.lineTo((3, 4))
131        pen.closePath()
132        self.assertEqual("3 4 lineto closepath", repr(pen))
133        self.assertEqual(None, pen.getCurrentPoint())
134
135    def test_endPath(self):
136        pen = _TestPen()
137        pen.lineTo((3, 4))
138        pen.endPath()
139        self.assertEqual("3 4 lineto endpath", repr(pen))
140        self.assertEqual(None, pen.getCurrentPoint())
141
142    def test_addComponent(self):
143        pen = _TestPen()
144        pen.glyphSet["oslash"] = _TestGlyph()
145        pen.addComponent("oslash", (2, 3, 0.5, 2, -10, 0))
146        self.assertEqual(
147            "-10.0 0.0 moveto "
148            "40.0 200.0 lineto "
149            "127.5 300.0 131.25 290.0 125.0 265.0 curveto "
150            "118.75 240.0 102.5 200.0 -10.0 0.0 curveto "
151            "closepath",
152            repr(pen),
153        )
154        self.assertEqual(None, pen.getCurrentPoint())
155
156    def test_addComponent_skip_missing(self):
157        pen = _TestPen()
158        with CapturingLogHandler(pen.log, "WARNING") as captor:
159            pen.addComponent("nonexistent", (1, 0, 0, 1, 0, 0))
160        captor.assertRegex("glyph '.*' is missing from glyphSet; skipped")
161
162
163class DecomposeSegmentTest(unittest.TestCase):
164    def test_decomposeSuperBezierSegment(self):
165        decompose = decomposeSuperBezierSegment
166        self.assertRaises(AssertionError, decompose, [])
167        self.assertRaises(AssertionError, decompose, [(0, 0)])
168        self.assertRaises(AssertionError, decompose, [(0, 0), (1, 1)])
169        self.assertEqual(
170            [((0, 0), (1, 1), (2, 2))], decompose([(0, 0), (1, 1), (2, 2)])
171        )
172        self.assertEqual(
173            [((0, 0), (2, -2), (4, 0)), ((6, 2), (8, 8), (12, -12))],
174            decompose([(0, 0), (4, -4), (8, 8), (12, -12)]),
175        )
176
177    def test_decomposeQuadraticSegment(self):
178        decompose = decomposeQuadraticSegment
179        self.assertRaises(AssertionError, decompose, [])
180        self.assertRaises(AssertionError, decompose, [(0, 0)])
181        self.assertEqual([((0, 0), (4, 8))], decompose([(0, 0), (4, 8)]))
182        self.assertEqual(
183            [((0, 0), (2, 4)), ((4, 8), (9, -9))], decompose([(0, 0), (4, 8), (9, -9)])
184        )
185        self.assertEqual(
186            [((0, 0), (2.0, 4.0)), ((4, 8), (6.5, -0.5)), ((9, -9), (10, 10))],
187            decompose([(0, 0), (4, 8), (9, -9), (10, 10)]),
188        )
189
190
191if __name__ == "__main__":
192    import sys
193
194    sys.exit(unittest.main())
195