Merge pull request #3525 from fonttools/cff-converters-fixes
CFF converters fixes
This commit is contained in:
commit
ed7701a0a2
@ -2,7 +2,13 @@
|
||||
|
||||
from fontTools.ttLib import TTFont, newTable
|
||||
from fontTools.misc.cliTools import makeOutputFileName
|
||||
from fontTools.cffLib import TopDictIndex, buildOrder, topDictOperators
|
||||
from fontTools.cffLib import (
|
||||
TopDictIndex,
|
||||
buildOrder,
|
||||
buildDefaults,
|
||||
topDictOperators,
|
||||
privateDictOperators,
|
||||
)
|
||||
from .width import optimizeWidths
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
@ -36,16 +42,32 @@ def _convertCFF2ToCFF(cff, otFont):
|
||||
if hasattr(topDict, "VarStore"):
|
||||
raise ValueError("Variable CFF2 font cannot be converted to CFF format.")
|
||||
|
||||
if hasattr(topDict, "Private"):
|
||||
privateDict = topDict.Private
|
||||
else:
|
||||
privateDict = None
|
||||
opOrder = buildOrder(topDictOperators)
|
||||
topDict.order = opOrder
|
||||
for key in topDict.rawDict.keys():
|
||||
if key not in opOrder:
|
||||
del topDict.rawDict[key]
|
||||
if hasattr(topDict, key):
|
||||
delattr(topDict, key)
|
||||
|
||||
fdArray = topDict.FDArray
|
||||
charStrings = topDict.CharStrings
|
||||
|
||||
defaults = buildDefaults(privateDictOperators)
|
||||
order = buildOrder(privateDictOperators)
|
||||
for fd in fdArray:
|
||||
fd.setCFF2(False)
|
||||
privateDict = fd.Private
|
||||
privateDict.order = order
|
||||
for key in order:
|
||||
if key not in privateDict.rawDict and key in defaults:
|
||||
privateDict.rawDict[key] = defaults[key]
|
||||
for key in privateDict.rawDict.keys():
|
||||
if key not in order:
|
||||
del privateDict.rawDict[key]
|
||||
if hasattr(privateDict, key):
|
||||
delattr(privateDict, key)
|
||||
|
||||
for cs in charStrings.values():
|
||||
cs.decompile()
|
||||
cs.program.append("endchar")
|
||||
|
@ -43,7 +43,15 @@ def _convertCFFToCFF2(cff, otFont):
|
||||
fdArray = topDict.FDArray if hasattr(topDict, "FDArray") else None
|
||||
charStrings = topDict.CharStrings
|
||||
globalSubrs = cff.GlobalSubrs
|
||||
localSubrs = [getattr(fd.Private, "Subrs", []) for fd in fdArray] if fdArray else []
|
||||
localSubrs = (
|
||||
[getattr(fd.Private, "Subrs", []) for fd in fdArray]
|
||||
if fdArray
|
||||
else (
|
||||
[topDict.Private.Subrs]
|
||||
if hasattr(topDict, "Private") and hasattr(topDict.Private, "Subrs")
|
||||
else []
|
||||
)
|
||||
)
|
||||
|
||||
for glyphName in charStrings.keys():
|
||||
cs, fdIndex = charStrings.getItemAndSelector(glyphName)
|
||||
@ -70,13 +78,21 @@ def _convertCFFToCFF2(cff, otFont):
|
||||
for glyphName in charStrings.keys():
|
||||
cs, fdIndex = charStrings.getItemAndSelector(glyphName)
|
||||
program = cs.program
|
||||
if fdIndex == None:
|
||||
fdIndex = 0
|
||||
|
||||
thisLocalSubrs = (
|
||||
localSubrs[fdIndex]
|
||||
if fdIndex
|
||||
else (
|
||||
getattr(topDict.Private, "Subrs", [])
|
||||
if hasattr(topDict, "Private")
|
||||
else []
|
||||
)
|
||||
)
|
||||
|
||||
# Intentionally use custom type for nominalWidthX, such that any
|
||||
# CharString that has an explicit width encoded will throw back to us.
|
||||
extractor = T2WidthExtractor(
|
||||
localSubrs[fdIndex] if localSubrs else [],
|
||||
thisLocalSubrs,
|
||||
globalSubrs,
|
||||
nominalWidthXError,
|
||||
0,
|
||||
@ -94,7 +110,7 @@ def _convertCFFToCFF2(cff, otFont):
|
||||
op = program.pop(0)
|
||||
bias = extractor.localBias if op == "callsubr" else extractor.globalBias
|
||||
subrNumber += bias
|
||||
subrSet = localSubrs[fdIndex] if op == "callsubr" else globalSubrs
|
||||
subrSet = thisLocalSubrs if op == "callsubr" else globalSubrs
|
||||
subrProgram = subrSet[subrNumber].program
|
||||
program[:0] = subrProgram
|
||||
# Now pop the actual width
|
||||
|
@ -1470,7 +1470,7 @@ class CharsetConverter(SimpleConverter):
|
||||
else: # offset == 0 -> no charset data.
|
||||
if isCID or "CharStrings" not in parent.rawDict:
|
||||
# We get here only when processing fontDicts from the FDArray of
|
||||
# CFF-CID fonts. Only the real topDict references the chrset.
|
||||
# CFF-CID fonts. Only the real topDict references the charset.
|
||||
assert value == 0
|
||||
charset = None
|
||||
elif value == 0:
|
||||
|
@ -342,7 +342,7 @@ def _cs_drop_hints(charstring):
|
||||
del charstring._hints
|
||||
|
||||
|
||||
def remove_hints(cff):
|
||||
def remove_hints(cff, *, removeUnusedSubrs: bool = True):
|
||||
for fontname in cff.keys():
|
||||
font = cff[fontname]
|
||||
cs = font.CharStrings
|
||||
@ -404,6 +404,7 @@ def remove_hints(cff):
|
||||
]:
|
||||
if hasattr(priv, k):
|
||||
setattr(priv, k, None)
|
||||
if removeUnusedSubrs:
|
||||
remove_unused_subroutines(cff)
|
||||
|
||||
|
||||
|
@ -5,6 +5,7 @@ import copy
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
class CffLibTest(DataFilesHandler):
|
||||
@ -119,5 +120,17 @@ class CffLibTest(DataFilesHandler):
|
||||
self.assertEqual(len(glyphOrder), len(set(glyphOrder)))
|
||||
|
||||
|
||||
class CFFToCFF2Test(DataFilesHandler):
|
||||
|
||||
def test_conversion(self):
|
||||
font_path = self.getpath("CFFToCFF2-1.otf")
|
||||
font = TTFont(font_path)
|
||||
from fontTools.cffLib.CFFToCFF2 import convertCFFToCFF2
|
||||
|
||||
convertCFFToCFF2(font)
|
||||
f = BytesIO()
|
||||
font.save(f)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(unittest.main())
|
||||
|
BIN
Tests/cffLib/data/CFFToCFF2-1.otf
Normal file
BIN
Tests/cffLib/data/CFFToCFF2-1.otf
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user