# Source code for fontTools.ttLib.tables.E_B_D_T_

    from fontTools.misc import sstruct
    from fontTools.misc.textTools import bytechr, byteord, bytesjoin, strjoin, safeEval, readHex, hexStr, deHexStr
    from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
    from . import DefaultTable
    import itertools
    import os
    import struct
    import logging

    log = logging.getLogger(__name__)

    ebdtTableVersionFormat = """
    	> # big endian
    	version: 16.16F
    """

    ebdtComponentFormat = """
    	> # big endian
    	glyphCode: H
    	xOffset:   b
    	yOffset:   b
    """

    class table_E_B_D_T_(DefaultTable.DefaultTable):

    	# Keep a reference to the name of the data locator table.
    	locatorName = 'EBLC'

    	# This method can be overridden in subclasses to support new formats
    	# without changing the other implementation. Also can be used as a
    	# convenience method for coverting a font file to an alternative format.
    	def getImageFormatClass(self, imageFormat):
    		return ebdt_bitmap_classes[imageFormat]

    	def decompile(self, data, ttFont):
    		# Get the version but don't advance the slice.
    		# Most of the lookup for this table is done relative
    		# to the begining so slice by the offsets provided
    		# in the EBLC table.
    		sstruct.unpack2(ebdtTableVersionFormat, data, self)

    		# Keep a dict of glyphs that have been seen so they aren't remade.
    		# This dict maps intervals of data to the BitmapGlyph.
    		glyphDict = {}

    		# Pull out the EBLC table and loop through glyphs.
    		# A strike is a concept that spans both tables.
    		# The actual bitmap data is stored in the EBDT.
    		locator = ttFont[self.__class__.locatorName]
    		self.strikeData = []
    		for curStrike in locator.strikes:
    			bitmapGlyphDict = {}
    			self.strikeData.append(bitmapGlyphDict)
    			for indexSubTable in curStrike.indexSubTables:
    				dataIter = zip(indexSubTable.names, indexSubTable.locations)
    				for curName, curLoc in dataIter:
    					# Don't create duplicate data entries for the same glyphs.
    					# Instead just use the structures that already exist if they exist.
    					if curLoc in glyphDict:
    						curGlyph = glyphDict[curLoc]
    					else:
    						curGlyphData = data[slice(*curLoc)]
    						imageFormatClass = self.getImageFormatClass(indexSubTable.imageFormat)
    						curGlyph = imageFormatClass(curGlyphData, ttFont)
    						glyphDict[curLoc] = curGlyph
    					bitmapGlyphDict[curName] = curGlyph

    	def compile(self, ttFont):

    		dataList = []
    		dataList.append(sstruct.pack(ebdtTableVersionFormat, self))
    		dataSize = len(dataList[0])

    		# Keep a dict of glyphs that have been seen so they aren't remade.
    		# This dict maps the id of the BitmapGlyph to the interval
    		# in the data.
    		glyphDict = {}

    		# Go through the bitmap glyph data. Just in case the data for a glyph
    		# changed the size metrics should be recalculated. There are a variety
    		# of formats and they get stored in the EBLC table. That is why
    		# recalculation is defered to the EblcIndexSubTable class and just
    		# pass what is known about bitmap glyphs from this particular table.
    		locator = ttFont[self.__class__.locatorName]
    		for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
    			for curIndexSubTable in curStrike.indexSubTables:
    				dataLocations = []
    				for curName in curIndexSubTable.names:
    					# Handle the data placement based on seeing the glyph or not.
    					# Just save a reference to the location if the glyph has already
    					# been saved in compile. This code assumes that glyphs will only
    					# be referenced multiple times from indexFormat5. By luck the
    					# code may still work when referencing poorly ordered fonts with
    					# duplicate references. If there is a font that is unlucky the
    					# respective compile methods for the indexSubTables will fail
    					# their assertions. All fonts seem to follow this assumption.
    					# More complicated packing may be needed if a counter-font exists.
    					glyph = curGlyphDict[curName]
    					objectId = id(glyph)
    					if objectId not in glyphDict:
    						data = glyph.compile(ttFont)
    						data = curIndexSubTable.padBitmapData(data)
    						startByte = dataSize
    						dataSize += len(data)
    						endByte = dataSize
    						dataList.append(data)
    						dataLoc = (startByte, endByte)
    						glyphDict[objectId] = dataLoc
    					else:
    						dataLoc = glyphDict[objectId]
    					dataLocations.append(dataLoc)
    				# Just use the new data locations in the indexSubTable.
    				# The respective compile implementations will take care
    				# of any of the problems in the convertion that may arise.
    				curIndexSubTable.locations = dataLocations

    		return bytesjoin(dataList)

    	def toXML(self, writer, ttFont):
    		# When exporting to XML if one of the data export formats
    		# requires metrics then those metrics may be in the locator.
    		# In this case populate the bitmaps with "export metrics".
    		if ttFont.bitmapGlyphDataFormat in ('row', 'bitwise'):
    			locator = ttFont[self.__class__.locatorName]
    			for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
    				for curIndexSubTable in curStrike.indexSubTables:
    					for curName in curIndexSubTable.names:
    						glyph = curGlyphDict[curName]
    						# I'm not sure which metrics have priority here.
    						# For now if both metrics exist go with glyph metrics.
    						if hasattr(glyph, 'metrics'):
    							glyph.exportMetrics = glyph.metrics
    						else:
    							glyph.exportMetrics = curIndexSubTable.metrics
    						glyph.exportBitDepth = curStrike.bitmapSizeTable.bitDepth

    		writer.simpletag("header", [('version', self.version)])
    		writer.newline()
    		locator = ttFont[self.__class__.locatorName]
    		for strikeIndex, bitmapGlyphDict in enumerate(self.strikeData):
    			writer.begintag('strikedata', [('index', strikeIndex)])
    			writer.newline()
    			for curName, curBitmap in bitmapGlyphDict.items():
    				curBitmap.toXML(strikeIndex, curName, writer, ttFont)
    			writer.endtag('strikedata')
    			writer.newline()

    	def fromXML(self, name, attrs, content, ttFont):
    		if name == 'header':
    			self.version = safeEval(attrs['version'])
    		elif name == 'strikedata':
    			if not hasattr(self, 'strikeData'):
    				self.strikeData = []
    			strikeIndex = safeEval(attrs['index'])

    			bitmapGlyphDict = {}
    			for element in content:
    				if not isinstance(element, tuple):
    					continue
    				name, attrs, content = element
    				if name[4:].startswith(_bitmapGlyphSubclassPrefix[4:]):
    					imageFormat = safeEval(name[len(_bitmapGlyphSubclassPrefix):])
    					glyphName = attrs['name']
    					imageFormatClass = self.getImageFormatClass(imageFormat)
    					curGlyph = imageFormatClass(None, None)
    					curGlyph.fromXML(name, attrs, content, ttFont)
    					assert glyphName not in bitmapGlyphDict, "Duplicate glyphs with the same name '%s' in the same strike." % glyphName
    					bitmapGlyphDict[glyphName] = curGlyph
    				else:
    					log.warning("%s being ignored by %s", name, self.__class__.__name__)

    			# Grow the strike data array to the appropriate size. The XML
    			# format allows the strike index value to be out of order.
    			if strikeIndex >= len(self.strikeData):
    				self.strikeData += [None] * (strikeIndex + 1 - len(self.strikeData))
    			assert self.strikeData[strikeIndex] is None, "Duplicate strike EBDT indices."
    			self.strikeData[strikeIndex] = bitmapGlyphDict

    class EbdtComponent(object):

    	def toXML(self, writer, ttFont):
    		writer.begintag('ebdtComponent', [('name', self.name)])
    		writer.newline()
    		for componentName in sstruct.getformat(ebdtComponentFormat)[1][1:]:
    			writer.simpletag(componentName, value=getattr(self, componentName))
    			writer.newline()
    		writer.endtag('ebdtComponent')
    		writer.newline()

    	def fromXML(self, name, attrs, content, ttFont):
    		self.name = attrs['name']
    		componentNames = set(sstruct.getformat(ebdtComponentFormat)[1][1:])
    		for element in content:
    			if not isinstance(element, tuple):
    				continue
    			name, attrs, content = element
    			if name in componentNames:
    				vars(self)[name] = safeEval(attrs['value'])
    			else:
    				log.warning("unknown name '%s' being ignored by EbdtComponent.", name)

    # Helper functions for dealing with binary.

    def _data2binary(data, numBits):
    	binaryList = []
    	for curByte in data:
    		value = byteord(curByte)
    		numBitsCut = min(8, numBits)
    		for i in range(numBitsCut):
    			if value & 0x1:
    				binaryList.append('1')
    			else:
    				binaryList.append('0')
    			value = value >> 1
    		numBits -= numBitsCut
    	return strjoin(binaryList)

    def _binary2data(binary):
    	byteList = []
    	for bitLoc in range(0, len(binary), 8):
    		byteString = binary[bitLoc:bitLoc+8]
    		curByte = 0
    		for curBit in reversed(byteString):
    			curByte = curByte << 1
    			if curBit == '1':
    				curByte |= 1
    		byteList.append(bytechr(curByte))
    	return bytesjoin(byteList)

    def _memoize(f):
    	class memodict(dict):
    		def __missing__(self, key):
    			ret = f(key)
    			if len(key) == 1:
    				self[key] = ret
    			return ret
    	return memodict().__getitem__

    # 00100111 -> 11100100 per byte, not to be confused with little/big endian.
    # Bitmap data per byte is in the order that binary is written on the page
    # with the least significant bit as far right as possible. This is the
    # opposite of what makes sense algorithmically and hence this function.
    @_memoize
    def _reverseBytes(data):
    	if len(data) != 1:
    		return bytesjoin(map(_reverseBytes, data))
    	byte = byteord(data)
    	result = 0
    	for i in range(8):
    		result = result << 1
    		result |= byte & 1
    		byte = byte >> 1
    	return bytechr(result)

    # This section of code is for reading and writing image data to/from XML.

    def _writeRawImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
    	writer.begintag('rawimagedata')
    	writer.newline()
    	writer.dumphex(bitmapObject.imageData)
    	writer.endtag('rawimagedata')
    	writer.newline()

    def _readRawImageData(bitmapObject, name, attrs, content, ttFont):
    	bitmapObject.imageData = readHex(content)

    def _writeRowImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
    	metrics = bitmapObject.exportMetrics
    	del bitmapObject.exportMetrics
    	bitDepth = bitmapObject.exportBitDepth
    	del bitmapObject.exportBitDepth

    	writer.begintag('rowimagedata', bitDepth=bitDepth, width=metrics.width, height=metrics.height)
    	writer.newline()
    	for curRow in range(metrics.height):
    		rowData = bitmapObject.getRow(curRow, bitDepth=bitDepth, metrics=metrics)
    		writer.simpletag('row', value=hexStr(rowData))
    		writer.newline()
    	writer.endtag('rowimagedata')
    	writer.newline()

    def _readRowImageData(bitmapObject, name, attrs, content, ttFont):
    	bitDepth = safeEval(attrs['bitDepth'])
    	metrics = SmallGlyphMetrics()
    	metrics.width = safeEval(attrs['width'])
    	metrics.height = safeEval(attrs['height'])

    	dataRows = []
    	for element in content:
    		if not isinstance(element, tuple):
    			continue
    		name, attr, content = element
    		# Chop off 'imagedata' from the tag to get just the option.
    		if name == 'row':
    			dataRows.append(deHexStr(attr['value']))
    	bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics)

    def _writeBitwiseImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
    	metrics = bitmapObject.exportMetrics
    	del bitmapObject.exportMetrics
    	bitDepth = bitmapObject.exportBitDepth
    	del bitmapObject.exportBitDepth

    	# A dict for mapping binary to more readable/artistic ASCII characters.
    	binaryConv = {'0':'.', '1':'@'}

    	writer.begintag('bitwiseimagedata', bitDepth=bitDepth, width=metrics.width, height=metrics.height)
    	writer.newline()
    	for curRow in range(metrics.height):
    		rowData = bitmapObject.getRow(curRow, bitDepth=1, metrics=metrics, reverseBytes=True)
    		rowData = _data2binary(rowData, metrics.width)
    		# Make the output a readable ASCII art form.
    		rowData = strjoin(map(binaryConv.get, rowData))
    		writer.simpletag('row', value=rowData)
    		writer.newline()
    	writer.endtag('bitwiseimagedata')
    	writer.newline()

    def _readBitwiseImageData(bitmapObject, name, attrs, content, ttFont):
    	bitDepth = safeEval(attrs['bitDepth'])
    	metrics = SmallGlyphMetrics()
    	metrics.width = safeEval(attrs['width'])
    	metrics.height = safeEval(attrs['height'])

    	# A dict for mapping from ASCII to binary. All characters are considered
    	# a '1' except space, period and '0' which maps to '0'.
    	binaryConv = {' ':'0', '.':'0', '0':'0'}

    	dataRows = []
    	for element in content:
    		if not isinstance(element, tuple):
    			continue
    		name, attr, content = element
    		if name == 'row':
    			mapParams = zip(attr['value'], itertools.repeat('1'))
    			rowData = strjoin(itertools.starmap(binaryConv.get, mapParams))
    			dataRows.append(_binary2data(rowData))

    	bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics, reverseBytes=True)

    def _writeExtFileImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
    	try:
    		folder = os.path.dirname(writer.file.name)
    	except AttributeError:
    		# fall back to current directory if output file's directory isn't found
    		folder = '.'
    	folder = os.path.join(folder, 'bitmaps')
    	filename = glyphName + bitmapObject.fileExtension
    	if not os.path.isdir(folder):
    		os.makedirs(folder)
    	folder = os.path.join(folder, 'strike%d' % strikeIndex)
    	if not os.path.isdir(folder):
    		os.makedirs(folder)

    	fullPath = os.path.join(folder, filename)
    	writer.simpletag('extfileimagedata', value=fullPath)
    	writer.newline()

    	with open(fullPath, "wb") as file:
    		file.write(bitmapObject.imageData)

    def _readExtFileImageData(bitmapObject, name, attrs, content, ttFont):
    	fullPath = attrs['value']
    	with open(fullPath, "rb") as file:
    		bitmapObject.imageData = file.read()

    # End of XML writing code.

    # Important information about the naming scheme. Used for identifying formats
    # in XML.
    _bitmapGlyphSubclassPrefix = 'ebdt_bitmap_format_'

    class BitmapGlyph(object):

    	# For the external file format. This can be changed in subclasses. This way
    	# when the extfile option is turned on files have the form: glyphName.ext
    	# The default is just a flat binary file with no meaning.
    	fileExtension = '.bin'

    	# Keep track of reading and writing of various forms.
    	xmlDataFunctions = {
    		'raw':		(_writeRawImageData, _readRawImageData),
    		'row':		(_writeRowImageData, _readRowImageData),
    		'bitwise':	(_writeBitwiseImageData, _readBitwiseImageData),
    		'extfile':	(_writeExtFileImageData, _readExtFileImageData),
    		}

    	def __init__(self, data, ttFont):
    		self.data = data
    		self.ttFont = ttFont
    		# TODO Currently non-lazy decompilation is untested here...
    		#if not ttFont.lazy:
    		#	self.decompile()
    		#	del self.data

    	def __getattr__(self, attr):
    		# Allow lazy decompile.
    		if attr[:2] == '__':
    			raise AttributeError(attr)
    		if attr == "data":
    			raise AttributeError(attr)
    		self.decompile()
    		del self.data
    		return getattr(self, attr)

    	def ensureDecompiled(self, recurse=False):
    		if hasattr(self, "data"):
    			self.decompile()
    			del self.data

    	# Not a fan of this but it is needed for safer safety checking.
    	def getFormat(self):
    		return safeEval(self.__class__.__name__[len(_bitmapGlyphSubclassPrefix):])

    	def toXML(self, strikeIndex, glyphName, writer, ttFont):
    		writer.begintag(self.__class__.__name__, [('name', glyphName)])
    		writer.newline()

    		self.writeMetrics(writer, ttFont)
    		# Use the internal write method to write using the correct output format.
    		self.writeData(strikeIndex, glyphName, writer, ttFont)

    		writer.endtag(self.__class__.__name__)
    		writer.newline()

    	def fromXML(self, name, attrs, content, ttFont):
    		self.readMetrics(name, attrs, content, ttFont)
    		for element in content:
    			if not isinstance(element, tuple):
    				continue
    			name, attr, content = element
    			if not name.endswith('imagedata'):
    				continue
    			# Chop off 'imagedata' from the tag to get just the option.
    			option = name[:-len('imagedata')]
    			assert option in self.__class__.xmlDataFunctions
    			self.readData(name, attr, content, ttFont)

    	# Some of the glyphs have the metrics. This allows for metrics to be
    	# added if the glyph format has them. Default behavior is to do nothing.
    	def writeMetrics(self, writer, ttFont):
    		pass

    	# The opposite of write metrics.
    	def readMetrics(self, name, attrs, content, ttFont):
    		pass

    	def writeData(self, strikeIndex, glyphName, writer, ttFont):
    		try:
    			writeFunc, readFunc = self.__class__.xmlDataFunctions[ttFont.bitmapGlyphDataFormat]
    		except KeyError:
    			writeFunc = _writeRawImageData
    		writeFunc(strikeIndex, glyphName, self, writer, ttFont)

    	def readData(self, name, attrs, content, ttFont):
    		# Chop off 'imagedata' from the tag to get just the option.
    		option = name[:-len('imagedata')]
    		writeFunc, readFunc = self.__class__.xmlDataFunctions[option]
    		readFunc(self, name, attrs, content, ttFont)

    # A closure for creating a mixin for the two types of metrics handling.
    # Most of the code is very similar so its easier to deal with here.
    # Everything works just by passing the class that the mixin is for.
    def _createBitmapPlusMetricsMixin(metricsClass):
    	# Both metrics names are listed here to make meaningful error messages.
    	metricStrings = [BigGlyphMetrics.__name__, SmallGlyphMetrics.__name__]
    	curMetricsName = metricsClass.__name__
    	# Find which metrics this is for and determine the opposite name.
    	metricsId = metricStrings.index(curMetricsName)
    	oppositeMetricsName = metricStrings[1-metricsId]

    	class BitmapPlusMetricsMixin(object):

    		def writeMetrics(self, writer, ttFont):
    			self.metrics.toXML(writer, ttFont)

    		def readMetrics(self, name, attrs, content, ttFont):
    			for element in content:
    				if not isinstance(element, tuple):
    					continue
    				name, attrs, content = element
    				if name == curMetricsName:
    					self.metrics = metricsClass()
    					self.metrics.fromXML(name, attrs, content, ttFont)
    				elif name == oppositeMetricsName:
    					log.warning("Warning: %s being ignored in format %d.", oppositeMetricsName, self.getFormat())

    	return BitmapPlusMetricsMixin

    # Since there are only two types of mixin's just create them here.
    BitmapPlusBigMetricsMixin = _createBitmapPlusMetricsMixin(BigGlyphMetrics)
    BitmapPlusSmallMetricsMixin = _createBitmapPlusMetricsMixin(SmallGlyphMetrics)

    # Data that is bit aligned can be tricky to deal with. These classes implement
    # helper functionality for dealing with the data and getting a particular row
    # of bitwise data. Also helps implement fancy data export/import in XML.
    class BitAlignedBitmapMixin(object):

    	def _getBitRange(self, row, bitDepth, metrics):
    		rowBits = (bitDepth * metrics.width)
    		bitOffset = row * rowBits
    		return (bitOffset, bitOffset+rowBits)

    	def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
    		if metrics is None:
    			metrics = self.metrics
    		assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"

    		# Loop through each byte. This can cover two bytes in the original data or
    		# a single byte if things happen to be aligned. The very last entry might
    		# not be aligned so take care to trim the binary data to size and pad with
    		# zeros in the row data. Bit aligned data is somewhat tricky.
    		#
    		# Example of data cut. Data cut represented in x's.
    		# '|' represents byte boundary.
    		# data = ...0XX|XXXXXX00|000... => XXXXXXXX
    		#		or
    		# data = ...0XX|XXXX0000|000... => XXXXXX00
    		#   or
    		# data = ...000|XXXXXXXX|000... => XXXXXXXX
    		#   or
    		# data = ...000|00XXXX00|000... => XXXX0000
    		#
    		dataList = []
    		bitRange = self._getBitRange(row, bitDepth, metrics)
    		stepRange = bitRange + (8,)
    		for curBit in range(*stepRange):
    			endBit = min(curBit+8, bitRange[1])
    			numBits = endBit - curBit
    			cutPoint = curBit % 8
    			firstByteLoc = curBit // 8
    			secondByteLoc = endBit // 8
    			if firstByteLoc < secondByteLoc:
    				numBitsCut = 8 - cutPoint
    			else:
    				numBitsCut = endBit - curBit
    			curByte = _reverseBytes(self.imageData[firstByteLoc])
    			firstHalf = byteord(curByte) >> cutPoint
    			firstHalf = ((1<<numBitsCut)-1) & firstHalf
    			newByte = firstHalf
    			if firstByteLoc < secondByteLoc and secondByteLoc < len(self.imageData):
    				curByte = _reverseBytes(self.imageData[secondByteLoc])
    				secondHalf = byteord(curByte) << numBitsCut
    				newByte = (firstHalf | secondHalf) & ((1<<numBits)-1)
    			dataList.append(bytechr(newByte))

    		# The way the data is kept is opposite the algorithm used.
    		data = bytesjoin(dataList)
    		if not reverseBytes:
    			data = _reverseBytes(data)
    		return data

    	def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
    		if metrics is None:
    			metrics = self.metrics
    		if not reverseBytes:
    			dataRows = list(map(_reverseBytes, dataRows))

    		# Keep track of a list of ordinal values as they are easier to modify
    		# than a list of strings. Map to actual strings later.
    		numBytes = (self._getBitRange(len(dataRows), bitDepth, metrics)[0] + 7) // 8
    		ordDataList = [0] * numBytes
    		for row, data in enumerate(dataRows):
    			bitRange = self._getBitRange(row, bitDepth, metrics)
    			stepRange = bitRange + (8,)
    			for curBit, curByte in zip(range(*stepRange), data):
    				endBit = min(curBit+8, bitRange[1])
    				cutPoint = curBit % 8
    				firstByteLoc = curBit // 8
    				secondByteLoc = endBit // 8
    				if firstByteLoc < secondByteLoc:
    					numBitsCut = 8 - cutPoint
    				else:
    					numBitsCut = endBit - curBit
    				curByte = byteord(curByte)
    				firstByte = curByte & ((1<<numBitsCut)-1)
    				ordDataList[firstByteLoc] |= (firstByte << cutPoint)
    				if firstByteLoc < secondByteLoc and secondByteLoc < numBytes:
    					secondByte = (curByte >> numBitsCut) & ((1<<8-numBitsCut)-1)
    					ordDataList[secondByteLoc] |= secondByte

    		# Save the image data with the bits going the correct way.
    		self.imageData = _reverseBytes(bytesjoin(map(bytechr, ordDataList)))

    class ByteAlignedBitmapMixin(object):

    	def _getByteRange(self, row, bitDepth, metrics):
    		rowBytes = (bitDepth * metrics.width + 7) // 8
    		byteOffset = row * rowBytes
    		return (byteOffset, byteOffset+rowBytes)

    	def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
    		if metrics is None:
    			metrics = self.metrics
    		assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
    		byteRange = self._getByteRange(row, bitDepth, metrics)
    		data = self.imageData[slice(*byteRange)]
    		if reverseBytes:
    			data = _reverseBytes(data)
    		return data

    	def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
    		if metrics is None:
    			metrics = self.metrics
    		if reverseBytes:
    			dataRows = map(_reverseBytes, dataRows)
    		self.imageData = bytesjoin(dataRows)

    class ebdt_bitmap_format_1(ByteAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph):

    	def decompile(self):
    		self.metrics = SmallGlyphMetrics()
    		dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
    		self.imageData = data

    	def compile(self, ttFont):
    		data = sstruct.pack(smallGlyphMetricsFormat, self.metrics)
    		return data + self.imageData

    class ebdt_bitmap_format_2(BitAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph):

    	def decompile(self):
    		self.metrics = SmallGlyphMetrics()
    		dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
    		self.imageData = data

    	def compile(self, ttFont):
    		data = sstruct.pack(smallGlyphMetricsFormat, self.metrics)
    		return data + self.imageData

    class ebdt_bitmap_format_5(BitAlignedBitmapMixin, BitmapGlyph):

    	def decompile(self):
    		self.imageData = self.data

    	def compile(self, ttFont):
    		return self.imageData

    class ebdt_bitmap_format_6(ByteAlignedBitmapMixin, BitmapPlusBigMetricsMixin, BitmapGlyph):

    	def decompile(self):
    		self.metrics = BigGlyphMetrics()
    		dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
    		self.imageData = data

    	def compile(self, ttFont):
    		data = sstruct.pack(bigGlyphMetricsFormat, self.metrics)
    		return data + self.imageData

    class ebdt_bitmap_format_7(BitAlignedBitmapMixin, BitmapPlusBigMetricsMixin, BitmapGlyph):

    	def decompile(self):
    		self.metrics = BigGlyphMetrics()
    		dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
    		self.imageData = data

    	def compile(self, ttFont):
    		data = sstruct.pack(bigGlyphMetricsFormat, self.metrics)
    		return data + self.imageData

    class ComponentBitmapGlyph(BitmapGlyph):

    	def toXML(self, strikeIndex, glyphName, writer, ttFont):
    		writer.begintag(self.__class__.__name__, [('name', glyphName)])
    		writer.newline()

    		self.writeMetrics(writer, ttFont)

    		writer.begintag('components')
    		writer.newline()
    		for curComponent in self.componentArray:
    			curComponent.toXML(writer, ttFont)
    		writer.endtag('components')
    		writer.newline()

    		writer.endtag(self.__class__.__name__)
    		writer.newline()

    	def fromXML(self, name, attrs, content, ttFont):
    		self.readMetrics(name, attrs, content, ttFont)
    		for element in content:
    			if not isinstance(element, tuple):
    				continue
    			name, attr, content = element
    			if name == 'components':
    				self.componentArray = []
    				for compElement in content:
    					if not isinstance(compElement, tuple):
    						continue
    					name, attrs, content = compElement
    					if name == 'ebdtComponent':
    						curComponent = EbdtComponent()
    						curComponent.fromXML(name, attrs, content, ttFont)
    						self.componentArray.append(curComponent)
    					else:
    						log.warning("'%s' being ignored in component array.", name)

    class ebdt_bitmap_format_8(BitmapPlusSmallMetricsMixin, ComponentBitmapGlyph):

    	def decompile(self):
    		self.metrics = SmallGlyphMetrics()
    		dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
    		data = data[1:]

    		(numComponents,) = struct.unpack(">H", data[:2])
    		data = data[2:]
    		self.componentArray = []
    		for i in range(numComponents):
    			curComponent = EbdtComponent()
    			dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
    			curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
    			self.componentArray.append(curComponent)

    	def compile(self, ttFont):
    		dataList = []
    		dataList.append(sstruct.pack(smallGlyphMetricsFormat, self.metrics))
    		dataList.append(b'\0')
    		dataList.append(struct.pack(">H", len(self.componentArray)))
    		for curComponent in self.componentArray:
    			curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
    			dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
    		return bytesjoin(dataList)

    class ebdt_bitmap_format_9(BitmapPlusBigMetricsMixin, ComponentBitmapGlyph):

    	def decompile(self):
    		self.metrics = BigGlyphMetrics()
    		dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
    		(numComponents,) = struct.unpack(">H", data[:2])
    		data = data[2:]
    		self.componentArray = []
    		for i in range(numComponents):
    			curComponent = EbdtComponent()
    			dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
    			curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
    			self.componentArray.append(curComponent)

    	def compile(self, ttFont):
    		dataList = []
    		dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
    		dataList.append(struct.pack(">H", len(self.componentArray)))
    		for curComponent in self.componentArray:
    			curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
    			dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
    		return bytesjoin(dataList)

    # Dictionary of bitmap formats to the class representing that format
    # currently only the ones listed in this map are the ones supported.
    ebdt_bitmap_classes = {
    		1: ebdt_bitmap_format_1,
    		2: ebdt_bitmap_format_2,
    		5: ebdt_bitmap_format_5,
    		6: ebdt_bitmap_format_6,
    		7: ebdt_bitmap_format_7,
    		8: ebdt_bitmap_format_8,
    		9: ebdt_bitmap_format_9,
    	}