1from fontTools.ttLib import TTFont 2from fontTools.varLib import build 3from fontTools.varLib.interpolate_layout import interpolate_layout 4from fontTools.varLib.interpolate_layout import main as interpolate_layout_main 5from fontTools.designspaceLib import DesignSpaceDocument, DesignSpaceDocumentError 6from fontTools.feaLib.builder import addOpenTypeFeaturesFromString 7import difflib 8import os 9import shutil 10import sys 11import tempfile 12import unittest 13 14 15class InterpolateLayoutTest(unittest.TestCase): 16 def __init__(self, methodName): 17 unittest.TestCase.__init__(self, methodName) 18 # Python 3 renamed assertRaisesRegexp to assertRaisesRegex, 19 # and fires deprecation warnings if a program uses the old name. 20 if not hasattr(self, "assertRaisesRegex"): 21 self.assertRaisesRegex = self.assertRaisesRegexp 22 23 def setUp(self): 24 self.tempdir = None 25 self.num_tempfiles = 0 26 27 def tearDown(self): 28 if self.tempdir: 29 shutil.rmtree(self.tempdir) 30 31 @staticmethod 32 def get_test_input(test_file_or_folder): 33 path, _ = os.path.split(__file__) 34 return os.path.join(path, "data", test_file_or_folder) 35 36 @staticmethod 37 def get_test_output(test_file_or_folder): 38 path, _ = os.path.split(__file__) 39 return os.path.join(path, "data", "test_results", test_file_or_folder) 40 41 @staticmethod 42 def get_file_list(folder, suffix, prefix=''): 43 all_files = os.listdir(folder) 44 file_list = [] 45 for p in all_files: 46 if p.startswith(prefix) and p.endswith(suffix): 47 file_list.append(os.path.abspath(os.path.join(folder, p))) 48 return file_list 49 50 def temp_path(self, suffix): 51 self.temp_dir() 52 self.num_tempfiles += 1 53 return os.path.join(self.tempdir, 54 "tmp%d%s" % (self.num_tempfiles, suffix)) 55 56 def temp_dir(self): 57 if not self.tempdir: 58 self.tempdir = tempfile.mkdtemp() 59 60 def read_ttx(self, path): 61 lines = [] 62 with open(path, "r", encoding="utf-8") as ttx: 63 for line in ttx.readlines(): 64 # Elide ttFont attributes because ttLibVersion may change. 65 if line.startswith("<ttFont "): 66 lines.append("<ttFont>\n") 67 else: 68 lines.append(line.rstrip() + "\n") 69 return lines 70 71 def expect_ttx(self, font, expected_ttx, tables): 72 path = self.temp_path(suffix=".ttx") 73 font.saveXML(path, tables=tables) 74 actual = self.read_ttx(path) 75 expected = self.read_ttx(expected_ttx) 76 if actual != expected: 77 for line in difflib.unified_diff( 78 expected, actual, fromfile=expected_ttx, tofile=path): 79 sys.stdout.write(line) 80 self.fail("TTX output is different from expected") 81 82 def check_ttx_dump(self, font, expected_ttx, tables, suffix): 83 """Ensure the TTX dump is the same after saving and reloading the font.""" 84 path = self.temp_path(suffix=suffix) 85 font.save(path) 86 self.expect_ttx(TTFont(path), expected_ttx, tables) 87 88 def compile_font(self, path, suffix, temp_dir, features=None): 89 ttx_filename = os.path.basename(path) 90 savepath = os.path.join(temp_dir, ttx_filename.replace('.ttx', suffix)) 91 font = TTFont(recalcBBoxes=False, recalcTimestamp=False) 92 font.importXML(path) 93 if features: 94 addOpenTypeFeaturesFromString(font, features) 95 font.save(savepath, reorderTables=None) 96 return font, savepath 97 98# ----- 99# Tests 100# ----- 101 102 def test_varlib_interpolate_layout_GSUB_only_ttf(self): 103 """Only GSUB, and only in the base master. 104 105 The variable font will inherit the GSUB table from the 106 base master. 107 """ 108 suffix = '.ttf' 109 ds_path = self.get_test_input('InterpolateLayout.designspace') 110 ufo_dir = self.get_test_input('master_ufo') 111 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 112 113 self.temp_dir() 114 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 115 for path in ttx_paths: 116 self.compile_font(path, suffix, self.tempdir) 117 118 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 119 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 120 121 tables = ['GSUB'] 122 expected_ttx_path = self.get_test_output('InterpolateLayout.ttx') 123 self.expect_ttx(instfont, expected_ttx_path, tables) 124 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 125 126 127 def test_varlib_interpolate_layout_no_GSUB_ttf(self): 128 """The base master has no GSUB table. 129 130 The variable font will end up without a GSUB table. 131 """ 132 suffix = '.ttf' 133 ds_path = self.get_test_input('InterpolateLayout2.designspace') 134 ufo_dir = self.get_test_input('master_ufo') 135 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 136 137 self.temp_dir() 138 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 139 for path in ttx_paths: 140 self.compile_font(path, suffix, self.tempdir) 141 142 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 143 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 144 145 tables = ['GSUB'] 146 expected_ttx_path = self.get_test_output('InterpolateLayout2.ttx') 147 self.expect_ttx(instfont, expected_ttx_path, tables) 148 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 149 150 151 def test_varlib_interpolate_layout_GSUB_only_no_axes_ttf(self): 152 """Only GSUB, and only in the base master. 153 Designspace file has no <axes> element. 154 155 The variable font will inherit the GSUB table from the 156 base master. 157 """ 158 ds_path = self.get_test_input('InterpolateLayout3.designspace') 159 with self.assertRaisesRegex(DesignSpaceDocumentError, "No axes defined"): 160 instfont = interpolate_layout(ds_path, {'weight': 500}) 161 162 def test_varlib_interpolate_layout_GPOS_only_size_feat_same_val_ttf(self): 163 """Only GPOS; 'size' feature; same values in all masters. 164 """ 165 suffix = '.ttf' 166 ds_path = self.get_test_input('InterpolateLayout.designspace') 167 ufo_dir = self.get_test_input('master_ufo') 168 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 169 170 fea_str = """ 171 feature size { 172 parameters 10.0 0; 173 } size; 174 """ 175 features = [fea_str] * 2 176 177 self.temp_dir() 178 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 179 for i, path in enumerate(ttx_paths): 180 self.compile_font(path, suffix, self.tempdir, features[i]) 181 182 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 183 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 184 185 tables = ['GPOS'] 186 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_size_feat_same.ttx') 187 self.expect_ttx(instfont, expected_ttx_path, tables) 188 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 189 190 191 def test_varlib_interpolate_layout_GPOS_only_LookupType_1_same_val_ttf(self): 192 """Only GPOS; LookupType 1; same values in all masters. 193 """ 194 suffix = '.ttf' 195 ds_path = self.get_test_input('InterpolateLayout.designspace') 196 ufo_dir = self.get_test_input('master_ufo') 197 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 198 199 fea_str = """ 200 feature xxxx { 201 pos A <-80 0 -160 0>; 202 } xxxx; 203 """ 204 features = [fea_str] * 2 205 206 self.temp_dir() 207 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 208 for i, path in enumerate(ttx_paths): 209 self.compile_font(path, suffix, self.tempdir, features[i]) 210 211 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 212 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 213 214 tables = ['GPOS'] 215 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_1_same.ttx') 216 self.expect_ttx(instfont, expected_ttx_path, tables) 217 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 218 219 220 def test_varlib_interpolate_layout_GPOS_only_LookupType_1_diff_val_ttf(self): 221 """Only GPOS; LookupType 1; different values in each master. 222 """ 223 suffix = '.ttf' 224 ds_path = self.get_test_input('InterpolateLayout.designspace') 225 ufo_dir = self.get_test_input('master_ufo') 226 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 227 228 fea_str_0 = """ 229 feature xxxx { 230 pos A <-80 0 -160 0>; 231 } xxxx; 232 """ 233 fea_str_1 = """ 234 feature xxxx { 235 pos A <-97 0 -195 0>; 236 } xxxx; 237 """ 238 features = [fea_str_0, fea_str_1] 239 240 self.temp_dir() 241 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 242 for i, path in enumerate(ttx_paths): 243 self.compile_font(path, suffix, self.tempdir, features[i]) 244 245 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 246 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 247 248 tables = ['GPOS'] 249 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_1_diff.ttx') 250 self.expect_ttx(instfont, expected_ttx_path, tables) 251 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 252 253 254 def test_varlib_interpolate_layout_GPOS_only_LookupType_1_diff2_val_ttf(self): 255 """Only GPOS; LookupType 1; different values and items in each master. 256 """ 257 suffix = '.ttf' 258 ds_path = self.get_test_input('InterpolateLayout.designspace') 259 ufo_dir = self.get_test_input('master_ufo') 260 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 261 262 fea_str_0 = """ 263 feature xxxx { 264 pos A <-80 0 -160 0>; 265 pos a <-55 0 -105 0>; 266 } xxxx; 267 """ 268 fea_str_1 = """ 269 feature xxxx { 270 pos A <-97 0 -195 0>; 271 } xxxx; 272 """ 273 features = [fea_str_0, fea_str_1] 274 275 self.temp_dir() 276 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 277 for i, path in enumerate(ttx_paths): 278 self.compile_font(path, suffix, self.tempdir, features[i]) 279 280 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 281 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 282 283 tables = ['GPOS'] 284 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_1_diff2.ttx') 285 self.expect_ttx(instfont, expected_ttx_path, tables) 286 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 287 288 289 def test_varlib_interpolate_layout_GPOS_only_LookupType_2_spec_pairs_same_val_ttf(self): 290 """Only GPOS; LookupType 2 specific pairs; same values in all masters. 291 """ 292 suffix = '.ttf' 293 ds_path = self.get_test_input('InterpolateLayout.designspace') 294 ufo_dir = self.get_test_input('master_ufo') 295 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 296 297 fea_str = """ 298 feature xxxx { 299 pos A a -53; 300 } xxxx; 301 """ 302 features = [fea_str] * 2 303 304 self.temp_dir() 305 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 306 for i, path in enumerate(ttx_paths): 307 self.compile_font(path, suffix, self.tempdir, features[i]) 308 309 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 310 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 311 312 tables = ['GPOS'] 313 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_spec_same.ttx') 314 self.expect_ttx(instfont, expected_ttx_path, tables) 315 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 316 317 318 def test_varlib_interpolate_layout_GPOS_only_LookupType_2_spec_pairs_diff_val_ttf(self): 319 """Only GPOS; LookupType 2 specific pairs; different values in each master. 320 """ 321 suffix = '.ttf' 322 ds_path = self.get_test_input('InterpolateLayout.designspace') 323 ufo_dir = self.get_test_input('master_ufo') 324 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 325 326 fea_str_0 = """ 327 feature xxxx { 328 pos A a -53; 329 } xxxx; 330 """ 331 fea_str_1 = """ 332 feature xxxx { 333 pos A a -27; 334 } xxxx; 335 """ 336 features = [fea_str_0, fea_str_1] 337 338 self.temp_dir() 339 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 340 for i, path in enumerate(ttx_paths): 341 self.compile_font(path, suffix, self.tempdir, features[i]) 342 343 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 344 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 345 346 tables = ['GPOS'] 347 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_spec_diff.ttx') 348 self.expect_ttx(instfont, expected_ttx_path, tables) 349 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 350 351 352 def test_varlib_interpolate_layout_GPOS_only_LookupType_2_spec_pairs_diff2_val_ttf(self): 353 """Only GPOS; LookupType 2 specific pairs; different values and items in each master. 354 """ 355 suffix = '.ttf' 356 ds_path = self.get_test_input('InterpolateLayout.designspace') 357 ufo_dir = self.get_test_input('master_ufo') 358 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 359 360 fea_str_0 = """ 361 feature xxxx { 362 pos A a -53; 363 } xxxx; 364 """ 365 fea_str_1 = """ 366 feature xxxx { 367 pos A a -27; 368 pos a a 19; 369 } xxxx; 370 """ 371 features = [fea_str_0, fea_str_1] 372 373 self.temp_dir() 374 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 375 for i, path in enumerate(ttx_paths): 376 self.compile_font(path, suffix, self.tempdir, features[i]) 377 378 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 379 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 380 381 tables = ['GPOS'] 382 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_spec_diff2.ttx') 383 self.expect_ttx(instfont, expected_ttx_path, tables) 384 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 385 386 387 def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_same_val_ttf(self): 388 """Only GPOS; LookupType 2 class pairs; same values in all masters. 389 """ 390 suffix = '.ttf' 391 ds_path = self.get_test_input('InterpolateLayout.designspace') 392 ufo_dir = self.get_test_input('master_ufo') 393 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 394 395 fea_str = """ 396 feature xxxx { 397 pos [A] [a] -53; 398 } xxxx; 399 """ 400 features = [fea_str] * 2 401 402 self.temp_dir() 403 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 404 for i, path in enumerate(ttx_paths): 405 self.compile_font(path, suffix, self.tempdir, features[i]) 406 407 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 408 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 409 410 tables = ['GPOS'] 411 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_same.ttx') 412 self.expect_ttx(instfont, expected_ttx_path, tables) 413 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 414 415 416 def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_diff_val_ttf(self): 417 """Only GPOS; LookupType 2 class pairs; different values in each master. 418 """ 419 suffix = '.ttf' 420 ds_path = self.get_test_input('InterpolateLayout.designspace') 421 ufo_dir = self.get_test_input('master_ufo') 422 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 423 424 fea_str_0 = """ 425 feature xxxx { 426 pos [A] [a] -53; 427 } xxxx; 428 """ 429 fea_str_1 = """ 430 feature xxxx { 431 pos [A] [a] -27; 432 } xxxx; 433 """ 434 features = [fea_str_0, fea_str_1] 435 436 self.temp_dir() 437 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 438 for i, path in enumerate(ttx_paths): 439 self.compile_font(path, suffix, self.tempdir, features[i]) 440 441 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 442 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 443 444 tables = ['GPOS'] 445 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_diff.ttx') 446 self.expect_ttx(instfont, expected_ttx_path, tables) 447 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 448 449 450 def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_diff2_val_ttf(self): 451 """Only GPOS; LookupType 2 class pairs; different values and items in each master. 452 """ 453 suffix = '.ttf' 454 ds_path = self.get_test_input('InterpolateLayout.designspace') 455 ufo_dir = self.get_test_input('master_ufo') 456 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 457 458 fea_str_0 = """ 459 feature xxxx { 460 pos [A] [a] -53; 461 } xxxx; 462 """ 463 fea_str_1 = """ 464 feature xxxx { 465 pos [A] [a] -27; 466 pos [a] [a] 19; 467 } xxxx; 468 """ 469 features = [fea_str_0, fea_str_1] 470 471 self.temp_dir() 472 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 473 for i, path in enumerate(ttx_paths): 474 self.compile_font(path, suffix, self.tempdir, features[i]) 475 476 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 477 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 478 479 tables = ['GPOS'] 480 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_diff2.ttx') 481 self.expect_ttx(instfont, expected_ttx_path, tables) 482 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 483 484 485 def test_varlib_interpolate_layout_GPOS_only_LookupType_3_same_val_ttf(self): 486 """Only GPOS; LookupType 3; same values in all masters. 487 """ 488 suffix = '.ttf' 489 ds_path = self.get_test_input('InterpolateLayout.designspace') 490 ufo_dir = self.get_test_input('master_ufo') 491 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 492 493 fea_str = """ 494 feature xxxx { 495 pos cursive a <anchor 60 15> <anchor 405 310>; 496 } xxxx; 497 """ 498 features = [fea_str] * 2 499 500 self.temp_dir() 501 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 502 for i, path in enumerate(ttx_paths): 503 self.compile_font(path, suffix, self.tempdir, features[i]) 504 505 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 506 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 507 508 tables = ['GPOS'] 509 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_3_same.ttx') 510 self.expect_ttx(instfont, expected_ttx_path, tables) 511 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 512 513 514 def test_varlib_interpolate_layout_GPOS_only_LookupType_3_diff_val_ttf(self): 515 """Only GPOS; LookupType 3; different values in each master. 516 """ 517 suffix = '.ttf' 518 ds_path = self.get_test_input('InterpolateLayout.designspace') 519 ufo_dir = self.get_test_input('master_ufo') 520 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 521 522 fea_str_0 = """ 523 feature xxxx { 524 pos cursive a <anchor 60 15> <anchor 405 310>; 525 } xxxx; 526 """ 527 fea_str_1 = """ 528 feature xxxx { 529 pos cursive a <anchor 38 42> <anchor 483 279>; 530 } xxxx; 531 """ 532 features = [fea_str_0, fea_str_1] 533 534 self.temp_dir() 535 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 536 for i, path in enumerate(ttx_paths): 537 self.compile_font(path, suffix, self.tempdir, features[i]) 538 539 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 540 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 541 542 tables = ['GPOS'] 543 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_3_diff.ttx') 544 self.expect_ttx(instfont, expected_ttx_path, tables) 545 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 546 547 548 def test_varlib_interpolate_layout_GPOS_only_LookupType_4_same_val_ttf(self): 549 """Only GPOS; LookupType 4; same values in all masters. 550 """ 551 suffix = '.ttf' 552 ds_path = self.get_test_input('InterpolateLayout.designspace') 553 ufo_dir = self.get_test_input('master_ufo') 554 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 555 556 fea_str = """ 557 markClass uni0303 <anchor 0 500> @MARKS_ABOVE; 558 feature xxxx { 559 pos base a <anchor 260 500> mark @MARKS_ABOVE; 560 } xxxx; 561 """ 562 features = [fea_str] * 2 563 564 self.temp_dir() 565 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 566 for i, path in enumerate(ttx_paths): 567 self.compile_font(path, suffix, self.tempdir, features[i]) 568 569 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 570 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 571 572 tables = ['GPOS'] 573 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_4_same.ttx') 574 self.expect_ttx(instfont, expected_ttx_path, tables) 575 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 576 577 578 def test_varlib_interpolate_layout_GPOS_only_LookupType_4_diff_val_ttf(self): 579 """Only GPOS; LookupType 4; different values in each master. 580 """ 581 suffix = '.ttf' 582 ds_path = self.get_test_input('InterpolateLayout.designspace') 583 ufo_dir = self.get_test_input('master_ufo') 584 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 585 586 fea_str_0 = """ 587 markClass uni0303 <anchor 0 500> @MARKS_ABOVE; 588 feature xxxx { 589 pos base a <anchor 260 500> mark @MARKS_ABOVE; 590 } xxxx; 591 """ 592 fea_str_1 = """ 593 markClass uni0303 <anchor 0 520> @MARKS_ABOVE; 594 feature xxxx { 595 pos base a <anchor 285 520> mark @MARKS_ABOVE; 596 } xxxx; 597 """ 598 features = [fea_str_0, fea_str_1] 599 600 self.temp_dir() 601 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 602 for i, path in enumerate(ttx_paths): 603 self.compile_font(path, suffix, self.tempdir, features[i]) 604 605 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 606 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 607 608 tables = ['GPOS'] 609 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_4_diff.ttx') 610 self.expect_ttx(instfont, expected_ttx_path, tables) 611 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 612 613 614 def test_varlib_interpolate_layout_GPOS_only_LookupType_5_same_val_ttf(self): 615 """Only GPOS; LookupType 5; same values in all masters. 616 """ 617 suffix = '.ttf' 618 ds_path = self.get_test_input('InterpolateLayout.designspace') 619 ufo_dir = self.get_test_input('master_ufo') 620 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 621 622 fea_str = """ 623 markClass uni0330 <anchor 0 -50> @MARKS_BELOW; 624 feature xxxx { 625 pos ligature f_t <anchor 115 -50> mark @MARKS_BELOW 626 ligComponent <anchor 430 -50> mark @MARKS_BELOW; 627 } xxxx; 628 """ 629 features = [fea_str] * 2 630 631 self.temp_dir() 632 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 633 for i, path in enumerate(ttx_paths): 634 self.compile_font(path, suffix, self.tempdir, features[i]) 635 636 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 637 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 638 639 tables = ['GPOS'] 640 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_5_same.ttx') 641 self.expect_ttx(instfont, expected_ttx_path, tables) 642 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 643 644 645 def test_varlib_interpolate_layout_GPOS_only_LookupType_5_diff_val_ttf(self): 646 """Only GPOS; LookupType 5; different values in each master. 647 """ 648 suffix = '.ttf' 649 ds_path = self.get_test_input('InterpolateLayout.designspace') 650 ufo_dir = self.get_test_input('master_ufo') 651 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 652 653 fea_str_0 = """ 654 markClass uni0330 <anchor 0 -50> @MARKS_BELOW; 655 feature xxxx { 656 pos ligature f_t <anchor 115 -50> mark @MARKS_BELOW 657 ligComponent <anchor 430 -50> mark @MARKS_BELOW; 658 } xxxx; 659 """ 660 fea_str_1 = """ 661 markClass uni0330 <anchor 0 -20> @MARKS_BELOW; 662 feature xxxx { 663 pos ligature f_t <anchor 173 -20> mark @MARKS_BELOW 664 ligComponent <anchor 577 -20> mark @MARKS_BELOW; 665 } xxxx; 666 """ 667 features = [fea_str_0, fea_str_1] 668 669 self.temp_dir() 670 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 671 for i, path in enumerate(ttx_paths): 672 self.compile_font(path, suffix, self.tempdir, features[i]) 673 674 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 675 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 676 677 tables = ['GPOS'] 678 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_5_diff.ttx') 679 self.expect_ttx(instfont, expected_ttx_path, tables) 680 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 681 682 683 def test_varlib_interpolate_layout_GPOS_only_LookupType_6_same_val_ttf(self): 684 """Only GPOS; LookupType 6; same values in all masters. 685 """ 686 suffix = '.ttf' 687 ds_path = self.get_test_input('InterpolateLayout.designspace') 688 ufo_dir = self.get_test_input('master_ufo') 689 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 690 691 fea_str = """ 692 markClass uni0303 <anchor 0 500> @MARKS_ABOVE; 693 feature xxxx { 694 pos mark uni0308 <anchor 0 675> mark @MARKS_ABOVE; 695 } xxxx; 696 """ 697 features = [fea_str] * 2 698 699 self.temp_dir() 700 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 701 for i, path in enumerate(ttx_paths): 702 self.compile_font(path, suffix, self.tempdir, features[i]) 703 704 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 705 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 706 707 tables = ['GPOS'] 708 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_6_same.ttx') 709 self.expect_ttx(instfont, expected_ttx_path, tables) 710 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 711 712 713 def test_varlib_interpolate_layout_GPOS_only_LookupType_6_diff_val_ttf(self): 714 """Only GPOS; LookupType 6; different values in each master. 715 """ 716 suffix = '.ttf' 717 ds_path = self.get_test_input('InterpolateLayout.designspace') 718 ufo_dir = self.get_test_input('master_ufo') 719 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 720 721 fea_str_0 = """ 722 markClass uni0303 <anchor 0 500> @MARKS_ABOVE; 723 feature xxxx { 724 pos mark uni0308 <anchor 0 675> mark @MARKS_ABOVE; 725 } xxxx; 726 """ 727 fea_str_1 = """ 728 markClass uni0303 <anchor 0 520> @MARKS_ABOVE; 729 feature xxxx { 730 pos mark uni0308 <anchor 0 730> mark @MARKS_ABOVE; 731 } xxxx; 732 """ 733 features = [fea_str_0, fea_str_1] 734 735 self.temp_dir() 736 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 737 for i, path in enumerate(ttx_paths): 738 self.compile_font(path, suffix, self.tempdir, features[i]) 739 740 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 741 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 742 743 tables = ['GPOS'] 744 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_6_diff.ttx') 745 self.expect_ttx(instfont, expected_ttx_path, tables) 746 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 747 748 749 def test_varlib_interpolate_layout_GPOS_only_LookupType_7_same_val_ttf(self): 750 """Only GPOS; LookupType 7; same values in all masters. 751 """ 752 suffix = '.ttf' 753 ds_path = self.get_test_input('InterpolateLayout.designspace') 754 ufo_dir = self.get_test_input('master_ufo') 755 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 756 757 fea_str = """ 758 markClass uni0303 <anchor 0 500> @MARKS_ABOVE; 759 lookup CNTXT_PAIR_POS { 760 pos A a -23; 761 } CNTXT_PAIR_POS; 762 763 lookup CNTXT_MARK_TO_BASE { 764 pos base a <anchor 260 500> mark @MARKS_ABOVE; 765 } CNTXT_MARK_TO_BASE; 766 767 feature xxxx { 768 pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE; 769 } xxxx; 770 """ 771 features = [fea_str] * 2 772 773 self.temp_dir() 774 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 775 for i, path in enumerate(ttx_paths): 776 self.compile_font(path, suffix, self.tempdir, features[i]) 777 778 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 779 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 780 781 tables = ['GPOS'] 782 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_7_same.ttx') 783 self.expect_ttx(instfont, expected_ttx_path, tables) 784 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 785 786 787 def test_varlib_interpolate_layout_GPOS_only_LookupType_7_diff_val_ttf(self): 788 """Only GPOS; LookupType 7; different values in each master. 789 """ 790 suffix = '.ttf' 791 ds_path = self.get_test_input('InterpolateLayout.designspace') 792 ufo_dir = self.get_test_input('master_ufo') 793 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 794 795 fea_str_0 = """ 796 markClass uni0303 <anchor 0 500> @MARKS_ABOVE; 797 lookup CNTXT_PAIR_POS { 798 pos A a -23; 799 } CNTXT_PAIR_POS; 800 801 lookup CNTXT_MARK_TO_BASE { 802 pos base a <anchor 260 500> mark @MARKS_ABOVE; 803 } CNTXT_MARK_TO_BASE; 804 805 feature xxxx { 806 pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE; 807 } xxxx; 808 """ 809 fea_str_1 = """ 810 markClass uni0303 <anchor 0 520> @MARKS_ABOVE; 811 lookup CNTXT_PAIR_POS { 812 pos A a 57; 813 } CNTXT_PAIR_POS; 814 815 lookup CNTXT_MARK_TO_BASE { 816 pos base a <anchor 285 520> mark @MARKS_ABOVE; 817 } CNTXT_MARK_TO_BASE; 818 819 feature xxxx { 820 pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE; 821 } xxxx; 822 """ 823 features = [fea_str_0, fea_str_1] 824 825 self.temp_dir() 826 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') 827 for i, path in enumerate(ttx_paths): 828 self.compile_font(path, suffix, self.tempdir, features[i]) 829 830 finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) 831 instfont = interpolate_layout(ds_path, {'weight': 500}, finder) 832 833 tables = ['GPOS'] 834 expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_7_diff.ttx') 835 self.expect_ttx(instfont, expected_ttx_path, tables) 836 self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix) 837 838 839 def test_varlib_interpolate_layout_main_ttf(self): 840 """Mostly for testing varLib.interpolate_layout.main() 841 """ 842 suffix = '.ttf' 843 ds_path = self.get_test_input('Build.designspace') 844 ufo_dir = self.get_test_input('master_ufo') 845 ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') 846 847 self.temp_dir() 848 ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable') 849 os.makedirs(ttf_dir) 850 ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-') 851 for path in ttx_paths: 852 self.compile_font(path, suffix, ttf_dir) 853 854 finder = lambda s: s.replace(ufo_dir, ttf_dir).replace('.ufo', suffix) 855 varfont, _, _ = build(ds_path, finder) 856 varfont_name = 'InterpolateLayoutMain' 857 varfont_path = os.path.join(self.tempdir, varfont_name + suffix) 858 varfont.save(varfont_path) 859 860 ds_copy = os.path.splitext(varfont_path)[0] + '.designspace' 861 shutil.copy2(ds_path, ds_copy) 862 args = [ds_copy, 'weight=500', 'contrast=50'] 863 interpolate_layout_main(args) 864 865 instfont_path = os.path.splitext(varfont_path)[0] + '-instance' + suffix 866 instfont = TTFont(instfont_path) 867 tables = [table_tag for table_tag in instfont.keys() if table_tag != 'head'] 868 expected_ttx_path = self.get_test_output(varfont_name + '.ttx') 869 self.expect_ttx(instfont, expected_ttx_path, tables) 870 871 872if __name__ == "__main__": 873 sys.exit(unittest.main()) 874