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