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