11ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import
230e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import *
38413c108d21e8cf0e9059bbfffde8d13f2616340Behdad Esfahbodfrom fontTools.misc import sstruct
4c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainefrom fontTools.misc.textTools import safeEval, readHex, hexStr, deHexStr
52b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
630e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom . import DefaultTable
730e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport itertools
830e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport os
930e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport struct
10c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
11c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontaineebdtTableVersionFormat = """
12c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	> # big endian
13c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	version: 16.16F
14c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine"""
15c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
16c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontaineebdtComponentFormat = """
17c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	> # big endian
18c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	glyphCode: H
19c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	xOffset:   b
20c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	yOffset:   b
21c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine"""
22c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
23c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass table_E_B_D_T_(DefaultTable.DefaultTable):
24c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
25c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# Keep a reference to the name of the data locator table.
26c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	locatorName = 'EBLC'
27c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
28c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# This method can be overridden in subclasses to support new formats
29c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# without changing the other implementation. Also can be used as a
30c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# convenience method for coverting a font file to an alternative format.
31c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def getImageFormatClass(self, imageFormat):
32c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return ebdt_bitmap_classes[imageFormat]
33c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
34c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self, data, ttFont):
35c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Get the version but don't advance the slice.
36c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Most of the lookup for this table is done relative
37c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# to the begining so slice by the offsets provided
38c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# in the EBLC table.
39c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		sstruct.unpack2(ebdtTableVersionFormat, data, self)
40c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
41c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Keep a dict of glyphs that have been seen so they aren't remade.
42c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# This dict maps intervals of data to the BitmapGlyph.
43c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		glyphDict = {}
44c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
45c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Pull out the EBLC table and loop through glyphs.
46c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# A strike is a concept that spans both tables.
47c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# The actual bitmap data is stored in the EBDT.
48c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		locator = ttFont[self.__class__.locatorName]
49c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.strikeData = []
50c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for curStrike in locator.strikes:
51c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			bitmapGlyphDict = {}
52c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			self.strikeData.append(bitmapGlyphDict)
53c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			for indexSubTable in curStrike.indexSubTables:
544b775ee5f16b7feef22dd796f2faefb366f535f3Behdad Esfahbod				dataIter = zip(indexSubTable.names, indexSubTable.locations)
55c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				for curName, curLoc in dataIter:
56c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					# Don't create duplicate data entries for the same glyphs.
57c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					# Instead just use the structures that already exist if they exist.
58c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					if curLoc in glyphDict:
59c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						curGlyph = glyphDict[curLoc]
60c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					else:
61c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						curGlyphData = data[slice(*curLoc)]
62c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						imageFormatClass = self.getImageFormatClass(indexSubTable.imageFormat)
63c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						curGlyph = imageFormatClass(curGlyphData, ttFont)
64c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						glyphDict[curLoc] = curGlyph
65c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					bitmapGlyphDict[curName] = curGlyph
66c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
67c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
68c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
69c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList = []
70c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(sstruct.pack(ebdtTableVersionFormat, self))
71c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataSize = len(dataList[0])
72c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
73c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Keep a dict of glyphs that have been seen so they aren't remade.
74c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# This dict maps the id of the BitmapGlyph to the interval
75c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# in the data.
76c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		glyphDict = {}
77c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
78c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Go through the bitmap glyph data. Just in case the data for a glyph
79c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# changed the size metrics should be recalculated. There are a variety
80c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# of formats and they get stored in the EBLC table. That is why
81c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# recalculation is defered to the EblcIndexSubTable class and just
82c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# pass what is known about bitmap glyphs from this particular table.
83c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		locator = ttFont[self.__class__.locatorName]
844b775ee5f16b7feef22dd796f2faefb366f535f3Behdad Esfahbod		for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
85c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			for curIndexSubTable in curStrike.indexSubTables:
86c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				dataLocations = []
87c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				for curName in curIndexSubTable.names:
88c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					# Handle the data placement based on seeing the glyph or not.
89c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					# Just save a reference to the location if the glyph has already
90c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					# been saved in compile. This code assumes that glyphs will only
91c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					# be referenced multiple times from indexFormat5. By luck the
92c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					# code may still work when referencing poorly ordered fonts with
93c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					# duplicate references. If there is a font that is unlucky the
94c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					# respective compile methods for the indexSubTables will fail
95c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					# their assertions. All fonts seem to follow this assumption.
96c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					# More complicated packing may be needed if a counter-font exists.
97c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					glyph = curGlyphDict[curName]
98c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					objectId = id(glyph)
99c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					if objectId not in glyphDict:
100c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						data = glyph.compile(ttFont)
101c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						data = curIndexSubTable.padBitmapData(data)
102c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						startByte = dataSize
103c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						dataSize += len(data)
104c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						endByte = dataSize
105c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						dataList.append(data)
106c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						dataLoc = (startByte, endByte)
107c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						glyphDict[objectId] = dataLoc
108c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					else:
109c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						dataLoc = glyphDict[objectId]
110c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					dataLocations.append(dataLoc)
111c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				# Just use the new data locations in the indexSubTable.
112c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				# The respective compile implementations will take care
113c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				# of any of the problems in the convertion that may arise.
114c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				curIndexSubTable.locations = dataLocations
115c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
11618316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		return bytesjoin(dataList)
117c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
118c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def toXML(self, writer, ttFont):
119c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# When exporting to XML if one of the data export formats
120c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# requires metrics then those metrics may be in the locator.
121c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# In this case populate the bitmaps with "export metrics".
122c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if ttFont.bitmapGlyphDataFormat in ('row', 'bitwise'):
123c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			locator = ttFont[self.__class__.locatorName]
1244b775ee5f16b7feef22dd796f2faefb366f535f3Behdad Esfahbod			for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
125c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				for curIndexSubTable in curStrike.indexSubTables:
126c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					for curName in curIndexSubTable.names:
127c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						glyph = curGlyphDict[curName]
128c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						# I'm not sure which metrics have priority here.
129c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						# For now if both metrics exist go with glyph metrics.
130c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						if hasattr(glyph, 'metrics'):
131c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine							glyph.exportMetrics = glyph.metrics
132c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						else:
133c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine							glyph.exportMetrics = curIndexSubTable.metrics
134c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						glyph.exportBitDepth = curStrike.bitmapSizeTable.bitDepth
135c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
136c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.simpletag("header", [('version', self.version)])
137c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
138c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		locator = ttFont[self.__class__.locatorName]
139c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for strikeIndex, bitmapGlyphDict in enumerate(self.strikeData):
140c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.begintag('strikedata', [('index', strikeIndex)])
141c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.newline()
142c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			for curName, curBitmap in bitmapGlyphDict.items():
143c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				curBitmap.toXML(strikeIndex, curName, writer, ttFont)
144c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.endtag('strikedata')
145c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.newline()
146c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
1473a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
148c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if name == 'header':
149c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			self.version = safeEval(attrs['version'])
150c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		elif name == 'strikedata':
151c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if not hasattr(self, 'strikeData'):
152c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				self.strikeData = []
153c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			strikeIndex = safeEval(attrs['index'])
154c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
155c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			bitmapGlyphDict = {}
156c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			for element in content:
157b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod				if not isinstance(element, tuple):
158c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					continue
159c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				name, attrs, content = element
160c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				if name[4:].startswith(_bitmapGlyphSubclassPrefix[4:]):
161c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					imageFormat =	safeEval(name[len(_bitmapGlyphSubclassPrefix):])
162c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					glyphName = attrs['name']
163c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					imageFormatClass = self.getImageFormatClass(imageFormat)
164c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					curGlyph = imageFormatClass(None, None)
1653a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod					curGlyph.fromXML(name, attrs, content, ttFont)
166c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					assert glyphName not in bitmapGlyphDict, "Duplicate glyphs with the same name '%s' in the same strike." % glyphName
167c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					bitmapGlyphDict[glyphName] = curGlyph
168c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				else:
1693ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod					print("Warning: %s being ignored by %s", name, self.__class__.__name__)
170c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
171c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# Grow the strike data array to the appropriate size. The XML
172c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# format allows the strike index value to be out of order.
173c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if strikeIndex >= len(self.strikeData):
174c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				self.strikeData += [None] * (strikeIndex + 1 - len(self.strikeData))
1759e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod			assert self.strikeData[strikeIndex] is None, "Duplicate strike EBDT indices."
176c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			self.strikeData[strikeIndex] = bitmapGlyphDict
177c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
178e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass EbdtComponent(object):
179c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
180c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def toXML(self, writer, ttFont):
181c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.begintag('ebdtComponent', [('name', self.name)])
182c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
183c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for componentName in sstruct.getformat(ebdtComponentFormat)[1][1:]:
184c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.simpletag(componentName, value=getattr(self, componentName))
185c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.newline()
186c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.endtag('ebdtComponent')
187c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
188c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
1893a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
190c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.name = attrs['name']
191c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		componentNames = set(sstruct.getformat(ebdtComponentFormat)[1][1:])
192c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for element in content:
193b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
194c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				continue
195c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			name, attrs, content = element
196c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if name in componentNames:
197c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				vars(self)[name] = safeEval(attrs['value'])
198c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			else:
1993ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod				print("Warning: unknown name '%s' being ignored by EbdtComponent." % name)
200c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
201c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# Helper functions for dealing with binary.
202c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
203c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainedef _data2binary(data, numBits):
204c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	binaryList = []
205c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	for curByte in data:
206319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod		value = byteord(curByte)
207c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		numBitsCut = min(8, numBits)
20897dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		for i in range(numBitsCut):
209c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if value & 0x1:
210c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				binaryList.append('1')
211c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			else:
212c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				binaryList.append('0')
213c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			value = value >> 1
214c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		numBits -= numBitsCut
21518316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod	return strjoin(binaryList)
216c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
217c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainedef _binary2data(binary):
218c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	byteList = []
21997dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod	for bitLoc in range(0, len(binary), 8):
220c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		byteString = binary[bitLoc:bitLoc+8]
221c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		curByte = 0
222c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for curBit in reversed(byteString):
223c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curByte = curByte << 1
224c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if curBit == '1':
225c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				curByte |= 1
226b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod		byteList.append(bytechr(curByte))
22718316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod	return bytesjoin(byteList)
228c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
229c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainedef _memoize(f):
230c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	class memodict(dict):
231c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		def __missing__(self, key):
232c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			ret = f(key)
233c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if len(key) == 1:
234c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				self[key] = ret
235c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			return ret
236c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	return memodict().__getitem__
237c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
238c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# 00100111 -> 11100100 per byte, not to be confused with little/big endian.
239c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# Bitmap data per byte is in the order that binary is written on the page
240c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# with the least significant bit as far right as possible. This is the
241c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# opposite of what makes sense algorithmically and hence this function.
242c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine@_memoize
243c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainedef _reverseBytes(data):
244c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	if len(data) != 1:
24518316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		return bytesjoin(map(_reverseBytes, data))
246319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod	byte = byteord(data)
247c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	result = 0
24897dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod	for i in range(8):
249c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		result = result << 1
250c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		result |= byte & 1
251c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		byte = byte >> 1
252b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod	return bytechr(result)
253c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
254c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# This section of code is for reading and writing image data to/from XML.
255c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
256c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainedef _writeRawImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
257c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.begintag('rawimagedata')
258c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.newline()
259c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.dumphex(bitmapObject.imageData)
260c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.endtag('rawimagedata')
261c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.newline()
262c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
2633a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahboddef _readRawImageData(bitmapObject, name, attrs, content, ttFont):
264c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	bitmapObject.imageData = readHex(content)
265c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
266c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainedef _writeRowImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
267c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	metrics = bitmapObject.exportMetrics
268c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	del bitmapObject.exportMetrics
269c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	bitDepth = bitmapObject.exportBitDepth
270c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	del bitmapObject.exportBitDepth
271c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
272c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.begintag('rowimagedata', bitDepth=bitDepth, width=metrics.width, height=metrics.height)
273c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.newline()
27497dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod	for curRow in range(metrics.height):
275c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		rowData = bitmapObject.getRow(curRow, bitDepth=bitDepth, metrics=metrics)
276c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.simpletag('row', value=hexStr(rowData))
277c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
278c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.endtag('rowimagedata')
279c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.newline()
280c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
2813a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahboddef _readRowImageData(bitmapObject, name, attrs, content, ttFont):
282c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	bitDepth = safeEval(attrs['bitDepth'])
283c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	metrics = SmallGlyphMetrics()
284c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	metrics.width = safeEval(attrs['width'])
285c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	metrics.height = safeEval(attrs['height'])
286c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
287c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	dataRows = []
288c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	for element in content:
289b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod		if not isinstance(element, tuple):
290c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			continue
291c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		name, attr, content = element
292c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Chop off 'imagedata' from the tag to get just the option.
293c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if name == 'row':
294c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataRows.append(deHexStr(attr['value']))
295c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics)
296c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
297c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainedef _writeBitwiseImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
298c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	metrics = bitmapObject.exportMetrics
299c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	del bitmapObject.exportMetrics
300c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	bitDepth = bitmapObject.exportBitDepth
301c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	del bitmapObject.exportBitDepth
302c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
303c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# A dict for mapping binary to more readable/artistic ASCII characters.
304c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	binaryConv = {'0':'.', '1':'@'}
305c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
306c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.begintag('bitwiseimagedata', bitDepth=bitDepth, width=metrics.width, height=metrics.height)
307c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.newline()
30897dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod	for curRow in range(metrics.height):
309c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		rowData = bitmapObject.getRow(curRow, bitDepth=1, metrics=metrics, reverseBytes=True)
310c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		rowData = _data2binary(rowData, metrics.width)
311c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Make the output a readable ASCII art form.
31218316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		rowData = strjoin(map(binaryConv.get, rowData))
313c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.simpletag('row', value=rowData)
314c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
315c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.endtag('bitwiseimagedata')
316c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.newline()
317c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
3183a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahboddef _readBitwiseImageData(bitmapObject, name, attrs, content, ttFont):
319c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	bitDepth = safeEval(attrs['bitDepth'])
320c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	metrics = SmallGlyphMetrics()
321c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	metrics.width = safeEval(attrs['width'])
322c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	metrics.height = safeEval(attrs['height'])
323c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
324c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# A dict for mapping from ASCII to binary. All characters are considered
325c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# a '1' except space, period and '0' which maps to '0'.
326c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	binaryConv = {' ':'0', '.':'0', '0':'0'}
327c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
328c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	dataRows = []
329c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	for element in content:
330b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod		if not isinstance(element, tuple):
331c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			continue
332c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		name, attr, content = element
333c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if name == 'row':
3344b775ee5f16b7feef22dd796f2faefb366f535f3Behdad Esfahbod			mapParams = zip(attr['value'], itertools.repeat('1'))
33518316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod			rowData = strjoin(itertools.starmap(binaryConv.get, mapParams))
336c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataRows.append(_binary2data(rowData))
337c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
338c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics, reverseBytes=True)
339c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
340c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainedef _writeExtFileImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
341c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	folder = 'bitmaps/'
342c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	filename = glyphName + bitmapObject.fileExtension
343c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	if not os.path.isdir(folder):
344c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		os.makedirs(folder)
345c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	folder += 'strike%d/' % strikeIndex
346c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	if not os.path.isdir(folder):
347c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		os.makedirs(folder)
348c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
349c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	fullPath = folder + filename
350c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.simpletag('extfileimagedata', value=fullPath)
351c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	writer.newline()
352c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
353c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	with open(fullPath, "wb") as file:
354c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		file.write(bitmapObject.imageData)
355c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
3563a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahboddef _readExtFileImageData(bitmapObject, name, attrs, content, ttFont):
357c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	fullPath = attrs['value']
358c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	with open(fullPath, "rb") as file:
359c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		bitmapObject.imageData = file.read()
360c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
361c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# End of XML writing code.
362c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
363c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# Important information about the naming scheme. Used for identifying formats
364c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# in XML.
365c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine_bitmapGlyphSubclassPrefix = 'ebdt_bitmap_format_'
366c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
367e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass BitmapGlyph(object):
368c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
369c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# For the external file format. This can be changed in subclasses. This way
370c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# when the extfile option is turned on files have the form: glyphName.ext
371c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# The default is just a flat binary file with no meaning.
372c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	fileExtension = '.bin'
373c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
374c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# Keep track of reading and writing of various forms.
375c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	xmlDataFunctions = {
376c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		'raw':       (_writeRawImageData, _readRawImageData),
377c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		'row':       (_writeRowImageData, _readRowImageData),
378c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		'bitwise':   (_writeBitwiseImageData, _readBitwiseImageData),
379c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		'extfile':   (_writeExtFileImageData, _readExtFileImageData),
380c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		}
381c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
382c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def __init__(self, data, ttFont):
383c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.data = data
384c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.ttFont = ttFont
3854be8db031d5df7a3bdd1e4e0fa02de0246cb20aeBehdad Esfahbod		# TODO Currently non-lazy decompilation is untested here...
3864be8db031d5df7a3bdd1e4e0fa02de0246cb20aeBehdad Esfahbod		#if not ttFont.lazy:
3874be8db031d5df7a3bdd1e4e0fa02de0246cb20aeBehdad Esfahbod		#	self.decompile()
3884be8db031d5df7a3bdd1e4e0fa02de0246cb20aeBehdad Esfahbod		#	del self.data
389c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
390c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def __getattr__(self, attr):
391c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Allow lazy decompile.
392c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if attr[:2] == '__':
393cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise AttributeError(attr)
3943d8d5cd10d2d30ba9997b7cc43336e504111be9aBehdad Esfahbod		if not hasattr(self, "data"):
395cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise AttributeError(attr)
396c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.decompile()
3973d8d5cd10d2d30ba9997b7cc43336e504111be9aBehdad Esfahbod		del self.data
398c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return getattr(self, attr)
399c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
400c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# Not a fan of this but it is needed for safer safety checking.
401c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def getFormat(self):
402c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return safeEval(self.__class__.__name__[len(_bitmapGlyphSubclassPrefix):])
403c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
404c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def toXML(self, strikeIndex, glyphName, writer, ttFont):
405c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.begintag(self.__class__.__name__, [('name', glyphName)])
406c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
407c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
408c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.writeMetrics(writer, ttFont)
409c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Use the internal write method to write using the correct output format.
410c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.writeData(strikeIndex, glyphName, writer, ttFont)
411c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
412c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.endtag(self.__class__.__name__)
413c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
414c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
4153a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
4163a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod		self.readMetrics(name, attrs, content, ttFont)
417c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for element in content:
418b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
419c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				continue
420c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			name, attr, content = element
421faaca764a1cd62d376f783df20cd1edce21ca816Behdad Esfahbod			if not name.endswith('imagedata'):
422faaca764a1cd62d376f783df20cd1edce21ca816Behdad Esfahbod				continue
423c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# Chop off 'imagedata' from the tag to get just the option.
424c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			option = name[:-len('imagedata')]
425faaca764a1cd62d376f783df20cd1edce21ca816Behdad Esfahbod			assert option in self.__class__.xmlDataFunctions
426faaca764a1cd62d376f783df20cd1edce21ca816Behdad Esfahbod			self.readData(name, attrs, content, ttFont)
427c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
428c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# Some of the glyphs have the metrics. This allows for metrics to be
429c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# added if the glyph format has them. Default behavior is to do nothing.
430c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def writeMetrics(self, writer, ttFont):
431c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		pass
432c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
433c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# The opposite of write metrics.
4343a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def readMetrics(self, name, attrs, content, ttFont):
435c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		pass
436c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
437c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def writeData(self, strikeIndex, glyphName, writer, ttFont):
438c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		try:
439c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writeFunc, readFunc = self.__class__.xmlDataFunctions[ttFont.bitmapGlyphDataFormat]
440c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		except KeyError:
441c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writeFunc = _writeRawImageData
442c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writeFunc(strikeIndex, glyphName, self, writer, ttFont)
443c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
4443a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def readData(self, name, attrs, content, ttFont):
445c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Chop off 'imagedata' from the tag to get just the option.
446c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		option = name[:-len('imagedata')]
447c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writeFunc, readFunc = self.__class__.xmlDataFunctions[option]
4483a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod		readFunc(self, name, attrs, content, ttFont)
449c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
450c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
451c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# A closure for creating a mixin for the two types of metrics handling.
452c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# Most of the code is very similar so its easier to deal with here.
453c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# Everything works just by passing the class that the mixin is for.
454c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainedef _createBitmapPlusMetricsMixin(metricsClass):
455c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# Both metrics names are listed here to make meaningful error messages.
456c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	metricStrings = [BigGlyphMetrics.__name__, SmallGlyphMetrics.__name__]
457c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	curMetricsName = metricsClass.__name__
458c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# Find which metrics this is for and determine the opposite name.
459c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	metricsId = metricStrings.index(curMetricsName)
460c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	oppositeMetricsName = metricStrings[1-metricsId]
461c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
462e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbod	class BitmapPlusMetricsMixin(object):
463c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
464c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		def writeMetrics(self, writer, ttFont):
465c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			self.metrics.toXML(writer, ttFont)
466c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
4673a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod		def readMetrics(self, name, attrs, content, ttFont):
468c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			for element in content:
469b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod				if not isinstance(element, tuple):
470c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					continue
471c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				name, attrs, content = element
472c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				if name == curMetricsName:
473c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					self.metrics = metricsClass()
4743a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod					self.metrics.fromXML(name, attrs, content, ttFont)
475c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				elif name == oppositeMetricsName:
4763ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod					print("Warning: %s being ignored in format %d." % oppositeMetricsName, self.getFormat())
477c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
478c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	return BitmapPlusMetricsMixin
479c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
480c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# Since there are only two types of mixin's just create them here.
481c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontaineBitmapPlusBigMetricsMixin = _createBitmapPlusMetricsMixin(BigGlyphMetrics)
482c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontaineBitmapPlusSmallMetricsMixin = _createBitmapPlusMetricsMixin(SmallGlyphMetrics)
483c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
484c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# Data that is bit aligned can be tricky to deal with. These classes implement
485c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# helper functionality for dealing with the data and getting a particular row
486c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# of bitwise data. Also helps implement fancy data export/import in XML.
487e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass BitAlignedBitmapMixin(object):
488c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
489c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def _getBitRange(self, row, bitDepth, metrics):
490c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		rowBits = (bitDepth * metrics.width)
491c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		bitOffset = row * rowBits
492c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return (bitOffset, bitOffset+rowBits)
493c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
494c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
4959e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod		if metrics is None:
496c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			metrics = self.metrics
497c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
498c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
499c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Loop through each byte. This can cover two bytes in the original data or
500c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# a single byte if things happen to be aligned. The very last entry might
501c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# not be aligned so take care to trim the binary data to size and pad with
502c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# zeros in the row data. Bit aligned data is somewhat tricky.
503c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#
504c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Example of data cut. Data cut represented in x's.
505c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# '|' represents byte boundary.
506c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# data = ...0XX|XXXXXX00|000... => XXXXXXXX
507c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#		or
508c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# data = ...0XX|XXXX0000|000... => XXXXXX00
509c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#   or
510c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# data = ...000|XXXXXXXX|000... => XXXXXXXX
511c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#   or
512c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# data = ...000|00XXXX00|000... => XXXX0000
513c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#
514c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList = []
515c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		bitRange = self._getBitRange(row, bitDepth, metrics)
516c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		stepRange = bitRange + (8,)
51797dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		for curBit in range(*stepRange):
518c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			endBit = min(curBit+8, bitRange[1])
519c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			numBits = endBit - curBit
520c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			cutPoint = curBit % 8
52132c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod			firstByteLoc = curBit // 8
52232c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod			secondByteLoc = endBit // 8
523c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if firstByteLoc < secondByteLoc:
524c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				numBitsCut = 8 - cutPoint
525c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			else:
526c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				numBitsCut = endBit - curBit
527c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curByte = _reverseBytes(self.imageData[firstByteLoc])
528319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod			firstHalf = byteord(curByte) >> cutPoint
529c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			firstHalf = ((1<<numBitsCut)-1) & firstHalf
530c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			newByte = firstHalf
531c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if firstByteLoc < secondByteLoc and secondByteLoc < len(self.imageData):
532c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				curByte = _reverseBytes(self.imageData[secondByteLoc])
533319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod				secondHalf = byteord(curByte) << numBitsCut
534c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				newByte = (firstHalf | secondHalf) & ((1<<numBits)-1)
535b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod			dataList.append(bytechr(newByte))
536c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
537c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# The way the data is kept is opposite the algorithm used.
53818316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		data = bytesjoin(dataList)
539c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if not reverseBytes:
540c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			data = _reverseBytes(data)
541c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return data
542c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
543c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
5449e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod		if metrics is None:
545c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			metrics = self.metrics
546c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if not reverseBytes:
547e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			dataRows = list(map(_reverseBytes, dataRows))
548c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
549c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Keep track of a list of ordinal values as they are easier to modify
550c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# than a list of strings. Map to actual strings later.
55132c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod		numBytes = (self._getBitRange(len(dataRows), bitDepth, metrics)[0] + 7) // 8
552c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		ordDataList = [0] * numBytes
553c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for row, data in enumerate(dataRows):
554c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			bitRange = self._getBitRange(row, bitDepth, metrics)
555c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			stepRange = bitRange + (8,)
5564b775ee5f16b7feef22dd796f2faefb366f535f3Behdad Esfahbod			for curBit, curByte in zip(range(*stepRange), data):
557c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				endBit = min(curBit+8, bitRange[1])
558c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				cutPoint = curBit % 8
55932c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod				firstByteLoc = curBit // 8
56032c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod				secondByteLoc = endBit // 8
561c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				if firstByteLoc < secondByteLoc:
562c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					numBitsCut = 8 - cutPoint
563c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				else:
564c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					numBitsCut = endBit - curBit
565319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod				curByte = byteord(curByte)
566c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				firstByte = curByte & ((1<<numBitsCut)-1)
567c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				ordDataList[firstByteLoc] |= (firstByte << cutPoint)
568c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				if firstByteLoc < secondByteLoc and secondByteLoc < numBytes:
569c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					secondByte = (curByte >> numBitsCut) & ((1<<8-numBitsCut)-1)
570c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					ordDataList[secondByteLoc] |= secondByte
571c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
572c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Save the image data with the bits going the correct way.
57318316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		self.imageData = _reverseBytes(bytesjoin(map(bytechr, ordDataList)))
574c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
575e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass ByteAlignedBitmapMixin(object):
576c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
577c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def _getByteRange(self, row, bitDepth, metrics):
57832c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod		rowBytes = (bitDepth * metrics.width + 7) // 8
579c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		byteOffset = row * rowBytes
580c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return (byteOffset, byteOffset+rowBytes)
581c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
582c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
5839e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod		if metrics is None:
584c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			metrics = self.metrics
585c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
586c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		byteRange = self._getByteRange(row, bitDepth, metrics)
587c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		data = self.imageData[slice(*byteRange)]
588c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if reverseBytes:
589c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			data = _reverseBytes(data)
590c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return data
591c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
592c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
5939e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod		if metrics is None:
594c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			metrics = self.metrics
595c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if reverseBytes:
596c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataRows = map(_reverseBytes, dataRows)
59718316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		self.imageData = bytesjoin(dataRows)
598c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
599c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass ebdt_bitmap_format_1(ByteAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph):
600c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
601c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self):
602c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.metrics = SmallGlyphMetrics()
603c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
604c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.imageData = data
605c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
606c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
607c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		data = sstruct.pack(smallGlyphMetricsFormat, self.metrics)
608c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return data + self.imageData
609c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
610c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
611c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass ebdt_bitmap_format_2(BitAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph):
612c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
613c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self):
614c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.metrics = SmallGlyphMetrics()
615c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
616c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.imageData = data
617c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
618c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
619c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		data = sstruct.pack(smallGlyphMetricsFormat, self.metrics)
620c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return data + self.imageData
621c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
622c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
623c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass ebdt_bitmap_format_5(BitAlignedBitmapMixin, BitmapGlyph):
624c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
625c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self):
626c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.imageData = self.data
627c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
628c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
629c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return self.imageData
630c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
631c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass ebdt_bitmap_format_6(ByteAlignedBitmapMixin, BitmapPlusBigMetricsMixin, BitmapGlyph):
632c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
633c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self):
634c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.metrics = BigGlyphMetrics()
635c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
636c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.imageData = data
637c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
638c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
639c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		data = sstruct.pack(bigGlyphMetricsFormat, self.metrics)
640c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return data + self.imageData
641c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
642c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
643c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass ebdt_bitmap_format_7(BitAlignedBitmapMixin, BitmapPlusBigMetricsMixin, BitmapGlyph):
644c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
645c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self):
646c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.metrics = BigGlyphMetrics()
647c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
648c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.imageData = data
649c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
650c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
651c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		data = sstruct.pack(bigGlyphMetricsFormat, self.metrics)
652c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return data + self.imageData
653c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
654c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
655c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass ComponentBitmapGlyph(BitmapGlyph):
656c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
657c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def toXML(self, strikeIndex, glyphName, writer, ttFont):
658c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.begintag(self.__class__.__name__, [('name', glyphName)])
659c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
660c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
661c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.writeMetrics(writer, ttFont)
662c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
663c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.begintag('components')
664c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
665c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for curComponent in self.componentArray:
666c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curComponent.toXML(writer, ttFont)
667c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.endtag('components')
668c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
669c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
670c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.endtag(self.__class__.__name__)
671c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
672c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
6733a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
6743a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod		self.readMetrics(name, attrs, content, ttFont)
675c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for element in content:
676b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
677c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				continue
678c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			name, attr, content = element
679c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if name == 'components':
680c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				self.componentArray = []
681c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				for compElement in content:
682b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod					if not isinstance(compElement, tuple):
683c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						continue
6843a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod					name, attrs, content = compElement
685c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					if name == 'ebdtComponent':
686c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						curComponent = EbdtComponent()
6873a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod						curComponent.fromXML(name, attrs, content, ttFont)
688c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine						self.componentArray.append(curComponent)
689c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					else:
6903ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod						print("Warning: '%s' being ignored in component array." % name)
691c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
692c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
693c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass ebdt_bitmap_format_8(BitmapPlusSmallMetricsMixin, ComponentBitmapGlyph):
694c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
695c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self):
696c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.metrics = SmallGlyphMetrics()
697c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
698c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		data = data[1:]
699c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
700c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		(numComponents,) = struct.unpack(">H", data[:2])
701c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		data = data[2:]
702c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.componentArray = []
70397dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		for i in range(numComponents):
704c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curComponent = EbdtComponent()
705c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
706c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
707c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			self.componentArray.append(curComponent)
708c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
709c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
710c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList = []
711c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(sstruct.pack(smallGlyphMetricsFormat, self.metrics))
71218316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		dataList.append(b'\0')
713c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(struct.pack(">H", len(self.componentArray)))
714c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for curComponent in self.componentArray:
715c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
716c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
71718316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		return bytesjoin(dataList)
718c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
719c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
720c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass ebdt_bitmap_format_9(BitmapPlusBigMetricsMixin, ComponentBitmapGlyph):
721c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
722c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self):
723c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.metrics = BigGlyphMetrics()
724c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
725c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		(numComponents,) = struct.unpack(">H", data[:2])
726c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		data = data[2:]
727c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.componentArray = []
72897dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		for i in range(numComponents):
729c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curComponent = EbdtComponent()
730c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
731c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
732c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			self.componentArray.append(curComponent)
733c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
734c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
735c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList = []
736c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
737c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(struct.pack(">H", len(self.componentArray)))
738c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for curComponent in self.componentArray:
739c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
740c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
74118316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		return bytesjoin(dataList)
742c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
743c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
744c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# Dictionary of bitmap formats to the class representing that format
745c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# currently only the ones listed in this map are the ones supported.
746c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineebdt_bitmap_classes = {
747c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		1: ebdt_bitmap_format_1,
748c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		2: ebdt_bitmap_format_2,
749c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		5: ebdt_bitmap_format_5,
750c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		6: ebdt_bitmap_format_6,
751c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		7: ebdt_bitmap_format_7,
752c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		8: ebdt_bitmap_format_8,
753c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		9: ebdt_bitmap_format_9,
754c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	}
755