1__all__ = ["maxCtxFont"] 2 3 4def maxCtxFont(font): 5 """Calculate the usMaxContext value for an entire font.""" 6 7 maxCtx = 0 8 for tag in ("GSUB", "GPOS"): 9 if tag not in font: 10 continue 11 table = font[tag].table 12 if not table.LookupList: 13 continue 14 for lookup in table.LookupList.Lookup: 15 for st in lookup.SubTable: 16 maxCtx = maxCtxSubtable(maxCtx, tag, lookup.LookupType, st) 17 return maxCtx 18 19 20def maxCtxSubtable(maxCtx, tag, lookupType, st): 21 """Calculate usMaxContext based on a single lookup table (and an existing 22 max value). 23 """ 24 25 # single positioning, single / multiple substitution 26 if (tag == "GPOS" and lookupType == 1) or ( 27 tag == "GSUB" and lookupType in (1, 2, 3) 28 ): 29 maxCtx = max(maxCtx, 1) 30 31 # pair positioning 32 elif tag == "GPOS" and lookupType == 2: 33 maxCtx = max(maxCtx, 2) 34 35 # ligatures 36 elif tag == "GSUB" and lookupType == 4: 37 for ligatures in st.ligatures.values(): 38 for ligature in ligatures: 39 maxCtx = max(maxCtx, ligature.CompCount) 40 41 # context 42 elif (tag == "GPOS" and lookupType == 7) or (tag == "GSUB" and lookupType == 5): 43 maxCtx = maxCtxContextualSubtable(maxCtx, st, "Pos" if tag == "GPOS" else "Sub") 44 45 # chained context 46 elif (tag == "GPOS" and lookupType == 8) or (tag == "GSUB" and lookupType == 6): 47 maxCtx = maxCtxContextualSubtable( 48 maxCtx, st, "Pos" if tag == "GPOS" else "Sub", "Chain" 49 ) 50 51 # extensions 52 elif (tag == "GPOS" and lookupType == 9) or (tag == "GSUB" and lookupType == 7): 53 maxCtx = maxCtxSubtable(maxCtx, tag, st.ExtensionLookupType, st.ExtSubTable) 54 55 # reverse-chained context 56 elif tag == "GSUB" and lookupType == 8: 57 maxCtx = maxCtxContextualRule(maxCtx, st, "Reverse") 58 59 return maxCtx 60 61 62def maxCtxContextualSubtable(maxCtx, st, ruleType, chain=""): 63 """Calculate usMaxContext based on a contextual feature subtable.""" 64 65 if st.Format == 1: 66 for ruleset in getattr(st, "%s%sRuleSet" % (chain, ruleType)): 67 if ruleset is None: 68 continue 69 for rule in getattr(ruleset, "%s%sRule" % (chain, ruleType)): 70 if rule is None: 71 continue 72 maxCtx = maxCtxContextualRule(maxCtx, rule, chain) 73 74 elif st.Format == 2: 75 for ruleset in getattr(st, "%s%sClassSet" % (chain, ruleType)): 76 if ruleset is None: 77 continue 78 for rule in getattr(ruleset, "%s%sClassRule" % (chain, ruleType)): 79 if rule is None: 80 continue 81 maxCtx = maxCtxContextualRule(maxCtx, rule, chain) 82 83 elif st.Format == 3: 84 maxCtx = maxCtxContextualRule(maxCtx, st, chain) 85 86 return maxCtx 87 88 89def maxCtxContextualRule(maxCtx, st, chain): 90 """Calculate usMaxContext based on a contextual feature rule.""" 91 92 if not chain: 93 return max(maxCtx, st.GlyphCount) 94 elif chain == "Reverse": 95 return max(maxCtx, st.GlyphCount + st.LookAheadGlyphCount) 96 return max(maxCtx, st.InputGlyphCount + st.LookAheadGlyphCount) 97