1import io 2import os 3import re 4from fontTools import ttLib 5from fontTools.fontBuilder import FontBuilder 6import unittest 7from fontTools.ttLib.tables._c_m_a_p import CmapSubtable, table__c_m_a_p 8 9CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) 10DATA_DIR = os.path.join(CURR_DIR, "data") 11CMAP_FORMAT_14_TTX = os.path.join(DATA_DIR, "_c_m_a_p_format_14.ttx") 12CMAP_FORMAT_14_BW_COMPAT_TTX = os.path.join( 13 DATA_DIR, "_c_m_a_p_format_14_bw_compat.ttx" 14) 15 16 17def strip_VariableItems(string): 18 # ttlib changes with the fontTools version 19 string = re.sub(' ttLibVersion=".*"', "", string) 20 return string 21 22 23class CmapSubtableTest(unittest.TestCase): 24 def makeSubtable(self, cmapFormat, platformID, platEncID, langID): 25 subtable = CmapSubtable.newSubtable(cmapFormat) 26 subtable.platformID, subtable.platEncID, subtable.language = ( 27 platformID, 28 platEncID, 29 langID, 30 ) 31 return subtable 32 33 def test_toUnicode_utf16be(self): 34 subtable = self.makeSubtable(4, 0, 2, 7) 35 self.assertEqual("utf_16_be", subtable.getEncoding()) 36 self.assertEqual(True, subtable.isUnicode()) 37 38 def test_toUnicode_macroman(self): 39 subtable = self.makeSubtable(4, 1, 0, 7) # MacRoman 40 self.assertEqual("mac_roman", subtable.getEncoding()) 41 self.assertEqual(False, subtable.isUnicode()) 42 43 def test_toUnicode_macromanian(self): 44 subtable = self.makeSubtable(4, 1, 0, 37) # Mac Romanian 45 self.assertNotEqual(None, subtable.getEncoding()) 46 self.assertEqual(False, subtable.isUnicode()) 47 48 def test_extended_mac_encodings(self): 49 subtable = self.makeSubtable(4, 1, 1, 0) # Mac Japanese 50 self.assertNotEqual(None, subtable.getEncoding()) 51 self.assertEqual(False, subtable.isUnicode()) 52 53 def test_extended_unknown(self): 54 subtable = self.makeSubtable(4, 10, 11, 12) 55 self.assertEqual(subtable.getEncoding(), None) 56 self.assertEqual(subtable.getEncoding("ascii"), "ascii") 57 self.assertEqual(subtable.getEncoding(default="xyz"), "xyz") 58 59 def test_compile_2(self): 60 subtable = self.makeSubtable(2, 1, 2, 0) 61 subtable.cmap = {c: "cid%05d" % c for c in range(32, 8192)} 62 font = ttLib.TTFont() 63 font.setGlyphOrder([".notdef"] + list(subtable.cmap.values())) 64 data = subtable.compile(font) 65 66 subtable2 = CmapSubtable.newSubtable(2) 67 subtable2.decompile(data, font) 68 self.assertEqual(subtable2.cmap, subtable.cmap) 69 70 def test_compile_2_rebuild_rev_glyph_order(self): 71 for fmt in [2, 4, 12]: 72 subtable = self.makeSubtable(fmt, 1, 2, 0) 73 subtable.cmap = {c: "cid%05d" % c for c in range(32, 8192)} 74 font = ttLib.TTFont() 75 font.setGlyphOrder([".notdef"] + list(subtable.cmap.values())) 76 font._reverseGlyphOrderDict = ( 77 {} 78 ) # force first KeyError branch in subtable.compile() 79 data = subtable.compile(font) 80 subtable2 = CmapSubtable.newSubtable(fmt) 81 subtable2.decompile(data, font) 82 self.assertEqual(subtable2.cmap, subtable.cmap, str(fmt)) 83 84 def test_compile_2_gids(self): 85 for fmt in [2, 4, 12]: 86 subtable = self.makeSubtable(fmt, 1, 3, 0) 87 subtable.cmap = {0x0041: "gid001", 0x0042: "gid002"} 88 font = ttLib.TTFont() 89 font.setGlyphOrder([".notdef"]) 90 data = subtable.compile(font) 91 92 def test_compile_decompile_4_empty(self): 93 subtable = self.makeSubtable(4, 3, 1, 0) 94 subtable.cmap = {} 95 font = ttLib.TTFont() 96 font.setGlyphOrder([]) 97 data = subtable.compile(font) 98 subtable2 = CmapSubtable.newSubtable(4) 99 subtable2.decompile(data, font) 100 self.assertEqual(subtable2.cmap, {}) 101 102 def test_decompile_4(self): 103 subtable = CmapSubtable.newSubtable(4) 104 font = ttLib.TTFont() 105 font.setGlyphOrder([]) 106 subtable.decompile(b"\0" * 3 + b"\x10" + b"\0" * 12, font) 107 108 def test_decompile_12(self): 109 subtable = CmapSubtable.newSubtable(12) 110 font = ttLib.TTFont() 111 font.setGlyphOrder([]) 112 subtable.decompile(b"\0" * 7 + b"\x10" + b"\0" * 8, font) 113 114 def test_buildReversed(self): 115 c4 = self.makeSubtable(4, 3, 1, 0) 116 c4.cmap = {0x0041: "A", 0x0391: "A"} 117 c12 = self.makeSubtable(12, 3, 10, 0) 118 c12.cmap = {0x10314: "u10314"} 119 cmap = table__c_m_a_p() 120 cmap.tables = [c4, c12] 121 self.assertEqual( 122 cmap.buildReversed(), {"A": {0x0041, 0x0391}, "u10314": {0x10314}} 123 ) 124 125 def test_getBestCmap(self): 126 c4 = self.makeSubtable(4, 3, 1, 0) 127 c4.cmap = {0x0041: "A", 0x0391: "A"} 128 c12 = self.makeSubtable(12, 3, 10, 0) 129 c12.cmap = {0x10314: "u10314"} 130 cmap = table__c_m_a_p() 131 cmap.tables = [c4, c12] 132 self.assertEqual(cmap.getBestCmap(), {0x10314: "u10314"}) 133 self.assertEqual( 134 cmap.getBestCmap(cmapPreferences=[(3, 1)]), {0x0041: "A", 0x0391: "A"} 135 ) 136 self.assertEqual(cmap.getBestCmap(cmapPreferences=[(0, 4)]), None) 137 138 def test_font_getBestCmap(self): 139 c4 = self.makeSubtable(4, 3, 1, 0) 140 c4.cmap = {0x0041: "A", 0x0391: "A"} 141 c12 = self.makeSubtable(12, 3, 10, 0) 142 c12.cmap = {0x10314: "u10314"} 143 cmap = table__c_m_a_p() 144 cmap.tables = [c4, c12] 145 font = ttLib.TTFont() 146 font["cmap"] = cmap 147 self.assertEqual(font.getBestCmap(), {0x10314: "u10314"}) 148 self.assertEqual( 149 font.getBestCmap(cmapPreferences=[(3, 1)]), {0x0041: "A", 0x0391: "A"} 150 ) 151 self.assertEqual(font.getBestCmap(cmapPreferences=[(0, 4)]), None) 152 153 def test_format_14(self): 154 subtable = self.makeSubtable(14, 0, 5, 0) 155 subtable.cmap = {} # dummy 156 subtable.uvsDict = { 157 0xFE00: [(0x0030, "zero.slash")], 158 0xFE01: [(0x0030, None)], 159 } 160 fb = FontBuilder(1024, isTTF=True) 161 font = fb.font 162 fb.setupGlyphOrder([".notdef", "zero.slash"]) 163 fb.setupMaxp() 164 fb.setupPost() 165 cmap = table__c_m_a_p() 166 cmap.tableVersion = 0 167 cmap.tables = [subtable] 168 font["cmap"] = cmap 169 f = io.BytesIO() 170 font.save(f) 171 f.seek(0) 172 font = ttLib.TTFont(f) 173 self.assertEqual(font["cmap"].getcmap(0, 5).uvsDict, subtable.uvsDict) 174 f = io.StringIO(newline=None) 175 font.saveXML(f, tables=["cmap"]) 176 ttx = strip_VariableItems(f.getvalue()) 177 with open(CMAP_FORMAT_14_TTX) as f: 178 expected = strip_VariableItems(f.read()) 179 self.assertEqual(ttx, expected) 180 with open(CMAP_FORMAT_14_BW_COMPAT_TTX) as f: 181 font.importXML(f) 182 self.assertEqual(font["cmap"].getcmap(0, 5).uvsDict, subtable.uvsDict) 183 184 185if __name__ == "__main__": 186 import sys 187 188 sys.exit(unittest.main()) 189