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