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