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