Update how table packing falls back to fontTools from harfbuzz.
Introduce three compilation modes: 1. Pure python: only uses the existing fonttools packing and overflow resolution. 2. Harfbuzz+python: uses harfbuzz packing and python overflow resolution. Extensions are allowed to be shared. 3. Python fallback: if harfbuzz+python runs out of resolution options, this disables extension sharing and only uses python packing. Once it succeeds control is passed back to the harfbuzz packer to produce the final packing with extension sharing enabled.
This commit is contained in:
parent
97958a95e1
commit
c63b84db7e
@ -52,6 +52,17 @@ class BaseTTXConverter(DefaultTable):
|
|||||||
self.table = tableClass()
|
self.table = tableClass()
|
||||||
self.table.decompile(reader, font)
|
self.table.decompile(reader, font)
|
||||||
|
|
||||||
|
# Pack only with fontTools, don't allow sharing between extensions.
|
||||||
|
MODE_PURE_FT = 1
|
||||||
|
|
||||||
|
# Attempt to pack with harfbuzz (allowing sharing between extensions)
|
||||||
|
# use fontTools to attempt overflow resolution.
|
||||||
|
MODE_HB_FT = 2
|
||||||
|
|
||||||
|
# Fallback if HB/FT packing gets stuck. Pack only with fontTools, don't allow sharing between
|
||||||
|
# extensions.
|
||||||
|
MODE_FT_FALLBACK = 3
|
||||||
|
|
||||||
def compile(self, font):
|
def compile(self, font):
|
||||||
"""Compiles the table into binary. Called automatically on save."""
|
"""Compiles the table into binary. Called automatically on save."""
|
||||||
|
|
||||||
@ -96,16 +107,45 @@ class BaseTTXConverter(DefaultTable):
|
|||||||
self.tableTag,
|
self.tableTag,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (use_hb_repack in (None, True)
|
||||||
|
and have_uharfbuzz
|
||||||
|
and self.tableTag in ("GSUB", "GPOS")):
|
||||||
|
mode = self.MODE_HB_FT
|
||||||
|
else:
|
||||||
|
mode = self.MODE_PURE_FT
|
||||||
|
|
||||||
hb_first_error_logged = False
|
hb_first_error_logged = False
|
||||||
|
lastOverflowRecord = None
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
writer = OTTableWriter(tableTag=self.tableTag)
|
writer = OTTableWriter(tableTag=self.tableTag)
|
||||||
self.table.compile(writer, font)
|
self.table.compile(writer, font)
|
||||||
if (
|
if mode == self.MODE_HB_FT:
|
||||||
use_hb_repack in (None, True)
|
return self.tryPackingHarfbuzz(writer, hb_first_error_logged)
|
||||||
and have_uharfbuzz
|
elif mode == self.MODE_PURE_FT:
|
||||||
and self.tableTag in ("GSUB", "GPOS")
|
return self.tryPackingFontTools(writer)
|
||||||
):
|
elif mode == self.MODE_FT_FALLBACK:
|
||||||
|
self.tryPackingFontTools(writer)
|
||||||
|
log.info("Re-enabling sharing between extensions and switching back to "
|
||||||
|
"harfbuzz+fontTools packing.")
|
||||||
|
mode = self.MODE_HB_FT
|
||||||
|
|
||||||
|
except OTLOffsetOverflowError as e:
|
||||||
|
hb_first_error_logged = True
|
||||||
|
ok = self.tryResolveOverflow(font, e, lastOverflowRecord)
|
||||||
|
lastOverflowRecord = e.value
|
||||||
|
|
||||||
|
if ok:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if mode is self.MODE_HB_FT:
|
||||||
|
log.info("Harfbuzz packing out of resolutions, disabling sharing between extensions and "
|
||||||
|
"switching to fontTools only packing.")
|
||||||
|
mode = self.MODE_FT_FALLBACK
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def tryPackingHarfbuzz(self, writer, hb_first_error_logged):
|
||||||
try:
|
try:
|
||||||
log.debug("serializing '%s' with hb.repack", self.tableTag)
|
log.debug("serializing '%s' with hb.repack", self.tableTag)
|
||||||
return writer.getAllDataUsingHarfbuzz()
|
return writer.getAllDataUsingHarfbuzz()
|
||||||
@ -119,39 +159,43 @@ class BaseTTXConverter(DefaultTable):
|
|||||||
if str(e) != "":
|
if str(e) != "":
|
||||||
error_msg += f": {e}"
|
error_msg += f": {e}"
|
||||||
log.warning(
|
log.warning(
|
||||||
"hb.repack failed to serialize '%s', reverting to "
|
"hb.repack failed to serialize '%s', attempting fonttools resolutions "
|
||||||
"pure-python serializer; the error message was: %s",
|
"; the error message was: %s",
|
||||||
self.tableTag,
|
self.tableTag,
|
||||||
error_msg,
|
error_msg,
|
||||||
)
|
)
|
||||||
hb_first_error_logged = True
|
hb_first_error_logged = True
|
||||||
return writer.getAllData(remove_duplicate=False)
|
return writer.getAllData(remove_duplicate=False)
|
||||||
|
|
||||||
|
|
||||||
|
def tryPackingFontTools(self, writer):
|
||||||
return writer.getAllData()
|
return writer.getAllData()
|
||||||
|
|
||||||
except OTLOffsetOverflowError as e:
|
|
||||||
|
|
||||||
if overflowRecord == e.value:
|
def tryResolveOverflow(self, font, e, lastOverflowRecord):
|
||||||
raise # Oh well...
|
ok = 0
|
||||||
|
if lastOverflowRecord == e.value:
|
||||||
|
# Oh well...
|
||||||
|
return ok
|
||||||
|
|
||||||
overflowRecord = e.value
|
overflowRecord = e.value
|
||||||
log.info("Attempting to fix OTLOffsetOverflowError %s", e)
|
log.info("Attempting to fix OTLOffsetOverflowError %s", e)
|
||||||
lastItem = overflowRecord
|
|
||||||
|
|
||||||
ok = 0
|
|
||||||
if overflowRecord.itemName is None:
|
if overflowRecord.itemName is None:
|
||||||
from .otTables import fixLookupOverFlows
|
from .otTables import fixLookupOverFlows
|
||||||
ok = fixLookupOverFlows(font, overflowRecord)
|
ok = fixLookupOverFlows(font, overflowRecord)
|
||||||
else:
|
else:
|
||||||
from .otTables import fixSubTableOverFlows
|
from .otTables import fixSubTableOverFlows
|
||||||
ok = fixSubTableOverFlows(font, overflowRecord)
|
ok = fixSubTableOverFlows(font, overflowRecord)
|
||||||
if not ok:
|
|
||||||
|
if ok:
|
||||||
|
return ok
|
||||||
|
|
||||||
# Try upgrading lookup to Extension and hope
|
# Try upgrading lookup to Extension and hope
|
||||||
# that cross-lookup sharing not happening would
|
# that cross-lookup sharing not happening would
|
||||||
# fix overflow...
|
# fix overflow...
|
||||||
from .otTables import fixLookupOverFlows
|
from .otTables import fixLookupOverFlows
|
||||||
ok = fixLookupOverFlows(font, overflowRecord)
|
return fixLookupOverFlows(font, overflowRecord)
|
||||||
if not ok:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def toXML(self, writer, font):
|
def toXML(self, writer, font):
|
||||||
self.table.toXML2(writer, font)
|
self.table.toXML2(writer, font)
|
||||||
@ -535,7 +579,7 @@ class OTTableWriter(object):
|
|||||||
internedTables = {}
|
internedTables = {}
|
||||||
# TODO: Restore shareExtension=True after we fix
|
# TODO: Restore shareExtension=True after we fix
|
||||||
# https://github.com/fonttools/fonttools/issues/2661
|
# https://github.com/fonttools/fonttools/issues/2661
|
||||||
self._doneWriting(internedTables, shareExtension=False)
|
self._doneWriting(internedTables, shareExtension=True)
|
||||||
tables = []
|
tables = []
|
||||||
obj_list = []
|
obj_list = []
|
||||||
done = {}
|
done = {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user