| from fontTools.misc import psCharStrings |
| from fontTools import ttLib |
| from fontTools.pens.basePen import NullPen |
| from fontTools.misc.roundTools import otRound |
| from fontTools.misc.loggingTools import deprecateFunction |
| from fontTools.subset.util import _add_method, _uniq_sort |
|
|
|
|
| class _ClosureGlyphsT2Decompiler(psCharStrings.SimpleT2Decompiler): |
| def __init__(self, components, localSubrs, globalSubrs): |
| psCharStrings.SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs) |
| self.components = components |
|
|
| def op_endchar(self, index): |
| args = self.popall() |
| if len(args) >= 4: |
| from fontTools.encodings.StandardEncoding import StandardEncoding |
|
|
| |
| |
| adx, ady, bchar, achar = args[-4:] |
| baseGlyph = StandardEncoding[bchar] |
| accentGlyph = StandardEncoding[achar] |
| self.components.add(baseGlyph) |
| self.components.add(accentGlyph) |
|
|
|
|
| @_add_method(ttLib.getTableClass("CFF ")) |
| def closure_glyphs(self, s): |
| cff = self.cff |
| assert len(cff) == 1 |
| font = cff[cff.keys()[0]] |
| glyphSet = font.CharStrings |
|
|
| decompose = s.glyphs |
| while decompose: |
| components = set() |
| for g in decompose: |
| if g not in glyphSet: |
| continue |
| gl = glyphSet[g] |
|
|
| subrs = getattr(gl.private, "Subrs", []) |
| decompiler = _ClosureGlyphsT2Decompiler(components, subrs, gl.globalSubrs) |
| decompiler.execute(gl) |
| components -= s.glyphs |
| s.glyphs.update(components) |
| decompose = components |
|
|
|
|
| def _empty_charstring(font, glyphName, isCFF2, ignoreWidth=False): |
| c, fdSelectIndex = font.CharStrings.getItemAndSelector(glyphName) |
| if isCFF2 or ignoreWidth: |
| |
| c.setProgram([] if isCFF2 else ["endchar"]) |
| else: |
| if hasattr(font, "FDArray") and font.FDArray is not None: |
| private = font.FDArray[fdSelectIndex].Private |
| else: |
| private = font.Private |
| dfltWdX = private.defaultWidthX |
| nmnlWdX = private.nominalWidthX |
| pen = NullPen() |
| c.draw(pen) |
| if c.width != dfltWdX: |
| c.program = [c.width - nmnlWdX, "endchar"] |
| else: |
| c.program = ["endchar"] |
|
|
|
|
| @_add_method(ttLib.getTableClass("CFF ")) |
| def prune_pre_subset(self, font, options): |
| cff = self.cff |
| |
| cff.fontNames = cff.fontNames[:1] |
|
|
| if options.notdef_glyph and not options.notdef_outline: |
| isCFF2 = cff.major > 1 |
| for fontname in cff.keys(): |
| font = cff[fontname] |
| _empty_charstring(font, ".notdef", isCFF2=isCFF2) |
|
|
| |
| for fontname in cff.keys(): |
| font = cff[fontname] |
| |
| font.Encoding = "StandardEncoding" |
|
|
| return True |
|
|
|
|
| @_add_method(ttLib.getTableClass("CFF ")) |
| def subset_glyphs(self, s): |
| cff = self.cff |
| for fontname in cff.keys(): |
| font = cff[fontname] |
| cs = font.CharStrings |
|
|
| glyphs = s.glyphs.union(s.glyphs_emptied) |
|
|
| |
| for g in font.charset: |
| if g not in glyphs: |
| continue |
| c, _ = cs.getItemAndSelector(g) |
|
|
| if cs.charStringsAreIndexed: |
| indices = [i for i, g in enumerate(font.charset) if g in glyphs] |
| csi = cs.charStringsIndex |
| csi.items = [csi.items[i] for i in indices] |
| del csi.file, csi.offsets |
| if hasattr(font, "FDSelect"): |
| sel = font.FDSelect |
| sel.format = None |
| sel.gidArray = [sel.gidArray[i] for i in indices] |
| newCharStrings = {} |
| for indicesIdx, charsetIdx in enumerate(indices): |
| g = font.charset[charsetIdx] |
| if g in cs.charStrings: |
| newCharStrings[g] = indicesIdx |
| cs.charStrings = newCharStrings |
| else: |
| cs.charStrings = {g: v for g, v in cs.charStrings.items() if g in glyphs} |
| font.charset = [g for g in font.charset if g in glyphs] |
| font.numGlyphs = len(font.charset) |
|
|
| if s.options.retain_gids: |
| isCFF2 = cff.major > 1 |
| for g in s.glyphs_emptied: |
| _empty_charstring(font, g, isCFF2=isCFF2, ignoreWidth=True) |
|
|
| return True |
|
|
|
|
| @_add_method(ttLib.getTableClass("CFF ")) |
| def prune_post_subset(self, ttfFont, options): |
| cff = self.cff |
| for fontname in cff.keys(): |
| font = cff[fontname] |
| cs = font.CharStrings |
|
|
| |
| if hasattr(font, "FDSelect"): |
| sel = font.FDSelect |
| indices = _uniq_sort(sel.gidArray) |
| sel.gidArray = [indices.index(ss) for ss in sel.gidArray] |
| arr = font.FDArray |
| arr.items = [arr[i] for i in indices] |
| del arr.file, arr.offsets |
|
|
| |
| if options.desubroutinize: |
| cff.desubroutinize() |
|
|
| |
| if not options.hinting: |
| self.remove_hints() |
| elif not options.desubroutinize: |
| self.remove_unused_subroutines() |
| return True |
|
|
|
|
| @deprecateFunction( |
| "use 'CFFFontSet.desubroutinize()' instead", category=DeprecationWarning |
| ) |
| @_add_method(ttLib.getTableClass("CFF ")) |
| def desubroutinize(self): |
| self.cff.desubroutinize() |
|
|
|
|
| @deprecateFunction( |
| "use 'CFFFontSet.remove_hints()' instead", category=DeprecationWarning |
| ) |
| @_add_method(ttLib.getTableClass("CFF ")) |
| def remove_hints(self): |
| self.cff.remove_hints() |
|
|
|
|
| @deprecateFunction( |
| "use 'CFFFontSet.remove_unused_subroutines' instead", category=DeprecationWarning |
| ) |
| @_add_method(ttLib.getTableClass("CFF ")) |
| def remove_unused_subroutines(self): |
| self.cff.remove_unused_subroutines() |
|
|