|
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() |
|
|