11ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import
230e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import *
330e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc import sstruct
42b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom . import DefaultTable
530e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.textTools import safeEval
630e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
7c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineimport struct
8c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineimport itertools
9c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainefrom collections import deque
10c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
11c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontaineeblcHeaderFormat = """
12c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	> # big endian
13c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	version:  16.16F
14c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	numSizes: I
15c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine"""
16c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# The table format string is split to handle sbitLineMetrics simply.
17c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontainebitmapSizeTableFormatPart1 = """
18c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	> # big endian
19c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	indexSubTableArrayOffset: I
20c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	indexTablesSize:          I
21c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	numberOfIndexSubTables:   I
22c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	colorRef:                 I
23c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine"""
24c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# The compound type for hori and vert.
25c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontainesbitLineMetricsFormat = """
26c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	> # big endian
27c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	ascender:              b
28c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	descender:             b
29c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	widthMax:              B
30c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	caretSlopeNumerator:   b
31c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	caretSlopeDenominator: b
32c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	caretOffset:           b
33c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	minOriginSB:           b
34c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	minAdvanceSB:          b
35c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	maxBeforeBL:           b
36c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	minAfterBL:            b
37c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	pad1:                  b
38c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	pad2:                  b
39c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine"""
40c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# hori and vert go between the two parts.
41c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontainebitmapSizeTableFormatPart2 = """
42c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	> # big endian
43c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	startGlyphIndex: H
44c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	endGlyphIndex:   H
45c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	ppemX:           B
46c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	ppemY:           B
47c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	bitDepth:        B
48c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	flags:           b
49c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine"""
50c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
51c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontaineindexSubTableArrayFormat = ">HHL"
52c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontaineindexSubTableArraySize = struct.calcsize(indexSubTableArrayFormat)
53c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
54c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontaineindexSubHeaderFormat = ">HHL"
55c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontaineindexSubHeaderSize = struct.calcsize(indexSubHeaderFormat)
56c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
57c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontainecodeOffsetPairFormat = ">HH"
58c33b0a22ef0046c392275e3dba974dfbadee24faMatt FontainecodeOffsetPairSize = struct.calcsize(codeOffsetPairFormat)
59c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
60c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass table_E_B_L_C_(DefaultTable.DefaultTable):
61c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
62c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	dependencies = ['EBDT']
63c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
64c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# This method can be overridden in subclasses to support new formats
65c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# without changing the other implementation. Also can be used as a
66c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# convenience method for coverting a font file to an alternative format.
67c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def getIndexFormatClass(self, indexFormat):
68c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return eblc_sub_table_classes[indexFormat]
69c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
70c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self, data, ttFont):
71c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
72c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Save the original data because offsets are from the start of the table.
73c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		origData = data
74c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
75c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dummy, data = sstruct.unpack2(eblcHeaderFormat, data, self)
76c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
77c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.strikes = []
7897dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		for curStrikeIndex in range(self.numSizes):
79c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curStrike = Strike()
80c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			self.strikes.append(curStrike)
81c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curTable = curStrike.bitmapSizeTable
82c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dummy, data = sstruct.unpack2(bitmapSizeTableFormatPart1, data, curTable)
83c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			for metric in ('hori', 'vert'):
84c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				metricObj = SbitLineMetrics()
85c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				vars(curTable)[metric] = metricObj
86c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				dummy, data = sstruct.unpack2(sbitLineMetricsFormat, data, metricObj)
87c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dummy, data = sstruct.unpack2(bitmapSizeTableFormatPart2, data, curTable)
88c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
89c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for curStrike in self.strikes:
90c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curTable = curStrike.bitmapSizeTable
9197dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod			for subtableIndex in range(curTable.numberOfIndexSubTables):
92c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				lowerBound = curTable.indexSubTableArrayOffset + subtableIndex * indexSubTableArraySize
93c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				upperBound = lowerBound + indexSubTableArraySize
94c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				data = origData[lowerBound:upperBound]
95c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
96c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				tup = struct.unpack(indexSubTableArrayFormat, data)
97c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				(firstGlyphIndex, lastGlyphIndex, additionalOffsetToIndexSubtable) = tup
98c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				offsetToIndexSubTable = curTable.indexSubTableArrayOffset + additionalOffsetToIndexSubtable
99c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				data = origData[offsetToIndexSubTable:]
100c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
101c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				tup = struct.unpack(indexSubHeaderFormat, data[:indexSubHeaderSize])
102c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				(indexFormat, imageFormat, imageDataOffset) = tup
103c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
104c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexFormatClass = self.getIndexFormatClass(indexFormat)
105c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable = indexFormatClass(data[indexSubHeaderSize:], ttFont)
106c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable.firstGlyphIndex = firstGlyphIndex
107c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable.lastGlyphIndex = lastGlyphIndex
108c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable.additionalOffsetToIndexSubtable = additionalOffsetToIndexSubtable
109c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable.indexFormat = indexFormat
110c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable.imageFormat = imageFormat
111c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable.imageDataOffset = imageDataOffset
112c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				curStrike.indexSubTables.append(indexSubTable)
113c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
114c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
115c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
116c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList = []
117c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.numSizes = len(self.strikes)
118c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(sstruct.pack(eblcHeaderFormat, self))
119c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
120c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Data size of the header + bitmapSizeTable needs to be calculated
121c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# in order to form offsets. This value will hold the size of the data
122c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# in dataList after all the data is consolidated in dataList.
123c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataSize = len(dataList[0])
124c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
125c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# The table will be structured in the following order:
126c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# (0) header
127c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# (1) Each bitmapSizeTable [1 ... self.numSizes]
128c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# (2) Alternate between indexSubTableArray and indexSubTable
129c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#     for each bitmapSizeTable present.
130c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#
131c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# The issue is maintaining the proper offsets when table information
132c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# gets moved around. All offsets and size information must be recalculated
133c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# when building the table to allow editing within ttLib and also allow easy
134c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# import/export to and from XML. All of this offset information is lost
135c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# when exporting to XML so everything must be calculated fresh so importing
136c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# from XML will work cleanly. Only byte offset and size information is
137c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# calculated fresh. Count information like numberOfIndexSubTables is
138c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# checked through assertions. If the information in this table was not
139c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# touched or was changed properly then these types of values should match.
140c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#
141c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# The table will be rebuilt the following way:
142c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# (0) Precompute the size of all the bitmapSizeTables. This is needed to
143c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#     compute the offsets properly.
144c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# (1) For each bitmapSizeTable compute the indexSubTable and
145c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#    	indexSubTableArray pair. The indexSubTable must be computed first
146c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#     so that the offset information in indexSubTableArray can be
147c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		#     calculated. Update the data size after each pairing.
148c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# (2) Build each bitmapSizeTable.
149c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# (3) Consolidate all the data into the main dataList in the correct order.
150c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
151c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for curStrike in self.strikes:
152c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataSize += sstruct.calcsize(bitmapSizeTableFormatPart1)
153c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataSize += len(('hori', 'vert')) * sstruct.calcsize(sbitLineMetricsFormat)
154c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataSize += sstruct.calcsize(bitmapSizeTableFormatPart2)
155c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
156c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		indexSubTablePairDataList = []
157c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for curStrike in self.strikes:
158c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curTable = curStrike.bitmapSizeTable
159c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curTable.numberOfIndexSubTables = len(curStrike.indexSubTables)
160c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curTable.indexSubTableArrayOffset = dataSize
161c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
162c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# Precompute the size of the indexSubTableArray. This information
163c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# is important for correctly calculating the new value for
164c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# additionalOffsetToIndexSubtable.
165c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			sizeOfSubTableArray = curTable.numberOfIndexSubTables * indexSubTableArraySize
166c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			lowerBound = dataSize
167c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataSize += sizeOfSubTableArray
168c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			upperBound = dataSize
169c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
170c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			indexSubTableDataList = []
171c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			for indexSubTable in curStrike.indexSubTables:
172c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable.additionalOffsetToIndexSubtable = dataSize - curTable.indexSubTableArrayOffset
173e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod				glyphIds = list(map(ttFont.getGlyphID, indexSubTable.names))
174c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable.firstGlyphIndex = min(glyphIds)
175c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable.lastGlyphIndex = max(glyphIds)
176c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				data = indexSubTable.compile(ttFont)
177c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTableDataList.append(data)
178c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				dataSize += len(data)
179c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curTable.startGlyphIndex = min(ist.firstGlyphIndex for ist in curStrike.indexSubTables)
180c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curTable.endGlyphIndex = max(ist.lastGlyphIndex for ist in curStrike.indexSubTables)
181c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
182c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			for i in curStrike.indexSubTables:
183c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				data = struct.pack(indexSubHeaderFormat, i.firstGlyphIndex, i.lastGlyphIndex, i.additionalOffsetToIndexSubtable)
184c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTablePairDataList.append(data)
185c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			indexSubTablePairDataList.extend(indexSubTableDataList)
186c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curTable.indexTablesSize = dataSize - curTable.indexSubTableArrayOffset
187c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
188c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for curStrike in self.strikes:
189c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curTable = curStrike.bitmapSizeTable
190c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			data = sstruct.pack(bitmapSizeTableFormatPart1, curTable)
191c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataList.append(data)
192c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			for metric in ('hori', 'vert'):
193c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				metricObj = vars(curTable)[metric]
194c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				data = sstruct.pack(sbitLineMetricsFormat, metricObj)
195c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				dataList.append(data)
196c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			data = sstruct.pack(bitmapSizeTableFormatPart2, curTable)
197c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataList.append(data)
198c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.extend(indexSubTablePairDataList)
199c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
20018316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		return bytesjoin(dataList)
201c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
202c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def toXML(self, writer, ttFont):
203c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.simpletag('header', [('version', self.version)])
204c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
205c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for curIndex, curStrike in enumerate(self.strikes):
206c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curStrike.toXML(curIndex, writer, ttFont)
207c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
2083a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
209c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if name == 'header':
210c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			self.version = safeEval(attrs['version'])
211c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		elif name == 'strike':
212c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if not hasattr(self, 'strikes'):
213c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				self.strikes = []
214c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			strikeIndex = safeEval(attrs['index'])
215c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			curStrike = Strike()
2163a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod			curStrike.fromXML(name, attrs, content, ttFont, self)
217c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
218c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# Grow the strike array to the appropriate size. The XML format
219c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# allows for the strike index value to be out of order.
220c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if strikeIndex >= len(self.strikes):
221c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				self.strikes += [None] * (strikeIndex + 1 - len(self.strikes))
2229e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod			assert self.strikes[strikeIndex] is None, "Duplicate strike EBLC indices."
223c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			self.strikes[strikeIndex] = curStrike
224c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
225e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass Strike(object):
226c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
227c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def __init__(self):
228c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.bitmapSizeTable = BitmapSizeTable()
229c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.indexSubTables = []
230c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
231c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def toXML(self, strikeIndex, writer, ttFont):
232c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.begintag('strike', [('index', strikeIndex)])
233c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
234c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.bitmapSizeTable.toXML(writer, ttFont)
235c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.comment('GlyphIds are written but not read. The firstGlyphIndex and\nlastGlyphIndex values will be recalculated by the compiler.')
236c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
237c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for indexSubTable in self.indexSubTables:
238c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			indexSubTable.toXML(writer, ttFont)
239c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.endtag('strike')
240c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
241c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
2423a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont, locator):
243c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for element in content:
244b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
245c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				continue
246c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			name, attrs, content = element
247c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if name == 'bitmapSizeTable':
2483a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				self.bitmapSizeTable.fromXML(name, attrs, content, ttFont)
249c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			elif name.startswith(_indexSubTableSubclassPrefix):
250c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexFormat = safeEval(name[len(_indexSubTableSubclassPrefix):])
251c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexFormatClass = locator.getIndexFormatClass(indexFormat)
252c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable = indexFormatClass(None, None)
253c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				indexSubTable.indexFormat = indexFormat
2543a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				indexSubTable.fromXML(name, attrs, content, ttFont)
255c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				self.indexSubTables.append(indexSubTable)
256c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
257c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
258e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass BitmapSizeTable(object):
259c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
260c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# Returns all the simple metric names that bitmap size table
261c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# cares about in terms of XML creation.
262c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def _getXMLMetricNames(self):
263c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataNames = sstruct.getformat(bitmapSizeTableFormatPart1)[1]
264c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataNames = dataNames + sstruct.getformat(bitmapSizeTableFormatPart2)[1]
265c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Skip the first 3 data names because they are byte offsets and counts.
266c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return dataNames[3:]
267c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
268c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def toXML(self, writer, ttFont):
269c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.begintag('bitmapSizeTable')
270c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
271c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for metric in ('hori', 'vert'):
272c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			getattr(self, metric).toXML(metric, writer, ttFont)
273c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for metricName in self._getXMLMetricNames():
274c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.simpletag(metricName, value=getattr(self, metricName))
275c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.newline()
276c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.endtag('bitmapSizeTable')
277c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
278c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
2793a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
280c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Create a lookup for all the simple names that make sense to
281c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# bitmap size table. Only read the information from these names.
282c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataNames = set(self._getXMLMetricNames())
283c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for element in content:
284b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
285c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				continue
286c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			name, attrs, content = element
287c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if name == 'sbitLineMetrics':
288c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				direction = attrs['direction']
289c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				assert direction in ('hori', 'vert'), "SbitLineMetrics direction specified invalid."
290c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				metricObj = SbitLineMetrics()
2913a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				metricObj.fromXML(name, attrs, content, ttFont)
292c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				vars(self)[direction] = metricObj
293c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			elif name in dataNames:
294c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				vars(self)[name] = safeEval(attrs['value'])
295c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			else:
2963ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod				print("Warning: unknown name '%s' being ignored in BitmapSizeTable." % name)
297c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
298c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
299e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass SbitLineMetrics(object):
300c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
301c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def toXML(self, name, writer, ttFont):
302c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.begintag('sbitLineMetrics', [('direction', name)])
303c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
304c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for metricName in sstruct.getformat(sbitLineMetricsFormat)[1]:
305c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.simpletag(metricName, value=getattr(self, metricName))
306c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.newline()
307c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.endtag('sbitLineMetrics')
308c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
309c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
3103a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
311c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		metricNames = set(sstruct.getformat(sbitLineMetricsFormat)[1])
312c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for element in content:
313b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
314c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				continue
315c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			name, attrs, content = element
316c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if name in metricNames:
317c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				vars(self)[name] = safeEval(attrs['value'])
318c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
319c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# Important information about the naming scheme. Used for identifying subtables.
320c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine_indexSubTableSubclassPrefix = 'eblc_index_sub_table_'
321c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
322e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass EblcIndexSubTable(object):
323c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
324c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def __init__(self, data, ttFont):
325c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.data = data
326c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.ttFont = ttFont
3274be8db031d5df7a3bdd1e4e0fa02de0246cb20aeBehdad Esfahbod		# TODO Currently non-lazy decompiling doesn't work for this class...
3284be8db031d5df7a3bdd1e4e0fa02de0246cb20aeBehdad Esfahbod		#if not ttFont.lazy:
3294be8db031d5df7a3bdd1e4e0fa02de0246cb20aeBehdad Esfahbod		#	self.decompile()
3304be8db031d5df7a3bdd1e4e0fa02de0246cb20aeBehdad Esfahbod		#	del self.data, self.ttFont
331c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
332c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def __getattr__(self, attr):
333c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Allow lazy decompile.
334c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if attr[:2] == '__':
335cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise AttributeError(attr)
3363d8d5cd10d2d30ba9997b7cc43336e504111be9aBehdad Esfahbod		if not hasattr(self, "data"):
337cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise AttributeError(attr)
338c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.decompile()
3393d8d5cd10d2d30ba9997b7cc43336e504111be9aBehdad Esfahbod		del self.data, self.ttFont
340c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return getattr(self, attr)
341c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
342c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# This method just takes care of the indexSubHeader. Implementing subclasses
343c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# should call it to compile the indexSubHeader and then continue compiling
344c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# the remainder of their unique format.
345c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
346c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return struct.pack(indexSubHeaderFormat, self.indexFormat, self.imageFormat, self.imageDataOffset)
347c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
348c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# Creates the XML for bitmap glyphs. Each index sub table basically makes
349c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# the same XML except for specific metric information that is written
350c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# out via a method call that a subclass implements optionally.
351c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def toXML(self, writer, ttFont):
352c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.begintag(self.__class__.__name__, [
353c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				('imageFormat', self.imageFormat),
354c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				('firstGlyphIndex', self.firstGlyphIndex),
355c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				('lastGlyphIndex', self.lastGlyphIndex),
356c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				])
357c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
358c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.writeMetrics(writer, ttFont)
359c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Write out the names as thats all thats needed to rebuild etc.
360c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# For font debugging of consecutive formats the ids are also written.
361c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# The ids are not read when moving from the XML format.
362c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		glyphIds = map(ttFont.getGlyphID, self.names)
3634b775ee5f16b7feef22dd796f2faefb366f535f3Behdad Esfahbod		for glyphName, glyphId in zip(self.names, glyphIds):
364c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.simpletag('glyphLoc', name=glyphName, id=glyphId)
365c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			writer.newline()
366c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.endtag(self.__class__.__name__)
367c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
368c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
3693a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
370c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Read all the attributes. Even though the glyph indices are
371c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# recalculated, they are still read in case there needs to
372c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# be an immediate export of the data.
373c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.imageFormat = safeEval(attrs['imageFormat'])
374c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.firstGlyphIndex = safeEval(attrs['firstGlyphIndex'])
375c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.lastGlyphIndex = safeEval(attrs['lastGlyphIndex'])
376c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
3773a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod		self.readMetrics(name, attrs, content, ttFont)
378c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
379c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.names = []
380c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for element in content:
381b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
382c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				continue
383c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			name, attrs, content = element
384c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if name == 'glyphLoc':
385c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				self.names.append(attrs['name'])
386c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
387c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# A helper method that writes the metrics for the index sub table. It also
388c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# is responsible for writing the image size for fixed size data since fixed
389c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# size is not recalculated on compile. Default behavior is to do nothing.
390c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def writeMetrics(self, writer, ttFont):
391c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		pass
392c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
393c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# A helper method that is the inverse of writeMetrics.
3943a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def readMetrics(self, name, attrs, content, ttFont):
395c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		pass
396c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
397c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# This method is for fixed glyph data sizes. There are formats where
398c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# the glyph data is fixed but are actually composite glyphs. To handle
399c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# this the font spec in indexSubTable makes the data the size of the
400c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# fixed size by padding the component arrays. This function abstracts
401c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# out this padding process. Input is data unpadded. Output is data
402c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# padded only in fixed formats. Default behavior is to return the data.
403c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def padBitmapData(self, data):
404c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return data
405c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
406c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# Remove any of the glyph locations and names that are flagged as skipped.
407c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# This only occurs in formats {1,3}.
408c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def removeSkipGlyphs(self):
409c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Determines if a name, location pair is a valid data location.
410c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Skip glyphs are marked when the size is equal to zero.
4113a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod		def isValidLocation(args):
4123a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod			(name, (startByte, endByte)) = args
413c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			return startByte < endByte
414c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Remove all skip glyphs.
41528aeabb08b2656cb240063865c37f192532badf5Behdad Esfahbod		dataPairs = list(filter(isValidLocation, zip(self.names, self.locations)))
416e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		self.names, self.locations = list(map(list, zip(*dataPairs)))
417c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
418c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# A closure for creating a custom mixin. This is done because formats 1 and 3
419c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# are very similar. The only difference between them is the size per offset
420c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# value. Code put in here should handle both cases generally.
421c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontainedef _createOffsetArrayIndexSubTableMixin(formatStringForDataType):
422c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
423c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	# Prep the data size for the offset array data format.
424c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	dataFormat = '>'+formatStringForDataType
425c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	offsetDataSize = struct.calcsize(dataFormat)
426c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
427e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbod	class OffsetArrayIndexSubTableMixin(object):
428c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
429c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		def decompile(self):
430c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
431c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			numGlyphs = self.lastGlyphIndex - self.firstGlyphIndex + 1
43297dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod			indexingOffsets = [glyphIndex * offsetDataSize for glyphIndex in range(numGlyphs+2)]
433c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
434c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			offsetArray = [struct.unpack(dataFormat, self.data[slice(*loc)])[0] for loc in indexingLocations]
435c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
43697dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod			glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
437c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			modifiedOffsets = [offset + self.imageDataOffset for offset in offsetArray]
438fa5f2e85ab49c349468f5ae08f15163daa256a04Behdad Esfahbod			self.locations = list(zip(modifiedOffsets, modifiedOffsets[1:]))
439c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
440e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			self.names = list(map(self.ttFont.getGlyphName, glyphIds))
441c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			self.removeSkipGlyphs()
442c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
443c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		def compile(self, ttFont):
444c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# First make sure that all the data lines up properly. Formats 1 and 3
445c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# must have all its data lined up consecutively. If not this will fail.
4464b775ee5f16b7feef22dd796f2faefb366f535f3Behdad Esfahbod			for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
447c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				assert curLoc[1] == nxtLoc[0], "Data must be consecutive in indexSubTable offset formats"
448c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
449e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			glyphIds = list(map(ttFont.getGlyphID, self.names))
450c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# Make sure that all ids are sorted strictly increasing.
45197dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod			assert all(glyphIds[i] < glyphIds[i+1] for i in range(len(glyphIds)-1))
452c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
453c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# Run a simple algorithm to add skip glyphs to the data locations at
454c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# the places where an id is not present.
455c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			idQueue = deque(glyphIds)
456c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			locQueue = deque(self.locations)
45797dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod			allGlyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
458c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			allLocations = []
459c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			for curId in allGlyphIds:
460c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				if curId != idQueue[0]:
461c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					allLocations.append((locQueue[0][0], locQueue[0][0]))
462c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				else:
463c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					idQueue.popleft()
464c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine					allLocations.append(locQueue.popleft())
465c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
466c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# Now that all the locations are collected, pack them appropriately into
467c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# offsets. This is the form where offset[i] is the location and
468c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# offset[i+1]-offset[i] is the size of the data location.
469c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			offsets = list(allLocations[0]) + [loc[1] for loc in allLocations[1:]]
470c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# Image data offset must be less than or equal to the minimum of locations.
471c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# This offset may change the value for round tripping but is safer and
472c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# allows imageDataOffset to not be required to be in the XML version.
473c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			self.imageDataOffset = min(offsets)
474c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			offsetArray = [offset - self.imageDataOffset for offset in offsets]
475c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
476c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataList = [EblcIndexSubTable.compile(self, ttFont)]
477c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataList += [struct.pack(dataFormat, offsetValue) for offsetValue in offsetArray]
478c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			# Take care of any padding issues. Only occurs in format 3.
479c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if offsetDataSize * len(dataList) % 4 != 0:
480c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				dataList.append(struct.pack(dataFormat, 0))
48118316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod			return bytesjoin(dataList)
482c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
483c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	return OffsetArrayIndexSubTableMixin
484c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
485c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# A Mixin for functionality shared between the different kinds
486c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# of fixed sized data handling. Both kinds have big metrics so
487c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# that kind of special processing is also handled in this mixin.
488e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass FixedSizeIndexSubTableMixin(object):
489c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
490c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def writeMetrics(self, writer, ttFont):
491c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.simpletag('imageSize', value=self.imageSize)
492c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		writer.newline()
493c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.metrics.toXML(writer, ttFont)
494c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
4953a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def readMetrics(self, name, attrs, content, ttFont):
496c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		for element in content:
497b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
498c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				continue
499c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			name, attrs, content = element
500c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			if name == 'imageSize':
501c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				self.imageSize = safeEval(attrs['value'])
502c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			elif name == BigGlyphMetrics.__name__:
503c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine				self.metrics = BigGlyphMetrics()
5043a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod				self.metrics.fromXML(name, attrs, content, ttFont)
505c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			elif name == SmallGlyphMetrics.__name__:
5063ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod				print("Warning: SmallGlyphMetrics being ignored in format %d." % self.indexFormat)
507c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
508c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def padBitmapData(self, data):
509c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Make sure that the data isn't bigger than the fixed size.
510c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		assert len(data) <= self.imageSize, "Data in indexSubTable format %d must be less than the fixed size." % self.indexFormat
511c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Pad the data so that it matches the fixed size.
51218316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		pad = (self.imageSize - len(data)) * b'\0'
513c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return data + pad
514c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
515c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass eblc_index_sub_table_1(_createOffsetArrayIndexSubTableMixin('L'), EblcIndexSubTable):
516c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	pass
517c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
518c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass eblc_index_sub_table_2(FixedSizeIndexSubTableMixin, EblcIndexSubTable):
519c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
520c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self):
521c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		(self.imageSize,) = struct.unpack(">L", self.data[:4])
522c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.metrics = BigGlyphMetrics()
523c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		sstruct.unpack2(bigGlyphMetricsFormat, self.data[4:], self.metrics)
52497dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
52597dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		offsets = [self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds)+1)]
526fa5f2e85ab49c349468f5ae08f15163daa256a04Behdad Esfahbod		self.locations = list(zip(offsets, offsets[1:]))
527e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		self.names = list(map(self.ttFont.getGlyphName, glyphIds))
528c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
529c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
530e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		glyphIds = list(map(ttFont.getGlyphID, self.names))
531c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Make sure all the ids are consecutive. This is required by Format 2.
53297dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		assert glyphIds == list(range(self.firstGlyphIndex, self.lastGlyphIndex+1)), "Format 2 ids must be consecutive."
533c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.imageDataOffset = min(zip(*self.locations)[0])
534c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
535c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList = [EblcIndexSubTable.compile(self, ttFont)]
536c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(struct.pack(">L", self.imageSize))
537c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
53818316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		return bytesjoin(dataList)
539c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
540c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass eblc_index_sub_table_3(_createOffsetArrayIndexSubTableMixin('H'), EblcIndexSubTable):
541c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	pass
542c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
543c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass eblc_index_sub_table_4(EblcIndexSubTable):
544c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
545c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self):
546c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
547c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		(numGlyphs,) = struct.unpack(">L", self.data[:4])
548c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		data = self.data[4:]
54997dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		indexingOffsets = [glyphIndex * codeOffsetPairSize for glyphIndex in range(numGlyphs+2)]
550c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
551c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		glyphArray = [struct.unpack(codeOffsetPairFormat, data[slice(*loc)]) for loc in indexingLocations]
552e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		glyphIds, offsets = list(map(list, zip(*glyphArray)))
553c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# There are one too many glyph ids. Get rid of the last one.
554c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		glyphIds.pop()
555c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
556c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		offsets = [offset + self.imageDataOffset for offset in offsets]
557fa5f2e85ab49c349468f5ae08f15163daa256a04Behdad Esfahbod		self.locations = list(zip(offsets, offsets[1:]))
558e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		self.names = list(map(self.ttFont.getGlyphName, glyphIds))
559c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
560c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
561c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# First make sure that all the data lines up properly. Format 4
562c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# must have all its data lined up consecutively. If not this will fail.
5634b775ee5f16b7feef22dd796f2faefb366f535f3Behdad Esfahbod		for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
564c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			assert curLoc[1] == nxtLoc[0], "Data must be consecutive in indexSubTable format 4"
565c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
566c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		offsets = list(self.locations[0]) + [loc[1] for loc in self.locations[1:]]
567c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Image data offset must be less than or equal to the minimum of locations.
568c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Resetting this offset may change the value for round tripping but is safer
569c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# and allows imageDataOffset to not be required to be in the XML version.
570c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.imageDataOffset = min(offsets)
571c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		offsets = [offset - self.imageDataOffset for offset in offsets]
572e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		glyphIds = list(map(ttFont.getGlyphID, self.names))
573c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		# Create an iterator over the ids plus a padding value.
574c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		idsPlusPad = list(itertools.chain(glyphIds, [0]))
575c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
576c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList = [EblcIndexSubTable.compile(self, ttFont)]
577c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(struct.pack(">L", len(glyphIds)))
5784b775ee5f16b7feef22dd796f2faefb366f535f3Behdad Esfahbod		tmp = [struct.pack(codeOffsetPairFormat, *cop) for cop in zip(idsPlusPad, offsets)]
579c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList += tmp
58018316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		data = bytesjoin(dataList)
581c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		return data
582c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
583c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineclass eblc_index_sub_table_5(FixedSizeIndexSubTableMixin, EblcIndexSubTable):
584c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
585c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def decompile(self):
586c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.origDataLen = 0
587c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		(self.imageSize,) = struct.unpack(">L", self.data[:4])
588c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		data = self.data[4:]
589c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.metrics, data = sstruct.unpack2(bigGlyphMetricsFormat, data, BigGlyphMetrics())
590c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		(numGlyphs,) = struct.unpack(">L", data[:4])
591c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		data = data[4:]
59297dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		glyphIds = [struct.unpack(">H", data[2*i:2*(i+1)])[0] for i in range(numGlyphs)]
593c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
59497dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod		offsets = [self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds)+1)]
595fa5f2e85ab49c349468f5ae08f15163daa256a04Behdad Esfahbod		self.locations = list(zip(offsets, offsets[1:]))
596e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		self.names = list(map(self.ttFont.getGlyphName, glyphIds))
597c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
598c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	def compile(self, ttFont):
599c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.imageDataOffset = min(zip(*self.locations)[0])
600c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList = [EblcIndexSubTable.compile(self, ttFont)]
601c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(struct.pack(">L", self.imageSize))
602c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
603e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod		glyphIds = list(map(ttFont.getGlyphID, self.names))
604c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList.append(struct.pack(">L", len(glyphIds)))
605c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		dataList += [struct.pack(">H", curId) for curId in glyphIds]
606c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		if len(glyphIds) % 2 == 1:
607c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			dataList.append(struct.pack(">H", 0))
60818316aa769566eeb6f3f4a6ed2685fa8f8e861c2Behdad Esfahbod		return bytesjoin(dataList)
609c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine
610c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine# Dictionary of indexFormat to the class representing that format.
611c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaineeblc_sub_table_classes = {
612c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		1: eblc_index_sub_table_1,
613c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		2: eblc_index_sub_table_2,
614c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		3: eblc_index_sub_table_3,
615c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		4: eblc_index_sub_table_4,
616c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		5: eblc_index_sub_table_5,
617c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine	}
618