1from fontTools.misc import sstruct 2from fontTools.misc.loggingTools import CapturingLogHandler 3from fontTools.misc.testTools import FakeFont 4from fontTools.misc.textTools import bytesjoin, tostr 5from fontTools.misc.xmlWriter import XMLWriter 6from io import BytesIO 7import struct 8import unittest 9from fontTools.ttLib import TTFont, newTable 10from fontTools.ttLib.tables._n_a_m_e import ( 11 table__n_a_m_e, 12 NameRecord, 13 nameRecordFormat, 14 nameRecordSize, 15 makeName, 16 log, 17) 18 19 20def names(nameTable): 21 result = [ 22 (n.nameID, n.platformID, n.platEncID, n.langID, n.string) 23 for n in nameTable.names 24 ] 25 result.sort() 26 return result 27 28 29class NameTableTest(unittest.TestCase): 30 def test_getDebugName(self): 31 table = table__n_a_m_e() 32 table.names = [ 33 makeName("Bold", 258, 1, 0, 0), # Mac, MacRoman, English 34 makeName("Gras", 258, 1, 0, 1), # Mac, MacRoman, French 35 makeName("Fett", 258, 1, 0, 2), # Mac, MacRoman, German 36 makeName("Sem Fracções", 292, 1, 0, 8), # Mac, MacRoman, Portuguese 37 ] 38 self.assertEqual("Bold", table.getDebugName(258)) 39 self.assertEqual("Sem Fracções", table.getDebugName(292)) 40 self.assertEqual(None, table.getDebugName(999)) 41 42 def test_setName(self): 43 table = table__n_a_m_e() 44 table.setName("Regular", 2, 1, 0, 0) 45 table.setName("Version 1.000", 5, 3, 1, 0x409) 46 table.setName("寬鬆", 276, 1, 2, 0x13) 47 self.assertEqual("Regular", table.getName(2, 1, 0, 0).toUnicode()) 48 self.assertEqual("Version 1.000", table.getName(5, 3, 1, 0x409).toUnicode()) 49 self.assertEqual("寬鬆", table.getName(276, 1, 2, 0x13).toUnicode()) 50 self.assertTrue(len(table.names) == 3) 51 table.setName("緊縮", 276, 1, 2, 0x13) 52 self.assertEqual("緊縮", table.getName(276, 1, 2, 0x13).toUnicode()) 53 self.assertTrue(len(table.names) == 3) 54 # passing bytes issues a warning 55 with CapturingLogHandler(log, "WARNING") as captor: 56 table.setName(b"abc", 0, 1, 0, 0) 57 self.assertTrue( 58 len([r for r in captor.records if "string is bytes" in r.msg]) == 1 59 ) 60 # anything other than unicode or bytes raises an error 61 with self.assertRaises(TypeError): 62 table.setName(1.000, 5, 1, 0, 0) 63 64 def test_names_sort_bytes_str(self): 65 # Corner case: If a user appends a name record directly to `names`, the 66 # `__lt__` method on NameRecord may run into duplicate name records where 67 # one `string` is a str and the other one bytes, leading to an exception. 68 table = table__n_a_m_e() 69 table.names = [ 70 makeName("Test", 25, 3, 1, 0x409), 71 makeName("Test".encode("utf-16be"), 25, 3, 1, 0x409), 72 ] 73 table.compile(None) 74 75 def test_names_sort_attributes(self): 76 table = table__n_a_m_e() 77 # Create an actual invalid NameRecord object 78 broken = makeName("Test", 25, 3, 1, 0x409) 79 delattr(broken, "platformID") 80 table.names = [ 81 makeName("Test", 25, 3, 1, 0x409), 82 broken, 83 ] 84 # Sorting these two is impossible, expect an error to be raised 85 with self.assertRaises(TypeError): 86 table.names.sort() 87 88 def test_names_sort_encoding(self): 89 """ 90 Confirm that encoding errors in name table strings do not prevent at 91 least sorting by other IDs 92 """ 93 table = table__n_a_m_e() 94 table.names = [ 95 makeName("Mac Unicode 寬 encodes ok", 25, 3, 0, 0x409), 96 makeName("Win Latin 寬 fails to encode", 25, 1, 0, 0), 97 ] 98 table.names.sort() 99 # Encoding errors or not, sort based on other IDs nonetheless 100 self.assertEqual(table.names[0].platformID, 1) 101 self.assertEqual(table.names[1].platformID, 3) 102 103 def test_addName(self): 104 table = table__n_a_m_e() 105 nameIDs = [] 106 for string in ("Width", "Weight", "Custom"): 107 nameIDs.append(table.addName(string)) 108 109 self.assertEqual(nameIDs[0], 256) 110 self.assertEqual(nameIDs[1], 257) 111 self.assertEqual(nameIDs[2], 258) 112 self.assertEqual(len(table.names), 6) 113 self.assertEqual(table.names[0].string, "Width") 114 self.assertEqual(table.names[1].string, "Width") 115 self.assertEqual(table.names[2].string, "Weight") 116 self.assertEqual(table.names[3].string, "Weight") 117 self.assertEqual(table.names[4].string, "Custom") 118 self.assertEqual(table.names[5].string, "Custom") 119 120 with self.assertRaises(ValueError): 121 table.addName("Invalid nameID", minNameID=32767) 122 with self.assertRaises(TypeError): 123 table.addName(b"abc") # must be unicode string 124 125 def test_removeNames(self): 126 table = table__n_a_m_e() 127 table.setName("Regular", 2, 1, 0, 0) 128 table.setName("Regular", 2, 3, 1, 0x409) 129 table.removeNames(nameID=2) 130 self.assertEqual(table.names, []) 131 132 table = table__n_a_m_e() 133 table.setName("FamilyName", 1, 1, 0, 0) 134 table.setName("Regular", 2, 1, 0, 0) 135 table.setName("FamilyName", 1, 3, 1, 0x409) 136 table.setName("Regular", 2, 3, 1, 0x409) 137 table.removeNames(platformID=1) 138 self.assertEqual(len(table.names), 2) 139 self.assertIsNone(table.getName(1, 1, 0, 0)) 140 self.assertIsNone(table.getName(2, 1, 0, 0)) 141 rec1 = table.getName(1, 3, 1, 0x409) 142 self.assertEqual(str(rec1), "FamilyName") 143 rec2 = table.getName(2, 3, 1, 0x409) 144 self.assertEqual(str(rec2), "Regular") 145 146 table = table__n_a_m_e() 147 table.setName("FamilyName", 1, 1, 0, 0) 148 table.setName("Regular", 2, 1, 0, 0) 149 table.removeNames(nameID=1) 150 self.assertEqual(len(table.names), 1) 151 self.assertIsNone(table.getName(1, 1, 0, 0)) 152 rec = table.getName(2, 1, 0, 0) 153 self.assertEqual(str(rec), "Regular") 154 155 table = table__n_a_m_e() 156 table.setName("FamilyName", 1, 1, 0, 0) 157 table.setName("Regular", 2, 1, 0, 0) 158 table.removeNames(2, 1, 0, 0) 159 self.assertEqual(len(table.names), 1) 160 self.assertIsNone(table.getName(2, 1, 0, 0)) 161 rec = table.getName(1, 1, 0, 0) 162 self.assertEqual(str(rec), "FamilyName") 163 164 table = table__n_a_m_e() 165 table.setName("FamilyName", 1, 1, 0, 0) 166 table.setName("Regular", 2, 1, 0, 0) 167 table.removeNames() 168 self.assertEqual(len(table.names), 2) 169 rec1 = table.getName(1, 1, 0, 0) 170 self.assertEqual(str(rec1), "FamilyName") 171 rec2 = table.getName(2, 1, 0, 0) 172 self.assertEqual(str(rec2), "Regular") 173 174 @staticmethod 175 def _get_test_names(): 176 names = { 177 "en": "Width", 178 "de-CH": "Breite", 179 "gsw-LI": "Bräiti", 180 } 181 namesSubSet = names.copy() 182 del namesSubSet["gsw-LI"] 183 namesSuperSet = names.copy() 184 namesSuperSet["nl"] = "Breedte" 185 return names, namesSubSet, namesSuperSet 186 187 def test_findMultilingualName(self): 188 table = table__n_a_m_e() 189 names, namesSubSet, namesSuperSet = self._get_test_names() 190 nameID = table.addMultilingualName(names) 191 assert nameID is not None 192 self.assertEqual(nameID, table.findMultilingualName(names)) 193 self.assertEqual(nameID, table.findMultilingualName(namesSubSet)) 194 self.assertEqual(None, table.findMultilingualName(namesSuperSet)) 195 196 def test_findMultilingualName_compiled(self): 197 table = table__n_a_m_e() 198 names, namesSubSet, namesSuperSet = self._get_test_names() 199 nameID = table.addMultilingualName(names) 200 assert nameID is not None 201 # After compile/decompile, name.string is a bytes sequence, which 202 # findMultilingualName() should also handle 203 data = table.compile(None) 204 table = table__n_a_m_e() 205 table.decompile(data, None) 206 self.assertEqual(nameID, table.findMultilingualName(names)) 207 self.assertEqual(nameID, table.findMultilingualName(namesSubSet)) 208 self.assertEqual(None, table.findMultilingualName(namesSuperSet)) 209 210 def test_addMultilingualNameReuse(self): 211 table = table__n_a_m_e() 212 names, namesSubSet, namesSuperSet = self._get_test_names() 213 nameID = table.addMultilingualName(names) 214 assert nameID is not None 215 self.assertEqual(nameID, table.addMultilingualName(names)) 216 self.assertEqual(nameID, table.addMultilingualName(namesSubSet)) 217 self.assertNotEqual(None, table.addMultilingualName(namesSuperSet)) 218 219 def test_findMultilingualNameNoMac(self): 220 table = table__n_a_m_e() 221 names, namesSubSet, namesSuperSet = self._get_test_names() 222 nameID = table.addMultilingualName(names, mac=False) 223 assert nameID is not None 224 self.assertEqual(nameID, table.findMultilingualName(names, mac=False)) 225 self.assertEqual(None, table.findMultilingualName(names)) 226 self.assertEqual(nameID, table.findMultilingualName(namesSubSet, mac=False)) 227 self.assertEqual(None, table.findMultilingualName(namesSubSet)) 228 self.assertEqual(None, table.findMultilingualName(namesSuperSet)) 229 230 def test_addMultilingualName(self): 231 # Microsoft Windows has language codes for “English” (en) 232 # and for “Standard German as used in Switzerland” (de-CH). 233 # In this case, we expect that the implementation just 234 # encodes the name for the Windows platform; Apple platforms 235 # have been able to decode Windows names since the early days 236 # of OSX (~2001). However, Windows has no language code for 237 # “Swiss German as used in Liechtenstein” (gsw-LI), so we 238 # expect that the implementation populates the 'ltag' table 239 # to represent that particular, rather exotic BCP47 code. 240 font = FakeFont(glyphs=[".notdef", "A"]) 241 nameTable = font.tables["name"] = newTable("name") 242 with CapturingLogHandler(log, "WARNING") as captor: 243 widthID = nameTable.addMultilingualName( 244 { 245 "en": "Width", 246 "de-CH": "Breite", 247 "gsw-LI": "Bräiti", 248 }, 249 ttFont=font, 250 mac=False, 251 ) 252 self.assertEqual(widthID, 256) 253 xHeightID = nameTable.addMultilingualName( 254 {"en": "X-Height", "gsw-LI": "X-Hööchi"}, ttFont=font, mac=False 255 ) 256 self.assertEqual(xHeightID, 257) 257 captor.assertRegex("cannot add Windows name in language gsw-LI") 258 self.assertEqual( 259 names(nameTable), 260 [ 261 (256, 0, 4, 0, "Bräiti"), 262 (256, 3, 1, 0x0409, "Width"), 263 (256, 3, 1, 0x0807, "Breite"), 264 (257, 0, 4, 0, "X-Hööchi"), 265 (257, 3, 1, 0x0409, "X-Height"), 266 ], 267 ) 268 self.assertEqual(set(font.tables.keys()), {"ltag", "name"}) 269 self.assertEqual(font["ltag"].tags, ["gsw-LI"]) 270 271 def test_addMultilingualName_legacyMacEncoding(self): 272 # Windows has no language code for Latin; MacOS has a code; 273 # and we actually can convert the name to the legacy MacRoman 274 # encoding. In this case, we expect that the name gets encoded 275 # as Macintosh name (platformID 1) with the corresponding Mac 276 # language code (133); the 'ltag' table should not be used. 277 font = FakeFont(glyphs=[".notdef", "A"]) 278 nameTable = font.tables["name"] = newTable("name") 279 with CapturingLogHandler(log, "WARNING") as captor: 280 nameTable.addMultilingualName({"la": "SPQR"}, ttFont=font) 281 captor.assertRegex("cannot add Windows name in language la") 282 self.assertEqual(names(nameTable), [(256, 1, 0, 131, "SPQR")]) 283 self.assertNotIn("ltag", font.tables.keys()) 284 285 def test_addMultilingualName_legacyMacEncodingButUnencodableName(self): 286 # Windows has no language code for Latin; MacOS has a code; 287 # but we cannot encode the name into this encoding because 288 # it contains characters that are not representable. 289 # In this case, we expect that the name gets encoded as 290 # Unicode name (platformID 0) with the language tag being 291 # added to the 'ltag' table. 292 font = FakeFont(glyphs=[".notdef", "A"]) 293 nameTable = font.tables["name"] = newTable("name") 294 with CapturingLogHandler(log, "WARNING") as captor: 295 nameTable.addMultilingualName({"la": "ⱾƤℚⱤ"}, ttFont=font) 296 captor.assertRegex("cannot add Windows name in language la") 297 self.assertEqual(names(nameTable), [(256, 0, 4, 0, "ⱾƤℚⱤ")]) 298 self.assertIn("ltag", font.tables) 299 self.assertEqual(font["ltag"].tags, ["la"]) 300 301 def test_addMultilingualName_legacyMacEncodingButNoCodec(self): 302 # Windows has no language code for “Azeri written in the 303 # Arabic script” (az-Arab); MacOS would have a code (50); 304 # but we cannot encode the name into the legacy encoding 305 # because we have no codec for MacArabic in fonttools. 306 # In this case, we expect that the name gets encoded as 307 # Unicode name (platformID 0) with the language tag being 308 # added to the 'ltag' table. 309 font = FakeFont(glyphs=[".notdef", "A"]) 310 nameTable = font.tables["name"] = newTable("name") 311 with CapturingLogHandler(log, "WARNING") as captor: 312 nameTable.addMultilingualName({"az-Arab": "آذربايجان ديلی"}, ttFont=font) 313 captor.assertRegex("cannot add Windows name in language az-Arab") 314 self.assertEqual(names(nameTable), [(256, 0, 4, 0, "آذربايجان ديلی")]) 315 self.assertIn("ltag", font.tables) 316 self.assertEqual(font["ltag"].tags, ["az-Arab"]) 317 318 def test_addMultilingualName_noTTFont(self): 319 # If the ttFont argument is not passed, the implementation 320 # should add whatever names it can, but it should not crash 321 # just because it cannot build an ltag table. 322 nameTable = newTable("name") 323 with CapturingLogHandler(log, "WARNING") as captor: 324 nameTable.addMultilingualName({"en": "A", "la": "ⱾƤℚⱤ"}) 325 captor.assertRegex("cannot store language la into 'ltag' table") 326 327 def test_addMultilingualName_TTFont(self): 328 # if ttFont argument is passed, it should not WARN about not being able 329 # to create ltag table. 330 font = FakeFont(glyphs=[".notdef", "A"]) 331 nameTable = newTable("name") 332 with CapturingLogHandler(log, "WARNING") as captor: 333 nameTable.addMultilingualName({"en": "A", "ar": "ع"}, ttFont=font) 334 self.assertFalse(captor.records) 335 336 def test_addMultilingualName_minNameID(self): 337 table = table__n_a_m_e() 338 names, namesSubSet, namesSuperSet = self._get_test_names() 339 nameID = table.addMultilingualName(names, nameID=2) 340 self.assertEqual(nameID, 2) 341 nameID = table.addMultilingualName(names) 342 self.assertEqual(nameID, 2) 343 nameID = table.addMultilingualName(names, minNameID=256) 344 self.assertGreaterEqual(nameID, 256) 345 self.assertEqual(nameID, table.findMultilingualName(names, minNameID=256)) 346 347 def test_addMultilingualName_name_inconsistencies(self): 348 # Check what happens, when there are 349 # inconsistencies in the name table 350 table = table__n_a_m_e() 351 table.setName("Weight", 270, 3, 1, 0x409) 352 names = { 353 "en": "Weight", 354 } 355 nameID = table.addMultilingualName(names, minNameID=256) 356 # Because there is an inconsistency in the names, 357 # addMultilingualName adds a new name ID 358 self.assertEqual(271, nameID) 359 360 def test_decompile_badOffset(self): 361 # https://github.com/fonttools/fonttools/issues/525 362 table = table__n_a_m_e() 363 badRecord = { 364 "platformID": 1, 365 "platEncID": 3, 366 "langID": 7, 367 "nameID": 1, 368 "length": 3, 369 "offset": 8765, # out of range 370 } 371 data = bytesjoin( 372 [ 373 struct.pack(tostr(">HHH"), 1, 1, 6 + nameRecordSize), 374 sstruct.pack(nameRecordFormat, badRecord), 375 ] 376 ) 377 table.decompile(data, ttFont=None) 378 self.assertEqual(table.names, []) 379 380 381class NameRecordTest(unittest.TestCase): 382 def test_toUnicode_utf16be(self): 383 name = makeName("Foo Bold", 111, 0, 2, 7) 384 self.assertEqual("utf_16_be", name.getEncoding()) 385 self.assertEqual("Foo Bold", name.toUnicode()) 386 387 def test_toUnicode_macroman(self): 388 name = makeName("Foo Italic", 222, 1, 0, 7) # MacRoman 389 self.assertEqual("mac_roman", name.getEncoding()) 390 self.assertEqual("Foo Italic", name.toUnicode()) 391 392 def test_toUnicode_macromanian(self): 393 name = makeName(b"Foo Italic\xfb", 222, 1, 0, 37) # Mac Romanian 394 self.assertEqual("mac_romanian", name.getEncoding()) 395 self.assertEqual("Foo Italic" + chr(0x02DA), name.toUnicode()) 396 397 def test_toUnicode_UnicodeDecodeError(self): 398 name = makeName(b"\1", 111, 0, 2, 7) 399 self.assertEqual("utf_16_be", name.getEncoding()) 400 self.assertRaises(UnicodeDecodeError, name.toUnicode) 401 402 def test_toUnicode_singleChar(self): 403 # https://github.com/fonttools/fonttools/issues/1997 404 name = makeName("A", 256, 3, 1, 0x409) 405 self.assertEqual(name.toUnicode(), "A") 406 407 def toXML(self, name): 408 writer = XMLWriter(BytesIO()) 409 name.toXML(writer, ttFont=None) 410 xml = writer.file.getvalue().decode("utf_8").strip() 411 return xml.split(writer.newlinestr.decode("utf_8"))[1:] 412 413 def test_toXML_utf16be(self): 414 name = makeName("Foo Bold", 111, 0, 2, 7) 415 self.assertEqual( 416 [ 417 '<namerecord nameID="111" platformID="0" platEncID="2" langID="0x7">', 418 " Foo Bold", 419 "</namerecord>", 420 ], 421 self.toXML(name), 422 ) 423 424 def test_toXML_utf16be_odd_length1(self): 425 name = makeName(b"\0F\0o\0o\0", 111, 0, 2, 7) 426 self.assertEqual( 427 [ 428 '<namerecord nameID="111" platformID="0" platEncID="2" langID="0x7">', 429 " Foo", 430 "</namerecord>", 431 ], 432 self.toXML(name), 433 ) 434 435 def test_toXML_utf16be_odd_length2(self): 436 name = makeName(b"\0Fooz", 111, 0, 2, 7) 437 self.assertEqual( 438 [ 439 '<namerecord nameID="111" platformID="0" platEncID="2" langID="0x7">', 440 " Fooz", 441 "</namerecord>", 442 ], 443 self.toXML(name), 444 ) 445 446 def test_toXML_utf16be_double_encoded(self): 447 name = makeName(b"\0\0\0F\0\0\0o", 111, 0, 2, 7) 448 self.assertEqual( 449 [ 450 '<namerecord nameID="111" platformID="0" platEncID="2" langID="0x7">', 451 " Fo", 452 "</namerecord>", 453 ], 454 self.toXML(name), 455 ) 456 457 def test_toXML_macroman(self): 458 name = makeName("Foo Italic", 222, 1, 0, 7) # MacRoman 459 self.assertEqual( 460 [ 461 '<namerecord nameID="222" platformID="1" platEncID="0" langID="0x7" unicode="True">', 462 " Foo Italic", 463 "</namerecord>", 464 ], 465 self.toXML(name), 466 ) 467 468 def test_toXML_macroman_actual_utf16be(self): 469 name = makeName("\0F\0o\0o", 222, 1, 0, 7) 470 self.assertEqual( 471 [ 472 '<namerecord nameID="222" platformID="1" platEncID="0" langID="0x7" unicode="True">', 473 " Foo", 474 "</namerecord>", 475 ], 476 self.toXML(name), 477 ) 478 479 def test_toXML_unknownPlatEncID_nonASCII(self): 480 name = makeName(b"B\x8arli", 333, 1, 9876, 7) # Unknown Mac encodingID 481 self.assertEqual( 482 [ 483 '<namerecord nameID="333" platformID="1" platEncID="9876" langID="0x7" unicode="False">', 484 " BŠrli", 485 "</namerecord>", 486 ], 487 self.toXML(name), 488 ) 489 490 def test_toXML_unknownPlatEncID_ASCII(self): 491 name = makeName(b"Barli", 333, 1, 9876, 7) # Unknown Mac encodingID 492 self.assertEqual( 493 [ 494 '<namerecord nameID="333" platformID="1" platEncID="9876" langID="0x7" unicode="True">', 495 " Barli", 496 "</namerecord>", 497 ], 498 self.toXML(name), 499 ) 500 501 def test_encoding_macroman_misc(self): 502 name = makeName("", 123, 1, 0, 17) # Mac Turkish 503 self.assertEqual(name.getEncoding(), "mac_turkish") 504 name.langID = 37 505 self.assertEqual(name.getEncoding(), "mac_romanian") 506 name.langID = 45 # Other 507 self.assertEqual(name.getEncoding(), "mac_roman") 508 509 def test_extended_mac_encodings(self): 510 name = makeName(b"\xfe", 123, 1, 1, 0) # Mac Japanese 511 self.assertEqual(name.toUnicode(), chr(0x2122)) 512 513 def test_extended_mac_encodings_errors(self): 514 s = "汉仪彩云体简" 515 name = makeName(s.encode("x_mac_simp_chinese_ttx"), 123, 1, 25, 0) 516 # first check we round-trip with 'strict' 517 self.assertEqual(name.toUnicode(errors="strict"), s) 518 519 # append an incomplete invalid sequence and check that we handle 520 # errors with the requested error handler 521 name.string += b"\xba" 522 self.assertEqual(name.toUnicode(errors="backslashreplace"), s + "\\xba") 523 self.assertEqual(name.toUnicode(errors="replace"), s + "�") 524 525 def test_extended_unknown(self): 526 name = makeName(b"\xfe", 123, 10, 11, 12) 527 self.assertEqual(name.getEncoding(), "ascii") 528 self.assertEqual(name.getEncoding(None), None) 529 self.assertEqual(name.getEncoding(default=None), None) 530 531 def test_get_family_name(self): 532 name = table__n_a_m_e() 533 name.names = [ 534 makeName("Copyright", 0, 1, 0, 0), 535 makeName("Family Name ID 1", 1, 1, 0, 0), 536 makeName("SubFamily Name ID 2", 2, 1, 0, 0), 537 makeName("Unique Name ID 3", 3, 1, 0, 0), 538 makeName("Full Name ID 4", 4, 1, 0, 0), 539 makeName("PS Name ID 6", 6, 1, 0, 0), 540 makeName("Version Name ID 5", 5, 1, 0, 0), 541 makeName("Trademark Name ID 7", 7, 1, 0, 0), 542 ] 543 544 result_value = name.getBestFamilyName() 545 self.assertEqual("Family Name ID 1", result_value) 546 547 expected_value = "Family Name ID 16" 548 name.setName(expected_value, 16, 1, 0, 0) 549 result_value = name.getBestFamilyName() 550 self.assertEqual(expected_value, result_value) 551 552 expected_value = "Family Name ID 21" 553 name.setName(expected_value, 21, 1, 0, 0) 554 result_value = name.getBestFamilyName() 555 self.assertEqual(expected_value, result_value) 556 557 def test_get_subfamily_name(self): 558 name = table__n_a_m_e() 559 name.names = [ 560 makeName("Copyright", 0, 1, 0, 0), 561 makeName("Family Name ID 1", 1, 1, 0, 0), 562 makeName("SubFamily Name ID 2", 2, 1, 0, 0), 563 makeName("Unique Name ID 3", 3, 1, 0, 0), 564 makeName("Full Name ID 4", 4, 1, 0, 0), 565 makeName("PS Name ID 6", 6, 1, 0, 0), 566 makeName("Version Name ID 5", 5, 1, 0, 0), 567 makeName("Trademark Name ID 7", 7, 1, 0, 0), 568 ] 569 570 result_value = name.getBestSubFamilyName() 571 self.assertEqual("SubFamily Name ID 2", result_value) 572 573 expected_value = "Family Name ID 17" 574 name.setName(expected_value, 17, 1, 0, 0) 575 result_value = name.getBestSubFamilyName() 576 self.assertEqual(expected_value, result_value) 577 578 expected_value = "Family Name ID 22" 579 name.setName(expected_value, 22, 1, 0, 0) 580 result_value = name.getBestSubFamilyName() 581 self.assertEqual(expected_value, result_value) 582 583 def test_get_nice_full_name(self): 584 name = table__n_a_m_e() 585 name.names = [ 586 makeName("NID 1", 1, 1, 0, 0), 587 makeName("NID 2", 2, 1, 0, 0), 588 makeName("NID 4", 4, 1, 0, 0), 589 makeName("NID 6", 6, 1, 0, 0), 590 ] 591 592 result_value = name.getBestFullName() 593 self.assertEqual("NID 1 NID 2", result_value) 594 595 expected_value = "NID 1 NID 2" 596 # expection is still NID 1 NID 2, 597 # because name ID 17 is missing 598 name.setName("NID 16", 16, 1, 0, 0) 599 result_value = name.getBestFullName() 600 self.assertEqual(expected_value, result_value) 601 602 name.setName("NID 17", 17, 1, 0, 0) 603 result_value = name.getBestFullName() 604 self.assertEqual("NID 16 NID 17", result_value) 605 606 expected_value = "NID 16 NID 17" 607 # expection is still NID 16 NID 17, 608 # because name ID 21 is missing 609 name.setName("NID 21", 21, 1, 0, 0) 610 result_value = name.getBestFullName() 611 self.assertEqual(expected_value, result_value) 612 613 name.setName("NID 22", 22, 1, 0, 0) 614 result_value = name.getBestFullName() 615 self.assertEqual("NID 21 NID 22", result_value) 616 617 for NID in [2, 16, 17, 21, 22]: 618 name.removeNames(NID) 619 620 result_value = name.getBestFullName() 621 self.assertEqual("NID 4", result_value) 622 623 name.setName("Regular", 2, 1, 0, 0) 624 result_value = name.getBestFullName() 625 self.assertEqual("NID 1", result_value) 626 627 628if __name__ == "__main__": 629 import sys 630 631 sys.exit(unittest.main()) 632