116 lines
4.0 KiB
Python
116 lines
4.0 KiB
Python
|
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()
|