[cffLib] Move desubroutinize code to new cffLib.transforms module
This commit is contained in:
parent
65cc8bfb3c
commit
a851d02519
@ -45,93 +45,6 @@ maxStackLimit = 513
|
|||||||
# maxstack operator has been deprecated. max stack is now always 513.
|
# maxstack operator has been deprecated. max stack is now always 513.
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
]
|
|
||||||
|
|
||||||
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))
|
|
||||||
|
|
||||||
|
|
||||||
class CFFFontSet(object):
|
class CFFFontSet(object):
|
||||||
"""A CFF font "file" can contain more than one font, although this is
|
"""A CFF font "file" can contain more than one font, although this is
|
||||||
extremely rare (and not allowed within OpenType fonts).
|
extremely rare (and not allowed within OpenType fonts).
|
||||||
@ -396,34 +309,9 @@ class CFFFontSet(object):
|
|||||||
_convertCFF2ToCFF(self, otFont)
|
_convertCFF2ToCFF(self, otFont)
|
||||||
|
|
||||||
def desubroutinize(self):
|
def desubroutinize(self):
|
||||||
for fontName in self.fontNames:
|
from .transforms import desubroutinize
|
||||||
font = self[fontName]
|
|
||||||
cs = font.CharStrings
|
desubroutinize(self)
|
||||||
for c in cs.values():
|
|
||||||
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
|
|
||||||
self.GlobalSubrs.clear()
|
|
||||||
|
|
||||||
|
|
||||||
class CFFWriter(object):
|
class CFFWriter(object):
|
||||||
|
115
Lib/fontTools/cffLib/transforms.py
Normal file
115
Lib/fontTools/cffLib/transforms.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
from fontTools.misc.psCharStrings import SimpleT2Decompiler
|
||||||
|
|
||||||
|
|
||||||
|
class StopHintCountEvent(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class _DesubroutinizingT2Decompiler(SimpleT2Decompiler):
|
||||||
|
stop_hintcount_ops = (
|
||||||
|
"op_hintmask",
|
||||||
|
"op_cntrmask",
|
||||||
|
"op_rmoveto",
|
||||||
|
"op_hmoveto",
|
||||||
|
"op_vmoveto",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, localSubrs, globalSubrs, private=None):
|
||||||
|
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:
|
||||||
|
SimpleT2Decompiler.execute(self, charString)
|
||||||
|
except StopHintCountEvent:
|
||||||
|
del self.callingStack[-1]
|
||||||
|
return
|
||||||
|
|
||||||
|
charString._patches = []
|
||||||
|
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
|
||||||
|
]
|
||||||
|
|
||||||
|
charString._desubroutinized = desubroutinized
|
||||||
|
del charString._patches
|
||||||
|
|
||||||
|
def op_callsubr(self, index):
|
||||||
|
subr = self.localSubrs[self.operandStack[-1] + self.localBias]
|
||||||
|
SimpleT2Decompiler.op_callsubr(self, index)
|
||||||
|
self.processSubr(index, subr)
|
||||||
|
|
||||||
|
def op_callgsubr(self, index):
|
||||||
|
subr = self.globalSubrs[self.operandStack[-1] + self.globalBias]
|
||||||
|
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):
|
||||||
|
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))
|
||||||
|
|
||||||
|
|
||||||
|
def desubroutinize(cff):
|
||||||
|
for fontName in cff.fontNames:
|
||||||
|
font = cff[fontName]
|
||||||
|
cs = font.CharStrings
|
||||||
|
for c in cs.values():
|
||||||
|
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()
|
Loading…
x
Reference in New Issue
Block a user