# 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__)

    class table__h_m_t_x(DefaultTable.DefaultTable):

    	headerTag = 'hhea'
    	advanceName = 'width'
    	sideBearingName = 'lsb'
    	numberOfMetricsName = 'numberOfHMetrics'
    	longMetricFormat = 'Hh'

    	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])

    	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

    	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()

    	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)