maxContextCalc.py 3.1 KB

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