xref: /aosp_15_r20/external/fonttools/Tests/pens/t2CharStringPen_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1from fontTools.pens.t2CharStringPen import T2CharStringPen
2import unittest
3
4
5class T2CharStringPenTest(unittest.TestCase):
6    def __init__(self, methodName):
7        unittest.TestCase.__init__(self, methodName)
8
9    def assertAlmostEqualProgram(self, expected, actual):
10        self.assertEqual(len(expected), len(actual))
11        for i1, i2 in zip(expected, actual):
12            if isinstance(i1, str):
13                self.assertIsInstance(i2, str)
14                self.assertEqual(i1, i2)
15            else:
16                self.assertAlmostEqual(i1, i2)
17
18    def test_draw_h_v_lines(self):
19        pen = T2CharStringPen(100, {})
20        pen.moveTo((0, 0))
21        pen.lineTo((10, 0))
22        pen.lineTo((10, 10))
23        pen.lineTo((0, 10))
24        pen.closePath()  # no-op
25        pen.moveTo((10, 10))
26        pen.lineTo((10, 20))
27        pen.lineTo((0, 20))
28        pen.lineTo((0, 10))
29        pen.closePath()
30        charstring = pen.getCharString(None, None)
31
32        self.assertEqual(
33            [
34                100,
35                0,
36                "hmoveto",
37                10,
38                10,
39                -10,
40                "hlineto",
41                10,
42                "hmoveto",
43                10,
44                -10,
45                -10,
46                "vlineto",
47                "endchar",
48            ],
49            charstring.program,
50        )
51
52    def test_draw_lines(self):
53        pen = T2CharStringPen(100, {})
54        pen.moveTo((5, 5))
55        pen.lineTo((25, 15))
56        pen.lineTo((35, 35))
57        pen.lineTo((15, 25))
58        pen.closePath()  # no-op
59        charstring = pen.getCharString(None, None)
60
61        self.assertEqual(
62            [100, 5, 5, "rmoveto", 20, 10, 10, 20, -20, -10, "rlineto", "endchar"],
63            charstring.program,
64        )
65
66    def test_draw_h_v_curves(self):
67        pen = T2CharStringPen(100, {})
68        pen.moveTo((0, 0))
69        pen.curveTo((10, 0), (20, 10), (20, 20))
70        pen.curveTo((20, 30), (10, 40), (0, 40))
71        pen.endPath()  # no-op
72        charstring = pen.getCharString(None, None)
73
74        self.assertEqual(
75            [
76                100,
77                0,
78                "hmoveto",
79                10,
80                10,
81                10,
82                10,
83                10,
84                -10,
85                10,
86                -10,
87                "hvcurveto",
88                "endchar",
89            ],
90            charstring.program,
91        )
92
93    def test_draw_curves(self):
94        pen = T2CharStringPen(100, {})
95        pen.moveTo((95, 25))
96        pen.curveTo((115, 44), (115, 76), (95, 95))
97        pen.curveTo((76, 114), (44, 115), (25, 95))
98        pen.endPath()  # no-op
99        charstring = pen.getCharString(None, None)
100
101        self.assertEqual(
102            [
103                100,
104                95,
105                25,
106                "rmoveto",
107                20,
108                19,
109                0,
110                32,
111                -20,
112                19,
113                -19,
114                19,
115                -32,
116                1,
117                -19,
118                -20,
119                "rrcurveto",
120                "endchar",
121            ],
122            charstring.program,
123        )
124
125    def test_draw_more_curves(self):
126        pen = T2CharStringPen(100, {})
127        pen.moveTo((10, 10))
128        pen.curveTo((20, 10), (50, 10), (60, 10))
129        pen.curveTo((60, 20), (60, 50), (60, 60))
130        pen.curveTo((50, 50), (40, 60), (30, 60))
131        pen.curveTo((40, 50), (30, 40), (30, 30))
132        pen.curveTo((30, 25), (25, 19), (20, 20))
133        pen.curveTo((15, 20), (9, 25), (10, 30))
134        pen.curveTo((7, 25), (6, 15), (10, 10))
135        pen.endPath()  # no-op
136        charstring = pen.getCharString(None, None)
137
138        self.assertEqual(
139            [
140                100,
141                10,
142                10,
143                "rmoveto",
144                10,
145                30,
146                0,
147                10,
148                "hhcurveto",
149                10,
150                0,
151                30,
152                10,
153                "vvcurveto",
154                -10,
155                -10,
156                -10,
157                10,
158                -10,
159                "hhcurveto",
160                10,
161                -10,
162                -10,
163                -10,
164                -10,
165                "vvcurveto",
166                -5,
167                -5,
168                -6,
169                -5,
170                1,
171                "vhcurveto",
172                -5,
173                -6,
174                5,
175                5,
176                1,
177                "hvcurveto",
178                -3,
179                -5,
180                -1,
181                -10,
182                4,
183                -5,
184                "rrcurveto",
185                "endchar",
186            ],
187            charstring.program,
188        )
189
190    def test_default_width(self):
191        pen = T2CharStringPen(None, {})
192        charstring = pen.getCharString(None, None)
193        self.assertEqual(["endchar"], charstring.program)
194
195    def test_no_round(self):
196        pen = T2CharStringPen(100.1, {}, roundTolerance=0.0)
197        pen.moveTo((0, 0))
198        pen.curveTo((10.1, 0.1), (19.9, 9.9), (20.49, 20.49))
199        pen.curveTo((20.49, 30.49), (9.9, 39.9), (0.1, 40.1))
200        pen.closePath()
201        charstring = pen.getCharString(None, None)
202
203        self.assertAlmostEqualProgram(
204            [
205                100,  # we always round the advance width
206                0,
207                "hmoveto",
208                10.1,
209                0.1,
210                9.8,
211                9.8,
212                0.59,
213                10.59,
214                "rrcurveto",
215                10,
216                -10.59,
217                9.41,
218                -9.8,
219                0.2,
220                "vhcurveto",
221                "endchar",
222            ],
223            charstring.program,
224        )
225
226    def test_round_all(self):
227        pen = T2CharStringPen(100.1, {}, roundTolerance=0.5)
228        pen.moveTo((0, 0))
229        pen.curveTo((10.1, 0.1), (19.9, 9.9), (20.49, 20.49))
230        pen.curveTo((20.49, 30.5), (9.9, 39.9), (0.1, 40.1))
231        pen.closePath()
232        charstring = pen.getCharString(None, None)
233
234        self.assertEqual(
235            [
236                100,
237                0,
238                "hmoveto",
239                10,
240                10,
241                10,
242                10,
243                11,
244                -10,
245                9,
246                -10,
247                "hvcurveto",
248                "endchar",
249            ],
250            charstring.program,
251        )
252
253    def test_round_some(self):
254        pen = T2CharStringPen(100, {}, roundTolerance=0.2)
255        pen.moveTo((0, 0))
256        # the following two are rounded as within the tolerance
257        pen.lineTo((10.1, 0.1))
258        pen.lineTo((19.9, 9.9))
259        # this one is not rounded as it exceeds the tolerance
260        pen.lineTo((20.49, 20.49))
261        pen.closePath()
262        charstring = pen.getCharString(None, None)
263
264        self.assertAlmostEqualProgram(
265            [
266                100,
267                0,
268                "hmoveto",
269                10,
270                "hlineto",
271                10,
272                10,
273                0.49,
274                10.49,
275                "rlineto",
276                "endchar",
277            ],
278            charstring.program,
279        )
280
281    def test_invalid_tolerance(self):
282        self.assertRaisesRegex(
283            ValueError,
284            "Rounding tolerance must be positive",
285            T2CharStringPen,
286            None,
287            {},
288            roundTolerance=-0.1,
289        )
290
291
292if __name__ == "__main__":
293    import sys
294
295    sys.exit(unittest.main())
296