aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/merge/cmap.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/merge/cmap.py')
-rw-r--r--Lib/fontTools/merge/cmap.py129
1 files changed, 129 insertions, 0 deletions
diff --git a/Lib/fontTools/merge/cmap.py b/Lib/fontTools/merge/cmap.py
new file mode 100644
index 00000000..7ade4ac9
--- /dev/null
+++ b/Lib/fontTools/merge/cmap.py
@@ -0,0 +1,129 @@
+# Copyright 2013 Google, Inc. All Rights Reserved.
+#
+# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
+
+from fontTools.merge.unicode import is_Default_Ignorable
+from fontTools.pens.recordingPen import DecomposingRecordingPen
+import logging
+
+
+log = logging.getLogger("fontTools.merge")
+
+
+def computeMegaGlyphOrder(merger, glyphOrders):
+ """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
+ # 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
+
+# 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)}
+
+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)
+
+
+def renameCFFCharStrings(merger, glyphOrder, cffTable):
+ """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
+
+ td.charset = list(glyphOrder)