aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/subset/cff.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/subset/cff.py')
-rw-r--r--Lib/fontTools/subset/cff.py146
1 files changed, 133 insertions, 13 deletions
diff --git a/Lib/fontTools/subset/cff.py b/Lib/fontTools/subset/cff.py
index 0dcb7975..b59c6b96 100644
--- a/Lib/fontTools/subset/cff.py
+++ b/Lib/fontTools/subset/cff.py
@@ -2,10 +2,27 @@ 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.varLib.varStore import VarStoreInstancer
-from fontTools.subset.util import _add_method, _uniq_sort
+def _add_method(*clazzes):
+ """Returns a decorator function that adds a new method to one or
+ more classes."""
+ def wrapper(method):
+ done = []
+ for clazz in clazzes:
+ if clazz in done: continue # Support multiple names of a clazz
+ done.append(clazz)
+ assert clazz.__name__ != 'DefaultTable', \
+ 'Oops, table class not found.'
+ assert not hasattr(clazz, method.__name__), \
+ "Oops, class '%s' has method '%s'." % (clazz.__name__,
+ method.__name__)
+ setattr(clazz, method.__name__, method)
+ return None
+ return wrapper
+
+def _uniq_sort(l):
+ return sorted(set(l))
class _ClosureGlyphsT2Decompiler(psCharStrings.SimpleT2Decompiler):
@@ -53,7 +70,8 @@ def _empty_charstring(font, glyphName, isCFF2, ignoreWidth=False):
c, fdSelectIndex = font.CharStrings.getItemAndSelector(glyphName)
if isCFF2 or ignoreWidth:
# CFF2 charstrings have no widths nor 'endchar' operators
- c.setProgram([] if isCFF2 else ['endchar'])
+ c.decompile()
+ c.program = [] if isCFF2 else ['endchar']
else:
if hasattr(font, 'FDArray') and font.FDArray is not None:
private = font.FDArray[fdSelectIndex].Private
@@ -119,12 +137,9 @@ def subset_glyphs(self, s):
#sel.format = None
sel.format = 3
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
+ cs.charStrings = {g:indices.index(v)
+ for g,v in cs.charStrings.items()
+ if g in glyphs}
else:
cs.charStrings = {g:v
for g,v in cs.charStrings.items()
@@ -348,6 +363,86 @@ class _DehintingT2Decompiler(psCharStrings.T2WidthExtractor):
hints.status = max(hints.status, subr_hints.status)
+class StopHintCountEvent(Exception):
+ pass
+
+
+
+
+class _DesubroutinizingT2Decompiler(psCharStrings.SimpleT2Decompiler):
+ stop_hintcount_ops = ("op_hintmask", "op_cntrmask", "op_rmoveto", "op_hmoveto",
+ "op_vmoveto")
+
+ def __init__(self, localSubrs, globalSubrs, private=None):
+ psCharStrings.SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs,
+ private)
+
+ def execute(self, charString):
+ self.need_hintcount = True # until proven otherwise
+ for op_name in self.stop_hintcount_ops:
+ setattr(self, op_name, self.stop_hint_count)
+
+ if hasattr(charString, '_desubroutinized'):
+ # If a charstring has already been desubroutinized, we will still
+ # need to execute it if we need to count hints in order to
+ # compute the byte length for mask arguments, and haven't finished
+ # counting hints pairs.
+ if self.need_hintcount and self.callingStack:
+ try:
+ psCharStrings.SimpleT2Decompiler.execute(self, charString)
+ except StopHintCountEvent:
+ del self.callingStack[-1]
+ return
+
+ charString._patches = []
+ psCharStrings.SimpleT2Decompiler.execute(self, charString)
+ desubroutinized = charString.program[:]
+ for idx, expansion in reversed(charString._patches):
+ assert idx >= 2
+ assert desubroutinized[idx - 1] in ['callsubr', 'callgsubr'], desubroutinized[idx - 1]
+ assert type(desubroutinized[idx - 2]) == int
+ if expansion[-1] == 'return':
+ expansion = expansion[:-1]
+ desubroutinized[idx-2:idx] = expansion
+ if not self.private.in_cff2:
+ if 'endchar' in desubroutinized:
+ # Cut off after first endchar
+ desubroutinized = desubroutinized[:desubroutinized.index('endchar') + 1]
+ else:
+ if not len(desubroutinized) or desubroutinized[-1] != 'return':
+ desubroutinized.append('return')
+
+ charString._desubroutinized = desubroutinized
+ del charString._patches
+
+ def op_callsubr(self, index):
+ subr = self.localSubrs[self.operandStack[-1]+self.localBias]
+ psCharStrings.SimpleT2Decompiler.op_callsubr(self, index)
+ self.processSubr(index, subr)
+
+ def op_callgsubr(self, index):
+ subr = self.globalSubrs[self.operandStack[-1]+self.globalBias]
+ psCharStrings.SimpleT2Decompiler.op_callgsubr(self, index)
+ self.processSubr(index, subr)
+
+ def stop_hint_count(self, *args):
+ self.need_hintcount = False
+ for op_name in self.stop_hintcount_ops:
+ setattr(self, op_name, None)
+ cs = self.callingStack[-1]
+ if hasattr(cs, '_desubroutinized'):
+ raise StopHintCountEvent()
+
+ def op_hintmask(self, index):
+ psCharStrings.SimpleT2Decompiler.op_hintmask(self, index)
+ if self.need_hintcount:
+ self.stop_hint_count()
+
+ def processSubr(self, index, subr):
+ cs = self.callingStack[-1]
+ if not hasattr(cs, '_desubroutinized'):
+ cs._patches.append((index, subr._desubroutinized))
+
@_add_method(ttLib.getTableClass('CFF '))
def prune_post_subset(self, ttfFont, options):
@@ -367,7 +462,7 @@ def prune_post_subset(self, ttfFont, options):
# Desubroutinize if asked for
if options.desubroutinize:
- cff.desubroutinize()
+ self.desubroutinize()
# Drop hints if not needed
if not options.hinting:
@@ -383,11 +478,36 @@ def _delete_empty_subrs(private_dict):
del private_dict.rawDict['Subrs']
del private_dict.Subrs
-
-@deprecateFunction("use 'CFFFontSet.desubroutinize()' instead", category=DeprecationWarning)
@_add_method(ttLib.getTableClass('CFF '))
def desubroutinize(self):
- self.cff.desubroutinize()
+ cff = self.cff
+ for fontname in cff.keys():
+ font = cff[fontname]
+ cs = font.CharStrings
+ for g in font.charset:
+ c, _ = cs.getItemAndSelector(g)
+ c.decompile()
+ subrs = getattr(c.private, "Subrs", [])
+ decompiler = _DesubroutinizingT2Decompiler(subrs, c.globalSubrs, c.private)
+ decompiler.execute(c)
+ c.program = c._desubroutinized
+ del c._desubroutinized
+ # Delete all the local subrs
+ if hasattr(font, 'FDArray'):
+ for fd in font.FDArray:
+ pd = fd.Private
+ if hasattr(pd, 'Subrs'):
+ del pd.Subrs
+ if 'Subrs' in pd.rawDict:
+ del pd.rawDict['Subrs']
+ else:
+ pd = font.Private
+ if hasattr(pd, 'Subrs'):
+ del pd.Subrs
+ if 'Subrs' in pd.rawDict:
+ del pd.rawDict['Subrs']
+ # as well as the global subrs
+ cff.GlobalSubrs.clear()
@_add_method(ttLib.getTableClass('CFF '))