• 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
9vheaFormat = """
10		>	# big endian
11		tableVersion:		L
12		ascent:			h
13		descent:		h
14		lineGap:		h
15		advanceHeightMax:	H
16		minTopSideBearing:	h
17		minBottomSideBearing:	h
18		yMaxExtent:		h
19		caretSlopeRise:		h
20		caretSlopeRun:		h
21		caretOffset:		h
22		reserved1:		h
23		reserved2:		h
24		reserved3:		h
25		reserved4:		h
26		metricDataFormat:	h
27		numberOfVMetrics:	H
28"""
29
30class table__v_h_e_a(DefaultTable.DefaultTable):
31
32	# Note: Keep in sync with table__h_h_e_a
33
34	dependencies = ['vmtx', 'glyf', 'CFF ', 'CFF2']
35
36	def decompile(self, data, ttFont):
37		sstruct.unpack(vheaFormat, data, self)
38
39	def compile(self, ttFont):
40		if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ') or ttFont.isLoaded('CFF2')):
41			self.recalc(ttFont)
42		self.tableVersion = fi2ve(self.tableVersion)
43		return sstruct.pack(vheaFormat, self)
44
45	def recalc(self, ttFont):
46		if 'vmtx' in ttFont:
47			vmtxTable = ttFont['vmtx']
48			self.advanceHeightMax = max(adv for adv, _ in vmtxTable.metrics.values())
49
50		boundsHeightDict = {}
51		if 'glyf' in ttFont:
52			glyfTable = ttFont['glyf']
53			for name in ttFont.getGlyphOrder():
54				g = glyfTable[name]
55				if g.numberOfContours == 0:
56					continue
57				if g.numberOfContours < 0 and not hasattr(g, "yMax"):
58					# Composite glyph without extents set.
59					# Calculate those.
60					g.recalcBounds(glyfTable)
61				boundsHeightDict[name] = g.yMax - g.yMin
62		elif 'CFF ' in ttFont or 'CFF2' in ttFont:
63			if 'CFF ' in ttFont:
64				topDict = ttFont['CFF '].cff.topDictIndex[0]
65			else:
66				topDict = ttFont['CFF2'].cff.topDictIndex[0]
67			charStrings = topDict.CharStrings
68			for name in ttFont.getGlyphOrder():
69				cs = charStrings[name]
70				bounds = cs.calcBounds(charStrings)
71				if bounds is not None:
72					boundsHeightDict[name] = int(
73						math.ceil(bounds[3]) - math.floor(bounds[1]))
74
75		if boundsHeightDict:
76			minTopSideBearing = float('inf')
77			minBottomSideBearing = float('inf')
78			yMaxExtent = -float('inf')
79			for name, boundsHeight in boundsHeightDict.items():
80				advanceHeight, tsb = vmtxTable[name]
81				bsb = advanceHeight - tsb - boundsHeight
82				extent = tsb + boundsHeight
83				minTopSideBearing = min(minTopSideBearing, tsb)
84				minBottomSideBearing = min(minBottomSideBearing, bsb)
85				yMaxExtent = max(yMaxExtent, extent)
86			self.minTopSideBearing = minTopSideBearing
87			self.minBottomSideBearing = minBottomSideBearing
88			self.yMaxExtent = yMaxExtent
89
90		else:  # No glyph has outlines.
91			self.minTopSideBearing = 0
92			self.minBottomSideBearing = 0
93			self.yMaxExtent = 0
94
95	def toXML(self, writer, ttFont):
96		formatstring, names, fixes = sstruct.getformat(vheaFormat)
97		for name in names:
98			value = getattr(self, name)
99			if name == "tableVersion":
100				value = fi2ve(value)
101				value = "0x%08x" % value
102			writer.simpletag(name, value=value)
103			writer.newline()
104
105	def fromXML(self, name, attrs, content, ttFont):
106		if name == "tableVersion":
107			setattr(self, name, ve2fi(attrs["value"]))
108			return
109		setattr(self, name, safeEval(attrs["value"]))
110
111	# reserved0 is caretOffset for legacy reasons
112	@property
113	def reserved0(self):
114		return self.caretOffset
115
116	@reserved0.setter
117	def reserved0(self, value):
118		self.caretOffset = value
119