up follow livre
This commit is contained in:
		
							parent
							
								
									b4b4398bb0
								
							
						
					
					
						commit
						3a7a3849ae
					
				
					 12242 changed files with 2564461 additions and 6914 deletions
				
			
		
							
								
								
									
										526
									
								
								venv/lib/python3.13/site-packages/fontTools/merge/layout.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										526
									
								
								venv/lib/python3.13/site-packages/fontTools/merge/layout.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,526 @@ | |||
| # 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.base import add_method, mergeObjects | ||||
| from fontTools.merge.util import * | ||||
| import logging | ||||
| 
 | ||||
| 
 | ||||
| log = logging.getLogger("fontTools.merge") | ||||
| 
 | ||||
| 
 | ||||
| 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 '%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 | ||||
| 
 | ||||
| 
 | ||||
| # 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) | ||||
| 
 | ||||
| 
 | ||||
| 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 | ||||
|                 and GDEF.table.MarkGlyphSetsDef | ||||
|             ): | ||||
|                 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) | ||||
| 
 | ||||
|         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 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Tykayn
						Tykayn