1from __future__ import print_function, division, absolute_import
2from fontTools.misc.py23 import *
3
4import os
5import unittest
6import struct
7
8from fontTools import ttLib
9from fontTools.misc.testTools import TestCase
10from fontTools.pens.ttGlyphPen import TTGlyphPen, MAX_F2DOT14
11
12
13class 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
244class _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
255if __name__ == '__main__':
256    import sys
257    sys.exit(unittest.main())
258