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