1 from __future__ import print_function, division, absolute_import 2 from fontTools.misc.py23 import * 3 4 import os 5 import unittest 6 import struct 7 8 from fontTools import ttLib 9 from fontTools.misc.testTools import TestCase 10 from fontTools.pens.ttGlyphPen import TTGlyphPen, MAX_F2DOT14 11 12 13 class TTGlyphPenTest(TestCase): 14 15 def runEndToEnd(self, filename): 16 font = ttLib.TTFont() 17 ttx_path = os.path.join( 18 os.path.abspath(os.path.dirname(os.path.realpath(__file__))), 19 '..', 'ttLib', 'data', filename) 20 font.importXML(ttx_path) 21 22 glyphSet = font.getGlyphSet() 23 glyfTable = font['glyf'] 24 pen = TTGlyphPen(font.getGlyphSet()) 25 26 for name in font.getGlyphOrder(): 27 oldGlyph = glyphSet[name] 28 oldGlyph.draw(pen) 29 oldGlyph = oldGlyph._glyph 30 newGlyph = pen.glyph() 31 32 if hasattr(oldGlyph, 'program'): 33 newGlyph.program = oldGlyph.program 34 35 self.assertEqual( 36 oldGlyph.compile(glyfTable), newGlyph.compile(glyfTable)) 37 38 def test_e2e_linesAndSimpleComponents(self): 39 self.runEndToEnd('TestTTF-Regular.ttx') 40 41 def test_e2e_curvesAndComponentTransforms(self): 42 self.runEndToEnd('TestTTFComplex-Regular.ttx') 43 44 def test_moveTo_errorWithinContour(self): 45 pen = TTGlyphPen(None) 46 pen.moveTo((0, 0)) 47 with self.assertRaises(AssertionError): 48 pen.moveTo((1, 0)) 49 50 def test_closePath_ignoresAnchors(self): 51 pen = TTGlyphPen(None) 52 pen.moveTo((0, 0)) 53 pen.closePath() 54 self.assertFalse(pen.points) 55 self.assertFalse(pen.types) 56 self.assertFalse(pen.endPts) 57 58 def test_endPath_sameAsClosePath(self): 59 pen = TTGlyphPen(None) 60 61 pen.moveTo((0, 0)) 62 pen.lineTo((0, 1)) 63 pen.lineTo((1, 0)) 64 pen.closePath() 65 closePathGlyph = pen.glyph() 66 67 pen.moveTo((0, 0)) 68 pen.lineTo((0, 1)) 69 pen.lineTo((1, 0)) 70 pen.endPath() 71 endPathGlyph = pen.glyph() 72 73 self.assertEqual(closePathGlyph, endPathGlyph) 74 75 def test_glyph_errorOnUnendedContour(self): 76 pen = TTGlyphPen(None) 77 pen.moveTo((0, 0)) 78 with self.assertRaises(AssertionError): 79 pen.glyph() 80 81 def test_glyph_decomposes(self): 82 componentName = 'a' 83 glyphSet = {} 84 pen = TTGlyphPen(glyphSet) 85 86 pen.moveTo((0, 0)) 87 pen.lineTo((0, 1)) 88 pen.lineTo((1, 0)) 89 pen.closePath() 90 glyphSet[componentName] = _TestGlyph(pen.glyph()) 91 92 pen.moveTo((0, 0)) 93 pen.lineTo((0, 1)) 94 pen.lineTo((1, 0)) 95 pen.closePath() 96 pen.addComponent(componentName, (1, 0, 0, 1, 2, 0)) 97 pen.addComponent("missing", (1, 0, 0, 1, 0, 0)) # skipped 98 compositeGlyph = pen.glyph() 99 100 pen.moveTo((0, 0)) 101 pen.lineTo((0, 1)) 102 pen.lineTo((1, 0)) 103 pen.closePath() 104 pen.moveTo((2, 0)) 105 pen.lineTo((2, 1)) 106 pen.lineTo((3, 0)) 107 pen.closePath() 108 plainGlyph = pen.glyph() 109 110 self.assertEqual(plainGlyph, compositeGlyph) 111 112 def test_remove_extra_move_points(self): 113 pen = TTGlyphPen(None) 114 pen.moveTo((0, 0)) 115 pen.lineTo((100, 0)) 116 pen.qCurveTo((100, 50), (50, 100), (0, 0)) 117 pen.closePath() 118 self.assertEqual(len(pen.points), 4) 119 self.assertEqual(pen.points[0], (0, 0)) 120 121 def test_keep_move_point(self): 122 pen = TTGlyphPen(None) 123 pen.moveTo((0, 0)) 124 pen.lineTo((100, 0)) 125 pen.qCurveTo((100, 50), (50, 100), (30, 30)) 126 # when last and move pts are different, closePath() implies a lineTo 127 pen.closePath() 128 self.assertEqual(len(pen.points), 5) 129 self.assertEqual(pen.points[0], (0, 0)) 130 131 def test_keep_duplicate_end_point(self): 132 pen = TTGlyphPen(None) 133 pen.moveTo((0, 0)) 134 pen.lineTo((100, 0)) 135 pen.qCurveTo((100, 50), (50, 100), (0, 0)) 136 pen.lineTo((0, 0)) # the duplicate point is not removed 137 pen.closePath() 138 self.assertEqual(len(pen.points), 5) 139 self.assertEqual(pen.points[0], (0, 0)) 140 141 def test_within_range_component_transform(self): 142 componentName = 'a' 143 glyphSet = {} 144 pen = TTGlyphPen(glyphSet) 145 146 pen.moveTo((0, 0)) 147 pen.lineTo((0, 1)) 148 pen.lineTo((1, 0)) 149 pen.closePath() 150 glyphSet[componentName] = _TestGlyph(pen.glyph()) 151 152 pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0)) 153 pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0)) 154 compositeGlyph = pen.glyph() 155 156 pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0)) 157 pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0)) 158 expectedGlyph = pen.glyph() 159 160 self.assertEqual(expectedGlyph, compositeGlyph) 161 162 def test_clamp_to_almost_2_component_transform(self): 163 componentName = 'a' 164 glyphSet = {} 165 pen = TTGlyphPen(glyphSet) 166 167 pen.moveTo((0, 0)) 168 pen.lineTo((0, 1)) 169 pen.lineTo((1, 0)) 170 pen.closePath() 171 glyphSet[componentName] = _TestGlyph(pen.glyph()) 172 173 pen.addComponent(componentName, (1.99999, 0, 0, 1, 0, 0)) 174 pen.addComponent(componentName, (1, 2, 0, 1, 0, 0)) 175 pen.addComponent(componentName, (1, 0, 2, 1, 0, 0)) 176 pen.addComponent(componentName, (1, 0, 0, 2, 0, 0)) 177 pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0)) 178 compositeGlyph = pen.glyph() 179 180 almost2 = MAX_F2DOT14 # 0b1.11111111111111 181 pen.addComponent(componentName, (almost2, 0, 0, 1, 0, 0)) 182 pen.addComponent(componentName, (1, almost2, 0, 1, 0, 0)) 183 pen.addComponent(componentName, (1, 0, almost2, 1, 0, 0)) 184 pen.addComponent(componentName, (1, 0, 0, almost2, 0, 0)) 185 pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0)) 186 expectedGlyph = pen.glyph() 187 188 self.assertEqual(expectedGlyph, compositeGlyph) 189 190 def test_out_of_range_transform_decomposed(self): 191 componentName = 'a' 192 glyphSet = {} 193 pen = TTGlyphPen(glyphSet) 194 195 pen.moveTo((0, 0)) 196 pen.lineTo((0, 1)) 197 pen.lineTo((1, 0)) 198 pen.closePath() 199 glyphSet[componentName] = _TestGlyph(pen.glyph()) 200 201 pen.addComponent(componentName, (3, 0, 0, 2, 0, 0)) 202 pen.addComponent(componentName, (1, 0, 0, 1, -1, 2)) 203 pen.addComponent(componentName, (2, 0, 0, -3, 0, 0)) 204 compositeGlyph = pen.glyph() 205 206 pen.moveTo((0, 0)) 207 pen.lineTo((0, 2)) 208 pen.lineTo((3, 0)) 209 pen.closePath() 210 pen.moveTo((-1, 2)) 211 pen.lineTo((-1, 3)) 212 pen.lineTo((0, 2)) 213 pen.closePath() 214 pen.moveTo((0, 0)) 215 pen.lineTo((0, -3)) 216 pen.lineTo((2, 0)) 217 pen.closePath() 218 expectedGlyph = pen.glyph() 219 220 self.assertEqual(expectedGlyph, compositeGlyph) 221 222 def test_no_handle_overflowing_transform(self): 223 componentName = 'a' 224 glyphSet = {} 225 pen = TTGlyphPen(glyphSet, handleOverflowingTransforms=False) 226 227 pen.moveTo((0, 0)) 228 pen.lineTo((0, 1)) 229 pen.lineTo((1, 0)) 230 pen.closePath() 231 baseGlyph = pen.glyph() 232 glyphSet[componentName] = _TestGlyph(baseGlyph) 233 234 pen.addComponent(componentName, (3, 0, 0, 1, 0, 0)) 235 compositeGlyph = pen.glyph() 236 237 self.assertEqual(compositeGlyph.components[0].transform, 238 ((3, 0), (0, 1))) 239 240 with self.assertRaises(struct.error): 241 compositeGlyph.compile({'a': baseGlyph}) 242 243 244 class _TestGlyph(object): 245 def __init__(self, glyph): 246 self.coordinates = glyph.coordinates 247 248 def draw(self, pen): 249 pen.moveTo(self.coordinates[0]) 250 for point in self.coordinates[1:]: 251 pen.lineTo(point) 252 pen.closePath() 253 254 255 if __name__ == '__main__': 256 import sys 257 sys.exit(unittest.main()) 258