aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/ttLib/tables/E_B_L_C_.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/ttLib/tables/E_B_L_C_.py')
-rw-r--r--Lib/fontTools/ttLib/tables/E_B_L_C_.py1139
1 files changed, 610 insertions, 529 deletions
diff --git a/Lib/fontTools/ttLib/tables/E_B_L_C_.py b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
index bb3d2140..6046d910 100644
--- a/Lib/fontTools/ttLib/tables/E_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
@@ -1,7 +1,12 @@
from fontTools.misc import sstruct
from . import DefaultTable
from fontTools.misc.textTools import bytesjoin, safeEval
-from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+from .BitmapGlyphMetrics import (
+ BigGlyphMetrics,
+ bigGlyphMetricsFormat,
+ SmallGlyphMetrics,
+ smallGlyphMetricsFormat,
+)
import struct
import itertools
from collections import deque
@@ -59,571 +64,647 @@ indexSubHeaderSize = struct.calcsize(indexSubHeaderFormat)
codeOffsetPairFormat = ">HH"
codeOffsetPairSize = struct.calcsize(codeOffsetPairFormat)
+
class table_E_B_L_C_(DefaultTable.DefaultTable):
+ dependencies = ["EBDT"]
+
+ # 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 getIndexFormatClass(self, indexFormat):
+ return eblc_sub_table_classes[indexFormat]
+
+ def decompile(self, data, ttFont):
+ # Save the original data because offsets are from the start of the table.
+ origData = data
+ i = 0
+
+ dummy = sstruct.unpack(eblcHeaderFormat, data[:8], self)
+ i += 8
+
+ self.strikes = []
+ for curStrikeIndex in range(self.numSizes):
+ curStrike = Strike()
+ self.strikes.append(curStrike)
+ curTable = curStrike.bitmapSizeTable
+ dummy = sstruct.unpack2(
+ bitmapSizeTableFormatPart1, data[i : i + 16], curTable
+ )
+ i += 16
+ for metric in ("hori", "vert"):
+ metricObj = SbitLineMetrics()
+ vars(curTable)[metric] = metricObj
+ dummy = sstruct.unpack2(
+ sbitLineMetricsFormat, data[i : i + 12], metricObj
+ )
+ i += 12
+ dummy = sstruct.unpack(
+ bitmapSizeTableFormatPart2, data[i : i + 8], curTable
+ )
+ i += 8
+
+ for curStrike in self.strikes:
+ curTable = curStrike.bitmapSizeTable
+ for subtableIndex in range(curTable.numberOfIndexSubTables):
+ i = (
+ curTable.indexSubTableArrayOffset
+ + subtableIndex * indexSubTableArraySize
+ )
+
+ tup = struct.unpack(
+ indexSubTableArrayFormat, data[i : i + indexSubTableArraySize]
+ )
+ (firstGlyphIndex, lastGlyphIndex, additionalOffsetToIndexSubtable) = tup
+ i = curTable.indexSubTableArrayOffset + additionalOffsetToIndexSubtable
+
+ tup = struct.unpack(
+ indexSubHeaderFormat, data[i : i + indexSubHeaderSize]
+ )
+ (indexFormat, imageFormat, imageDataOffset) = tup
+
+ indexFormatClass = self.getIndexFormatClass(indexFormat)
+ indexSubTable = indexFormatClass(data[i + indexSubHeaderSize :], ttFont)
+ indexSubTable.firstGlyphIndex = firstGlyphIndex
+ indexSubTable.lastGlyphIndex = lastGlyphIndex
+ indexSubTable.additionalOffsetToIndexSubtable = (
+ additionalOffsetToIndexSubtable
+ )
+ indexSubTable.indexFormat = indexFormat
+ indexSubTable.imageFormat = imageFormat
+ indexSubTable.imageDataOffset = imageDataOffset
+ indexSubTable.decompile() # https://github.com/fonttools/fonttools/issues/317
+ curStrike.indexSubTables.append(indexSubTable)
+
+ def compile(self, ttFont):
+ dataList = []
+ self.numSizes = len(self.strikes)
+ dataList.append(sstruct.pack(eblcHeaderFormat, self))
+
+ # Data size of the header + bitmapSizeTable needs to be calculated
+ # in order to form offsets. This value will hold the size of the data
+ # in dataList after all the data is consolidated in dataList.
+ dataSize = len(dataList[0])
+
+ # The table will be structured in the following order:
+ # (0) header
+ # (1) Each bitmapSizeTable [1 ... self.numSizes]
+ # (2) Alternate between indexSubTableArray and indexSubTable
+ # for each bitmapSizeTable present.
+ #
+ # The issue is maintaining the proper offsets when table information
+ # gets moved around. All offsets and size information must be recalculated
+ # when building the table to allow editing within ttLib and also allow easy
+ # import/export to and from XML. All of this offset information is lost
+ # when exporting to XML so everything must be calculated fresh so importing
+ # from XML will work cleanly. Only byte offset and size information is
+ # calculated fresh. Count information like numberOfIndexSubTables is
+ # checked through assertions. If the information in this table was not
+ # touched or was changed properly then these types of values should match.
+ #
+ # The table will be rebuilt the following way:
+ # (0) Precompute the size of all the bitmapSizeTables. This is needed to
+ # compute the offsets properly.
+ # (1) For each bitmapSizeTable compute the indexSubTable and
+ # indexSubTableArray pair. The indexSubTable must be computed first
+ # so that the offset information in indexSubTableArray can be
+ # calculated. Update the data size after each pairing.
+ # (2) Build each bitmapSizeTable.
+ # (3) Consolidate all the data into the main dataList in the correct order.
+
+ for _ in self.strikes:
+ dataSize += sstruct.calcsize(bitmapSizeTableFormatPart1)
+ dataSize += len(("hori", "vert")) * sstruct.calcsize(sbitLineMetricsFormat)
+ dataSize += sstruct.calcsize(bitmapSizeTableFormatPart2)
+
+ indexSubTablePairDataList = []
+ for curStrike in self.strikes:
+ curTable = curStrike.bitmapSizeTable
+ curTable.numberOfIndexSubTables = len(curStrike.indexSubTables)
+ curTable.indexSubTableArrayOffset = dataSize
+
+ # Precompute the size of the indexSubTableArray. This information
+ # is important for correctly calculating the new value for
+ # additionalOffsetToIndexSubtable.
+ sizeOfSubTableArray = (
+ curTable.numberOfIndexSubTables * indexSubTableArraySize
+ )
+ lowerBound = dataSize
+ dataSize += sizeOfSubTableArray
+ upperBound = dataSize
+
+ indexSubTableDataList = []
+ for indexSubTable in curStrike.indexSubTables:
+ indexSubTable.additionalOffsetToIndexSubtable = (
+ dataSize - curTable.indexSubTableArrayOffset
+ )
+ glyphIds = list(map(ttFont.getGlyphID, indexSubTable.names))
+ indexSubTable.firstGlyphIndex = min(glyphIds)
+ indexSubTable.lastGlyphIndex = max(glyphIds)
+ data = indexSubTable.compile(ttFont)
+ indexSubTableDataList.append(data)
+ dataSize += len(data)
+ curTable.startGlyphIndex = min(
+ ist.firstGlyphIndex for ist in curStrike.indexSubTables
+ )
+ curTable.endGlyphIndex = max(
+ ist.lastGlyphIndex for ist in curStrike.indexSubTables
+ )
+
+ for i in curStrike.indexSubTables:
+ data = struct.pack(
+ indexSubHeaderFormat,
+ i.firstGlyphIndex,
+ i.lastGlyphIndex,
+ i.additionalOffsetToIndexSubtable,
+ )
+ indexSubTablePairDataList.append(data)
+ indexSubTablePairDataList.extend(indexSubTableDataList)
+ curTable.indexTablesSize = dataSize - curTable.indexSubTableArrayOffset
+
+ for curStrike in self.strikes:
+ curTable = curStrike.bitmapSizeTable
+ data = sstruct.pack(bitmapSizeTableFormatPart1, curTable)
+ dataList.append(data)
+ for metric in ("hori", "vert"):
+ metricObj = vars(curTable)[metric]
+ data = sstruct.pack(sbitLineMetricsFormat, metricObj)
+ dataList.append(data)
+ data = sstruct.pack(bitmapSizeTableFormatPart2, curTable)
+ dataList.append(data)
+ dataList.extend(indexSubTablePairDataList)
+
+ return bytesjoin(dataList)
+
+ def toXML(self, writer, ttFont):
+ writer.simpletag("header", [("version", self.version)])
+ writer.newline()
+ for curIndex, curStrike in enumerate(self.strikes):
+ curStrike.toXML(curIndex, writer, ttFont)
+
+ def fromXML(self, name, attrs, content, ttFont):
+ if name == "header":
+ self.version = safeEval(attrs["version"])
+ elif name == "strike":
+ if not hasattr(self, "strikes"):
+ self.strikes = []
+ strikeIndex = safeEval(attrs["index"])
+ curStrike = Strike()
+ curStrike.fromXML(name, attrs, content, ttFont, self)
+
+ # Grow the strike array to the appropriate size. The XML format
+ # allows for the strike index value to be out of order.
+ if strikeIndex >= len(self.strikes):
+ self.strikes += [None] * (strikeIndex + 1 - len(self.strikes))
+ assert self.strikes[strikeIndex] is None, "Duplicate strike EBLC indices."
+ self.strikes[strikeIndex] = curStrike
- dependencies = ['EBDT']
-
- # 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 getIndexFormatClass(self, indexFormat):
- return eblc_sub_table_classes[indexFormat]
-
- def decompile(self, data, ttFont):
-
- # Save the original data because offsets are from the start of the table.
- origData = data
- i = 0;
-
- dummy = sstruct.unpack(eblcHeaderFormat, data[:8], self)
- i += 8;
-
- self.strikes = []
- for curStrikeIndex in range(self.numSizes):
- curStrike = Strike()
- self.strikes.append(curStrike)
- curTable = curStrike.bitmapSizeTable
- dummy = sstruct.unpack2(bitmapSizeTableFormatPart1, data[i:i+16], curTable)
- i += 16
- for metric in ('hori', 'vert'):
- metricObj = SbitLineMetrics()
- vars(curTable)[metric] = metricObj
- dummy = sstruct.unpack2(sbitLineMetricsFormat, data[i:i+12], metricObj)
- i += 12
- dummy = sstruct.unpack(bitmapSizeTableFormatPart2, data[i:i+8], curTable)
- i += 8
-
- for curStrike in self.strikes:
- curTable = curStrike.bitmapSizeTable
- for subtableIndex in range(curTable.numberOfIndexSubTables):
- i = curTable.indexSubTableArrayOffset + subtableIndex * indexSubTableArraySize
-
- tup = struct.unpack(indexSubTableArrayFormat, data[i:i+indexSubTableArraySize])
- (firstGlyphIndex, lastGlyphIndex, additionalOffsetToIndexSubtable) = tup
- i = curTable.indexSubTableArrayOffset + additionalOffsetToIndexSubtable
-
- tup = struct.unpack(indexSubHeaderFormat, data[i:i+indexSubHeaderSize])
- (indexFormat, imageFormat, imageDataOffset) = tup
-
- indexFormatClass = self.getIndexFormatClass(indexFormat)
- indexSubTable = indexFormatClass(data[i+indexSubHeaderSize:], ttFont)
- indexSubTable.firstGlyphIndex = firstGlyphIndex
- indexSubTable.lastGlyphIndex = lastGlyphIndex
- indexSubTable.additionalOffsetToIndexSubtable = additionalOffsetToIndexSubtable
- indexSubTable.indexFormat = indexFormat
- indexSubTable.imageFormat = imageFormat
- indexSubTable.imageDataOffset = imageDataOffset
- indexSubTable.decompile() # https://github.com/fonttools/fonttools/issues/317
- curStrike.indexSubTables.append(indexSubTable)
-
- def compile(self, ttFont):
-
- dataList = []
- self.numSizes = len(self.strikes)
- dataList.append(sstruct.pack(eblcHeaderFormat, self))
-
- # Data size of the header + bitmapSizeTable needs to be calculated
- # in order to form offsets. This value will hold the size of the data
- # in dataList after all the data is consolidated in dataList.
- dataSize = len(dataList[0])
-
- # The table will be structured in the following order:
- # (0) header
- # (1) Each bitmapSizeTable [1 ... self.numSizes]
- # (2) Alternate between indexSubTableArray and indexSubTable
- # for each bitmapSizeTable present.
- #
- # The issue is maintaining the proper offsets when table information
- # gets moved around. All offsets and size information must be recalculated
- # when building the table to allow editing within ttLib and also allow easy
- # import/export to and from XML. All of this offset information is lost
- # when exporting to XML so everything must be calculated fresh so importing
- # from XML will work cleanly. Only byte offset and size information is
- # calculated fresh. Count information like numberOfIndexSubTables is
- # checked through assertions. If the information in this table was not
- # touched or was changed properly then these types of values should match.
- #
- # The table will be rebuilt the following way:
- # (0) Precompute the size of all the bitmapSizeTables. This is needed to
- # compute the offsets properly.
- # (1) For each bitmapSizeTable compute the indexSubTable and
- # indexSubTableArray pair. The indexSubTable must be computed first
- # so that the offset information in indexSubTableArray can be
- # calculated. Update the data size after each pairing.
- # (2) Build each bitmapSizeTable.
- # (3) Consolidate all the data into the main dataList in the correct order.
-
- for _ in self.strikes:
- dataSize += sstruct.calcsize(bitmapSizeTableFormatPart1)
- dataSize += len(('hori', 'vert')) * sstruct.calcsize(sbitLineMetricsFormat)
- dataSize += sstruct.calcsize(bitmapSizeTableFormatPart2)
-
- indexSubTablePairDataList = []
- for curStrike in self.strikes:
- curTable = curStrike.bitmapSizeTable
- curTable.numberOfIndexSubTables = len(curStrike.indexSubTables)
- curTable.indexSubTableArrayOffset = dataSize
-
- # Precompute the size of the indexSubTableArray. This information
- # is important for correctly calculating the new value for
- # additionalOffsetToIndexSubtable.
- sizeOfSubTableArray = curTable.numberOfIndexSubTables * indexSubTableArraySize
- lowerBound = dataSize
- dataSize += sizeOfSubTableArray
- upperBound = dataSize
-
- indexSubTableDataList = []
- for indexSubTable in curStrike.indexSubTables:
- indexSubTable.additionalOffsetToIndexSubtable = dataSize - curTable.indexSubTableArrayOffset
- glyphIds = list(map(ttFont.getGlyphID, indexSubTable.names))
- indexSubTable.firstGlyphIndex = min(glyphIds)
- indexSubTable.lastGlyphIndex = max(glyphIds)
- data = indexSubTable.compile(ttFont)
- indexSubTableDataList.append(data)
- dataSize += len(data)
- curTable.startGlyphIndex = min(ist.firstGlyphIndex for ist in curStrike.indexSubTables)
- curTable.endGlyphIndex = max(ist.lastGlyphIndex for ist in curStrike.indexSubTables)
-
- for i in curStrike.indexSubTables:
- data = struct.pack(indexSubHeaderFormat, i.firstGlyphIndex, i.lastGlyphIndex, i.additionalOffsetToIndexSubtable)
- indexSubTablePairDataList.append(data)
- indexSubTablePairDataList.extend(indexSubTableDataList)
- curTable.indexTablesSize = dataSize - curTable.indexSubTableArrayOffset
-
- for curStrike in self.strikes:
- curTable = curStrike.bitmapSizeTable
- data = sstruct.pack(bitmapSizeTableFormatPart1, curTable)
- dataList.append(data)
- for metric in ('hori', 'vert'):
- metricObj = vars(curTable)[metric]
- data = sstruct.pack(sbitLineMetricsFormat, metricObj)
- dataList.append(data)
- data = sstruct.pack(bitmapSizeTableFormatPart2, curTable)
- dataList.append(data)
- dataList.extend(indexSubTablePairDataList)
-
- return bytesjoin(dataList)
-
- def toXML(self, writer, ttFont):
- writer.simpletag('header', [('version', self.version)])
- writer.newline()
- for curIndex, curStrike in enumerate(self.strikes):
- curStrike.toXML(curIndex, writer, ttFont)
-
- def fromXML(self, name, attrs, content, ttFont):
- if name == 'header':
- self.version = safeEval(attrs['version'])
- elif name == 'strike':
- if not hasattr(self, 'strikes'):
- self.strikes = []
- strikeIndex = safeEval(attrs['index'])
- curStrike = Strike()
- curStrike.fromXML(name, attrs, content, ttFont, self)
-
- # Grow the strike array to the appropriate size. The XML format
- # allows for the strike index value to be out of order.
- if strikeIndex >= len(self.strikes):
- self.strikes += [None] * (strikeIndex + 1 - len(self.strikes))
- assert self.strikes[strikeIndex] is None, "Duplicate strike EBLC indices."
- self.strikes[strikeIndex] = curStrike
class Strike(object):
-
- def __init__(self):
- self.bitmapSizeTable = BitmapSizeTable()
- self.indexSubTables = []
-
- def toXML(self, strikeIndex, writer, ttFont):
- writer.begintag('strike', [('index', strikeIndex)])
- writer.newline()
- self.bitmapSizeTable.toXML(writer, ttFont)
- writer.comment('GlyphIds are written but not read. The firstGlyphIndex and\nlastGlyphIndex values will be recalculated by the compiler.')
- writer.newline()
- for indexSubTable in self.indexSubTables:
- indexSubTable.toXML(writer, ttFont)
- writer.endtag('strike')
- writer.newline()
-
- def fromXML(self, name, attrs, content, ttFont, locator):
- for element in content:
- if not isinstance(element, tuple):
- continue
- name, attrs, content = element
- if name == 'bitmapSizeTable':
- self.bitmapSizeTable.fromXML(name, attrs, content, ttFont)
- elif name.startswith(_indexSubTableSubclassPrefix):
- indexFormat = safeEval(name[len(_indexSubTableSubclassPrefix):])
- indexFormatClass = locator.getIndexFormatClass(indexFormat)
- indexSubTable = indexFormatClass(None, None)
- indexSubTable.indexFormat = indexFormat
- indexSubTable.fromXML(name, attrs, content, ttFont)
- self.indexSubTables.append(indexSubTable)
+ def __init__(self):
+ self.bitmapSizeTable = BitmapSizeTable()
+ self.indexSubTables = []
+
+ def toXML(self, strikeIndex, writer, ttFont):
+ writer.begintag("strike", [("index", strikeIndex)])
+ writer.newline()
+ self.bitmapSizeTable.toXML(writer, ttFont)
+ writer.comment(
+ "GlyphIds are written but not read. The firstGlyphIndex and\nlastGlyphIndex values will be recalculated by the compiler."
+ )
+ writer.newline()
+ for indexSubTable in self.indexSubTables:
+ indexSubTable.toXML(writer, ttFont)
+ writer.endtag("strike")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont, locator):
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "bitmapSizeTable":
+ self.bitmapSizeTable.fromXML(name, attrs, content, ttFont)
+ elif name.startswith(_indexSubTableSubclassPrefix):
+ indexFormat = safeEval(name[len(_indexSubTableSubclassPrefix) :])
+ indexFormatClass = locator.getIndexFormatClass(indexFormat)
+ indexSubTable = indexFormatClass(None, None)
+ indexSubTable.indexFormat = indexFormat
+ indexSubTable.fromXML(name, attrs, content, ttFont)
+ self.indexSubTables.append(indexSubTable)
class BitmapSizeTable(object):
-
- # Returns all the simple metric names that bitmap size table
- # cares about in terms of XML creation.
- def _getXMLMetricNames(self):
- dataNames = sstruct.getformat(bitmapSizeTableFormatPart1)[1]
- dataNames = dataNames + sstruct.getformat(bitmapSizeTableFormatPart2)[1]
- # Skip the first 3 data names because they are byte offsets and counts.
- return dataNames[3:]
-
- def toXML(self, writer, ttFont):
- writer.begintag('bitmapSizeTable')
- writer.newline()
- for metric in ('hori', 'vert'):
- getattr(self, metric).toXML(metric, writer, ttFont)
- for metricName in self._getXMLMetricNames():
- writer.simpletag(metricName, value=getattr(self, metricName))
- writer.newline()
- writer.endtag('bitmapSizeTable')
- writer.newline()
-
- def fromXML(self, name, attrs, content, ttFont):
- # Create a lookup for all the simple names that make sense to
- # bitmap size table. Only read the information from these names.
- dataNames = set(self._getXMLMetricNames())
- for element in content:
- if not isinstance(element, tuple):
- continue
- name, attrs, content = element
- if name == 'sbitLineMetrics':
- direction = attrs['direction']
- assert direction in ('hori', 'vert'), "SbitLineMetrics direction specified invalid."
- metricObj = SbitLineMetrics()
- metricObj.fromXML(name, attrs, content, ttFont)
- vars(self)[direction] = metricObj
- elif name in dataNames:
- vars(self)[name] = safeEval(attrs['value'])
- else:
- log.warning("unknown name '%s' being ignored in BitmapSizeTable.", name)
+ # Returns all the simple metric names that bitmap size table
+ # cares about in terms of XML creation.
+ def _getXMLMetricNames(self):
+ dataNames = sstruct.getformat(bitmapSizeTableFormatPart1)[1]
+ dataNames = dataNames + sstruct.getformat(bitmapSizeTableFormatPart2)[1]
+ # Skip the first 3 data names because they are byte offsets and counts.
+ return dataNames[3:]
+
+ def toXML(self, writer, ttFont):
+ writer.begintag("bitmapSizeTable")
+ writer.newline()
+ for metric in ("hori", "vert"):
+ getattr(self, metric).toXML(metric, writer, ttFont)
+ for metricName in self._getXMLMetricNames():
+ writer.simpletag(metricName, value=getattr(self, metricName))
+ writer.newline()
+ writer.endtag("bitmapSizeTable")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ # Create a lookup for all the simple names that make sense to
+ # bitmap size table. Only read the information from these names.
+ dataNames = set(self._getXMLMetricNames())
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "sbitLineMetrics":
+ direction = attrs["direction"]
+ assert direction in (
+ "hori",
+ "vert",
+ ), "SbitLineMetrics direction specified invalid."
+ metricObj = SbitLineMetrics()
+ metricObj.fromXML(name, attrs, content, ttFont)
+ vars(self)[direction] = metricObj
+ elif name in dataNames:
+ vars(self)[name] = safeEval(attrs["value"])
+ else:
+ log.warning("unknown name '%s' being ignored in BitmapSizeTable.", name)
class SbitLineMetrics(object):
+ def toXML(self, name, writer, ttFont):
+ writer.begintag("sbitLineMetrics", [("direction", name)])
+ writer.newline()
+ for metricName in sstruct.getformat(sbitLineMetricsFormat)[1]:
+ writer.simpletag(metricName, value=getattr(self, metricName))
+ writer.newline()
+ writer.endtag("sbitLineMetrics")
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ metricNames = set(sstruct.getformat(sbitLineMetricsFormat)[1])
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name in metricNames:
+ vars(self)[name] = safeEval(attrs["value"])
- def toXML(self, name, writer, ttFont):
- writer.begintag('sbitLineMetrics', [('direction', name)])
- writer.newline()
- for metricName in sstruct.getformat(sbitLineMetricsFormat)[1]:
- writer.simpletag(metricName, value=getattr(self, metricName))
- writer.newline()
- writer.endtag('sbitLineMetrics')
- writer.newline()
-
- def fromXML(self, name, attrs, content, ttFont):
- metricNames = set(sstruct.getformat(sbitLineMetricsFormat)[1])
- for element in content:
- if not isinstance(element, tuple):
- continue
- name, attrs, content = element
- if name in metricNames:
- vars(self)[name] = safeEval(attrs['value'])
# Important information about the naming scheme. Used for identifying subtables.
-_indexSubTableSubclassPrefix = 'eblc_index_sub_table_'
+_indexSubTableSubclassPrefix = "eblc_index_sub_table_"
+
class EblcIndexSubTable(object):
+ def __init__(self, data, ttFont):
+ self.data = data
+ self.ttFont = ttFont
+ # TODO Currently non-lazy decompiling doesn't work for this class...
+ # if not ttFont.lazy:
+ # self.decompile()
+ # del self.data, self.ttFont
+
+ def __getattr__(self, attr):
+ # Allow lazy decompile.
+ if attr[:2] == "__":
+ raise AttributeError(attr)
+ if attr == "data":
+ raise AttributeError(attr)
+ self.decompile()
+ return getattr(self, attr)
+
+ def ensureDecompiled(self, recurse=False):
+ if hasattr(self, "data"):
+ self.decompile()
+
+ # This method just takes care of the indexSubHeader. Implementing subclasses
+ # should call it to compile the indexSubHeader and then continue compiling
+ # the remainder of their unique format.
+ def compile(self, ttFont):
+ return struct.pack(
+ indexSubHeaderFormat,
+ self.indexFormat,
+ self.imageFormat,
+ self.imageDataOffset,
+ )
+
+ # Creates the XML for bitmap glyphs. Each index sub table basically makes
+ # the same XML except for specific metric information that is written
+ # out via a method call that a subclass implements optionally.
+ def toXML(self, writer, ttFont):
+ writer.begintag(
+ self.__class__.__name__,
+ [
+ ("imageFormat", self.imageFormat),
+ ("firstGlyphIndex", self.firstGlyphIndex),
+ ("lastGlyphIndex", self.lastGlyphIndex),
+ ],
+ )
+ writer.newline()
+ self.writeMetrics(writer, ttFont)
+ # Write out the names as thats all thats needed to rebuild etc.
+ # For font debugging of consecutive formats the ids are also written.
+ # The ids are not read when moving from the XML format.
+ glyphIds = map(ttFont.getGlyphID, self.names)
+ for glyphName, glyphId in zip(self.names, glyphIds):
+ writer.simpletag("glyphLoc", name=glyphName, id=glyphId)
+ writer.newline()
+ writer.endtag(self.__class__.__name__)
+ writer.newline()
+
+ def fromXML(self, name, attrs, content, ttFont):
+ # Read all the attributes. Even though the glyph indices are
+ # recalculated, they are still read in case there needs to
+ # be an immediate export of the data.
+ self.imageFormat = safeEval(attrs["imageFormat"])
+ self.firstGlyphIndex = safeEval(attrs["firstGlyphIndex"])
+ self.lastGlyphIndex = safeEval(attrs["lastGlyphIndex"])
+
+ self.readMetrics(name, attrs, content, ttFont)
+
+ self.names = []
+ for element in content:
+ if not isinstance(element, tuple):
+ continue
+ name, attrs, content = element
+ if name == "glyphLoc":
+ self.names.append(attrs["name"])
+
+ # A helper method that writes the metrics for the index sub table. It also
+ # is responsible for writing the image size for fixed size data since fixed
+ # size is not recalculated on compile. Default behavior is to do nothing.
+ def writeMetrics(self, writer, ttFont):
+ pass
+
+ # A helper method that is the inverse of writeMetrics.
+ def readMetrics(self, name, attrs, content, ttFont):
+ pass
+
+ # This method is for fixed glyph data sizes. There are formats where
+ # the glyph data is fixed but are actually composite glyphs. To handle
+ # this the font spec in indexSubTable makes the data the size of the
+ # fixed size by padding the component arrays. This function abstracts
+ # out this padding process. Input is data unpadded. Output is data
+ # padded only in fixed formats. Default behavior is to return the data.
+ def padBitmapData(self, data):
+ return data
+
+ # Remove any of the glyph locations and names that are flagged as skipped.
+ # This only occurs in formats {1,3}.
+ def removeSkipGlyphs(self):
+ # Determines if a name, location pair is a valid data location.
+ # Skip glyphs are marked when the size is equal to zero.
+ def isValidLocation(args):
+ (name, (startByte, endByte)) = args
+ return startByte < endByte
+
+ # Remove all skip glyphs.
+ dataPairs = list(filter(isValidLocation, zip(self.names, self.locations)))
+ self.names, self.locations = list(map(list, zip(*dataPairs)))
- def __init__(self, data, ttFont):
- self.data = data
- self.ttFont = ttFont
- # TODO Currently non-lazy decompiling doesn't work for this class...
- #if not ttFont.lazy:
- # self.decompile()
- # del self.data, self.ttFont
-
- def __getattr__(self, attr):
- # Allow lazy decompile.
- if attr[:2] == '__':
- raise AttributeError(attr)
- if attr == "data":
- raise AttributeError(attr)
- self.decompile()
- return getattr(self, attr)
-
- def ensureDecompiled(self, recurse=False):
- if hasattr(self, "data"):
- self.decompile()
-
- # This method just takes care of the indexSubHeader. Implementing subclasses
- # should call it to compile the indexSubHeader and then continue compiling
- # the remainder of their unique format.
- def compile(self, ttFont):
- return struct.pack(indexSubHeaderFormat, self.indexFormat, self.imageFormat, self.imageDataOffset)
-
- # Creates the XML for bitmap glyphs. Each index sub table basically makes
- # the same XML except for specific metric information that is written
- # out via a method call that a subclass implements optionally.
- def toXML(self, writer, ttFont):
- writer.begintag(self.__class__.__name__, [
- ('imageFormat', self.imageFormat),
- ('firstGlyphIndex', self.firstGlyphIndex),
- ('lastGlyphIndex', self.lastGlyphIndex),
- ])
- writer.newline()
- self.writeMetrics(writer, ttFont)
- # Write out the names as thats all thats needed to rebuild etc.
- # For font debugging of consecutive formats the ids are also written.
- # The ids are not read when moving from the XML format.
- glyphIds = map(ttFont.getGlyphID, self.names)
- for glyphName, glyphId in zip(self.names, glyphIds):
- writer.simpletag('glyphLoc', name=glyphName, id=glyphId)
- writer.newline()
- writer.endtag(self.__class__.__name__)
- writer.newline()
-
- def fromXML(self, name, attrs, content, ttFont):
- # Read all the attributes. Even though the glyph indices are
- # recalculated, they are still read in case there needs to
- # be an immediate export of the data.
- self.imageFormat = safeEval(attrs['imageFormat'])
- self.firstGlyphIndex = safeEval(attrs['firstGlyphIndex'])
- self.lastGlyphIndex = safeEval(attrs['lastGlyphIndex'])
-
- self.readMetrics(name, attrs, content, ttFont)
-
- self.names = []
- for element in content:
- if not isinstance(element, tuple):
- continue
- name, attrs, content = element
- if name == 'glyphLoc':
- self.names.append(attrs['name'])
-
- # A helper method that writes the metrics for the index sub table. It also
- # is responsible for writing the image size for fixed size data since fixed
- # size is not recalculated on compile. Default behavior is to do nothing.
- def writeMetrics(self, writer, ttFont):
- pass
-
- # A helper method that is the inverse of writeMetrics.
- def readMetrics(self, name, attrs, content, ttFont):
- pass
-
- # This method is for fixed glyph data sizes. There are formats where
- # the glyph data is fixed but are actually composite glyphs. To handle
- # this the font spec in indexSubTable makes the data the size of the
- # fixed size by padding the component arrays. This function abstracts
- # out this padding process. Input is data unpadded. Output is data
- # padded only in fixed formats. Default behavior is to return the data.
- def padBitmapData(self, data):
- return data
-
- # Remove any of the glyph locations and names that are flagged as skipped.
- # This only occurs in formats {1,3}.
- def removeSkipGlyphs(self):
- # Determines if a name, location pair is a valid data location.
- # Skip glyphs are marked when the size is equal to zero.
- def isValidLocation(args):
- (name, (startByte, endByte)) = args
- return startByte < endByte
- # Remove all skip glyphs.
- dataPairs = list(filter(isValidLocation, zip(self.names, self.locations)))
- self.names, self.locations = list(map(list, zip(*dataPairs)))
# A closure for creating a custom mixin. This is done because formats 1 and 3
# are very similar. The only difference between them is the size per offset
# value. Code put in here should handle both cases generally.
def _createOffsetArrayIndexSubTableMixin(formatStringForDataType):
+ # Prep the data size for the offset array data format.
+ dataFormat = ">" + formatStringForDataType
+ offsetDataSize = struct.calcsize(dataFormat)
+
+ class OffsetArrayIndexSubTableMixin(object):
+ def decompile(self):
+ numGlyphs = self.lastGlyphIndex - self.firstGlyphIndex + 1
+ indexingOffsets = [
+ glyphIndex * offsetDataSize for glyphIndex in range(numGlyphs + 2)
+ ]
+ indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
+ offsetArray = [
+ struct.unpack(dataFormat, self.data[slice(*loc)])[0]
+ for loc in indexingLocations
+ ]
+
+ glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex + 1))
+ modifiedOffsets = [offset + self.imageDataOffset for offset in offsetArray]
+ self.locations = list(zip(modifiedOffsets, modifiedOffsets[1:]))
+
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
+ self.removeSkipGlyphs()
+ del self.data, self.ttFont
+
+ def compile(self, ttFont):
+ # First make sure that all the data lines up properly. Formats 1 and 3
+ # must have all its data lined up consecutively. If not this will fail.
+ for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
+ assert (
+ curLoc[1] == nxtLoc[0]
+ ), "Data must be consecutive in indexSubTable offset formats"
+
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
+ # Make sure that all ids are sorted strictly increasing.
+ assert all(glyphIds[i] < glyphIds[i + 1] for i in range(len(glyphIds) - 1))
+
+ # Run a simple algorithm to add skip glyphs to the data locations at
+ # the places where an id is not present.
+ idQueue = deque(glyphIds)
+ locQueue = deque(self.locations)
+ allGlyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex + 1))
+ allLocations = []
+ for curId in allGlyphIds:
+ if curId != idQueue[0]:
+ allLocations.append((locQueue[0][0], locQueue[0][0]))
+ else:
+ idQueue.popleft()
+ allLocations.append(locQueue.popleft())
+
+ # Now that all the locations are collected, pack them appropriately into
+ # offsets. This is the form where offset[i] is the location and
+ # offset[i+1]-offset[i] is the size of the data location.
+ offsets = list(allLocations[0]) + [loc[1] for loc in allLocations[1:]]
+ # Image data offset must be less than or equal to the minimum of locations.
+ # This offset may change the value for round tripping but is safer and
+ # allows imageDataOffset to not be required to be in the XML version.
+ self.imageDataOffset = min(offsets)
+ offsetArray = [offset - self.imageDataOffset for offset in offsets]
+
+ dataList = [EblcIndexSubTable.compile(self, ttFont)]
+ dataList += [
+ struct.pack(dataFormat, offsetValue) for offsetValue in offsetArray
+ ]
+ # Take care of any padding issues. Only occurs in format 3.
+ if offsetDataSize * len(offsetArray) % 4 != 0:
+ dataList.append(struct.pack(dataFormat, 0))
+ return bytesjoin(dataList)
+
+ return OffsetArrayIndexSubTableMixin
- # Prep the data size for the offset array data format.
- dataFormat = '>'+formatStringForDataType
- offsetDataSize = struct.calcsize(dataFormat)
-
- class OffsetArrayIndexSubTableMixin(object):
-
- def decompile(self):
-
- numGlyphs = self.lastGlyphIndex - self.firstGlyphIndex + 1
- indexingOffsets = [glyphIndex * offsetDataSize for glyphIndex in range(numGlyphs+2)]
- indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
- offsetArray = [struct.unpack(dataFormat, self.data[slice(*loc)])[0] for loc in indexingLocations]
-
- glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
- modifiedOffsets = [offset + self.imageDataOffset for offset in offsetArray]
- self.locations = list(zip(modifiedOffsets, modifiedOffsets[1:]))
-
- self.names = list(map(self.ttFont.getGlyphName, glyphIds))
- self.removeSkipGlyphs()
- del self.data, self.ttFont
-
- def compile(self, ttFont):
- # First make sure that all the data lines up properly. Formats 1 and 3
- # must have all its data lined up consecutively. If not this will fail.
- for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
- assert curLoc[1] == nxtLoc[0], "Data must be consecutive in indexSubTable offset formats"
-
- glyphIds = list(map(ttFont.getGlyphID, self.names))
- # Make sure that all ids are sorted strictly increasing.
- assert all(glyphIds[i] < glyphIds[i+1] for i in range(len(glyphIds)-1))
-
- # Run a simple algorithm to add skip glyphs to the data locations at
- # the places where an id is not present.
- idQueue = deque(glyphIds)
- locQueue = deque(self.locations)
- allGlyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
- allLocations = []
- for curId in allGlyphIds:
- if curId != idQueue[0]:
- allLocations.append((locQueue[0][0], locQueue[0][0]))
- else:
- idQueue.popleft()
- allLocations.append(locQueue.popleft())
-
- # Now that all the locations are collected, pack them appropriately into
- # offsets. This is the form where offset[i] is the location and
- # offset[i+1]-offset[i] is the size of the data location.
- offsets = list(allLocations[0]) + [loc[1] for loc in allLocations[1:]]
- # Image data offset must be less than or equal to the minimum of locations.
- # This offset may change the value for round tripping but is safer and
- # allows imageDataOffset to not be required to be in the XML version.
- self.imageDataOffset = min(offsets)
- offsetArray = [offset - self.imageDataOffset for offset in offsets]
-
- dataList = [EblcIndexSubTable.compile(self, ttFont)]
- dataList += [struct.pack(dataFormat, offsetValue) for offsetValue in offsetArray]
- # Take care of any padding issues. Only occurs in format 3.
- if offsetDataSize * len(offsetArray) % 4 != 0:
- dataList.append(struct.pack(dataFormat, 0))
- return bytesjoin(dataList)
-
- return OffsetArrayIndexSubTableMixin
# A Mixin for functionality shared between the different kinds
# of fixed sized data handling. Both kinds have big metrics so
# that kind of special processing is also handled in this mixin.
class FixedSizeIndexSubTableMixin(object):
+ def writeMetrics(self, writer, ttFont):
+ writer.simpletag("imageSize", value=self.imageSize)
+ writer.newline()
+ 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 == "imageSize":
+ self.imageSize = safeEval(attrs["value"])
+ elif name == BigGlyphMetrics.__name__:
+ self.metrics = BigGlyphMetrics()
+ self.metrics.fromXML(name, attrs, content, ttFont)
+ elif name == SmallGlyphMetrics.__name__:
+ log.warning(
+ "SmallGlyphMetrics being ignored in format %d.", self.indexFormat
+ )
+
+ def padBitmapData(self, data):
+ # Make sure that the data isn't bigger than the fixed size.
+ assert len(data) <= self.imageSize, (
+ "Data in indexSubTable format %d must be less than the fixed size."
+ % self.indexFormat
+ )
+ # Pad the data so that it matches the fixed size.
+ pad = (self.imageSize - len(data)) * b"\0"
+ return data + pad
+
+
+class eblc_index_sub_table_1(
+ _createOffsetArrayIndexSubTableMixin("L"), EblcIndexSubTable
+):
+ pass
- def writeMetrics(self, writer, ttFont):
- writer.simpletag('imageSize', value=self.imageSize)
- writer.newline()
- 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 == 'imageSize':
- self.imageSize = safeEval(attrs['value'])
- elif name == BigGlyphMetrics.__name__:
- self.metrics = BigGlyphMetrics()
- self.metrics.fromXML(name, attrs, content, ttFont)
- elif name == SmallGlyphMetrics.__name__:
- log.warning("SmallGlyphMetrics being ignored in format %d.", self.indexFormat)
-
- def padBitmapData(self, data):
- # Make sure that the data isn't bigger than the fixed size.
- assert len(data) <= self.imageSize, "Data in indexSubTable format %d must be less than the fixed size." % self.indexFormat
- # Pad the data so that it matches the fixed size.
- pad = (self.imageSize - len(data)) * b'\0'
- return data + pad
-
-class eblc_index_sub_table_1(_createOffsetArrayIndexSubTableMixin('L'), EblcIndexSubTable):
- pass
class eblc_index_sub_table_2(FixedSizeIndexSubTableMixin, EblcIndexSubTable):
+ def decompile(self):
+ (self.imageSize,) = struct.unpack(">L", self.data[:4])
+ self.metrics = BigGlyphMetrics()
+ sstruct.unpack2(bigGlyphMetricsFormat, self.data[4:], self.metrics)
+ glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex + 1))
+ offsets = [
+ self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds) + 1)
+ ]
+ self.locations = list(zip(offsets, offsets[1:]))
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
+ del self.data, self.ttFont
+
+ def compile(self, ttFont):
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
+ # Make sure all the ids are consecutive. This is required by Format 2.
+ assert glyphIds == list(
+ range(self.firstGlyphIndex, self.lastGlyphIndex + 1)
+ ), "Format 2 ids must be consecutive."
+ self.imageDataOffset = min(next(iter(zip(*self.locations))))
+
+ dataList = [EblcIndexSubTable.compile(self, ttFont)]
+ dataList.append(struct.pack(">L", self.imageSize))
+ dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
+ return bytesjoin(dataList)
+
+
+class eblc_index_sub_table_3(
+ _createOffsetArrayIndexSubTableMixin("H"), EblcIndexSubTable
+):
+ pass
- def decompile(self):
- (self.imageSize,) = struct.unpack(">L", self.data[:4])
- self.metrics = BigGlyphMetrics()
- sstruct.unpack2(bigGlyphMetricsFormat, self.data[4:], self.metrics)
- glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
- offsets = [self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds)+1)]
- self.locations = list(zip(offsets, offsets[1:]))
- self.names = list(map(self.ttFont.getGlyphName, glyphIds))
- del self.data, self.ttFont
-
- def compile(self, ttFont):
- glyphIds = list(map(ttFont.getGlyphID, self.names))
- # Make sure all the ids are consecutive. This is required by Format 2.
- assert glyphIds == list(range(self.firstGlyphIndex, self.lastGlyphIndex+1)), "Format 2 ids must be consecutive."
- self.imageDataOffset = min(next(iter(zip(*self.locations))))
-
- dataList = [EblcIndexSubTable.compile(self, ttFont)]
- dataList.append(struct.pack(">L", self.imageSize))
- dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
- return bytesjoin(dataList)
-
-class eblc_index_sub_table_3(_createOffsetArrayIndexSubTableMixin('H'), EblcIndexSubTable):
- pass
class eblc_index_sub_table_4(EblcIndexSubTable):
+ def decompile(self):
+ (numGlyphs,) = struct.unpack(">L", self.data[:4])
+ data = self.data[4:]
+ indexingOffsets = [
+ glyphIndex * codeOffsetPairSize for glyphIndex in range(numGlyphs + 2)
+ ]
+ indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
+ glyphArray = [
+ struct.unpack(codeOffsetPairFormat, data[slice(*loc)])
+ for loc in indexingLocations
+ ]
+ glyphIds, offsets = list(map(list, zip(*glyphArray)))
+ # There are one too many glyph ids. Get rid of the last one.
+ glyphIds.pop()
+
+ offsets = [offset + self.imageDataOffset for offset in offsets]
+ self.locations = list(zip(offsets, offsets[1:]))
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
+ del self.data, self.ttFont
+
+ def compile(self, ttFont):
+ # First make sure that all the data lines up properly. Format 4
+ # must have all its data lined up consecutively. If not this will fail.
+ for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
+ assert (
+ curLoc[1] == nxtLoc[0]
+ ), "Data must be consecutive in indexSubTable format 4"
+
+ offsets = list(self.locations[0]) + [loc[1] for loc in self.locations[1:]]
+ # Image data offset must be less than or equal to the minimum of locations.
+ # Resetting this offset may change the value for round tripping but is safer
+ # and allows imageDataOffset to not be required to be in the XML version.
+ self.imageDataOffset = min(offsets)
+ offsets = [offset - self.imageDataOffset for offset in offsets]
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
+ # Create an iterator over the ids plus a padding value.
+ idsPlusPad = list(itertools.chain(glyphIds, [0]))
+
+ dataList = [EblcIndexSubTable.compile(self, ttFont)]
+ dataList.append(struct.pack(">L", len(glyphIds)))
+ tmp = [
+ struct.pack(codeOffsetPairFormat, *cop) for cop in zip(idsPlusPad, offsets)
+ ]
+ dataList += tmp
+ data = bytesjoin(dataList)
+ return data
- def decompile(self):
-
- (numGlyphs,) = struct.unpack(">L", self.data[:4])
- data = self.data[4:]
- indexingOffsets = [glyphIndex * codeOffsetPairSize for glyphIndex in range(numGlyphs+2)]
- indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
- glyphArray = [struct.unpack(codeOffsetPairFormat, data[slice(*loc)]) for loc in indexingLocations]
- glyphIds, offsets = list(map(list, zip(*glyphArray)))
- # There are one too many glyph ids. Get rid of the last one.
- glyphIds.pop()
-
- offsets = [offset + self.imageDataOffset for offset in offsets]
- self.locations = list(zip(offsets, offsets[1:]))
- self.names = list(map(self.ttFont.getGlyphName, glyphIds))
- del self.data, self.ttFont
-
- def compile(self, ttFont):
- # First make sure that all the data lines up properly. Format 4
- # must have all its data lined up consecutively. If not this will fail.
- for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
- assert curLoc[1] == nxtLoc[0], "Data must be consecutive in indexSubTable format 4"
-
- offsets = list(self.locations[0]) + [loc[1] for loc in self.locations[1:]]
- # Image data offset must be less than or equal to the minimum of locations.
- # Resetting this offset may change the value for round tripping but is safer
- # and allows imageDataOffset to not be required to be in the XML version.
- self.imageDataOffset = min(offsets)
- offsets = [offset - self.imageDataOffset for offset in offsets]
- glyphIds = list(map(ttFont.getGlyphID, self.names))
- # Create an iterator over the ids plus a padding value.
- idsPlusPad = list(itertools.chain(glyphIds, [0]))
-
- dataList = [EblcIndexSubTable.compile(self, ttFont)]
- dataList.append(struct.pack(">L", len(glyphIds)))
- tmp = [struct.pack(codeOffsetPairFormat, *cop) for cop in zip(idsPlusPad, offsets)]
- dataList += tmp
- data = bytesjoin(dataList)
- return data
class eblc_index_sub_table_5(FixedSizeIndexSubTableMixin, EblcIndexSubTable):
+ def decompile(self):
+ self.origDataLen = 0
+ (self.imageSize,) = struct.unpack(">L", self.data[:4])
+ data = self.data[4:]
+ self.metrics, data = sstruct.unpack2(
+ bigGlyphMetricsFormat, data, BigGlyphMetrics()
+ )
+ (numGlyphs,) = struct.unpack(">L", data[:4])
+ data = data[4:]
+ glyphIds = [
+ struct.unpack(">H", data[2 * i : 2 * (i + 1)])[0] for i in range(numGlyphs)
+ ]
+
+ offsets = [
+ self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds) + 1)
+ ]
+ self.locations = list(zip(offsets, offsets[1:]))
+ self.names = list(map(self.ttFont.getGlyphName, glyphIds))
+ del self.data, self.ttFont
+
+ def compile(self, ttFont):
+ self.imageDataOffset = min(next(iter(zip(*self.locations))))
+ dataList = [EblcIndexSubTable.compile(self, ttFont)]
+ dataList.append(struct.pack(">L", self.imageSize))
+ dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
+ glyphIds = list(map(ttFont.getGlyphID, self.names))
+ dataList.append(struct.pack(">L", len(glyphIds)))
+ dataList += [struct.pack(">H", curId) for curId in glyphIds]
+ if len(glyphIds) % 2 == 1:
+ dataList.append(struct.pack(">H", 0))
+ return bytesjoin(dataList)
- def decompile(self):
- self.origDataLen = 0
- (self.imageSize,) = struct.unpack(">L", self.data[:4])
- data = self.data[4:]
- self.metrics, data = sstruct.unpack2(bigGlyphMetricsFormat, data, BigGlyphMetrics())
- (numGlyphs,) = struct.unpack(">L", data[:4])
- data = data[4:]
- glyphIds = [struct.unpack(">H", data[2*i:2*(i+1)])[0] for i in range(numGlyphs)]
-
- offsets = [self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds)+1)]
- self.locations = list(zip(offsets, offsets[1:]))
- self.names = list(map(self.ttFont.getGlyphName, glyphIds))
- del self.data, self.ttFont
-
- def compile(self, ttFont):
- self.imageDataOffset = min(next(iter(zip(*self.locations))))
- dataList = [EblcIndexSubTable.compile(self, ttFont)]
- dataList.append(struct.pack(">L", self.imageSize))
- dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
- glyphIds = list(map(ttFont.getGlyphID, self.names))
- dataList.append(struct.pack(">L", len(glyphIds)))
- dataList += [struct.pack(">H", curId) for curId in glyphIds]
- if len(glyphIds) % 2 == 1:
- dataList.append(struct.pack(">H", 0))
- return bytesjoin(dataList)
# Dictionary of indexFormat to the class representing that format.
eblc_sub_table_classes = {
- 1: eblc_index_sub_table_1,
- 2: eblc_index_sub_table_2,
- 3: eblc_index_sub_table_3,
- 4: eblc_index_sub_table_4,
- 5: eblc_index_sub_table_5,
- }
+ 1: eblc_index_sub_table_1,
+ 2: eblc_index_sub_table_2,
+ 3: eblc_index_sub_table_3,
+ 4: eblc_index_sub_table_4,
+ 5: eblc_index_sub_table_5,
+}