results of morning-after code review, added some doc strings, etc.
git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@210 4cde692c-a291-49d1-8350-778aa11640f8
This commit is contained in:
parent
3f776b83cb
commit
64b5c80e80
@ -6,6 +6,10 @@ from types import TupleType
|
||||
|
||||
class BaseTTXConverter(DefaultTable):
|
||||
|
||||
"""Generic base class for TTX table converters. Functions as an adapter
|
||||
between the TTX (ttLib actually) table model and the model we use for
|
||||
OpenType tables, which is neccesarily subtly different."""
|
||||
|
||||
def decompile(self, data, font):
|
||||
import otTables
|
||||
reader = OTTableReader(data, self.tableTag)
|
||||
@ -31,6 +35,8 @@ class BaseTTXConverter(DefaultTable):
|
||||
|
||||
class OTTableReader:
|
||||
|
||||
"""Helper class to retrieve data from an OpenType table."""
|
||||
|
||||
def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None):
|
||||
self.data = data
|
||||
self.offset = offset
|
||||
@ -102,6 +108,8 @@ class OTTableReader:
|
||||
|
||||
class OTTableWriter:
|
||||
|
||||
"""Helper class to gather and assemble data for OpenType tables."""
|
||||
|
||||
def __init__(self, tableType, valueFormat=None):
|
||||
self.items = []
|
||||
self.tableType = tableType
|
||||
@ -169,6 +177,7 @@ class OTTableWriter:
|
||||
|
||||
|
||||
class CountReference:
|
||||
"""A reference to a Count value, not a count of a reference."""
|
||||
def __init__(self, table, name):
|
||||
self.table = table
|
||||
self.name = name
|
||||
@ -176,14 +185,42 @@ class CountReference:
|
||||
return packUShort(self.table[self.name])
|
||||
|
||||
|
||||
def packUShort(offset):
|
||||
assert 0 <= offset < 0x10000
|
||||
return struct.pack(">H", offset)
|
||||
def packUShort(value):
|
||||
assert 0 <= value < 0x10000
|
||||
return struct.pack(">H", value)
|
||||
|
||||
|
||||
|
||||
class TableStack:
|
||||
"""A stack of table dicts, working as a stack of namespaces so we can
|
||||
retrieve values from (and store values to) tables higher up the stack."""
|
||||
def __init__(self):
|
||||
self.stack = []
|
||||
def push(self, table):
|
||||
self.stack.insert(0, table)
|
||||
def pop(self):
|
||||
self.stack.pop(0)
|
||||
def getTop(self):
|
||||
return self.stack[0]
|
||||
def getValue(self, name):
|
||||
return self.__findTable(name)[name]
|
||||
def storeValue(self, name, value):
|
||||
table = self.__findTable(name)
|
||||
if table[name] is None:
|
||||
table[name] = value
|
||||
else:
|
||||
assert table[name] == value, (table[name], value)
|
||||
def __findTable(self, name):
|
||||
for table in self.stack:
|
||||
if table.has_key(name):
|
||||
return table
|
||||
raise KeyError, name
|
||||
|
||||
|
||||
class BaseTable:
|
||||
|
||||
"""Generic base class for all OpenType (sub)tables."""
|
||||
|
||||
def getConverters(self):
|
||||
return self.converters
|
||||
|
||||
@ -220,7 +257,7 @@ class BaseTable:
|
||||
value = table.get(conv.name)
|
||||
if conv.repeat:
|
||||
if value is None:
|
||||
value = [] # XXXXXX
|
||||
value = []
|
||||
tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset)
|
||||
for item in value:
|
||||
conv.write(writer, font, tableStack, item)
|
||||
@ -249,7 +286,7 @@ class BaseTable:
|
||||
if attrs is None:
|
||||
attrs = []
|
||||
if hasattr(self, "Format"):
|
||||
attrs = attrs + [("Format", str(self.Format))]
|
||||
attrs = attrs + [("Format", self.Format)]
|
||||
xmlWriter.begintag(tableName, attrs)
|
||||
xmlWriter.newline()
|
||||
self.toXML2(xmlWriter, font)
|
||||
@ -262,30 +299,30 @@ class BaseTable:
|
||||
# do it ourselves. I think I'm getting schizophrenic...
|
||||
for conv in self.getConverters():
|
||||
value = getattr(self, conv.name)
|
||||
if not conv.repeat:
|
||||
conv.xmlWrite(xmlWriter, font, value, conv.name, [])
|
||||
else:
|
||||
if conv.repeat:
|
||||
for i in range(len(value)):
|
||||
item = value[i]
|
||||
conv.xmlWrite(xmlWriter, font, item, conv.name, [("index", i)])
|
||||
conv.xmlWrite(xmlWriter, font, item, conv.name,
|
||||
[("index", i)])
|
||||
else:
|
||||
conv.xmlWrite(xmlWriter, font, value, conv.name, [])
|
||||
|
||||
def fromXML(self, (name, attrs, content), font):
|
||||
try:
|
||||
conv = self.getConverterByName(name)
|
||||
except KeyError:
|
||||
print self, name, attrs, content
|
||||
## print self, name, attrs, content
|
||||
raise # XXX on KeyError, raise nice error
|
||||
value = conv.xmlRead(attrs, content, font)
|
||||
name = conv.name
|
||||
if conv.repeat:
|
||||
try:
|
||||
seq = getattr(self, name)
|
||||
seq = getattr(self, conv.name)
|
||||
except AttributeError:
|
||||
seq = []
|
||||
setattr(self, name, seq)
|
||||
setattr(self, conv.name, seq)
|
||||
seq.append(value)
|
||||
else:
|
||||
setattr(self, name, value)
|
||||
setattr(self, conv.name, value)
|
||||
|
||||
def __cmp__(self, other):
|
||||
# this is only for debugging, so it's ok to barf
|
||||
@ -300,6 +337,9 @@ class BaseTable:
|
||||
|
||||
class FormatSwitchingBaseTable(BaseTable):
|
||||
|
||||
"""Small specialization of BaseTable, for tables that have multiple
|
||||
formats, eg. CoverageFormat1 vs. CoverageFormat2."""
|
||||
|
||||
def getConverters(self):
|
||||
return self.converters[self.Format]
|
||||
|
||||
@ -316,6 +356,14 @@ class FormatSwitchingBaseTable(BaseTable):
|
||||
BaseTable.compile(self, writer, font, tableStack)
|
||||
|
||||
|
||||
#
|
||||
# Support for ValueRecords
|
||||
#
|
||||
# This data type is so different from all other OpenType data types that
|
||||
# it requires quite a bit of code for itself. It even has special support
|
||||
# in OTTableReader and OTTableWriter...
|
||||
#
|
||||
|
||||
valueRecordFormat = [
|
||||
# Mask Name isDevice signed
|
||||
(0x0001, "XPlacement", 0, 1),
|
||||
@ -348,6 +396,8 @@ valueRecordFormatDict = _buildDict()
|
||||
|
||||
class ValueRecordFactory:
|
||||
|
||||
"""Given a format code, this object convert ValueRecords."""
|
||||
|
||||
def setFormat(self, valueFormat):
|
||||
format = []
|
||||
for mask, name, isDevice, signed in valueRecordFormat:
|
||||
@ -453,27 +503,3 @@ class ValueRecord:
|
||||
else:
|
||||
return rv
|
||||
|
||||
|
||||
class TableStack:
|
||||
def __init__(self):
|
||||
self.stack = []
|
||||
def push(self, table):
|
||||
self.stack.insert(0, table)
|
||||
def pop(self):
|
||||
self.stack.pop(0)
|
||||
def getTop(self):
|
||||
return self.stack[0]
|
||||
def getValue(self, name):
|
||||
return self.__findTable(name)[name]
|
||||
def storeValue(self, name, value):
|
||||
table = self.__findTable(name)
|
||||
if table[name] is None:
|
||||
table[name] = value
|
||||
else:
|
||||
assert table[name] == value, (table[name], value)
|
||||
def __findTable(self, name):
|
||||
for table in self.stack:
|
||||
if table.has_key(name):
|
||||
return table
|
||||
raise KeyError, name
|
||||
|
||||
|
@ -2,7 +2,10 @@ from types import TupleType
|
||||
from fontTools.misc.textTools import safeEval
|
||||
|
||||
|
||||
def buildConverterList(tableSpec, tableNamespace):
|
||||
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."""
|
||||
converters = []
|
||||
convertersByName = {}
|
||||
for tp, name, repeat, repeatOffset, descr in tableSpec:
|
||||
@ -35,6 +38,9 @@ def buildConverterList(tableSpec, tableNamespace):
|
||||
|
||||
class BaseConverter:
|
||||
|
||||
"""Base class for converter objects. Apart from the constructor, this
|
||||
is an abstract class."""
|
||||
|
||||
def __init__(self, name, repeat, repeatOffset, tableClass):
|
||||
self.name = name
|
||||
self.repeat = repeat
|
||||
@ -43,15 +49,19 @@ class BaseConverter:
|
||||
self.isCount = name.endswith("Count")
|
||||
|
||||
def read(self, reader, font, tableStack):
|
||||
"""Read a value from the reader."""
|
||||
raise NotImplementedError, self
|
||||
|
||||
def write(self, writer, font, tableStack, value):
|
||||
raise NotImplementedError, self
|
||||
|
||||
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
||||
"""Write a value to the writer."""
|
||||
raise NotImplementedError, self
|
||||
|
||||
def xmlRead(self, attrs, content, font):
|
||||
"""Read a value from XML."""
|
||||
raise NotImplementedError, self
|
||||
|
||||
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
||||
"""Write a value to XML."""
|
||||
raise NotImplementedError, self
|
||||
|
||||
|
||||
@ -240,6 +250,7 @@ class DeltaValue(BaseConverter):
|
||||
writer.writeUShort(tmp)
|
||||
|
||||
def xmlWrite(self, xmlWriter, font, value, name, attrs):
|
||||
# XXX this could do with a nicer format
|
||||
xmlWriter.simpletag(name, attrs + [("value", value)])
|
||||
xmlWriter.newline()
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
"""fontTools.ttLib.tables.otTables -- A collection of classes representing the various
|
||||
OpenType subtables.
|
||||
|
||||
Most are constructed upon import from data in otData.py. Most smartness is contained
|
||||
in otBase.BaseTable.
|
||||
Most are constructed upon import from data in otData.py, all are populated with
|
||||
converter objects from otConverters.py.
|
||||
"""
|
||||
|
||||
from otBase import BaseTable, FormatSwitchingBaseTable
|
||||
@ -15,28 +15,35 @@ class FeatureParams(BaseTable):
|
||||
"""Dummy class; this table isn't defined, but is used, and is always NULL."""
|
||||
|
||||
|
||||
_equivalents = [
|
||||
('MarkArray', ("Mark1Array",)),
|
||||
('LangSys', ('DefaultLangSys',)),
|
||||
('Coverage', ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage',
|
||||
#
|
||||
# For each subtable format there is a class. However, we don't really distinguish
|
||||
# between "field name" and "format name": often these are the same. Yet there's
|
||||
# a whole bunch of fields with different names. The following dict is a mapping
|
||||
# from "format name" to "field name". _buildClasses() uses this to create a
|
||||
# subclass for each alternate field name.
|
||||
#
|
||||
_equivalents = {
|
||||
'MarkArray': ("Mark1Array",),
|
||||
'LangSys': ('DefaultLangSys',),
|
||||
'Coverage': ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage',
|
||||
'Mark2Coverage', 'BacktrackCoverage', 'InputCoverage',
|
||||
'LookaheadCoverage')),
|
||||
('ClassDef', ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef',
|
||||
'LookaheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef')),
|
||||
('Anchor', ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor',
|
||||
'Mark2Anchor', 'MarkAnchor')),
|
||||
('Device', ('XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice',
|
||||
'XDeviceTable', 'YDeviceTable', 'DeviceTable')),
|
||||
('Axis', ('HorizAxis', 'VertAxis',)),
|
||||
('MinMax', ('DefaultMinMax',)),
|
||||
('BaseCoord', ('MinCoord', 'MaxCoord',)),
|
||||
('JstfLangSys', ('DefJstfLangSys',)),
|
||||
('JstfGSUBModList', ('ShrinkageEnableGSUB', 'ShrinkageDisableGSUB', 'ExtensionEnableGSUB',
|
||||
'ExtensionDisableGSUB',)),
|
||||
('JstfGPOSModList', ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS',
|
||||
'ExtensionDisableGPOS',)),
|
||||
('JstfMax', ('ShrinkageJstfMax', 'ExtensionJstfMax',)),
|
||||
]
|
||||
'LookaheadCoverage'),
|
||||
'ClassDef': ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef',
|
||||
'LookaheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef'),
|
||||
'Anchor': ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor',
|
||||
'Mark2Anchor', 'MarkAnchor'),
|
||||
'Device': ('XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice',
|
||||
'XDeviceTable', 'YDeviceTable', 'DeviceTable'),
|
||||
'Axis': ('HorizAxis', 'VertAxis',),
|
||||
'MinMax': ('DefaultMinMax',),
|
||||
'BaseCoord': ('MinCoord', 'MaxCoord',),
|
||||
'JstfLangSys': ('DefJstfLangSys',),
|
||||
'JstfGSUBModList': ('ShrinkageEnableGSUB', 'ShrinkageDisableGSUB', 'ExtensionEnableGSUB',
|
||||
'ExtensionDisableGSUB',),
|
||||
'JstfGPOSModList': ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS',
|
||||
'ExtensionDisableGPOS',),
|
||||
'JstfMax': ('ShrinkageJstfMax', 'ExtensionJstfMax',),
|
||||
}
|
||||
|
||||
|
||||
def _buildClasses():
|
||||
@ -55,10 +62,11 @@ def _buildClasses():
|
||||
name = m.group(1)
|
||||
baseClass = FormatSwitchingBaseTable
|
||||
if not namespace.has_key(name):
|
||||
# the class doesn't exist yet, so the base implementation is used.
|
||||
cls = new.classobj(name, (baseClass,), {})
|
||||
namespace[name] = cls
|
||||
|
||||
for base, alts in _equivalents:
|
||||
for base, alts in _equivalents.items():
|
||||
base = namespace[base]
|
||||
for alt in alts:
|
||||
namespace[alt] = new.classobj(alt, (base,), {})
|
||||
@ -87,9 +95,12 @@ def _buildClasses():
|
||||
},
|
||||
}
|
||||
lookupTypes['JSTF'] = lookupTypes['GPOS'] # JSTF contains GPOS
|
||||
for lookupEnum in lookupTypes.values():
|
||||
for enum, cls in lookupEnum.items():
|
||||
cls.LookupType = enum
|
||||
|
||||
# add converters to classes
|
||||
from otConverters import buildConverterList
|
||||
from otConverters import buildConverters
|
||||
for name, table in otData:
|
||||
m = formatPat.match(name)
|
||||
if m:
|
||||
@ -100,12 +111,12 @@ def _buildClasses():
|
||||
if not hasattr(cls, "converters"):
|
||||
cls.converters = {}
|
||||
cls.convertersByName = {}
|
||||
converters, convertersByName = buildConverterList(table[1:], namespace)
|
||||
converters, convertersByName = buildConverters(table[1:], namespace)
|
||||
cls.converters[format] = converters
|
||||
cls.convertersByName[format] = convertersByName
|
||||
else:
|
||||
cls = namespace[name]
|
||||
cls.converters, cls.convertersByName = buildConverterList(table, namespace)
|
||||
cls.converters, cls.convertersByName = buildConverters(table, namespace)
|
||||
|
||||
|
||||
_buildClasses()
|
||||
|
Loading…
x
Reference in New Issue
Block a user