diff --git a/Lib/fontTools/merge/__init__.py b/Lib/fontTools/merge/__init__.py index f696aef05..f639d8923 100644 --- a/Lib/fontTools/merge/__init__.py +++ b/Lib/fontTools/merge/__init__.py @@ -3,9 +3,8 @@ # Google Author(s): Behdad Esfahbod, Roozbeh Pournader from fontTools import ttLib -from fontTools.ttLib.tables import otTables -from fontTools.ttLib.tables.DefaultTable import DefaultTable from fontTools.merge.base import * +from fontTools.merge.layout import * from fontTools.merge.util import * from fontTools.misc.loggingTools import Timer from fontTools.pens.recordingPen import DecomposingRecordingPen @@ -193,373 +192,6 @@ def merge(self, m, tables): return self -def mergeLookupLists(lst): - # TODO Do smarter merge. - return sumLists(lst) - -def mergeFeatures(lst): - assert lst - self = otTables.Feature() - self.FeatureParams = None - self.LookupListIndex = mergeLookupLists([l.LookupListIndex for l in lst if l.LookupListIndex]) - self.LookupCount = len(self.LookupListIndex) - return self - -def mergeFeatureLists(lst): - d = {} - for l in lst: - for f in l: - tag = f.FeatureTag - if tag not in d: - d[tag] = [] - d[tag].append(f.Feature) - ret = [] - for tag in sorted(d.keys()): - rec = otTables.FeatureRecord() - rec.FeatureTag = tag - rec.Feature = mergeFeatures(d[tag]) - ret.append(rec) - return ret - -def mergeLangSyses(lst): - assert lst - - # TODO Support merging ReqFeatureIndex - assert all(l.ReqFeatureIndex == 0xFFFF for l in lst) - - self = otTables.LangSys() - self.LookupOrder = None - self.ReqFeatureIndex = 0xFFFF - self.FeatureIndex = mergeFeatureLists([l.FeatureIndex for l in lst if l.FeatureIndex]) - self.FeatureCount = len(self.FeatureIndex) - return self - -def mergeScripts(lst): - assert lst - - if len(lst) == 1: - return lst[0] - langSyses = {} - for sr in lst: - for lsr in sr.LangSysRecord: - if lsr.LangSysTag not in langSyses: - langSyses[lsr.LangSysTag] = [] - langSyses[lsr.LangSysTag].append(lsr.LangSys) - lsrecords = [] - for tag, langSys_list in sorted(langSyses.items()): - lsr = otTables.LangSysRecord() - lsr.LangSys = mergeLangSyses(langSys_list) - lsr.LangSysTag = tag - lsrecords.append(lsr) - - self = otTables.Script() - self.LangSysRecord = lsrecords - self.LangSysCount = len(lsrecords) - dfltLangSyses = [s.DefaultLangSys for s in lst if s.DefaultLangSys] - if dfltLangSyses: - self.DefaultLangSys = mergeLangSyses(dfltLangSyses) - else: - self.DefaultLangSys = None - return self - -def mergeScriptRecords(lst): - d = {} - for l in lst: - for s in l: - tag = s.ScriptTag - if tag not in d: - d[tag] = [] - d[tag].append(s.Script) - ret = [] - for tag in sorted(d.keys()): - rec = otTables.ScriptRecord() - rec.ScriptTag = tag - rec.Script = mergeScripts(d[tag]) - ret.append(rec) - return ret - -otTables.ScriptList.mergeMap = { - 'ScriptCount': lambda lst: None, # TODO - 'ScriptRecord': mergeScriptRecords, -} -otTables.BaseScriptList.mergeMap = { - 'BaseScriptCount': lambda lst: None, # TODO - # TODO: Merge duplicate entries - 'BaseScriptRecord': lambda lst: sorted(sumLists(lst), key=lambda s: s.BaseScriptTag), -} - -otTables.FeatureList.mergeMap = { - 'FeatureCount': sum, - 'FeatureRecord': lambda lst: sorted(sumLists(lst), key=lambda s: s.FeatureTag), -} - -otTables.LookupList.mergeMap = { - 'LookupCount': sum, - 'Lookup': sumLists, -} - -otTables.Coverage.mergeMap = { - 'Format': min, - 'glyphs': sumLists, -} - -otTables.ClassDef.mergeMap = { - 'Format': min, - 'classDefs': sumDicts, -} - -otTables.LigCaretList.mergeMap = { - 'Coverage': mergeObjects, - 'LigGlyphCount': sum, - 'LigGlyph': sumLists, -} - -otTables.AttachList.mergeMap = { - 'Coverage': mergeObjects, - 'GlyphCount': sum, - 'AttachPoint': sumLists, -} - -# XXX Renumber MarkFilterSets of lookups -otTables.MarkGlyphSetsDef.mergeMap = { - 'MarkSetTableFormat': equal, - 'MarkSetCount': sum, - 'Coverage': sumLists, -} - -otTables.Axis.mergeMap = { - '*': mergeObjects, -} - -# XXX Fix BASE table merging -otTables.BaseTagList.mergeMap = { - 'BaseTagCount': sum, - 'BaselineTag': sumLists, -} - -otTables.GDEF.mergeMap = \ -otTables.GSUB.mergeMap = \ -otTables.GPOS.mergeMap = \ -otTables.BASE.mergeMap = \ -otTables.JSTF.mergeMap = \ -otTables.MATH.mergeMap = \ -{ - '*': mergeObjects, - 'Version': max, -} - -ttLib.getTableClass('GDEF').mergeMap = \ -ttLib.getTableClass('GSUB').mergeMap = \ -ttLib.getTableClass('GPOS').mergeMap = \ -ttLib.getTableClass('BASE').mergeMap = \ -ttLib.getTableClass('JSTF').mergeMap = \ -ttLib.getTableClass('MATH').mergeMap = \ -{ - 'tableTag': onlyExisting(equal), # XXX clean me up - 'table': mergeObjects, -} - -@add_method(ttLib.getTableClass('GSUB')) -def merge(self, m, tables): - - assert len(tables) == len(m.duplicateGlyphsPerFont) - for i,(table,dups) in enumerate(zip(tables, m.duplicateGlyphsPerFont)): - if not dups: continue - if table is None or table is NotImplemented: - log.warning("Have non-identical duplicates to resolve for font %d but no GSUB. Are duplicates intended?: %s" % (i + 1, dups)) - continue - - synthFeature = None - synthLookup = None - for script in table.table.ScriptList.ScriptRecord: - if script.ScriptTag == 'DFLT': continue # XXX - for langsys in [script.Script.DefaultLangSys] + [l.LangSys for l in script.Script.LangSysRecord]: - if langsys is None: continue # XXX Create! - feature = [v for v in langsys.FeatureIndex if v.FeatureTag == 'locl'] - assert len(feature) <= 1 - if feature: - feature = feature[0] - else: - if not synthFeature: - synthFeature = otTables.FeatureRecord() - synthFeature.FeatureTag = 'locl' - f = synthFeature.Feature = otTables.Feature() - f.FeatureParams = None - f.LookupCount = 0 - f.LookupListIndex = [] - table.table.FeatureList.FeatureRecord.append(synthFeature) - table.table.FeatureList.FeatureCount += 1 - feature = synthFeature - langsys.FeatureIndex.append(feature) - langsys.FeatureIndex.sort(key=lambda v: v.FeatureTag) - - if not synthLookup: - subtable = otTables.SingleSubst() - subtable.mapping = dups - synthLookup = otTables.Lookup() - synthLookup.LookupFlag = 0 - synthLookup.LookupType = 1 - synthLookup.SubTableCount = 1 - synthLookup.SubTable = [subtable] - if table.table.LookupList is None: - # mtiLib uses None as default value for LookupList, - # while feaLib points to an empty array with count 0 - # TODO: make them do the same - table.table.LookupList = otTables.LookupList() - table.table.LookupList.Lookup = [] - table.table.LookupList.LookupCount = 0 - table.table.LookupList.Lookup.append(synthLookup) - table.table.LookupList.LookupCount += 1 - - if feature.Feature.LookupListIndex[:1] != [synthLookup]: - feature.Feature.LookupListIndex[:0] = [synthLookup] - feature.Feature.LookupCount += 1 - - DefaultTable.merge(self, m, tables) - return self - -@add_method(otTables.SingleSubst, - otTables.MultipleSubst, - otTables.AlternateSubst, - otTables.LigatureSubst, - otTables.ReverseChainSingleSubst, - otTables.SinglePos, - otTables.PairPos, - otTables.CursivePos, - otTables.MarkBasePos, - otTables.MarkLigPos, - otTables.MarkMarkPos) -def mapLookups(self, lookupMap): - pass - -# Copied and trimmed down from subset.py -@add_method(otTables.ContextSubst, - otTables.ChainContextSubst, - otTables.ContextPos, - otTables.ChainContextPos) -def __merge_classify_context(self): - - class ContextHelper(object): - def __init__(self, klass, Format): - if klass.__name__.endswith('Subst'): - Typ = 'Sub' - Type = 'Subst' - else: - Typ = 'Pos' - Type = 'Pos' - if klass.__name__.startswith('Chain'): - Chain = 'Chain' - else: - Chain = '' - ChainTyp = Chain+Typ - - self.Typ = Typ - self.Type = Type - self.Chain = Chain - self.ChainTyp = ChainTyp - - self.LookupRecord = Type+'LookupRecord' - - if Format == 1: - self.Rule = ChainTyp+'Rule' - self.RuleSet = ChainTyp+'RuleSet' - elif Format == 2: - self.Rule = ChainTyp+'ClassRule' - self.RuleSet = ChainTyp+'ClassSet' - - if self.Format not in [1, 2, 3]: - return None # Don't shoot the messenger; let it go - if not hasattr(self.__class__, "_merge__ContextHelpers"): - self.__class__._merge__ContextHelpers = {} - if self.Format not in self.__class__._merge__ContextHelpers: - helper = ContextHelper(self.__class__, self.Format) - self.__class__._merge__ContextHelpers[self.Format] = helper - return self.__class__._merge__ContextHelpers[self.Format] - - -@add_method(otTables.ContextSubst, - otTables.ChainContextSubst, - otTables.ContextPos, - otTables.ChainContextPos) -def mapLookups(self, lookupMap): - c = self.__merge_classify_context() - - if self.Format in [1, 2]: - for rs in getattr(self, c.RuleSet): - if not rs: continue - for r in getattr(rs, c.Rule): - if not r: continue - for ll in getattr(r, c.LookupRecord): - if not ll: continue - ll.LookupListIndex = lookupMap[ll.LookupListIndex] - elif self.Format == 3: - for ll in getattr(self, c.LookupRecord): - if not ll: continue - ll.LookupListIndex = lookupMap[ll.LookupListIndex] - else: - assert 0, "unknown format: %s" % self.Format - -@add_method(otTables.ExtensionSubst, - otTables.ExtensionPos) -def mapLookups(self, lookupMap): - if self.Format == 1: - self.ExtSubTable.mapLookups(lookupMap) - else: - assert 0, "unknown format: %s" % self.Format - -@add_method(otTables.Lookup) -def mapLookups(self, lookupMap): - for st in self.SubTable: - if not st: continue - st.mapLookups(lookupMap) - -@add_method(otTables.LookupList) -def mapLookups(self, lookupMap): - for l in self.Lookup: - if not l: continue - l.mapLookups(lookupMap) - -@add_method(otTables.Lookup) -def mapMarkFilteringSets(self, markFilteringSetMap): - if self.LookupFlag & 0x0010: - self.MarkFilteringSet = markFilteringSetMap[self.MarkFilteringSet] - -@add_method(otTables.LookupList) -def mapMarkFilteringSets(self, markFilteringSetMap): - for l in self.Lookup: - if not l: continue - l.mapMarkFilteringSets(markFilteringSetMap) - -@add_method(otTables.Feature) -def mapLookups(self, lookupMap): - self.LookupListIndex = [lookupMap[i] for i in self.LookupListIndex] - -@add_method(otTables.FeatureList) -def mapLookups(self, lookupMap): - for f in self.FeatureRecord: - if not f or not f.Feature: continue - f.Feature.mapLookups(lookupMap) - -@add_method(otTables.DefaultLangSys, - otTables.LangSys) -def mapFeatures(self, featureMap): - self.FeatureIndex = [featureMap[i] for i in self.FeatureIndex] - if self.ReqFeatureIndex != 65535: - self.ReqFeatureIndex = featureMap[self.ReqFeatureIndex] - -@add_method(otTables.Script) -def mapFeatures(self, featureMap): - if self.DefaultLangSys: - self.DefaultLangSys.mapFeatures(featureMap) - for l in self.LangSysRecord: - if not l or not l.LangSys: continue - l.LangSys.mapFeatures(featureMap) - -@add_method(otTables.ScriptList) -def mapFeatures(self, featureMap): - for s in self.ScriptRecord: - if not s or not s.Script: continue - s.Script.mapFeatures(featureMap) class Options(object): diff --git a/Lib/fontTools/merge/base.py b/Lib/fontTools/merge/base.py index 36a5b6fd2..233dc18b1 100644 --- a/Lib/fontTools/merge/base.py +++ b/Lib/fontTools/merge/base.py @@ -3,6 +3,7 @@ # Google Author(s): Behdad Esfahbod, Roozbeh Pournader from fontTools import ttLib, cffLib +from fontTools.ttLib.tables.DefaultTable import DefaultTable from fontTools.merge.util import * diff --git a/Lib/fontTools/merge/layout.py b/Lib/fontTools/merge/layout.py new file mode 100644 index 000000000..b80581cbc --- /dev/null +++ b/Lib/fontTools/merge/layout.py @@ -0,0 +1,378 @@ +# Copyright 2013 Google, Inc. All Rights Reserved. +# +# Google Author(s): Behdad Esfahbod, Roozbeh Pournader + +from fontTools import ttLib +from fontTools.ttLib.tables.DefaultTable import DefaultTable +from fontTools.ttLib.tables import otTables +from fontTools.merge.util import * + + + +def mergeLookupLists(lst): + # TODO Do smarter merge. + return sumLists(lst) + +def mergeFeatures(lst): + assert lst + self = otTables.Feature() + self.FeatureParams = None + self.LookupListIndex = mergeLookupLists([l.LookupListIndex for l in lst if l.LookupListIndex]) + self.LookupCount = len(self.LookupListIndex) + return self + +def mergeFeatureLists(lst): + d = {} + for l in lst: + for f in l: + tag = f.FeatureTag + if tag not in d: + d[tag] = [] + d[tag].append(f.Feature) + ret = [] + for tag in sorted(d.keys()): + rec = otTables.FeatureRecord() + rec.FeatureTag = tag + rec.Feature = mergeFeatures(d[tag]) + ret.append(rec) + return ret + +def mergeLangSyses(lst): + assert lst + + # TODO Support merging ReqFeatureIndex + assert all(l.ReqFeatureIndex == 0xFFFF for l in lst) + + self = otTables.LangSys() + self.LookupOrder = None + self.ReqFeatureIndex = 0xFFFF + self.FeatureIndex = mergeFeatureLists([l.FeatureIndex for l in lst if l.FeatureIndex]) + self.FeatureCount = len(self.FeatureIndex) + return self + +def mergeScripts(lst): + assert lst + + if len(lst) == 1: + return lst[0] + langSyses = {} + for sr in lst: + for lsr in sr.LangSysRecord: + if lsr.LangSysTag not in langSyses: + langSyses[lsr.LangSysTag] = [] + langSyses[lsr.LangSysTag].append(lsr.LangSys) + lsrecords = [] + for tag, langSys_list in sorted(langSyses.items()): + lsr = otTables.LangSysRecord() + lsr.LangSys = mergeLangSyses(langSys_list) + lsr.LangSysTag = tag + lsrecords.append(lsr) + + self = otTables.Script() + self.LangSysRecord = lsrecords + self.LangSysCount = len(lsrecords) + dfltLangSyses = [s.DefaultLangSys for s in lst if s.DefaultLangSys] + if dfltLangSyses: + self.DefaultLangSys = mergeLangSyses(dfltLangSyses) + else: + self.DefaultLangSys = None + return self + +def mergeScriptRecords(lst): + d = {} + for l in lst: + for s in l: + tag = s.ScriptTag + if tag not in d: + d[tag] = [] + d[tag].append(s.Script) + ret = [] + for tag in sorted(d.keys()): + rec = otTables.ScriptRecord() + rec.ScriptTag = tag + rec.Script = mergeScripts(d[tag]) + ret.append(rec) + return ret + +otTables.ScriptList.mergeMap = { + 'ScriptCount': lambda lst: None, # TODO + 'ScriptRecord': mergeScriptRecords, +} +otTables.BaseScriptList.mergeMap = { + 'BaseScriptCount': lambda lst: None, # TODO + # TODO: Merge duplicate entries + 'BaseScriptRecord': lambda lst: sorted(sumLists(lst), key=lambda s: s.BaseScriptTag), +} + +otTables.FeatureList.mergeMap = { + 'FeatureCount': sum, + 'FeatureRecord': lambda lst: sorted(sumLists(lst), key=lambda s: s.FeatureTag), +} + +otTables.LookupList.mergeMap = { + 'LookupCount': sum, + 'Lookup': sumLists, +} + +otTables.Coverage.mergeMap = { + 'Format': min, + 'glyphs': sumLists, +} + +otTables.ClassDef.mergeMap = { + 'Format': min, + 'classDefs': sumDicts, +} + +otTables.LigCaretList.mergeMap = { + 'Coverage': mergeObjects, + 'LigGlyphCount': sum, + 'LigGlyph': sumLists, +} + +otTables.AttachList.mergeMap = { + 'Coverage': mergeObjects, + 'GlyphCount': sum, + 'AttachPoint': sumLists, +} + +# XXX Renumber MarkFilterSets of lookups +otTables.MarkGlyphSetsDef.mergeMap = { + 'MarkSetTableFormat': equal, + 'MarkSetCount': sum, + 'Coverage': sumLists, +} + +otTables.Axis.mergeMap = { + '*': mergeObjects, +} + +# XXX Fix BASE table merging +otTables.BaseTagList.mergeMap = { + 'BaseTagCount': sum, + 'BaselineTag': sumLists, +} + +otTables.GDEF.mergeMap = \ +otTables.GSUB.mergeMap = \ +otTables.GPOS.mergeMap = \ +otTables.BASE.mergeMap = \ +otTables.JSTF.mergeMap = \ +otTables.MATH.mergeMap = \ +{ + '*': mergeObjects, + 'Version': max, +} + +ttLib.getTableClass('GDEF').mergeMap = \ +ttLib.getTableClass('GSUB').mergeMap = \ +ttLib.getTableClass('GPOS').mergeMap = \ +ttLib.getTableClass('BASE').mergeMap = \ +ttLib.getTableClass('JSTF').mergeMap = \ +ttLib.getTableClass('MATH').mergeMap = \ +{ + 'tableTag': onlyExisting(equal), # XXX clean me up + 'table': mergeObjects, +} + +@add_method(ttLib.getTableClass('GSUB')) +def merge(self, m, tables): + + assert len(tables) == len(m.duplicateGlyphsPerFont) + for i,(table,dups) in enumerate(zip(tables, m.duplicateGlyphsPerFont)): + if not dups: continue + if table is None or table is NotImplemented: + log.warning("Have non-identical duplicates to resolve for font %d but no GSUB. Are duplicates intended?: %s" % (i + 1, dups)) + continue + + synthFeature = None + synthLookup = None + for script in table.table.ScriptList.ScriptRecord: + if script.ScriptTag == 'DFLT': continue # XXX + for langsys in [script.Script.DefaultLangSys] + [l.LangSys for l in script.Script.LangSysRecord]: + if langsys is None: continue # XXX Create! + feature = [v for v in langsys.FeatureIndex if v.FeatureTag == 'locl'] + assert len(feature) <= 1 + if feature: + feature = feature[0] + else: + if not synthFeature: + synthFeature = otTables.FeatureRecord() + synthFeature.FeatureTag = 'locl' + f = synthFeature.Feature = otTables.Feature() + f.FeatureParams = None + f.LookupCount = 0 + f.LookupListIndex = [] + table.table.FeatureList.FeatureRecord.append(synthFeature) + table.table.FeatureList.FeatureCount += 1 + feature = synthFeature + langsys.FeatureIndex.append(feature) + langsys.FeatureIndex.sort(key=lambda v: v.FeatureTag) + + if not synthLookup: + subtable = otTables.SingleSubst() + subtable.mapping = dups + synthLookup = otTables.Lookup() + synthLookup.LookupFlag = 0 + synthLookup.LookupType = 1 + synthLookup.SubTableCount = 1 + synthLookup.SubTable = [subtable] + if table.table.LookupList is None: + # mtiLib uses None as default value for LookupList, + # while feaLib points to an empty array with count 0 + # TODO: make them do the same + table.table.LookupList = otTables.LookupList() + table.table.LookupList.Lookup = [] + table.table.LookupList.LookupCount = 0 + table.table.LookupList.Lookup.append(synthLookup) + table.table.LookupList.LookupCount += 1 + + if feature.Feature.LookupListIndex[:1] != [synthLookup]: + feature.Feature.LookupListIndex[:0] = [synthLookup] + feature.Feature.LookupCount += 1 + + DefaultTable.merge(self, m, tables) + return self + +@add_method(otTables.SingleSubst, + otTables.MultipleSubst, + otTables.AlternateSubst, + otTables.LigatureSubst, + otTables.ReverseChainSingleSubst, + otTables.SinglePos, + otTables.PairPos, + otTables.CursivePos, + otTables.MarkBasePos, + otTables.MarkLigPos, + otTables.MarkMarkPos) +def mapLookups(self, lookupMap): + pass + +# Copied and trimmed down from subset.py +@add_method(otTables.ContextSubst, + otTables.ChainContextSubst, + otTables.ContextPos, + otTables.ChainContextPos) +def __merge_classify_context(self): + + class ContextHelper(object): + def __init__(self, klass, Format): + if klass.__name__.endswith('Subst'): + Typ = 'Sub' + Type = 'Subst' + else: + Typ = 'Pos' + Type = 'Pos' + if klass.__name__.startswith('Chain'): + Chain = 'Chain' + else: + Chain = '' + ChainTyp = Chain+Typ + + self.Typ = Typ + self.Type = Type + self.Chain = Chain + self.ChainTyp = ChainTyp + + self.LookupRecord = Type+'LookupRecord' + + if Format == 1: + self.Rule = ChainTyp+'Rule' + self.RuleSet = ChainTyp+'RuleSet' + elif Format == 2: + self.Rule = ChainTyp+'ClassRule' + self.RuleSet = ChainTyp+'ClassSet' + + if self.Format not in [1, 2, 3]: + return None # Don't shoot the messenger; let it go + if not hasattr(self.__class__, "_merge__ContextHelpers"): + self.__class__._merge__ContextHelpers = {} + if self.Format not in self.__class__._merge__ContextHelpers: + helper = ContextHelper(self.__class__, self.Format) + self.__class__._merge__ContextHelpers[self.Format] = helper + return self.__class__._merge__ContextHelpers[self.Format] + + +@add_method(otTables.ContextSubst, + otTables.ChainContextSubst, + otTables.ContextPos, + otTables.ChainContextPos) +def mapLookups(self, lookupMap): + c = self.__merge_classify_context() + + if self.Format in [1, 2]: + for rs in getattr(self, c.RuleSet): + if not rs: continue + for r in getattr(rs, c.Rule): + if not r: continue + for ll in getattr(r, c.LookupRecord): + if not ll: continue + ll.LookupListIndex = lookupMap[ll.LookupListIndex] + elif self.Format == 3: + for ll in getattr(self, c.LookupRecord): + if not ll: continue + ll.LookupListIndex = lookupMap[ll.LookupListIndex] + else: + assert 0, "unknown format: %s" % self.Format + +@add_method(otTables.ExtensionSubst, + otTables.ExtensionPos) +def mapLookups(self, lookupMap): + if self.Format == 1: + self.ExtSubTable.mapLookups(lookupMap) + else: + assert 0, "unknown format: %s" % self.Format + +@add_method(otTables.Lookup) +def mapLookups(self, lookupMap): + for st in self.SubTable: + if not st: continue + st.mapLookups(lookupMap) + +@add_method(otTables.LookupList) +def mapLookups(self, lookupMap): + for l in self.Lookup: + if not l: continue + l.mapLookups(lookupMap) + +@add_method(otTables.Lookup) +def mapMarkFilteringSets(self, markFilteringSetMap): + if self.LookupFlag & 0x0010: + self.MarkFilteringSet = markFilteringSetMap[self.MarkFilteringSet] + +@add_method(otTables.LookupList) +def mapMarkFilteringSets(self, markFilteringSetMap): + for l in self.Lookup: + if not l: continue + l.mapMarkFilteringSets(markFilteringSetMap) + +@add_method(otTables.Feature) +def mapLookups(self, lookupMap): + self.LookupListIndex = [lookupMap[i] for i in self.LookupListIndex] + +@add_method(otTables.FeatureList) +def mapLookups(self, lookupMap): + for f in self.FeatureRecord: + if not f or not f.Feature: continue + f.Feature.mapLookups(lookupMap) + +@add_method(otTables.DefaultLangSys, + otTables.LangSys) +def mapFeatures(self, featureMap): + self.FeatureIndex = [featureMap[i] for i in self.FeatureIndex] + if self.ReqFeatureIndex != 65535: + self.ReqFeatureIndex = featureMap[self.ReqFeatureIndex] + +@add_method(otTables.Script) +def mapFeatures(self, featureMap): + if self.DefaultLangSys: + self.DefaultLangSys.mapFeatures(featureMap) + for l in self.LangSysRecord: + if not l or not l.LangSys: continue + l.LangSys.mapFeatures(featureMap) + +@add_method(otTables.ScriptList) +def mapFeatures(self, featureMap): + for s in self.ScriptRecord: + if not s or not s.Script: continue + s.Script.mapFeatures(featureMap)