diff options
Diffstat (limited to 'Lib/fontTools/merge/cmap.py')
-rw-r--r-- | Lib/fontTools/merge/cmap.py | 214 |
1 files changed, 113 insertions, 101 deletions
diff --git a/Lib/fontTools/merge/cmap.py b/Lib/fontTools/merge/cmap.py index 7d98b588..3209a5d7 100644 --- a/Lib/fontTools/merge/cmap.py +++ b/Lib/fontTools/merge/cmap.py @@ -11,119 +11,131 @@ log = logging.getLogger("fontTools.merge") def computeMegaGlyphOrder(merger, glyphOrders): - """Modifies passed-in glyphOrders to reflect new glyph names. + """Modifies passed-in glyphOrders to reflect new glyph names. Stores merger.glyphOrder.""" - megaOrder = {} - for glyphOrder in glyphOrders: - for i,glyphName in enumerate(glyphOrder): - if glyphName in megaOrder: - n = megaOrder[glyphName] - while (glyphName + "." + repr(n)) in megaOrder: - n += 1 - megaOrder[glyphName] = n - glyphName += "." + repr(n) - glyphOrder[i] = glyphName - megaOrder[glyphName] = 1 - merger.glyphOrder = megaOrder = list(megaOrder.keys()) - - -def _glyphsAreSame(glyphSet1, glyphSet2, glyph1, glyph2, - advanceTolerance=.05, - advanceToleranceEmpty=.20): - pen1 = DecomposingRecordingPen(glyphSet1) - pen2 = DecomposingRecordingPen(glyphSet2) - g1 = glyphSet1[glyph1] - g2 = glyphSet2[glyph2] - g1.draw(pen1) - g2.draw(pen2) - if pen1.value != pen2.value: - return False - # Allow more width tolerance for glyphs with no ink - tolerance = advanceTolerance if pen1.value else advanceToleranceEmpty + megaOrder = {} + for glyphOrder in glyphOrders: + for i, glyphName in enumerate(glyphOrder): + if glyphName in megaOrder: + n = megaOrder[glyphName] + while (glyphName + "." + repr(n)) in megaOrder: + n += 1 + megaOrder[glyphName] = n + glyphName += "." + repr(n) + glyphOrder[i] = glyphName + megaOrder[glyphName] = 1 + merger.glyphOrder = megaOrder = list(megaOrder.keys()) + + +def _glyphsAreSame( + glyphSet1, + glyphSet2, + glyph1, + glyph2, + advanceTolerance=0.05, + advanceToleranceEmpty=0.20, +): + pen1 = DecomposingRecordingPen(glyphSet1) + pen2 = DecomposingRecordingPen(glyphSet2) + g1 = glyphSet1[glyph1] + g2 = glyphSet2[glyph2] + g1.draw(pen1) + g2.draw(pen2) + if pen1.value != pen2.value: + return False + # Allow more width tolerance for glyphs with no ink + tolerance = advanceTolerance if pen1.value else advanceToleranceEmpty # TODO Warn if advances not the same but within tolerance. - if abs(g1.width - g2.width) > g1.width * tolerance: - return False - if hasattr(g1, 'height') and g1.height is not None: - if abs(g1.height - g2.height) > g1.height * tolerance: - return False - return True + if abs(g1.width - g2.width) > g1.width * tolerance: + return False + if hasattr(g1, "height") and g1.height is not None: + if abs(g1.height - g2.height) > g1.height * tolerance: + return False + return True + # Valid (format, platformID, platEncID) triplets for cmap subtables containing # Unicode BMP-only and Unicode Full Repertoire semantics. # Cf. OpenType spec for "Platform specific encodings": # https://docs.microsoft.com/en-us/typography/opentype/spec/name class _CmapUnicodePlatEncodings: - BMP = {(4, 3, 1), (4, 0, 3), (4, 0, 4), (4, 0, 6)} - FullRepertoire = {(12, 3, 10), (12, 0, 4), (12, 0, 6)} + BMP = {(4, 3, 1), (4, 0, 3), (4, 0, 4), (4, 0, 6)} + FullRepertoire = {(12, 3, 10), (12, 0, 4), (12, 0, 6)} + def computeMegaCmap(merger, cmapTables): - """Sets merger.cmap and merger.glyphOrder.""" - - # TODO Handle format=14. - # Only merge format 4 and 12 Unicode subtables, ignores all other subtables - # If there is a format 12 table for a font, ignore the format 4 table of it - chosenCmapTables = [] - for fontIdx,table in enumerate(cmapTables): - format4 = None - format12 = None - for subtable in table.tables: - properties = (subtable.format, subtable.platformID, subtable.platEncID) - if properties in _CmapUnicodePlatEncodings.BMP: - format4 = subtable - elif properties in _CmapUnicodePlatEncodings.FullRepertoire: - format12 = subtable - else: - log.warning( - "Dropped cmap subtable from font '%s':\t" - "format %2s, platformID %2s, platEncID %2s", - fontIdx, subtable.format, subtable.platformID, subtable.platEncID - ) - if format12 is not None: - chosenCmapTables.append((format12, fontIdx)) - elif format4 is not None: - chosenCmapTables.append((format4, fontIdx)) - - # Build the unicode mapping - merger.cmap = cmap = {} - fontIndexForGlyph = {} - glyphSets = [None for f in merger.fonts] if hasattr(merger, 'fonts') else None - - for table,fontIdx in chosenCmapTables: - # handle duplicates - for uni,gid in table.cmap.items(): - oldgid = cmap.get(uni, None) - if oldgid is None: - cmap[uni] = gid - fontIndexForGlyph[gid] = fontIdx - elif is_Default_Ignorable(uni) or uni in (0x25CC,): # U+25CC DOTTED CIRCLE - continue - elif oldgid != gid: - # Char previously mapped to oldgid, now to gid. - # Record, to fix up in GSUB 'locl' later. - if merger.duplicateGlyphsPerFont[fontIdx].get(oldgid) is None: - if glyphSets is not None: - oldFontIdx = fontIndexForGlyph[oldgid] - for idx in (fontIdx, oldFontIdx): - if glyphSets[idx] is None: - glyphSets[idx] = merger.fonts[idx].getGlyphSet() - #if _glyphsAreSame(glyphSets[oldFontIdx], glyphSets[fontIdx], oldgid, gid): - # continue - merger.duplicateGlyphsPerFont[fontIdx][oldgid] = gid - elif merger.duplicateGlyphsPerFont[fontIdx][oldgid] != gid: - # Char previously mapped to oldgid but oldgid is already remapped to a different - # gid, because of another Unicode character. - # TODO: Try harder to do something about these. - log.warning("Dropped mapping from codepoint %#06X to glyphId '%s'", uni, gid) + """Sets merger.cmap and merger.glyphOrder.""" + + # TODO Handle format=14. + # Only merge format 4 and 12 Unicode subtables, ignores all other subtables + # If there is a format 12 table for a font, ignore the format 4 table of it + chosenCmapTables = [] + for fontIdx, table in enumerate(cmapTables): + format4 = None + format12 = None + for subtable in table.tables: + properties = (subtable.format, subtable.platformID, subtable.platEncID) + if properties in _CmapUnicodePlatEncodings.BMP: + format4 = subtable + elif properties in _CmapUnicodePlatEncodings.FullRepertoire: + format12 = subtable + else: + log.warning( + "Dropped cmap subtable from font '%s':\t" + "format %2s, platformID %2s, platEncID %2s", + fontIdx, + subtable.format, + subtable.platformID, + subtable.platEncID, + ) + if format12 is not None: + chosenCmapTables.append((format12, fontIdx)) + elif format4 is not None: + chosenCmapTables.append((format4, fontIdx)) + + # Build the unicode mapping + merger.cmap = cmap = {} + fontIndexForGlyph = {} + glyphSets = [None for f in merger.fonts] if hasattr(merger, "fonts") else None + + for table, fontIdx in chosenCmapTables: + # handle duplicates + for uni, gid in table.cmap.items(): + oldgid = cmap.get(uni, None) + if oldgid is None: + cmap[uni] = gid + fontIndexForGlyph[gid] = fontIdx + elif is_Default_Ignorable(uni) or uni in (0x25CC,): # U+25CC DOTTED CIRCLE + continue + elif oldgid != gid: + # Char previously mapped to oldgid, now to gid. + # Record, to fix up in GSUB 'locl' later. + if merger.duplicateGlyphsPerFont[fontIdx].get(oldgid) is None: + if glyphSets is not None: + oldFontIdx = fontIndexForGlyph[oldgid] + for idx in (fontIdx, oldFontIdx): + if glyphSets[idx] is None: + glyphSets[idx] = merger.fonts[idx].getGlyphSet() + # if _glyphsAreSame(glyphSets[oldFontIdx], glyphSets[fontIdx], oldgid, gid): + # continue + merger.duplicateGlyphsPerFont[fontIdx][oldgid] = gid + elif merger.duplicateGlyphsPerFont[fontIdx][oldgid] != gid: + # Char previously mapped to oldgid but oldgid is already remapped to a different + # gid, because of another Unicode character. + # TODO: Try harder to do something about these. + log.warning( + "Dropped mapping from codepoint %#06X to glyphId '%s'", uni, gid + ) def renameCFFCharStrings(merger, glyphOrder, cffTable): - """Rename topDictIndex charStrings based on glyphOrder.""" - td = cffTable.cff.topDictIndex[0] + """Rename topDictIndex charStrings based on glyphOrder.""" + td = cffTable.cff.topDictIndex[0] - charStrings = {} - for i, v in enumerate(td.CharStrings.charStrings.values()): - glyphName = glyphOrder[i] - charStrings[glyphName] = v - td.CharStrings.charStrings = charStrings + charStrings = {} + for i, v in enumerate(td.CharStrings.charStrings.values()): + glyphName = glyphOrder[i] + charStrings[glyphName] = v + td.CharStrings.charStrings = charStrings - td.charset = list(glyphOrder) + td.charset = list(glyphOrder) |