• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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