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