xref: /aosp_15_r20/external/fonttools/Lib/fontTools/merge/tables.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughes# Copyright 2013 Google, Inc. All Rights Reserved.
2*e1fe3e4aSElliott Hughes#
3*e1fe3e4aSElliott Hughes# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
4*e1fe3e4aSElliott Hughes
5*e1fe3e4aSElliott Hughesfrom fontTools import ttLib, cffLib
6*e1fe3e4aSElliott Hughesfrom fontTools.misc.psCharStrings import T2WidthExtractor
7*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables.DefaultTable import DefaultTable
8*e1fe3e4aSElliott Hughesfrom fontTools.merge.base import add_method, mergeObjects
9*e1fe3e4aSElliott Hughesfrom fontTools.merge.cmap import computeMegaCmap
10*e1fe3e4aSElliott Hughesfrom fontTools.merge.util import *
11*e1fe3e4aSElliott Hughesimport logging
12*e1fe3e4aSElliott Hughes
13*e1fe3e4aSElliott Hughes
14*e1fe3e4aSElliott Hugheslog = logging.getLogger("fontTools.merge")
15*e1fe3e4aSElliott Hughes
16*e1fe3e4aSElliott Hughes
17*e1fe3e4aSElliott HughesttLib.getTableClass("maxp").mergeMap = {
18*e1fe3e4aSElliott Hughes    "*": max,
19*e1fe3e4aSElliott Hughes    "tableTag": equal,
20*e1fe3e4aSElliott Hughes    "tableVersion": equal,
21*e1fe3e4aSElliott Hughes    "numGlyphs": sum,
22*e1fe3e4aSElliott Hughes    "maxStorage": first,
23*e1fe3e4aSElliott Hughes    "maxFunctionDefs": first,
24*e1fe3e4aSElliott Hughes    "maxInstructionDefs": first,
25*e1fe3e4aSElliott Hughes    # TODO When we correctly merge hinting data, update these values:
26*e1fe3e4aSElliott Hughes    # maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions
27*e1fe3e4aSElliott Hughes}
28*e1fe3e4aSElliott Hughes
29*e1fe3e4aSElliott HughesheadFlagsMergeBitMap = {
30*e1fe3e4aSElliott Hughes    "size": 16,
31*e1fe3e4aSElliott Hughes    "*": bitwise_or,
32*e1fe3e4aSElliott Hughes    1: bitwise_and,  # Baseline at y = 0
33*e1fe3e4aSElliott Hughes    2: bitwise_and,  # lsb at x = 0
34*e1fe3e4aSElliott Hughes    3: bitwise_and,  # Force ppem to integer values. FIXME?
35*e1fe3e4aSElliott Hughes    5: bitwise_and,  # Font is vertical
36*e1fe3e4aSElliott Hughes    6: lambda bit: 0,  # Always set to zero
37*e1fe3e4aSElliott Hughes    11: bitwise_and,  # Font data is 'lossless'
38*e1fe3e4aSElliott Hughes    13: bitwise_and,  # Optimized for ClearType
39*e1fe3e4aSElliott Hughes    14: bitwise_and,  # Last resort font. FIXME? equal or first may be better
40*e1fe3e4aSElliott Hughes    15: lambda bit: 0,  # Always set to zero
41*e1fe3e4aSElliott Hughes}
42*e1fe3e4aSElliott Hughes
43*e1fe3e4aSElliott HughesttLib.getTableClass("head").mergeMap = {
44*e1fe3e4aSElliott Hughes    "tableTag": equal,
45*e1fe3e4aSElliott Hughes    "tableVersion": max,
46*e1fe3e4aSElliott Hughes    "fontRevision": max,
47*e1fe3e4aSElliott Hughes    "checkSumAdjustment": lambda lst: 0,  # We need *something* here
48*e1fe3e4aSElliott Hughes    "magicNumber": equal,
49*e1fe3e4aSElliott Hughes    "flags": mergeBits(headFlagsMergeBitMap),
50*e1fe3e4aSElliott Hughes    "unitsPerEm": equal,
51*e1fe3e4aSElliott Hughes    "created": current_time,
52*e1fe3e4aSElliott Hughes    "modified": current_time,
53*e1fe3e4aSElliott Hughes    "xMin": min,
54*e1fe3e4aSElliott Hughes    "yMin": min,
55*e1fe3e4aSElliott Hughes    "xMax": max,
56*e1fe3e4aSElliott Hughes    "yMax": max,
57*e1fe3e4aSElliott Hughes    "macStyle": first,
58*e1fe3e4aSElliott Hughes    "lowestRecPPEM": max,
59*e1fe3e4aSElliott Hughes    "fontDirectionHint": lambda lst: 2,
60*e1fe3e4aSElliott Hughes    "indexToLocFormat": first,
61*e1fe3e4aSElliott Hughes    "glyphDataFormat": equal,
62*e1fe3e4aSElliott Hughes}
63*e1fe3e4aSElliott Hughes
64*e1fe3e4aSElliott HughesttLib.getTableClass("hhea").mergeMap = {
65*e1fe3e4aSElliott Hughes    "*": equal,
66*e1fe3e4aSElliott Hughes    "tableTag": equal,
67*e1fe3e4aSElliott Hughes    "tableVersion": max,
68*e1fe3e4aSElliott Hughes    "ascent": max,
69*e1fe3e4aSElliott Hughes    "descent": min,
70*e1fe3e4aSElliott Hughes    "lineGap": max,
71*e1fe3e4aSElliott Hughes    "advanceWidthMax": max,
72*e1fe3e4aSElliott Hughes    "minLeftSideBearing": min,
73*e1fe3e4aSElliott Hughes    "minRightSideBearing": min,
74*e1fe3e4aSElliott Hughes    "xMaxExtent": max,
75*e1fe3e4aSElliott Hughes    "caretSlopeRise": first,
76*e1fe3e4aSElliott Hughes    "caretSlopeRun": first,
77*e1fe3e4aSElliott Hughes    "caretOffset": first,
78*e1fe3e4aSElliott Hughes    "numberOfHMetrics": recalculate,
79*e1fe3e4aSElliott Hughes}
80*e1fe3e4aSElliott Hughes
81*e1fe3e4aSElliott HughesttLib.getTableClass("vhea").mergeMap = {
82*e1fe3e4aSElliott Hughes    "*": equal,
83*e1fe3e4aSElliott Hughes    "tableTag": equal,
84*e1fe3e4aSElliott Hughes    "tableVersion": max,
85*e1fe3e4aSElliott Hughes    "ascent": max,
86*e1fe3e4aSElliott Hughes    "descent": min,
87*e1fe3e4aSElliott Hughes    "lineGap": max,
88*e1fe3e4aSElliott Hughes    "advanceHeightMax": max,
89*e1fe3e4aSElliott Hughes    "minTopSideBearing": min,
90*e1fe3e4aSElliott Hughes    "minBottomSideBearing": min,
91*e1fe3e4aSElliott Hughes    "yMaxExtent": max,
92*e1fe3e4aSElliott Hughes    "caretSlopeRise": first,
93*e1fe3e4aSElliott Hughes    "caretSlopeRun": first,
94*e1fe3e4aSElliott Hughes    "caretOffset": first,
95*e1fe3e4aSElliott Hughes    "numberOfVMetrics": recalculate,
96*e1fe3e4aSElliott Hughes}
97*e1fe3e4aSElliott Hughes
98*e1fe3e4aSElliott Hughesos2FsTypeMergeBitMap = {
99*e1fe3e4aSElliott Hughes    "size": 16,
100*e1fe3e4aSElliott Hughes    "*": lambda bit: 0,
101*e1fe3e4aSElliott Hughes    1: bitwise_or,  # no embedding permitted
102*e1fe3e4aSElliott Hughes    2: bitwise_and,  # allow previewing and printing documents
103*e1fe3e4aSElliott Hughes    3: bitwise_and,  # allow editing documents
104*e1fe3e4aSElliott Hughes    8: bitwise_or,  # no subsetting permitted
105*e1fe3e4aSElliott Hughes    9: bitwise_or,  # no embedding of outlines permitted
106*e1fe3e4aSElliott Hughes}
107*e1fe3e4aSElliott Hughes
108*e1fe3e4aSElliott Hughes
109*e1fe3e4aSElliott Hughesdef mergeOs2FsType(lst):
110*e1fe3e4aSElliott Hughes    lst = list(lst)
111*e1fe3e4aSElliott Hughes    if all(item == 0 for item in lst):
112*e1fe3e4aSElliott Hughes        return 0
113*e1fe3e4aSElliott Hughes
114*e1fe3e4aSElliott Hughes    # Compute least restrictive logic for each fsType value
115*e1fe3e4aSElliott Hughes    for i in range(len(lst)):
116*e1fe3e4aSElliott Hughes        # unset bit 1 (no embedding permitted) if either bit 2 or 3 is set
117*e1fe3e4aSElliott Hughes        if lst[i] & 0x000C:
118*e1fe3e4aSElliott Hughes            lst[i] &= ~0x0002
119*e1fe3e4aSElliott Hughes        # set bit 2 (allow previewing) if bit 3 is set (allow editing)
120*e1fe3e4aSElliott Hughes        elif lst[i] & 0x0008:
121*e1fe3e4aSElliott Hughes            lst[i] |= 0x0004
122*e1fe3e4aSElliott Hughes        # set bits 2 and 3 if everything is allowed
123*e1fe3e4aSElliott Hughes        elif lst[i] == 0:
124*e1fe3e4aSElliott Hughes            lst[i] = 0x000C
125*e1fe3e4aSElliott Hughes
126*e1fe3e4aSElliott Hughes    fsType = mergeBits(os2FsTypeMergeBitMap)(lst)
127*e1fe3e4aSElliott Hughes    # unset bits 2 and 3 if bit 1 is set (some font is "no embedding")
128*e1fe3e4aSElliott Hughes    if fsType & 0x0002:
129*e1fe3e4aSElliott Hughes        fsType &= ~0x000C
130*e1fe3e4aSElliott Hughes    return fsType
131*e1fe3e4aSElliott Hughes
132*e1fe3e4aSElliott Hughes
133*e1fe3e4aSElliott HughesttLib.getTableClass("OS/2").mergeMap = {
134*e1fe3e4aSElliott Hughes    "*": first,
135*e1fe3e4aSElliott Hughes    "tableTag": equal,
136*e1fe3e4aSElliott Hughes    "version": max,
137*e1fe3e4aSElliott Hughes    "xAvgCharWidth": first,  # Will be recalculated at the end on the merged font
138*e1fe3e4aSElliott Hughes    "fsType": mergeOs2FsType,  # Will be overwritten
139*e1fe3e4aSElliott Hughes    "panose": first,  # FIXME: should really be the first Latin font
140*e1fe3e4aSElliott Hughes    "ulUnicodeRange1": bitwise_or,
141*e1fe3e4aSElliott Hughes    "ulUnicodeRange2": bitwise_or,
142*e1fe3e4aSElliott Hughes    "ulUnicodeRange3": bitwise_or,
143*e1fe3e4aSElliott Hughes    "ulUnicodeRange4": bitwise_or,
144*e1fe3e4aSElliott Hughes    "fsFirstCharIndex": min,
145*e1fe3e4aSElliott Hughes    "fsLastCharIndex": max,
146*e1fe3e4aSElliott Hughes    "sTypoAscender": max,
147*e1fe3e4aSElliott Hughes    "sTypoDescender": min,
148*e1fe3e4aSElliott Hughes    "sTypoLineGap": max,
149*e1fe3e4aSElliott Hughes    "usWinAscent": max,
150*e1fe3e4aSElliott Hughes    "usWinDescent": max,
151*e1fe3e4aSElliott Hughes    # Version 1
152*e1fe3e4aSElliott Hughes    "ulCodePageRange1": onlyExisting(bitwise_or),
153*e1fe3e4aSElliott Hughes    "ulCodePageRange2": onlyExisting(bitwise_or),
154*e1fe3e4aSElliott Hughes    # Version 2, 3, 4
155*e1fe3e4aSElliott Hughes    "sxHeight": onlyExisting(max),
156*e1fe3e4aSElliott Hughes    "sCapHeight": onlyExisting(max),
157*e1fe3e4aSElliott Hughes    "usDefaultChar": onlyExisting(first),
158*e1fe3e4aSElliott Hughes    "usBreakChar": onlyExisting(first),
159*e1fe3e4aSElliott Hughes    "usMaxContext": onlyExisting(max),
160*e1fe3e4aSElliott Hughes    # version 5
161*e1fe3e4aSElliott Hughes    "usLowerOpticalPointSize": onlyExisting(min),
162*e1fe3e4aSElliott Hughes    "usUpperOpticalPointSize": onlyExisting(max),
163*e1fe3e4aSElliott Hughes}
164*e1fe3e4aSElliott Hughes
165*e1fe3e4aSElliott Hughes
166*e1fe3e4aSElliott Hughes@add_method(ttLib.getTableClass("OS/2"))
167*e1fe3e4aSElliott Hughesdef merge(self, m, tables):
168*e1fe3e4aSElliott Hughes    DefaultTable.merge(self, m, tables)
169*e1fe3e4aSElliott Hughes    if self.version < 2:
170*e1fe3e4aSElliott Hughes        # bits 8 and 9 are reserved and should be set to zero
171*e1fe3e4aSElliott Hughes        self.fsType &= ~0x0300
172*e1fe3e4aSElliott Hughes    if self.version >= 3:
173*e1fe3e4aSElliott Hughes        # Only one of bits 1, 2, and 3 may be set. We already take
174*e1fe3e4aSElliott Hughes        # care of bit 1 implications in mergeOs2FsType. So unset
175*e1fe3e4aSElliott Hughes        # bit 2 if bit 3 is already set.
176*e1fe3e4aSElliott Hughes        if self.fsType & 0x0008:
177*e1fe3e4aSElliott Hughes            self.fsType &= ~0x0004
178*e1fe3e4aSElliott Hughes    return self
179*e1fe3e4aSElliott Hughes
180*e1fe3e4aSElliott Hughes
181*e1fe3e4aSElliott HughesttLib.getTableClass("post").mergeMap = {
182*e1fe3e4aSElliott Hughes    "*": first,
183*e1fe3e4aSElliott Hughes    "tableTag": equal,
184*e1fe3e4aSElliott Hughes    "formatType": max,
185*e1fe3e4aSElliott Hughes    "isFixedPitch": min,
186*e1fe3e4aSElliott Hughes    "minMemType42": max,
187*e1fe3e4aSElliott Hughes    "maxMemType42": lambda lst: 0,
188*e1fe3e4aSElliott Hughes    "minMemType1": max,
189*e1fe3e4aSElliott Hughes    "maxMemType1": lambda lst: 0,
190*e1fe3e4aSElliott Hughes    "mapping": onlyExisting(sumDicts),
191*e1fe3e4aSElliott Hughes    "extraNames": lambda lst: [],
192*e1fe3e4aSElliott Hughes}
193*e1fe3e4aSElliott Hughes
194*e1fe3e4aSElliott HughesttLib.getTableClass("vmtx").mergeMap = ttLib.getTableClass("hmtx").mergeMap = {
195*e1fe3e4aSElliott Hughes    "tableTag": equal,
196*e1fe3e4aSElliott Hughes    "metrics": sumDicts,
197*e1fe3e4aSElliott Hughes}
198*e1fe3e4aSElliott Hughes
199*e1fe3e4aSElliott HughesttLib.getTableClass("name").mergeMap = {
200*e1fe3e4aSElliott Hughes    "tableTag": equal,
201*e1fe3e4aSElliott Hughes    "names": first,  # FIXME? Does mixing name records make sense?
202*e1fe3e4aSElliott Hughes}
203*e1fe3e4aSElliott Hughes
204*e1fe3e4aSElliott HughesttLib.getTableClass("loca").mergeMap = {
205*e1fe3e4aSElliott Hughes    "*": recalculate,
206*e1fe3e4aSElliott Hughes    "tableTag": equal,
207*e1fe3e4aSElliott Hughes}
208*e1fe3e4aSElliott Hughes
209*e1fe3e4aSElliott HughesttLib.getTableClass("glyf").mergeMap = {
210*e1fe3e4aSElliott Hughes    "tableTag": equal,
211*e1fe3e4aSElliott Hughes    "glyphs": sumDicts,
212*e1fe3e4aSElliott Hughes    "glyphOrder": sumLists,
213*e1fe3e4aSElliott Hughes    "_reverseGlyphOrder": recalculate,
214*e1fe3e4aSElliott Hughes    "axisTags": equal,
215*e1fe3e4aSElliott Hughes}
216*e1fe3e4aSElliott Hughes
217*e1fe3e4aSElliott Hughes
218*e1fe3e4aSElliott Hughes@add_method(ttLib.getTableClass("glyf"))
219*e1fe3e4aSElliott Hughesdef merge(self, m, tables):
220*e1fe3e4aSElliott Hughes    for i, table in enumerate(tables):
221*e1fe3e4aSElliott Hughes        for g in table.glyphs.values():
222*e1fe3e4aSElliott Hughes            if i:
223*e1fe3e4aSElliott Hughes                # Drop hints for all but first font, since
224*e1fe3e4aSElliott Hughes                # we don't map functions / CVT values.
225*e1fe3e4aSElliott Hughes                g.removeHinting()
226*e1fe3e4aSElliott Hughes            # Expand composite glyphs to load their
227*e1fe3e4aSElliott Hughes            # composite glyph names.
228*e1fe3e4aSElliott Hughes            if g.isComposite() or g.isVarComposite():
229*e1fe3e4aSElliott Hughes                g.expand(table)
230*e1fe3e4aSElliott Hughes    return DefaultTable.merge(self, m, tables)
231*e1fe3e4aSElliott Hughes
232*e1fe3e4aSElliott Hughes
233*e1fe3e4aSElliott HughesttLib.getTableClass("prep").mergeMap = lambda self, lst: first(lst)
234*e1fe3e4aSElliott HughesttLib.getTableClass("fpgm").mergeMap = lambda self, lst: first(lst)
235*e1fe3e4aSElliott HughesttLib.getTableClass("cvt ").mergeMap = lambda self, lst: first(lst)
236*e1fe3e4aSElliott HughesttLib.getTableClass("gasp").mergeMap = lambda self, lst: first(
237*e1fe3e4aSElliott Hughes    lst
238*e1fe3e4aSElliott Hughes)  # FIXME? Appears irreconcilable
239*e1fe3e4aSElliott Hughes
240*e1fe3e4aSElliott Hughes
241*e1fe3e4aSElliott Hughes@add_method(ttLib.getTableClass("CFF "))
242*e1fe3e4aSElliott Hughesdef merge(self, m, tables):
243*e1fe3e4aSElliott Hughes    if any(hasattr(table.cff[0], "FDSelect") for table in tables):
244*e1fe3e4aSElliott Hughes        raise NotImplementedError("Merging CID-keyed CFF tables is not supported yet")
245*e1fe3e4aSElliott Hughes
246*e1fe3e4aSElliott Hughes    for table in tables:
247*e1fe3e4aSElliott Hughes        table.cff.desubroutinize()
248*e1fe3e4aSElliott Hughes
249*e1fe3e4aSElliott Hughes    newcff = tables[0]
250*e1fe3e4aSElliott Hughes    newfont = newcff.cff[0]
251*e1fe3e4aSElliott Hughes    private = newfont.Private
252*e1fe3e4aSElliott Hughes    newDefaultWidthX, newNominalWidthX = private.defaultWidthX, private.nominalWidthX
253*e1fe3e4aSElliott Hughes    storedNamesStrings = []
254*e1fe3e4aSElliott Hughes    glyphOrderStrings = []
255*e1fe3e4aSElliott Hughes    glyphOrder = set(newfont.getGlyphOrder())
256*e1fe3e4aSElliott Hughes
257*e1fe3e4aSElliott Hughes    for name in newfont.strings.strings:
258*e1fe3e4aSElliott Hughes        if name not in glyphOrder:
259*e1fe3e4aSElliott Hughes            storedNamesStrings.append(name)
260*e1fe3e4aSElliott Hughes        else:
261*e1fe3e4aSElliott Hughes            glyphOrderStrings.append(name)
262*e1fe3e4aSElliott Hughes
263*e1fe3e4aSElliott Hughes    chrset = list(newfont.charset)
264*e1fe3e4aSElliott Hughes    newcs = newfont.CharStrings
265*e1fe3e4aSElliott Hughes    log.debug("FONT 0 CharStrings: %d.", len(newcs))
266*e1fe3e4aSElliott Hughes
267*e1fe3e4aSElliott Hughes    for i, table in enumerate(tables[1:], start=1):
268*e1fe3e4aSElliott Hughes        font = table.cff[0]
269*e1fe3e4aSElliott Hughes        defaultWidthX, nominalWidthX = (
270*e1fe3e4aSElliott Hughes            font.Private.defaultWidthX,
271*e1fe3e4aSElliott Hughes            font.Private.nominalWidthX,
272*e1fe3e4aSElliott Hughes        )
273*e1fe3e4aSElliott Hughes        widthsDiffer = (
274*e1fe3e4aSElliott Hughes            defaultWidthX != newDefaultWidthX or nominalWidthX != newNominalWidthX
275*e1fe3e4aSElliott Hughes        )
276*e1fe3e4aSElliott Hughes        font.Private = private
277*e1fe3e4aSElliott Hughes        fontGlyphOrder = set(font.getGlyphOrder())
278*e1fe3e4aSElliott Hughes        for name in font.strings.strings:
279*e1fe3e4aSElliott Hughes            if name in fontGlyphOrder:
280*e1fe3e4aSElliott Hughes                glyphOrderStrings.append(name)
281*e1fe3e4aSElliott Hughes        cs = font.CharStrings
282*e1fe3e4aSElliott Hughes        gs = table.cff.GlobalSubrs
283*e1fe3e4aSElliott Hughes        log.debug("Font %d CharStrings: %d.", i, len(cs))
284*e1fe3e4aSElliott Hughes        chrset.extend(font.charset)
285*e1fe3e4aSElliott Hughes        if newcs.charStringsAreIndexed:
286*e1fe3e4aSElliott Hughes            for i, name in enumerate(cs.charStrings, start=len(newcs)):
287*e1fe3e4aSElliott Hughes                newcs.charStrings[name] = i
288*e1fe3e4aSElliott Hughes                newcs.charStringsIndex.items.append(None)
289*e1fe3e4aSElliott Hughes        for name in cs.charStrings:
290*e1fe3e4aSElliott Hughes            if widthsDiffer:
291*e1fe3e4aSElliott Hughes                c = cs[name]
292*e1fe3e4aSElliott Hughes                defaultWidthXToken = object()
293*e1fe3e4aSElliott Hughes                extractor = T2WidthExtractor([], [], nominalWidthX, defaultWidthXToken)
294*e1fe3e4aSElliott Hughes                extractor.execute(c)
295*e1fe3e4aSElliott Hughes                width = extractor.width
296*e1fe3e4aSElliott Hughes                if width is not defaultWidthXToken:
297*e1fe3e4aSElliott Hughes                    c.program.pop(0)
298*e1fe3e4aSElliott Hughes                else:
299*e1fe3e4aSElliott Hughes                    width = defaultWidthX
300*e1fe3e4aSElliott Hughes                if width != newDefaultWidthX:
301*e1fe3e4aSElliott Hughes                    c.program.insert(0, width - newNominalWidthX)
302*e1fe3e4aSElliott Hughes            newcs[name] = cs[name]
303*e1fe3e4aSElliott Hughes
304*e1fe3e4aSElliott Hughes    newfont.charset = chrset
305*e1fe3e4aSElliott Hughes    newfont.numGlyphs = len(chrset)
306*e1fe3e4aSElliott Hughes    newfont.strings.strings = glyphOrderStrings + storedNamesStrings
307*e1fe3e4aSElliott Hughes
308*e1fe3e4aSElliott Hughes    return newcff
309*e1fe3e4aSElliott Hughes
310*e1fe3e4aSElliott Hughes
311*e1fe3e4aSElliott Hughes@add_method(ttLib.getTableClass("cmap"))
312*e1fe3e4aSElliott Hughesdef merge(self, m, tables):
313*e1fe3e4aSElliott Hughes    # TODO Handle format=14.
314*e1fe3e4aSElliott Hughes    if not hasattr(m, "cmap"):
315*e1fe3e4aSElliott Hughes        computeMegaCmap(m, tables)
316*e1fe3e4aSElliott Hughes    cmap = m.cmap
317*e1fe3e4aSElliott Hughes
318*e1fe3e4aSElliott Hughes    cmapBmpOnly = {uni: gid for uni, gid in cmap.items() if uni <= 0xFFFF}
319*e1fe3e4aSElliott Hughes    self.tables = []
320*e1fe3e4aSElliott Hughes    module = ttLib.getTableModule("cmap")
321*e1fe3e4aSElliott Hughes    if len(cmapBmpOnly) != len(cmap):
322*e1fe3e4aSElliott Hughes        # format-12 required.
323*e1fe3e4aSElliott Hughes        cmapTable = module.cmap_classes[12](12)
324*e1fe3e4aSElliott Hughes        cmapTable.platformID = 3
325*e1fe3e4aSElliott Hughes        cmapTable.platEncID = 10
326*e1fe3e4aSElliott Hughes        cmapTable.language = 0
327*e1fe3e4aSElliott Hughes        cmapTable.cmap = cmap
328*e1fe3e4aSElliott Hughes        self.tables.append(cmapTable)
329*e1fe3e4aSElliott Hughes    # always create format-4
330*e1fe3e4aSElliott Hughes    cmapTable = module.cmap_classes[4](4)
331*e1fe3e4aSElliott Hughes    cmapTable.platformID = 3
332*e1fe3e4aSElliott Hughes    cmapTable.platEncID = 1
333*e1fe3e4aSElliott Hughes    cmapTable.language = 0
334*e1fe3e4aSElliott Hughes    cmapTable.cmap = cmapBmpOnly
335*e1fe3e4aSElliott Hughes    # ordered by platform then encoding
336*e1fe3e4aSElliott Hughes    self.tables.insert(0, cmapTable)
337*e1fe3e4aSElliott Hughes    self.tableVersion = 0
338*e1fe3e4aSElliott Hughes    self.numSubTables = len(self.tables)
339*e1fe3e4aSElliott Hughes    return self
340