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.merge.unicode import is_Default_Ignorable 6*e1fe3e4aSElliott Hughesfrom fontTools.pens.recordingPen import DecomposingRecordingPen 7*e1fe3e4aSElliott Hughesimport logging 8*e1fe3e4aSElliott Hughes 9*e1fe3e4aSElliott Hughes 10*e1fe3e4aSElliott Hugheslog = logging.getLogger("fontTools.merge") 11*e1fe3e4aSElliott Hughes 12*e1fe3e4aSElliott Hughes 13*e1fe3e4aSElliott Hughesdef computeMegaGlyphOrder(merger, glyphOrders): 14*e1fe3e4aSElliott Hughes """Modifies passed-in glyphOrders to reflect new glyph names. 15*e1fe3e4aSElliott Hughes Stores merger.glyphOrder.""" 16*e1fe3e4aSElliott Hughes megaOrder = {} 17*e1fe3e4aSElliott Hughes for glyphOrder in glyphOrders: 18*e1fe3e4aSElliott Hughes for i, glyphName in enumerate(glyphOrder): 19*e1fe3e4aSElliott Hughes if glyphName in megaOrder: 20*e1fe3e4aSElliott Hughes n = megaOrder[glyphName] 21*e1fe3e4aSElliott Hughes while (glyphName + "." + repr(n)) in megaOrder: 22*e1fe3e4aSElliott Hughes n += 1 23*e1fe3e4aSElliott Hughes megaOrder[glyphName] = n 24*e1fe3e4aSElliott Hughes glyphName += "." + repr(n) 25*e1fe3e4aSElliott Hughes glyphOrder[i] = glyphName 26*e1fe3e4aSElliott Hughes megaOrder[glyphName] = 1 27*e1fe3e4aSElliott Hughes merger.glyphOrder = megaOrder = list(megaOrder.keys()) 28*e1fe3e4aSElliott Hughes 29*e1fe3e4aSElliott Hughes 30*e1fe3e4aSElliott Hughesdef _glyphsAreSame( 31*e1fe3e4aSElliott Hughes glyphSet1, 32*e1fe3e4aSElliott Hughes glyphSet2, 33*e1fe3e4aSElliott Hughes glyph1, 34*e1fe3e4aSElliott Hughes glyph2, 35*e1fe3e4aSElliott Hughes advanceTolerance=0.05, 36*e1fe3e4aSElliott Hughes advanceToleranceEmpty=0.20, 37*e1fe3e4aSElliott Hughes): 38*e1fe3e4aSElliott Hughes pen1 = DecomposingRecordingPen(glyphSet1) 39*e1fe3e4aSElliott Hughes pen2 = DecomposingRecordingPen(glyphSet2) 40*e1fe3e4aSElliott Hughes g1 = glyphSet1[glyph1] 41*e1fe3e4aSElliott Hughes g2 = glyphSet2[glyph2] 42*e1fe3e4aSElliott Hughes g1.draw(pen1) 43*e1fe3e4aSElliott Hughes g2.draw(pen2) 44*e1fe3e4aSElliott Hughes if pen1.value != pen2.value: 45*e1fe3e4aSElliott Hughes return False 46*e1fe3e4aSElliott Hughes # Allow more width tolerance for glyphs with no ink 47*e1fe3e4aSElliott Hughes tolerance = advanceTolerance if pen1.value else advanceToleranceEmpty 48*e1fe3e4aSElliott Hughes # TODO Warn if advances not the same but within tolerance. 49*e1fe3e4aSElliott Hughes if abs(g1.width - g2.width) > g1.width * tolerance: 50*e1fe3e4aSElliott Hughes return False 51*e1fe3e4aSElliott Hughes if hasattr(g1, "height") and g1.height is not None: 52*e1fe3e4aSElliott Hughes if abs(g1.height - g2.height) > g1.height * tolerance: 53*e1fe3e4aSElliott Hughes return False 54*e1fe3e4aSElliott Hughes return True 55*e1fe3e4aSElliott Hughes 56*e1fe3e4aSElliott Hughes 57*e1fe3e4aSElliott Hughes# Valid (format, platformID, platEncID) triplets for cmap subtables containing 58*e1fe3e4aSElliott Hughes# Unicode BMP-only and Unicode Full Repertoire semantics. 59*e1fe3e4aSElliott Hughes# Cf. OpenType spec for "Platform specific encodings": 60*e1fe3e4aSElliott Hughes# https://docs.microsoft.com/en-us/typography/opentype/spec/name 61*e1fe3e4aSElliott Hughesclass _CmapUnicodePlatEncodings: 62*e1fe3e4aSElliott Hughes BMP = {(4, 3, 1), (4, 0, 3), (4, 0, 4), (4, 0, 6)} 63*e1fe3e4aSElliott Hughes FullRepertoire = {(12, 3, 10), (12, 0, 4), (12, 0, 6)} 64*e1fe3e4aSElliott Hughes 65*e1fe3e4aSElliott Hughes 66*e1fe3e4aSElliott Hughesdef computeMegaCmap(merger, cmapTables): 67*e1fe3e4aSElliott Hughes """Sets merger.cmap and merger.glyphOrder.""" 68*e1fe3e4aSElliott Hughes 69*e1fe3e4aSElliott Hughes # TODO Handle format=14. 70*e1fe3e4aSElliott Hughes # Only merge format 4 and 12 Unicode subtables, ignores all other subtables 71*e1fe3e4aSElliott Hughes # If there is a format 12 table for a font, ignore the format 4 table of it 72*e1fe3e4aSElliott Hughes chosenCmapTables = [] 73*e1fe3e4aSElliott Hughes for fontIdx, table in enumerate(cmapTables): 74*e1fe3e4aSElliott Hughes format4 = None 75*e1fe3e4aSElliott Hughes format12 = None 76*e1fe3e4aSElliott Hughes for subtable in table.tables: 77*e1fe3e4aSElliott Hughes properties = (subtable.format, subtable.platformID, subtable.platEncID) 78*e1fe3e4aSElliott Hughes if properties in _CmapUnicodePlatEncodings.BMP: 79*e1fe3e4aSElliott Hughes format4 = subtable 80*e1fe3e4aSElliott Hughes elif properties in _CmapUnicodePlatEncodings.FullRepertoire: 81*e1fe3e4aSElliott Hughes format12 = subtable 82*e1fe3e4aSElliott Hughes else: 83*e1fe3e4aSElliott Hughes log.warning( 84*e1fe3e4aSElliott Hughes "Dropped cmap subtable from font '%s':\t" 85*e1fe3e4aSElliott Hughes "format %2s, platformID %2s, platEncID %2s", 86*e1fe3e4aSElliott Hughes fontIdx, 87*e1fe3e4aSElliott Hughes subtable.format, 88*e1fe3e4aSElliott Hughes subtable.platformID, 89*e1fe3e4aSElliott Hughes subtable.platEncID, 90*e1fe3e4aSElliott Hughes ) 91*e1fe3e4aSElliott Hughes if format12 is not None: 92*e1fe3e4aSElliott Hughes chosenCmapTables.append((format12, fontIdx)) 93*e1fe3e4aSElliott Hughes elif format4 is not None: 94*e1fe3e4aSElliott Hughes chosenCmapTables.append((format4, fontIdx)) 95*e1fe3e4aSElliott Hughes 96*e1fe3e4aSElliott Hughes # Build the unicode mapping 97*e1fe3e4aSElliott Hughes merger.cmap = cmap = {} 98*e1fe3e4aSElliott Hughes fontIndexForGlyph = {} 99*e1fe3e4aSElliott Hughes glyphSets = [None for f in merger.fonts] if hasattr(merger, "fonts") else None 100*e1fe3e4aSElliott Hughes 101*e1fe3e4aSElliott Hughes for table, fontIdx in chosenCmapTables: 102*e1fe3e4aSElliott Hughes # handle duplicates 103*e1fe3e4aSElliott Hughes for uni, gid in table.cmap.items(): 104*e1fe3e4aSElliott Hughes oldgid = cmap.get(uni, None) 105*e1fe3e4aSElliott Hughes if oldgid is None: 106*e1fe3e4aSElliott Hughes cmap[uni] = gid 107*e1fe3e4aSElliott Hughes fontIndexForGlyph[gid] = fontIdx 108*e1fe3e4aSElliott Hughes elif is_Default_Ignorable(uni) or uni in (0x25CC,): # U+25CC DOTTED CIRCLE 109*e1fe3e4aSElliott Hughes continue 110*e1fe3e4aSElliott Hughes elif oldgid != gid: 111*e1fe3e4aSElliott Hughes # Char previously mapped to oldgid, now to gid. 112*e1fe3e4aSElliott Hughes # Record, to fix up in GSUB 'locl' later. 113*e1fe3e4aSElliott Hughes if merger.duplicateGlyphsPerFont[fontIdx].get(oldgid) is None: 114*e1fe3e4aSElliott Hughes if glyphSets is not None: 115*e1fe3e4aSElliott Hughes oldFontIdx = fontIndexForGlyph[oldgid] 116*e1fe3e4aSElliott Hughes for idx in (fontIdx, oldFontIdx): 117*e1fe3e4aSElliott Hughes if glyphSets[idx] is None: 118*e1fe3e4aSElliott Hughes glyphSets[idx] = merger.fonts[idx].getGlyphSet() 119*e1fe3e4aSElliott Hughes # if _glyphsAreSame(glyphSets[oldFontIdx], glyphSets[fontIdx], oldgid, gid): 120*e1fe3e4aSElliott Hughes # continue 121*e1fe3e4aSElliott Hughes merger.duplicateGlyphsPerFont[fontIdx][oldgid] = gid 122*e1fe3e4aSElliott Hughes elif merger.duplicateGlyphsPerFont[fontIdx][oldgid] != gid: 123*e1fe3e4aSElliott Hughes # Char previously mapped to oldgid but oldgid is already remapped to a different 124*e1fe3e4aSElliott Hughes # gid, because of another Unicode character. 125*e1fe3e4aSElliott Hughes # TODO: Try harder to do something about these. 126*e1fe3e4aSElliott Hughes log.warning( 127*e1fe3e4aSElliott Hughes "Dropped mapping from codepoint %#06X to glyphId '%s'", uni, gid 128*e1fe3e4aSElliott Hughes ) 129*e1fe3e4aSElliott Hughes 130*e1fe3e4aSElliott Hughes 131*e1fe3e4aSElliott Hughesdef renameCFFCharStrings(merger, glyphOrder, cffTable): 132*e1fe3e4aSElliott Hughes """Rename topDictIndex charStrings based on glyphOrder.""" 133*e1fe3e4aSElliott Hughes td = cffTable.cff.topDictIndex[0] 134*e1fe3e4aSElliott Hughes 135*e1fe3e4aSElliott Hughes charStrings = {} 136*e1fe3e4aSElliott Hughes for i, v in enumerate(td.CharStrings.charStrings.values()): 137*e1fe3e4aSElliott Hughes glyphName = glyphOrder[i] 138*e1fe3e4aSElliott Hughes charStrings[glyphName] = v 139*e1fe3e4aSElliott Hughes td.CharStrings.charStrings = charStrings 140*e1fe3e4aSElliott Hughes 141*e1fe3e4aSElliott Hughes td.charset = list(glyphOrder) 142