# Source code for fontTools.ttLib.tables.V_D_M_X_

    from . import DefaultTable
    from fontTools.misc import sstruct
    from fontTools.misc.textTools import safeEval
    import struct

    VDMX_HeaderFmt = """
    	>                 # big endian
    	version:     H    # Version number (0 or 1)
    	numRecs:     H    # Number of VDMX groups present
    	numRatios:   H    # Number of aspect ratio groupings
    """
    # the VMDX header is followed by an array of RatRange[numRatios] (i.e. aspect
    # ratio ranges);
    VDMX_RatRangeFmt = """
    	>                 # big endian
    	bCharSet:    B    # Character set
    	xRatio:      B    # Value to use for x-Ratio
    	yStartRatio: B    # Starting y-Ratio value
    	yEndRatio:   B    # Ending y-Ratio value
    """
    # followed by an array of offset[numRatios] from start of VDMX table to the
    # VDMX Group for this ratio range (offsets will be re-calculated on compile);
    # followed by an array of Group[numRecs] records;
    VDMX_GroupFmt = """
    	>                 # big endian
    	recs:        H    # Number of height records in this group
    	startsz:     B    # Starting yPelHeight
    	endsz:       B    # Ending yPelHeight
    """
    # followed by an array of vTable[recs] records.
    VDMX_vTableFmt = """
    	>                 # big endian
    	yPelHeight:  H    # yPelHeight to which values apply
    	yMax:        h    # Maximum value (in pels) for this yPelHeight
    	yMin:        h    # Minimum value (in pels) for this yPelHeight
    """

    class table_V_D_M_X_(DefaultTable.DefaultTable):

    	def decompile(self, data, ttFont):
    		pos = 0  # track current position from to start of VDMX table
    		dummy, data = sstruct.unpack2(VDMX_HeaderFmt, data, self)
    		pos += sstruct.calcsize(VDMX_HeaderFmt)
    		self.ratRanges = []
    		for i in range(self.numRatios):
    			ratio, data = sstruct.unpack2(VDMX_RatRangeFmt, data)
    			pos += sstruct.calcsize(VDMX_RatRangeFmt)
    			# the mapping between a ratio and a group is defined further below
    			ratio['groupIndex'] = None
    			self.ratRanges.append(ratio)
    		lenOffset = struct.calcsize('>H')
    		_offsets = []  # temporarily store offsets to groups
    		for i in range(self.numRatios):
    			offset = struct.unpack('>H', data[0:lenOffset])[0]
    			data = data[lenOffset:]
    			pos += lenOffset
    			_offsets.append(offset)
    		self.groups = []
    		for groupIndex in range(self.numRecs):
    			# the offset to this group from beginning of the VDMX table
    			currOffset = pos
    			group, data = sstruct.unpack2(VDMX_GroupFmt, data)
    			# the group lenght and bounding sizes are re-calculated on compile
    			recs = group.pop('recs')
    			startsz = group.pop('startsz')
    			endsz = group.pop('endsz')
    			pos += sstruct.calcsize(VDMX_GroupFmt)
    			for j in range(recs):
    				vTable, data = sstruct.unpack2(VDMX_vTableFmt, data)
    				vTableLength = sstruct.calcsize(VDMX_vTableFmt)
    				pos += vTableLength
    				# group is a dict of (yMax, yMin) tuples keyed by yPelHeight
    				group[vTable['yPelHeight']] = (vTable['yMax'], vTable['yMin'])
    			# make sure startsz and endsz match the calculated values
    			minSize = min(group.keys())
    			maxSize = max(group.keys())
    			assert startsz == minSize, \
    				"startsz (%s) must equal min yPelHeight (%s): group %d" % \
    				(group.startsz, minSize, groupIndex)
    			assert endsz == maxSize, \
    				"endsz (%s) must equal max yPelHeight (%s): group %d" % \
    				(group.endsz, maxSize, groupIndex)
    			self.groups.append(group)
    			# match the defined offsets with the current group's offset
    			for offsetIndex, offsetValue in enumerate(_offsets):
    				# when numRecs < numRatios there can more than one ratio range
    				# sharing the same VDMX group
    				if currOffset == offsetValue:
    					# map the group with the ratio range thas has the same
    					# index as the offset to that group (it took me a while..)
    					self.ratRanges[offsetIndex]['groupIndex'] = groupIndex
    		# check that all ratio ranges have a group
    		for i in range(self.numRatios):
    			ratio = self.ratRanges[i]
    			if ratio['groupIndex'] is None:
    				from fontTools import ttLib
    				raise ttLib.TTLibError(
    					"no group defined for ratRange %d" % i)

    	def _getOffsets(self):
    		"""
    		Calculate offsets to VDMX_Group records.
    		For each ratRange return a list of offset values from the beginning of
    		the VDMX table to a VDMX_Group.
    		"""
    		lenHeader = sstruct.calcsize(VDMX_HeaderFmt)
    		lenRatRange = sstruct.calcsize(VDMX_RatRangeFmt)
    		lenOffset = struct.calcsize('>H')
    		lenGroupHeader = sstruct.calcsize(VDMX_GroupFmt)
    		lenVTable = sstruct.calcsize(VDMX_vTableFmt)
    		# offset to the first group
    		pos = lenHeader + self.numRatios*lenRatRange + self.numRatios*lenOffset
    		groupOffsets = []
    		for group in self.groups:
    			groupOffsets.append(pos)
    			lenGroup = lenGroupHeader + len(group) * lenVTable
    			pos += lenGroup  # offset to next group
    		offsets = []
    		for ratio in self.ratRanges:
    			groupIndex = ratio['groupIndex']
    			offsets.append(groupOffsets[groupIndex])
    		return offsets

    	def compile(self, ttFont):
    		if not(self.version == 0 or self.version == 1):
    			from fontTools import ttLib
    			raise ttLib.TTLibError(
    				"unknown format for VDMX table: version %s" % self.version)
    		data = sstruct.pack(VDMX_HeaderFmt, self)
    		for ratio in self.ratRanges:
    			data += sstruct.pack(VDMX_RatRangeFmt, ratio)
    		# recalculate offsets to VDMX groups
    		for offset in self._getOffsets():
    			data += struct.pack('>H', offset)
    		for group in self.groups:
    			recs = len(group)
    			startsz = min(group.keys())
    			endsz = max(group.keys())
    			gHeader = {'recs': recs, 'startsz': startsz, 'endsz': endsz}
    			data += sstruct.pack(VDMX_GroupFmt, gHeader)
    			for yPelHeight, (yMax, yMin) in sorted(group.items()):
    				vTable = {'yPelHeight': yPelHeight, 'yMax': yMax, 'yMin': yMin}
    				data += sstruct.pack(VDMX_vTableFmt, vTable)
    		return data

    	def toXML(self, writer, ttFont):
    		writer.simpletag("version", value=self.version)
    		writer.newline()
    		writer.begintag("ratRanges")
    		writer.newline()
    		for ratio in self.ratRanges:
    			groupIndex = ratio['groupIndex']
    			writer.simpletag(
    				"ratRange",
    				bCharSet=ratio['bCharSet'],
    				xRatio=ratio['xRatio'],
    				yStartRatio=ratio['yStartRatio'],
    				yEndRatio=ratio['yEndRatio'],
    				groupIndex=groupIndex
    				)
    			writer.newline()
    		writer.endtag("ratRanges")
    		writer.newline()
    		writer.begintag("groups")
    		writer.newline()
    		for groupIndex in range(self.numRecs):
    			group = self.groups[groupIndex]
    			recs = len(group)
    			startsz = min(group.keys())
    			endsz = max(group.keys())
    			writer.begintag("group", index=groupIndex)
    			writer.newline()
    			writer.comment("recs=%d, startsz=%d, endsz=%d" %
    							(recs, startsz, endsz))
    			writer.newline()
    			for yPelHeight, (yMax, yMin) in sorted(group.items()):
    				writer.simpletag(
    					"record",
    					[('yPelHeight', yPelHeight), ('yMax', yMax), ('yMin', yMin)])
    				writer.newline()
    			writer.endtag("group")
    			writer.newline()
    		writer.endtag("groups")
    		writer.newline()

    	def fromXML(self, name, attrs, content, ttFont):
    		if name == "version":
    			self.version = safeEval(attrs["value"])
    		elif name == "ratRanges":
    			if not hasattr(self, "ratRanges"):
    				self.ratRanges = []
    			for element in content:
    				if not isinstance(element, tuple):
    					continue
    				name, attrs, content = element
    				if name == "ratRange":
    					if not hasattr(self, "numRatios"):
    						self.numRatios = 1
    					else:
    						self.numRatios += 1
    					ratio = {
    						"bCharSet": safeEval(attrs["bCharSet"]),
    						"xRatio": safeEval(attrs["xRatio"]),
    						"yStartRatio": safeEval(attrs["yStartRatio"]),
    						"yEndRatio": safeEval(attrs["yEndRatio"]),
    						"groupIndex": safeEval(attrs["groupIndex"])
    						}
    					self.ratRanges.append(ratio)
    		elif name == "groups":
    			if not hasattr(self, "groups"):
    				self.groups = []
    			for element in content:
    				if not isinstance(element, tuple):
    					continue
    				name, attrs, content = element
    				if name == "group":
    					if not hasattr(self, "numRecs"):
    						self.numRecs = 1
    					else:
    						self.numRecs += 1
    					group = {}
    					for element in content:
    						if not isinstance(element, tuple):
    							continue
    						name, attrs, content = element
    						if name == "record":
    							yPelHeight = safeEval(attrs["yPelHeight"])
    							yMax = safeEval(attrs["yMax"])
    							yMin = safeEval(attrs["yMin"])
    							group[yPelHeight] = (yMax, yMin)
    					self.groups.append(group)