builder.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. from fontTools import ttLib
  2. from fontTools.ttLib.tables import otTables as ot
  3. # VariationStore
  4. def buildVarRegionAxis(axisSupport):
  5. self = ot.VarRegionAxis()
  6. self.StartCoord, self.PeakCoord, self.EndCoord = [float(v) for v in axisSupport]
  7. return self
  8. def buildVarRegion(support, axisTags):
  9. assert all(tag in axisTags for tag in support.keys()), (
  10. "Unknown axis tag found.",
  11. support,
  12. axisTags,
  13. )
  14. self = ot.VarRegion()
  15. self.VarRegionAxis = []
  16. for tag in axisTags:
  17. self.VarRegionAxis.append(buildVarRegionAxis(support.get(tag, (0, 0, 0))))
  18. return self
  19. def buildVarRegionList(supports, axisTags):
  20. self = ot.VarRegionList()
  21. self.RegionAxisCount = len(axisTags)
  22. self.Region = []
  23. for support in supports:
  24. self.Region.append(buildVarRegion(support, axisTags))
  25. self.RegionCount = len(self.Region)
  26. return self
  27. def _reorderItem(lst, mapping):
  28. return [lst[i] for i in mapping]
  29. def VarData_calculateNumShorts(self, optimize=False):
  30. count = self.VarRegionCount
  31. items = self.Item
  32. bit_lengths = [0] * count
  33. for item in items:
  34. # The "+ (i < -1)" magic is to handle two's-compliment.
  35. # That is, we want to get back 7 for -128, whereas
  36. # bit_length() returns 8. Similarly for -65536.
  37. # The reason "i < -1" is used instead of "i < 0" is that
  38. # the latter would make it return 0 for "-1" instead of 1.
  39. bl = [(i + (i < -1)).bit_length() for i in item]
  40. bit_lengths = [max(*pair) for pair in zip(bl, bit_lengths)]
  41. # The addition of 8, instead of seven, is to account for the sign bit.
  42. # This "((b + 8) >> 3) if b else 0" when combined with the above
  43. # "(i + (i < -1)).bit_length()" is a faster way to compute byte-lengths
  44. # conforming to:
  45. #
  46. # byte_length = (0 if i == 0 else
  47. # 1 if -128 <= i < 128 else
  48. # 2 if -65536 <= i < 65536 else
  49. # ...)
  50. byte_lengths = [((b + 8) >> 3) if b else 0 for b in bit_lengths]
  51. # https://github.com/fonttools/fonttools/issues/2279
  52. longWords = any(b > 2 for b in byte_lengths)
  53. if optimize:
  54. # Reorder columns such that wider columns come before narrower columns
  55. mapping = []
  56. mapping.extend(i for i, b in enumerate(byte_lengths) if b > 2)
  57. mapping.extend(i for i, b in enumerate(byte_lengths) if b == 2)
  58. mapping.extend(i for i, b in enumerate(byte_lengths) if b == 1)
  59. byte_lengths = _reorderItem(byte_lengths, mapping)
  60. self.VarRegionIndex = _reorderItem(self.VarRegionIndex, mapping)
  61. self.VarRegionCount = len(self.VarRegionIndex)
  62. for i in range(len(items)):
  63. items[i] = _reorderItem(items[i], mapping)
  64. if longWords:
  65. self.NumShorts = (
  66. max((i for i, b in enumerate(byte_lengths) if b > 2), default=-1) + 1
  67. )
  68. self.NumShorts |= 0x8000
  69. else:
  70. self.NumShorts = (
  71. max((i for i, b in enumerate(byte_lengths) if b > 1), default=-1) + 1
  72. )
  73. self.VarRegionCount = len(self.VarRegionIndex)
  74. return self
  75. ot.VarData.calculateNumShorts = VarData_calculateNumShorts
  76. def VarData_CalculateNumShorts(self, optimize=True):
  77. """Deprecated name for VarData_calculateNumShorts() which
  78. defaults to optimize=True. Use varData.calculateNumShorts()
  79. or varData.optimize()."""
  80. return VarData_calculateNumShorts(self, optimize=optimize)
  81. def VarData_optimize(self):
  82. return VarData_calculateNumShorts(self, optimize=True)
  83. ot.VarData.optimize = VarData_optimize
  84. def buildVarData(varRegionIndices, items, optimize=True):
  85. self = ot.VarData()
  86. self.VarRegionIndex = list(varRegionIndices)
  87. regionCount = self.VarRegionCount = len(self.VarRegionIndex)
  88. records = self.Item = []
  89. if items:
  90. for item in items:
  91. assert len(item) == regionCount
  92. records.append(list(item))
  93. self.ItemCount = len(self.Item)
  94. self.calculateNumShorts(optimize=optimize)
  95. return self
  96. def buildVarStore(varRegionList, varDataList):
  97. self = ot.VarStore()
  98. self.Format = 1
  99. self.VarRegionList = varRegionList
  100. self.VarData = list(varDataList)
  101. self.VarDataCount = len(self.VarData)
  102. return self
  103. # Variation helpers
  104. def buildVarIdxMap(varIdxes, glyphOrder):
  105. self = ot.VarIdxMap()
  106. self.mapping = {g: v for g, v in zip(glyphOrder, varIdxes)}
  107. return self
  108. def buildDeltaSetIndexMap(varIdxes):
  109. mapping = list(varIdxes)
  110. if all(i == v for i, v in enumerate(mapping)):
  111. return None
  112. self = ot.DeltaSetIndexMap()
  113. self.mapping = mapping
  114. self.Format = 1 if len(mapping) > 0xFFFF else 0
  115. return self
  116. def buildVarDevTable(varIdx):
  117. self = ot.Device()
  118. self.DeltaFormat = 0x8000
  119. self.StartSize = varIdx >> 16
  120. self.EndSize = varIdx & 0xFFFF
  121. return self