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