• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from __future__ import print_function, division, absolute_import
2from __future__ import unicode_literals
3from fontTools.misc.py23 import *
4from fontTools.ttLib import TTFont, tagToXML
5import os
6import sys
7import re
8import contextlib
9import pytest
10
11try:
12    import unicodedata2
13except ImportError:
14    if sys.version_info[:2] < (3, 6):
15        unicodedata2 = None
16    else:
17        # on 3.6 the built-in unicodedata is the same as unicodedata2 backport
18        import unicodedata
19        unicodedata2 = unicodedata
20
21
22# Font files in data/*.{o,t}tf; output gets compared to data/*.ttx.*
23TESTS = {
24    "aots/base.otf":                                ('CFF ', 'cmap', 'head',
25                                                     'hhea', 'hmtx', 'maxp',
26                                                     'name', 'OS/2', 'post'),
27    "aots/classdef1_font1.otf":                     ('GSUB',),
28    "aots/classdef1_font2.otf":                     ('GSUB',),
29    "aots/classdef1_font3.otf":                     ('GSUB',),
30    "aots/classdef1_font4.otf":                     ('GSUB',),
31    "aots/classdef2_font1.otf":                     ('GSUB',),
32    "aots/classdef2_font2.otf":                     ('GSUB',),
33    "aots/classdef2_font3.otf":                     ('GSUB',),
34    "aots/classdef2_font4.otf":                     ('GSUB',),
35    "aots/cmap0_font1.otf":                         ('cmap',),
36    "aots/cmap10_font1.otf":                        ('cmap',),
37    "aots/cmap10_font2.otf":                        ('cmap',),
38    "aots/cmap12_font1.otf":                        ('cmap',),
39    "aots/cmap14_font1.otf":                        ('cmap',),
40    "aots/cmap2_font1.otf":                         ('cmap',),
41    "aots/cmap4_font1.otf":                         ('cmap',),
42    "aots/cmap4_font2.otf":                         ('cmap',),
43    "aots/cmap4_font3.otf":                         ('cmap',),
44    "aots/cmap4_font4.otf":                         ('cmap',),
45    "aots/cmap6_font1.otf":                         ('cmap',),
46    "aots/cmap6_font2.otf":                         ('cmap',),
47    "aots/cmap8_font1.otf":                         ('cmap',),
48    "aots/cmap_composition_font1.otf":              ('cmap',),
49    "aots/cmap_subtableselection_font1.otf":        ('cmap',),
50    "aots/cmap_subtableselection_font2.otf":        ('cmap',),
51    "aots/cmap_subtableselection_font3.otf":        ('cmap',),
52    "aots/cmap_subtableselection_font4.otf":        ('cmap',),
53    "aots/cmap_subtableselection_font5.otf":        ('cmap',),
54    "aots/gpos1_1_lookupflag_f1.otf":               ('GDEF', 'GPOS'),
55    "aots/gpos1_1_simple_f1.otf":                   ('GPOS',),
56    "aots/gpos1_1_simple_f2.otf":                   ('GPOS',),
57    "aots/gpos1_1_simple_f3.otf":                   ('GPOS',),
58    "aots/gpos1_1_simple_f4.otf":                   ('GPOS',),
59    "aots/gpos1_2_font1.otf":                       ('GPOS',),
60    "aots/gpos1_2_font2.otf":                       ('GDEF', 'GPOS'),
61    "aots/gpos2_1_font6.otf":                       ('GPOS',),
62    "aots/gpos2_1_font7.otf":                       ('GPOS',),
63    "aots/gpos2_1_lookupflag_f1.otf":               ('GDEF', 'GPOS'),
64    "aots/gpos2_1_lookupflag_f2.otf":               ('GDEF', 'GPOS'),
65    "aots/gpos2_1_next_glyph_f1.otf":               ('GPOS',),
66    "aots/gpos2_1_next_glyph_f2.otf":               ('GPOS',),
67    "aots/gpos2_1_simple_f1.otf":                   ('GPOS',),
68    "aots/gpos2_2_font1.otf":                       ('GPOS',),
69    "aots/gpos2_2_font2.otf":                       ('GDEF', 'GPOS'),
70    "aots/gpos2_2_font3.otf":                       ('GDEF', 'GPOS'),
71    "aots/gpos2_2_font4.otf":                       ('GPOS',),
72    "aots/gpos2_2_font5.otf":                       ('GPOS',),
73    "aots/gpos3_font1.otf":                         ('GPOS',),
74    "aots/gpos3_font2.otf":                         ('GDEF', 'GPOS'),
75    "aots/gpos3_font3.otf":                         ('GDEF', 'GPOS'),
76    "aots/gpos4_lookupflag_f1.otf":                 ('GDEF', 'GPOS'),
77    "aots/gpos4_lookupflag_f2.otf":                 ('GDEF', 'GPOS'),
78    "aots/gpos4_multiple_anchors_1.otf":            ('GDEF', 'GPOS'),
79    "aots/gpos4_simple_1.otf":                      ('GDEF', 'GPOS'),
80    "aots/gpos5_font1.otf":                         ('GDEF', 'GPOS', 'GSUB'),
81    "aots/gpos6_font1.otf":                         ('GDEF', 'GPOS'),
82    "aots/gpos7_1_font1.otf":                       ('GPOS',),
83    "aots/gpos9_font1.otf":                         ('GPOS',),
84    "aots/gpos9_font2.otf":                         ('GPOS',),
85    "aots/gpos_chaining1_boundary_f1.otf":          ('GDEF', 'GPOS'),
86    "aots/gpos_chaining1_boundary_f2.otf":          ('GDEF', 'GPOS'),
87    "aots/gpos_chaining1_boundary_f3.otf":          ('GDEF', 'GPOS'),
88    "aots/gpos_chaining1_boundary_f4.otf":          ('GDEF', 'GPOS'),
89    "aots/gpos_chaining1_lookupflag_f1.otf":        ('GDEF', 'GPOS'),
90    "aots/gpos_chaining1_multiple_subrules_f1.otf": ('GDEF', 'GPOS'),
91    "aots/gpos_chaining1_multiple_subrules_f2.otf": ('GDEF', 'GPOS'),
92    "aots/gpos_chaining1_next_glyph_f1.otf":        ('GDEF', 'GPOS'),
93    "aots/gpos_chaining1_simple_f1.otf":            ('GDEF', 'GPOS'),
94    "aots/gpos_chaining1_simple_f2.otf":            ('GDEF', 'GPOS'),
95    "aots/gpos_chaining1_successive_f1.otf":        ('GDEF', 'GPOS'),
96    "aots/gpos_chaining2_boundary_f1.otf":          ('GDEF', 'GPOS'),
97    "aots/gpos_chaining2_boundary_f2.otf":          ('GDEF', 'GPOS'),
98    "aots/gpos_chaining2_boundary_f3.otf":          ('GDEF', 'GPOS'),
99    "aots/gpos_chaining2_boundary_f4.otf":          ('GDEF', 'GPOS'),
100    "aots/gpos_chaining2_lookupflag_f1.otf":        ('GDEF', 'GPOS'),
101    "aots/gpos_chaining2_multiple_subrules_f1.otf": ('GDEF', 'GPOS'),
102    "aots/gpos_chaining2_multiple_subrules_f2.otf": ('GDEF', 'GPOS'),
103    "aots/gpos_chaining2_next_glyph_f1.otf":        ('GDEF', 'GPOS'),
104    "aots/gpos_chaining2_simple_f1.otf":            ('GDEF', 'GPOS'),
105    "aots/gpos_chaining2_simple_f2.otf":            ('GDEF', 'GPOS'),
106    "aots/gpos_chaining2_successive_f1.otf":        ('GDEF', 'GPOS'),
107    "aots/gpos_chaining3_boundary_f1.otf":          ('GDEF', 'GPOS'),
108    "aots/gpos_chaining3_boundary_f2.otf":          ('GDEF', 'GPOS'),
109    "aots/gpos_chaining3_boundary_f3.otf":          ('GDEF', 'GPOS'),
110    "aots/gpos_chaining3_boundary_f4.otf":          ('GDEF', 'GPOS'),
111    "aots/gpos_chaining3_lookupflag_f1.otf":        ('GDEF', 'GPOS'),
112    "aots/gpos_chaining3_next_glyph_f1.otf":        ('GDEF', 'GPOS'),
113    "aots/gpos_chaining3_simple_f1.otf":            ('GDEF', 'GPOS'),
114    "aots/gpos_chaining3_simple_f2.otf":            ('GDEF', 'GPOS'),
115    "aots/gpos_chaining3_successive_f1.otf":        ('GDEF', 'GPOS'),
116    "aots/gpos_context1_boundary_f1.otf":           ('GDEF', 'GPOS'),
117    "aots/gpos_context1_boundary_f2.otf":           ('GDEF', 'GPOS'),
118    "aots/gpos_context1_expansion_f1.otf":          ('GDEF', 'GPOS'),
119    "aots/gpos_context1_lookupflag_f1.otf":         ('GDEF', 'GPOS'),
120    "aots/gpos_context1_lookupflag_f2.otf":         ('GDEF', 'GPOS'),
121    "aots/gpos_context1_multiple_subrules_f1.otf":  ('GDEF', 'GPOS'),
122    "aots/gpos_context1_multiple_subrules_f2.otf":  ('GDEF', 'GPOS'),
123    "aots/gpos_context1_next_glyph_f1.otf":         ('GDEF', 'GPOS'),
124    "aots/gpos_context1_simple_f1.otf":             ('GDEF', 'GPOS'),
125    "aots/gpos_context1_simple_f2.otf":             ('GDEF', 'GPOS'),
126    "aots/gpos_context1_successive_f1.otf":         ('GDEF', 'GPOS'),
127    "aots/gpos_context2_boundary_f1.otf":           ('GDEF', 'GPOS'),
128    "aots/gpos_context2_boundary_f2.otf":           ('GDEF', 'GPOS'),
129    "aots/gpos_context2_classes_f1.otf":            ('GDEF', 'GPOS'),
130    "aots/gpos_context2_classes_f2.otf":            ('GDEF', 'GPOS'),
131    "aots/gpos_context2_expansion_f1.otf":          ('GDEF', 'GPOS'),
132    "aots/gpos_context2_lookupflag_f1.otf":         ('GDEF', 'GPOS'),
133    "aots/gpos_context2_lookupflag_f2.otf":         ('GDEF', 'GPOS'),
134    "aots/gpos_context2_multiple_subrules_f1.otf":  ('GDEF', 'GPOS'),
135    "aots/gpos_context2_multiple_subrules_f2.otf":  ('GDEF', 'GPOS'),
136    "aots/gpos_context2_next_glyph_f1.otf":         ('GDEF', 'GPOS'),
137    "aots/gpos_context2_simple_f1.otf":             ('GDEF', 'GPOS'),
138    "aots/gpos_context2_simple_f2.otf":             ('GDEF', 'GPOS'),
139    "aots/gpos_context2_successive_f1.otf":         ('GDEF', 'GPOS'),
140    "aots/gpos_context3_boundary_f1.otf":           ('GDEF', 'GPOS'),
141    "aots/gpos_context3_boundary_f2.otf":           ('GDEF', 'GPOS'),
142    "aots/gpos_context3_lookupflag_f1.otf":         ('GDEF', 'GPOS'),
143    "aots/gpos_context3_lookupflag_f2.otf":         ('GDEF', 'GPOS'),
144    "aots/gpos_context3_next_glyph_f1.otf":         ('GDEF', 'GPOS'),
145    "aots/gpos_context3_simple_f1.otf":             ('GDEF', 'GPOS'),
146    "aots/gpos_context3_successive_f1.otf":         ('GDEF', 'GPOS'),
147    "aots/gsub1_1_lookupflag_f1.otf":               ('GDEF', 'GSUB'),
148    "aots/gsub1_1_modulo_f1.otf":                   ('GSUB',),
149    "aots/gsub1_1_simple_f1.otf":                   ('GSUB',),
150    "aots/gsub1_2_lookupflag_f1.otf":               ('GDEF', 'GSUB'),
151    "aots/gsub1_2_simple_f1.otf":                   ('GSUB',),
152    "aots/gsub2_1_lookupflag_f1.otf":               ('GDEF', 'GSUB'),
153    "aots/gsub2_1_multiple_sequences_f1.otf":       ('GSUB',),
154    "aots/gsub2_1_simple_f1.otf":                   ('GSUB',),
155    "aots/gsub3_1_lookupflag_f1.otf":               ('GDEF', 'GSUB'),
156    "aots/gsub3_1_multiple_f1.otf":                 ('GSUB',),
157    "aots/gsub3_1_simple_f1.otf":                   ('GSUB',),
158    "aots/gsub4_1_lookupflag_f1.otf":               ('GDEF', 'GSUB'),
159    "aots/gsub4_1_multiple_ligatures_f1.otf":       ('GSUB',),
160    "aots/gsub4_1_multiple_ligatures_f2.otf":       ('GSUB',),
161    "aots/gsub4_1_multiple_ligsets_f1.otf":         ('GSUB',),
162    "aots/gsub4_1_simple_f1.otf":                   ('GSUB',),
163    "aots/gsub7_font1.otf":                         ('GSUB',),
164    "aots/gsub7_font2.otf":                         ('GSUB',),
165    "aots/gsub_chaining1_boundary_f1.otf":          ('GDEF', 'GSUB'),
166    "aots/gsub_chaining1_boundary_f2.otf":          ('GDEF', 'GSUB'),
167    "aots/gsub_chaining1_boundary_f3.otf":          ('GDEF', 'GSUB'),
168    "aots/gsub_chaining1_boundary_f4.otf":          ('GDEF', 'GSUB'),
169    "aots/gsub_chaining1_lookupflag_f1.otf":        ('GDEF', 'GSUB'),
170    "aots/gsub_chaining1_multiple_subrules_f1.otf": ('GDEF', 'GSUB'),
171    "aots/gsub_chaining1_multiple_subrules_f2.otf": ('GDEF', 'GSUB'),
172    "aots/gsub_chaining1_next_glyph_f1.otf":        ('GDEF', 'GSUB'),
173    "aots/gsub_chaining1_simple_f1.otf":            ('GDEF', 'GSUB'),
174    "aots/gsub_chaining1_simple_f2.otf":            ('GDEF', 'GSUB'),
175    "aots/gsub_chaining1_successive_f1.otf":        ('GDEF', 'GSUB'),
176    "aots/gsub_chaining2_boundary_f1.otf":          ('GDEF', 'GSUB'),
177    "aots/gsub_chaining2_boundary_f2.otf":          ('GDEF', 'GSUB'),
178    "aots/gsub_chaining2_boundary_f3.otf":          ('GDEF', 'GSUB'),
179    "aots/gsub_chaining2_boundary_f4.otf":          ('GDEF', 'GSUB'),
180    "aots/gsub_chaining2_lookupflag_f1.otf":        ('GDEF', 'GSUB'),
181    "aots/gsub_chaining2_multiple_subrules_f1.otf": ('GDEF', 'GSUB'),
182    "aots/gsub_chaining2_multiple_subrules_f2.otf": ('GDEF', 'GSUB'),
183    "aots/gsub_chaining2_next_glyph_f1.otf":        ('GDEF', 'GSUB'),
184    "aots/gsub_chaining2_simple_f1.otf":            ('GDEF', 'GSUB'),
185    "aots/gsub_chaining2_simple_f2.otf":            ('GDEF', 'GSUB'),
186    "aots/gsub_chaining2_successive_f1.otf":        ('GDEF', 'GSUB'),
187    "aots/gsub_chaining3_boundary_f1.otf":          ('GDEF', 'GSUB'),
188    "aots/gsub_chaining3_boundary_f2.otf":          ('GDEF', 'GSUB'),
189    "aots/gsub_chaining3_boundary_f3.otf":          ('GDEF', 'GSUB'),
190    "aots/gsub_chaining3_boundary_f4.otf":          ('GDEF', 'GSUB'),
191    "aots/gsub_chaining3_lookupflag_f1.otf":        ('GDEF', 'GSUB'),
192    "aots/gsub_chaining3_next_glyph_f1.otf":        ('GDEF', 'GSUB'),
193    "aots/gsub_chaining3_simple_f1.otf":            ('GDEF', 'GSUB'),
194    "aots/gsub_chaining3_simple_f2.otf":            ('GDEF', 'GSUB'),
195    "aots/gsub_chaining3_successive_f1.otf":        ('GDEF', 'GSUB'),
196    "aots/gsub_context1_boundary_f1.otf":           ('GDEF', 'GSUB'),
197    "aots/gsub_context1_boundary_f2.otf":           ('GDEF', 'GSUB'),
198    "aots/gsub_context1_expansion_f1.otf":          ('GDEF', 'GSUB'),
199    "aots/gsub_context1_lookupflag_f1.otf":         ('GDEF', 'GSUB'),
200    "aots/gsub_context1_lookupflag_f2.otf":         ('GDEF', 'GSUB'),
201    "aots/gsub_context1_multiple_subrules_f1.otf":  ('GDEF', 'GSUB'),
202    "aots/gsub_context1_multiple_subrules_f2.otf":  ('GDEF', 'GSUB'),
203    "aots/gsub_context1_next_glyph_f1.otf":         ('GDEF', 'GSUB'),
204    "aots/gsub_context1_simple_f1.otf":             ('GDEF', 'GSUB'),
205    "aots/gsub_context1_simple_f2.otf":             ('GDEF', 'GSUB'),
206    "aots/gsub_context1_successive_f1.otf":         ('GDEF', 'GSUB'),
207    "aots/gsub_context2_boundary_f1.otf":           ('GDEF', 'GSUB'),
208    "aots/gsub_context2_boundary_f2.otf":           ('GDEF', 'GSUB'),
209    "aots/gsub_context2_classes_f1.otf":            ('GDEF', 'GSUB'),
210    "aots/gsub_context2_classes_f2.otf":            ('GDEF', 'GSUB'),
211    "aots/gsub_context2_expansion_f1.otf":          ('GDEF', 'GSUB'),
212    "aots/gsub_context2_lookupflag_f1.otf":         ('GDEF', 'GSUB'),
213    "aots/gsub_context2_lookupflag_f2.otf":         ('GDEF', 'GSUB'),
214    "aots/gsub_context2_multiple_subrules_f1.otf":  ('GDEF', 'GSUB'),
215    "aots/gsub_context2_multiple_subrules_f2.otf":  ('GDEF', 'GSUB'),
216    "aots/gsub_context2_next_glyph_f1.otf":         ('GDEF', 'GSUB'),
217    "aots/gsub_context2_simple_f1.otf":             ('GDEF', 'GSUB'),
218    "aots/gsub_context2_simple_f2.otf":             ('GDEF', 'GSUB'),
219    "aots/gsub_context2_successive_f1.otf":         ('GDEF', 'GSUB'),
220    "aots/gsub_context3_boundary_f1.otf":           ('GDEF', 'GSUB'),
221    "aots/gsub_context3_boundary_f2.otf":           ('GDEF', 'GSUB'),
222    "aots/gsub_context3_lookupflag_f1.otf":         ('GDEF', 'GSUB'),
223    "aots/gsub_context3_lookupflag_f2.otf":         ('GDEF', 'GSUB'),
224    "aots/gsub_context3_next_glyph_f1.otf":         ('GDEF', 'GSUB'),
225    "aots/gsub_context3_simple_f1.otf":             ('GDEF', 'GSUB'),
226    "aots/gsub_context3_successive_f1.otf":         ('GDEF', 'GSUB'),
227    "aots/lookupflag_ignore_attach_f1.otf":         ('GDEF', 'GSUB'),
228    "aots/lookupflag_ignore_base_f1.otf":           ('GDEF', 'GSUB'),
229    "aots/lookupflag_ignore_combination_f1.otf":    ('GDEF', 'GSUB'),
230    "aots/lookupflag_ignore_ligatures_f1.otf":      ('GDEF', 'GSUB'),
231    "aots/lookupflag_ignore_marks_f1.otf":          ('GDEF', 'GSUB'),
232    "graphite/graphite_tests.ttf":                  ('Silf', 'Glat', 'Feat', 'Sill'),
233}
234
235
236TEST_REQUIREMENTS = {
237    "aots/cmap4_font4.otf":                         ("unicodedata2",),
238}
239
240
241ttLibVersion_RE = re.compile(r' ttLibVersion=".*"')
242
243
244def getpath(testfile):
245    path = os.path.dirname(__file__)
246    return os.path.join(path, "data", testfile)
247
248
249def read_expected_ttx(testfile, tableTag):
250    name = os.path.splitext(testfile)[0]
251    xml_expected_path = getpath("%s.ttx.%s" % (name, tagToXML(tableTag)))
252    with open(xml_expected_path, 'r', encoding="utf-8") as xml_file:
253        xml_expected = ttLibVersion_RE.sub('', xml_file.read())
254    return xml_expected
255
256
257def dump_ttx(font, tableTag):
258    f = UnicodeIO()
259    font.saveXML(f, newlinestr='\n', tables=[tableTag])
260    return ttLibVersion_RE.sub('', f.getvalue())
261
262
263def load_ttx(ttx):
264    f = UnicodeIO()
265    f.write(ttx)
266    f.seek(0)
267    font = TTFont()
268    font.importXML(f)
269    return font
270
271
272@contextlib.contextmanager
273def open_font(testfile):
274    font = TTFont(getpath(testfile))
275    try:
276        yield font
277    finally:
278        font.close()
279
280
281def _skip_if_requirement_missing(testfile):
282    if testfile in TEST_REQUIREMENTS:
283        for req in TEST_REQUIREMENTS[testfile]:
284            if globals()[req] is None:
285                pytest.skip('%s not installed' % req)
286
287
288def test_xml_from_binary(testfile, tableTag):
289    """Check XML from decompiled object."""
290    _skip_if_requirement_missing(testfile)
291
292    xml_expected = read_expected_ttx(testfile, tableTag)
293
294    with open_font(testfile) as font:
295        xml_from_binary = dump_ttx(font, tableTag)
296
297    assert xml_expected == xml_from_binary
298
299
300def test_xml_from_xml(testfile, tableTag):
301    """Check XML from object read from XML."""
302    _skip_if_requirement_missing(testfile)
303
304    xml_expected = read_expected_ttx(testfile, tableTag)
305
306    font = load_ttx(xml_expected)
307    name = os.path.splitext(testfile)[0]
308    setupfile = getpath("%s.ttx.%s.setup" % (name, tagToXML(tableTag)))
309    if os.path.exists(setupfile):
310#        import pdb; pdb.set_trace()
311        font.importXML(setupfile)
312    xml_from_xml = dump_ttx(font, tableTag)
313
314    assert xml_expected == xml_from_xml
315
316
317def pytest_generate_tests(metafunc):
318    # http://doc.pytest.org/en/latest/parametrize.html#basic-pytest-generate-tests-example
319    fixturenames = metafunc.fixturenames
320    argnames = ("testfile", "tableTag")
321    if all(fn in fixturenames for fn in argnames):
322        argvalues = [(testfile, tableTag)
323                     for testfile, tableTags in sorted(TESTS.items())
324                     for tableTag in tableTags]
325        metafunc.parametrize(argnames, argvalues)
326
327
328if __name__ == '__main__':
329    sys.exit(pytest.main(sys.argv))
330