Created a new library directory called "FreeLib". All OpenSource RFMKII components will reside there, fontTools being the flagship.


git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@2 4cde692c-a291-49d1-8350-778aa11640f8
diff --git a/Lib/fontTools/ttLib/tables/C_F_F_.py b/Lib/fontTools/ttLib/tables/C_F_F_.py
new file mode 100644
index 0000000..8148f06
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/C_F_F_.py
@@ -0,0 +1,35 @@
+import DefaultTable
+from fontTools import cffLib
+
+
+class table_C_F_F_(DefaultTable.DefaultTable, cffLib.CFFFontSet):
+	
+	def __init__(self, tag):
+		DefaultTable.DefaultTable.__init__(self, tag)
+		cffLib.CFFFontSet.__init__(self)
+		self._gaveGlyphOrder = 0
+	
+	def decompile(self, data, otFont):
+		self.data = data  # XXX while work is in progress...
+		cffLib.CFFFontSet.decompile(self, data)
+		assert len(self.fonts) == 1, "can't deal with multi-font CFF tables."
+	
+	#def compile(self, otFont):
+	#	xxx
+	
+	def getGlyphOrder(self):
+		if self._gaveGlyphOrder:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "illegal use of getGlyphOrder()"
+		self._gaveGlyphOrder = 1
+		return self.fonts[self.fontNames[0]].getGlyphOrder()
+	
+	def setGlyphOrder(self, glyphOrder):
+		self.fonts[self.fontNames[0]].setGlyphOrder(glyphOrder)
+	
+	def toXML(self, writer, otFont, progress=None):
+		cffLib.CFFFontSet.toXML(self, writer, progress)
+	
+	#def fromXML(self, (name, attrs, content), otFont):
+	#	xxx
+
diff --git a/Lib/fontTools/ttLib/tables/D_S_I_G_.py b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
new file mode 100644
index 0000000..bea5654
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
@@ -0,0 +1,12 @@
+import DefaultTable
+
+class table_D_S_I_G_(DefaultTable.DefaultTable):
+	
+	def toXML(self, xmlWriter, ttFont):
+		xmlWriter.comment("note that the Digital Signature will be invalid after recompilation!")
+		xmlWriter.newline()
+		xmlWriter.begintag("hexdata")
+		xmlWriter.newline()
+		xmlWriter.dumphex(self.compile(ttFont))
+		xmlWriter.endtag("hexdata")
+		xmlWriter.newline()
diff --git a/Lib/fontTools/ttLib/tables/DefaultTable.py b/Lib/fontTools/ttLib/tables/DefaultTable.py
new file mode 100644
index 0000000..745f237
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/DefaultTable.py
@@ -0,0 +1,36 @@
+import string
+import sys
+
+class DefaultTable:
+	
+	dependencies = []
+	
+	def __init__(self, tag):
+		self.tableTag = tag
+	
+	def decompile(self, data, ttFont):
+		self.data = data
+	
+	def compile(self, ttFont):
+		return self.data
+	
+	def toXML(self, writer, ttFont):
+		writer.begintag("hexdata")
+		writer.newline()
+		writer.dumphex(self.compile(ttFont))
+		writer.endtag("hexdata")
+		writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		from fontTools.misc.textTools import readHex
+		from fontTools import ttLib
+		if name <> "hexdata":
+			raise ttLib.TTLibError, "can't handle '%s' element" % name
+		self.decompile(readHex(content), ttFont)
+	
+	def __repr__(self):
+		return "<'%s' table at %x>" % (self.tableTag, id(self))
+	
+	def __cmp__(self, other):
+		return cmp(self.__dict__, other.__dict__)
+
diff --git a/Lib/fontTools/ttLib/tables/G_P_O_S_.py b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
new file mode 100644
index 0000000..7bec887
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
@@ -0,0 +1,294 @@
+import otCommon
+
+
+class table_G_P_O_S_(otCommon.base_GPOS_GSUB):
+	
+	def getLookupTypeClass(self, lookupType):
+		return lookupTypeClasses[lookupType]
+
+
+class SinglePos:
+	
+	def decompile(self, reader, otFont):
+		pass
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.comment("XXX")
+		xmlWriter.newline()
+			
+	def fromXML(self, (name, attrs, content), otFont):
+		xxx
+
+
+class PairPos:
+	
+	def decompile(self, reader, otFont):
+		self.format = reader.readUShort()
+		if self.format == 1:
+			self.decompileFormat1(reader, otFont)
+		elif self.format == 2:
+			self.decompileFormat2(reader, otFont)
+		else:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "unknown PairPos format: %d" % self.format
+	
+	def decompileFormat1(self, reader, otFont):
+		coverage = reader.readTable(otCommon.CoverageTable, otFont)
+		glyphNames = coverage.glyphNames
+		valueFactory1 = ValueRecordFactory(reader.readUShort())
+		valueFactory2 = ValueRecordFactory(reader.readUShort())
+		self.pairs = pairs = {}
+		for i in range(reader.readUShort()):
+			firstGlyphName = glyphNames[i]
+			offset = reader.readOffset()
+			setData = reader.getSubString(offset)
+			set = PairSet()
+			set.decompile(setData, otFont, valueFactory1, valueFactory2)
+			pairs[firstGlyphName] = set.values
+	
+	def decompileFormat2(self, reader, otFont):
+		coverage = reader.readTable(otCommon.CoverageTable, otFont)
+		glyphNames = coverage.glyphNames
+		valueFactory1 = ValueRecordFactory(reader.readUShort())
+		valueFactory2 = ValueRecordFactory(reader.readUShort())
+		self.classDef1 = reader.readTable(otCommon.ClassDefinitionTable, otFont)
+		self.classDef2 = reader.readTable(otCommon.ClassDefinitionTable, otFont)
+		class1Count = reader.readUShort()
+		class2Count = reader.readUShort()
+		self.pairs = pairs = {}  # sparse matrix
+		for i in range(class1Count):
+			row = {}
+			for j in range(class2Count):
+				value1 = valueFactory1.getValueRecord(reader)
+				value2 = valueFactory2.getValueRecord(reader)
+				if value1 or value2:
+					row[j] = (value1, value2)
+			if row:
+				pairs[i] = row
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		if self.format == 1:
+			self.toXMLFormat1(xmlWriter, otFont)
+		elif self.format == 2:
+			self.toXMLFormat2(xmlWriter, otFont)
+		else:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "unknown PairPos format: %d" % self.format
+	
+	def toXMLFormat1(self, xmlWriter, otFont):
+		pairs = self.pairs.items()
+		pairs.sort()
+		for firstGlyph, secondGlyphs in pairs:
+			for secondGlyph, value1, value2 in secondGlyphs:
+				#XXXXXXXXX
+				xmlWriter.begintag("Pair", first=firstGlyph, second=secondGlyph)
+				xmlWriter.newline()
+				if value1:
+					value1.toXML(xmlWriter, otFont)
+				if value2:
+					value2.toXML(xmlWriter, otFont)
+				xmlWriter.endtag("Pair")
+				xmlWriter.newline()
+	
+	def toXMLFormat2(self, xmlWriter, otFont):
+		xmlWriter.comment("XXX")
+		xmlWriter.newline()
+	
+	def fromXML(self, (name, attrs, content), otFont):
+		xxx
+
+
+class PairSet:
+	
+	def decompile(self, reader, otFont, valueFactory1, valueFactory2):
+		pairValueCount = reader.readUShort()
+		self.values = values = []
+		for j in range(pairValueCount):
+			secondGlyphID = reader.readUShort()
+			secondGlyphName = otFont.getGlyphName(secondGlyphID)
+			value1 = valueFactory1.getValueRecord(reader)
+			value2 = valueFactory2.getValueRecord(reader)
+			values.append((secondGlyphName, value1, value2))
+	
+	def compile(self, otFont):
+		xxx
+
+#
+# ------------------
+#
+
+class CursivePos:
+	
+	def decompile(self, reader, otFont):
+		pass
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.comment("XXX")
+		xmlWriter.newline()
+
+
+class MarkBasePos:
+	
+	def decompile(self, reader, otFont):
+		pass
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.comment("XXX")
+		xmlWriter.newline()
+
+
+class MarkLigPos:
+	
+	def decompile(self, reader, otFont):
+		pass
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.comment("XXX")
+		xmlWriter.newline()
+
+
+class MarkMarkPos:
+	
+	def decompile(self, reader, otFont):
+		pass
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.comment("XXX")
+		xmlWriter.newline()
+
+
+class ContextPos:
+	
+	def decompile(self, reader, otFont):
+		pass
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.comment("XXX")
+		xmlWriter.newline()
+
+
+class ChainContextPos:
+	
+	def decompile(self, reader, otFont):
+		pass
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.comment("XXX")
+		xmlWriter.newline()
+
+
+lookupTypeClasses = {
+	1: SinglePos,
+	2: PairPos,
+	3: CursivePos,
+	4: MarkBasePos,
+	5: MarkLigPos,
+	6: MarkMarkPos,
+	7: ContextPos,
+	8: ChainContextPos,
+}
+
+
+valueRecordFormat = [
+#	Mask	 Name		     struct format char
+	(0x0001, "XPlacement",   "h"),
+	(0x0002, "YPlacement",   "h"),
+	(0x0004, "XAdvance",     "h"),
+	(0x0008, "YAdvance",     "h"),
+	(0x0010, "XPlaDevice",   "H"),
+	(0x0020, "YPlaDevice",   "H"),
+	(0x0040, "XAdvDevice",   "H"),
+	(0x0080, "YAdvDevice",   "H"),
+# 	reserved:
+	(0x0100, "Reserved1",    "H"),
+	(0x0200, "Reserved2",    "H"),
+	(0x0400, "Reserved3",    "H"),
+	(0x0800, "Reserved4",    "H"),
+	(0x1000, "Reserved5",    "H"),
+	(0x2000, "Reserved6",    "H"),
+	(0x4000, "Reserved7",    "H"),
+	(0x8000, "Reserved8",    "H"),
+]
+
+
+class ValueRecordFactory:
+	
+	def __init__(self, valueFormat):
+		format = ">"
+		names = []
+		for mask, name, formatChar in valueRecordFormat:
+			if valueFormat & mask:
+				names.append(name)
+				format = format + formatChar
+		self.names, self.format = names, format
+		self.size = 2 * len(names)
+	
+	def getValueRecord(self, reader):
+		names = self.names
+		if not names:
+			return None
+		values = reader.readStruct(self.format, self.size)
+		values = map(int, values)
+		valueRecord = ValueRecord()
+		items = map(None, names, values)
+		for name, value in items:
+			setattr(valueRecord, name, value)
+		return valueRecord
+
+
+class ValueRecord:
+	# see ValueRecordFactory
+	
+	def __nonzero__(self):
+		for value in self.__dict__.values():
+			if value:
+				return 1
+		return 0
+			
+	def toXML(self, xmlWriter, otFont):
+		simpleItems = []
+		for mask, name, format in valueRecordFormat[:4]:  # "simple" values
+			if hasattr(self, name):
+				simpleItems.append((name, getattr(self, name)))
+		deviceItems = []
+		for mask, name, format in valueRecordFormat[4:8]:  # device records
+			if hasattr(self, name):
+				deviceItems.append((name, getattr(self, name)))
+		if deviceItems:
+			xmlWriter.begintag("ValueRecord", simpleItems)
+			xmlWriter.newline()
+			for name, deviceRecord in deviceItems:
+				xxx
+			xmlWriter.endtag("ValueRecord")
+			xmlWriter.newline()
+		else:
+			xmlWriter.simpletag("ValueRecord", simpleItems)
+			xmlWriter.newline()
+	
+	def __repr__(self):
+		return "<ValueRecord>"
+
diff --git a/Lib/fontTools/ttLib/tables/G_S_U_B_.py b/Lib/fontTools/ttLib/tables/G_S_U_B_.py
new file mode 100644
index 0000000..d0c4dce
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/G_S_U_B_.py
@@ -0,0 +1,283 @@
+import otCommon
+
+
+class table_G_S_U_B_(otCommon.base_GPOS_GSUB):
+	
+	def getLookupTypeClass(self, lookupType):
+		return lookupTypeClasses[lookupType]
+
+
+class SingleSubst:
+	
+	def decompile(self, reader, otFont):
+		self.format = reader.readUShort()
+		if self.format == 1:
+			self.decompileFormat1(reader, otFont)
+		elif self.format == 2:
+			self.decompileFormat2(reader, otFont)
+		else:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "unknown SingleSub format: %d" % self.format
+	
+	def decompileFormat1(self, reader, otFont):
+		coverage = reader.readTable(otCommon.CoverageTable, otFont)
+		glyphIDs = coverage.getGlyphIDs()
+		glyphNames = coverage.getGlyphNames()
+		self.substitutions = substitutions = {}
+		deltaGlyphID = reader.readShort()
+		for i in range(len(glyphIDs)):
+			input = glyphNames[i]
+			output = otFont.getGlyphName(glyphIDs[i] + deltaGlyphID)
+			substitutions[input] = output
+	
+	def decompileFormat2(self, reader, otFont):
+		coverage = reader.readTable(otCommon.CoverageTable, otFont)
+		glyphNames = coverage.getGlyphNames()
+		glyphCount = reader.readUShort()
+		self.substitutions = substitutions = {}
+		for i in range(glyphCount):
+			glyphID = reader.readUShort()
+			output = otFont.getGlyphName(glyphID)
+			input = glyphNames[i]
+			substitutions[input] = output
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		substitutions = self.substitutions.items()
+		substitutions.sort()
+		for input, output in substitutions:
+			xmlWriter.simpletag("Subst", [("in", input), ("out", output)])
+			xmlWriter.newline()
+	
+	def fromXML(self, (name, attrs, content), otFont):
+		xxx
+
+
+class MultipleSubst:
+	
+	def decompile(self, reader, otFont):
+		format = reader.readUShort()
+		if format <> 1:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "unknown MultipleSubst format: %d" % format
+		glyphNames = reader.readTable(otCommon.CoverageTable, otFont).getGlyphNames()
+		sequenceCount = reader.readUShort()
+		self.substitutions = substitutions = {}
+		for i in range(sequenceCount):
+			sequence = reader.readTable(Sequence, otFont)
+			substitutions[glyphNames[i]] = sequence.glyphs
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		import string
+		items = self.substitutions.items()
+		items.sort()
+		for input, output in items:
+			xmlWriter.simpletag("Subst", [("in", input), ("out", string.join(output, ","))])
+			xmlWriter.newline()
+
+
+class Sequence:
+	
+	def decompile(self, reader, otFont):
+		self.glyphs = []
+		for i in range(reader.readUShort()):
+			self.glyphs.append(otFont.getGlyphName(reader.readUShort()))
+	
+	def compile(self, otFont):
+		xxx
+
+
+class AlternateSubst:
+	
+	def decompile(self, reader, otFont):
+		format = reader.readUShort()
+		if format <> 1:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "unknown AlternateSubst format: %d" % format
+		coverage = reader.readTable(otCommon.CoverageTable, otFont)
+		glyphNames = coverage.getGlyphNames()
+		alternateSetCount = reader.readUShort()
+		self.alternateSet = alternateSet = {}
+		for i in range(alternateSetCount):
+			set = reader.readTable(AlternateSet, otFont)
+			alternateSet[glyphNames[i]] = set.glyphs
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		alternates = self.alternateSet.items()
+		alternates.sort()
+		for input, substList in alternates:
+			xmlWriter.begintag("AlternateSet", [("in", input)])
+			xmlWriter.newline()
+			for output in substList:
+				xmlWriter.simpletag("Subst", out=output)
+				xmlWriter.newline()
+			xmlWriter.endtag("AlternateSet")
+			xmlWriter.newline()
+
+
+class AlternateSet:
+	
+	def decompile(self, reader, otFont):
+		glyphCount = reader.readUShort()
+		glyphIDs = reader.readUShortArray(glyphCount)
+		self.glyphs = map(otFont.getGlyphName, glyphIDs)
+	
+	def compile(self, otFont):
+		xxx
+
+
+class LigatureSubst:
+	
+	def decompile(self, reader, otFont):
+		format = reader.readUShort()
+		if format <> 1:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "unknown LigatureSubst format: %d" % format
+		coverage = reader.readTable(otCommon.CoverageTable, otFont)
+		glyphNames = coverage.getGlyphNames()
+		ligSetCount = reader.readUShort()
+		self.ligatures = ligatures = []
+		for i in range(ligSetCount):
+			firstGlyph = glyphNames[i]
+			ligSet = reader.readTable(LigatureSet, otFont)
+			for ligatureGlyph, components in ligSet.ligatures:
+				ligatures.append(((firstGlyph,) + tuple(components)), ligatureGlyph)
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		import string
+		for input, output in self.ligatures:
+			xmlWriter.simpletag("Subst", [("in", string.join(input, ",")), ("out", output)])
+			xmlWriter.newline()
+
+
+class LigatureSet:
+	
+	def decompile(self, reader, otFont):
+		ligatureCount = reader.readUShort()
+		self.ligatures = ligatures = []
+		for i in range(ligatureCount):
+			lig = reader.readTable(Ligature, otFont)
+			ligatures.append((lig.ligatureGlyph, lig.components))
+	
+	def compile(self, otFont):
+		xxx
+
+
+class Ligature:
+	
+	def decompile(self, reader, otFont):
+		self.ligatureGlyph = otFont.getGlyphName(reader.readUShort())
+		compCount = reader.readUShort()
+		self.components = components = []
+		for i in range(compCount-1):
+			components.append(otFont.getGlyphName(reader.readUShort()))
+	
+	def compile(self, otFont):
+		xxx
+
+
+class ContextSubst:
+	
+	def decompile(self, reader, otFont):
+		format = reader.readUShort()
+		if format == 1:
+			self.decompileFormat1(reader, otFont)
+		elif format == 2:
+			self.decompileFormat2(reader, otFont)
+		elif format == 3:
+			self.decompileFormat3(reader, otFont)
+		else:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "unknown ContextSubst format: %d" % format
+	
+	def decompileFormat1(self, reader, otFont):
+		xxx
+	
+	def decompileFormat2(self, reader, otFont):
+		xxx
+	
+	def decompileFormat3(self, reader, otFont):
+		glyphCount = reader.readUShort()
+		substCount = reader.readUShort()
+		coverage = []
+		for i in range(glyphCount):
+			coverage.append(reader.readTable(otCommon.CoverageTable, otFont))
+		self.substitutions = substitutions = []
+		for i in range(substCount):
+			lookupRecord = SubstLookupRecord()
+			lookupRecord.decompile(reader, otFont)
+			substitutions.append((coverage[i].getGlyphNames(), lookupRecord))
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.comment("XXX")
+		xmlWriter.newline()
+
+
+class ChainContextSubst:
+	
+	def decompile(self, reader, otFont):
+		self.format = reader.readUShort()
+		if self.format == 1:
+			self.decompileFormat1(reader, otFont)
+		elif self.format == 2:
+			self.decompileFormat2(reader, otFont)
+		elif self.format == 3:
+			self.decompileFormat3(reader, otFont)
+		else:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "unknown ChainContextSubst format: %d" % self.format
+	
+	def decompileFormat1(self, reader, otFont):
+		pass
+	
+	def decompileFormat2(self, reader, otFont):
+		pass
+	
+	def decompileFormat3(self, reader, otFont):
+		pass
+		
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.comment("XXX")
+		xmlWriter.newline()
+
+
+lookupTypeClasses = {
+	1: SingleSubst,
+	2: MultipleSubst,
+	3: AlternateSubst,
+	4: LigatureSubst,
+	5: ContextSubst,
+	6: ChainContextSubst,
+}
+
+
+#
+# Shared classes
+#
+
+class SubstLookupRecord:
+	
+	def decompile(self, reader, otFont):
+		self.sequenceIndex = reader.readUShort()
+		self.lookupListIndex = reader.readUShort()
+	
+	def compile(self, otFont):
+		xxx
+
diff --git a/Lib/fontTools/ttLib/tables/L_T_S_H_.py b/Lib/fontTools/ttLib/tables/L_T_S_H_.py
new file mode 100644
index 0000000..20701d6
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/L_T_S_H_.py
@@ -0,0 +1,50 @@
+import DefaultTable
+import array
+import struct
+from fontTools.misc.textTools import safeEval
+
+# XXX I've lowered the strictness, to make sure Apple's own Chicago
+# XXX gets through. They're looking into it, I hope to raise the standards
+# XXX back to normal eventually.
+
+class table_L_T_S_H_(DefaultTable.DefaultTable):
+	
+	def decompile(self, data, ttFont):
+		version, numGlyphs = struct.unpack(">HH", data[:4])
+		data = data[4:]
+		assert version == 0
+		assert len(data) == numGlyphs
+		# ouch: the assertion is not true in Chicago!
+		#assert numGlyphs == ttFont['maxp'].numGlyphs
+		yPels = array.array("B")
+		yPels.fromstring(data)
+		self.yPels = {}
+		for i in range(numGlyphs):
+			self.yPels[ttFont.getGlyphName(i)] = yPels[i]
+	
+	def compile(self, ttFont):
+		version = 0
+		names = self.yPels.keys()
+		numGlyphs = len(names)
+		yPels = [0] * numGlyphs
+		# ouch: the assertion is not true in Chicago!
+		#assert len(self.yPels) == ttFont['maxp'].numGlyphs == numGlyphs
+		for name in names:
+			yPels[ttFont.getGlyphID(name)] = self.yPels[name]
+		yPels = array.array("B", yPels)
+		return struct.pack(">HH", version, numGlyphs) + yPels.tostring()
+	
+	def toXML(self, writer, ttFont):
+		names = self.yPels.keys()
+		names.sort()
+		for name in names:
+			writer.simpletag("yPel", name=name, value=self.yPels[name])
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if not hasattr(self, "yPels"):
+			self.yPels = {}
+		if name <> "yPel":
+			return # ignore unknown tags
+		self.yPels[attrs["name"]] = safeEval(attrs["value"])
+
diff --git a/Lib/fontTools/ttLib/tables/O_S_2f_2.py b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
new file mode 100644
index 0000000..498275f
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
@@ -0,0 +1,164 @@
+import DefaultTable
+import sstruct
+from fontTools.misc.textTools import safeEval, num2binary, binary2num
+
+# panose classification
+
+panoseFormat = """
+	bFamilyType:		B
+	bSerifStyle:		B
+	bWeight:			B
+	bProportion:		B
+	bContrast:			B
+	bStrokeVariation:	B
+	bArmStyle:			B
+	bLetterForm:		B
+	bMidline:			B
+	bXHeight:			B
+"""
+
+class Panose:
+	
+	def toXML(self, writer, ttFont):
+		formatstring, names, fixes = sstruct.getformat(panoseFormat)
+		for name in names:
+			writer.simpletag(name, value=getattr(self, name))
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		setattr(self, name, safeEval(attrs["value"]))
+
+
+# 'sfnt' OS/2 and Windows Metrics table - 'OS/2'
+
+OS2_format_0 = """
+	>	# big endian
+	version:				H		# version
+	xAvgCharWidth:			h		# average character width
+	usWeightClass:			H		# degree of thickness of strokes
+	usWidthClass:			H		# aspect ratio
+	fsType:					h		# type flags
+	ySubscriptXSize:		h		# subscript horizontal font size
+	ySubscriptYSize:		h		# subscript vertical font size
+	ySubscriptXOffset:		h		# subscript x offset
+	ySubscriptYOffset:		h		# subscript y offset
+	ySuperscriptXSize:		h		# superscript horizontal font size
+	ySuperscriptYSize:		h		# superscript vertical font size
+	ySuperscriptXOffset:	h		# superscript x offset
+	ySuperscriptYOffset:	h		# superscript y offset
+	yStrikeoutSize:			h		# strikeout size
+	yStrikeoutPosition:		h		# strikeout position
+	sFamilyClass:			h		# font family class and subclass
+	panose: 				10s		# panose classification number
+	ulUnicodeRange1:		l		# character range
+	ulUnicodeRange2:		l		# character range
+	ulUnicodeRange3:		l		# character range
+	ulUnicodeRange4:		l		# character range
+	achVendID:				4s		# font vendor identification
+	fsSelection:			H		# font selection flags
+	fsFirstCharIndex:		H		# first unicode character index
+	fsLastCharIndex:		H		# last unicode character index
+	usTypoAscender:			H		# typographic ascender
+	usTypoDescender:		H		# typographic descender
+	usTypoLineGap:			H		# typographic line gap
+	usWinAscent:			H		# Windows ascender
+	usWinDescent:			H		# Windows descender
+"""
+
+OS2_format_1_addition =  """
+	ulCodePageRange1:	l
+	ulCodePageRange2:	l	
+"""
+
+OS2_format_2_addition =  OS2_format_1_addition + """
+	sxHeight:			h
+	sCapHeight:			h
+	usDefaultChar:		H
+	usBreakChar:		H
+	usMaxContex:		H
+"""
+
+bigendian = "	>	# big endian\n"
+
+OS2_format_1 = OS2_format_0 + OS2_format_1_addition
+OS2_format_2 = OS2_format_0 + OS2_format_2_addition
+OS2_format_1_addition = bigendian + OS2_format_1_addition
+OS2_format_2_addition = bigendian + OS2_format_2_addition
+
+
+class table_O_S_2f_2(DefaultTable.DefaultTable):
+	
+	"""the OS/2 table"""
+	
+	def decompile(self, data, ttFont):
+		dummy, data = sstruct.unpack2(OS2_format_0, data, self)
+		if self.version == 1:
+			sstruct.unpack(OS2_format_1_addition, data, self)
+		elif self.version == 2:
+			sstruct.unpack(OS2_format_2_addition, data, self)
+		elif self.version <> 0:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "unknown format for OS/2 table: version %s" % self.version
+		self.panose = sstruct.unpack(panoseFormat, self.panose, Panose())
+	
+	def compile(self, ttFont):
+		panose = self.panose
+		self.panose = sstruct.pack(panoseFormat, self.panose)
+		if self.version == 0:
+			data = sstruct.pack(OS2_format_0, self)
+		elif self.version == 1:
+			data = sstruct.pack(OS2_format_1, self)
+		elif self.version == 2:
+			data = sstruct.pack(OS2_format_2, self)
+		else:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "unknown format for OS/2 table: version %s" % self.version
+		self.panose = panose
+		return data
+	
+	def toXML(self, writer, ttFont):
+		if self.version == 1:
+			format = OS2_format_1
+		elif self.version == 2:
+			format = OS2_format_2
+		else:
+			format = OS2_format_0
+		formatstring, names, fixes = sstruct.getformat(format)
+		for name in names:
+			value = getattr(self, name)
+			if type(value) == type(0L):
+				value = int(value)
+			if name=="panose":
+				writer.begintag("panose")
+				writer.newline()
+				value.toXML(writer, ttFont)
+				writer.endtag("panose")
+			elif name in ("ulUnicodeRange1", "ulUnicodeRange2", 
+					"ulUnicodeRange3", "ulUnicodeRange4",
+					"ulCodePageRange1", "ulCodePageRange2"):
+				writer.simpletag(name, value=num2binary(value))
+			elif name in ("fsType", "fsSelection"):
+				writer.simpletag(name, value=num2binary(value, 16))
+			elif name == "achVendID":
+				writer.simpletag(name, value=repr(value)[1:-1])
+			else:
+				writer.simpletag(name, value=value)
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if name == "panose":
+			self.panose = panose = Panose()
+			for element in content:
+				if type(element) == type(()):
+					panose.fromXML(element, ttFont)
+		elif name in ("ulUnicodeRange1", "ulUnicodeRange2", 
+				"ulUnicodeRange3", "ulUnicodeRange4",
+				"ulCodePageRange1", "ulCodePageRange2",
+				"fsType", "fsSelection"):
+			setattr(self, name, binary2num(attrs["value"]))
+		elif name == "achVendID":
+			setattr(self, name, safeEval("'''" + attrs["value"] + "'''"))
+		else:
+			setattr(self, name, safeEval(attrs["value"]))
+
+
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__0.py b/Lib/fontTools/ttLib/tables/T_S_I__0.py
new file mode 100644
index 0000000..988d29a
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_S_I__0.py
@@ -0,0 +1,45 @@
+import DefaultTable
+import struct
+
+tsi0Format = '>HHl'
+
+def fixlongs((glyphID, textLength, textOffset)):
+	return int(glyphID), int(textLength), textOffset	
+
+
+class table_T_S_I__0(DefaultTable.DefaultTable):
+	
+	dependencies = ["TSI1"]
+	
+	def decompile(self, data, ttFont):
+		numGlyphs = ttFont['maxp'].numGlyphs
+		indices = []
+		size = struct.calcsize(tsi0Format)
+		for i in range(numGlyphs + 5):
+			glyphID, textLength, textOffset = fixlongs(struct.unpack(tsi0Format, data[:size]))
+			indices.append((glyphID, textLength, textOffset))
+			data = data[size:]
+		assert len(data) == 0
+		assert indices[-5] == (0XFFFE, 0, 0xABFC1F34), "bad magic number"
+		self.indices = indices[:-5]
+		self.extra_indices = indices[-4:]
+	
+	def compile(self, ttFont):
+		data = ""
+		size = struct.calcsize(tsi0Format)
+		for index, textLength, textOffset in self.indices:
+			data = data + struct.pack(tsi0Format, index, textLength, textOffset)
+		data = data + struct.pack(tsi0Format, 0XFFFE, 0, 0xABFC1F34)
+		for index, textLength, textOffset in self.extra_indices:
+			data = data + struct.pack(tsi0Format, index, textLength, textOffset)
+		return data
+	
+	def set(self, indices, extra_indices):
+		# gets called by 'TSI1' or 'TSI3'
+		self.indices = indices
+		self.extra_indices = extra_indices
+	
+	def toXML(self, writer, ttFont):
+		writer.comment("This table will be calculated by the compiler")
+		writer.newline()
+
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__1.py b/Lib/fontTools/ttLib/tables/T_S_I__1.py
new file mode 100644
index 0000000..8ee4e16
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_S_I__1.py
@@ -0,0 +1,116 @@
+import DefaultTable
+import string
+
+class table_T_S_I__1(DefaultTable.DefaultTable):
+	
+	extras = {0xfffa: "ppgm", 0xfffb: "cvt", 0xfffc: "reserved", 0xfffd: "fpgm"}
+	
+	indextable = "TSI0"
+	
+	def decompile(self, data, ttFont):
+		indextable = ttFont[self.indextable]
+		self.glyphPrograms = {}
+		for i in range(len(indextable.indices)):
+			glyphID, textLength, textOffset = indextable.indices[i]
+			if textLength == 0x8000:
+				# Ugh. Hi Beat!
+				textLength = indextable.indices[i+1][1]
+			if textLength > 0x8000:
+				pass  # XXX Hmmm.
+			text = data[textOffset:textOffset+textLength]
+			assert len(text) == textLength
+			if text:
+				self.glyphPrograms[ttFont.getGlyphName(glyphID)] = text
+		
+		self.extraPrograms = {}
+		for i in range(len(indextable.extra_indices)):
+			extraCode, textLength, textOffset = indextable.extra_indices[i]
+			if textLength == 0x8000:
+				if extraName == "fpgm":	# this is the last one
+					textLength = len(data) - textOffset
+				else:
+					textLength = indextable.extra_indices[i+1][1]
+			text = data[textOffset:textOffset+textLength]
+			assert len(text) == textLength
+			if text:
+				self.extraPrograms[self.extras[extraCode]] = text
+	
+	def compile(self, ttFont):
+		data = ''
+		indextable = ttFont[self.indextable]
+		glyphNames = ttFont.getGlyphOrder()
+		
+		indices = []
+		for i in range(len(glyphNames)):
+			if len(data) % 2:
+				data = data + "\015"  # align on 2-byte boundaries, fill with return chars. Yum.
+			name = glyphNames[i]
+			if self.glyphPrograms.has_key(name):
+				text = self.glyphPrograms[name]
+			else:
+				text = ""
+			textLength = len(text)
+			if textLength >= 0x8000:
+				textLength = 0x8000  # XXX ???
+			indices.append((i, textLength, len(data)))
+			data = data + text
+		
+		extra_indices = []
+		codes = self.extras.items()
+		codes.sort()
+		for i in range(len(codes)):
+			if len(data) % 2:
+				data = data + "\015"  # align on 2-byte boundaries, fill with return chars.
+			code, name = codes[i]
+			if self.extraPrograms.has_key(name):
+				text = self.extraPrograms[name]
+			else:
+				text = ""
+			textLength = len(text)
+			if textLength >= 0x8000:
+				textLength = 0x8000  # XXX ???
+			extra_indices.append((code, textLength, len(data)))
+			data = data + text
+		indextable.set(indices, extra_indices)
+		return data
+	
+	def toXML(self, writer, ttFont):
+		names = self.glyphPrograms.keys()
+		names.sort()
+		writer.newline()
+		for name in names:
+			text = self.glyphPrograms[name]
+			if not text:
+				continue
+			writer.begintag("glyphProgram", name=name)
+			writer.newline()
+			writer.write_noindent(string.replace(text, "\r", "\n"))
+			writer.newline()
+			writer.endtag("glyphProgram")
+			writer.newline()
+			writer.newline()
+		extra_names = self.extraPrograms.keys()
+		extra_names.sort()
+		for name in extra_names:
+			text = self.extraPrograms[name]
+			if not text:
+				continue
+			writer.begintag("extraProgram", name=name)
+			writer.newline()
+			writer.write_noindent(string.replace(text, "\r", "\n"))
+			writer.newline()
+			writer.endtag("extraProgram")
+			writer.newline()
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if not hasattr(self, "glyphPrograms"):
+			self.glyphPrograms = {}
+			self.extraPrograms = {}
+		lines = string.split(string.replace(string.join(content, ""), "\r", "\n"), "\n")
+		text = string.join(lines[1:-1], "\r")
+		if name == "glyphProgram":
+			self.glyphPrograms[attrs["name"]] = text
+		elif name == "extraProgram":
+			self.extraPrograms[attrs["name"]] = text
+
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__2.py b/Lib/fontTools/ttLib/tables/T_S_I__2.py
new file mode 100644
index 0000000..15c02ab
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_S_I__2.py
@@ -0,0 +1,8 @@
+from fontTools import ttLib
+
+superclass = ttLib.getTableClass("TSI0")
+
+class table_T_S_I__2(superclass):
+	
+	dependencies = ["TSI3"]
+
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__3.py b/Lib/fontTools/ttLib/tables/T_S_I__3.py
new file mode 100644
index 0000000..eb4087c
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_S_I__3.py
@@ -0,0 +1,11 @@
+from fontTools import ttLib
+
+superclass = ttLib.getTableClass("TSI1")
+
+class table_T_S_I__3(superclass):
+	
+	extras = {0xfffa: "reserved0", 0xfffb: "reserved1", 0xfffc: "reserved2", 0xfffd: "reserved3"}
+	
+	indextable = "TSI2"
+
+
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__5.py b/Lib/fontTools/ttLib/tables/T_S_I__5.py
new file mode 100644
index 0000000..35b9ee1
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_S_I__5.py
@@ -0,0 +1,42 @@
+import DefaultTable
+import array
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+
+
+class table_T_S_I__5(DefaultTable.DefaultTable):
+	
+	def decompile(self, data, ttFont):
+		numGlyphs = ttFont['maxp'].numGlyphs
+		assert len(data) == 2 * numGlyphs
+		a = array.array("H")
+		a.fromstring(data)
+		if ttLib.endian <> "big":
+			a.byteswap()
+		self.glyphGrouping = {}
+		for i in range(numGlyphs):
+			self.glyphGrouping[ttFont.getGlyphName(i)] = a[i]
+	
+	def compile(self, ttFont):
+		glyphNames = ttFont.getGlyphOrder()
+		a = array.array("H")
+		for i in range(len(glyphNames)):
+			a.append(self.glyphGrouping[glyphNames[i]])
+		if ttLib.endian <> "big":
+			a.byteswap()
+		return a.tostring()
+	
+	def toXML(self, writer, ttFont):
+		names = self.glyphGrouping.keys()
+		names.sort()
+		for glyphName in names:
+			writer.simpletag("glyphgroup", name=glyphName, value=self.glyphGrouping[glyphName])
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if not hasattr(self, "glyphGrouping"):
+			self.glyphGrouping = {}
+		if name <> "glyphgroup":
+			return
+		self.glyphGrouping[attrs["name"]] = safeEval(attrs["value"])
+
diff --git a/Lib/fontTools/ttLib/tables/__init__.py b/Lib/fontTools/ttLib/tables/__init__.py
new file mode 100644
index 0000000..2f17637
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/__init__.py
@@ -0,0 +1 @@
+# dummy file, so Python knows ttLib.tables is a subpackage
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
new file mode 100644
index 0000000..b067ac6
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -0,0 +1,397 @@
+import DefaultTable
+import struct
+import string
+import array
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval, readHex
+
+
+class table__c_m_a_p(DefaultTable.DefaultTable):
+	
+	def getcmap(self, platformID, platEncID):
+		for subtable in self.tables:
+			if (subtable.platformID == platformID and 
+					subtable.platEncID == platEncID):
+				return subtable
+		return None # not found
+	
+	def decompile(self, data, ttFont):
+		tableVersion, numSubTables = struct.unpack(">HH", data[:4])
+		self.tableVersion = int(tableVersion)
+		self.tables = tables = []
+		for i in range(numSubTables):
+			platformID, platEncID, offset = struct.unpack(
+					">HHl", data[4+i*8:4+(i+1)*8])
+			platformID, platEncID = int(platformID), int(platEncID)
+			format, length = struct.unpack(">HH", data[offset:offset+4])
+			if not cmap_classes.has_key(format):
+				table = cmap_format_unknown(format)
+			else:
+				table = cmap_classes[format](format)
+			table.platformID = platformID
+			table.platEncID = platEncID
+			table.decompile(data[offset:offset+int(length)], ttFont)
+			tables.append(table)
+	
+	def compile(self, ttFont):
+		self.tables.sort()    # sort according to the spec; see CmapSubtable.__cmp__()
+		numSubTables = len(self.tables)
+		totalOffset = 4 + 8 * numSubTables
+		data = struct.pack(">HH", self.tableVersion, numSubTables)
+		tableData = ""
+		done = {}  # remember the data so we can reuse the "pointers"
+		for table in self.tables:
+			chunk = table.compile(ttFont)
+			if done.has_key(chunk):
+				offset = done[chunk]
+			else:
+				offset = done[chunk] = totalOffset + len(tableData)
+			tableData = tableData + chunk
+			data = data + struct.pack(">HHl", table.platformID, table.platEncID, offset)
+		return data + tableData
+	
+	def toXML(self, writer, ttFont):
+		writer.simpletag("tableVersion", version=self.tableVersion)
+		writer.newline()
+		for table in self.tables:
+			table.toXML(writer, ttFont)
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if name == "tableVersion":
+			self.tableVersion = safeEval(attrs["version"])
+			return
+		if name[:12] <> "cmap_format_":
+			return
+		if not hasattr(self, "tables"):
+			self.tables = []
+		format = safeEval(name[12])
+		if not cmap_classes.has_key(format):
+			table = cmap_format_unknown(format)
+		else:
+			table = cmap_classes[format](format)
+		table.platformID = safeEval(attrs["platformID"])
+		table.platEncID = safeEval(attrs["platEncID"])
+		table.fromXML((name, attrs, content), ttFont)
+		self.tables.append(table)
+
+
+class CmapSubtable:
+	
+	def __init__(self, format):
+		self.format = format
+	
+	def toXML(self, writer, ttFont):
+		writer.begintag(self.__class__.__name__, [
+				("platformID", self.platformID),
+				("platEncID", self.platEncID),
+				])
+		writer.newline()
+		writer.dumphex(self.compile(ttFont))
+		writer.endtag(self.__class__.__name__)
+		writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		self.decompile(readHex(content), ttFont)
+	
+	def __cmp__(self, other):
+		# implemented so that list.sort() sorts according to the cmap spec.
+		selfTuple = (
+					self.platformID,
+					self.platEncID,
+					self.version,
+					self.__dict__)
+		otherTuple = (
+					other.platformID,
+					other.platEncID,
+					other.version,
+					other.__dict__)
+		return cmp(selfTuple, otherTuple)
+
+
+class cmap_format_0(CmapSubtable):
+	
+	def decompile(self, data, ttFont):
+		format, length, version = struct.unpack(">HHH", data[:6])
+		self.version = int(version)
+		assert len(data) == 262 == length
+		glyphIdArray = array.array("B")
+		glyphIdArray.fromstring(data[6:])
+		self.cmap = cmap = {}
+		for charCode in range(len(glyphIdArray)):
+			cmap[charCode] = ttFont.getGlyphName(glyphIdArray[charCode])
+	
+	def compile(self, ttFont):
+		charCodes = self.cmap.keys()
+		charCodes.sort()
+		assert charCodes == range(256)  # charCodes[charCode] == charCode
+		for charCode in charCodes:
+			# reusing the charCodes list!
+			charCodes[charCode] = ttFont.getGlyphID(self.cmap[charCode])
+		glyphIdArray = array.array("B", charCodes)
+		data = struct.pack(">HHH", 0, 262, self.version) + glyphIdArray.tostring()
+		assert len(data) == 262
+		return data
+	
+	def toXML(self, writer, ttFont):
+		writer.begintag(self.__class__.__name__, [
+				("platformID", self.platformID),
+				("platEncID", self.platEncID),
+				("version", self.version),
+				])
+		writer.newline()
+		items = self.cmap.items()
+		items.sort()
+		for code, name in items:
+			writer.simpletag("map", code=hex(code), name=name)
+			writer.newline()
+		writer.endtag(self.__class__.__name__)
+		writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		self.version = safeEval(attrs["version"])
+		self.cmap = {}
+		for element in content:
+			if type(element) <> type(()):
+				continue
+			name, attrs, content = element
+			if name <> "map":
+				continue
+			self.cmap[safeEval(attrs["code"])] = attrs["name"]
+			
+
+
+class cmap_format_2(CmapSubtable):
+	
+	def decompile(self, data, ttFont):
+		format, length, version = struct.unpack(">HHH", data[:6])
+		self.version = int(version)
+		self.data = data
+	
+	def compile(self, ttFont):
+		return self.data
+
+
+cmap_format_4_format = ">7H"
+
+#uint16	endCode[segCount]			# Ending character code for each segment, last = 0xFFFF.
+#uint16	reservedPad					# This value should be zero
+#uint16	startCode[segCount]			# Starting character code for each segment
+#uint16	idDelta[segCount]			# Delta for all character codes in segment
+#uint16	idRangeOffset[segCount]		# Offset in bytes to glyph indexArray, or 0
+#uint16	glyphIndexArray[variable]	# Glyph index array
+
+class cmap_format_4(CmapSubtable):
+	
+	def decompile(self, data, ttFont):
+		(format, length, self.version, segCountX2, 
+				searchRange, entrySelector, rangeShift) = \
+					struct.unpack(cmap_format_4_format, data[:14])
+		assert len(data) == length, "corrupt cmap table (%d, %d)" % (len(data), length)
+		data = data[14:]
+		segCountX2 = int(segCountX2)
+		segCount = segCountX2 / 2
+		
+		allcodes = array.array("H")
+		allcodes.fromstring(data)
+		if ttLib.endian <> "big":
+			allcodes.byteswap()
+		
+		# divide the data
+		endCode = allcodes[:segCount]
+		allcodes = allcodes[segCount+1:]
+		startCode = allcodes[:segCount]
+		allcodes = allcodes[segCount:]
+		idDelta = allcodes[:segCount]
+		allcodes = allcodes[segCount:]
+		idRangeOffset = allcodes[:segCount]
+		glyphIndexArray = allcodes[segCount:]
+		
+		# build 2-byte character mapping
+		cmap = {}
+		for i in range(len(startCode) - 1):	# don't do 0xffff!
+			for charCode in range(startCode[i], endCode[i] + 1):
+				rangeOffset = idRangeOffset[i]
+				if rangeOffset == 0:
+					glyphID = charCode + idDelta[i]
+				else:
+					# *someone* needs to get killed.
+					index = idRangeOffset[i] / 2 + (charCode - startCode[i]) + i - len(idRangeOffset)
+					if glyphIndexArray[index] <> 0:  # if not missing glyph
+						glyphID = glyphIndexArray[index] + idDelta[i]
+					else:
+						glyphID = 0  # missing glyph
+				cmap[charCode] = ttFont.getGlyphName(glyphID % 0x10000)
+		self.cmap = cmap
+	
+	def compile(self, ttFont):
+		from fontTools.ttLib.sfnt import maxpoweroftwo
+		
+		codes = self.cmap.items()
+		codes.sort()
+		
+		# build startCode and endCode lists
+		last = codes[0][0]
+		endCode = []
+		startCode = [last]
+		for charCode, glyphName in codes[1:]:  # skip the first code, it's the first start code
+			if charCode == last + 1:
+				last = charCode
+				continue
+			endCode.append(last)
+			startCode.append(charCode)
+			last = charCode
+		endCode.append(last)
+		startCode.append(0xffff)
+		endCode.append(0xffff)
+		
+		# build up rest of cruft.
+		idDelta = []
+		idRangeOffset = []
+		glyphIndexArray = []
+		
+		for i in range(len(endCode)-1):  # skip the closing codes (0xffff)
+			indices = []
+			for charCode in range(startCode[i], endCode[i]+1):
+				indices.append(ttFont.getGlyphID(self.cmap[charCode]))
+			if indices == range(indices[0], indices[0] + len(indices)):
+				idDelta.append((indices[0] - startCode[i]) % 0x10000)
+				idRangeOffset.append(0)
+			else:
+				# someone *definitely* needs to get killed.
+				idDelta.append(0)
+				idRangeOffset.append(2 * (len(endCode) + len(glyphIndexArray) - i))
+				glyphIndexArray = glyphIndexArray + indices
+		idDelta.append(1)  # 0xffff + 1 == (tadaa!) 0. So this end code maps to .notdef
+		idRangeOffset.append(0)
+		
+		# Insane. 
+		segCount = len(endCode)
+		segCountX2 = segCount * 2
+		maxexponent = maxpoweroftwo(segCount)
+		searchRange = 2 * (2 ** maxexponent)
+		entrySelector = maxexponent
+		rangeShift = 2 * segCount - searchRange
+		
+		allcodes = array.array("H", 
+				endCode + [0] + startCode + idDelta + idRangeOffset + glyphIndexArray)
+		if ttLib.endian <> "big":
+			allcodes.byteswap()
+		data = allcodes.tostring()
+		length = struct.calcsize(cmap_format_4_format) + len(data)
+		header = struct.pack(cmap_format_4_format, self.format, length, self.version, 
+				segCountX2, searchRange, entrySelector, rangeShift)
+		return header + data
+	
+	def toXML(self, writer, ttFont):
+		from fontTools.unicode import Unicode
+		codes = self.cmap.items()
+		codes.sort()
+		writer.begintag(self.__class__.__name__, [
+				("platformID", self.platformID),
+				("platEncID", self.platEncID),
+				("version", self.version),
+				])
+		writer.newline()
+		
+		for code, name in codes:
+			writer.simpletag("map", code=hex(code), name=name)
+			writer.comment(Unicode[code])
+			writer.newline()
+		
+		writer.endtag(self.__class__.__name__)
+		writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		self.version = safeEval(attrs["version"])
+		self.cmap = {}
+		for element in content:
+			if type(element) <> type(()):
+				continue
+			name, attrs, content = element
+			if name <> "map":
+				continue
+			self.cmap[safeEval(attrs["code"])] = attrs["name"]
+
+
+class cmap_format_6(CmapSubtable):
+	
+	def decompile(self, data, ttFont):
+		format, length, version, firstCode, entryCount = struct.unpack(
+				">HHHHH", data[:10])
+		self.version = int(version)
+		firstCode = int(firstCode)
+		self.version = int(version)
+		data = data[10:]
+		assert len(data) == 2 * entryCount
+		glyphIndexArray = array.array("H")
+		glyphIndexArray.fromstring(data)
+		if ttLib.endian <> "big":
+			glyphIndexArray.byteswap()
+		self.cmap = cmap = {}
+		for i in range(len(glyphIndexArray)):
+			glyphID = glyphIndexArray[i]
+			glyphName = ttFont.getGlyphName(glyphID)
+			cmap[i+firstCode] = glyphName
+	
+	def compile(self, ttFont):
+		codes = self.cmap.keys()
+		codes.sort()
+		assert codes == range(codes[0], codes[0] + len(codes))
+		glyphIndexArray = array.array("H", [0] * len(codes))
+		firstCode = codes[0]
+		for i in range(len(codes)):
+			code = codes[i]
+			glyphIndexArray[code-firstCode] = ttFont.getGlyphID(self.cmap[code])
+		if ttLib.endian <> "big":
+			glyphIndexArray.byteswap()
+		data = glyphIndexArray.tostring()
+		header = struct.pack(">HHHHH", 
+				6, len(data) + 10, self.version, firstCode, len(self.cmap))
+		return header + data
+	
+	def toXML(self, writer, ttFont):
+		codes = self.cmap.items()
+		codes.sort()
+		writer.begintag(self.__class__.__name__, [
+				("platformID", self.platformID),
+				("platEncID", self.platEncID),
+				("version", self.version),
+				])
+		writer.newline()
+		
+		for code, name in codes:
+			writer.simpletag("map", code=hex(code), name=name)
+			writer.newline()
+		
+		writer.endtag(self.__class__.__name__)
+		writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		self.version = safeEval(attrs["version"])
+		self.cmap = {}
+		for element in content:
+			if type(element) <> type(()):
+				continue
+			name, attrs, content = element
+			if name <> "map":
+				continue
+			self.cmap[safeEval(attrs["code"])] = attrs["name"]
+
+
+class cmap_format_unknown(CmapSubtable):
+	
+	def decompile(self, data, ttFont):
+		self.data = data
+	
+	def compile(self, ttFont):
+		return self.data
+
+
+cmap_classes = {
+		0: cmap_format_0,
+		2: cmap_format_2,
+		4: cmap_format_4,
+		6: cmap_format_6,
+		}
+
+
diff --git a/Lib/fontTools/ttLib/tables/_c_v_t.py b/Lib/fontTools/ttLib/tables/_c_v_t.py
new file mode 100644
index 0000000..ced5523
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_c_v_t.py
@@ -0,0 +1,48 @@
+import DefaultTable
+import array
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+
+class table__c_v_t(DefaultTable.DefaultTable):
+	
+	def decompile(self, data, ttFont):
+		values = array.array("h")
+		values.fromstring(data)
+		if ttLib.endian <> "big":
+			values.byteswap()
+		self.values = values
+	
+	def compile(self, ttFont):
+		values = self.values[:]
+		if ttLib.endian <> "big":
+			values.byteswap()
+		return values.tostring()
+	
+	def toXML(self, writer, ttFont):
+		for i in range(len(self.values)):
+			value = self.values[i]
+			writer.simpletag("cv", value=value, index=i)
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if not hasattr(self, "values"):
+			self.values = array.array("h")
+		if name == "cv":
+			index = safeEval(attrs["index"])
+			value = safeEval(attrs["value"])
+			for i in range(1 + index - len(self.values)):
+				self.values.append(0)
+			self.values[index] = value
+	
+	def __len__(self):
+		return len(self.values)
+	
+	def __getitem__(self, index):
+		return self.values[index]
+	
+	def __setitem__(self, index, value):
+		self.values[index] = value
+	
+	def __delitem__(self, index):
+		del self.values[index]
+
diff --git a/Lib/fontTools/ttLib/tables/_f_p_g_m.py b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
new file mode 100644
index 0000000..f7cc8a3
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
@@ -0,0 +1,14 @@
+import DefaultTable
+import array
+
+class table__f_p_g_m(DefaultTable.DefaultTable):
+	
+	def decompile(self, data, ttFont):
+		self.fpgm = data
+	
+	def compile(self, ttFont):
+		return self.fpgm
+	
+	def __len__(self):
+		return len(self.fpgm)
+	
diff --git a/Lib/fontTools/ttLib/tables/_g_a_s_p.py b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
new file mode 100644
index 0000000..e49cceb
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
@@ -0,0 +1,46 @@
+import DefaultTable
+import struct
+from fontTools.misc.textTools import safeEval
+
+
+GASP_DOGRAY = 0x0002
+GASP_GRIDFIT = 0x0001
+
+class table__g_a_s_p(DefaultTable.DefaultTable):
+	
+	def decompile(self, data, ttFont):
+		self.version, numRanges = struct.unpack(">HH", data[:4])
+		assert self.version == 0, "unknown 'gasp' format: %s" % self.version
+		data = data[4:]
+		self.gaspRange = {}
+		for i in range(numRanges):
+			rangeMaxPPEM, rangeGaspBehavior = struct.unpack(">HH", data[:4])
+			self.gaspRange[int(rangeMaxPPEM)] = int(rangeGaspBehavior)
+			data = data[4:]
+		assert not data, "too much data"
+	
+	def compile(self, ttFont):
+		numRanges = len(self.gaspRange)
+		data = struct.pack(">HH", 0, numRanges)
+		items = self.gaspRange.items()
+		items.sort()
+		for rangeMaxPPEM, rangeGaspBehavior in items:
+			data = data + struct.pack(">HH", rangeMaxPPEM, rangeGaspBehavior)
+		return data
+	
+	def toXML(self, writer, ttFont):
+		items = self.gaspRange.items()
+		items.sort()
+		for rangeMaxPPEM, rangeGaspBehavior in items:
+			writer.simpletag("gaspRange", [
+					("rangeMaxPPEM", rangeMaxPPEM),
+					("rangeGaspBehavior", rangeGaspBehavior)])
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if name <> "gaspRange":
+			return
+		if not hasattr(self, "gaspRange"):
+			self.gaspRange = {}
+		self.gaspRange[safeEval(attrs["rangeMaxPPEM"])] = safeEval(attrs["rangeGaspBehavior"])
+
diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
new file mode 100644
index 0000000..29f9be5
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -0,0 +1,778 @@
+"""_g_l_y_f.py -- Converter classes for the 'glyf' table."""
+
+
+#
+# The Apple and MS rasterizers behave differently for 
+# scaled composite components: one does scale first and then translate
+# and the other does it vice versa. MS defined some flags to indicate
+# the difference, but it seems nobody actually _sets_ those flags.
+#
+# Funny thing: Apple seems to _only_ do their thing in the
+# WE_HAVE_A_SCALE (eg. Chicago) case, and not when it's WE_HAVE_AN_X_AND_Y_SCALE 
+# (eg. Charcoal)...
+#
+SCALE_COMPONENT_OFFSET_DEFAULT = 0   # 0 == MS, 1 == Apple
+
+
+import struct, sstruct
+import DefaultTable
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval, readHex
+import array
+import Numeric
+import types
+
+class table__g_l_y_f(DefaultTable.DefaultTable):
+	
+	def decompile(self, data, ttFont):
+		loca = ttFont['loca']
+		last = loca[0]
+		self.glyphs = {}
+		self.glyphOrder = []
+		self.glyphOrder = glyphOrder = ttFont.getGlyphOrder()
+		for i in range(0, len(loca)-1):
+			glyphName = glyphOrder[i]
+			next = loca[i+1]
+			glyphdata = data[last:next]
+			if len(glyphdata) <> (next - last):
+				raise ttLib.TTLibError, "not enough 'glyf' table data"
+			glyph = Glyph(glyphdata)
+			self.glyphs[glyphName] = glyph
+			last = next
+		if len(data) > next:
+			raise ttLib.TTLibError, "too much 'glyf' table data"
+	
+	def compile(self, ttFont):
+		import string
+		locations = []
+		currentLocation = 0
+		dataList = []
+		for glyphName in ttFont.getGlyphOrder():
+			glyph = self[glyphName]
+			glyphData = glyph.compile(self)
+			locations.append(currentLocation)
+			currentLocation = currentLocation + len(glyphData)
+			dataList.append(glyphData)
+		locations.append(currentLocation)
+		data = string.join(dataList, "")
+		ttFont['loca'].set(locations)
+		ttFont['maxp'].numGlyphs = len(self.glyphs)
+		return data
+	
+	def toXML(self, writer, ttFont, progress=None, compactGlyphs=0):
+		writer.newline()
+		glyphOrder = ttFont.getGlyphOrder()
+		writer.begintag("GlyphOrder")
+		writer.newline()
+		for i in range(len(glyphOrder)):
+			glyphName = glyphOrder[i]
+			writer.simpletag("GlyphID", id=i, name=glyphName)
+			writer.newline()
+		writer.endtag("GlyphOrder")
+		writer.newline()
+		writer.newline()
+		glyphNames = ttFont.getGlyphNames()
+		writer.comment("The xMin, yMin, xMax and yMax values\nwill be recalculated by the compiler.")
+		writer.newline()
+		writer.newline()
+		for glyphName in glyphNames:
+			if progress:
+				progress.setlabel("Dumping 'glyf' table... (%s)" % glyphName)
+				progress.increment()
+			glyph = self[glyphName]
+			if glyph.numberOfContours:
+				writer.begintag('TTGlyph', [
+						("name", glyphName),
+						("xMin", glyph.xMin),
+						("yMin", glyph.yMin),
+						("xMax", glyph.xMax),
+						("yMax", glyph.yMax),
+						])
+				writer.newline()
+				glyph.toXML(writer, ttFont)
+				if compactGlyphs:
+					glyph.compact(self)
+				writer.endtag('TTGlyph')
+				writer.newline()
+			else:
+				writer.simpletag('TTGlyph', name=glyphName)
+				writer.comment("contains no outline data")
+				writer.newline()
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if name == "GlyphOrder":
+			glyphOrder = []
+			for element in content:
+				if type(element) == types.StringType:
+					continue
+				name, attrs, content = element
+				if name == "GlyphID":
+					index = safeEval(attrs["id"])
+					glyphName = attrs["name"]
+					glyphOrder = glyphOrder + (1 + index - len(glyphOrder)) * [".notdef"]
+					glyphOrder[index] = glyphName
+			ttFont.setGlyphOrder(glyphOrder)
+		elif name == "TTGlyph":
+			if not hasattr(self, "glyphs"):
+				self.glyphs = {}
+			glyphName = attrs["name"]
+			if ttFont.verbose:
+				ttLib.debugmsg("unpacking glyph '%s'" % glyphName)
+			glyph = Glyph()
+			for attr in ['xMin', 'yMin', 'xMax', 'yMax']:
+				setattr(glyph, attr, safeEval(attrs.get(attr, '0')))
+			self.glyphs[glyphName] = glyph
+			for element in content:
+				if type(element) == types.StringType:
+					continue
+				glyph.fromXML(element, ttFont)
+	
+	def setGlyphOrder(self, glyphOrder):
+		self.glyphOrder = glyphOrder
+	
+	def getGlyphName(self, glyphID):
+		return self.glyphOrder[glyphID]
+	
+	def getGlyphID(self, glyphName):
+		# XXX optimize with reverse dict!!!
+		return self.glyphOrder.index(glyphName)
+	
+	#def keys(self):
+	#	return self.glyphOrder[:]
+	#
+	#def has_key(self, glyphName):
+	#	return self.glyphs.has_key(glyphName)
+	#
+	def __getitem__(self, glyphName):
+		glyph = self.glyphs[glyphName]
+		glyph.expand(self)
+		return glyph
+	
+	def __setitem__(self, glyphName, glyph):
+		self.glyphs[glyphName] = glyph
+		if glyphName not in self.glyphOrder:
+			self.glyphOrder.append(glyphName)
+	
+	def __delitem__(self, glyphName):
+		del self.glyphs[glyphName]
+		self.glyphOrder.remove(glyphName)
+	
+	def __len__(self):
+		assert len(self.glyphOrder) == len(self.glyphs)
+		return len(self.glyphs)
+
+
+glyphHeaderFormat = """
+		>	# big endian
+		numberOfContours:	h
+		xMin:				h
+		yMin:				h
+		xMax:				h
+		yMax:				h
+"""
+
+# flags
+flagOnCurve = 0x01
+flagXShort = 0x02
+flagYShort = 0x04
+flagRepeat = 0x08
+flagXsame =  0x10
+flagYsame = 0x20
+flagReserved1 = 0x40
+flagReserved2 = 0x80
+
+
+ARG_1_AND_2_ARE_WORDS      = 0x0001  # if set args are words otherwise they are bytes 
+ARGS_ARE_XY_VALUES         = 0x0002  # if set args are xy values, otherwise they are points 
+ROUND_XY_TO_GRID           = 0x0004  # for the xy values if above is true 
+WE_HAVE_A_SCALE            = 0x0008  # Sx = Sy, otherwise scale == 1.0 
+NON_OVERLAPPING            = 0x0010  # set to same value for all components (obsolete!)
+MORE_COMPONENTS            = 0x0020  # indicates at least one more glyph after this one 
+WE_HAVE_AN_X_AND_Y_SCALE   = 0x0040  # Sx, Sy 
+WE_HAVE_A_TWO_BY_TWO       = 0x0080  # t00, t01, t10, t11 
+WE_HAVE_INSTRUCTIONS       = 0x0100  # instructions follow 
+USE_MY_METRICS             = 0x0200  # apply these metrics to parent glyph 
+OVERLAP_COMPOUND           = 0x0400  # used by Apple in GX fonts 
+SCALED_COMPONENT_OFFSET    = 0x0800  # composite designed to have the component offset scaled (designed for Apple) 
+UNSCALED_COMPONENT_OFFSET  = 0x1000  # composite designed not to have the component offset scaled (designed for MS) 
+
+
+class Glyph:
+	
+	def __init__(self, data=""):
+		if not data:
+			# empty char
+			self.numberOfContours = 0
+			return
+		self.data = data
+	
+	def compact(self, glyfTable):
+		data = self.compile(glyfTable)
+		self.__dict__.clear()
+		self.data = data
+	
+	def expand(self, glyfTable):
+		if not hasattr(self, "data"):
+			# already unpacked
+			return
+		dummy, data = sstruct.unpack2(glyphHeaderFormat, self.data, self)
+		del self.data
+		if self.numberOfContours == -1:
+			self.decompileComponents(data, glyfTable)
+		else:
+			self.decompileCoordinates(data)
+	
+	def compile(self, glyfTable):
+		if hasattr(self, "data"):
+			return self.data
+		if self.numberOfContours == 0:
+			return ""
+		self.recalcBounds(glyfTable)
+		data = sstruct.pack(glyphHeaderFormat, self)
+		if self.numberOfContours == -1:
+			data = data + self.compileComponents(glyfTable)
+		else:
+			data = data + self.compileCoordinates()
+		# from the spec: "Note that the local offsets should be word-aligned"
+		if len(data) % 2:
+			# ...so if the length of the data is odd, append a null byte
+			data = data + "\0"
+		return data
+	
+	def toXML(self, writer, ttFont):
+		if self.numberOfContours == -1:
+			for compo in self.components:
+				compo.toXML(writer, ttFont)
+			if hasattr(self, "instructions"):
+				writer.begintag("instructions")
+				writer.newline()
+				writer.dumphex(self.instructions)
+				writer.endtag("instructions")
+				writer.newline()
+		else:
+			last = 0
+			for i in range(self.numberOfContours):
+				writer.begintag("contour")
+				writer.newline()
+				for j in range(last, self.endPtsOfContours[i] + 1):
+					writer.simpletag("pt", [
+							("x", self.coordinates[j][0]), 
+							("y", self.coordinates[j][1]),
+							("on", self.flags[j] & flagOnCurve)])
+					writer.newline()
+				last = self.endPtsOfContours[i] + 1
+				writer.endtag("contour")
+				writer.newline()
+			if self.numberOfContours:
+				writer.begintag("instructions")
+				writer.newline()
+				writer.dumphex(self.instructions)
+				writer.endtag("instructions")
+				writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if name == "contour":
+			self.numberOfContours = self.numberOfContours + 1
+			if self.numberOfContours < 0:
+				raise ttLib.TTLibError, "can't mix composites and contours in glyph"
+			coordinates = []
+			flags = []
+			for element in content:
+				if type(element) == types.StringType:
+					continue
+				name, attrs, content = element
+				if name <> "pt":
+					continue  # ignore anything but "pt"
+				coordinates.append([safeEval(attrs["x"]), safeEval(attrs["y"])])
+				flags.append(not not safeEval(attrs["on"]))
+			coordinates = Numeric.array(coordinates, Numeric.Int16)
+			flags = Numeric.array(flags, Numeric.Int8)
+			if not hasattr(self, "coordinates"):
+				self.coordinates = coordinates
+				self.flags = flags
+				self.endPtsOfContours = [len(coordinates)-1]
+			else:
+				self.coordinates = Numeric.concatenate((self.coordinates, coordinates))
+				self.flags = Numeric.concatenate((self.flags, flags))
+				self.endPtsOfContours.append(len(self.coordinates)-1)
+		elif name == "component":
+			if self.numberOfContours > 0:
+				raise ttLib.TTLibError, "can't mix composites and contours in glyph"
+			self.numberOfContours = -1
+			if not hasattr(self, "components"):
+				self.components = []
+			component = GlyphComponent()
+			self.components.append(component)
+			component.fromXML((name, attrs, content), ttFont)
+		elif name == "instructions":
+			self.instructions = readHex(content)
+	
+	def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
+		assert self.numberOfContours == -1
+		nContours = 0
+		nPoints = 0
+		for compo in self.components:
+			baseGlyph = glyfTable[compo.glyphName]
+			if baseGlyph.numberOfContours == 0:
+				continue
+			elif baseGlyph.numberOfContours > 0:
+				nP, nC = baseGlyph.getMaxpValues()
+			else:
+				nP, nC, maxComponentDepth = baseGlyph.getCompositeMaxpValues(
+						glyfTable, maxComponentDepth + 1)
+			nPoints = nPoints + nP
+			nContours = nContours + nC
+		return nPoints, nContours, maxComponentDepth
+	
+	def getMaxpValues(self):
+		assert self.numberOfContours > 0
+		return len(self.coordinates), len(self.endPtsOfContours)
+	
+	def decompileComponents(self, data, glyfTable):
+		self.components = []
+		more = 1
+		haveInstructions = 0
+		while more:
+			component = GlyphComponent()
+			more, haveInstr, data = component.decompile(data, glyfTable)
+			haveInstructions = haveInstructions | haveInstr
+			self.components.append(component)
+		if haveInstructions:
+			numInstructions, = struct.unpack(">h", data[:2])
+			data = data[2:]
+			self.instructions = data[:numInstructions]
+			data = data[numInstructions:]
+			assert len(data) in (0, 1), "bad composite data"
+	
+	def decompileCoordinates(self, data):
+		endPtsOfContours = array.array("h")
+		endPtsOfContours.fromstring(data[:2*self.numberOfContours])
+		if ttLib.endian <> "big":
+			endPtsOfContours.byteswap()
+		self.endPtsOfContours = endPtsOfContours.tolist()
+		
+		data = data[2*self.numberOfContours:]
+		
+		instructionLength, = struct.unpack(">h", data[:2])
+		data = data[2:]
+		self.instructions = data[:instructionLength]
+		data = data[instructionLength:]
+		nCoordinates = self.endPtsOfContours[-1] + 1
+		flags, xCoordinates, yCoordinates = \
+				self.decompileCoordinatesRaw(nCoordinates, data)
+		
+		# fill in repetitions and apply signs
+		coordinates = Numeric.zeros((nCoordinates, 2), Numeric.Int16)
+		xIndex = 0
+		yIndex = 0
+		for i in range(nCoordinates):
+			flag = flags[i]
+			# x coordinate
+			if flag & flagXShort:
+				if flag & flagXsame:
+					x = xCoordinates[xIndex]
+				else:
+					x = -xCoordinates[xIndex]
+				xIndex = xIndex + 1
+			elif flag & flagXsame:
+				x = 0
+			else:
+				x = xCoordinates[xIndex]
+				xIndex = xIndex + 1
+			# y coordinate
+			if flag & flagYShort:
+				if flag & flagYsame:
+					y = yCoordinates[yIndex]
+				else:
+					y = -yCoordinates[yIndex]
+				yIndex = yIndex + 1
+			elif flag & flagYsame:
+				y = 0
+			else:
+				y = yCoordinates[yIndex]
+				yIndex = yIndex + 1
+			coordinates[i] = (x, y)
+		assert xIndex == len(xCoordinates)
+		assert yIndex == len(yCoordinates)
+		# convert relative to absolute coordinates
+		self.coordinates = Numeric.add.accumulate(coordinates)
+		# discard all flags but for "flagOnCurve"
+		if hasattr(Numeric, "__version__"):
+			self.flags = Numeric.bitwise_and(flags, flagOnCurve).astype(Numeric.Int8)
+		else:
+			self.flags = Numeric.boolean_and(flags, flagOnCurve).astype(Numeric.Int8)
+	
+	def decompileCoordinatesRaw(self, nCoordinates, data):
+		# unpack flags and prepare unpacking of coordinates
+		flags = Numeric.array([0] * nCoordinates, Numeric.Int8)
+		# Warning: deep Python trickery going on. We use the struct module to unpack
+		# the coordinates. We build a format string based on the flags, so we can
+		# unpack the coordinates in one struct.unpack() call.
+		xFormat = ">" # big endian
+		yFormat = ">" # big endian
+		i = j = 0
+		while 1:
+			flag = ord(data[i])
+			i = i + 1
+			repeat = 1
+			if flag & flagRepeat:
+				repeat = ord(data[i]) + 1
+				i = i + 1
+			for k in range(repeat):
+				if flag & flagXShort:
+					xFormat = xFormat + 'B'
+				elif not (flag & flagXsame):
+					xFormat = xFormat + 'h'
+				if flag & flagYShort:
+					yFormat = yFormat + 'B'
+				elif not (flag & flagYsame):
+					yFormat = yFormat + 'h'
+				flags[j] = flag
+				j = j + 1
+			if j >= nCoordinates:
+				break
+		assert j == nCoordinates, "bad glyph flags"
+		data = data[i:]
+		# unpack raw coordinates, krrrrrr-tching!
+		xDataLen = struct.calcsize(xFormat)
+		yDataLen = struct.calcsize(yFormat)
+		if (len(data) - (xDataLen + yDataLen)) not in (0, 1):
+			raise ttLib.TTLibError, "bad glyph record"
+		xCoordinates = struct.unpack(xFormat, data[:xDataLen])
+		yCoordinates = struct.unpack(yFormat, data[xDataLen:xDataLen+yDataLen])
+		return flags, xCoordinates, yCoordinates
+	
+	def compileComponents(self, glyfTable):
+		data = ""
+		lastcomponent = len(self.components) - 1
+		more = 1
+		haveInstructions = 0
+		for i in range(len(self.components)):
+			if i == lastcomponent:
+				haveInstructions = hasattr(self, "instructions")
+				more = 0
+			compo = self.components[i]
+			data = data + compo.compile(more, haveInstructions, glyfTable)
+		if haveInstructions:
+			data = data + struct.pack(">h", len(self.instructions)) + self.instructions
+		return data
+			
+	
+	def compileCoordinates(self):
+		assert len(self.coordinates) == len(self.flags)
+		data = ""
+		endPtsOfContours = array.array("h", self.endPtsOfContours)
+		if ttLib.endian <> "big":
+			endPtsOfContours.byteswap()
+		data = data + endPtsOfContours.tostring()
+		data = data + struct.pack(">h", len(self.instructions))
+		data = data + self.instructions
+		nCoordinates = len(self.coordinates)
+		
+		# make a copy
+		coordinates = self.coordinates.astype(self.coordinates.typecode())
+		# absolute to relative coordinates
+		coordinates[1:] = Numeric.subtract(coordinates[1:], coordinates[:-1])
+		flags = self.flags
+		compressedflags = []
+		xPoints = []
+		yPoints = []
+		xFormat = ">"
+		yFormat = ">"
+		lastflag = None
+		repeat = 0
+		for i in range(len(coordinates)):
+			# Oh, the horrors of TrueType
+			flag = self.flags[i]
+			x, y = coordinates[i]
+			# do x
+			if x == 0:
+				flag = flag | flagXsame
+			elif -255 <= x <= 255:
+				flag = flag | flagXShort
+				if x > 0:
+					flag = flag | flagXsame
+				else:
+					x = -x
+				xPoints.append(x)
+				xFormat = xFormat + 'B'
+			else:
+				xPoints.append(x)
+				xFormat = xFormat + 'h'
+			# do y
+			if y == 0:
+				flag = flag | flagYsame
+			elif -255 <= y <= 255:
+				flag = flag | flagYShort
+				if y > 0:
+					flag = flag | flagYsame
+				else:
+					y = -y
+				yPoints.append(y)
+				yFormat = yFormat + 'B'
+			else:
+				yPoints.append(y)
+				yFormat = yFormat + 'h'
+			# handle repeating flags
+			if flag == lastflag:
+				repeat = repeat + 1
+				if repeat == 1:
+					compressedflags.append(flag)
+				elif repeat > 1:
+					compressedflags[-2] = flag | flagRepeat
+					compressedflags[-1] = repeat
+				else:
+					compressedflags[-1] = repeat
+			else:
+				repeat = 0
+				compressedflags.append(flag)
+			lastflag = flag
+		data = data + array.array("B", compressedflags).tostring()
+		data = data + apply(struct.pack, (xFormat,)+tuple(xPoints))
+		data = data + apply(struct.pack, (yFormat,)+tuple(yPoints))
+		return data
+	
+	def recalcBounds(self, glyfTable):
+		coordinates, endPts, flags = self.getCoordinates(glyfTable)
+		self.xMin, self.yMin = Numeric.minimum.reduce(coordinates)
+		self.xMax, self.yMax = Numeric.maximum.reduce(coordinates)
+	
+	def getCoordinates(self, glyfTable):
+		if self.numberOfContours > 0:
+			return self.coordinates, self.endPtsOfContours, self.flags
+		elif self.numberOfContours == -1:
+			# it's a composite
+			allCoords = None
+			allFlags = None
+			allEndPts = None
+			for compo in self.components:
+				g = glyfTable[compo.glyphName]
+				coordinates, endPts, flags = g.getCoordinates(glyfTable)
+				if hasattr(compo, "firstpt"):
+					# move according to two reference points
+					move = allCoords[compo.firstpt] - coordinates[compo.secondpt]
+				else:
+					move = compo.x, compo.y
+				
+				if not hasattr(compo, "transform"):
+					coordinates = coordinates + move  # I love NumPy!
+				else:
+					apple_way = compo.flags & SCALED_COMPONENT_OFFSET
+					ms_way = compo.flags & UNSCALED_COMPONENT_OFFSET
+					assert not (apple_way and ms_way)
+					if not (apple_way or ms_way):
+						scale_component_offset = SCALE_COMPONENT_OFFSET_DEFAULT  # see top of this file
+					else:
+						scale_component_offset = apple_way
+					if scale_component_offset:
+						# the Apple way: first move, then scale (ie. scale the component offset)
+						coordinates = coordinates + move
+						coordinates = Numeric.dot(coordinates, compo.transform)
+					else:
+						# the MS way: first scale, then move
+						coordinates = Numeric.dot(coordinates, compo.transform)
+						coordinates = coordinates + move
+					# due to the transformation the coords. are now floats;
+					# round them off nicely, and cast to short
+					coordinates = Numeric.floor(coordinates + 0.5).astype(Numeric.Int16)
+				if allCoords is None:
+					allCoords = coordinates
+					allEndPts = endPts
+					allFlags = flags
+				else:
+					allEndPts = allEndPts + (Numeric.array(endPts) + len(allCoords)).tolist()
+					allCoords = Numeric.concatenate((allCoords, coordinates))
+					allFlags = Numeric.concatenate((allFlags, flags))
+			return allCoords, allEndPts, allFlags
+		else:
+			return Numeric.array([], Numeric.Int16), [], Numeric.array([], Numeric.Int8)
+	
+	def __cmp__(self, other):
+		if self.numberOfContours <= 0:
+			return cmp(self.__dict__, other.__dict__)
+		else:
+			if cmp(len(self.coordinates), len(other.coordinates)):
+				return 1
+			ctest = Numeric.alltrue(Numeric.alltrue(Numeric.equal(self.coordinates, other.coordinates)))
+			ftest = Numeric.alltrue(Numeric.equal(self.flags, other.flags))
+			if not ctest or not ftest:
+				return 1
+			return (
+					cmp(self.endPtsOfContours, other.endPtsOfContours) or
+					cmp(self.instructions, other.instructions)
+				)
+
+
+class GlyphComponent:
+	
+	def __init__(self):
+		pass
+	
+	def decompile(self, data, glyfTable):
+		flags, glyphID = struct.unpack(">HH", data[:4])
+		self.flags = int(flags)
+		glyphID = int(glyphID)
+		self.glyphName = glyfTable.getGlyphName(int(glyphID))
+		#print ">>", reprflag(self.flags)
+		data = data[4:]
+		
+		if self.flags & ARG_1_AND_2_ARE_WORDS:
+			if self.flags & ARGS_ARE_XY_VALUES:
+				self.x, self.y = struct.unpack(">hh", data[:4])
+			else:
+				x, y = struct.unpack(">HH", data[:4])
+				self.firstpt, self.secondpt = int(x), int(y)
+			data = data[4:]
+		else:
+			if self.flags & ARGS_ARE_XY_VALUES:
+				self.x, self.y = struct.unpack(">bb", data[:2])
+			else:
+				x, y = struct.unpack(">BB", data[:4])
+				self.firstpt, self.secondpt = int(x), int(y)
+			data = data[2:]
+		
+		if self.flags & WE_HAVE_A_SCALE:
+			scale, = struct.unpack(">h", data[:2])
+			self.transform = Numeric.array(
+					[[scale, 0], [0, scale]]) / float(0x4000)  # fixed 2.14
+			data = data[2:]
+		elif self.flags & WE_HAVE_AN_X_AND_Y_SCALE:
+			xscale, yscale = struct.unpack(">hh", data[:4])
+			self.transform = Numeric.array(
+					[[xscale, 0], [0, yscale]]) / float(0x4000)  # fixed 2.14
+			data = data[4:]
+		elif self.flags & WE_HAVE_A_TWO_BY_TWO:
+			(xscale, scale01, 
+					scale10, yscale) = struct.unpack(">hhhh", data[:8])
+			self.transform = Numeric.array(
+					[[xscale, scale01], [scale10, yscale]]) / float(0x4000)  # fixed 2.14
+			data = data[8:]
+		more = self.flags & MORE_COMPONENTS
+		haveInstructions = self.flags & WE_HAVE_INSTRUCTIONS
+		self.flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS | 
+				SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET |
+				NON_OVERLAPPING)
+		return more, haveInstructions, data
+	
+	def compile(self, more, haveInstructions, glyfTable):
+		data = ""
+		
+		# reset all flags we will calculate ourselves
+		flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS | 
+				SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET |
+				NON_OVERLAPPING)
+		if more:
+			flags = flags | MORE_COMPONENTS
+		if haveInstructions:
+			flags = flags | WE_HAVE_INSTRUCTIONS
+		
+		if hasattr(self, "firstpt"):
+			if (0 <= self.firstpt <= 255) and (0 <= self.secondpt <= 255):
+				data = data + struct.pack(">BB", self.firstpt, self.secondpt)
+			else:
+				data = data + struct.pack(">HH", self.firstpt, self.secondpt)
+				flags = flags | ARG_1_AND_2_ARE_WORDS
+		else:
+			flags = flags | ARGS_ARE_XY_VALUES
+			if (-128 <= self.x <= 127) and (-128 <= self.y <= 127):
+				data = data + struct.pack(">bb", self.x, self.y)
+			else:
+				data = data + struct.pack(">hh", self.x, self.y)
+				flags = flags | ARG_1_AND_2_ARE_WORDS
+		
+		if hasattr(self, "transform"):
+			# XXX needs more testing
+			transform = Numeric.floor(self.transform * 0x4000 + 0.5)
+			if transform[0][1] or transform[1][0]:
+				flags = flags | WE_HAVE_A_TWO_BY_TWO
+				data = data + struct.pack(">hhhh", 
+						transform[0][0], transform[0][1],
+						transform[1][0], transform[1][1])
+			elif transform[0][0] <> transform[1][1]:
+				flags = flags | WE_HAVE_AN_X_AND_Y_SCALE
+				data = data + struct.pack(">hh", 
+						transform[0][0], transform[1][1])
+			else:
+				flags = flags | WE_HAVE_A_SCALE
+				data = data + struct.pack(">h", 
+						transform[0][0])
+		
+		glyphID = glyfTable.getGlyphID(self.glyphName)
+		return struct.pack(">HH", flags, glyphID) + data
+	
+	def toXML(self, writer, ttFont):
+		attrs = [("glyphName", self.glyphName)]
+		if not hasattr(self, "firstpt"):
+			attrs = attrs + [("x", self.x), ("y", self.y)]
+		else:
+			attrs = attrs + [("firstpt", self.firstpt), ("secondpt", self.secondpt)]
+		
+		if hasattr(self, "transform"):
+			# XXX needs more testing
+			transform = self.transform
+			if transform[0][1] or transform[1][0]:
+				attrs = attrs + [
+						("scalex", transform[0][0]), ("scale01", transform[0][1]),
+						("scale10", transform[1][0]), ("scaley", transform[1][1]),
+						]
+			elif transform[0][0] <> transform[1][1]:
+				attrs = attrs + [
+						("scalex", transform[0][0]), ("scaley", transform[1][1]),
+						]
+			else:
+				attrs = attrs + [("scale", transform[0][0])]
+		attrs = attrs + [("flags", hex(self.flags))]
+		writer.simpletag("component", attrs)
+		writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		self.glyphName = attrs["glyphName"]
+		if attrs.has_key("firstpt"):
+			self.firstpt = safeEval(attrs["firstpt"])
+			self.secondpt = safeEval(attrs["secondpt"])
+		else:
+			self.x = safeEval(attrs["x"])
+			self.y = safeEval(attrs["y"])
+		if attrs.has_key("scale01"):
+			scalex = safeEval(attrs["scalex"])
+			scale01 = safeEval(attrs["scale01"])
+			scale10 = safeEval(attrs["scale10"])
+			scaley = safeEval(attrs["scaley"])
+			self.transform = Numeric.array([[scalex, scale01], [scale10, scaley]])
+		elif attrs.has_key("scalex"):
+			scalex = safeEval(attrs["scalex"])
+			scaley = safeEval(attrs["scaley"])
+			self.transform = Numeric.array([[scalex, 0], [0, scaley]])
+		elif attrs.has_key("scale"):
+			scale = safeEval(attrs["scale"])
+			self.transform = Numeric.array([[scale, 0], [0, scale]])
+		self.flags = safeEval(attrs["flags"])
+	
+	def __cmp__(self, other):
+		if hasattr(self, "transform"):
+			if Numeric.alltrue(Numeric.equal(self.transform, other.transform)):
+				selfdict = self.__dict__.copy()
+				otherdict = other.__dict__.copy()
+				del selfdict["transform"]
+				del otherdict["transform"]
+				return cmp(selfdict, otherdict)
+			else:
+				return 1
+		else:
+			return cmp(self.__dict__, other.__dict__)
+
+
+def reprflag(flag):
+	bin = ""
+	if type(flag) == types.StringType:
+		flag = ord(flag)
+	while flag:
+		if flag & 0x01:
+			bin = "1" + bin
+		else:
+			bin = "0" + bin
+		flag = flag >> 1
+	bin = (14 - len(bin)) * "0" + bin
+	return bin
+
diff --git a/Lib/fontTools/ttLib/tables/_h_d_m_x.py b/Lib/fontTools/ttLib/tables/_h_d_m_x.py
new file mode 100644
index 0000000..e5c396c
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_h_d_m_x.py
@@ -0,0 +1,95 @@
+import DefaultTable
+import sstruct
+import string
+
+hdmxHeaderFormat = """
+	version:	H
+	numRecords:	H
+	recordSize:	l
+"""
+
+class table__h_d_m_x(DefaultTable.DefaultTable):
+	
+	def decompile(self, data, ttFont):
+		numGlyphs = ttFont['maxp'].numGlyphs
+		glyphOrder = ttFont.getGlyphOrder()
+		dummy, data = sstruct.unpack2(hdmxHeaderFormat, data, self)
+		self.hdmx = {}
+		for i in range(self.numRecords):
+			ppem = ord(data[0])
+			maxSize = ord(data[1])
+			widths = {}
+			for glyphID in range(numGlyphs):
+				widths[glyphOrder[glyphID]] = ord(data[glyphID+2])
+			self.hdmx[ppem] = widths
+			data = data[self.recordSize:]
+		assert len(data) == 0, "too much hdmx data"
+	
+	def compile(self, ttFont):
+		self.version = 0
+		numGlyphs = ttFont['maxp'].numGlyphs
+		glyphOrder = ttFont.getGlyphOrder()
+		self.recordSize = 4 * ((2 + numGlyphs + 3) / 4)
+		pad = (self.recordSize - 2 - numGlyphs) * "\0"
+		self.numRecords = len(self.hdmx)
+		data = sstruct.pack(hdmxHeaderFormat, self)
+		items = self.hdmx.items()
+		items.sort()
+		for ppem, widths in items:
+			data = data + chr(ppem) + chr(max(widths.values()))
+			for glyphID in range(len(glyphOrder)):
+				width = widths[glyphOrder[glyphID]]
+				data = data + chr(width)
+			data = data + pad
+		return data
+	
+	def toXML(self, writer, ttFont):
+		writer.begintag("hdmxData")
+		writer.newline()
+		ppems = self.hdmx.keys()
+		ppems.sort()
+		records = []
+		format = ""
+		for ppem in ppems:
+			widths = self.hdmx[ppem]
+			records.append(widths)
+			format = format + "%4d"
+		glyphNames = ttFont.getGlyphOrder()[:]
+		glyphNames.sort()
+		maxNameLen = max(map(len, glyphNames))
+		format = "%" + `maxNameLen` + 's:' + format + ' ;'
+		writer.write(format % (("ppem",) + tuple(ppems)))
+		writer.newline()
+		writer.newline()
+		for glyphName in glyphNames:
+			row = []
+			for ppem in ppems:
+				widths = self.hdmx[ppem]
+				row.append(widths[glyphName])
+			writer.write(format % ((glyphName,) + tuple(row)))
+			writer.newline()
+		writer.endtag("hdmxData")
+		writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if name <> "hdmxData":
+			return
+		content = string.join(content, " ")
+		lines = string.split(content, ";")
+		topRow = string.split(lines[0])
+		assert topRow[0] == "ppem:", "illegal hdmx format"
+		ppems = map(int, topRow[1:])
+		self.hdmx = hdmx = {}
+		for ppem in ppems:
+			hdmx[ppem] = {}
+		lines = map(string.split, lines[1:])
+		for line in lines:
+			if not line:
+				continue
+			assert line[0][-1] == ":", "illegal hdmx format"
+			glyphName = line[0][:-1]
+			line = map(int, line[1:])
+			assert len(line) == len(ppems), "illegal hdmx format"
+			for i in range(len(ppems)):
+				hdmx[ppems[i]][glyphName] = line[i]
+
diff --git a/Lib/fontTools/ttLib/tables/_h_e_a_d.py b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
new file mode 100644
index 0000000..fc33edb
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
@@ -0,0 +1,136 @@
+import DefaultTable
+import sstruct
+import time
+import string
+import calendar
+from fontTools.misc.textTools import safeEval, num2binary, binary2num
+
+
+headFormat = """
+		>	# big endian
+		tableVersion:		16.16F
+		fontRevision:		16.16F
+		checkSumAdjustment:	l
+		magicNumber:		l
+							x	# pad byte
+		flags:				b
+		unitsPerEm:			H
+		created:			8s
+		modified:			8s
+		xMin:				h
+		yMin:				h
+		xMax:				h
+		yMax:				h
+		macStyle:			H
+		lowestRecPPEM:		H
+		fontDirectionHint:	h
+		indexToLocFormat:	h
+		glyphDataFormat:	h
+"""
+
+class table__h_e_a_d(DefaultTable.DefaultTable):
+	
+	dependencies = ['maxp', 'loca']
+	
+	def decompile(self, data, ttFont):
+		sstruct.unpack(headFormat, data, self)
+		self.unitsPerEm = int(self.unitsPerEm)
+		self.strings2dates()
+	
+	def compile(self, ttFont):
+		self.modified = long(time.time() - mac_epoch_diff)
+		self.dates2strings()
+		data = sstruct.pack(headFormat, self)
+		self.strings2dates()
+		return data
+	
+	def strings2dates(self):
+		self.created = bin2long(self.created)
+		self.modified = bin2long(self.modified)
+	
+	def dates2strings(self):
+		self.created = long2bin(self.created)
+		self.modified = long2bin(self.modified)
+	
+	def toXML(self, writer, ttFont):
+		writer.comment("Most of this table will be recalculated by the compiler")
+		writer.newline()
+		formatstring, names, fixes = sstruct.getformat(headFormat)
+		for name in names:
+			value = getattr(self, name)
+			if name in ("created", "modified"):
+				value = time.asctime(time.gmtime(max(0, value + mac_epoch_diff)))
+			if type(value) == type(0L):
+				value=int(value)
+			if name in ("magicNumber", "checkSumAdjustment"):
+				value = hex(value)
+			elif name == "flags":
+				value = num2binary(value, 16)
+			writer.simpletag(name, value=value)
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		value = attrs["value"]
+		if name in ("created", "modified"):
+			value = parse_date(value) - mac_epoch_diff
+		elif name == "flags":
+			value = binary2num(value)
+		else:
+			value = safeEval(value)
+		setattr(self, name, value)
+	
+	def __cmp__(self, other):
+		selfdict = self.__dict__.copy()
+		otherdict = other.__dict__.copy()
+		# for testing purposes, compare without the modified and checkSumAdjustment
+		# fields, since they are allowed to be different.
+		for key in ["modified", "checkSumAdjustment"]:
+			del selfdict[key]
+			del otherdict[key]
+		return cmp(selfdict, otherdict)
+
+
+def calc_mac_epoch_diff():
+	"""calculate the difference between the original Mac epoch (1904)
+	to the epoch on this machine.
+	"""
+	safe_epoch_t = (1971, 1, 1, 0, 0, 0, 0, 0, 0)
+	safe_epoch = time.mktime(safe_epoch_t) - time.timezone
+	assert time.gmtime(safe_epoch)[:6] == safe_epoch_t[:6]
+	seconds1904to1971 = 60 * 60 * 24 * (365 * (1971-1904) + 17) # thanks, Laurence!
+	return long(safe_epoch - seconds1904to1971)
+
+mac_epoch_diff = calc_mac_epoch_diff()
+
+
+_months = map(string.lower, calendar.month_abbr)
+_weekdays = map(string.lower, calendar.day_abbr)
+
+def parse_date(datestring):
+	datestring = string.lower(datestring)
+	weekday, month, day, tim, year = string.split(datestring)
+	weekday = _weekdays.index(weekday)
+	month = _months.index(month)
+	year = int(year)
+	day = int(day)
+	hour, minute, second = map(int, string.split(tim, ":"))
+	t = (year, month, day, hour, minute, second, weekday, 0, 0)
+	return long(time.mktime(t) - time.timezone)
+
+
+def bin2long(data):
+	# thanks </F>!
+	v = 0L
+	for i in map(ord, data):
+	    v = v<<8 | i
+	return v
+
+def long2bin(v, bytes=8):
+	data = ""
+	while v:
+		data = chr(v & 0xff) + data
+		v = v >> 8
+	data = (bytes - len(data)) * "\0" + data
+	assert len(data) == 8, "long too long"
+	return data
+
diff --git a/Lib/fontTools/ttLib/tables/_h_h_e_a.py b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
new file mode 100644
index 0000000..fca75cb
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
@@ -0,0 +1,78 @@
+import DefaultTable
+import sstruct
+from fontTools.misc.textTools import safeEval
+
+hheaFormat = """
+		>	# big endian
+		tableVersion:			16.16F
+		ascent:					h
+		descent:				h
+		lineGap:				h
+		advanceWidthMax:		H
+		minLeftSideBearing:		h
+		minRightSideBearing:	h
+		xMaxExtent:				h
+		caretSlopeRise:			h
+		caretSlopeRun:			h
+		reserved0:				h
+		reserved1:				h
+		reserved2:				h
+		reserved3:				h
+		reserved4:				h
+		metricDataFormat:		h
+		numberOfHMetrics:		H
+"""
+
+class table__h_h_e_a(DefaultTable.DefaultTable):
+	
+	dependencies = ['hmtx', 'glyf']
+	
+	def decompile(self, data, ttFont):
+		sstruct.unpack(hheaFormat, data, self)
+	
+	def compile(self, ttFont):
+		self.recalc(ttFont)
+		return sstruct.pack(hheaFormat, self)
+	
+	def recalc(self, ttFont):
+		hmtxTable = ttFont['hmtx']
+		if ttFont.has_key('glyf'):
+			if not ttFont.isLoaded('glyf'):
+				return
+			glyfTable = ttFont['glyf']
+			advanceWidthMax = -100000    # arbitrary big negative number
+			minLeftSideBearing = 100000  # arbitrary big number
+			minRightSideBearing = 100000 # arbitrary big number
+			xMaxExtent = -100000         # arbitrary big negative number
+			
+			for name in ttFont.getGlyphOrder():
+				width, lsb = hmtxTable[name]
+				g = glyfTable[name]
+				if g.numberOfContours <= 0:
+					continue
+				advanceWidthMax = max(advanceWidthMax, width)
+				minLeftSideBearing = min(minLeftSideBearing, lsb)
+				rsb = width - lsb - (g.xMax - g.xMin)
+				minRightSideBearing = min(minRightSideBearing, rsb)
+				extent = lsb + (g.xMax - g.xMin)
+				xMaxExtent = max(xMaxExtent, extent)
+			self.advanceWidthMax = advanceWidthMax
+			self.minLeftSideBearing = minLeftSideBearing
+			self.minRightSideBearing = minRightSideBearing
+			self.xMaxExtent = xMaxExtent
+		else:
+			# XXX CFF recalc...
+			pass
+	
+	def toXML(self, writer, ttFont):
+		formatstring, names, fixes = sstruct.getformat(hheaFormat)
+		for name in names:
+			value = getattr(self, name)
+			if type(value) == type(0L):
+				value = int(value)
+			writer.simpletag(name, value=value)
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		setattr(self, name, safeEval(attrs["value"]))
+
diff --git a/Lib/fontTools/ttLib/tables/_h_m_t_x.py b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
new file mode 100644
index 0000000..35fa8cd
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
@@ -0,0 +1,94 @@
+import DefaultTable
+import Numeric
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+
+
+class table__h_m_t_x(DefaultTable.DefaultTable):
+	
+	headerTag = 'hhea'
+	advanceName = 'width'
+	sideBearingName = 'lsb'
+	numberOfMetricsName = 'numberOfHMetrics'
+	
+	def decompile(self, data, ttFont):
+		numberOfMetrics = int(getattr(ttFont[self.headerTag], self.numberOfMetricsName))
+		metrics = Numeric.fromstring(data[:4 * numberOfMetrics], 
+				Numeric.Int16)
+		if ttLib.endian <> "big":
+			metrics = metrics.byteswapped()
+		metrics.shape = (numberOfMetrics, 2)
+		data = data[4 * numberOfMetrics:]
+		numberOfSideBearings = ttFont['maxp'].numGlyphs - numberOfMetrics
+		numberOfSideBearings = int(numberOfSideBearings)
+		if numberOfSideBearings:
+			assert numberOfSideBearings > 0, "bad hmtx/vmtx table"
+			lastAdvance = metrics[-1][0]
+			advances = Numeric.array([lastAdvance] * numberOfSideBearings, 
+					Numeric.Int16)
+			sideBearings = Numeric.fromstring(data[:2 * numberOfSideBearings], 
+					Numeric.Int16)
+			if ttLib.endian <> "big":
+				sideBearings = sideBearings.byteswapped()
+			data = data[2 * numberOfSideBearings:]
+			additionalMetrics = Numeric.array([advances, sideBearings], 
+					Numeric.Int16)
+			metrics = Numeric.concatenate((metrics, 
+					Numeric.transpose(additionalMetrics)))
+		if data:
+			raise  ttLib.TTLibError, "too much data for hmtx/vmtx table"
+		metrics = metrics.tolist()
+		self.metrics = {}
+		for i in range(len(metrics)):
+			glyphName = ttFont.getGlyphName(i)
+			self.metrics[glyphName] = metrics[i]
+	
+	def compile(self, ttFont):
+		metrics = []
+		for glyphName in ttFont.getGlyphOrder():
+			metrics.append(self.metrics[glyphName])
+		lastAdvance = metrics[-1][0]
+		lastIndex = len(metrics)
+		while metrics[lastIndex-2][0] == lastAdvance:
+			lastIndex = lastIndex - 1
+		additionalMetrics = metrics[lastIndex:]
+		additionalMetrics = map(lambda (advance, sb): sb, additionalMetrics)
+		metrics = metrics[:lastIndex]
+		setattr(ttFont[self.headerTag], self.numberOfMetricsName, len(metrics))
+		
+		metrics = Numeric.array(metrics, Numeric.Int16)
+		if ttLib.endian <> "big":
+			metrics = metrics.byteswapped()
+		data = metrics.tostring()
+		
+		additionalMetrics = Numeric.array(additionalMetrics, Numeric.Int16)
+		if ttLib.endian <> "big":
+			additionalMetrics = additionalMetrics.byteswapped()
+		data = data + additionalMetrics.tostring()
+		return data
+	
+	def toXML(self, writer, ttFont):
+		names = self.metrics.keys()
+		names.sort()
+		for glyphName in names:
+			advance, sb = self.metrics[glyphName]
+			writer.simpletag("mtx", [
+					("name", glyphName), 
+					(self.advanceName, advance), 
+					(self.sideBearingName, sb),
+					])
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if not hasattr(self, "metrics"):
+			self.metrics = {}
+		if name == "mtx":
+			self.metrics[attrs["name"]] = [safeEval(attrs[self.advanceName]), 
+					safeEval(attrs[self.sideBearingName])]
+	
+	def __getitem__(self, glyphName):
+		return self.metrics[glyphName]
+	
+	def __setitem__(self, glyphName, (advance, sb)):
+		self.metrics[glyphName] = advance, sb
+
diff --git a/Lib/fontTools/ttLib/tables/_k_e_r_n.py b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
new file mode 100644
index 0000000..283c52f
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
@@ -0,0 +1,186 @@
+import DefaultTable
+import struct
+import ttLib.sfnt
+from fontTools.misc.textTools import safeEval, readHex
+
+
+class table__k_e_r_n(DefaultTable.DefaultTable):
+	
+	def getkern(self, format):
+		for subtable in self.kernTables:
+			if subtable.version == format:
+				return subtable
+		return None  # not found
+	
+	def decompile(self, data, ttFont):
+		version, nTables = struct.unpack(">HH", data[:4])
+		if version == 1:
+			# Apple's new format. Hm.
+			version, nTables = struct.unpack(">ll", data[:8])
+			self.version = version / float(0x10000)
+			data = data[8:]
+		else:
+			self.version = version
+			data = data[4:]
+		tablesIndex = []
+		self.kernTables = []
+		for i in range(nTables):
+			version, length = struct.unpack(">HH", data[:4])
+			length = int(length)
+			if not kern_classes.has_key(version):
+				subtable = KernTable_format_unkown()
+			else:
+				subtable = kern_classes[version]()
+			subtable.decompile(data[:length], ttFont)
+			self.kernTables.append(subtable)
+			data = data[length:]
+	
+	def compile(self, ttFont):
+		nTables = len(self.kernTables)
+		if self.version == 1.0:
+			# Apple's new format.
+			data = struct.pack(">ll", self.version * 0x1000, nTables)
+		else:
+			data = struct.pack(">HH", self.version, nTables)
+		for subtable in self.kernTables:
+			data = data + subtable.compile(ttFont)
+		return data
+	
+	def toXML(self, writer, ttFont):
+		writer.simpletag("version", value=self.version)
+		writer.newline()
+		for subtable in self.kernTables:
+			subtable.toXML(writer, ttFont)
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if name == "version":
+			self.version = safeEval(attrs["value"])
+			return
+		if name <> "kernsubtable":
+			return
+		if not hasattr(self, "kernTables"):
+			self.kernTables = []
+		format = safeEval(attrs["format"])
+		if not kern_classes.has_key(format):
+			subtable = KernTable_format_unkown()
+		else:
+			subtable = kern_classes[format]()
+		self.kernTables.append(subtable)
+		subtable.fromXML((name, attrs, content), ttFont)
+
+
+class KernTable_format_0:
+	
+	def decompile(self, data, ttFont):
+		version, length, coverage = struct.unpack(">HHH", data[:6])
+		self.version, self.coverage = int(version), int(coverage)
+		data = data[6:]
+		
+		self.kernTable = kernTable = {}
+		
+		nPairs, searchRange, entrySelector, rangeShift = struct.unpack(">HHHH", data[:8])
+		data = data[8:]
+		
+		for k in range(nPairs):
+			left, right, value = struct.unpack(">HHh", data[:6])
+			data = data[6:]
+			left, right = int(left), int(right)
+			kernTable[(ttFont.getGlyphName(left), ttFont.getGlyphName(right))] = value
+		assert len(data) == 0
+	
+	def compile(self, ttFont):
+		nPairs = len(self.kernTable)
+		entrySelector = ttLib.sfnt.maxpoweroftwo(nPairs)
+		searchRange = (2 ** entrySelector) * 6
+		rangeShift = (nPairs - (2 ** entrySelector)) * 6
+		data = struct.pack(">HHHH", nPairs, searchRange, entrySelector, rangeShift)
+		
+		# yeehee! (I mean, turn names into indices)
+		kernTable = map(lambda ((left, right), value), getGlyphID=ttFont.getGlyphID:
+					(getGlyphID(left), getGlyphID(right), value), 
+				self.kernTable.items())
+		kernTable.sort()
+		for left, right, value in kernTable:
+			data = data + struct.pack(">HHh", left, right, value)
+		return struct.pack(">HHH", self.version, len(data) + 6, self.coverage) + data
+	
+	def toXML(self, writer, ttFont):
+		writer.begintag("kernsubtable", coverage=self.coverage, format=0)
+		writer.newline()
+		items = self.kernTable.items()
+		items.sort()
+		for (left, right), value in items:
+			writer.simpletag("pair", [
+					("l", left),
+					("r", right),
+					("v", value)
+					])
+			writer.newline()
+		writer.endtag("kernsubtable")
+		writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		self.coverage = safeEval(attrs["coverage"])
+		self.version = safeEval(attrs["format"])
+		if not hasattr(self, "kernTable"):
+			self.kernTable = {}
+		for element in content:
+			if type(element) <> type(()):
+				continue
+			name, attrs, content = element
+			self.kernTable[(attrs["l"], attrs["r"])] = safeEval(attrs["v"])
+	
+	def __getitem__(self, pair):
+		return self.kernTable[pair]
+	
+	def __setitem__(self, pair, value):
+		self.kernTable[pair] = value
+	
+	def __delitem__(self, pair):
+		del self.kernTable[pair]
+	
+	def __cmp__(self, other):
+		return cmp(self.__dict__, other.__dict__)
+
+
+class KernTable_format_2:
+	
+	def decompile(self, data, ttFont):
+		self.data = data
+	
+	def compile(self, ttFont, ttFont):
+		return data
+	
+	def toXML(self, writer):
+		writer.begintag("kernsubtable", format=2)
+		writer.newline()
+		writer.dumphex(self.data)
+		writer.endtag("kernsubtable")
+		writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		self.decompile(readHex(content))
+
+
+class KernTable_format_unkown:
+	
+	def decompile(self, data, ttFont):
+		self.data = data
+	
+	def compile(self, ttFont):
+		return data
+	
+	def toXML(self, writer, ttFont):
+		writer.begintag("kernsubtable", format="-1")
+		writer.newline()
+		writer.comment("unknown 'kern' subtable format")
+		writer.dumphex(self.data)
+		writer.endtag("kernsubtable")
+		writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		self.decompile(readHex(content))
+
+
+
+kern_classes = {0: KernTable_format_0, 1: KernTable_format_2}
diff --git a/Lib/fontTools/ttLib/tables/_l_o_c_a.py b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
new file mode 100644
index 0000000..332a708
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
@@ -0,0 +1,55 @@
+import DefaultTable
+import array
+import Numeric
+from fontTools import ttLib
+import struct
+
+class table__l_o_c_a(DefaultTable.DefaultTable):
+	
+	dependencies = ['glyf']
+	
+	def decompile(self, data, ttFont):
+		longFormat = ttFont['head'].indexToLocFormat
+		if longFormat:
+			format = "l"
+		else:
+			format = "H"
+		locations = array.array(format)
+		locations.fromstring(data)
+		if ttLib.endian <> "big":
+			locations.byteswap()
+		locations = Numeric.array(locations, Numeric.Int32)
+		if not longFormat:
+			locations = locations * 2
+		if len(locations) <> (ttFont['maxp'].numGlyphs + 1):
+			raise  ttLib.TTLibError, "corrupt 'loca' table"
+		self.locations = locations
+	
+	def compile(self, ttFont):
+		locations = self.locations
+		if max(locations) < 0x20000:
+			locations = locations / 2
+			locations = locations.astype(Numeric.Int16)
+			ttFont['head'].indexToLocFormat = 0
+		else:
+			ttFont['head'].indexToLocFormat = 1
+		if ttLib.endian <> "big":
+			locations = locations.byteswapped()
+		return locations.tostring()
+	
+	def set(self, locations):
+		self.locations = Numeric.array(locations, Numeric.Int32)
+	
+	def toXML(self, writer, ttFont):
+		writer.comment("The 'loca' table will be calculated by the compiler")
+		writer.newline()
+	
+	def __getitem__(self, index):
+		return self.locations[index]
+	
+	def __len__(self):
+		return len(self.locations)
+	
+	def __cmp__(self, other):
+		return cmp(len(self), len(other)) or not Numeric.alltrue(Numeric.equal(self.locations, other.locations))
+
diff --git a/Lib/fontTools/ttLib/tables/_m_a_x_p.py b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
new file mode 100644
index 0000000..1c75e6d
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
@@ -0,0 +1,138 @@
+import DefaultTable
+import sstruct
+from fontTools.misc.textTools import safeEval
+
+maxpFormat_0_5 = """
+		>	# big endian
+		tableVersion:			i
+		numGlyphs:				H
+"""
+
+maxpFormat_1_0_add = """
+		>	# big endian
+		maxPoints:				H
+		maxContours:			H
+		maxCompositePoints:		H
+		maxCompositeContours:	H
+		maxZones:				H
+		maxTwilightPoints:		H
+		maxStorage:				H
+		maxFunctionDefs:		H
+		maxInstructionDefs:		H
+		maxStackElements:		H
+		maxSizeOfInstructions:	H
+		maxComponentElements:	H
+		maxComponentDepth:		H
+"""
+
+
+class table__m_a_x_p(DefaultTable.DefaultTable):
+	
+	dependencies = ['glyf']
+	
+	def decompile(self, data, ttFont):
+		dummy, data = sstruct.unpack2(maxpFormat_0_5, data, self)
+		self.numGlyphs = int(self.numGlyphs)
+		if self.tableVersion == 0x00010000:
+			dummy, data = sstruct.unpack2(maxpFormat_1_0_add, data, self)
+		else:
+			assert self.tableVersion == 0x00005000, "unknown 'maxp' format: %x" % self.tableVersion
+		assert len(data) == 0
+	
+	def compile(self, ttFont):
+		if ttFont.has_key('glyf'):
+			if ttFont.isLoaded('glyf'):
+				self.recalc(ttFont)
+		else:
+			pass # XXX CFF!!!
+		data = sstruct.pack(maxpFormat_0_5, self)
+		if self.tableVersion == 0x00010000:
+			data = data + sstruct.pack(maxpFormat_1_0_add, self)
+		else:
+			assert self.tableVersion == 0x00005000, "unknown 'maxp' format: %f" % self.tableVersion
+		return data
+	
+	def recalc(self, ttFont):
+		"""Recalculate the font bounding box, and most other maxp values except
+		for the TT instructions values. Also recalculate the value of bit 1
+		of the flags field of the 'head' table.
+		"""
+		glyfTable = ttFont['glyf']
+		hmtxTable = ttFont['hmtx']
+		headTable = ttFont['head']
+		self.numGlyphs = len(glyfTable)
+		xMin = 100000
+		yMin = 100000
+		xMax = -100000
+		yMax = -100000
+		maxPoints = 0
+		maxContours = 0
+		maxCompositePoints = 0
+		maxCompositeContours = 0
+		maxComponentElements = 0
+		maxComponentDepth = 0
+		allXMaxIsLsb = 1
+		for glyphName in ttFont.getGlyphOrder():
+			g = glyfTable[glyphName]
+			if g.numberOfContours:
+				if hmtxTable[glyphName][1] <> g.xMin:
+					allXMaxIsLsb = 0
+				xMin = min(xMin, g.xMin)
+				yMin = min(yMin, g.yMin)
+				xMax = max(xMax, g.xMax)
+				yMax = max(yMax, g.yMax)
+				if g.numberOfContours > 0:
+					nPoints, nContours = g.getMaxpValues()
+					maxPoints = max(maxPoints, nPoints)
+					maxContours = max(maxContours, nContours)
+				else:
+					nPoints, nContours, componentDepth = g.getCompositeMaxpValues(glyfTable)
+					maxCompositePoints = max(maxCompositePoints, nPoints)
+					maxCompositeContours = max(maxCompositeContours, nContours)
+					maxComponentElements = max(maxComponentElements, len(g.components))
+					maxComponentDepth = max(maxComponentDepth, componentDepth)
+		self.xMin = xMin
+		self.yMin = yMin
+		self.xMax = xMax
+		self.yMax = yMax
+		self.maxPoints = maxPoints
+		self.maxContours = maxContours
+		self.maxCompositePoints = maxCompositePoints
+		self.maxCompositeContours = maxCompositeContours
+		self.maxComponentDepth = maxComponentDepth
+		if allXMaxIsLsb:
+			headTable.flags = headTable.flags | 0x2
+		else:
+			headTable.flags = headTable.flags & ~0x2
+	
+	def testrepr(self):
+		items = self.__dict__.items()
+		items.sort()
+		print ". . . . . . . . ."
+		for combo in items:
+			print "  %s: %s" % combo
+		print ". . . . . . . . ."
+	
+	def toXML(self, writer, ttFont):
+		if self.tableVersion <> 0x00005000:
+			writer.comment("Most of this table will be recalculated by the compiler")
+			writer.newline()
+		formatstring, names, fixes = sstruct.getformat(maxpFormat_0_5)
+		if self.tableVersion == 0x00010000:
+			formatstring, names_1_0, fixes = sstruct.getformat(maxpFormat_1_0_add)
+			names = names + names_1_0
+		else:
+			assert self.tableVersion == 0x00005000, "unknown 'maxp' format: %f" % self.tableVersion
+		for name in names:
+			value = getattr(self, name)
+			if type(value) == type(0L):
+				value=int(value)
+			if name == "tableVersion":
+				value = hex(value)
+			writer.simpletag(name, value=value)
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		setattr(self, name, safeEval(attrs["value"]))
+		
+
diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
new file mode 100644
index 0000000..05cde33
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
@@ -0,0 +1,136 @@
+import DefaultTable
+import struct, sstruct
+from fontTools.misc.textTools import safeEval
+import string
+import types
+
+nameRecordFormat = """
+		>	# big endian
+		platformID:	H
+		platEncID:	H
+		langID:		H
+		nameID:		H
+		length:		H
+		offset:		H
+"""
+
+class table__n_a_m_e(DefaultTable.DefaultTable):
+	
+	def decompile(self, data, ttFont):
+		format, n, stringoffset = struct.unpack(">HHH", data[:6])
+		stringoffset = int(stringoffset)
+		stringData = data[stringoffset:]
+		data = data[6:stringoffset]
+		self.names = []
+		for i in range(n):
+			name, data = sstruct.unpack2(nameRecordFormat, data, NameRecord())
+			name.fixlongs()
+			name.string = stringData[name.offset:name.offset+name.length]
+			del name.offset, name.length
+			self.names.append(name)
+	
+	def compile(self, ttFont):
+		self.names.sort()  # sort according to the spec; see NameRecord.__cmp__()
+		stringData = ""
+		format = 0
+		n = len(self.names)
+		stringoffset = 6 + n * sstruct.calcsize(nameRecordFormat)
+		data = struct.pack(">HHH", format, n, stringoffset)
+		lastoffset = 0
+		done = {}  # remember the data so we can reuse the "pointers"
+		for name in self.names:
+			if done.has_key(name.string):
+				name.offset, name.length = done[name.string]
+			else:
+				name.offset, name.length = done[name.string] = len(stringData), len(name.string)
+				stringData = stringData + name.string
+			data = data + sstruct.pack(nameRecordFormat, name)
+		return data + stringData
+	
+	def toXML(self, writer, ttFont):
+		for name in self.names:
+			name.toXML(writer, ttFont)
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if name <> "namerecord":
+			return # ignore unknown tags
+		if not hasattr(self, "names"):
+			self.names = []
+		name = NameRecord()
+		self.names.append(name)
+		name.fromXML((name, attrs, content), ttFont)
+	
+	def getname(self, nameID, platformID, platEncID, langID=None):
+		for namerecord in self.names:
+			if (	namerecord.nameID == nameID and 
+					namerecord.platformID == platformID and 
+					namerecord.platEncID == platEncID):
+				if langID is None or namerecord.langID == langID:
+					return namerecord
+		return None # not found
+	
+	def __cmp__(self, other):
+		return cmp(self.names, other.names)
+	
+
+class NameRecord:
+	
+	def toXML(self, writer, ttFont):
+		writer.begintag("namerecord", [
+				("nameID", self.nameID),
+				("platformID", self.platformID),
+				("platEncID", self.platEncID),
+				("langID", hex(self.langID)),
+						])
+		writer.newline()
+		if self.platformID == 0 or (self.platformID == 3 and self.platEncID == 1):
+			writer.write16bit(self.string)
+		else:
+			writer.write8bit(self.string)
+		writer.newline()
+		writer.endtag("namerecord")
+		writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		self.nameID = safeEval(attrs["nameID"])
+		self.platformID = safeEval(attrs["platformID"])
+		self.platEncID = safeEval(attrs["platEncID"])
+		self.langID =  safeEval(attrs["langID"])
+		if self.platformID == 0 or (self.platformID == 3 and self.platEncID == 1):
+			from fontTools.ttLib.xmlImport import UnicodeString
+			str = UnicodeString("")
+			for element in content:
+				str = str + element
+			self.string = str.stripped().tostring()
+		else:
+			self.string = string.strip(string.join(content, ""))
+	
+	def __cmp__(self, other):
+		"""Compare method, so a list of NameRecords can be sorted
+		according to the spec by just sorting it..."""
+		selftuple = (self.platformID,
+				self.platEncID,
+				self.langID,
+				self.nameID,
+				self.string)
+		othertuple = (other.platformID,
+				other.platEncID,
+				other.langID,
+				other.nameID,
+				other.string)
+		return cmp(selftuple, othertuple)
+	
+	def __repr__(self):
+		return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
+				self.nameID, self.platformID, self.langID)
+	
+	def fixlongs(self):
+		"""correct effects from bug in Python 1.5.1, where "H" 
+		returns a Python Long int. 
+		This has been fixed in Python 1.5.2.
+		"""
+		for attr in dir(self):
+			val = getattr(self, attr)
+			if type(val) == types.LongType:
+				setattr(self, attr, int(val))
+
diff --git a/Lib/fontTools/ttLib/tables/_p_o_s_t.py b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
new file mode 100644
index 0000000..273de79
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
@@ -0,0 +1,214 @@
+from fontTools.ttLib.standardGlyphOrder import standardGlyphOrder
+import DefaultTable
+import struct, sstruct
+import array
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval, readHex
+
+
+postFormat = """
+	>
+	formatType:			16.16F
+	italicAngle:		16.16F		# italic angle in degrees			
+	underlinePosition:	h
+	underlineThickness:	h
+	isFixedPitch:		l
+	minMemType42:		l			# minimum memory if TrueType font is downloaded
+	maxMemType42:		l			# maximum memory if TrueType font is downloaded
+	minMemType1:		l			# minimum memory if Type1 font is downloaded
+	maxMemType1:		l			# maximum memory if Type1 font is downloaded
+"""
+
+postFormatSize = sstruct.calcsize(postFormat)
+
+
+class table__p_o_s_t(DefaultTable.DefaultTable):
+	
+	def decompile(self, data, ttFont):
+		sstruct.unpack(postFormat, data[:postFormatSize], self)
+		data = data[postFormatSize:]
+		if self.formatType == 1.0:
+			self.decode_format_1_0(data, ttFont)
+		elif self.formatType == 2.0:
+			self.decode_format_2_0(data, ttFont)
+		elif self.formatType == 3.0:
+			self.decode_format_3_0(data, ttFont)
+		else:
+			# supported format
+			raise ttLib.TTLibError, "'post' table format %f not supported" % self.formatType
+	
+	def compile(self, ttFont):
+		data = sstruct.pack(postFormat, self)
+		if self.formatType == 1.0:
+			pass # we're done
+		elif self.formatType == 2.0:
+			data = data + self.encode_format_2_0(ttFont)
+		elif self.formatType == 3.0:
+			pass # we're done
+		else:
+			# supported format
+			raise ttLib.TTLibError, "'post' table format %f not supported" % self.formatType
+		return data
+	
+	def getGlyphOrder(self):
+		"""This function will get called by a ttLib.TTFont instance.
+		Do not call this function yourself, use TTFont().getGlyphOrder()
+		or its relatives instead!
+		"""
+		if not hasattr(self, "glyphOrder"):
+			raise ttLib.TTLibError, "illegal use of getGlyphOrder()"
+		glyphOrder = self.glyphOrder
+		del self.glyphOrder
+		return glyphOrder
+	
+	def decode_format_1_0(self, data, ttFont):
+		self.glyphOrder = standardGlyphOrder[:]
+	
+	def decode_format_2_0(self, data, ttFont):
+		numGlyphs, = struct.unpack(">H", data[:2])
+		numGlyphs = int(numGlyphs)
+		data = data[2:]
+		indices = array.array("H")
+		indices.fromstring(data[:2*numGlyphs])
+		if ttLib.endian <> "big":
+			indices.byteswap()
+		data = data[2*numGlyphs:]
+		self.extraNames = extraNames = unpackPStrings(data)
+		self.glyphOrder = glyphOrder = [None] * int(ttFont['maxp'].numGlyphs)
+		for glyphID in range(numGlyphs):
+			index = indices[glyphID]
+			if index > 257:
+				name = extraNames[index-258]
+			else:
+				# fetch names from standard list
+				name = standardGlyphOrder[index]
+			glyphOrder[glyphID] = name
+		#AL990511: code added to handle the case of new glyphs without
+		#          entries into the 'post' table
+		if numGlyphs < ttFont['maxp'].numGlyphs:
+			for i in range(numGlyphs, ttFont['maxp'].numGlyphs):
+				glyphOrder[i] = "glyph#%.5d" % i
+				self.extraNames.append(glyphOrder[i])
+		self.build_psNameMapping(ttFont)
+	
+	def build_psNameMapping(self, ttFont):
+		mapping = {}
+		allNames = {}
+		for i in range(ttFont['maxp'].numGlyphs):
+			glyphName = psName = self.glyphOrder[i]
+			if allNames.has_key(glyphName):
+				# make up a new glyphName that's unique
+				n = 1
+				while allNames.has_key(glyphName + "#" + `n`):
+					n = n + 1
+				glyphName = glyphName + "#" + `n`
+				self.glyphOrder[i] = glyphName
+				mapping[glyphName] = psName
+			allNames[glyphName] = psName
+		self.mapping = mapping
+	
+	def decode_format_3_0(self, data, ttFont):
+		# Setting self.glyphOrder to None will cause the TTFont object
+		# try and construct glyph names from a Unicode cmap table.
+		self.glyphOrder = None
+	
+	def encode_format_2_0(self, ttFont):
+		numGlyphs = ttFont['maxp'].numGlyphs
+		glyphOrder = ttFont.getGlyphOrder()
+		assert len(glyphOrder) == numGlyphs
+		indices = array.array("H")
+		for glyphID in range(numGlyphs):
+			glyphName = glyphOrder[glyphID]
+			if self.mapping.has_key(glyphName):
+				psName = self.mapping[glyphName]
+			else:
+				psName = glyphName
+			if psName in self.extraNames:
+				index = 258 + self.extraNames.index(psName)
+			elif psName in standardGlyphOrder:
+				index = standardGlyphOrder.index(psName)
+			else:
+				index = 258 + len(self.extraNames)
+				extraNames.append(psName)
+			indices.append(index)
+		if ttLib.endian <> "big":
+			indices.byteswap()
+		return struct.pack(">H", numGlyphs) + indices.tostring() + packPStrings(self.extraNames)
+	
+	def toXML(self, writer, ttFont):
+		formatstring, names, fixes = sstruct.getformat(postFormat)
+		for name in names:
+			value = getattr(self, name)
+			writer.simpletag(name, value=value)
+			writer.newline()
+		if hasattr(self, "mapping"):
+			writer.begintag("psNames")
+			writer.newline()
+			writer.comment("This file uses unique glyph names based on the information\n"
+						"found in the 'post' table. Since these names might not be unique,\n"
+						"we have to invent artificial names in case of clashes. In order to\n"
+						"be able to retain the original information, we need a name to\n"
+						"ps name mapping for those cases where they differ. That's what\n"
+						"you see below.\n")
+			writer.newline()
+			items = self.mapping.items()
+			items.sort()
+			for name, psName in items:
+				writer.simpletag("psName", name=name, psName=psName)
+				writer.newline()
+			writer.endtag("psNames")
+			writer.newline()
+		if hasattr(self, "extraNames"):
+			writer.begintag("extraNames")
+			writer.newline()
+			writer.comment("following are the name that are not taken from the standard Mac glyph order")
+			writer.newline()
+			for name in self.extraNames:
+				writer.simpletag("psName", name=name)
+				writer.newline()
+			writer.endtag("extraNames")
+			writer.newline()
+		if hasattr(self, "data"):
+			writer.begintag("hexdata")
+			writer.newline()
+			writer.dumphex(self.data)
+			writer.endtag("hexdata")
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		if name not in ("psNames", "extraNames", "hexdata"):
+			setattr(self, name, safeEval(attrs["value"]))
+		elif name == "psNames":
+			self.mapping = {}
+			for element in content:
+				if type(element) <> type(()):
+					continue
+				name, attrs, content = element
+				if name == "psName":
+					self.mapping[attrs["name"]] = attrs["psName"]
+		elif name == "extraNames":
+			self.extraNames = []
+			for element in content:
+				if type(element) <> type(()):
+					continue
+				name, attrs, content = element
+				if name == "psName":
+					self.extraNames.append(attrs["name"])
+		else:
+			self.data = readHex(content)
+
+
+def unpackPStrings(data):
+	strings = []
+	while data:
+		length = ord(data[0])
+		strings.append(data[1:1+length])
+		data = data[1+length:]
+	return strings
+
+def packPStrings(strings):
+	data = ""
+	for s in strings:
+		data = data + chr(len(s)) + s
+	return data
+
diff --git a/Lib/fontTools/ttLib/tables/_p_r_e_p.py b/Lib/fontTools/ttLib/tables/_p_r_e_p.py
new file mode 100644
index 0000000..005c8ee
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_p_r_e_p.py
@@ -0,0 +1,14 @@
+import DefaultTable
+import array
+
+class table__p_r_e_p(DefaultTable.DefaultTable):
+	
+	def decompile(self, data, ttFont):
+		self.prep = data
+	
+	def compile(self, ttFont):
+		return self.prep
+	
+	def __len__(self):
+		return len(self.prep)
+	
diff --git a/Lib/fontTools/ttLib/tables/_v_h_e_a.py b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
new file mode 100644
index 0000000..43fd614
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
@@ -0,0 +1,78 @@
+import DefaultTable
+import sstruct
+from fontTools.misc.textTools import safeEval
+
+vheaFormat = """
+		>	# big endian
+		tableVersion:			16.16F
+		ascent:					h
+		descent:				h
+		lineGap:				h
+		advanceHeightMax:		H
+		minTopSideBearing:		h
+		minBottomSideBearing:	h
+		yMaxExtent:				h
+		caretSlopeRise:			h
+		caretSlopeRun:			h
+		reserved0:				h
+		reserved1:				h
+		reserved2:				h
+		reserved3:				h
+		reserved4:				h
+		metricDataFormat:		h
+		numberOfVMetrics:		H
+"""
+
+class table__v_h_e_a(DefaultTable.DefaultTable):
+	
+	dependencies = ['vmtx', 'glyf']
+	
+	def decompile(self, data, ttFont):
+		sstruct.unpack(vheaFormat, data, self)
+	
+	def compile(self, ttFont):
+		self.recalc(ttFont)
+		return sstruct.pack(vheaFormat, self)
+	
+	def recalc(self, ttFont):
+		vtmxTable = ttFont['vmtx']
+		if ttFont.has_key('glyf'):
+			if not ttFont.isLoaded('glyf'):
+				return
+			glyfTable = ttFont['glyf']
+			advanceHeightMax = -100000    # arbitrary big negative number
+			minTopSideBearing = 100000    # arbitrary big number
+			minBottomSideBearing = 100000 # arbitrary big number
+			yMaxExtent = -100000          # arbitrary big negative number
+			
+			for name in ttFont.getGlyphOrder():
+				height, tsb = vtmxTable[name]
+				g = glyfTable[name]
+				if g.numberOfContours <= 0:
+					continue
+				advanceHeightMax = max(advanceHeightMax, height)
+				minTopSideBearing = min(minTopSideBearing, tsb)
+				rsb = height - tsb - (g.yMax - g.yMin)
+				minBottomSideBearing = min(minBottomSideBearing, rsb)
+				extent = tsb + (g.yMax - g.yMin)
+				yMaxExtent = max(yMaxExtent, extent)
+			self.advanceHeightMax = advanceHeightMax
+			self.minTopSideBearing = minTopSideBearing
+			self.minBottomSideBearing = minBottomSideBearing
+			self.yMaxExtent = yMaxExtent
+		else:
+			# XXX CFF recalc...
+			pass
+	
+	def toXML(self, writer, ttFont):
+		formatstring, names, fixes = sstruct.getformat(vheaFormat)
+		for name in names:
+			value = getattr(self, name)
+			if type(value) == type(0L):
+				value = int(value)
+			writer.simpletag(name, value=value)
+			writer.newline()
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		setattr(self, name, safeEval(attrs["value"]))
+
diff --git a/Lib/fontTools/ttLib/tables/_v_m_t_x.py b/Lib/fontTools/ttLib/tables/_v_m_t_x.py
new file mode 100644
index 0000000..dabefe6
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_v_m_t_x.py
@@ -0,0 +1,14 @@
+import DefaultTable
+import Numeric
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+
+superclass = ttLib.getTableClass("hmtx")
+
+class table__v_m_t_x(superclass):
+	
+	headerTag = 'vhea'
+	advanceName = 'height'
+	sideBearingName = 'tsb'
+	numberOfMetricsName = 'numberOfVMetrics'
+
diff --git a/Lib/fontTools/ttLib/tables/otCommon.py b/Lib/fontTools/ttLib/tables/otCommon.py
new file mode 100644
index 0000000..c312c63
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/otCommon.py
@@ -0,0 +1,605 @@
+"""ttLib.tables.otCommon.py -- Various data structures used by various OpenType tables.
+"""
+
+import struct, sstruct
+import DefaultTable
+from fontTools import ttLib
+
+
+class base_GPOS_GSUB(DefaultTable.DefaultTable):
+	
+	"""Base class for GPOS and GSUB tables; they share the same high-level structure."""
+	
+	version = 0x00010000
+	
+	def decompile(self, data, otFont):
+		reader = OTTableReader(data)
+		self.version = reader.readLong()
+		if self.version <> 0x00010000:
+			raise ttLib.TTLibError, "unknown table version: 0x%8x" % self.version
+		
+		self.scriptList = reader.readTable(ScriptList, otFont, self.tableTag)
+		self.featureList = reader.readTable(FeatureList, otFont, self.tableTag)
+		self.lookupList = reader.readTable(LookupList, otFont, self.tableTag)
+	
+	def compile(self, otFont):
+		XXXXXX
+	
+	def toXML(self, xmlWriter, otFont):
+		names = [("ScriptList", "scriptList"), 
+				("FeatureList", "featureList"), 
+				("LookupList", "lookupList")]
+		for name, attr in names:
+			xmlWriter.newline()
+			xmlWriter.begintag(name)
+			xmlWriter.newline()
+			table = getattr(self, attr)
+			table.toXML(xmlWriter, otFont)
+			xmlWriter.endtag(name)
+			xmlWriter.newline()
+		xmlWriter.newline()
+	
+	def fromXML(self, (name, attrs, content), otFont):
+		xxx
+
+
+#
+# Script List and subtables
+#
+
+class ScriptList:
+	
+	def __init__(self, parentTag):
+		self.parentTag = parentTag
+	
+	def decompile(self, reader, otFont):
+		scriptCount = reader.readUShort()
+		self.scripts = reader.readTagList(scriptCount, Script, otFont)
+	
+	def compile(self, otFont):
+		XXXXXX
+	
+	def toXML(self, xmlWriter, otFont):
+		for tag, script in self.scripts:
+			xmlWriter.begintag("Script", tag=tag)
+			xmlWriter.newline()
+			script.toXML(xmlWriter, otFont)
+			xmlWriter.endtag("Script")
+			xmlWriter.newline()
+	
+	def fromXML(self, (name, attrs, content), otFont):
+		xxx
+
+
+class Script:
+	
+	def decompile(self, reader, otFont):
+		self.defaultLangSystem = None
+		self.defaultLangSystem = reader.readTable(LanguageSystem, otFont)
+		langSysCount = reader.readUShort()
+		self.languageSystems = reader.readTagList(langSysCount, LanguageSystem, otFont)
+	
+	def compile(self, otFont):
+		XXXXX
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.begintag("DefaultLanguageSystem")
+		xmlWriter.newline()
+		self.defaultLangSystem.toXML(xmlWriter, otFont)
+		xmlWriter.endtag("DefaultLanguageSystem")
+		xmlWriter.newline()
+		for tag, langSys in self.languageSystems:
+			xmlWriter.begintag("LanguageSystem", tag=tag)
+			xmlWriter.newline()
+			langSys.toXML(xmlWriter, otFont)
+			xmlWriter.endtag("LanguageSystem")
+			xmlWriter.newline()
+
+
+class LanguageSystem:
+	
+	def decompile(self, reader, otFont):
+		self.lookupOrder = reader.readUShort()
+		self.reqFeatureIndex = reader.readUShort()
+		featureCount = reader.readUShort()
+		self.featureIndex = reader.readUShortArray(featureCount)
+	
+	def compile(self, otFont):
+		xxx
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.simpletag("LookupOrder", value=self.lookupOrder)
+		xmlWriter.newline()
+		xmlWriter.simpletag("ReqFeature", index=hex(self.reqFeatureIndex))
+		xmlWriter.newline()
+		for index in self.featureIndex:
+			xmlWriter.simpletag("Feature", index=index)
+			xmlWriter.newline()
+
+
+#
+# Feature List and subtables
+#
+
+class FeatureList:
+	
+	def __init__(self, parentTag):
+		self.parentTag = parentTag
+	
+	def decompile(self, reader, otFont):
+		featureCount = reader.readUShort()
+		self.features = reader.readTagList(featureCount, Feature, otFont)
+	
+	def compile(self, otFont):
+		XXXXX
+	
+	def toXML(self, xmlWriter, otFont):
+		for index in range(len(self.features)):
+			tag, feature = self.features[index]
+			xmlWriter.begintag("Feature", index=index, tag=tag)
+			xmlWriter.newline()
+			feature.toXML(xmlWriter, otFont)
+			xmlWriter.endtag("Feature")
+			xmlWriter.newline()
+			
+	def fromXML(self, (name, attrs, content), otFont):
+		xxx
+
+
+class Feature:
+	
+	def decompile(self, reader, otFont):
+		self.featureParams = reader.readUShort()
+		lookupCount = reader.readUShort()
+		self.lookupListIndex = reader.readUShortArray(lookupCount)
+	
+	def compile(self, otFont):
+		XXXXX
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.simpletag("FeatureParams", value=hex(self.featureParams))
+		xmlWriter.newline()
+		for lookupIndex in self.lookupListIndex:
+			xmlWriter.simpletag("LookupTable", index=lookupIndex)
+			xmlWriter.newline()
+	
+	def fromXML(self, (name, attrs, content), otFont):
+		xxx
+
+
+#
+# Lookup List and subtables
+#
+
+class LookupList:
+	
+	def __init__(self, parentTag):
+		self.parentTag = parentTag
+	
+	def decompile(self, reader, otFont):
+		lookupCount = reader.readUShort()
+		self.lookup = lookup = []
+		for i in range(lookupCount):
+			lookup.append(reader.readTable(LookupTable, otFont, self.parentTag))
+	
+	def compile(self, otFont):
+		XXXXX
+	
+	def toXML(self, xmlWriter, otFont):
+		for i in range(len(self.lookup)):
+			xmlWriter.newline()
+			lookupTable = self.lookup[i]
+			xmlWriter.begintag("LookupTable", index=i)
+			xmlWriter.newline()
+			lookupTable.toXML(xmlWriter, otFont)
+			xmlWriter.endtag("LookupTable")
+			xmlWriter.newline()
+		xmlWriter.newline()
+		
+	def fromXML(self, (name, attrs, content), otFont):
+		xxx
+
+
+class LookupTable:
+	
+	def __init__(self, parentTag):
+		self.parentTag = parentTag
+	
+	def decompile(self, reader, otFont):
+		parentTable = otFont[self.parentTag]
+		self.lookupType = reader.readUShort()
+		self.lookupFlag = reader.readUShort()
+		subTableCount = reader.readUShort()
+		self.subTables = subTables = []
+		lookupTypeClass = parentTable.getLookupTypeClass(self.lookupType)
+		for i in range(subTableCount):
+			subTables.append(reader.readTable(lookupTypeClass, otFont))
+	
+	def compile(self, otFont):
+		XXXXXX
+	
+	def __repr__(self):
+		if not hasattr(self, "lookupTypeName"):
+			m = ttLib.getTableModule(self.parentTag)
+			self.lookupTypeName = m.lookupTypeClasses[self.lookupType].__name__
+		return "<%s LookupTable at %x>" % (self.lookupTypeName, id(self))
+	
+	def toXML(self, xmlWriter, otFont):
+		xmlWriter.simpletag("LookupFlag", value=hex(self.lookupFlag))
+		xmlWriter.newline()
+		for subTable in self.subTables:
+			name = subTable.__class__.__name__
+			xmlWriter.begintag(name)
+			xmlWriter.newline()
+			subTable.toXML(xmlWriter, otFont)
+			xmlWriter.endtag(name)
+			xmlWriter.newline()
+	
+	def fromXML(self, (name, attrs, content), otFont):
+		xxx
+
+
+#
+# Other common formats
+#
+
+class CoverageTable:
+	
+	def getGlyphIDs(self):
+		return self.glyphIDs
+	
+	def getGlyphNames(self):
+		return self.glyphNames
+	
+	def makeGlyphNames(self, otFont):
+		self.glyphNames = map(lambda i, o=otFont.getGlyphOrder(): o[i], self.glyphIDs)
+	
+	def decompile(self, reader, otFont):
+		format = reader.readUShort()
+		if format == 1:
+			self.decompileFormat1(reader, otFont)
+		elif format == 2:
+			self.decompileFormat2(reader, otFont)
+		else:
+			raise ttLib.TTLibError, "unknown Coverage table format: %d" % format
+		self.makeGlyphNames(otFont)
+	
+	def decompileFormat1(self, reader, otFont):
+		glyphCount = reader.readUShort()
+		self.glyphIDs = glyphIDs = []
+		for i in range(glyphCount):
+			glyphID = reader.readUShort()
+			glyphIDs.append(glyphID)
+	
+	def decompileFormat2(self, reader, otFont):
+		rangeCount = reader.readUShort()
+		self.glyphIDs = glyphIDs = []
+		for i in range(rangeCount):
+			startID = reader.readUShort()
+			endID = reader.readUShort()
+			startCoverageIndex = reader.readUShort()
+			for glyphID in range(startID, endID + 1):
+				glyphIDs.append(glyphID)
+	
+	def compile(self, otFont):
+		# brute force ;-)
+		data1 = self.compileFormat1(otFont)
+		data2 = self.compileFormat2(otFont)
+		if len(data1) <= len(data2):
+			format = 1
+			reader = data1
+		else:
+			format = 2
+			reader = data2
+		return struct.pack(">H", format) + reader
+	
+	def compileFormat1(self, otFont):
+		xxxxx
+		glyphIDs = map(otFont.getGlyphID, self.glyphNames)
+		data = pack(">H", len(glyphIDs))
+		pack = struct.pack
+		for glyphID in glyphIDs:
+			data = data + pack(">H", glyphID)
+		return data
+	
+	def compileFormat2(self, otFont):
+		xxxxx
+		glyphIDs = map(otFont.getGlyphID, self.glyphNames)
+		ranges = []
+		lastID = startID = glyphIDs[0]
+		startCoverageIndex = 0
+		glyphCount = len(glyphIDs)
+		for i in range(1, glyphCount+1):
+			if i == glyphCount:
+				glyphID = 0x1ffff  # arbitrary, but larger than 0x10000
+			else:
+				glyphID = glyphIDs[i]
+			if glyphID <> (lastID + 1):
+				ranges.append((startID, lastID, startCoverageIndex))
+				startCoverageIndex = i
+				startID = glyphID
+			lastID = glyphID
+		ranges.sort()  # sort by startID
+		rangeData = ""
+		for startID, endID, startCoverageIndex in ranges:
+			rangeData = rangeData + struct.pack(">HHH", startID, endID, startCoverageIndex)
+		return pack(">H", len(ranges)) + rangeData
+
+
+class ClassDefinitionTable:
+	
+	def decompile(self, reader, otFont):
+		format = reader.readUShort()
+		if format == 1:
+			self.decompileFormat1(reader, otFont)
+		elif format == 2:
+			self.decompileFormat2(reader, otFont)
+		else:
+			raise ttLib.TTLibError, "unknown Class table format: %d" % format
+		self.reverse()
+	
+	def reverse(self):
+		classDefs = {}
+		for glyphName, classCode in self.classDefs:
+			try:
+				classDefs[classCode].append(glyphName)
+			except KeyError:
+				classDefs[classCode] = [glyphName]
+		self.classDefs = classDefs
+	
+	def decompileFormat1(self, reader, otFont):
+		self.classDefs = classDefs = []
+		startGlyphID = reader.readUShort()
+		glyphCount = reader.readUShort()
+		for i in range(glyphCount):
+			glyphName = otFont.getglyphName(startGlyphID + i)
+			classValue = reader.readUShort()
+			if classValue:
+				classDefs.append((glyphName, classValue))
+	
+	def decompileFormat2(self, reader, otFont):
+		self.classDefs = classDefs = []
+		classRangeCount = reader.readUShort()
+		for i in range(classRangeCount):
+			startID = reader.readUShort()
+			endID = reader.readUShort()
+			classValue = reader.readUShort()
+			for glyphID in range(startID, endID + 1):
+				if classValue:
+					glyphName = otFont.getGlyphName(glyphID)
+					classDefs.append((glyphName, classValue))
+	
+	def compile(self, otFont):
+		# brute force again
+		data1 = self.compileFormat1(otFont)
+		data2 = self.compileFormat2(otFont)
+		if len(data1) <= len(data2):
+			format = 1
+			data = data1
+		else:
+			format = 2
+			data = data2
+		return struct.pack(">H", format) + data
+	
+	def compileFormat1(self, otFont):
+		items = map(lambda (glyphName, classValue), getGlyphID=otFont.getGlyphID:
+				(getGlyphID(glyphName), classValue), self.glyphs.items())
+		items.sort()
+		startGlyphID = items[0][0]
+		endGlyphID = items[-1][0]
+		data = ""
+		lastID = startGlyphID
+		for glyphID, classValue in items:
+			for i in range(lastID + 1, glyphID - 1):
+				data = data + "\0\0"  # 0 == default class
+			data = data + struct.pack(">H", classValue)
+			lastID = glyphID
+		return struct.pack(">H", endGlyphID - startGlyphID + 1) + data
+	
+	def compileFormat2(self, otFont):
+		items = map(lambda (glyphName, classValue), getGlyphID=otFont.getGlyphID:
+				(getGlyphID(glyphName), classValue), self.glyphs.items())
+		items.sort()
+		ranges = []
+		lastID, lastClassValue = items[0][0]
+		startID = lastID
+		itemCount = len(items)
+		for i in range(1, itemCount+1):
+			if i == itemCount:
+				glyphID = 0x1ffff  # arbitrary, but larger than 0x10000
+				classValue = 0
+			else:
+				glyphID, classValue = items[i]
+			if glyphID <> (lastID + 1) or lastClassValue <> classValue:
+				ranges.append((startID, lastID, lastClassValue))
+				startID = glyphID
+				lastClassValue = classValue
+			lastID = glyphID
+			lastClassValue = classValue
+		rangeData = ""
+		for startID, endID, classValue in ranges:
+			rangeData = rangeData + struct.pack(">HHH", startID, endID, classValue)
+		return pack(">H", len(ranges)) + rangeData
+	
+	def __getitem__(self, glyphName):
+		if self.glyphs.has_key(glyphName):
+			return self.glyphs[glyphName]
+		else:
+			return 0  # default class
+
+
+class DeviceTable:
+	
+	def decompile(self, reader, otFont):
+		xxxxxx
+		self.startSize = unpack_uint16(reader[:2])
+		endSize = unpack_uint16(reader[2:4])
+		deltaFormat = unpack_uint16(reader[4:6])
+		reader = reader[6:]
+		if deltaFormat == 1:
+			bits = 2
+		elif deltaFormat == 2:
+			bits = 4
+		elif deltaFormat == 3:
+			bits = 8
+		else:
+			raise ttLib.TTLibError, "unknown Device table delta format: %d" % deltaFormat
+		numCount = 16 / bits
+		deltaCount = endSize - self.startSize + 1
+		deltaValues = []
+		mask = (1 << bits) - 1
+		threshold = (1 << bits) / 2
+		shift = 1 << bits
+		for i in range(0, deltaCount, numCount):
+			offset = 2*i/numCount
+			chunk = unpack_uint16(reader[offset:offset+2])
+			deltas = []
+			for j in range(numCount):
+				delta = chunk & mask
+				if delta >= threshold:
+					delta = delta - shift
+				deltas.append(delta)
+				chunk = chunk >> bits
+			deltas.reverse()
+			deltaValues = deltaValues + deltas
+		self.deltaValues = deltaValues[:deltaCount]
+	
+	def compile(self, otFont):
+		deltaValues = self.deltaValues
+		startSize = self.startSize
+		endSize = startSize + len(deltaValues) - 1
+		smallestDelta = min(deltas)
+		largestDelta = ma(deltas)
+		if smallestDelta >= -2 and largestDelta < 2:
+			deltaFormat = 1
+			bits = 2
+		elif smallestDelta >= -8 and largestDelta < 8:
+			deltaFormat = 2
+			bits = 4
+		elif smallestDelta >= -128 and largestDelta < 128:
+			deltaFormat = 3
+			bits = 8
+		else:
+			raise ttLib.TTLibError, "delta value too large: min=%d, max=%d" % (smallestDelta, largestDelta)
+		data = struct.pack(">HHH", startSize, endSize, deltaFormat)
+		numCount = 16 / bits
+		# pad the list to a multiple of numCount values
+		remainder = len(deltaValues) % numCount
+		if remainder:
+			deltaValues = deltaValues + [0] * (numCount - remainder)
+		deltaData = ""
+		for i in range(0, len(deltaValues), numCount):
+			chunk = 0
+			for j in range(numCount):
+				chunk = chunk << bits
+				chunk = chunk | deltaValues[i+j]
+			deltaData = deltaData + struct.pack(">H", chunk)
+		return data + deltaData
+
+
+#
+# Miscelaneous helper routines and classes
+#
+
+class OTTableReader:
+	
+	"""Data wrapper, mostly designed to make reading OT data less cumbersome."""
+	
+	def __init__(self, data, offset=0):
+		self.data = data
+		self.offset = offset
+		self.pos = offset
+	
+	def readUShort(self):
+		pos = self.pos
+		newpos = pos + 2
+		value = int(struct.unpack(">H", self.data[pos:newpos])[0])
+		self.pos = newpos
+		return value
+	
+	readOffset = readUShort
+	
+	def readShort(self):
+		pos = self.pos
+		newpos = pos + 2
+		value = int(struct.unpack(">h", self.data[pos:newpos])[0])
+		self.pos = newpos
+		return value
+	
+	def readLong(self):
+		pos = self.pos
+		newpos = pos + 4
+		value = int(struct.unpack(">l", self.data[pos:newpos])[0])
+		self.pos = newpos
+		return value
+	
+	def readTag(self):
+		pos = self.pos
+		newpos = pos + 4
+		value = self.data[pos:newpos]
+		assert len(value) == 4
+		self.pos = newpos
+		return value
+	
+	def readUShortArray(self, count):
+		return self.readArray(count, "H")
+	
+	readOffsetArray = readUShortArray
+	
+	def readShortArray(self, count):
+		return self.readArray(count, "h")
+	
+	def readArray(self, count, format):
+		assert format in "Hh"
+		from array import array
+		pos = self.pos
+		newpos = pos + 2 * count
+		a = array(format)
+		a.fromstring(self.data[pos:newpos])
+		if ttLib.endian <> 'big':
+			a.byteswap()
+		self.pos = newpos
+		return a.tolist()
+	
+	def readTable(self, tableClass, otFont, *args):
+		offset = self.readOffset()
+		if offset == 0:
+			return None
+		newReader = self.getSubString(offset)
+		table = apply(tableClass, args)
+		table.decompile(newReader, otFont)
+		return table
+	
+	def readTableArray(self, count, tableClass, otFont, *args):
+		list = []
+		for i in range(count):
+			list.append(apply(self.readTable, (tableClass, otFont) + args))
+		return list
+	
+	def readTagList(self, count, tableClass, otFont, *args):
+		list = []
+		for i in range(count):
+			tag = self.readTag()
+			table = apply(self.readTable, (tableClass, otFont) + args)
+			list.append((tag, table))
+		return list
+	
+	def readStruct(self, format, size=None):
+		if size is None:
+			size = struct.calcsize(format)
+		else:
+			assert size == struct.calcsize(format)
+		pos = self.pos
+		newpos = pos + size
+		values = struct.unpack(format, self.data[pos:newpos])
+		self.pos = newpos
+		return values
+	
+	def getSubString(self, offset):
+		return self.__class__(self.data, self.offset+offset)
+	
+	def seek(self, n):
+		"""Relative seek."""
+		self.pos = self.pos + n
+
+
diff --git a/Lib/fontTools/ttLib/tables/table_API_readme.txt b/Lib/fontTools/ttLib/tables/table_API_readme.txt
new file mode 100644
index 0000000..2c04bf8
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/table_API_readme.txt
@@ -0,0 +1,91 @@
+This folder is a subpackage of ttLib. Each module here is a 
+specialized TT/OT table converter: they can convert raw data 
+to Python objects and vice versa. Usually you don't need to 
+use the modules directly: they are imported and used 
+automatically when needed by ttLib.
+
+If you are writing you own table converter the following is 
+important.
+
+The modules here have pretty strange names: this is due to the 
+fact that we need to map TT table tags (which are case sensitive) 
+to filenames (which on Mac and Win aren't case sensitive) as well 
+as to Python identifiers. The latter means it can only contain 
+[A-Za-z0-9_] and cannot start with a number. 
+
+ttLib provides functions to expand a tag into the format used here:
+
+>>> from fontTools import ttLib
+>>> ttLib.tag2identifier("FOO ")
+'F_O_O_'
+>>> ttLib.tag2identifier("cvt ")
+'_c_v_t'
+>>> ttLib.tag2identifier("OS/2")
+'O_S_2f_2'
+>>> ttLib.tag2identifier("glyf")
+'_g_l_y_f'
+>>> 
+
+And vice versa:
+
+>>> ttLib.identifier2tag("F_O_O_")
+'FOO '
+>>> ttLib.identifier2tag("_c_v_t")
+'cvt '
+>>> ttLib.identifier2tag("O_S_2f_2")
+'OS/2'
+>>> ttLib.identifier2tag("_g_l_y_f")
+'glyf'
+>>> 
+
+Eg. the 'glyf' table converter lives in a Python file called:
+
+	_g_l_y_f.py
+
+The converter itself is a class, named "table_" + expandedtag. Eg:
+
+	class table__g_l_y_f:
+		etc.
+
+Note that if you _do_ need to use such modules or classes manually, 
+there are two convenient API functions that let you find them by tag:
+
+>>> ttLib.getTableModule('glyf')
+<module 'ttLib.tables._g_l_y_f'>
+>>> ttLib.getTableClass('glyf')
+<class ttLib.tables._g_l_y_f.table__g_l_y_f at 645f400>
+>>> 
+
+You must subclass from DefaultTable.DefaultTable. It provides some default
+behavior, as well as a constructor method (__init__) that you don't need to 
+override.
+
+Your converter should minimally provide two methods:
+
+class table_F_O_O_(DefaultTable.DefaultTable): # converter for table 'FOO '
+	
+	def decompile(self, data, ttFont):
+		# 'data' is the raw table data. Unpack it into a
+		# Python data structure.
+		# 'ttFont' is a ttLib.TTfile instance, enabling you to
+		# refer to other tables. Do ***not*** keep a reference to
+		# it: it will cause a circular reference (ttFont saves 
+		# a reference to us), and that means we'll be leaking 
+		# memory. If you need to use it in other methods, just 
+		# pass it around as a method argument.
+	
+	def compile(self, ttFont):
+		# Return the raw data, as converted from the Python
+		# data structure. 
+		# Again, 'ttFont' is there so you can access other tables.
+		# Same warning applies.
+
+If you want to support XML import/export as well, you need to provide two
+additional methods:
+
+	def toXML(self, writer, ttFont):
+		# XXX
+	
+	def fromXML(self, (name, attrs, content), ttFont):
+		# XXX
+
diff --git a/Lib/fontTools/ttLib/tables/ttProgram.py b/Lib/fontTools/ttLib/tables/ttProgram.py
new file mode 100644
index 0000000..8dd9faa
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/ttProgram.py
@@ -0,0 +1,249 @@
+"""ttLib.tables.ttProgram.py -- Assembler/disassembler for TrueType bytecode programs."""
+
+import array
+
+
+# first, the list of instructions that eat bytes or words from the instruction stream
+
+streamInstructions = [
+#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
+#	opcode     mnemonic argbits         descriptive name pops pushes        eats from instruction stream          pushes
+#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
+	(0x40,    'NPUSHB',     0,             'PushNBytes',  0, -1), #                      n, b1, b2,...bn      b1,b2...bn
+	(0x41,    'NPUSHW',     0,             'PushNWords',  0, -1), #                       n, w1, w2,...w      w1,w2...wn
+	(0xb0,     'PUSHB',     3,              'PushBytes',  0, -1), #                          b0, b1,..bn  b0, b1, ...,bn
+	(0xb8,     'PUSHW',     3,              'PushWords',  0, -1), #                           w0,w1,..wn   w0 ,w1, ...wn
+#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
+]
+
+
+# next, the list of "normal" instructions
+
+instructions = [
+#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
+#	opcode     mnemonic  argbits        descriptive name pops pushes                                pops          pushes
+#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
+	(0x7f,        'AA',     0,            'AdjustAngle',  1,  0), #                                    p               -
+	(0x64,       'ABS',     0,               'Absolute',  1,  1), #                                    n             |n|
+	(0x60,       'ADD',     0,                    'Add',  2,  1), #                               n2, n1       (n1 + n2)
+	(0x27,  'ALIGNPTS',     0,               'AlignPts',  2,  0), #                               p2, p1               -
+	(0x3c,   'ALIGNRP',     0,        'AlignRelativePt', -1,  0), #             p1, p2, ... , ploopvalue               -
+	(0x5a,       'AND',     0,             'LogicalAnd',  2,  1), #                               e2, e1               b
+	(0x2b,      'CALL',     0,           'CallFunction',  1,  0), #                                    f               -
+	(0x67,   'CEILING',     0,                'Ceiling',  1,  1), #                                    n         ceil(n)
+	(0x25,    'CINDEX',     0,        'CopyXToTopStack',  1,  1), #                                    k              ek
+	(0x22,     'CLEAR',     0,             'ClearStack', -1,  0), #               all items on the stack               -
+	(0x4f,     'DEBUG',     0,              'DebugCall',  1,  0), #                                    n               -
+	(0x73,   'DELTAC1',     0,       'DeltaExceptionC1', -1,  0), #    argn, cn, argn-1,cn-1, , arg1, c1               -
+	(0x74,   'DELTAC2',     0,       'DeltaExceptionC2', -1,  0), #    argn, cn, argn-1,cn-1, , arg1, c1               -
+	(0x75,   'DELTAC3',     0,       'DeltaExceptionC3', -1,  0), #    argn, cn, argn-1,cn-1, , arg1, c1               -
+	(0x5d,   'DELTAP1',     0,       'DeltaExceptionP1', -1,  0), #   argn, pn, argn-1, pn-1, , arg1, p1               -
+	(0x71,   'DELTAP2',     0,       'DeltaExceptionP2', -1,  0), #   argn, pn, argn-1, pn-1, , arg1, p1               -
+	(0x72,   'DELTAP3',     0,       'DeltaExceptionP3', -1,  0), #   argn, pn, argn-1, pn-1, , arg1, p1               -
+	(0x24,     'DEPTH',     0,          'GetDepthStack',  0,  1), #                                    -               n
+	(0x62,       'DIV',     0,                 'Divide',  2,  1), #                               n2, n1   (n1 * 64)/ n2
+	(0x20,       'DUP',     0,      'DuplicateTopStack',  1,  2), #                                    e            e, e
+	(0x59,       'EIF',     0,                  'EndIf',  0,  0), #                                    -               -
+	(0x1b,      'ELSE',     0,                   'Else',  0,  0), #                                    -               -
+	(0x2d,      'ENDF',     0,  'EndFunctionDefinition',  0,  0), #                                    -               -
+	(0x54,        'EQ',     0,                  'Equal',  2,  1), #                               e2, e1               b
+	(0x57,      'EVEN',     0,                   'Even',  1,  1), #                                    e               b
+	(0x2c,      'FDEF',     0,     'FunctionDefinition',  1,  0), #                                    f               -
+	(0x4e,   'FLIPOFF',     0,         'SetAutoFlipOff',  0,  0), #                                    -               -
+	(0x4d,    'FLIPON',     0,          'SetAutoFlipOn',  0,  0), #                                    -               -
+	(0x80,    'FLIPPT',     0,              'FlipPoint', -1,  0), #              p1, p2, ..., ploopvalue               -
+	(0x82, 'FLIPRGOFF',     0,           'FlipRangeOff',  2,  0), #                                 h, l               -
+	(0x81,  'FLIPRGON',     0,            'FlipRangeOn',  2,  0), #                                 h, l               -
+	(0x66,     'FLOOR',     0,                  'Floor',  1,  1), #                                    n        floor(n)
+	(0x46,        'GC',     1,      'GetCoordOnPVector',  1,  1), #                                    p               c
+	(0x88,   'GETINFO',     0,                'GetInfo',  1,  1), #                             selector          result
+	(0x0d,       'GFV',     0,             'GetFVector',  0,  2), #                                    -          px, py
+	(0x0c,       'GPV',     0,             'GetPVector',  0,  2), #                                    -          px, py
+	(0x52,        'GT',     0,            'GreaterThan',  2,  1), #                               e2, e1               b
+	(0x53,      'GTEQ',     0,     'GreaterThanOrEqual',  2,  1), #                               e2, e1               b
+	(0x89,      'IDEF',     0,  'InstructionDefinition',  1,  0), #                                    f               -
+	(0x58,        'IF',     0,                     'If',  1,  0), #                                    e               -
+	(0x8e,  'INSTCTRL',     0,    'SetInstrExecControl',  2,  0), #                                 s, v               -
+	(0x39,        'IP',     0,         'InterpolatePts', -1,  0), #             p1, p2, ... , ploopvalue               -
+	(0x0f,     'ISECT',     0,      'MovePtToIntersect',  5,  0), #                    a1, a0, b1, b0, p               -
+	(0x30,       'IUP',     1,      'InterpolateUntPts',  0,  0), #                                    -               -
+	(0x1c,      'JMPR',     0,                   'Jump',  1,  0), #                               offset               -
+	(0x79,      'JROF',     0,    'JumpRelativeOnFalse',  2,  0), #                            e, offset               -
+	(0x78,      'JROT',     0,     'JumpRelativeOnTrue',  2,  0), #                            e, offset               -
+	(0x2a,  'LOOPCALL',     0,    'LoopAndCallFunction',  2,  0), #                             f, count               -
+	(0x50,        'LT',     0,               'LessThan',  2,  1), #                               e2, e1               b
+	(0x51,      'LTEQ',     0,        'LessThenOrEqual',  2,  1), #                               e2, e1               b
+	(0x8b,       'MAX',     0,                'Maximum',  2,  1), #                               e2, e1     max(e1, e2)
+	(0x49,        'MD',     1,        'MeasureDistance',  2,  1), #                                p2,p1               d
+	(0x2e,      'MDAP',     1,        'MoveDirectAbsPt',  1,  0), #                                    p               -
+	(0xc0,      'MDRP',     5,        'MoveDirectRelPt',  1,  0), #                                    p               -
+	(0x3e,      'MIAP',     1,      'MoveIndirectAbsPt',  2,  0), #                                 n, p               -
+	(0x8c,       'MIN',     0,                'Minimum',  2,  1), #                               e2, e1     min(e1, e2)
+	(0x26,    'MINDEX',     0,        'MoveXToTopStack',  2,  1), #                                    k              ek
+	(0xe0,      'MIRP',     5,      'MoveIndirectRelPt',  1,  0), #                                 n, p               -
+	(0x4b,     'MPPEM',     0,      'MeasurePixelPerEm',  0,  1), #                                    -            ppem
+	(0x4c,       'MPS',     0,       'MeasurePointSize',  0,  1), #                                    -       pointSize
+	(0x3a,     'MSIRP',     1,    'MoveStackIndirRelPt',  2,  0), #                                 d, p               -
+	(0x63,       'MUL',     0,               'Multiply',  2,  1), #                               n2, n1    (n1 * n2)/64
+	(0x65,       'NEG',     0,                 'Negate',  1,  1), #                                    n              -n
+	(0x55,       'NEQ',     0,               'NotEqual',  2,  1), #                               e2, e1               b
+	(0x5c,       'NOT',     0,             'LogicalNot',  1,  1), #                                    e       ( not e )
+	(0x6c,    'NROUND',     2,                'NoRound',  1,  1), #                                   n1              n2
+	(0x56,       'ODD',     0,                    'Odd',  1,  1), #                                    e               b
+	(0x5b,        'OR',     0,              'LogicalOr',  2,  1), #                               e2, e1               b
+	(0x21,       'POP',     0,            'PopTopStack',  1,  0), #                                    e               -
+	(0x45,      'RCVT',     0,                'ReadCVT',  1,  1), #                             location           value
+	(0x7d,      'RDTG',     0,        'RoundDownToGrid',  0,  0), #                                    -               -
+	(0x7a,      'ROFF',     0,               'RoundOff',  0,  0), #                                    -               -
+	(0x8a,      'ROLL',     0,      'RollTopThreeStack',  3,  3), #                                a,b,c           b,a,c
+	(0x68,     'ROUND',     2,                  'Round',  1,  1), #                                   n1              n2
+	(0x43,        'RS',     0,              'ReadStore',  1,  1), #                                    n               v
+	(0x3d,      'RTDG',     0,      'RoundToDoubleGrid',  0,  0), #                                    -               -
+	(0x18,       'RTG',     0,            'RoundToGrid',  0,  0), #                                    -               -
+	(0x19,      'RTHG',     0,        'RoundToHalfGrid',  0,  0), #                                    -               -
+	(0x7c,      'RUTG',     0,          'RoundUpToGrid',  0,  0), #                                    -               -
+	(0x77,  'S45ROUND',     0,    'SuperRound45Degrees',  1,  0), #                                    n               -
+	(0x7e,     'SANGW',     0,         'SetAngleWeight',  1,  0), #                               weight               -
+	(0x85,  'SCANCTRL',     0,  'ScanConversionControl',  1,  0), #                                    n               -
+	(0x8d,  'SCANTYPE',     0,               'ScanType',  1,  0), #                                    n               -
+	(0x48,      'SCFS',     0,    'SetCoordFromStackFP',  2,  0), #                                 c, p               -
+	(0x1d,    'SCVTCI',     0,            'SetCVTCutIn',  1,  0), #                                    n               -
+	(0x5e,       'SDB',     0,   'SetDeltaBaseInGState',  1,  0), #                                    n               -
+	(0x86,    'SDPVTL',     1,   'SetDualPVectorToLine',  2,  0), #                               p2, p1               -
+	(0x5f,       'SDS',     0,  'SetDeltaShiftInGState',  1,  0), #                                    n               -
+	(0x0b,     'SFVFS',     0,    'SetFVectorFromStack',  2,  0), #                                 y, x               -
+	(0x04,    'SFVTCA',     1,       'SetFVectorToAxis',  0,  0), #                                    -               -
+	(0x08,     'SFVTL',     1,       'SetFVectorToLine',  2,  0), #                               p2, p1               -
+	(0x0e,    'SFVTPV',     0,    'SetFVectorToPVector',  0,  0), #                                    -               -
+	(0x34,       'SHC',     1,   'ShiftContourByLastPt',  1,  0), #                                    c               -
+	(0x32,       'SHP',     1,  'ShiftPointByLastPoint', -1,  0), #              p1, p2, ..., ploopvalue               -
+	(0x38,     'SHPIX',     0,       'ShiftZoneByPixel', -1,  0), #           d, p1, p2, ..., ploopvalue               -
+	(0x36,       'SHZ',     1,   'ShiftZoneByLastPoint',  1,  0), #                                    e               -
+	(0x17,     'SLOOP',     0,        'SetLoopVariable',  1,  0), #                                    n               -
+	(0x1a,       'SMD',     0,     'SetMinimumDistance',  1,  0), #                             distance               -
+	(0x0a,     'SPVFS',     0,    'SetPVectorFromStack',  2,  0), #                                 y, x               -
+	(0x02,    'SPVTCA',     1,       'SetPVectorToAxis',  0,  0), #                                    -               -
+	(0x06,     'SPVTL',     1,       'SetPVectorToLine',  2,  0), #                               p2, p1               -
+	(0x76,    'SROUND',     0,             'SuperRound',  1,  0), #                                    n               -
+	(0x10,      'SRP0',     0,           'SetRefPoint0',  1,  0), #                                    p               -
+	(0x11,      'SRP1',     0,           'SetRefPoint1',  1,  0), #                                    p               -
+	(0x12,      'SRP2',     0,           'SetRefPoint2',  1,  0), #                                    p               -
+	(0x1f,       'SSW',     0,         'SetSingleWidth',  1,  0), #                                    n               -
+	(0x1e,     'SSWCI',     0,    'SetSingleWidthCutIn',  1,  0), #                                    n               -
+	(0x61,       'SUB',     0,               'Subtract',  2,  1), #                               n2, n1       (n1 - n2)
+	(0x00,     'SVTCA',     1,      'SetFPVectorToAxis',  0,  0), #                                    -               -
+	(0x23,      'SWAP',     0,           'SwapTopStack',  2,  2), #                               e2, e1          e1, e2
+	(0x13,      'SZP0',     0,        'SetZonePointer0',  1,  0), #                                    n               -
+	(0x14,      'SZP1',     0,        'SetZonePointer1',  1,  0), #                                    n               -
+	(0x15,      'SZP2',     0,        'SetZonePointer2',  1,  0), #                                    n               -
+	(0x16,      'SZPS',     0,        'SetZonePointerS',  1,  0), #                                    n               -
+	(0x29,       'UTP',     0,              'UnTouchPt',  1,  0), #                                    p               -
+	(0x70,     'WCVTF',     0,       'WriteCVTInFUnits',  2,  0), #                                 n, l               -
+	(0x44,     'WCVTP',     0,       'WriteCVTInPixels',  2,  0), #                                 v, l               -
+	(0x42,        'WS',     0,             'WriteStore',  2,  0), #                                 v, l               -
+#	------  -----------  -----  ------------------------ ---  ------  ----------------------------------  --------------
+]
+
+
+def bitRepr(value, bits):
+	s = ""
+	for i in range(bits):
+		s = "01"[value & 0x1] + s
+		value = value >> 1
+	return s
+
+def makeOpcodeDict(instructionList):
+	opcodeDict = {}
+	for op, mnemonic, argbits, name, pops, pushes in instructionList:
+		if argbits:
+			argoffset = op
+			for i in range(1 << argbits):
+				opcodeDict[op+i] = mnemonic, argbits, argoffset, name
+		else:
+				opcodeDict[op] = mnemonic, 0, 0, name
+	return opcodeDict
+
+streamOpcodeDict = makeOpcodeDict(streamInstructions)
+opcodeDict = makeOpcodeDict(instructions)
+
+tt_instructions_error = "TT instructions error"
+
+
+class Program:
+	
+	def __init__(self):
+		pass
+	
+	def fromBytecode(self, bytecode):
+		self.bytecode = array.array("B")
+		self.bytecode.fromstring(bytecode)
+	
+	def fromAssembly(self, assembly):
+		self.assembly = assembly
+	
+	def getBytecode(self):
+		if not hasattr(self, "bytecode"):
+			self._assemble()
+		return self.bytecode.tostring()
+	
+	def getAssembly(self):
+		if not hasattr(self, "assembly"):
+			self._disassemble()
+		return self.assembly
+	
+	def _assemble(self):
+		xxx
+	
+	def _disassemble(self):
+		assembly = []
+		i = 0
+		bytecode = self.bytecode
+		numBytecode = len(bytecode)
+		while i < numBytecode:
+			op = bytecode[i]
+			arg = 0
+			try:
+				mnemonic, argbits, argoffset, name = opcodeDict[op]
+			except KeyError:
+				try:
+					mnemonic, argbits, argoffset, name = streamOpcodeDict[op]
+				except KeyError:
+					raise tt_instructions_error, "illegal opcode: 0x%.2x" % op
+				pushbytes = pushwords = 0
+				if argbits:
+					if mnemonic == "PUSHB":
+						pushbytes = op - argoffset + 1
+					else:
+						pushwords = op - argoffset + 1
+				else:
+					i = i + 1
+					if mnemonic == "NPUSHB":
+						pushbytes = bytecode[i]
+					else:
+						pushwords = bytecode[i]
+				i = i + 1
+				assembly.append(mnemonic + "[ ]")
+				for j in range(pushbytes):
+					assembly.append(`bytecode[i]`)
+					i = i + 1
+				for j in range(0, pushwords, 2):
+					assembly.append(`(bytecode[i] << 8) + bytecode[i+1]`)
+					i = i + 2
+			else:
+				if argbits:
+					assembly.append(mnemonic + "[%s]" % bitRepr(op - argoffset, argbits))
+				else:
+					assembly.append(mnemonic + "[ ]")
+				i = i + 1
+		self.assembly = assembly
+		del self.bytecode
+
+
+fpgm = '@\01476&%\037\023\022\015\014\005\004\002, \260\003%E#E#ah\212 Eh \212#D`D-,KRXED\033!!Y-,  EhD \260\001` E\260Fvh\030\212E`D-,\260\022+\260\002%E\260\002%Ej\260@\213`\260\002%#D!!!-,\260\023+\260\002%E\260\002%Ej\270\377\300\214`\260\002%#D!!!-,\261\000\003%EhTX\260\003%E\260\003%E`h \260\004%#D\260\004%#D\033\260\003% Eh \212#D\260\003%Eh`\260\003%#DY-,\260\003% Eh \212#D\260\003%Eh`\260\003%#D-,KRXED\033!!Y-,F#F`\212\212F# F\212`\212a\270\377\200b# \020#\212\261KK\212pE` \260\000PX\260\001a\270\377\272\213\033\260F\214Y\260\020`h\001:-, E\260\003%FRX?\033!\021Y-,KS#KQZX E\212`D\033!!Y-,KS#KQZX8\033!!Y-'
+gpgm = '@\022\011\003\207@\005\200\004\207\000\010\007\202\001\010\004\202\000\010\000\020\320\355\020\336\355\001\020\336\375\032}\336\032\030\375\31610'
+
+p = Program()
+p.fromBytecode(fpgm)
+for line in p.getAssembly():
+	print line
+