|
|
|
|
|
|
|
|
|
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=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 |
|
|
|
tolerance = advanceTolerance if pen1.value else advanceToleranceEmpty |
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
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.""" |
|
|
|
|
|
|
|
|
|
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)) |
|
|
|
|
|
merger.cmap = cmap = {} |
|
fontIndexForGlyph = {} |
|
glyphSets = [None for f in merger.fonts] if hasattr(merger, "fonts") else None |
|
|
|
for table, fontIdx in chosenCmapTables: |
|
|
|
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,): |
|
continue |
|
elif oldgid != gid: |
|
|
|
|
|
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() |
|
|
|
|
|
merger.duplicateGlyphsPerFont[fontIdx][oldgid] = gid |
|
elif merger.duplicateGlyphsPerFont[fontIdx][oldgid] != gid: |
|
|
|
|
|
|
|
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) |
|
|