aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/ttLib/tables/E_B_D_T_.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/ttLib/tables/E_B_D_T_.py')
-rw-r--r--Lib/fontTools/ttLib/tables/E_B_D_T_.py1375
1 files changed, 718 insertions, 657 deletions
diff --git a/Lib/fontTools/ttLib/tables/E_B_D_T_.py b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
index ae716512..9f7f82ef 100644
--- a/Lib/fontTools/ttLib/tables/E_B_D_T_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
@@ -1,6 +1,20 @@
from fontTools.misc import sstruct
-from fontTools.misc.textTools import bytechr, byteord, bytesjoin, strjoin, safeEval, readHex, hexStr, deHexStr
-from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+from fontTools.misc.textTools import (
+ bytechr,
+ byteord,
+ bytesjoin,
+ strjoin,
+ safeEval,
+ readHex,
+ hexStr,
+ deHexStr,
+)
+from .BitmapGlyphMetrics import (
+ BigGlyphMetrics,
+ bigGlyphMetricsFormat,
+ SmallGlyphMetrics,
+ smallGlyphMetricsFormat,
+)
from . import DefaultTable
import itertools
import os
@@ -22,220 +36,232 @@ ebdtComponentFormat = """
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 = zip(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 zip(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 bytesjoin(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 zip(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 not isinstance(element, tuple):
+ 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(name, attrs, content, ttFont)
+ assert glyphName not in bitmapGlyphDict, (
+ "Duplicate glyphs with the same name '%s' in the same strike."
+ % glyphName
+ )
+ bitmapGlyphDict[glyphName] = curGlyph
+ else:
+ log.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] is None
+ ), "Duplicate strike EBDT indices."
+ self.strikeData[strikeIndex] = bitmapGlyphDict
- # 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 = zip(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 zip(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 bytesjoin(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 zip(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 not isinstance(element, tuple):
- 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(name, attrs, content, ttFont)
- assert glyphName not in bitmapGlyphDict, "Duplicate glyphs with the same name '%s' in the same strike." % glyphName
- bitmapGlyphDict[glyphName] = curGlyph
- else:
- log.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] is None, "Duplicate strike EBDT indices."
- self.strikeData[strikeIndex] = bitmapGlyphDict
class EbdtComponent(object):
+ 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 not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name in componentNames:
+ vars(self)[name] = safeEval(attrs["value"])
+ else:
+ log.warning("unknown name '%s' being ignored by EbdtComponent.", name)
- 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 not isinstance(element, tuple):
- continue
- name, attrs, content = element
- if name in componentNames:
- vars(self)[name] = safeEval(attrs['value'])
- else:
- log.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 = byteord(curByte)
- numBitsCut = min(8, numBits)
- for i in range(numBitsCut):
- if value & 0x1:
- binaryList.append('1')
- else:
- binaryList.append('0')
- value = value >> 1
- numBits -= numBitsCut
- return strjoin(binaryList)
+ binaryList = []
+ for curByte in data:
+ value = byteord(curByte)
+ numBitsCut = min(8, numBits)
+ for i in range(numBitsCut):
+ if value & 0x1:
+ binaryList.append("1")
+ else:
+ binaryList.append("0")
+ value = value >> 1
+ numBits -= numBitsCut
+ return strjoin(binaryList)
+
def _binary2data(binary):
- byteList = []
- for bitLoc in range(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(bytechr(curByte))
- return bytesjoin(byteList)
+ byteList = []
+ for bitLoc in range(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(bytechr(curByte))
+ return bytesjoin(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__
+ class memodict(dict):
+ def __missing__(self, key):
+ ret = f(key)
+ if isinstance(key, int) or 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
@@ -243,524 +269,559 @@ def _memoize(f):
# opposite of what makes sense algorithmically and hence this function.
@_memoize
def _reverseBytes(data):
- if len(data) != 1:
- return bytesjoin(map(_reverseBytes, data))
- byte = byteord(data)
- result = 0
- for i in range(8):
- result = result << 1
- result |= byte & 1
- byte = byte >> 1
- return bytechr(result)
+ r"""
+ >>> bin(ord(_reverseBytes(0b00100111)))
+ '0b11100100'
+ >>> _reverseBytes(b'\x00\xf0')
+ b'\x00\x0f'
+ """
+ if isinstance(data, bytes) and len(data) != 1:
+ return bytesjoin(map(_reverseBytes, data))
+ byte = byteord(data)
+ result = 0
+ for i in range(8):
+ result = result << 1
+ result |= byte & 1
+ byte = byte >> 1
+ return bytechr(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()
+ 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)
+ 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 range(metrics.height):
- rowData = bitmapObject.getRow(curRow, bitDepth=bitDepth, metrics=metrics)
- writer.simpletag('row', value=hexStr(rowData))
- writer.newline()
- writer.endtag('rowimagedata')
- writer.newline()
+ 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 range(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 not isinstance(element, tuple):
- 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)
+ bitDepth = safeEval(attrs["bitDepth"])
+ metrics = SmallGlyphMetrics()
+ metrics.width = safeEval(attrs["width"])
+ metrics.height = safeEval(attrs["height"])
+
+ dataRows = []
+ for element in content:
+ if not isinstance(element, tuple):
+ 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 range(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 = strjoin(map(binaryConv.get, rowData))
- writer.simpletag('row', value=rowData)
- writer.newline()
- writer.endtag('bitwiseimagedata')
- writer.newline()
+ 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 range(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 = strjoin(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 not isinstance(element, tuple):
- continue
- name, attr, content = element
- if name == 'row':
- mapParams = zip(attr['value'], itertools.repeat('1'))
- rowData = strjoin(itertools.starmap(binaryConv.get, mapParams))
- dataRows.append(_binary2data(rowData))
-
- bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics, reverseBytes=True)
+ 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 not isinstance(element, tuple):
+ continue
+ name, attr, content = element
+ if name == "row":
+ mapParams = zip(attr["value"], itertools.repeat("1"))
+ rowData = strjoin(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):
- try:
- folder = os.path.dirname(writer.file.name)
- except AttributeError:
- # fall back to current directory if output file's directory isn't found
- folder = '.'
- folder = os.path.join(folder, 'bitmaps')
- filename = glyphName + bitmapObject.fileExtension
- if not os.path.isdir(folder):
- os.makedirs(folder)
- folder = os.path.join(folder, 'strike%d' % strikeIndex)
- if not os.path.isdir(folder):
- os.makedirs(folder)
-
- fullPath = os.path.join(folder, filename)
- writer.simpletag('extfileimagedata', value=fullPath)
- writer.newline()
-
- with open(fullPath, "wb") as file:
- file.write(bitmapObject.imageData)
+ try:
+ folder = os.path.dirname(writer.file.name)
+ except AttributeError:
+ # fall back to current directory if output file's directory isn't found
+ folder = "."
+ folder = os.path.join(folder, "bitmaps")
+ filename = glyphName + bitmapObject.fileExtension
+ if not os.path.isdir(folder):
+ os.makedirs(folder)
+ folder = os.path.join(folder, "strike%d" % strikeIndex)
+ if not os.path.isdir(folder):
+ os.makedirs(folder)
+
+ fullPath = os.path.join(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()
+ 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_'
+_bitmapGlyphSubclassPrefix = "ebdt_bitmap_format_"
-class BitmapGlyph(object):
- # 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
- # TODO Currently non-lazy decompilation is untested here...
- #if not ttFont.lazy:
- # self.decompile()
- # del self.data
-
- def __getattr__(self, attr):
- # Allow lazy decompile.
- if attr[:2] == '__':
- raise AttributeError(attr)
- if attr == "data":
- raise AttributeError(attr)
- self.decompile()
- del self.data
- return getattr(self, attr)
-
- def ensureDecompiled(self, recurse=False):
- if hasattr(self, "data"):
- self.decompile()
- del self.data
-
- # 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 not isinstance(element, tuple):
- continue
- name, attr, content = element
- if not name.endswith('imagedata'):
- continue
- # Chop off 'imagedata' from the tag to get just the option.
- option = name[:-len('imagedata')]
- assert option in self.__class__.xmlDataFunctions
- self.readData(name, attr, content, 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)
+class BitmapGlyph(object):
+ # 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
+ # TODO Currently non-lazy decompilation is untested here...
+ # if not ttFont.lazy:
+ # self.decompile()
+ # del self.data
+
+ def __getattr__(self, attr):
+ # Allow lazy decompile.
+ if attr[:2] == "__":
+ raise AttributeError(attr)
+ if attr == "data":
+ raise AttributeError(attr)
+ self.decompile()
+ del self.data
+ return getattr(self, attr)
+
+ def ensureDecompiled(self, recurse=False):
+ if hasattr(self, "data"):
+ self.decompile()
+ del self.data
+
+ # 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 not isinstance(element, tuple):
+ continue
+ name, attr, content = element
+ if not name.endswith("imagedata"):
+ continue
+ # Chop off 'imagedata' from the tag to get just the option.
+ option = name[: -len("imagedata")]
+ assert option in self.__class__.xmlDataFunctions
+ self.readData(name, attr, content, 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(object):
-
- def writeMetrics(self, writer, ttFont):
- self.metrics.toXML(writer, ttFont)
-
- def readMetrics(self, name, attrs, content, ttFont):
- for element in content:
- if not isinstance(element, tuple):
- continue
- name, attrs, content = element
- if name == curMetricsName:
- self.metrics = metricsClass()
- self.metrics.fromXML(name, attrs, content, ttFont)
- elif name == oppositeMetricsName:
- log.warning("Warning: %s being ignored in format %d.", oppositeMetricsName, self.getFormat())
-
- return BitmapPlusMetricsMixin
+ # 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(object):
+ def writeMetrics(self, writer, ttFont):
+ self.metrics.toXML(writer, ttFont)
+
+ def readMetrics(self, name, attrs, content, ttFont):
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == curMetricsName:
+ self.metrics = metricsClass()
+ self.metrics.fromXML(name, attrs, content, ttFont)
+ elif name == oppositeMetricsName:
+ log.warning(
+ "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(object):
+ 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 is 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 range(*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 = byteord(curByte) >> cutPoint
+ firstHalf = ((1 << numBitsCut) - 1) & firstHalf
+ newByte = firstHalf
+ if firstByteLoc < secondByteLoc and secondByteLoc < len(self.imageData):
+ curByte = _reverseBytes(self.imageData[secondByteLoc])
+ secondHalf = byteord(curByte) << numBitsCut
+ newByte = (firstHalf | secondHalf) & ((1 << numBits) - 1)
+ dataList.append(bytechr(newByte))
+
+ # The way the data is kept is opposite the algorithm used.
+ data = bytesjoin(dataList)
+ if not reverseBytes:
+ data = _reverseBytes(data)
+ return data
+
+ def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
+ if metrics is None:
+ metrics = self.metrics
+ if not reverseBytes:
+ dataRows = list(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 zip(range(*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 = byteord(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(bytesjoin(map(bytechr, ordDataList)))
- 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 is 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 range(*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 = byteord(curByte) >> cutPoint
- firstHalf = ((1<<numBitsCut)-1) & firstHalf
- newByte = firstHalf
- if firstByteLoc < secondByteLoc and secondByteLoc < len(self.imageData):
- curByte = _reverseBytes(self.imageData[secondByteLoc])
- secondHalf = byteord(curByte) << numBitsCut
- newByte = (firstHalf | secondHalf) & ((1<<numBits)-1)
- dataList.append(bytechr(newByte))
-
- # The way the data is kept is opposite the algorithm used.
- data = bytesjoin(dataList)
- if not reverseBytes:
- data = _reverseBytes(data)
- return data
-
- def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
- if metrics is None:
- metrics = self.metrics
- if not reverseBytes:
- dataRows = list(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 zip(range(*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 = byteord(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(bytesjoin(map(bytechr, ordDataList)))
class ByteAlignedBitmapMixin(object):
-
- 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 is 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 is None:
- metrics = self.metrics
- if reverseBytes:
- dataRows = map(_reverseBytes, dataRows)
- self.imageData = bytesjoin(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
+ 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 is 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 is None:
+ metrics = self.metrics
+ if reverseBytes:
+ dataRows = map(_reverseBytes, dataRows)
+ self.imageData = bytesjoin(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 decompile(self):
- self.imageData = self.data
-
- def compile(self, ttFont):
- return self.imageData
+ 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
+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
+ def compile(self, ttFont):
+ data = sstruct.pack(bigGlyphMetricsFormat, self.metrics)
+ return data + self.imageData
-class ebdt_bitmap_format_7(BitAlignedBitmapMixin, BitmapPlusBigMetricsMixin, BitmapGlyph):
+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 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
+ 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 not isinstance(element, tuple):
- continue
- name, attr, content = element
- if name == 'components':
- self.componentArray = []
- for compElement in content:
- if not isinstance(compElement, tuple):
- continue
- name, attrs, content = compElement
- if name == 'ebdtComponent':
- curComponent = EbdtComponent()
- curComponent.fromXML(name, attrs, content, ttFont)
- self.componentArray.append(curComponent)
- else:
- log.warning("'%s' being ignored in component array.", name)
+ 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 not isinstance(element, tuple):
+ continue
+ name, attr, content = element
+ if name == "components":
+ self.componentArray = []
+ for compElement in content:
+ if not isinstance(compElement, tuple):
+ continue
+ name, attrs, content = compElement
+ if name == "ebdtComponent":
+ curComponent = EbdtComponent()
+ curComponent.fromXML(name, attrs, content, ttFont)
+ self.componentArray.append(curComponent)
+ else:
+ log.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 range(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(b'\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 bytesjoin(dataList)
+ 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 range(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(b"\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 bytesjoin(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 range(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 bytesjoin(dataList)
+ 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 range(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 bytesjoin(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,
- }
+ 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,
+}