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