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