diff options
Diffstat (limited to 'Lib/fontTools/merge/layout.py')
-rw-r--r-- | Lib/fontTools/merge/layout.py | 776 |
1 files changed, 420 insertions, 356 deletions
diff --git a/Lib/fontTools/merge/layout.py b/Lib/fontTools/merge/layout.py index 4bf01c37..6b85cd50 100644 --- a/Lib/fontTools/merge/layout.py +++ b/Lib/fontTools/merge/layout.py @@ -14,453 +14,517 @@ log = logging.getLogger("fontTools.merge") def mergeLookupLists(lst): - # TODO Do smarter merge. - return sumLists(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 + 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 + 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 + assert lst - # TODO Support merging ReqFeatureIndex - assert all(l.ReqFeatureIndex == 0xFFFF for l in 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 - 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 + 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 + 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, + "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), + "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), + "FeatureCount": sum, + "FeatureRecord": lambda lst: sorted(sumLists(lst), key=lambda s: s.FeatureTag), } otTables.LookupList.mergeMap = { - 'LookupCount': sum, - 'Lookup': sumLists, + "LookupCount": sum, + "Lookup": sumLists, } otTables.Coverage.mergeMap = { - 'Format': min, - 'glyphs': sumLists, + "Format": min, + "glyphs": sumLists, } otTables.ClassDef.mergeMap = { - 'Format': min, - 'classDefs': sumDicts, + "Format": min, + "classDefs": sumDicts, } otTables.LigCaretList.mergeMap = { - 'Coverage': mergeObjects, - 'LigGlyphCount': sum, - 'LigGlyph': sumLists, + "Coverage": mergeObjects, + "LigGlyphCount": sum, + "LigGlyph": sumLists, } otTables.AttachList.mergeMap = { - 'Coverage': mergeObjects, - 'GlyphCount': sum, - 'AttachPoint': sumLists, + "Coverage": mergeObjects, + "GlyphCount": sum, + "AttachPoint": sumLists, } # XXX Renumber MarkFilterSets of lookups otTables.MarkGlyphSetsDef.mergeMap = { - 'MarkSetTableFormat': equal, - 'MarkSetCount': sum, - 'Coverage': sumLists, + "MarkSetTableFormat": equal, + "MarkSetCount": sum, + "Coverage": sumLists, } otTables.Axis.mergeMap = { - '*': mergeObjects, + "*": mergeObjects, } # XXX Fix BASE table merging otTables.BaseTagList.mergeMap = { - 'BaseTagCount': sum, - 'BaselineTag': sumLists, + "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, +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, +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 '%s' but no GSUB. Are duplicates intended?: %s", m.fonts[i]._merger__name, 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) +@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 '%s' but no GSUB. Are duplicates intended?: %s", + m.fonts[i]._merger__name, + 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 + pass + # Copied and trimmed down from subset.py -@add_method(otTables.ContextSubst, - otTables.ChainContextSubst, - otTables.ContextPos, - otTables.ChainContextPos) +@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) + 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) + 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 + 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) + 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) + 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] + 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) + 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] + 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) + for f in self.FeatureRecord: + if not f or not f.Feature: + continue + f.Feature.mapLookups(lookupMap) + -@add_method(otTables.DefaultLangSys, - otTables.LangSys) +@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] + 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) + 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) + for s in self.ScriptRecord: + if not s or not s.Script: + continue + s.Script.mapFeatures(featureMap) -def layoutPreMerge(font): - # Map indices to references - - GDEF = font.get('GDEF') - GSUB = font.get('GSUB') - GPOS = font.get('GPOS') - - for t in [GSUB, GPOS]: - if not t: continue - - if t.table.LookupList: - lookupMap = {i:v for i,v in enumerate(t.table.LookupList.Lookup)} - t.table.LookupList.mapLookups(lookupMap) - t.table.FeatureList.mapLookups(lookupMap) - - if GDEF and GDEF.table.Version >= 0x00010002: - markFilteringSetMap = {i:v for i,v in enumerate(GDEF.table.MarkGlyphSetsDef.Coverage)} - t.table.LookupList.mapMarkFilteringSets(markFilteringSetMap) - - if t.table.FeatureList and t.table.ScriptList: - featureMap = {i:v for i,v in enumerate(t.table.FeatureList.FeatureRecord)} - t.table.ScriptList.mapFeatures(featureMap) - - # TODO FeatureParams nameIDs - -def layoutPostMerge(font): - # Map references back to indices - GDEF = font.get('GDEF') - GSUB = font.get('GSUB') - GPOS = font.get('GPOS') - - for t in [GSUB, GPOS]: - if not t: continue - - if t.table.FeatureList and t.table.ScriptList: - - # Collect unregistered (new) features. - featureMap = GregariousIdentityDict(t.table.FeatureList.FeatureRecord) - t.table.ScriptList.mapFeatures(featureMap) - - # Record used features. - featureMap = AttendanceRecordingIdentityDict(t.table.FeatureList.FeatureRecord) - t.table.ScriptList.mapFeatures(featureMap) - usedIndices = featureMap.s - - # Remove unused features - t.table.FeatureList.FeatureRecord = [f for i,f in enumerate(t.table.FeatureList.FeatureRecord) if i in usedIndices] - - # Map back to indices. - featureMap = NonhashableDict(t.table.FeatureList.FeatureRecord) - t.table.ScriptList.mapFeatures(featureMap) - - t.table.FeatureList.FeatureCount = len(t.table.FeatureList.FeatureRecord) +def layoutPreMerge(font): + # Map indices to references - if t.table.LookupList: + GDEF = font.get("GDEF") + GSUB = font.get("GSUB") + GPOS = font.get("GPOS") - # Collect unregistered (new) lookups. - lookupMap = GregariousIdentityDict(t.table.LookupList.Lookup) - t.table.FeatureList.mapLookups(lookupMap) - t.table.LookupList.mapLookups(lookupMap) + for t in [GSUB, GPOS]: + if not t: + continue - # Record used lookups. - lookupMap = AttendanceRecordingIdentityDict(t.table.LookupList.Lookup) - t.table.FeatureList.mapLookups(lookupMap) - t.table.LookupList.mapLookups(lookupMap) - usedIndices = lookupMap.s + if t.table.LookupList: + lookupMap = {i: v for i, v in enumerate(t.table.LookupList.Lookup)} + t.table.LookupList.mapLookups(lookupMap) + t.table.FeatureList.mapLookups(lookupMap) - # Remove unused lookups - t.table.LookupList.Lookup = [l for i,l in enumerate(t.table.LookupList.Lookup) if i in usedIndices] + if ( + GDEF + and GDEF.table.Version >= 0x00010002 + and GDEF.table.MarkGlyphSetsDef + ): + markFilteringSetMap = { + i: v for i, v in enumerate(GDEF.table.MarkGlyphSetsDef.Coverage) + } + t.table.LookupList.mapMarkFilteringSets(markFilteringSetMap) - # Map back to indices. - lookupMap = NonhashableDict(t.table.LookupList.Lookup) - t.table.FeatureList.mapLookups(lookupMap) - t.table.LookupList.mapLookups(lookupMap) + if t.table.FeatureList and t.table.ScriptList: + featureMap = {i: v for i, v in enumerate(t.table.FeatureList.FeatureRecord)} + t.table.ScriptList.mapFeatures(featureMap) - t.table.LookupList.LookupCount = len(t.table.LookupList.Lookup) + # TODO FeatureParams nameIDs - if GDEF and GDEF.table.Version >= 0x00010002: - markFilteringSetMap = NonhashableDict(GDEF.table.MarkGlyphSetsDef.Coverage) - t.table.LookupList.mapMarkFilteringSets(markFilteringSetMap) - # TODO FeatureParams nameIDs +def layoutPostMerge(font): + # Map references back to indices + + GDEF = font.get("GDEF") + GSUB = font.get("GSUB") + GPOS = font.get("GPOS") + + for t in [GSUB, GPOS]: + if not t: + continue + + if t.table.FeatureList and t.table.ScriptList: + # Collect unregistered (new) features. + featureMap = GregariousIdentityDict(t.table.FeatureList.FeatureRecord) + t.table.ScriptList.mapFeatures(featureMap) + + # Record used features. + featureMap = AttendanceRecordingIdentityDict( + t.table.FeatureList.FeatureRecord + ) + t.table.ScriptList.mapFeatures(featureMap) + usedIndices = featureMap.s + + # Remove unused features + t.table.FeatureList.FeatureRecord = [ + f + for i, f in enumerate(t.table.FeatureList.FeatureRecord) + if i in usedIndices + ] + + # Map back to indices. + featureMap = NonhashableDict(t.table.FeatureList.FeatureRecord) + t.table.ScriptList.mapFeatures(featureMap) + + t.table.FeatureList.FeatureCount = len(t.table.FeatureList.FeatureRecord) + + if t.table.LookupList: + # Collect unregistered (new) lookups. + lookupMap = GregariousIdentityDict(t.table.LookupList.Lookup) + t.table.FeatureList.mapLookups(lookupMap) + t.table.LookupList.mapLookups(lookupMap) + + # Record used lookups. + lookupMap = AttendanceRecordingIdentityDict(t.table.LookupList.Lookup) + t.table.FeatureList.mapLookups(lookupMap) + t.table.LookupList.mapLookups(lookupMap) + usedIndices = lookupMap.s + + # Remove unused lookups + t.table.LookupList.Lookup = [ + l for i, l in enumerate(t.table.LookupList.Lookup) if i in usedIndices + ] + + # Map back to indices. + lookupMap = NonhashableDict(t.table.LookupList.Lookup) + t.table.FeatureList.mapLookups(lookupMap) + t.table.LookupList.mapLookups(lookupMap) + + t.table.LookupList.LookupCount = len(t.table.LookupList.Lookup) + + if GDEF and GDEF.table.Version >= 0x00010002: + markFilteringSetMap = NonhashableDict( + GDEF.table.MarkGlyphSetsDef.Coverage + ) + t.table.LookupList.mapMarkFilteringSets(markFilteringSetMap) + + # TODO FeatureParams nameIDs |