2013-11-27 02:34:11 -05:00

753 lines
26 KiB
Python

from . import DefaultTable
import os
import string
import struct
from fontTools.misc import sstruct
import itertools
from types import TupleType
from fontTools.misc.textTools import safeEval, readHex, hexStr, deHexStr
from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
ebdtTableVersionFormat = """
> # big endian
version: 16.16F
"""
ebdtComponentFormat = """
> # big endian
glyphCode: H
xOffset: b
yOffset: b
"""
class table_E_B_D_T_(DefaultTable.DefaultTable):
# Keep a reference to the name of the data locator table.
locatorName = 'EBLC'
# This method can be overridden in subclasses to support new formats
# without changing the other implementation. Also can be used as a
# convenience method for coverting a font file to an alternative format.
def getImageFormatClass(self, imageFormat):
return ebdt_bitmap_classes[imageFormat]
def decompile(self, data, ttFont):
# Get the version but don't advance the slice.
# Most of the lookup for this table is done relative
# to the begining so slice by the offsets provided
# in the EBLC table.
sstruct.unpack2(ebdtTableVersionFormat, data, self)
# Keep a dict of glyphs that have been seen so they aren't remade.
# This dict maps intervals of data to the BitmapGlyph.
glyphDict = {}
# Pull out the EBLC table and loop through glyphs.
# A strike is a concept that spans both tables.
# The actual bitmap data is stored in the EBDT.
locator = ttFont[self.__class__.locatorName]
self.strikeData = []
for curStrike in locator.strikes:
bitmapGlyphDict = {}
self.strikeData.append(bitmapGlyphDict)
for indexSubTable in curStrike.indexSubTables:
dataIter = itertools.izip(indexSubTable.names, indexSubTable.locations)
for curName, curLoc in dataIter:
# Don't create duplicate data entries for the same glyphs.
# Instead just use the structures that already exist if they exist.
if curLoc in glyphDict:
curGlyph = glyphDict[curLoc]
else:
curGlyphData = data[slice(*curLoc)]
imageFormatClass = self.getImageFormatClass(indexSubTable.imageFormat)
curGlyph = imageFormatClass(curGlyphData, ttFont)
glyphDict[curLoc] = curGlyph
bitmapGlyphDict[curName] = curGlyph
def compile(self, ttFont):
dataList = []
dataList.append(sstruct.pack(ebdtTableVersionFormat, self))
dataSize = len(dataList[0])
# Keep a dict of glyphs that have been seen so they aren't remade.
# This dict maps the id of the BitmapGlyph to the interval
# in the data.
glyphDict = {}
# Go through the bitmap glyph data. Just in case the data for a glyph
# changed the size metrics should be recalculated. There are a variety
# of formats and they get stored in the EBLC table. That is why
# recalculation is defered to the EblcIndexSubTable class and just
# pass what is known about bitmap glyphs from this particular table.
locator = ttFont[self.__class__.locatorName]
for curStrike, curGlyphDict in itertools.izip(locator.strikes, self.strikeData):
for curIndexSubTable in curStrike.indexSubTables:
dataLocations = []
for curName in curIndexSubTable.names:
# Handle the data placement based on seeing the glyph or not.
# Just save a reference to the location if the glyph has already
# been saved in compile. This code assumes that glyphs will only
# be referenced multiple times from indexFormat5. By luck the
# code may still work when referencing poorly ordered fonts with
# duplicate references. If there is a font that is unlucky the
# respective compile methods for the indexSubTables will fail
# their assertions. All fonts seem to follow this assumption.
# More complicated packing may be needed if a counter-font exists.
glyph = curGlyphDict[curName]
objectId = id(glyph)
if objectId not in glyphDict:
data = glyph.compile(ttFont)
data = curIndexSubTable.padBitmapData(data)
startByte = dataSize
dataSize += len(data)
endByte = dataSize
dataList.append(data)
dataLoc = (startByte, endByte)
glyphDict[objectId] = dataLoc
else:
dataLoc = glyphDict[objectId]
dataLocations.append(dataLoc)
# Just use the new data locations in the indexSubTable.
# The respective compile implementations will take care
# of any of the problems in the convertion that may arise.
curIndexSubTable.locations = dataLocations
return string.join(dataList, "")
def toXML(self, writer, ttFont):
# When exporting to XML if one of the data export formats
# requires metrics then those metrics may be in the locator.
# In this case populate the bitmaps with "export metrics".
if ttFont.bitmapGlyphDataFormat in ('row', 'bitwise'):
locator = ttFont[self.__class__.locatorName]
for curStrike, curGlyphDict in itertools.izip(locator.strikes, self.strikeData):
for curIndexSubTable in curStrike.indexSubTables:
for curName in curIndexSubTable.names:
glyph = curGlyphDict[curName]
# I'm not sure which metrics have priority here.
# For now if both metrics exist go with glyph metrics.
if hasattr(glyph, 'metrics'):
glyph.exportMetrics = glyph.metrics
else:
glyph.exportMetrics = curIndexSubTable.metrics
glyph.exportBitDepth = curStrike.bitmapSizeTable.bitDepth
writer.simpletag("header", [('version', self.version)])
writer.newline()
locator = ttFont[self.__class__.locatorName]
for strikeIndex, bitmapGlyphDict in enumerate(self.strikeData):
writer.begintag('strikedata', [('index', strikeIndex)])
writer.newline()
for curName, curBitmap in bitmapGlyphDict.items():
curBitmap.toXML(strikeIndex, curName, writer, ttFont)
writer.endtag('strikedata')
writer.newline()
def fromXML(self, (name, attrs, content), ttFont):
if name == 'header':
self.version = safeEval(attrs['version'])
elif name == 'strikedata':
if not hasattr(self, 'strikeData'):
self.strikeData = []
strikeIndex = safeEval(attrs['index'])
bitmapGlyphDict = {}
for element in content:
if type(element) != TupleType:
continue
name, attrs, content = element
if name[4:].startswith(_bitmapGlyphSubclassPrefix[4:]):
imageFormat = safeEval(name[len(_bitmapGlyphSubclassPrefix):])
glyphName = attrs['name']
imageFormatClass = self.getImageFormatClass(imageFormat)
curGlyph = imageFormatClass(None, None)
curGlyph.fromXML(element, ttFont)
assert glyphName not in bitmapGlyphDict, "Duplicate glyphs with the same name '%s' in the same strike." % glyphName
bitmapGlyphDict[glyphName] = curGlyph
else:
print "Warning: %s being ignored by %s", name, self.__class__.__name__
# Grow the strike data array to the appropriate size. The XML
# format allows the strike index value to be out of order.
if strikeIndex >= len(self.strikeData):
self.strikeData += [None] * (strikeIndex + 1 - len(self.strikeData))
assert self.strikeData[strikeIndex] == None, "Duplicate strike EBDT indices."
self.strikeData[strikeIndex] = bitmapGlyphDict
class EbdtComponent:
def toXML(self, writer, ttFont):
writer.begintag('ebdtComponent', [('name', self.name)])
writer.newline()
for componentName in sstruct.getformat(ebdtComponentFormat)[1][1:]:
writer.simpletag(componentName, value=getattr(self, componentName))
writer.newline()
writer.endtag('ebdtComponent')
writer.newline()
def fromXML(self, (name, attrs, content), ttFont):
self.name = attrs['name']
componentNames = set(sstruct.getformat(ebdtComponentFormat)[1][1:])
for element in content:
if type(element) != TupleType:
continue
name, attrs, content = element
if name in componentNames:
vars(self)[name] = safeEval(attrs['value'])
else:
print "Warning: unknown name '%s' being ignored by EbdtComponent." % name
# Helper functions for dealing with binary.
def _data2binary(data, numBits):
binaryList = []
for curByte in data:
value = ord(curByte)
numBitsCut = min(8, numBits)
for i in xrange(numBitsCut):
if value & 0x1:
binaryList.append('1')
else:
binaryList.append('0')
value = value >> 1
numBits -= numBitsCut
return string.join(binaryList, "")
def _binary2data(binary):
byteList = []
for bitLoc in xrange(0, len(binary), 8):
byteString = binary[bitLoc:bitLoc+8]
curByte = 0
for curBit in reversed(byteString):
curByte = curByte << 1
if curBit == '1':
curByte |= 1
byteList.append(chr(curByte))
return string.join(byteList, "")
def _memoize(f):
class memodict(dict):
def __missing__(self, key):
ret = f(key)
if len(key) == 1:
self[key] = ret
return ret
return memodict().__getitem__
# 00100111 -> 11100100 per byte, not to be confused with little/big endian.
# Bitmap data per byte is in the order that binary is written on the page
# with the least significant bit as far right as possible. This is the
# opposite of what makes sense algorithmically and hence this function.
@_memoize
def _reverseBytes(data):
if len(data) != 1:
return string.join(map(_reverseBytes, data), "")
byte = ord(data)
result = 0
for i in xrange(8):
result = result << 1
result |= byte & 1
byte = byte >> 1
return chr(result)
# This section of code is for reading and writing image data to/from XML.
def _writeRawImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
writer.begintag('rawimagedata')
writer.newline()
writer.dumphex(bitmapObject.imageData)
writer.endtag('rawimagedata')
writer.newline()
def _readRawImageData(bitmapObject, (name, attrs, content), ttFont):
bitmapObject.imageData = readHex(content)
def _writeRowImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
metrics = bitmapObject.exportMetrics
del bitmapObject.exportMetrics
bitDepth = bitmapObject.exportBitDepth
del bitmapObject.exportBitDepth
writer.begintag('rowimagedata', bitDepth=bitDepth, width=metrics.width, height=metrics.height)
writer.newline()
for curRow in xrange(metrics.height):
rowData = bitmapObject.getRow(curRow, bitDepth=bitDepth, metrics=metrics)
writer.simpletag('row', value=hexStr(rowData))
writer.newline()
writer.endtag('rowimagedata')
writer.newline()
def _readRowImageData(bitmapObject, (name, attrs, content), ttFont):
bitDepth = safeEval(attrs['bitDepth'])
metrics = SmallGlyphMetrics()
metrics.width = safeEval(attrs['width'])
metrics.height = safeEval(attrs['height'])
dataRows = []
for element in content:
if type(element) != TupleType:
continue
name, attr, content = element
# Chop off 'imagedata' from the tag to get just the option.
if name == 'row':
dataRows.append(deHexStr(attr['value']))
bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics)
def _writeBitwiseImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
metrics = bitmapObject.exportMetrics
del bitmapObject.exportMetrics
bitDepth = bitmapObject.exportBitDepth
del bitmapObject.exportBitDepth
# A dict for mapping binary to more readable/artistic ASCII characters.
binaryConv = {'0':'.', '1':'@'}
writer.begintag('bitwiseimagedata', bitDepth=bitDepth, width=metrics.width, height=metrics.height)
writer.newline()
for curRow in xrange(metrics.height):
rowData = bitmapObject.getRow(curRow, bitDepth=1, metrics=metrics, reverseBytes=True)
rowData = _data2binary(rowData, metrics.width)
# Make the output a readable ASCII art form.
rowData = string.join(map(binaryConv.get, rowData), "")
writer.simpletag('row', value=rowData)
writer.newline()
writer.endtag('bitwiseimagedata')
writer.newline()
def _readBitwiseImageData(bitmapObject, (name, attrs, content), ttFont):
bitDepth = safeEval(attrs['bitDepth'])
metrics = SmallGlyphMetrics()
metrics.width = safeEval(attrs['width'])
metrics.height = safeEval(attrs['height'])
# A dict for mapping from ASCII to binary. All characters are considered
# a '1' except space, period and '0' which maps to '0'.
binaryConv = {' ':'0', '.':'0', '0':'0'}
dataRows = []
for element in content:
if type(element) != TupleType:
continue
name, attr, content = element
if name == 'row':
mapParams = itertools.izip(attr['value'], itertools.repeat('1'))
rowData = string.join(itertools.starmap(binaryConv.get, mapParams), "")
dataRows.append(_binary2data(rowData))
bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics, reverseBytes=True)
def _writeExtFileImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
folder = 'bitmaps/'
filename = glyphName + bitmapObject.fileExtension
if not os.path.isdir(folder):
os.makedirs(folder)
folder += 'strike%d/' % strikeIndex
if not os.path.isdir(folder):
os.makedirs(folder)
fullPath = folder + filename
writer.simpletag('extfileimagedata', value=fullPath)
writer.newline()
with open(fullPath, "wb") as file:
file.write(bitmapObject.imageData)
def _readExtFileImageData(bitmapObject, (name, attrs, content), ttFont):
fullPath = attrs['value']
with open(fullPath, "rb") as file:
bitmapObject.imageData = file.read()
# End of XML writing code.
# Important information about the naming scheme. Used for identifying formats
# in XML.
_bitmapGlyphSubclassPrefix = 'ebdt_bitmap_format_'
class BitmapGlyph:
# For the external file format. This can be changed in subclasses. This way
# when the extfile option is turned on files have the form: glyphName.ext
# The default is just a flat binary file with no meaning.
fileExtension = '.bin'
# Keep track of reading and writing of various forms.
xmlDataFunctions = {
'raw': (_writeRawImageData, _readRawImageData),
'row': (_writeRowImageData, _readRowImageData),
'bitwise': (_writeBitwiseImageData, _readBitwiseImageData),
'extfile': (_writeExtFileImageData, _readExtFileImageData),
}
def __init__(self, data, ttFont):
self.data = data
self.ttFont = ttFont
if not ttFont.lazy:
self.decompile()
del self.data
def __getattr__(self, attr):
# Allow lazy decompile.
if attr[:2] == '__':
raise AttributeError, attr
if not hasattr(self, "data"):
raise AttributeError, attr
self.decompile()
del self.data
return getattr(self, attr)
# Not a fan of this but it is needed for safer safety checking.
def getFormat(self):
return safeEval(self.__class__.__name__[len(_bitmapGlyphSubclassPrefix):])
def toXML(self, strikeIndex, glyphName, writer, ttFont):
writer.begintag(self.__class__.__name__, [('name', glyphName)])
writer.newline()
self.writeMetrics(writer, ttFont)
# Use the internal write method to write using the correct output format.
self.writeData(strikeIndex, glyphName, writer, ttFont)
writer.endtag(self.__class__.__name__)
writer.newline()
def fromXML(self, (name, attrs, content), ttFont):
self.readMetrics((name, attrs, content), ttFont)
for element in content:
if type(element) != TupleType:
continue
name, attr, content = element
# Chop off 'imagedata' from the tag to get just the option.
option = name[:-len('imagedata')]
if option in self.__class__.xmlDataFunctions:
self.readData(element, ttFont)
# Some of the glyphs have the metrics. This allows for metrics to be
# added if the glyph format has them. Default behavior is to do nothing.
def writeMetrics(self, writer, ttFont):
pass
# The opposite of write metrics.
def readMetrics(self, (name, attrs, content), ttFont):
pass
def writeData(self, strikeIndex, glyphName, writer, ttFont):
try:
writeFunc, readFunc = self.__class__.xmlDataFunctions[ttFont.bitmapGlyphDataFormat]
except KeyError:
writeFunc = _writeRawImageData
writeFunc(strikeIndex, glyphName, self, writer, ttFont)
def readData(self, (name, attrs, content), ttFont):
# Chop off 'imagedata' from the tag to get just the option.
option = name[:-len('imagedata')]
writeFunc, readFunc = self.__class__.xmlDataFunctions[option]
readFunc(self, (name, attrs, content), ttFont)
# A closure for creating a mixin for the two types of metrics handling.
# Most of the code is very similar so its easier to deal with here.
# Everything works just by passing the class that the mixin is for.
def _createBitmapPlusMetricsMixin(metricsClass):
# Both metrics names are listed here to make meaningful error messages.
metricStrings = [BigGlyphMetrics.__name__, SmallGlyphMetrics.__name__]
curMetricsName = metricsClass.__name__
# Find which metrics this is for and determine the opposite name.
metricsId = metricStrings.index(curMetricsName)
oppositeMetricsName = metricStrings[1-metricsId]
class BitmapPlusMetricsMixin:
def writeMetrics(self, writer, ttFont):
self.metrics.toXML(writer, ttFont)
def readMetrics(self, (name, attrs, content), ttFont):
for element in content:
if type(element) != TupleType:
continue
name, attrs, content = element
if name == curMetricsName:
self.metrics = metricsClass()
self.metrics.fromXML(element, ttFont)
elif name == oppositeMetricsName:
print "Warning: %s being ignored in format %d." % oppositeMetricsName, self.getFormat()
return BitmapPlusMetricsMixin
# Since there are only two types of mixin's just create them here.
BitmapPlusBigMetricsMixin = _createBitmapPlusMetricsMixin(BigGlyphMetrics)
BitmapPlusSmallMetricsMixin = _createBitmapPlusMetricsMixin(SmallGlyphMetrics)
# Data that is bit aligned can be tricky to deal with. These classes implement
# helper functionality for dealing with the data and getting a particular row
# of bitwise data. Also helps implement fancy data export/import in XML.
class BitAlignedBitmapMixin:
def _getBitRange(self, row, bitDepth, metrics):
rowBits = (bitDepth * metrics.width)
bitOffset = row * rowBits
return (bitOffset, bitOffset+rowBits)
def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
if metrics == None:
metrics = self.metrics
assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
# Loop through each byte. This can cover two bytes in the original data or
# a single byte if things happen to be aligned. The very last entry might
# not be aligned so take care to trim the binary data to size and pad with
# zeros in the row data. Bit aligned data is somewhat tricky.
#
# Example of data cut. Data cut represented in x's.
# '|' represents byte boundary.
# data = ...0XX|XXXXXX00|000... => XXXXXXXX
# or
# data = ...0XX|XXXX0000|000... => XXXXXX00
# or
# data = ...000|XXXXXXXX|000... => XXXXXXXX
# or
# data = ...000|00XXXX00|000... => XXXX0000
#
dataList = []
bitRange = self._getBitRange(row, bitDepth, metrics)
stepRange = bitRange + (8,)
for curBit in xrange(*stepRange):
endBit = min(curBit+8, bitRange[1])
numBits = endBit - curBit
cutPoint = curBit % 8
firstByteLoc = curBit / 8
secondByteLoc = endBit / 8
if firstByteLoc < secondByteLoc:
numBitsCut = 8 - cutPoint
else:
numBitsCut = endBit - curBit
curByte = _reverseBytes(self.imageData[firstByteLoc])
firstHalf = ord(curByte) >> cutPoint
firstHalf = ((1<<numBitsCut)-1) & firstHalf
newByte = firstHalf
if firstByteLoc < secondByteLoc and secondByteLoc < len(self.imageData):
curByte = _reverseBytes(self.imageData[secondByteLoc])
secondHalf = ord(curByte) << numBitsCut
newByte = (firstHalf | secondHalf) & ((1<<numBits)-1)
dataList.append(chr(newByte))
# The way the data is kept is opposite the algorithm used.
data = string.join(dataList, "")
if not reverseBytes:
data = _reverseBytes(data)
return data
def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
if metrics == None:
metrics = self.metrics
if not reverseBytes:
dataRows = map(_reverseBytes, dataRows)
# Keep track of a list of ordinal values as they are easier to modify
# than a list of strings. Map to actual strings later.
numBytes = (self._getBitRange(len(dataRows), bitDepth, metrics)[0] + 7) / 8
ordDataList = [0] * numBytes
for row, data in enumerate(dataRows):
bitRange = self._getBitRange(row, bitDepth, metrics)
stepRange = bitRange + (8,)
for curBit, curByte in itertools.izip(xrange(*stepRange), data):
endBit = min(curBit+8, bitRange[1])
cutPoint = curBit % 8
firstByteLoc = curBit / 8
secondByteLoc = endBit / 8
if firstByteLoc < secondByteLoc:
numBitsCut = 8 - cutPoint
else:
numBitsCut = endBit - curBit
curByte = ord(curByte)
firstByte = curByte & ((1<<numBitsCut)-1)
ordDataList[firstByteLoc] |= (firstByte << cutPoint)
if firstByteLoc < secondByteLoc and secondByteLoc < numBytes:
secondByte = (curByte >> numBitsCut) & ((1<<8-numBitsCut)-1)
ordDataList[secondByteLoc] |= secondByte
# Save the image data with the bits going the correct way.
self.imageData = _reverseBytes(string.join(map(chr, ordDataList), ""))
class ByteAlignedBitmapMixin:
def _getByteRange(self, row, bitDepth, metrics):
rowBytes = (bitDepth * metrics.width + 7) / 8
byteOffset = row * rowBytes
return (byteOffset, byteOffset+rowBytes)
def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
if metrics == None:
metrics = self.metrics
assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
byteRange = self._getByteRange(row, bitDepth, metrics)
data = self.imageData[slice(*byteRange)]
if reverseBytes:
data = _reverseBytes(data)
return data
def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
if metrics == None:
metrics = self.metrics
if reverseBytes:
dataRows = map(_reverseBytes, dataRows)
self.imageData = string.join(dataRows, "")
class ebdt_bitmap_format_1(ByteAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph):
def decompile(self):
self.metrics = SmallGlyphMetrics()
dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
self.imageData = data
def compile(self, ttFont):
data = sstruct.pack(smallGlyphMetricsFormat, self.metrics)
return data + self.imageData
class ebdt_bitmap_format_2(BitAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph):
def decompile(self):
self.metrics = SmallGlyphMetrics()
dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
self.imageData = data
def compile(self, ttFont):
data = sstruct.pack(smallGlyphMetricsFormat, self.metrics)
return data + self.imageData
class ebdt_bitmap_format_5(BitAlignedBitmapMixin, BitmapGlyph):
def decompile(self):
self.imageData = self.data
def compile(self, ttFont):
return self.imageData
class ebdt_bitmap_format_6(ByteAlignedBitmapMixin, BitmapPlusBigMetricsMixin, BitmapGlyph):
def decompile(self):
self.metrics = BigGlyphMetrics()
dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
self.imageData = data
def compile(self, ttFont):
data = sstruct.pack(bigGlyphMetricsFormat, self.metrics)
return data + self.imageData
class ebdt_bitmap_format_7(BitAlignedBitmapMixin, BitmapPlusBigMetricsMixin, BitmapGlyph):
def decompile(self):
self.metrics = BigGlyphMetrics()
dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
self.imageData = data
def compile(self, ttFont):
data = sstruct.pack(bigGlyphMetricsFormat, self.metrics)
return data + self.imageData
class ComponentBitmapGlyph(BitmapGlyph):
def toXML(self, strikeIndex, glyphName, writer, ttFont):
writer.begintag(self.__class__.__name__, [('name', glyphName)])
writer.newline()
self.writeMetrics(writer, ttFont)
writer.begintag('components')
writer.newline()
for curComponent in self.componentArray:
curComponent.toXML(writer, ttFont)
writer.endtag('components')
writer.newline()
writer.endtag(self.__class__.__name__)
writer.newline()
def fromXML(self, (name, attrs, content), ttFont):
self.readMetrics((name, attrs, content), ttFont)
for element in content:
if type(element) != TupleType:
continue
name, attr, content = element
if name == 'components':
self.componentArray = []
for compElement in content:
if type(compElement) != TupleType:
continue
name, attr, content = compElement
if name == 'ebdtComponent':
curComponent = EbdtComponent()
curComponent.fromXML(compElement, ttFont)
self.componentArray.append(curComponent)
else:
print "Warning: '%s' being ignored in component array." % name
class ebdt_bitmap_format_8(BitmapPlusSmallMetricsMixin, ComponentBitmapGlyph):
def decompile(self):
self.metrics = SmallGlyphMetrics()
dummy, data = sstruct.unpack2(smallGlyphMetricsFormat, self.data, self.metrics)
data = data[1:]
(numComponents,) = struct.unpack(">H", data[:2])
data = data[2:]
self.componentArray = []
for i in xrange(numComponents):
curComponent = EbdtComponent()
dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
self.componentArray.append(curComponent)
def compile(self, ttFont):
dataList = []
dataList.append(sstruct.pack(smallGlyphMetricsFormat, self.metrics))
dataList.append('\0')
dataList.append(struct.pack(">H", len(self.componentArray)))
for curComponent in self.componentArray:
curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
return string.join(dataList, "")
class ebdt_bitmap_format_9(BitmapPlusBigMetricsMixin, ComponentBitmapGlyph):
def decompile(self):
self.metrics = BigGlyphMetrics()
dummy, data = sstruct.unpack2(bigGlyphMetricsFormat, self.data, self.metrics)
(numComponents,) = struct.unpack(">H", data[:2])
data = data[2:]
self.componentArray = []
for i in xrange(numComponents):
curComponent = EbdtComponent()
dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
self.componentArray.append(curComponent)
def compile(self, ttFont):
dataList = []
dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
dataList.append(struct.pack(">H", len(self.componentArray)))
for curComponent in self.componentArray:
curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
return string.join(dataList, "")
# Dictionary of bitmap formats to the class representing that format
# currently only the ones listed in this map are the ones supported.
ebdt_bitmap_classes = {
1: ebdt_bitmap_format_1,
2: ebdt_bitmap_format_2,
5: ebdt_bitmap_format_5,
6: ebdt_bitmap_format_6,
7: ebdt_bitmap_format_7,
8: ebdt_bitmap_format_8,
9: ebdt_bitmap_format_9,
}