1from fontTools.pens.t2CharStringPen import T2CharStringPen
2import unittest
3
4
5class T2CharStringPenTest(unittest.TestCase):
6
7    def __init__(self, methodName):
8        unittest.TestCase.__init__(self, methodName)
9
10    def assertAlmostEqualProgram(self, expected, actual):
11        self.assertEqual(len(expected), len(actual))
12        for i1, i2 in zip(expected, actual):
13            if isinstance(i1, str):
14                self.assertIsInstance(i2, str)
15                self.assertEqual(i1, i2)
16            else:
17                self.assertAlmostEqual(i1, i2)
18
19    def test_draw_h_v_lines(self):
20        pen = T2CharStringPen(100, {})
21        pen.moveTo((0, 0))
22        pen.lineTo((10, 0))
23        pen.lineTo((10, 10))
24        pen.lineTo((0, 10))
25        pen.closePath()  # no-op
26        pen.moveTo((10, 10))
27        pen.lineTo((10, 20))
28        pen.lineTo((0, 20))
29        pen.lineTo((0, 10))
30        pen.closePath()
31        charstring = pen.getCharString(None, None)
32
33        self.assertEqual(
34            [100,
35             0, 'hmoveto',
36             10, 10, -10, 'hlineto',
37             10, 'hmoveto',
38             10, -10, -10, 'vlineto',
39             'endchar'],
40            charstring.program)
41
42    def test_draw_lines(self):
43        pen = T2CharStringPen(100, {})
44        pen.moveTo((5, 5))
45        pen.lineTo((25, 15))
46        pen.lineTo((35, 35))
47        pen.lineTo((15, 25))
48        pen.closePath()  # no-op
49        charstring = pen.getCharString(None, None)
50
51        self.assertEqual(
52            [100,
53             5, 5, 'rmoveto',
54             20, 10, 10, 20, -20, -10, 'rlineto',
55             'endchar'],
56            charstring.program)
57
58    def test_draw_h_v_curves(self):
59        pen = T2CharStringPen(100, {})
60        pen.moveTo((0, 0))
61        pen.curveTo((10, 0), (20, 10), (20, 20))
62        pen.curveTo((20, 30), (10, 40), (0, 40))
63        pen.endPath()  # no-op
64        charstring = pen.getCharString(None, None)
65
66        self.assertEqual(
67            [100,
68             0, 'hmoveto',
69             10, 10, 10, 10, 10, -10, 10, -10, 'hvcurveto',
70             'endchar'],
71            charstring.program)
72
73    def test_draw_curves(self):
74        pen = T2CharStringPen(100, {})
75        pen.moveTo((95, 25))
76        pen.curveTo((115, 44), (115, 76), (95, 95))
77        pen.curveTo((76, 114), (44, 115), (25, 95))
78        pen.endPath()  # no-op
79        charstring = pen.getCharString(None, None)
80
81        self.assertEqual(
82            [100,
83             95, 25, 'rmoveto',
84             20, 19, 0, 32, -20, 19, -19, 19, -32, 1, -19, -20, 'rrcurveto',
85             'endchar'],
86            charstring.program)
87
88    def test_draw_more_curves(self):
89        pen = T2CharStringPen(100, {})
90        pen.moveTo((10, 10))
91        pen.curveTo((20, 10), (50, 10), (60, 10))
92        pen.curveTo((60, 20), (60, 50), (60, 60))
93        pen.curveTo((50, 50), (40, 60), (30, 60))
94        pen.curveTo((40, 50), (30, 40), (30, 30))
95        pen.curveTo((30, 25), (25, 19), (20, 20))
96        pen.curveTo((15, 20), (9, 25), (10, 30))
97        pen.curveTo((7, 25), (6, 15), (10, 10))
98        pen.endPath()  # no-op
99        charstring = pen.getCharString(None, None)
100
101        self.assertEqual(
102            [100,
103             10, 10, 'rmoveto',
104             10, 30, 0, 10, 'hhcurveto',
105             10, 0, 30, 10, 'vvcurveto',
106             -10, -10, -10, 10, -10, 'hhcurveto',
107             10, -10, -10, -10, -10, 'vvcurveto',
108             -5, -5, -6, -5, 1, 'vhcurveto',
109             -5, -6, 5, 5, 1, 'hvcurveto',
110             -3, -5, -1, -10, 4, -5, 'rrcurveto',
111             'endchar'],
112            charstring.program)
113
114    def test_default_width(self):
115        pen = T2CharStringPen(None, {})
116        charstring = pen.getCharString(None, None)
117        self.assertEqual(['endchar'], charstring.program)
118
119    def test_no_round(self):
120        pen = T2CharStringPen(100.1, {}, roundTolerance=0.0)
121        pen.moveTo((0, 0))
122        pen.curveTo((10.1, 0.1), (19.9, 9.9), (20.49, 20.49))
123        pen.curveTo((20.49, 30.49), (9.9, 39.9), (0.1, 40.1))
124        pen.closePath()
125        charstring = pen.getCharString(None, None)
126
127        self.assertAlmostEqualProgram(
128            [100,  # we always round the advance width
129             0, 'hmoveto',
130             10.1, 0.1, 9.8, 9.8, 0.59, 10.59, 'rrcurveto',
131             10, -10.59, 9.41, -9.8, 0.2, 'vhcurveto',
132             'endchar'],
133            charstring.program)
134
135    def test_round_all(self):
136        pen = T2CharStringPen(100.1, {}, roundTolerance=0.5)
137        pen.moveTo((0, 0))
138        pen.curveTo((10.1, 0.1), (19.9, 9.9), (20.49, 20.49))
139        pen.curveTo((20.49, 30.5), (9.9, 39.9), (0.1, 40.1))
140        pen.closePath()
141        charstring = pen.getCharString(None, None)
142
143        self.assertEqual(
144            [100,
145             0, 'hmoveto',
146             10, 10, 10, 10, 11, -10, 9, -10, 'hvcurveto',
147             'endchar'],
148            charstring.program)
149
150    def test_round_some(self):
151        pen = T2CharStringPen(100, {}, roundTolerance=0.2)
152        pen.moveTo((0, 0))
153        # the following two are rounded as within the tolerance
154        pen.lineTo((10.1, 0.1))
155        pen.lineTo((19.9, 9.9))
156        # this one is not rounded as it exceeds the tolerance
157        pen.lineTo((20.49, 20.49))
158        pen.closePath()
159        charstring = pen.getCharString(None, None)
160
161        self.assertAlmostEqualProgram(
162            [100,
163             0, 'hmoveto',
164             10, 'hlineto',
165             10, 10, 0.49, 10.49, 'rlineto',
166             'endchar'],
167            charstring.program)
168
169    def test_invalid_tolerance(self):
170        self.assertRaisesRegex(
171            ValueError,
172            "Rounding tolerance must be positive",
173            T2CharStringPen, None, {}, roundTolerance=-0.1)
174
175
176if __name__ == '__main__':
177    import sys
178    sys.exit(unittest.main())
179