xref: /aosp_15_r20/external/fonttools/Lib/fontTools/merge/layout.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughes# Copyright 2013 Google, Inc. All Rights Reserved.
2*e1fe3e4aSElliott Hughes#
3*e1fe3e4aSElliott Hughes# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
4*e1fe3e4aSElliott Hughes
5*e1fe3e4aSElliott Hughesfrom fontTools import ttLib
6*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables.DefaultTable import DefaultTable
7*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables import otTables
8*e1fe3e4aSElliott Hughesfrom fontTools.merge.base import add_method, mergeObjects
9*e1fe3e4aSElliott Hughesfrom fontTools.merge.util import *
10*e1fe3e4aSElliott Hughesimport logging
11*e1fe3e4aSElliott Hughes
12*e1fe3e4aSElliott Hughes
13*e1fe3e4aSElliott Hugheslog = logging.getLogger("fontTools.merge")
14*e1fe3e4aSElliott Hughes
15*e1fe3e4aSElliott Hughes
16*e1fe3e4aSElliott Hughesdef mergeLookupLists(lst):
17*e1fe3e4aSElliott Hughes    # TODO Do smarter merge.
18*e1fe3e4aSElliott Hughes    return sumLists(lst)
19*e1fe3e4aSElliott Hughes
20*e1fe3e4aSElliott Hughes
21*e1fe3e4aSElliott Hughesdef mergeFeatures(lst):
22*e1fe3e4aSElliott Hughes    assert lst
23*e1fe3e4aSElliott Hughes    self = otTables.Feature()
24*e1fe3e4aSElliott Hughes    self.FeatureParams = None
25*e1fe3e4aSElliott Hughes    self.LookupListIndex = mergeLookupLists(
26*e1fe3e4aSElliott Hughes        [l.LookupListIndex for l in lst if l.LookupListIndex]
27*e1fe3e4aSElliott Hughes    )
28*e1fe3e4aSElliott Hughes    self.LookupCount = len(self.LookupListIndex)
29*e1fe3e4aSElliott Hughes    return self
30*e1fe3e4aSElliott Hughes
31*e1fe3e4aSElliott Hughes
32*e1fe3e4aSElliott Hughesdef mergeFeatureLists(lst):
33*e1fe3e4aSElliott Hughes    d = {}
34*e1fe3e4aSElliott Hughes    for l in lst:
35*e1fe3e4aSElliott Hughes        for f in l:
36*e1fe3e4aSElliott Hughes            tag = f.FeatureTag
37*e1fe3e4aSElliott Hughes            if tag not in d:
38*e1fe3e4aSElliott Hughes                d[tag] = []
39*e1fe3e4aSElliott Hughes            d[tag].append(f.Feature)
40*e1fe3e4aSElliott Hughes    ret = []
41*e1fe3e4aSElliott Hughes    for tag in sorted(d.keys()):
42*e1fe3e4aSElliott Hughes        rec = otTables.FeatureRecord()
43*e1fe3e4aSElliott Hughes        rec.FeatureTag = tag
44*e1fe3e4aSElliott Hughes        rec.Feature = mergeFeatures(d[tag])
45*e1fe3e4aSElliott Hughes        ret.append(rec)
46*e1fe3e4aSElliott Hughes    return ret
47*e1fe3e4aSElliott Hughes
48*e1fe3e4aSElliott Hughes
49*e1fe3e4aSElliott Hughesdef mergeLangSyses(lst):
50*e1fe3e4aSElliott Hughes    assert lst
51*e1fe3e4aSElliott Hughes
52*e1fe3e4aSElliott Hughes    # TODO Support merging ReqFeatureIndex
53*e1fe3e4aSElliott Hughes    assert all(l.ReqFeatureIndex == 0xFFFF for l in lst)
54*e1fe3e4aSElliott Hughes
55*e1fe3e4aSElliott Hughes    self = otTables.LangSys()
56*e1fe3e4aSElliott Hughes    self.LookupOrder = None
57*e1fe3e4aSElliott Hughes    self.ReqFeatureIndex = 0xFFFF
58*e1fe3e4aSElliott Hughes    self.FeatureIndex = mergeFeatureLists(
59*e1fe3e4aSElliott Hughes        [l.FeatureIndex for l in lst if l.FeatureIndex]
60*e1fe3e4aSElliott Hughes    )
61*e1fe3e4aSElliott Hughes    self.FeatureCount = len(self.FeatureIndex)
62*e1fe3e4aSElliott Hughes    return self
63*e1fe3e4aSElliott Hughes
64*e1fe3e4aSElliott Hughes
65*e1fe3e4aSElliott Hughesdef mergeScripts(lst):
66*e1fe3e4aSElliott Hughes    assert lst
67*e1fe3e4aSElliott Hughes
68*e1fe3e4aSElliott Hughes    if len(lst) == 1:
69*e1fe3e4aSElliott Hughes        return lst[0]
70*e1fe3e4aSElliott Hughes    langSyses = {}
71*e1fe3e4aSElliott Hughes    for sr in lst:
72*e1fe3e4aSElliott Hughes        for lsr in sr.LangSysRecord:
73*e1fe3e4aSElliott Hughes            if lsr.LangSysTag not in langSyses:
74*e1fe3e4aSElliott Hughes                langSyses[lsr.LangSysTag] = []
75*e1fe3e4aSElliott Hughes            langSyses[lsr.LangSysTag].append(lsr.LangSys)
76*e1fe3e4aSElliott Hughes    lsrecords = []
77*e1fe3e4aSElliott Hughes    for tag, langSys_list in sorted(langSyses.items()):
78*e1fe3e4aSElliott Hughes        lsr = otTables.LangSysRecord()
79*e1fe3e4aSElliott Hughes        lsr.LangSys = mergeLangSyses(langSys_list)
80*e1fe3e4aSElliott Hughes        lsr.LangSysTag = tag
81*e1fe3e4aSElliott Hughes        lsrecords.append(lsr)
82*e1fe3e4aSElliott Hughes
83*e1fe3e4aSElliott Hughes    self = otTables.Script()
84*e1fe3e4aSElliott Hughes    self.LangSysRecord = lsrecords
85*e1fe3e4aSElliott Hughes    self.LangSysCount = len(lsrecords)
86*e1fe3e4aSElliott Hughes    dfltLangSyses = [s.DefaultLangSys for s in lst if s.DefaultLangSys]
87*e1fe3e4aSElliott Hughes    if dfltLangSyses:
88*e1fe3e4aSElliott Hughes        self.DefaultLangSys = mergeLangSyses(dfltLangSyses)
89*e1fe3e4aSElliott Hughes    else:
90*e1fe3e4aSElliott Hughes        self.DefaultLangSys = None
91*e1fe3e4aSElliott Hughes    return self
92*e1fe3e4aSElliott Hughes
93*e1fe3e4aSElliott Hughes
94*e1fe3e4aSElliott Hughesdef mergeScriptRecords(lst):
95*e1fe3e4aSElliott Hughes    d = {}
96*e1fe3e4aSElliott Hughes    for l in lst:
97*e1fe3e4aSElliott Hughes        for s in l:
98*e1fe3e4aSElliott Hughes            tag = s.ScriptTag
99*e1fe3e4aSElliott Hughes            if tag not in d:
100*e1fe3e4aSElliott Hughes                d[tag] = []
101*e1fe3e4aSElliott Hughes            d[tag].append(s.Script)
102*e1fe3e4aSElliott Hughes    ret = []
103*e1fe3e4aSElliott Hughes    for tag in sorted(d.keys()):
104*e1fe3e4aSElliott Hughes        rec = otTables.ScriptRecord()
105*e1fe3e4aSElliott Hughes        rec.ScriptTag = tag
106*e1fe3e4aSElliott Hughes        rec.Script = mergeScripts(d[tag])
107*e1fe3e4aSElliott Hughes        ret.append(rec)
108*e1fe3e4aSElliott Hughes    return ret
109*e1fe3e4aSElliott Hughes
110*e1fe3e4aSElliott Hughes
111*e1fe3e4aSElliott HughesotTables.ScriptList.mergeMap = {
112*e1fe3e4aSElliott Hughes    "ScriptCount": lambda lst: None,  # TODO
113*e1fe3e4aSElliott Hughes    "ScriptRecord": mergeScriptRecords,
114*e1fe3e4aSElliott Hughes}
115*e1fe3e4aSElliott HughesotTables.BaseScriptList.mergeMap = {
116*e1fe3e4aSElliott Hughes    "BaseScriptCount": lambda lst: None,  # TODO
117*e1fe3e4aSElliott Hughes    # TODO: Merge duplicate entries
118*e1fe3e4aSElliott Hughes    "BaseScriptRecord": lambda lst: sorted(
119*e1fe3e4aSElliott Hughes        sumLists(lst), key=lambda s: s.BaseScriptTag
120*e1fe3e4aSElliott Hughes    ),
121*e1fe3e4aSElliott Hughes}
122*e1fe3e4aSElliott Hughes
123*e1fe3e4aSElliott HughesotTables.FeatureList.mergeMap = {
124*e1fe3e4aSElliott Hughes    "FeatureCount": sum,
125*e1fe3e4aSElliott Hughes    "FeatureRecord": lambda lst: sorted(sumLists(lst), key=lambda s: s.FeatureTag),
126*e1fe3e4aSElliott Hughes}
127*e1fe3e4aSElliott Hughes
128*e1fe3e4aSElliott HughesotTables.LookupList.mergeMap = {
129*e1fe3e4aSElliott Hughes    "LookupCount": sum,
130*e1fe3e4aSElliott Hughes    "Lookup": sumLists,
131*e1fe3e4aSElliott Hughes}
132*e1fe3e4aSElliott Hughes
133*e1fe3e4aSElliott HughesotTables.Coverage.mergeMap = {
134*e1fe3e4aSElliott Hughes    "Format": min,
135*e1fe3e4aSElliott Hughes    "glyphs": sumLists,
136*e1fe3e4aSElliott Hughes}
137*e1fe3e4aSElliott Hughes
138*e1fe3e4aSElliott HughesotTables.ClassDef.mergeMap = {
139*e1fe3e4aSElliott Hughes    "Format": min,
140*e1fe3e4aSElliott Hughes    "classDefs": sumDicts,
141*e1fe3e4aSElliott Hughes}
142*e1fe3e4aSElliott Hughes
143*e1fe3e4aSElliott HughesotTables.LigCaretList.mergeMap = {
144*e1fe3e4aSElliott Hughes    "Coverage": mergeObjects,
145*e1fe3e4aSElliott Hughes    "LigGlyphCount": sum,
146*e1fe3e4aSElliott Hughes    "LigGlyph": sumLists,
147*e1fe3e4aSElliott Hughes}
148*e1fe3e4aSElliott Hughes
149*e1fe3e4aSElliott HughesotTables.AttachList.mergeMap = {
150*e1fe3e4aSElliott Hughes    "Coverage": mergeObjects,
151*e1fe3e4aSElliott Hughes    "GlyphCount": sum,
152*e1fe3e4aSElliott Hughes    "AttachPoint": sumLists,
153*e1fe3e4aSElliott Hughes}
154*e1fe3e4aSElliott Hughes
155*e1fe3e4aSElliott Hughes# XXX Renumber MarkFilterSets of lookups
156*e1fe3e4aSElliott HughesotTables.MarkGlyphSetsDef.mergeMap = {
157*e1fe3e4aSElliott Hughes    "MarkSetTableFormat": equal,
158*e1fe3e4aSElliott Hughes    "MarkSetCount": sum,
159*e1fe3e4aSElliott Hughes    "Coverage": sumLists,
160*e1fe3e4aSElliott Hughes}
161*e1fe3e4aSElliott Hughes
162*e1fe3e4aSElliott HughesotTables.Axis.mergeMap = {
163*e1fe3e4aSElliott Hughes    "*": mergeObjects,
164*e1fe3e4aSElliott Hughes}
165*e1fe3e4aSElliott Hughes
166*e1fe3e4aSElliott Hughes# XXX Fix BASE table merging
167*e1fe3e4aSElliott HughesotTables.BaseTagList.mergeMap = {
168*e1fe3e4aSElliott Hughes    "BaseTagCount": sum,
169*e1fe3e4aSElliott Hughes    "BaselineTag": sumLists,
170*e1fe3e4aSElliott Hughes}
171*e1fe3e4aSElliott Hughes
172*e1fe3e4aSElliott HughesotTables.GDEF.mergeMap = otTables.GSUB.mergeMap = otTables.GPOS.mergeMap = (
173*e1fe3e4aSElliott Hughes    otTables.BASE.mergeMap
174*e1fe3e4aSElliott Hughes) = otTables.JSTF.mergeMap = otTables.MATH.mergeMap = {
175*e1fe3e4aSElliott Hughes    "*": mergeObjects,
176*e1fe3e4aSElliott Hughes    "Version": max,
177*e1fe3e4aSElliott Hughes}
178*e1fe3e4aSElliott Hughes
179*e1fe3e4aSElliott HughesttLib.getTableClass("GDEF").mergeMap = ttLib.getTableClass("GSUB").mergeMap = (
180*e1fe3e4aSElliott Hughes    ttLib.getTableClass("GPOS").mergeMap
181*e1fe3e4aSElliott Hughes) = ttLib.getTableClass("BASE").mergeMap = ttLib.getTableClass(
182*e1fe3e4aSElliott Hughes    "JSTF"
183*e1fe3e4aSElliott Hughes).mergeMap = ttLib.getTableClass(
184*e1fe3e4aSElliott Hughes    "MATH"
185*e1fe3e4aSElliott Hughes).mergeMap = {
186*e1fe3e4aSElliott Hughes    "tableTag": onlyExisting(equal),  # XXX clean me up
187*e1fe3e4aSElliott Hughes    "table": mergeObjects,
188*e1fe3e4aSElliott Hughes}
189*e1fe3e4aSElliott Hughes
190*e1fe3e4aSElliott Hughes
191*e1fe3e4aSElliott Hughes@add_method(ttLib.getTableClass("GSUB"))
192*e1fe3e4aSElliott Hughesdef merge(self, m, tables):
193*e1fe3e4aSElliott Hughes    assert len(tables) == len(m.duplicateGlyphsPerFont)
194*e1fe3e4aSElliott Hughes    for i, (table, dups) in enumerate(zip(tables, m.duplicateGlyphsPerFont)):
195*e1fe3e4aSElliott Hughes        if not dups:
196*e1fe3e4aSElliott Hughes            continue
197*e1fe3e4aSElliott Hughes        if table is None or table is NotImplemented:
198*e1fe3e4aSElliott Hughes            log.warning(
199*e1fe3e4aSElliott Hughes                "Have non-identical duplicates to resolve for '%s' but no GSUB. Are duplicates intended?: %s",
200*e1fe3e4aSElliott Hughes                m.fonts[i]._merger__name,
201*e1fe3e4aSElliott Hughes                dups,
202*e1fe3e4aSElliott Hughes            )
203*e1fe3e4aSElliott Hughes            continue
204*e1fe3e4aSElliott Hughes
205*e1fe3e4aSElliott Hughes        synthFeature = None
206*e1fe3e4aSElliott Hughes        synthLookup = None
207*e1fe3e4aSElliott Hughes        for script in table.table.ScriptList.ScriptRecord:
208*e1fe3e4aSElliott Hughes            if script.ScriptTag == "DFLT":
209*e1fe3e4aSElliott Hughes                continue  # XXX
210*e1fe3e4aSElliott Hughes            for langsys in [script.Script.DefaultLangSys] + [
211*e1fe3e4aSElliott Hughes                l.LangSys for l in script.Script.LangSysRecord
212*e1fe3e4aSElliott Hughes            ]:
213*e1fe3e4aSElliott Hughes                if langsys is None:
214*e1fe3e4aSElliott Hughes                    continue  # XXX Create!
215*e1fe3e4aSElliott Hughes                feature = [v for v in langsys.FeatureIndex if v.FeatureTag == "locl"]
216*e1fe3e4aSElliott Hughes                assert len(feature) <= 1
217*e1fe3e4aSElliott Hughes                if feature:
218*e1fe3e4aSElliott Hughes                    feature = feature[0]
219*e1fe3e4aSElliott Hughes                else:
220*e1fe3e4aSElliott Hughes                    if not synthFeature:
221*e1fe3e4aSElliott Hughes                        synthFeature = otTables.FeatureRecord()
222*e1fe3e4aSElliott Hughes                        synthFeature.FeatureTag = "locl"
223*e1fe3e4aSElliott Hughes                        f = synthFeature.Feature = otTables.Feature()
224*e1fe3e4aSElliott Hughes                        f.FeatureParams = None
225*e1fe3e4aSElliott Hughes                        f.LookupCount = 0
226*e1fe3e4aSElliott Hughes                        f.LookupListIndex = []
227*e1fe3e4aSElliott Hughes                        table.table.FeatureList.FeatureRecord.append(synthFeature)
228*e1fe3e4aSElliott Hughes                        table.table.FeatureList.FeatureCount += 1
229*e1fe3e4aSElliott Hughes                    feature = synthFeature
230*e1fe3e4aSElliott Hughes                    langsys.FeatureIndex.append(feature)
231*e1fe3e4aSElliott Hughes                    langsys.FeatureIndex.sort(key=lambda v: v.FeatureTag)
232*e1fe3e4aSElliott Hughes
233*e1fe3e4aSElliott Hughes                if not synthLookup:
234*e1fe3e4aSElliott Hughes                    subtable = otTables.SingleSubst()
235*e1fe3e4aSElliott Hughes                    subtable.mapping = dups
236*e1fe3e4aSElliott Hughes                    synthLookup = otTables.Lookup()
237*e1fe3e4aSElliott Hughes                    synthLookup.LookupFlag = 0
238*e1fe3e4aSElliott Hughes                    synthLookup.LookupType = 1
239*e1fe3e4aSElliott Hughes                    synthLookup.SubTableCount = 1
240*e1fe3e4aSElliott Hughes                    synthLookup.SubTable = [subtable]
241*e1fe3e4aSElliott Hughes                    if table.table.LookupList is None:
242*e1fe3e4aSElliott Hughes                        # mtiLib uses None as default value for LookupList,
243*e1fe3e4aSElliott Hughes                        # while feaLib points to an empty array with count 0
244*e1fe3e4aSElliott Hughes                        # TODO: make them do the same
245*e1fe3e4aSElliott Hughes                        table.table.LookupList = otTables.LookupList()
246*e1fe3e4aSElliott Hughes                        table.table.LookupList.Lookup = []
247*e1fe3e4aSElliott Hughes                        table.table.LookupList.LookupCount = 0
248*e1fe3e4aSElliott Hughes                    table.table.LookupList.Lookup.append(synthLookup)
249*e1fe3e4aSElliott Hughes                    table.table.LookupList.LookupCount += 1
250*e1fe3e4aSElliott Hughes
251*e1fe3e4aSElliott Hughes                if feature.Feature.LookupListIndex[:1] != [synthLookup]:
252*e1fe3e4aSElliott Hughes                    feature.Feature.LookupListIndex[:0] = [synthLookup]
253*e1fe3e4aSElliott Hughes                    feature.Feature.LookupCount += 1
254*e1fe3e4aSElliott Hughes
255*e1fe3e4aSElliott Hughes    DefaultTable.merge(self, m, tables)
256*e1fe3e4aSElliott Hughes    return self
257*e1fe3e4aSElliott Hughes
258*e1fe3e4aSElliott Hughes
259*e1fe3e4aSElliott Hughes@add_method(
260*e1fe3e4aSElliott Hughes    otTables.SingleSubst,
261*e1fe3e4aSElliott Hughes    otTables.MultipleSubst,
262*e1fe3e4aSElliott Hughes    otTables.AlternateSubst,
263*e1fe3e4aSElliott Hughes    otTables.LigatureSubst,
264*e1fe3e4aSElliott Hughes    otTables.ReverseChainSingleSubst,
265*e1fe3e4aSElliott Hughes    otTables.SinglePos,
266*e1fe3e4aSElliott Hughes    otTables.PairPos,
267*e1fe3e4aSElliott Hughes    otTables.CursivePos,
268*e1fe3e4aSElliott Hughes    otTables.MarkBasePos,
269*e1fe3e4aSElliott Hughes    otTables.MarkLigPos,
270*e1fe3e4aSElliott Hughes    otTables.MarkMarkPos,
271*e1fe3e4aSElliott Hughes)
272*e1fe3e4aSElliott Hughesdef mapLookups(self, lookupMap):
273*e1fe3e4aSElliott Hughes    pass
274*e1fe3e4aSElliott Hughes
275*e1fe3e4aSElliott Hughes
276*e1fe3e4aSElliott Hughes# Copied and trimmed down from subset.py
277*e1fe3e4aSElliott Hughes@add_method(
278*e1fe3e4aSElliott Hughes    otTables.ContextSubst,
279*e1fe3e4aSElliott Hughes    otTables.ChainContextSubst,
280*e1fe3e4aSElliott Hughes    otTables.ContextPos,
281*e1fe3e4aSElliott Hughes    otTables.ChainContextPos,
282*e1fe3e4aSElliott Hughes)
283*e1fe3e4aSElliott Hughesdef __merge_classify_context(self):
284*e1fe3e4aSElliott Hughes    class ContextHelper(object):
285*e1fe3e4aSElliott Hughes        def __init__(self, klass, Format):
286*e1fe3e4aSElliott Hughes            if klass.__name__.endswith("Subst"):
287*e1fe3e4aSElliott Hughes                Typ = "Sub"
288*e1fe3e4aSElliott Hughes                Type = "Subst"
289*e1fe3e4aSElliott Hughes            else:
290*e1fe3e4aSElliott Hughes                Typ = "Pos"
291*e1fe3e4aSElliott Hughes                Type = "Pos"
292*e1fe3e4aSElliott Hughes            if klass.__name__.startswith("Chain"):
293*e1fe3e4aSElliott Hughes                Chain = "Chain"
294*e1fe3e4aSElliott Hughes            else:
295*e1fe3e4aSElliott Hughes                Chain = ""
296*e1fe3e4aSElliott Hughes            ChainTyp = Chain + Typ
297*e1fe3e4aSElliott Hughes
298*e1fe3e4aSElliott Hughes            self.Typ = Typ
299*e1fe3e4aSElliott Hughes            self.Type = Type
300*e1fe3e4aSElliott Hughes            self.Chain = Chain
301*e1fe3e4aSElliott Hughes            self.ChainTyp = ChainTyp
302*e1fe3e4aSElliott Hughes
303*e1fe3e4aSElliott Hughes            self.LookupRecord = Type + "LookupRecord"
304*e1fe3e4aSElliott Hughes
305*e1fe3e4aSElliott Hughes            if Format == 1:
306*e1fe3e4aSElliott Hughes                self.Rule = ChainTyp + "Rule"
307*e1fe3e4aSElliott Hughes                self.RuleSet = ChainTyp + "RuleSet"
308*e1fe3e4aSElliott Hughes            elif Format == 2:
309*e1fe3e4aSElliott Hughes                self.Rule = ChainTyp + "ClassRule"
310*e1fe3e4aSElliott Hughes                self.RuleSet = ChainTyp + "ClassSet"
311*e1fe3e4aSElliott Hughes
312*e1fe3e4aSElliott Hughes    if self.Format not in [1, 2, 3]:
313*e1fe3e4aSElliott Hughes        return None  # Don't shoot the messenger; let it go
314*e1fe3e4aSElliott Hughes    if not hasattr(self.__class__, "_merge__ContextHelpers"):
315*e1fe3e4aSElliott Hughes        self.__class__._merge__ContextHelpers = {}
316*e1fe3e4aSElliott Hughes    if self.Format not in self.__class__._merge__ContextHelpers:
317*e1fe3e4aSElliott Hughes        helper = ContextHelper(self.__class__, self.Format)
318*e1fe3e4aSElliott Hughes        self.__class__._merge__ContextHelpers[self.Format] = helper
319*e1fe3e4aSElliott Hughes    return self.__class__._merge__ContextHelpers[self.Format]
320*e1fe3e4aSElliott Hughes
321*e1fe3e4aSElliott Hughes
322*e1fe3e4aSElliott Hughes@add_method(
323*e1fe3e4aSElliott Hughes    otTables.ContextSubst,
324*e1fe3e4aSElliott Hughes    otTables.ChainContextSubst,
325*e1fe3e4aSElliott Hughes    otTables.ContextPos,
326*e1fe3e4aSElliott Hughes    otTables.ChainContextPos,
327*e1fe3e4aSElliott Hughes)
328*e1fe3e4aSElliott Hughesdef mapLookups(self, lookupMap):
329*e1fe3e4aSElliott Hughes    c = self.__merge_classify_context()
330*e1fe3e4aSElliott Hughes
331*e1fe3e4aSElliott Hughes    if self.Format in [1, 2]:
332*e1fe3e4aSElliott Hughes        for rs in getattr(self, c.RuleSet):
333*e1fe3e4aSElliott Hughes            if not rs:
334*e1fe3e4aSElliott Hughes                continue
335*e1fe3e4aSElliott Hughes            for r in getattr(rs, c.Rule):
336*e1fe3e4aSElliott Hughes                if not r:
337*e1fe3e4aSElliott Hughes                    continue
338*e1fe3e4aSElliott Hughes                for ll in getattr(r, c.LookupRecord):
339*e1fe3e4aSElliott Hughes                    if not ll:
340*e1fe3e4aSElliott Hughes                        continue
341*e1fe3e4aSElliott Hughes                    ll.LookupListIndex = lookupMap[ll.LookupListIndex]
342*e1fe3e4aSElliott Hughes    elif self.Format == 3:
343*e1fe3e4aSElliott Hughes        for ll in getattr(self, c.LookupRecord):
344*e1fe3e4aSElliott Hughes            if not ll:
345*e1fe3e4aSElliott Hughes                continue
346*e1fe3e4aSElliott Hughes            ll.LookupListIndex = lookupMap[ll.LookupListIndex]
347*e1fe3e4aSElliott Hughes    else:
348*e1fe3e4aSElliott Hughes        assert 0, "unknown format: %s" % self.Format
349*e1fe3e4aSElliott Hughes
350*e1fe3e4aSElliott Hughes
351*e1fe3e4aSElliott Hughes@add_method(otTables.ExtensionSubst, otTables.ExtensionPos)
352*e1fe3e4aSElliott Hughesdef mapLookups(self, lookupMap):
353*e1fe3e4aSElliott Hughes    if self.Format == 1:
354*e1fe3e4aSElliott Hughes        self.ExtSubTable.mapLookups(lookupMap)
355*e1fe3e4aSElliott Hughes    else:
356*e1fe3e4aSElliott Hughes        assert 0, "unknown format: %s" % self.Format
357*e1fe3e4aSElliott Hughes
358*e1fe3e4aSElliott Hughes
359*e1fe3e4aSElliott Hughes@add_method(otTables.Lookup)
360*e1fe3e4aSElliott Hughesdef mapLookups(self, lookupMap):
361*e1fe3e4aSElliott Hughes    for st in self.SubTable:
362*e1fe3e4aSElliott Hughes        if not st:
363*e1fe3e4aSElliott Hughes            continue
364*e1fe3e4aSElliott Hughes        st.mapLookups(lookupMap)
365*e1fe3e4aSElliott Hughes
366*e1fe3e4aSElliott Hughes
367*e1fe3e4aSElliott Hughes@add_method(otTables.LookupList)
368*e1fe3e4aSElliott Hughesdef mapLookups(self, lookupMap):
369*e1fe3e4aSElliott Hughes    for l in self.Lookup:
370*e1fe3e4aSElliott Hughes        if not l:
371*e1fe3e4aSElliott Hughes            continue
372*e1fe3e4aSElliott Hughes        l.mapLookups(lookupMap)
373*e1fe3e4aSElliott Hughes
374*e1fe3e4aSElliott Hughes
375*e1fe3e4aSElliott Hughes@add_method(otTables.Lookup)
376*e1fe3e4aSElliott Hughesdef mapMarkFilteringSets(self, markFilteringSetMap):
377*e1fe3e4aSElliott Hughes    if self.LookupFlag & 0x0010:
378*e1fe3e4aSElliott Hughes        self.MarkFilteringSet = markFilteringSetMap[self.MarkFilteringSet]
379*e1fe3e4aSElliott Hughes
380*e1fe3e4aSElliott Hughes
381*e1fe3e4aSElliott Hughes@add_method(otTables.LookupList)
382*e1fe3e4aSElliott Hughesdef mapMarkFilteringSets(self, markFilteringSetMap):
383*e1fe3e4aSElliott Hughes    for l in self.Lookup:
384*e1fe3e4aSElliott Hughes        if not l:
385*e1fe3e4aSElliott Hughes            continue
386*e1fe3e4aSElliott Hughes        l.mapMarkFilteringSets(markFilteringSetMap)
387*e1fe3e4aSElliott Hughes
388*e1fe3e4aSElliott Hughes
389*e1fe3e4aSElliott Hughes@add_method(otTables.Feature)
390*e1fe3e4aSElliott Hughesdef mapLookups(self, lookupMap):
391*e1fe3e4aSElliott Hughes    self.LookupListIndex = [lookupMap[i] for i in self.LookupListIndex]
392*e1fe3e4aSElliott Hughes
393*e1fe3e4aSElliott Hughes
394*e1fe3e4aSElliott Hughes@add_method(otTables.FeatureList)
395*e1fe3e4aSElliott Hughesdef mapLookups(self, lookupMap):
396*e1fe3e4aSElliott Hughes    for f in self.FeatureRecord:
397*e1fe3e4aSElliott Hughes        if not f or not f.Feature:
398*e1fe3e4aSElliott Hughes            continue
399*e1fe3e4aSElliott Hughes        f.Feature.mapLookups(lookupMap)
400*e1fe3e4aSElliott Hughes
401*e1fe3e4aSElliott Hughes
402*e1fe3e4aSElliott Hughes@add_method(otTables.DefaultLangSys, otTables.LangSys)
403*e1fe3e4aSElliott Hughesdef mapFeatures(self, featureMap):
404*e1fe3e4aSElliott Hughes    self.FeatureIndex = [featureMap[i] for i in self.FeatureIndex]
405*e1fe3e4aSElliott Hughes    if self.ReqFeatureIndex != 65535:
406*e1fe3e4aSElliott Hughes        self.ReqFeatureIndex = featureMap[self.ReqFeatureIndex]
407*e1fe3e4aSElliott Hughes
408*e1fe3e4aSElliott Hughes
409*e1fe3e4aSElliott Hughes@add_method(otTables.Script)
410*e1fe3e4aSElliott Hughesdef mapFeatures(self, featureMap):
411*e1fe3e4aSElliott Hughes    if self.DefaultLangSys:
412*e1fe3e4aSElliott Hughes        self.DefaultLangSys.mapFeatures(featureMap)
413*e1fe3e4aSElliott Hughes    for l in self.LangSysRecord:
414*e1fe3e4aSElliott Hughes        if not l or not l.LangSys:
415*e1fe3e4aSElliott Hughes            continue
416*e1fe3e4aSElliott Hughes        l.LangSys.mapFeatures(featureMap)
417*e1fe3e4aSElliott Hughes
418*e1fe3e4aSElliott Hughes
419*e1fe3e4aSElliott Hughes@add_method(otTables.ScriptList)
420*e1fe3e4aSElliott Hughesdef mapFeatures(self, featureMap):
421*e1fe3e4aSElliott Hughes    for s in self.ScriptRecord:
422*e1fe3e4aSElliott Hughes        if not s or not s.Script:
423*e1fe3e4aSElliott Hughes            continue
424*e1fe3e4aSElliott Hughes        s.Script.mapFeatures(featureMap)
425*e1fe3e4aSElliott Hughes
426*e1fe3e4aSElliott Hughes
427*e1fe3e4aSElliott Hughesdef layoutPreMerge(font):
428*e1fe3e4aSElliott Hughes    # Map indices to references
429*e1fe3e4aSElliott Hughes
430*e1fe3e4aSElliott Hughes    GDEF = font.get("GDEF")
431*e1fe3e4aSElliott Hughes    GSUB = font.get("GSUB")
432*e1fe3e4aSElliott Hughes    GPOS = font.get("GPOS")
433*e1fe3e4aSElliott Hughes
434*e1fe3e4aSElliott Hughes    for t in [GSUB, GPOS]:
435*e1fe3e4aSElliott Hughes        if not t:
436*e1fe3e4aSElliott Hughes            continue
437*e1fe3e4aSElliott Hughes
438*e1fe3e4aSElliott Hughes        if t.table.LookupList:
439*e1fe3e4aSElliott Hughes            lookupMap = {i: v for i, v in enumerate(t.table.LookupList.Lookup)}
440*e1fe3e4aSElliott Hughes            t.table.LookupList.mapLookups(lookupMap)
441*e1fe3e4aSElliott Hughes            t.table.FeatureList.mapLookups(lookupMap)
442*e1fe3e4aSElliott Hughes
443*e1fe3e4aSElliott Hughes            if (
444*e1fe3e4aSElliott Hughes                GDEF
445*e1fe3e4aSElliott Hughes                and GDEF.table.Version >= 0x00010002
446*e1fe3e4aSElliott Hughes                and GDEF.table.MarkGlyphSetsDef
447*e1fe3e4aSElliott Hughes            ):
448*e1fe3e4aSElliott Hughes                markFilteringSetMap = {
449*e1fe3e4aSElliott Hughes                    i: v for i, v in enumerate(GDEF.table.MarkGlyphSetsDef.Coverage)
450*e1fe3e4aSElliott Hughes                }
451*e1fe3e4aSElliott Hughes                t.table.LookupList.mapMarkFilteringSets(markFilteringSetMap)
452*e1fe3e4aSElliott Hughes
453*e1fe3e4aSElliott Hughes        if t.table.FeatureList and t.table.ScriptList:
454*e1fe3e4aSElliott Hughes            featureMap = {i: v for i, v in enumerate(t.table.FeatureList.FeatureRecord)}
455*e1fe3e4aSElliott Hughes            t.table.ScriptList.mapFeatures(featureMap)
456*e1fe3e4aSElliott Hughes
457*e1fe3e4aSElliott Hughes    # TODO FeatureParams nameIDs
458*e1fe3e4aSElliott Hughes
459*e1fe3e4aSElliott Hughes
460*e1fe3e4aSElliott Hughesdef layoutPostMerge(font):
461*e1fe3e4aSElliott Hughes    # Map references back to indices
462*e1fe3e4aSElliott Hughes
463*e1fe3e4aSElliott Hughes    GDEF = font.get("GDEF")
464*e1fe3e4aSElliott Hughes    GSUB = font.get("GSUB")
465*e1fe3e4aSElliott Hughes    GPOS = font.get("GPOS")
466*e1fe3e4aSElliott Hughes
467*e1fe3e4aSElliott Hughes    for t in [GSUB, GPOS]:
468*e1fe3e4aSElliott Hughes        if not t:
469*e1fe3e4aSElliott Hughes            continue
470*e1fe3e4aSElliott Hughes
471*e1fe3e4aSElliott Hughes        if t.table.FeatureList and t.table.ScriptList:
472*e1fe3e4aSElliott Hughes            # Collect unregistered (new) features.
473*e1fe3e4aSElliott Hughes            featureMap = GregariousIdentityDict(t.table.FeatureList.FeatureRecord)
474*e1fe3e4aSElliott Hughes            t.table.ScriptList.mapFeatures(featureMap)
475*e1fe3e4aSElliott Hughes
476*e1fe3e4aSElliott Hughes            # Record used features.
477*e1fe3e4aSElliott Hughes            featureMap = AttendanceRecordingIdentityDict(
478*e1fe3e4aSElliott Hughes                t.table.FeatureList.FeatureRecord
479*e1fe3e4aSElliott Hughes            )
480*e1fe3e4aSElliott Hughes            t.table.ScriptList.mapFeatures(featureMap)
481*e1fe3e4aSElliott Hughes            usedIndices = featureMap.s
482*e1fe3e4aSElliott Hughes
483*e1fe3e4aSElliott Hughes            # Remove unused features
484*e1fe3e4aSElliott Hughes            t.table.FeatureList.FeatureRecord = [
485*e1fe3e4aSElliott Hughes                f
486*e1fe3e4aSElliott Hughes                for i, f in enumerate(t.table.FeatureList.FeatureRecord)
487*e1fe3e4aSElliott Hughes                if i in usedIndices
488*e1fe3e4aSElliott Hughes            ]
489*e1fe3e4aSElliott Hughes
490*e1fe3e4aSElliott Hughes            # Map back to indices.
491*e1fe3e4aSElliott Hughes            featureMap = NonhashableDict(t.table.FeatureList.FeatureRecord)
492*e1fe3e4aSElliott Hughes            t.table.ScriptList.mapFeatures(featureMap)
493*e1fe3e4aSElliott Hughes
494*e1fe3e4aSElliott Hughes            t.table.FeatureList.FeatureCount = len(t.table.FeatureList.FeatureRecord)
495*e1fe3e4aSElliott Hughes
496*e1fe3e4aSElliott Hughes        if t.table.LookupList:
497*e1fe3e4aSElliott Hughes            # Collect unregistered (new) lookups.
498*e1fe3e4aSElliott Hughes            lookupMap = GregariousIdentityDict(t.table.LookupList.Lookup)
499*e1fe3e4aSElliott Hughes            t.table.FeatureList.mapLookups(lookupMap)
500*e1fe3e4aSElliott Hughes            t.table.LookupList.mapLookups(lookupMap)
501*e1fe3e4aSElliott Hughes
502*e1fe3e4aSElliott Hughes            # Record used lookups.
503*e1fe3e4aSElliott Hughes            lookupMap = AttendanceRecordingIdentityDict(t.table.LookupList.Lookup)
504*e1fe3e4aSElliott Hughes            t.table.FeatureList.mapLookups(lookupMap)
505*e1fe3e4aSElliott Hughes            t.table.LookupList.mapLookups(lookupMap)
506*e1fe3e4aSElliott Hughes            usedIndices = lookupMap.s
507*e1fe3e4aSElliott Hughes
508*e1fe3e4aSElliott Hughes            # Remove unused lookups
509*e1fe3e4aSElliott Hughes            t.table.LookupList.Lookup = [
510*e1fe3e4aSElliott Hughes                l for i, l in enumerate(t.table.LookupList.Lookup) if i in usedIndices
511*e1fe3e4aSElliott Hughes            ]
512*e1fe3e4aSElliott Hughes
513*e1fe3e4aSElliott Hughes            # Map back to indices.
514*e1fe3e4aSElliott Hughes            lookupMap = NonhashableDict(t.table.LookupList.Lookup)
515*e1fe3e4aSElliott Hughes            t.table.FeatureList.mapLookups(lookupMap)
516*e1fe3e4aSElliott Hughes            t.table.LookupList.mapLookups(lookupMap)
517*e1fe3e4aSElliott Hughes
518*e1fe3e4aSElliott Hughes            t.table.LookupList.LookupCount = len(t.table.LookupList.Lookup)
519*e1fe3e4aSElliott Hughes
520*e1fe3e4aSElliott Hughes            if GDEF and GDEF.table.Version >= 0x00010002:
521*e1fe3e4aSElliott Hughes                markFilteringSetMap = NonhashableDict(
522*e1fe3e4aSElliott Hughes                    GDEF.table.MarkGlyphSetsDef.Coverage
523*e1fe3e4aSElliott Hughes                )
524*e1fe3e4aSElliott Hughes                t.table.LookupList.mapMarkFilteringSets(markFilteringSetMap)
525*e1fe3e4aSElliott Hughes
526*e1fe3e4aSElliott Hughes    # TODO FeatureParams nameIDs
527