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