$ ttx -t CFF "font.otf"
Dumping "font.otf" to "font.ttx"...
Dumping 'CFF ' table...
ERROR: Unhandled exception has occurred
Traceback (most recent call last):
File "fonttools/Lib/fontTools/ttx.py", line 384, in main
process(jobs, options)
File "fonttools/Lib/fontTools/ttx.py", line 358, in process
action(input, output, options)
File "fonttools/Lib/fontTools/misc/loggingTools.py", line 372, in wrapper
return func(*args, **kwds)
File "fonttools/Lib/fontTools/ttx.py", line 258, in ttDump
newlinestr=options.newlinestr)
File "fonttools/Lib/fontTools/ttLib/__init__.py", line 311, in saveXML
self._tableToXML(tableWriter, tag, progress)
File "fonttools/Lib/fontTools/ttLib/__init__.py", line 348, in _tableToXML
table.toXML(writer, self, progress)
File "fonttools/Lib/fontTools/ttLib/tables/C_F_F_.py", line 42, in toXML
self.cff.toXML(writer, progress)
File "fonttools/Lib/fontTools/cffLib.py", line 135, in toXML
font.toXML(xmlWriter, progress)
File "fonttools/Lib/fontTools/cffLib.py", line 2178, in toXML
BaseDict.toXML(self, xmlWriter, progress)
File "fonttools/Lib/fontTools/cffLib.py", line 2128, in toXML
conv.xmlWrite(xmlWriter, name, value, progress)
File "fonttools/Lib/fontTools/cffLib.py", line 1120, in xmlWrite
value.toXML(xmlWriter, progress)
File "fonttools/Lib/fontTools/cffLib.py", line 2128, in toXML
conv.xmlWrite(xmlWriter, name, value, progress)
File "fonttools/Lib/fontTools/cffLib.py", line 1089, in xmlWrite
if isinstance(value[0], list):
IndexError: list index out of range
After 2b2aca1, DictCompiler/Decompiler's `arg_delta` method unconditionally attempts to get the first item to check if it's a list, but this fails with `IndexError` when the value is empty.
```
Traceback (most recent call last):
[...]
File "/Users/cosimolupo/Documents/Github/ufo2ft/Lib/ufo2ft/otfPostProcessor.py", line 15, in __init__
otf.save(stream)
File "/Users/cosimolupo/Documents/Github/fonttools/Lib/fontTools/ttLib/__init__.py", line 219, in save
self._writeTable(tag, writer, done)
File "/Users/cosimolupo/Documents/Github/fonttools/Lib/fontTools/ttLib/__init__.py", line 658, in _writeTable
tabledata = self.getTableData(tag)
File "/Users/cosimolupo/Documents/Github/fonttools/Lib/fontTools/ttLib/__init__.py", line 669, in getTableData
return self.tables[tag].compile(self)
File "/Users/cosimolupo/Documents/Github/fonttools/Lib/fontTools/ttLib/tables/C_F_F_.py", line 20, in compile
self.cff.compile(f, otFont)
File "/Users/cosimolupo/Documents/Github/fonttools/Lib/fontTools/cffLib.py", line 124, in compile
writer.toFile(file)
File "/Users/cosimolupo/Documents/Github/fonttools/Lib/fontTools/cffLib.py", line 300, in toFile
endPos = pos + item.getDataLength()
File "/Users/cosimolupo/Documents/Github/fonttools/Lib/fontTools/cffLib.py", line 1858, in getDataLength
return len(self.compile("getDataLength"))
File "/Users/cosimolupo/Documents/Github/fonttools/Lib/fontTools/cffLib.py", line 1879, in compile
data.append(arghandler(value))
File "/Users/cosimolupo/Documents/Github/fonttools/Lib/fontTools/cffLib.py", line 1910, in arg_delta
val0 = value[0]
IndexError: list index out of range
``
This is just to make the tests pass, otherwise the following re.sub call fails with TypeError on Python 3.
However, I wonder why we need to replace all newlines with spaces in 'Notice' and 'Copyright' fields?
```
if name in ['Notice', 'Copyright']:
value = re.sub(r"[\r\n]\s+", " ", value)
```
We shall ask Read about this.
Commits 3063def and 5b47971 introduced a separate fontDictOperators list for FontDict, only listing those TopDict key/value pairs that are actually used in the FontDict context. It provided a fallback that TTX files containing such "useless" key/value pairs would not be rejected.
However, the code still rejected binary fonts that contained such values, even though it didn't before, and yes, such fonts exist. Also: such fonts are not broken per spec, they just contain some fields that otherwise no one ever looks at, so it's a little harsh to reject them.
This patch removes most of the special FontDict code, and uses everything from TopDict, *except* the order attribute: it sets that to a list of the relevant keys for the FontDict. The effect of this is that "useless" key/value pairs are ignored, not just upon reading XML, but also upon decompilation and compilation of binary fonts. It improves on the previous XML reading behavior in that it no longer silently ignores key typos in the TTX input.
Ideally, we would *output* everything that is actually in the FontDict to TTX, and only ignore the values when compiling, but I didn't find a clean solution for that, so I decided to just fix the issue.
I noticed this issue while porting compreffor to py3. In my test fonts, the binary
CFF tables as generated with python 2 sometimes were slightly different from the
ones generated with python 3, although the TTX dump was identical!
It turns out, when running in Python 3, cffLib adds extra entries to the
list of CFF indexed strings, because of bytes vs str.
The `IndexedStrings.getSID` method takes an input string 's' and and returns
the SID integer for that string. If it's a new string, it gets appended to the
list, as well as to an internal strings-to-SID mapping, so that the same SID
value is returned for any given string.
The problem with python 3 was that, if the input string was of `bytes` type
instead of `str`, then the test for inclusion (the dict's `__contains__`)
would return False, and as a result the "same" string (e.g. "Regular" and
b"Regular") could be encoded twice in the list of CFF strings.
(yes, we desperately need unit tests for cffLib...)
All these debug messages were disabled, and I don't wish to re-enable them
while running TTX in verbose mode. So here I use a custom level less than
logging.DEBUGm to make sure they will be muted even when the logger's level
is equal to logging.DEBUG.
The latter hits the __eq__ method and can fail because we now
do not allow comparing objects of different types.
For example, was failing subsetting Andika-R.ttf.