Before this change, feaLib had assigned a lookup index at the same
time as compiling each lookup. For chains, the implicit assumption was
that the chain's targets would always come before the contexual chain.
Normally this was indeed the case, but feaLib (and also makeotf)
sometimes merge several chain targets into one single lookup,
and then this assumption was not true anymore.
In the new version, the lookups get compiled in a separate pass,
after assigning lookup indices.
https://github.com/behdad/fonttools/issues/463
Ie, now we get:
...
assert 0 <= value < 0x10000, value
AssertionError: (None, 'LookupListIndex', 'SubstLookupRecord[0]', 'ChainContextSubst[1]', 'Lookup[3]', 'LookupList')
The common stacktrace like this:
File "fonttools/Lib/fontTools/ttLib/__init__.py", line 202, in save
self._writeTable(tag, writer, done)
File "fonttools/Lib/fontTools/ttLib/__init__.py", line 631, in _writeTable
tabledata = self.getTableData(tag)
File "fonttools/Lib/fontTools/ttLib/__init__.py", line 644, in getTableData
return self.tables[tag].compile(self)
File "fonttools/Lib/fontTools/ttLib/tables/otBase.py", line 86, in compile
self.table.compile(writer, font)
File "fonttools/Lib/fontTools/ttLib/tables/otBase.py", line 681, in compile
conv.write(writer, font, table, value)
File "fonttools/Lib/fontTools/ttLib/tables/otConverters.py", line 354, in write
value.compile(subWriter, font)
File "fonttools/Lib/fontTools/ttLib/tables/otBase.py", line 661, in compile
conv.write(writer, font, table, value, i)
File "fonttools/Lib/fontTools/ttLib/tables/otConverters.py", line 354, in write
value.compile(subWriter, font)
File "fonttools/Lib/fontTools/ttLib/tables/otBase.py", line 661, in compile
conv.write(writer, font, table, value, i)
File "fonttools/Lib/fontTools/ttLib/tables/otConverters.py", line 354, in write
value.compile(subWriter, font)
File "fonttools/Lib/fontTools/ttLib/tables/otBase.py", line 661, in compile
conv.write(writer, font, table, value, i)
File "fonttools/Lib/fontTools/ttLib/tables/otConverters.py", line 277, in write
value.compile(writer, font)
File "fonttools/Lib/fontTools/ttLib/tables/otBase.py", line 681, in compile
conv.write(writer, font, table, value)
File "fonttools/Lib/fontTools/ttLib/tables/otConverters.py", line 175, in write
writer.writeUShort(value)
File "fonttools/Lib/fontTools/ttLib/tables/otBase.py", line 457, in writeUShort
assert 0 <= value < 0x10000, value
AssertionError: None
now has this as the last line:
AssertionError: (None, 'LookupListIndex', 'SubstLookupRecord', 'ChainContextSubst', 'Lookup', 'LookupList')
which means a value of None was tried for writing a LookupListIndex from a ChainContextSubset...
It's a hack, but a very useful one.
Not sure if empty ComponentRecords can be optimized away.
For the time being, leaving the output unchanged so that
the refactoring has no effect on the generated files.
Although the compiler (and now XML writer) assume None if an attribute
is not set, it's extremely frustrating for other code to have to check
before accessing an attribute. So, explicitly set please!