| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 | from fontTools.misc import sstructfrom fontTools.misc.textTools import safeEvalfrom fontTools.misc.fixedTools import (    ensureVersionIsLong as fi2ve,    versionToFixed as ve2fi,)from . import DefaultTableimport mathvheaFormat = """		>	# 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):    # 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
 |