diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-03-31 18:51:49 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-03-31 18:51:49 +0000 |
commit | 02a5f1e78c4b1b2ed412593f39612f50bbd624bd (patch) | |
tree | 98d81cb66669c50af608fd8a844e22039a00f5ae /Lib/fontTools/cffLib/__init__.py | |
parent | ec30550b149d3ba5c81fc88f2f90dd171b3f4013 (diff) | |
parent | 29956f91d34a6e7e114e9e04c4c22296e20b80c8 (diff) | |
download | fonttools-02a5f1e78c4b1b2ed412593f39612f50bbd624bd.tar.gz |
Snap for 8389696 from 29956f91d34a6e7e114e9e04c4c22296e20b80c8 to tm-d2-releaseandroid-13.0.0_r55android13-d2-release
Change-Id: Ia7295471df01d224f7e034f9ea778aca269e4f86
Diffstat (limited to 'Lib/fontTools/cffLib/__init__.py')
-rw-r--r-- | Lib/fontTools/cffLib/__init__.py | 111 |
1 files changed, 109 insertions, 2 deletions
diff --git a/Lib/fontTools/cffLib/__init__.py b/Lib/fontTools/cffLib/__init__.py index d4cd7a17..07d0d513 100644 --- a/Lib/fontTools/cffLib/__init__.py +++ b/Lib/fontTools/cffLib/__init__.py @@ -11,11 +11,10 @@ the demands of variable fonts. This module parses both original CFF and CFF2. """ -from fontTools.misc.py23 import bytechr, byteord, bytesjoin, tobytes, tostr from fontTools.misc import sstruct from fontTools.misc import psCharStrings from fontTools.misc.arrayTools import unionRect, intRect -from fontTools.misc.textTools import safeEval +from fontTools.misc.textTools import bytechr, byteord, bytesjoin, tobytes, tostr, safeEval from fontTools.ttLib import TTFont from fontTools.ttLib.tables.otBase import OTTableWriter from fontTools.ttLib.tables.otBase import OTTableReader @@ -39,6 +38,85 @@ maxStackLimit = 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] + 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)) + + class CFFFontSet(object): """A CFF font "file" can contain more than one font, although this is extremely rare (and not allowed within OpenType fonts). @@ -369,6 +447,35 @@ class CFFFontSet(object): file.seek(0) self.decompile(file, otFont, isCFF2=True) + def desubroutinize(self): + for fontName in self.fontNames: + font = self[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 + self.GlobalSubrs.clear() + class CFFWriter(object): """Helper class for serializing CFF data to binary. Used by |