Source code for fontTools.ttLib.tables._h_m_t_x

from fontTools.misc.roundTools import otRound
from fontTools import ttLib
from fontTools.misc.textTools import safeEval
from . import DefaultTable
import sys
import struct
import array
import logging


log = logging.getLogger(__name__)


[docs] class table__h_m_t_x(DefaultTable.DefaultTable): headerTag = "hhea" advanceName = "width" sideBearingName = "lsb" numberOfMetricsName = "numberOfHMetrics" longMetricFormat = "Hh"
[docs] def decompile(self, data, ttFont): numGlyphs = ttFont["maxp"].numGlyphs headerTable = ttFont.get(self.headerTag) if headerTable is not None: numberOfMetrics = int(getattr(headerTable, self.numberOfMetricsName)) else: numberOfMetrics = numGlyphs if numberOfMetrics > numGlyphs: log.warning( "The %s.%s exceeds the maxp.numGlyphs" % (self.headerTag, self.numberOfMetricsName) ) numberOfMetrics = numGlyphs if len(data) < 4 * numberOfMetrics: raise ttLib.TTLibError("not enough '%s' table data" % self.tableTag) # Note: advanceWidth is unsigned, but some font editors might # read/write as signed. We can't be sure whether it was a mistake # or not, so we read as unsigned but also issue a warning... metricsFmt = ">" + self.longMetricFormat * numberOfMetrics metrics = struct.unpack(metricsFmt, data[: 4 * numberOfMetrics]) data = data[4 * numberOfMetrics :] numberOfSideBearings = numGlyphs - numberOfMetrics sideBearings = array.array("h", data[: 2 * numberOfSideBearings]) data = data[2 * numberOfSideBearings :] if sys.byteorder != "big": sideBearings.byteswap() if data: log.warning("too much '%s' table data" % self.tableTag) self.metrics = {} glyphOrder = ttFont.getGlyphOrder() for i in range(numberOfMetrics): glyphName = glyphOrder[i] advanceWidth, lsb = metrics[i * 2 : i * 2 + 2] if advanceWidth > 32767: log.warning( "Glyph %r has a huge advance %s (%d); is it intentional or " "an (invalid) negative value?", glyphName, self.advanceName, advanceWidth, ) self.metrics[glyphName] = (advanceWidth, lsb) lastAdvance = metrics[-2] for i in range(numberOfSideBearings): glyphName = glyphOrder[i + numberOfMetrics] self.metrics[glyphName] = (lastAdvance, sideBearings[i])
[docs] def compile(self, ttFont): metrics = [] hasNegativeAdvances = False for glyphName in ttFont.getGlyphOrder(): advanceWidth, sideBearing = self.metrics[glyphName] if advanceWidth < 0: log.error( "Glyph %r has negative advance %s" % (glyphName, self.advanceName) ) hasNegativeAdvances = True metrics.append([advanceWidth, sideBearing]) headerTable = ttFont.get(self.headerTag) if headerTable is not None: lastAdvance = metrics[-1][0] lastIndex = len(metrics) while metrics[lastIndex - 2][0] == lastAdvance: lastIndex -= 1 if lastIndex <= 1: # all advances are equal lastIndex = 1 break additionalMetrics = metrics[lastIndex:] additionalMetrics = [otRound(sb) for _, sb in additionalMetrics] metrics = metrics[:lastIndex] numberOfMetrics = len(metrics) setattr(headerTable, self.numberOfMetricsName, numberOfMetrics) else: # no hhea/vhea, can't store numberOfMetrics; assume == numGlyphs numberOfMetrics = ttFont["maxp"].numGlyphs additionalMetrics = [] allMetrics = [] for advance, sb in metrics: allMetrics.extend([otRound(advance), otRound(sb)]) metricsFmt = ">" + self.longMetricFormat * numberOfMetrics try: data = struct.pack(metricsFmt, *allMetrics) except struct.error as e: if "out of range" in str(e) and hasNegativeAdvances: raise ttLib.TTLibError( "'%s' table can't contain negative advance %ss" % (self.tableTag, self.advanceName) ) else: raise additionalMetrics = array.array("h", additionalMetrics) if sys.byteorder != "big": additionalMetrics.byteswap() data = data + additionalMetrics.tobytes() return data
[docs] def toXML(self, writer, ttFont): names = sorted(self.metrics.keys()) for glyphName in names: advance, sb = self.metrics[glyphName] writer.simpletag( "mtx", [ ("name", glyphName), (self.advanceName, advance), (self.sideBearingName, sb), ], ) writer.newline()
[docs] def fromXML(self, name, attrs, content, ttFont): if not hasattr(self, "metrics"): self.metrics = {} if name == "mtx": self.metrics[attrs["name"]] = ( safeEval(attrs[self.advanceName]), safeEval(attrs[self.sideBearingName]), )
def __delitem__(self, glyphName): del self.metrics[glyphName] def __getitem__(self, glyphName): return self.metrics[glyphName] def __setitem__(self, glyphName, advance_sb_pair): self.metrics[glyphName] = tuple(advance_sb_pair)