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
|
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:
|
2013-12-09 00:39:25 -05:00
|
|
|
if not tp in converterMapping:
|
|
|
|
tableName = tp
|
|
|
|
converterClass = Struct
|
|
|
|
else:
|
|
|
|
converterClass = converterMapping[tp]
|
|
|
|
tableClass = tableNamespace.get(tableName)
|
2013-11-24 22:11:41 -05:00
|
|
|
conv = converterClass(name, repeat, aux, tableClass)
|
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
|
|
|
|
2013-11-24 22:11:41 -05:00
|
|
|
def __init__(self, name, repeat, aux, tableClass):
|
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")
|
2015-03-11 15:29:35 -07:00
|
|
|
self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag", "SettingsCount", "AxisCount"]
|
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):
|
|
|
|
for i in range(len(values)):
|
|
|
|
self.write(writer, font, tableDict, values[i], i)
|
|
|
|
|
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)
|
|
|
|
|
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
|
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
|
|
|
|
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
|
|
|
|
|
|
|
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
|
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
|
2013-11-27 04:57:33 -05:00
|
|
|
print("*** Warning: offset is not 0, yet suspiciously low (%s). 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
|
|
|
|
|
|
|
|
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):
|
2006-10-21 14:12:38 +00:00
|
|
|
writer.Extension = 1 # 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
|
2013-11-24 22:11:41 -05:00
|
|
|
def __init__(self, name, repeat, aux, tableClass):
|
|
|
|
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
|
|
|
|
2015-07-02 18:00:41 -07:00
|
|
|
def getRecordSize(self, reader):
|
|
|
|
# We can implement this but would need to wire up tableDict.
|
|
|
|
# This object is never used in arrays, so there's no point
|
|
|
|
# in implementing this.
|
|
|
|
return NotImplemented
|
|
|
|
|
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"])
|
|
|
|
|
|
|
|
|
|
|
|
converterMapping = {
|
2015-04-26 00:54:30 -04:00
|
|
|
# type class
|
2015-04-26 02:06:36 -04:00
|
|
|
"int16": Short,
|
|
|
|
"uint16": UShort,
|
|
|
|
"uint24": UInt24,
|
|
|
|
"uint32": ULong,
|
|
|
|
"Version": Version,
|
|
|
|
"Tag": Tag,
|
|
|
|
"GlyphID": GlyphID,
|
|
|
|
"DeciPoints": DeciPoints,
|
|
|
|
"Fixed": Fixed,
|
|
|
|
"struct": Struct,
|
|
|
|
"Offset": Table,
|
|
|
|
"LOffset": LTable,
|
|
|
|
"ValueRecord": ValueRecord,
|
|
|
|
"DeltaValue": DeltaValue,
|
2002-05-11 00:59:27 +00:00
|
|
|
}
|