"""CFF to CFF2 converter.""" from fontTools.ttLib import TTFont from fontTools.ttLib import TTFont, newTable from fontTools.misc.cliTools import makeOutputFileName from fontTools.cffLib import ( TopDictIndex, FDArrayIndex, FontDict, buildOrder, topDictOperators, privateDictOperators, topDictOperators2, privateDictOperators2, ) from io import BytesIO __all__ = ["convertCFFToCFF2", "main"] def convertCFFToCFF2(cff, otFont): """Converts this object from CFF format to CFF2 format. This conversion is done 'in-place'. The conversion cannot be reversed. This assumes a decompiled CFF table. (i.e. that the object has been filled via :meth:`decompile` and e.g. not loaded from XML.)""" cff.major = 2 cff2GetGlyphOrder = cff.otFont.getGlyphOrder topDictData = TopDictIndex(None, cff2GetGlyphOrder) for item in cff.topDictIndex: # Iterate over, such that all are decompiled topDictData.append(item) cff.topDictIndex = topDictData topDict = topDictData[0] if hasattr(topDict, "Private"): privateDict = topDict.Private else: privateDict = None opOrder = buildOrder(topDictOperators2) topDict.order = opOrder topDict.cff2GetGlyphOrder = cff2GetGlyphOrder for entry in topDictOperators: key = entry[1] if key not in opOrder: if key in topDict.rawDict: del topDict.rawDict[key] if hasattr(topDict, key): delattr(topDict, key) if not hasattr(topDict, "FDArray"): fdArray = topDict.FDArray = FDArrayIndex() fdArray.strings = None fdArray.GlobalSubrs = topDict.GlobalSubrs topDict.GlobalSubrs.fdArray = fdArray charStrings = topDict.CharStrings if charStrings.charStringsAreIndexed: charStrings.charStringsIndex.fdArray = fdArray else: charStrings.fdArray = fdArray fontDict = FontDict() fontDict.setCFF2(True) fdArray.append(fontDict) fontDict.Private = privateDict privateOpOrder = buildOrder(privateDictOperators2) for entry in privateDictOperators: key = entry[1] if key not in privateOpOrder: if key in privateDict.rawDict: # print "Removing private dict", key del privateDict.rawDict[key] if hasattr(privateDict, key): delattr(privateDict, key) # print "Removing privateDict attr", key else: # clean up the PrivateDicts in the fdArray fdArray = topDict.FDArray privateOpOrder = buildOrder(privateDictOperators2) for fontDict in fdArray: fontDict.setCFF2(True) for key in fontDict.rawDict.keys(): if key not in fontDict.order: del fontDict.rawDict[key] if hasattr(fontDict, key): delattr(fontDict, key) privateDict = fontDict.Private for entry in privateDictOperators: key = entry[1] if key not in privateOpOrder: if key in privateDict.rawDict: # print "Removing private dict", key del privateDict.rawDict[key] if hasattr(privateDict, key): delattr(privateDict, key) # print "Removing privateDict attr", key # TODO(behdad): What does the following comment even mean? Both CFF and CFF2 # use the same T2Charstring class. # What I see missing is dropping the endchar and return operators... # At this point, the Subrs and Charstrings are all still T2Charstring class # easiest to fix this by compiling, then decompiling again file = BytesIO() cff.compile(file, otFont, isCFF2=True) file.seek(0) cff.decompile(file, otFont, isCFF2=True) def main(args=None): """Convert CFF OTF font to CFF2 OTF font""" if args is None: import sys args = sys.argv[1:] import argparse parser = argparse.ArgumentParser( "fonttools cffLib.CFFToCFF2", description="Upgrade a CFF font to CFF2.", ) parser.add_argument( "input", metavar="INPUT.ttf", help="Input OTF file with CFF table." ) parser.add_argument( "-o", "--output", metavar="OUTPUT.ttf", default=None, help="Output instance OTF file (default: INPUT-CFF2.ttf).", ) parser.add_argument( "--no-recalc-timestamp", dest="recalc_timestamp", action="store_false", help="Don't set the output font's timestamp to the current time.", ) options = parser.parse_args(args) import os infile = options.input if not os.path.isfile(infile): parser.error("No such file '{}'".format(infile)) outfile = ( makeOutputFileName(infile, overWrite=True, suffix="-CFF2") if not options.output else options.output ) font = TTFont(infile) cff = font["CFF "].cff del font["CFF "] cff.convertCFFToCFF2(font) table = font["CFF2"] = newTable("CFF2") table.cff = cff if __name__ == "__main__": import sys sys.exit(main(sys.argv[1:]))