_h_h_e_a.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. from fontTools.misc import sstruct
  2. from fontTools.misc.textTools import safeEval
  3. from fontTools.misc.fixedTools import (
  4. ensureVersionIsLong as fi2ve,
  5. versionToFixed as ve2fi,
  6. )
  7. from . import DefaultTable
  8. import math
  9. hheaFormat = """
  10. > # big endian
  11. tableVersion: L
  12. ascent: h
  13. descent: h
  14. lineGap: h
  15. advanceWidthMax: H
  16. minLeftSideBearing: h
  17. minRightSideBearing: h
  18. xMaxExtent: h
  19. caretSlopeRise: h
  20. caretSlopeRun: h
  21. caretOffset: h
  22. reserved0: h
  23. reserved1: h
  24. reserved2: h
  25. reserved3: h
  26. metricDataFormat: h
  27. numberOfHMetrics: H
  28. """
  29. class table__h_h_e_a(DefaultTable.DefaultTable):
  30. # Note: Keep in sync with table__v_h_e_a
  31. dependencies = ["hmtx", "glyf", "CFF ", "CFF2"]
  32. # OpenType spec renamed these, add aliases for compatibility
  33. @property
  34. def ascender(self):
  35. return self.ascent
  36. @ascender.setter
  37. def ascender(self, value):
  38. self.ascent = value
  39. @property
  40. def descender(self):
  41. return self.descent
  42. @descender.setter
  43. def descender(self, value):
  44. self.descent = value
  45. def decompile(self, data, ttFont):
  46. sstruct.unpack(hheaFormat, data, self)
  47. def compile(self, ttFont):
  48. if ttFont.recalcBBoxes and (
  49. ttFont.isLoaded("glyf")
  50. or ttFont.isLoaded("CFF ")
  51. or ttFont.isLoaded("CFF2")
  52. ):
  53. self.recalc(ttFont)
  54. self.tableVersion = fi2ve(self.tableVersion)
  55. return sstruct.pack(hheaFormat, self)
  56. def recalc(self, ttFont):
  57. if "hmtx" not in ttFont:
  58. return
  59. hmtxTable = ttFont["hmtx"]
  60. self.advanceWidthMax = max(adv for adv, _ in hmtxTable.metrics.values())
  61. boundsWidthDict = {}
  62. if "glyf" in ttFont:
  63. glyfTable = ttFont["glyf"]
  64. for name in ttFont.getGlyphOrder():
  65. g = glyfTable[name]
  66. if g.numberOfContours == 0:
  67. continue
  68. if g.numberOfContours < 0 and not hasattr(g, "xMax"):
  69. # Composite glyph without extents set.
  70. # Calculate those.
  71. g.recalcBounds(glyfTable)
  72. boundsWidthDict[name] = g.xMax - g.xMin
  73. elif "CFF " in ttFont or "CFF2" in ttFont:
  74. if "CFF " in ttFont:
  75. topDict = ttFont["CFF "].cff.topDictIndex[0]
  76. else:
  77. topDict = ttFont["CFF2"].cff.topDictIndex[0]
  78. charStrings = topDict.CharStrings
  79. for name in ttFont.getGlyphOrder():
  80. cs = charStrings[name]
  81. bounds = cs.calcBounds(charStrings)
  82. if bounds is not None:
  83. boundsWidthDict[name] = int(
  84. math.ceil(bounds[2]) - math.floor(bounds[0])
  85. )
  86. if boundsWidthDict:
  87. minLeftSideBearing = float("inf")
  88. minRightSideBearing = float("inf")
  89. xMaxExtent = -float("inf")
  90. for name, boundsWidth in boundsWidthDict.items():
  91. advanceWidth, lsb = hmtxTable[name]
  92. rsb = advanceWidth - lsb - boundsWidth
  93. extent = lsb + boundsWidth
  94. minLeftSideBearing = min(minLeftSideBearing, lsb)
  95. minRightSideBearing = min(minRightSideBearing, rsb)
  96. xMaxExtent = max(xMaxExtent, extent)
  97. self.minLeftSideBearing = minLeftSideBearing
  98. self.minRightSideBearing = minRightSideBearing
  99. self.xMaxExtent = xMaxExtent
  100. else: # No glyph has outlines.
  101. self.minLeftSideBearing = 0
  102. self.minRightSideBearing = 0
  103. self.xMaxExtent = 0
  104. def toXML(self, writer, ttFont):
  105. formatstring, names, fixes = sstruct.getformat(hheaFormat)
  106. for name in names:
  107. value = getattr(self, name)
  108. if name == "tableVersion":
  109. value = fi2ve(value)
  110. value = "0x%08x" % value
  111. writer.simpletag(name, value=value)
  112. writer.newline()
  113. def fromXML(self, name, attrs, content, ttFont):
  114. if name == "tableVersion":
  115. setattr(self, name, ve2fi(attrs["value"]))
  116. return
  117. setattr(self, name, safeEval(attrs["value"]))