2014-01-14 15:07:50 +08:00
|
|
|
from __future__ import print_function, division, absolute_import
|
2013-11-27 17:27:45 -05:00
|
|
|
from fontTools.misc.py23 import *
|
2002-05-11 00:59:27 +00:00
|
|
|
from fontTools.misc.textTools import safeEval
|
2013-11-28 18:49:30 -05:00
|
|
|
from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
|
2013-11-27 02:34:11 -05:00
|
|
|
from .otBase import ValueRecordFactory
|
2016-08-09 22:51:41 -07:00
|
|
|
from functools import partial
|
2016-01-24 14:43:54 +00:00
|
|
|
import logging
|
|
|
|
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
2002-05-11 00:59:27 +00:00
|
|
|
|
|
|
|
|
2002-05-11 10:21:36 +00:00
|
|
|
def buildConverters(tableSpec, tableNamespace):
|
|
|
|
"""Given a table spec from otData.py, build a converter object for each
|
|
|
|
field of the table. This is called for each table in otData.py, and
|
|
|
|
the results are assigned to the corresponding class in otTables.py."""
|
2002-05-11 00:59:27 +00:00
|
|
|
converters = []
|
|
|
|
convertersByName = {}
|
2013-11-24 22:11:41 -05:00
|
|
|
for tp, name, repeat, aux, descr in tableSpec:
|
2013-12-09 00:39:25 -05:00
|
|
|
tableName = name
|
2002-05-11 00:59:27 +00:00
|
|
|
if name.startswith("ValueFormat"):
|
|
|
|
assert tp == "uint16"
|
|
|
|
converterClass = ValueFormat
|
2013-12-17 02:57:29 -05:00
|
|
|
elif name.endswith("Count") or name.endswith("LookupType"):
|
2002-05-11 00:59:27 +00:00
|
|
|
assert tp == "uint16"
|
2013-12-17 02:57:29 -05:00
|
|
|
converterClass = ComputedUShort
|
2002-05-11 00:59:27 +00:00
|
|
|
elif name == "SubTable":
|
|
|
|
converterClass = SubTable
|
2006-10-21 14:12:38 +00:00
|
|
|
elif name == "ExtSubTable":
|
|
|
|
converterClass = ExtSubTable
|
2013-11-26 19:23:08 -05:00
|
|
|
elif name == "FeatureParams":
|
|
|
|
converterClass = FeatureParams
|
2002-05-11 00:59:27 +00:00
|
|
|
else:
|
2016-08-09 22:51:41 -07:00
|
|
|
if not tp in converterMapping and '(' not in tp:
|
2013-12-09 00:39:25 -05:00
|
|
|
tableName = tp
|
|
|
|
converterClass = Struct
|
|
|
|
else:
|
2016-08-09 22:51:41 -07:00
|
|
|
converterClass = eval(tp, tableNamespace, converterMapping)
|
2013-12-09 00:39:25 -05:00
|
|
|
tableClass = tableNamespace.get(tableName)
|
2016-08-09 22:51:41 -07:00
|
|
|
if tableClass is not None:
|
|
|
|
conv = converterClass(name, repeat, aux, tableClass=tableClass)
|
|
|
|
else:
|
|
|
|
conv = converterClass(name, repeat, aux)
|
2006-10-21 14:12:38 +00:00
|
|
|
if name in ["SubTable", "ExtSubTable"]:
|
2002-05-11 00:59:27 +00:00
|
|
|
conv.lookupTypes = tableNamespace['lookupTypes']
|
|
|
|
# also create reverse mapping
|
|
|
|
for t in conv.lookupTypes.values():
|
|
|
|
for cls in t.values():
|
2013-11-24 22:11:41 -05:00
|
|
|
convertersByName[cls.__name__] = Table(name, repeat, aux, cls)
|
2013-11-26 19:23:08 -05:00
|
|
|
if name == "FeatureParams":
|
|
|
|
conv.featureParamTypes = tableNamespace['featureParamTypes']
|
|
|
|
conv.defaultFeatureParams = tableNamespace['FeatureParams']
|
|
|
|
for cls in conv.featureParamTypes.values():
|
|
|
|
convertersByName[cls.__name__] = Table(name, repeat, aux, cls)
|
2002-05-11 00:59:27 +00:00
|
|
|
converters.append(conv)
|
2013-12-09 00:39:25 -05:00
|
|
|
assert name not in convertersByName, name
|
2002-05-11 00:59:27 +00:00
|
|
|
convertersByName[name] = conv
|
|
|
|
return converters, convertersByName
|
|
|
|
|
|
|
|
|
2015-07-03 00:49:28 -07:00
|
|
|
class _MissingItem(tuple):
|
|
|
|
__slots__ = ()
|
|
|
|
|
|
|
|
try:
|
|
|
|
from collections import UserList
|
|
|
|
except:
|
|
|
|
from UserList import UserList
|
|
|
|
|
|
|
|
class _LazyList(UserList):
|
|
|
|
|
|
|
|
def __getslice__(self, i, j):
|
|
|
|
return self.__getitem__(slice(i, j))
|
|
|
|
def __getitem__(self, k):
|
|
|
|
if isinstance(k, slice):
|
|
|
|
indices = range(*k.indices(len(self)))
|
|
|
|
return [self[i] for i in indices]
|
|
|
|
item = self.data[k]
|
|
|
|
if isinstance(item, _MissingItem):
|
|
|
|
self.reader.seek(self.pos + item[0] * self.recordSize)
|
|
|
|
item = self.conv.read(self.reader, self.font, {})
|
|
|
|
self.data[k] = item
|
|
|
|
return item
|
|
|
|
|
2013-11-24 21:58:53 -05:00
|
|
|
class BaseConverter(object):
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2002-05-11 10:21:36 +00:00
|
|
|
"""Base class for converter objects. Apart from the constructor, this
|
|
|
|
is an abstract class."""
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2016-08-09 22:51:41 -07:00
|
|
|
def __init__(self, name, repeat, aux, tableClass=None):
|
2002-05-11 00:59:27 +00:00
|
|
|
self.name = name
|
|
|
|
self.repeat = repeat
|
2013-11-24 22:11:41 -05:00
|
|
|
self.aux = aux
|
2002-05-11 00:59:27 +00:00
|
|
|
self.tableClass = tableClass
|
|
|
|
self.isCount = name.endswith("Count")
|
2013-12-17 02:42:18 -05:00
|
|
|
self.isLookupType = name.endswith("LookupType")
|
2016-08-10 01:48:09 -07:00
|
|
|
self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag", "SettingsCount", "VarRegionCount", "MappingCount", "RegionAxisCount"]
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2015-07-01 22:54:12 -07:00
|
|
|
def readArray(self, reader, font, tableDict, count):
|
|
|
|
"""Read an array of values from the reader."""
|
2015-07-02 18:00:41 -07:00
|
|
|
lazy = font.lazy and count > 8
|
|
|
|
if lazy:
|
|
|
|
recordSize = self.getRecordSize(reader)
|
|
|
|
if recordSize is NotImplemented:
|
|
|
|
lazy = False
|
|
|
|
if not lazy:
|
|
|
|
l = []
|
|
|
|
for i in range(count):
|
|
|
|
l.append(self.read(reader, font, tableDict))
|
|
|
|
return l
|
|
|
|
else:
|
2015-07-03 00:49:28 -07:00
|
|
|
l = _LazyList()
|
2015-07-02 18:00:41 -07:00
|
|
|
l.reader = reader.copy()
|
|
|
|
l.pos = l.reader.pos
|
|
|
|
l.font = font
|
|
|
|
l.conv = self
|
|
|
|
l.recordSize = recordSize
|
2015-07-03 00:49:28 -07:00
|
|
|
l.extend(_MissingItem([i]) for i in range(count))
|
2015-07-03 00:53:15 -07:00
|
|
|
reader.advance(count * recordSize)
|
2015-07-02 18:00:41 -07:00
|
|
|
return l
|
|
|
|
|
|
|
|
def getRecordSize(self, reader):
|
|
|
|
if hasattr(self, 'staticSize'): return self.staticSize
|
|
|
|
return NotImplemented
|
2015-07-01 22:54:12 -07:00
|
|
|
|
2013-11-24 17:08:06 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2002-05-11 10:21:36 +00:00
|
|
|
"""Read a value from the reader."""
|
2013-11-27 02:42:28 -05:00
|
|
|
raise NotImplementedError(self)
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2015-07-01 22:54:12 -07:00
|
|
|
def writeArray(self, writer, font, tableDict, values):
|
2015-09-09 18:16:44 +01:00
|
|
|
for i, value in enumerate(values):
|
|
|
|
self.write(writer, font, tableDict, value, i)
|
2015-07-01 22:54:12 -07:00
|
|
|
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2002-05-11 10:21:36 +00:00
|
|
|
"""Write a value to the writer."""
|
2013-11-27 02:42:28 -05:00
|
|
|
raise NotImplementedError(self)
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2002-05-11 10:21:36 +00:00
|
|
|
def xmlRead(self, attrs, content, font):
|
|
|
|
"""Read a value from XML."""
|
2013-11-27 02:42:28 -05:00
|
|
|
raise NotImplementedError(self)
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2002-05-11 10:21:36 +00:00
|
|
|
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
|
|
|
"""Write a value to XML."""
|
2013-11-27 02:42:28 -05:00
|
|
|
raise NotImplementedError(self)
|
2002-05-11 00:59:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
class SimpleValue(BaseConverter):
|
|
|
|
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
|
|
|
xmlWriter.simpletag(name, attrs + [("value", value)])
|
|
|
|
xmlWriter.newline()
|
|
|
|
def xmlRead(self, attrs, content, font):
|
|
|
|
return attrs["value"]
|
|
|
|
|
|
|
|
class IntValue(SimpleValue):
|
|
|
|
def xmlRead(self, attrs, content, font):
|
2013-11-23 19:51:36 -05:00
|
|
|
return int(attrs["value"], 0)
|
2002-05-11 00:59:27 +00:00
|
|
|
|
|
|
|
class Long(IntValue):
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 4
|
2013-11-24 17:08:06 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2002-05-11 00:59:27 +00:00
|
|
|
return reader.readLong()
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2002-05-11 00:59:27 +00:00
|
|
|
writer.writeLong(value)
|
|
|
|
|
2014-09-30 18:55:57 -04:00
|
|
|
class ULong(IntValue):
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 4
|
2014-09-30 18:55:57 -04:00
|
|
|
def read(self, reader, font, tableDict):
|
|
|
|
return reader.readULong()
|
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
|
|
|
writer.writeULong(value)
|
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
class Short(IntValue):
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 2
|
2013-11-24 17:08:06 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2002-05-11 00:59:27 +00:00
|
|
|
return reader.readShort()
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2002-05-11 00:59:27 +00:00
|
|
|
writer.writeShort(value)
|
|
|
|
|
|
|
|
class UShort(IntValue):
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 2
|
2013-11-24 17:08:06 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2002-05-11 00:59:27 +00:00
|
|
|
return reader.readUShort()
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2002-05-11 00:59:27 +00:00
|
|
|
writer.writeUShort(value)
|
|
|
|
|
2016-06-06 22:01:09 -07:00
|
|
|
class Int8(IntValue):
|
|
|
|
staticSize = 1
|
|
|
|
def read(self, reader, font, tableDict):
|
|
|
|
return reader.readInt8()
|
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
|
|
|
writer.writeInt8(value)
|
|
|
|
|
2015-10-16 00:17:22 +02:00
|
|
|
class UInt8(IntValue):
|
|
|
|
staticSize = 1
|
|
|
|
def read(self, reader, font, tableDict):
|
|
|
|
return reader.readUInt8()
|
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
|
|
|
writer.writeUInt8(value)
|
|
|
|
|
2013-11-26 19:23:08 -05:00
|
|
|
class UInt24(IntValue):
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 3
|
2013-11-26 19:23:08 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
|
|
|
return reader.readUInt24()
|
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
|
|
|
writer.writeUInt24(value)
|
|
|
|
|
2013-12-17 02:57:29 -05:00
|
|
|
class ComputedUShort(UShort):
|
2002-05-11 00:59:27 +00:00
|
|
|
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
|
|
|
xmlWriter.comment("%s=%s" % (name, value))
|
|
|
|
xmlWriter.newline()
|
|
|
|
|
|
|
|
class Tag(SimpleValue):
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 4
|
2013-11-24 17:08:06 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2002-05-11 00:59:27 +00:00
|
|
|
return reader.readTag()
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2002-05-11 00:59:27 +00:00
|
|
|
writer.writeTag(value)
|
|
|
|
|
|
|
|
class GlyphID(SimpleValue):
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 2
|
2015-07-03 01:12:16 -07:00
|
|
|
def readArray(self, reader, font, tableDict, count):
|
|
|
|
glyphOrder = font.getGlyphOrder()
|
2015-10-17 06:47:52 +02:00
|
|
|
gids = reader.readUShortArray(count)
|
2015-07-03 01:12:16 -07:00
|
|
|
try:
|
|
|
|
l = [glyphOrder[gid] for gid in gids]
|
|
|
|
except IndexError:
|
|
|
|
# Slower, but will not throw an IndexError on an invalid glyph id.
|
|
|
|
l = [font.getGlyphName(gid) for gid in gids]
|
|
|
|
return l
|
2013-11-24 17:08:06 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2015-07-03 00:55:54 -07:00
|
|
|
return font.getGlyphName(reader.readUShort())
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2015-07-03 00:55:54 -07:00
|
|
|
writer.writeUShort(font.getGlyphID(value))
|
2002-05-11 00:59:27 +00:00
|
|
|
|
2016-06-30 16:39:20 -07:00
|
|
|
class VarAxisID(SimpleValue):
|
|
|
|
staticSize = 2
|
|
|
|
def read(self, reader, font, tableDict):
|
2016-07-01 15:31:00 -07:00
|
|
|
idx = reader.readUShort()
|
|
|
|
try:
|
|
|
|
return font['fvar'].axes[idx].axisTag
|
|
|
|
except (KeyError, IndexError):
|
|
|
|
return idx
|
2016-06-30 16:39:20 -07:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2016-07-01 15:31:00 -07:00
|
|
|
if not isinstance(value, int):
|
|
|
|
value = tobytes(value)
|
|
|
|
for i,axis in enumerate(font['fvar'].axes):
|
|
|
|
if axis.axisTag == value:
|
|
|
|
value = i
|
|
|
|
break
|
|
|
|
writer.writeUShort(value)
|
2016-06-30 16:39:20 -07:00
|
|
|
|
2013-11-26 19:42:55 -05:00
|
|
|
class FloatValue(SimpleValue):
|
|
|
|
def xmlRead(self, attrs, content, font):
|
|
|
|
return float(attrs["value"])
|
|
|
|
|
|
|
|
class DeciPoints(FloatValue):
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 2
|
2013-11-26 19:42:55 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2015-07-03 00:55:54 -07:00
|
|
|
return reader.readUShort() / 10
|
2013-11-26 19:42:55 -05:00
|
|
|
|
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
|
|
|
writer.writeUShort(int(round(value * 10)))
|
2002-05-13 18:10:05 +00:00
|
|
|
|
2015-03-11 15:29:35 -07:00
|
|
|
class Fixed(FloatValue):
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 4
|
2015-03-11 15:29:35 -07:00
|
|
|
def read(self, reader, font, tableDict):
|
2015-07-03 00:55:54 -07:00
|
|
|
return fi2fl(reader.readLong(), 16)
|
2015-03-11 15:29:35 -07:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2015-07-03 00:55:54 -07:00
|
|
|
writer.writeLong(fl2fi(value, 16))
|
2015-03-11 15:29:35 -07:00
|
|
|
|
2016-06-06 22:01:09 -07:00
|
|
|
class F2Dot14(FloatValue):
|
|
|
|
staticSize = 2
|
|
|
|
def read(self, reader, font, tableDict):
|
|
|
|
return fi2fl(reader.readShort(), 14)
|
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
|
|
|
writer.writeShort(fl2fi(value, 14))
|
|
|
|
|
2015-03-11 15:29:35 -07:00
|
|
|
class Version(BaseConverter):
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 4
|
2015-03-11 15:29:35 -07:00
|
|
|
def read(self, reader, font, tableDict):
|
|
|
|
value = reader.readLong()
|
|
|
|
assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
|
|
|
|
return fi2fl(value, 16)
|
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
|
|
|
if value < 0x10000:
|
|
|
|
value = fl2fi(value, 16)
|
|
|
|
value = int(round(value))
|
|
|
|
assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
|
|
|
|
writer.writeLong(value)
|
|
|
|
def xmlRead(self, attrs, content, font):
|
|
|
|
value = attrs["value"]
|
|
|
|
value = float(int(value, 0)) if value.startswith("0") else float(value)
|
|
|
|
if value >= 0x10000:
|
|
|
|
value = fi2fl(value, 16)
|
|
|
|
return value
|
|
|
|
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
|
|
|
if value >= 0x10000:
|
|
|
|
value = fi2fl(value, 16)
|
|
|
|
if value % 1 != 0:
|
|
|
|
# Write as hex
|
|
|
|
value = "0x%08x" % fl2fi(value, 16)
|
|
|
|
xmlWriter.simpletag(name, attrs + [("value", value)])
|
|
|
|
xmlWriter.newline()
|
|
|
|
|
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
class Struct(BaseConverter):
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2015-07-02 18:00:41 -07:00
|
|
|
def getRecordSize(self, reader):
|
|
|
|
return self.tableClass and self.tableClass.getRecordSize(reader)
|
|
|
|
|
2013-11-24 17:08:06 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2002-05-11 00:59:27 +00:00
|
|
|
table = self.tableClass()
|
2013-11-24 17:08:06 -05:00
|
|
|
table.decompile(reader, font)
|
2002-05-11 00:59:27 +00:00
|
|
|
return table
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
|
|
|
value.compile(writer, font)
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
|
|
|
if value is None:
|
2013-11-26 18:55:23 -05:00
|
|
|
if attrs:
|
|
|
|
# If there are attributes (probably index), then
|
|
|
|
# don't drop this even if it's NULL. It will mess
|
|
|
|
# up the array indices of the containing element.
|
2014-06-05 17:58:15 -04:00
|
|
|
xmlWriter.simpletag(name, attrs + [("empty", 1)])
|
2013-11-26 18:55:23 -05:00
|
|
|
xmlWriter.newline()
|
|
|
|
else:
|
|
|
|
pass # NULL table, ignore
|
2002-05-11 00:59:27 +00:00
|
|
|
else:
|
2013-12-09 00:39:25 -05:00
|
|
|
value.toXML(xmlWriter, font, attrs, name=name)
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
def xmlRead(self, attrs, content, font):
|
2014-06-05 17:58:15 -04:00
|
|
|
if "empty" in attrs and safeEval(attrs["empty"]):
|
2013-11-26 18:55:23 -05:00
|
|
|
return None
|
2014-06-05 17:58:15 -04:00
|
|
|
table = self.tableClass()
|
2002-05-11 00:59:27 +00:00
|
|
|
Format = attrs.get("Format")
|
|
|
|
if Format is not None:
|
|
|
|
table.Format = int(Format)
|
|
|
|
for element in content:
|
2013-11-27 05:17:37 -05:00
|
|
|
if isinstance(element, tuple):
|
2002-09-12 16:45:48 +00:00
|
|
|
name, attrs, content = element
|
2013-11-27 03:19:32 -05:00
|
|
|
table.fromXML(name, attrs, content, font)
|
2002-09-12 16:45:48 +00:00
|
|
|
else:
|
|
|
|
pass
|
2016-01-13 19:05:45 +00:00
|
|
|
# TODO Fill in items that are not set by XML.
|
2002-05-11 00:59:27 +00:00
|
|
|
return table
|
|
|
|
|
2015-07-01 23:02:40 -07:00
|
|
|
def __repr__(self):
|
|
|
|
return "Struct of " + repr(self.tableClass)
|
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
|
|
|
|
class Table(Struct):
|
2013-11-24 21:35:56 -05:00
|
|
|
|
2013-11-24 21:58:53 -05:00
|
|
|
longOffset = False
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 2
|
2013-11-24 21:58:53 -05:00
|
|
|
|
2013-11-24 21:35:56 -05:00
|
|
|
def readOffset(self, reader):
|
|
|
|
return reader.readUShort()
|
|
|
|
|
|
|
|
def writeNullOffset(self, writer):
|
2013-11-25 05:32:17 -05:00
|
|
|
if self.longOffset:
|
2013-11-24 21:35:56 -05:00
|
|
|
writer.writeULong(0)
|
|
|
|
else:
|
|
|
|
writer.writeUShort(0)
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2013-11-24 19:03:18 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2013-11-24 21:35:56 -05:00
|
|
|
offset = self.readOffset(reader)
|
2002-05-11 00:59:27 +00:00
|
|
|
if offset == 0:
|
|
|
|
return None
|
|
|
|
if offset <= 3:
|
|
|
|
# XXX hack to work around buggy pala.ttf
|
2016-01-24 14:43:54 +00:00
|
|
|
log.warning("offset is not 0, yet suspiciously low (%d). table: %s",
|
|
|
|
offset, self.tableClass.__name__)
|
2002-05-11 00:59:27 +00:00
|
|
|
return None
|
|
|
|
table = self.tableClass()
|
2013-12-17 00:58:02 -05:00
|
|
|
reader = reader.getSubReader(offset)
|
|
|
|
if font.lazy:
|
|
|
|
table.reader = reader
|
|
|
|
table.font = font
|
|
|
|
else:
|
|
|
|
table.decompile(reader, font)
|
2002-05-11 00:59:27 +00:00
|
|
|
return table
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2002-05-11 00:59:27 +00:00
|
|
|
if value is None:
|
2013-11-24 21:35:56 -05:00
|
|
|
self.writeNullOffset(writer)
|
2002-05-11 00:59:27 +00:00
|
|
|
else:
|
|
|
|
subWriter = writer.getSubWriter()
|
2013-11-25 05:32:17 -05:00
|
|
|
subWriter.longOffset = self.longOffset
|
2006-10-21 14:12:38 +00:00
|
|
|
subWriter.name = self.name
|
|
|
|
if repeatIndex is not None:
|
|
|
|
subWriter.repeatIndex = repeatIndex
|
2002-05-11 00:59:27 +00:00
|
|
|
writer.writeSubTable(subWriter)
|
2013-11-24 17:08:06 -05:00
|
|
|
value.compile(subWriter, font)
|
2002-05-11 00:59:27 +00:00
|
|
|
|
2013-11-24 21:58:53 -05:00
|
|
|
class LTable(Table):
|
|
|
|
|
|
|
|
longOffset = True
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 4
|
2013-11-24 21:58:53 -05:00
|
|
|
|
|
|
|
def readOffset(self, reader):
|
|
|
|
return reader.readULong()
|
|
|
|
|
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
class SubTable(Table):
|
|
|
|
def getConverter(self, tableType, lookupType):
|
2013-11-24 21:35:56 -05:00
|
|
|
tableClass = self.lookupTypes[tableType][lookupType]
|
2013-11-24 22:11:41 -05:00
|
|
|
return self.__class__(self.name, self.repeat, self.aux, tableClass)
|
2006-10-21 14:12:38 +00:00
|
|
|
|
2016-01-13 15:58:18 +00:00
|
|
|
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
|
|
|
Table.xmlWrite(self, xmlWriter, font, value, None, attrs)
|
|
|
|
|
2006-10-21 14:12:38 +00:00
|
|
|
|
2013-11-24 21:58:53 -05:00
|
|
|
class ExtSubTable(LTable, SubTable):
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2016-02-10 17:28:56 +07:00
|
|
|
writer.Extension = True # actually, mere presence of the field flags it as an Ext Subtable writer.
|
2013-11-24 21:35:56 -05:00
|
|
|
Table.write(self, writer, font, tableDict, value, repeatIndex)
|
2002-05-11 00:59:27 +00:00
|
|
|
|
2013-11-26 19:23:08 -05:00
|
|
|
class FeatureParams(Table):
|
|
|
|
def getConverter(self, featureTag):
|
|
|
|
tableClass = self.featureParamTypes.get(featureTag, self.defaultFeatureParams)
|
|
|
|
return self.__class__(self.name, self.repeat, self.aux, tableClass)
|
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
|
|
|
|
class ValueFormat(IntValue):
|
2015-07-02 18:00:41 -07:00
|
|
|
staticSize = 2
|
2016-08-09 22:51:41 -07:00
|
|
|
def __init__(self, name, repeat, aux, tableClass=None):
|
2013-11-24 22:11:41 -05:00
|
|
|
BaseConverter.__init__(self, name, repeat, aux, tableClass)
|
2013-11-26 17:07:37 -05:00
|
|
|
self.which = "ValueFormat" + ("2" if name[-1] == "2" else "1")
|
2013-11-24 17:08:06 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2002-05-11 00:59:27 +00:00
|
|
|
format = reader.readUShort()
|
2013-11-26 17:07:37 -05:00
|
|
|
reader[self.which] = ValueRecordFactory(format)
|
2002-05-11 00:59:27 +00:00
|
|
|
return format
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, format, repeatIndex=None):
|
2002-05-11 00:59:27 +00:00
|
|
|
writer.writeUShort(format)
|
2013-11-26 17:07:37 -05:00
|
|
|
writer[self.which] = ValueRecordFactory(format)
|
2002-05-11 00:59:27 +00:00
|
|
|
|
2002-05-13 18:10:05 +00:00
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
class ValueRecord(ValueFormat):
|
2015-07-02 18:00:41 -07:00
|
|
|
def getRecordSize(self, reader):
|
|
|
|
return 2 * len(reader[self.which])
|
2013-11-24 17:08:06 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2013-11-26 17:07:37 -05:00
|
|
|
return reader[self.which].readValueRecord(reader, font)
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2013-11-26 17:07:37 -05:00
|
|
|
writer[self.which].writeValueRecord(writer, font, value)
|
2002-05-11 00:59:27 +00:00
|
|
|
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
|
|
|
if value is None:
|
|
|
|
pass # NULL table, ignore
|
|
|
|
else:
|
|
|
|
value.toXML(xmlWriter, font, self.name, attrs)
|
|
|
|
def xmlRead(self, attrs, content, font):
|
2013-11-27 02:34:11 -05:00
|
|
|
from .otBase import ValueRecord
|
2002-05-11 00:59:27 +00:00
|
|
|
value = ValueRecord()
|
2013-11-27 03:19:32 -05:00
|
|
|
value.fromXML(None, attrs, content, font)
|
2002-05-11 00:59:27 +00:00
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
class DeltaValue(BaseConverter):
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2013-11-24 17:08:06 -05:00
|
|
|
def read(self, reader, font, tableDict):
|
2013-11-24 17:10:55 -05:00
|
|
|
StartSize = tableDict["StartSize"]
|
|
|
|
EndSize = tableDict["EndSize"]
|
|
|
|
DeltaFormat = tableDict["DeltaFormat"]
|
2002-05-11 00:59:27 +00:00
|
|
|
assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat"
|
|
|
|
nItems = EndSize - StartSize + 1
|
|
|
|
nBits = 1 << DeltaFormat
|
|
|
|
minusOffset = 1 << nBits
|
|
|
|
mask = (1 << nBits) - 1
|
|
|
|
signMask = 1 << (nBits - 1)
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
DeltaValue = []
|
|
|
|
tmp, shift = 0, 0
|
|
|
|
for i in range(nItems):
|
|
|
|
if shift == 0:
|
|
|
|
tmp, shift = reader.readUShort(), 16
|
|
|
|
shift = shift - nBits
|
|
|
|
value = (tmp >> shift) & mask
|
|
|
|
if value & signMask:
|
|
|
|
value = value - minusOffset
|
|
|
|
DeltaValue.append(value)
|
|
|
|
return DeltaValue
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2013-11-24 17:08:06 -05:00
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
2013-11-24 17:10:55 -05:00
|
|
|
StartSize = tableDict["StartSize"]
|
|
|
|
EndSize = tableDict["EndSize"]
|
|
|
|
DeltaFormat = tableDict["DeltaFormat"]
|
2013-11-24 16:09:57 -05:00
|
|
|
DeltaValue = value
|
2002-05-11 00:59:27 +00:00
|
|
|
assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat"
|
|
|
|
nItems = EndSize - StartSize + 1
|
|
|
|
nBits = 1 << DeltaFormat
|
|
|
|
assert len(DeltaValue) == nItems
|
|
|
|
mask = (1 << nBits) - 1
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
tmp, shift = 0, 16
|
|
|
|
for value in DeltaValue:
|
|
|
|
shift = shift - nBits
|
|
|
|
tmp = tmp | ((value & mask) << shift)
|
|
|
|
if shift == 0:
|
|
|
|
writer.writeUShort(tmp)
|
|
|
|
tmp, shift = 0, 16
|
2013-11-27 02:40:30 -05:00
|
|
|
if shift != 16:
|
2002-05-11 00:59:27 +00:00
|
|
|
writer.writeUShort(tmp)
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
|
|
|
xmlWriter.simpletag(name, attrs + [("value", value)])
|
|
|
|
xmlWriter.newline()
|
2015-04-26 02:01:01 -04:00
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
def xmlRead(self, attrs, content, font):
|
|
|
|
return safeEval(attrs["value"])
|
|
|
|
|
|
|
|
|
2016-08-10 01:17:45 -07:00
|
|
|
class VarIdxMapValue(BaseConverter):
|
|
|
|
|
|
|
|
def read(self, reader, font, tableDict):
|
|
|
|
fmt = tableDict['EntryFormat']
|
|
|
|
nItems = tableDict['MappingCount']
|
|
|
|
|
|
|
|
innerBits = 1 + (fmt & 0x000F)
|
|
|
|
innerMask = (1<<innerBits) - 1
|
|
|
|
outerMask = 0xFFFFFFFF - innerMask
|
|
|
|
outerShift = 16 - innerBits
|
|
|
|
|
|
|
|
entrySize = 1 + ((fmt & 0x0030) >> 4)
|
|
|
|
read = {
|
|
|
|
1: reader.readUInt8,
|
|
|
|
2: reader.readUShort,
|
|
|
|
3: reader.readUInt24,
|
|
|
|
4: reader.readULong,
|
|
|
|
}[entrySize]
|
|
|
|
|
|
|
|
mapping = []
|
|
|
|
for i in range(nItems):
|
|
|
|
raw = read()
|
|
|
|
idx = ((raw & outerMask) << outerShift) | (raw & innerMask)
|
|
|
|
mapping.append(idx)
|
|
|
|
|
|
|
|
return mapping
|
|
|
|
|
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
|
|
|
fmt = tableDict['EntryFormat']
|
|
|
|
mapping = value
|
|
|
|
writer['MappingCount'].setValue(len(mapping))
|
|
|
|
|
|
|
|
innerBits = 1 + (fmt & 0x000F)
|
|
|
|
innerMask = (1<<innerBits) - 1
|
|
|
|
outerShift = 16 - innerBits
|
|
|
|
|
|
|
|
entrySize = 1 + ((fmt & 0x0030) >> 4)
|
|
|
|
write = {
|
|
|
|
1: writer.writeUInt8,
|
|
|
|
2: writer.writeUShort,
|
|
|
|
3: writer.writeUInt24,
|
|
|
|
4: writer.writeULong,
|
|
|
|
}[entrySize]
|
|
|
|
|
|
|
|
for idx in mapping:
|
|
|
|
raw = ((idx & 0xFFFF0000) >> outerShift) | (idx & innerMask)
|
|
|
|
write(raw)
|
|
|
|
|
|
|
|
|
2016-08-10 03:15:38 -07:00
|
|
|
class VarDataValue(BaseConverter):
|
|
|
|
|
|
|
|
def read(self, reader, font, tableDict):
|
|
|
|
values = []
|
|
|
|
|
|
|
|
regionCount = tableDict["VarRegionCount"]
|
|
|
|
shortCount = tableDict["NumShorts"]
|
|
|
|
|
|
|
|
for i in range(min(regionCount, shortCount)):
|
|
|
|
values.append(reader.readShort())
|
|
|
|
for i in range(min(regionCount, shortCount), regionCount):
|
|
|
|
values.append(reader.readInt8())
|
|
|
|
for i in range(regionCount, shortCount):
|
|
|
|
reader.readInt8()
|
|
|
|
|
|
|
|
return values
|
|
|
|
|
|
|
|
def write(self, writer, font, tableDict, value, repeatIndex=None):
|
|
|
|
regionCount = tableDict["VarRegionCount"]
|
|
|
|
shortCount = tableDict["NumShorts"]
|
|
|
|
|
|
|
|
for i in range(min(regionCount, shortCount)):
|
|
|
|
writer.writeShort(value[i])
|
|
|
|
for i in range(min(regionCount, shortCount), regionCount):
|
|
|
|
writer.writeInt8(value[i])
|
|
|
|
for i in range(regionCount, shortCount):
|
|
|
|
writer.writeInt8(0)
|
|
|
|
|
|
|
|
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
|
|
|
xmlWriter.simpletag(name, attrs + [("value", value)])
|
|
|
|
xmlWriter.newline()
|
|
|
|
|
|
|
|
def xmlRead(self, attrs, content, font):
|
|
|
|
return safeEval(attrs["value"])
|
|
|
|
|
|
|
|
|
2002-05-11 00:59:27 +00:00
|
|
|
converterMapping = {
|
2015-04-26 00:54:30 -04:00
|
|
|
# type class
|
2016-06-06 22:01:09 -07:00
|
|
|
"int8": Int8,
|
2015-10-16 00:17:22 +02:00
|
|
|
"int16": Short,
|
|
|
|
"uint8": UInt8,
|
2016-06-06 22:01:09 -07:00
|
|
|
"uint8": UInt8,
|
2015-04-26 02:06:36 -04:00
|
|
|
"uint16": UShort,
|
|
|
|
"uint24": UInt24,
|
|
|
|
"uint32": ULong,
|
|
|
|
"Version": Version,
|
|
|
|
"Tag": Tag,
|
|
|
|
"GlyphID": GlyphID,
|
|
|
|
"DeciPoints": DeciPoints,
|
|
|
|
"Fixed": Fixed,
|
2016-07-01 15:31:00 -07:00
|
|
|
"F2Dot14": F2Dot14,
|
2015-04-26 02:06:36 -04:00
|
|
|
"struct": Struct,
|
|
|
|
"Offset": Table,
|
|
|
|
"LOffset": LTable,
|
|
|
|
"ValueRecord": ValueRecord,
|
2016-07-01 15:31:00 -07:00
|
|
|
"VarAxisID": VarAxisID,
|
2015-04-26 02:06:36 -04:00
|
|
|
"DeltaValue": DeltaValue,
|
2016-08-10 01:17:45 -07:00
|
|
|
"VarIdxMapValue": VarIdxMapValue,
|
2016-08-10 03:15:38 -07:00
|
|
|
"VarDataValue": VarDataValue,
|
2016-08-09 22:51:41 -07:00
|
|
|
# "Template" types
|
|
|
|
"OffsetTo": lambda C: partial(Table, tableClass=C),
|
|
|
|
"LOffsetTo": lambda C: partial(LTable, tableClass=C),
|
2002-05-11 00:59:27 +00:00
|
|
|
}
|