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