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:
jvr 2002-05-11 10:21:36 +00:00
parent 3f776b83cb
commit 64b5c80e80
3 changed files with 117 additions and 69 deletions

View File

@ -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

View File

@ -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()

View File

@ -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()