1from fontTools.ttLib import TTFont, newTable, getTableModule 2from fontTools.ttLib.tables.O_S_2f_2 import * 3import unittest 4 5 6class OS2TableTest(unittest.TestCase): 7 @staticmethod 8 def makeOS2_cmap(mapping): 9 font = TTFont() 10 font["OS/2"] = os2 = newTable("OS/2") 11 os2.version = 4 12 font["cmap"] = cmap = newTable("cmap") 13 st = getTableModule("cmap").CmapSubtable.newSubtable(4) 14 st.platformID, st.platEncID, st.language = 3, 1, 0 15 st.cmap = mapping 16 cmap.tables = [] 17 cmap.tables.append(st) 18 return font, os2, cmap 19 20 def test_getUnicodeRanges(self): 21 table = table_O_S_2f_2() 22 table.ulUnicodeRange1 = 0xFFFFFFFF 23 table.ulUnicodeRange2 = 0xFFFFFFFF 24 table.ulUnicodeRange3 = 0xFFFFFFFF 25 table.ulUnicodeRange4 = 0xFFFFFFFF 26 bits = table.getUnicodeRanges() 27 for i in range(127): 28 self.assertIn(i, bits) 29 30 def test_setUnicodeRanges(self): 31 table = table_O_S_2f_2() 32 table.ulUnicodeRange1 = 0 33 table.ulUnicodeRange2 = 0 34 table.ulUnicodeRange3 = 0 35 table.ulUnicodeRange4 = 0 36 bits = set(range(123)) 37 table.setUnicodeRanges(bits) 38 self.assertEqual(table.getUnicodeRanges(), bits) 39 with self.assertRaises(ValueError): 40 table.setUnicodeRanges([-1, 127, 255]) 41 42 def test_recalcUnicodeRanges(self): 43 font, os2, cmap = self.makeOS2_cmap( 44 {0x0041: "A", 0x03B1: "alpha", 0x0410: "Acyr"} 45 ) 46 os2.setUnicodeRanges({0, 1, 9}) 47 # 'pruneOnly' will clear any bits for which there's no intersection: 48 # bit 1 ('Latin 1 Supplement'), in this case. However, it won't set 49 # bit 7 ('Greek and Coptic') despite the "alpha" character is present. 50 self.assertEqual(os2.recalcUnicodeRanges(font, pruneOnly=True), {0, 9}) 51 # try again with pruneOnly=False: bit 7 is now set. 52 self.assertEqual(os2.recalcUnicodeRanges(font), {0, 7, 9}) 53 # add a non-BMP char from 'Mahjong Tiles' block (bit 122) 54 cmap.tables[0].cmap[0x1F000] = "eastwindtile" 55 # the bit 122 and the special bit 57 ('Non Plane 0') are also enabled 56 self.assertEqual(os2.recalcUnicodeRanges(font), {0, 7, 9, 57, 122}) 57 58 def test_intersectUnicodeRanges(self): 59 self.assertEqual(intersectUnicodeRanges([0x0410]), {9}) 60 self.assertEqual(intersectUnicodeRanges([0x0410, 0x1F000]), {9, 57, 122}) 61 self.assertEqual( 62 intersectUnicodeRanges([0x0410, 0x1F000], inverse=True), 63 (set(range(123)) - {9, 57, 122}), 64 ) 65 66 def test_getCodePageRanges(self): 67 table = table_O_S_2f_2() 68 # version 0 doesn't define these fields so by definition defines no cp ranges 69 table.version = 0 70 self.assertEqual(table.getCodePageRanges(), set()) 71 # version 1 and above do contain ulCodePageRange1 and 2 fields 72 table.version = 1 73 table.ulCodePageRange1 = 0xFFFFFFFF 74 table.ulCodePageRange2 = 0xFFFFFFFF 75 bits = table.getCodePageRanges() 76 for i in range(63): 77 self.assertIn(i, bits) 78 79 def test_setCodePageRanges(self): 80 table = table_O_S_2f_2() 81 table.version = 4 82 table.ulCodePageRange1 = 0 83 table.ulCodePageRange2 = 0 84 bits = set(range(64)) 85 table.setCodePageRanges(bits) 86 self.assertEqual(table.getCodePageRanges(), bits) 87 with self.assertRaises(ValueError): 88 table.setCodePageRanges([-1]) 89 with self.assertRaises(ValueError): 90 table.setCodePageRanges([64]) 91 with self.assertRaises(ValueError): 92 table.setCodePageRanges([255]) 93 94 def test_setCodePageRanges_bump_version(self): 95 # Setting codepage ranges on a OS/2 table version 0 automatically makes it 96 # a version 1 table 97 table = table_O_S_2f_2() 98 table.version = 0 99 self.assertEqual(table.getCodePageRanges(), set()) 100 table.setCodePageRanges({0, 1, 2}) 101 self.assertEqual(table.getCodePageRanges(), {0, 1, 2}) 102 self.assertEqual(table.version, 1) 103 104 def test_recalcCodePageRanges(self): 105 font, os2, cmap = self.makeOS2_cmap( 106 {ord("A"): "A", ord("Ά"): "Alphatonos", ord("Б"): "Be"} 107 ) 108 os2.setCodePageRanges({0, 2, 9}) 109 110 # With pruneOnly=True, should clear any CodePage for which there are no 111 # characters in the cmap. 112 self.assertEqual(os2.recalcCodePageRanges(font, pruneOnly=True), {2}) 113 114 # With pruneOnly=False, should also set CodePages not initially set. 115 self.assertEqual(os2.recalcCodePageRanges(font), {2, 3}) 116 117 # Add a Korean character, should set CodePage 21 (Korean Johab) 118 cmap.tables[0].cmap[ord("곴")] = "goss" 119 self.assertEqual(os2.recalcCodePageRanges(font), {2, 3, 21}) 120 121 # Remove all characters from cmap, should still set CodePage 0 (Latin 1) 122 cmap.tables[0].cmap = {} 123 self.assertEqual(os2.recalcCodePageRanges(font), {0}) 124 125 126if __name__ == "__main__": 127 import sys 128 129 sys.exit(unittest.main()) 130