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/__init__.py b/Lib/fontTools/ttLib/__init__.py
new file mode 100644
index 0000000..e087465
--- /dev/null
+++ b/Lib/fontTools/ttLib/__init__.py
@@ -0,0 +1,555 @@
+"""ttLib -- a package for dealing with TrueType fonts.
+
+This package offers translators to convert TrueType fonts to Python 
+objects and vice versa, and additionally from Python to XML and vice versa.
+
+Example interactive session:
+
+Python 1.5.2c1 (#43, Mar  9 1999, 13:06:43)  [CW PPC w/GUSI w/MSL]
+Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
+>>> from fontTools import ttLib
+>>> tt = ttLib.TTFont("afont.ttf")
+>>> tt['maxp'].numGlyphs
+242
+>>> tt['OS/2'].achVendID
+'B&H\000'
+>>> tt['head'].unitsPerEm
+2048
+>>> tt.saveXML("afont.xml")
+Dumping 'LTSH' table...
+Dumping 'OS/2' table...
+Dumping 'VDMX' table...
+Dumping 'cmap' table...
+Dumping 'cvt ' table...
+Dumping 'fpgm' table...
+Dumping 'glyf' table...
+Dumping 'hdmx' table...
+Dumping 'head' table...
+Dumping 'hhea' table...
+Dumping 'hmtx' table...
+Dumping 'loca' table...
+Dumping 'maxp' table...
+Dumping 'name' table...
+Dumping 'post' table...
+Dumping 'prep' table...
+>>> tt2 = ttLib.TTFont()
+>>> tt2.importXML("afont.xml")
+>>> tt2['maxp'].numGlyphs
+242
+>>> 
+
+"""
+
+__author__ = "Just van Rossum, just@letterror.com"
+__version__ = "1.0a5"
+
+
+import os
+import stat
+import types
+
+class TTLibError(Exception): pass
+
+
+class TTFont:
+	
+	"""The main font object. It manages file input and output, and offers
+	a convenient way of accessing tables. 
+	Tables will be only decompiled when neccesary, ie. when they're actually
+	accessed. This means that simple operations can be extremely fast.
+	"""
+	
+	def __init__(self, file=None, res_name_or_index=None, 
+			sfntVersion="\000\001\000\000", checkchecksums=0, verbose=0):
+		
+		"""The constructor can be called with a few different arguments.
+		When reading a font from disk, 'file' should be either a pathname
+		pointing to a file, or a readable file object. 
+		
+		It we're running on a Macintosh, 'res_name_or_index' maybe an sfnt 
+		resource name or an sfnt resource index number or zero. The latter 
+		case will cause TTLib to autodetect whether the file is a flat file 
+		or a suitcase. (If it's a suitcase, only the first 'sfnt' resource
+		will be read!)
+		
+		The 'checkchecksums' argument is used to specify how sfnt
+		checksums are treated upon reading a file from disk:
+			0: don't check (default)
+			1: check, print warnings if a wrong checksum is found (default)
+			2: check, raise an exception if a wrong checksum is found.
+		
+		The TTFont constructor can also be called without a 'file' 
+		argument: this is the way to create a new empty font. 
+		In this case you can optionally supply the 'sfntVersion' argument.
+		"""
+		
+		import sfnt
+		self.verbose = verbose
+		self.tables = {}
+		self.reader = None
+		if not file:
+			self.sfntVersion = sfntVersion
+			return
+		if type(file) == types.StringType:
+			if os.name == "mac" and res_name_or_index is not None:
+				# on the mac, we deal with sfnt resources as well as flat files
+				import macUtils
+				if res_name_or_index == 0:
+					if macUtils.getSFNTResIndices(file):
+						# get the first available sfnt font.
+						file = macUtils.SFNTResourceReader(file, 1)
+					else:
+						file = open(file, "rb")
+				else:
+					file = macUtils.SFNTResourceReader(file, res_name_or_index)
+			else:
+				file = open(file, "rb")
+		else:
+			pass # assume "file" is a readable file object
+		self.reader = sfnt.SFNTReader(file, checkchecksums)
+		self.sfntVersion = self.reader.sfntVersion
+	
+	def close(self):
+		"""If we still have a reader object, close it."""
+		if self.reader is not None:
+			self.reader.close()
+	
+	def save(self, file, make_suitcase=0):
+		"""Save the font to disk. Similarly to the constructor, 
+		the 'file' argument can be either a pathname or a writable
+		file object.
+		
+		On the Mac, if make_suitcase is non-zero, a suitcase file will
+		we made instead of a flat .ttf file. 
+		"""
+		import sfnt
+		if type(file) == types.StringType:
+			if os.name == "mac" and make_suitcase:
+				import macUtils
+				file = macUtils.SFNTResourceWriter(file, self)
+			else:
+				file = open(file, "wb")
+				if os.name == "mac":
+					import macfs
+					fss = macfs.FSSpec(file.name)
+					fss.SetCreatorType('mdos', 'BINA')
+		else:
+			pass # assume "file" is a writable file object
+		
+		tags = self.keys()
+		numTables = len(tags)
+		writer = sfnt.SFNTWriter(file, numTables, self.sfntVersion)
+		
+		done = []
+		for tag in tags:
+			self._writeTable(tag, writer, done)
+		
+		writer.close()
+	
+	def saveXML(self, file, progress=None, tables=None):
+		"""Export the font as an XML-based text file.
+		"""
+		import xmlWriter
+		writer = xmlWriter.XMLWriter(file)
+		writer.begintag("ttFont", sfntVersion=`self.sfntVersion`[1:-1], 
+				ttlibVersion=__version__)
+		writer.newline()
+		writer.newline()
+		if not tables:
+			tables = self.keys()
+		numTables = len(tables)
+		numGlyphs = self['maxp'].numGlyphs
+		if progress:
+			progress.set(0, numTables * numGlyphs)
+		for i in range(numTables):
+			tag = tables[i]
+			table = self[tag]
+			report = "Dumping '%s' table..." % tag
+			if progress:
+				progress.setlabel(report)
+			elif self.verbose:
+				debugmsg(report)
+			else:
+				print report
+			xmltag = tag2xmltag(tag)
+			writer.begintag(xmltag)
+			writer.newline()
+			if tag == "glyf":
+				table.toXML(writer, self, progress)
+			elif tag == "CFF ":
+				table.toXML(writer, self, progress)
+			else:
+				table.toXML(writer, self)
+			writer.endtag(xmltag)
+			writer.newline()
+			writer.newline()
+			if progress:
+				progress.set(i * numGlyphs, numTables * numGlyphs)
+		writer.endtag("ttFont")
+		writer.newline()
+		writer.close()
+		if self.verbose:
+			debugmsg("Done dumping XML")
+	
+	def importXML(self, file, progress=None):
+		"""Import an XML-based text file, so as to recreate
+		a font object.
+		"""
+		if self.tables:
+			raise error, "Can't import XML into existing font."
+		import xmlImport
+		from xml.parsers.xmlproc import xmlproc
+		builder = xmlImport.XMLApplication(self, progress)
+		if progress:
+			progress.set(0, os.stat(file)[stat.ST_SIZE] / 100 or 1)
+		proc = xmlImport.UnicodeProcessor()
+		proc.set_application(builder)
+		proc.set_error_handler(xmlImport.XMLErrorHandler(proc))
+		dir, filename = os.path.split(file)
+		if dir:
+			olddir = os.getcwd()
+			os.chdir(dir)
+		try:
+			proc.parse_resource(filename)
+			root = builder.root
+		finally:
+			if dir:
+				os.chdir(olddir)
+			# remove circular references
+			proc.deref()
+			del builder.progress
+	
+	def isLoaded(self, tag):
+		"""Return true if the table identified by 'tag' has been 
+		decompiled and loaded into memory."""
+		return self.tables.has_key(tag)
+	
+	def has_key(self, tag):
+		"""Pretend we're a dictionary."""
+		if self.isLoaded(tag):
+			return 1
+		elif self.reader and self.reader.has_key(tag):
+			return 1
+		else:
+			return 0
+	
+	def keys(self):
+		"""Pretend we're a dictionary."""
+		keys = self.tables.keys()
+		if self.reader:
+			for key in self.reader.keys():
+				if key not in keys:
+					keys.append(key)
+		keys.sort()
+		return keys
+	
+	def __len__(self):
+		"""Pretend we're a dictionary."""
+		return len(self.keys())
+	
+	def __getitem__(self, tag):
+		"""Pretend we're a dictionary."""
+		try:
+			return self.tables[tag]
+		except KeyError:
+			if self.reader is not None:
+				if self.verbose:
+					debugmsg("reading '%s' table from disk" % tag)
+				data = self.reader[tag]
+				tableclass = getTableClass(tag)
+				table = tableclass(tag)
+				self.tables[tag] = table
+				if self.verbose:
+					debugmsg("decompiling '%s' table" % tag)
+				table.decompile(data, self)
+				return table
+			else:
+				raise KeyError, "'%s' table not found" % tag
+	
+	def __setitem__(self, tag, table):
+		"""Pretend we're a dictionary."""
+		self.tables[tag] = table
+	
+	def __delitem__(self, tag):
+		"""Pretend we're a dictionary."""
+		del self.tables[tag]
+	
+	def setGlyphOrder(self, glyphOrder):
+		self.glyphOrder = glyphOrder
+		if self.has_key('CFF '):
+			self['CFF '].setGlyphOrder(glyphOrder)
+		if self.has_key('glyf'):
+			self['glyf'].setGlyphOrder(glyphOrder)
+	
+	def getGlyphOrder(self):
+		if not hasattr(self, "glyphOrder"):
+			if self.has_key('CFF '):
+				# CFF OpenType font
+				self.glyphOrder = self['CFF '].getGlyphOrder()
+			else:
+				# TrueType font
+				glyphOrder = self['post'].getGlyphOrder()
+				if glyphOrder is None:
+					#
+					# No names found in the 'post' table.
+					# Try to create glyph names from the unicode cmap (if available) 
+					# in combination with the Adobe Glyph List (AGL).
+					#
+					self._getGlyphNamesFromCmap()
+				else:
+					self.glyphOrder = glyphOrder
+			# XXX what if a font contains 'glyf'/'post' table *and* CFF?
+		return self.glyphOrder
+	
+	def _getGlyphNamesFromCmap(self):
+		# Make up glyph names based on glyphID, which will be used 
+		# in case we don't find a unicode cmap.
+		numGlyphs = int(self['maxp'].numGlyphs)
+		glyphOrder = [None] * numGlyphs
+		glyphOrder[0] = ".notdef"
+		for i in range(1, numGlyphs):
+			glyphOrder[i] = "glyph%.5d" % i
+		# Set the glyph order, so the cmap parser has something
+		# to work with
+		self.glyphOrder = glyphOrder
+		# Get the temporary cmap (based on the just invented names)
+		tempcmap = self['cmap'].getcmap(3, 1)
+		if tempcmap is not None:
+			# we have a unicode cmap
+			import agl, string
+			cmap = tempcmap.cmap
+			# create a reverse cmap dict
+			reversecmap = {}
+			for unicode, name in cmap.items():
+				reversecmap[name] = unicode
+			assert len(reversecmap) == len(cmap)
+			for i in range(numGlyphs):
+				tempName = glyphOrder[i]
+				if reversecmap.has_key(tempName):
+					unicode = reversecmap[tempName]
+					if agl.UV2AGL.has_key(unicode):
+						# get name from the Adobe Glyph List
+						glyphOrder[i] = agl.UV2AGL[unicode]
+					else:
+						# create uni<CODE> name
+						glyphOrder[i] = "uni" + string.upper(string.zfill(hex(unicode)[2:], 4))
+			# Delete the cmap table from the cache, so it can be 
+			# parsed again with the right names.
+			del self.tables['cmap']
+		else:
+			pass # no unicode cmap available, stick with the invented names
+		self.glyphOrder = glyphOrder
+	
+	def getGlyphNames(self):
+		"""Get a list of glyph names, sorted alphabetically."""
+		glyphNames = self.getGlyphOrder()[:]
+		glyphNames.sort()
+		return glyphNames
+	
+	def getGlyphNames2(self):
+		"""Get a list of glyph names, sorted alphabetically, but not case sensitive."""
+		from fontTools.misc import textTools
+		return textTools.caselessSort(self.getGlyphOrder())
+	
+	def getGlyphName(self, glyphID):
+		return self.getGlyphOrder()[glyphID]
+	
+	def getGlyphID(self, glyphName):
+		if not hasattr(self, "_reverseGlyphOrderDict"):
+			self._buildReverseGlyphOrderDict()
+		glyphOrder = self.getGlyphOrder()
+		d = self._reverseGlyphOrderDict
+		if not d.has_key(glyphName):
+			if glyphName in glyphOrder:
+				self._buildReverseGlyphOrderDict()
+				return self.getGlyphID(glyphName)
+			else:
+				raise KeyError, glyphName
+		glyphID = d[glyphName]
+		if glyphName <> glyphOrder[glyphID]:
+			self._buildReverseGlyphOrderDict()
+			return self.getGlyphID(glyphName)
+		return glyphID
+	
+	def _buildReverseGlyphOrderDict(self):
+		self._reverseGlyphOrderDict = d = {}
+		glyphOrder = self.getGlyphOrder()
+		for glyphID in range(len(glyphOrder)):
+			d[glyphOrder[glyphID]] = glyphID
+	
+	def _writeTable(self, tag, writer, done):
+		"""Internal helper function for self.save(). Keeps track of 
+		inter-table dependencies.
+		"""
+		if tag in done:
+			return
+		tableclass = getTableClass(tag)
+		for masterTable in tableclass.dependencies:
+			if masterTable not in done:
+				if self.has_key(masterTable):
+					self._writeTable(masterTable, writer, done)
+				else:
+					done.append(masterTable)
+		tabledata = self._getTableData(tag)
+		if self.verbose:
+			debugmsg("writing '%s' table to disk" % tag)
+		writer[tag] = tabledata
+		done.append(tag)
+	
+	def _getTableData(self, tag):
+		"""Internal helper function. Returns raw table data,
+		whether compiled or directly read from disk.
+		"""
+		if self.isLoaded(tag):
+			if self.verbose:
+				debugmsg("compiling '%s' table" % tag)
+			return self.tables[tag].compile(self)
+		elif self.reader and self.reader.has_key(tag):
+			if self.verbose:
+				debugmsg("reading '%s' table from disk" % tag)
+			return self.reader[tag]
+		else:
+			raise KeyError, tag
+
+
+def _test_endianness():
+	"""Test the endianness of the machine. This is crucial to know
+	since TrueType data is always big endian, even on little endian
+	machines. There are quite a few situations where we explicitly
+	need to swap some bytes.
+	"""
+	import struct
+	data = struct.pack("h", 0x01)
+	if data == "\000\001":
+		return "big"
+	elif data == "\001\000":
+		return "little"
+	else:
+		assert 0, "endian confusion!"
+
+endian = _test_endianness()
+
+
+def getTableModule(tag):
+	"""Fetch the packer/unpacker module for a table. 
+	Return None when no module is found.
+	"""
+	import imp
+	import tables
+	py_tag = tag2identifier(tag)
+	try:
+		f, path, kind = imp.find_module(py_tag, tables.__path__)
+		if f:
+			f.close()
+	except ImportError:
+		return None
+	else:
+		module = __import__("fontTools.ttLib.tables." + py_tag)
+		return getattr(tables, py_tag)
+
+
+def getTableClass(tag):
+	"""Fetch the packer/unpacker class for a table. 
+	Return None when no class is found.
+	"""
+	module = getTableModule(tag)
+	if module is None:
+		from tables.DefaultTable import DefaultTable
+		return DefaultTable
+	py_tag = tag2identifier(tag)
+	tableclass = getattr(module, "table_" + py_tag)
+	return tableclass
+
+
+def newtable(tag):
+	"""Return a new instance of a table."""
+	tableclass = getTableClass(tag)
+	return tableclass(tag)
+
+
+def _escapechar(c):
+	"""Helper function for tag2identifier()"""
+	import re
+	if re.match("[a-z0-9]", c):
+		return "_" + c
+	elif re.match("[A-Z]", c):
+		return c + "_"
+	else:
+		return hex(ord(c))[2:]
+
+
+def tag2identifier(tag):
+	"""Convert a table tag to a valid (but UGLY) python identifier, 
+	as well as a filename that's guaranteed to be unique even on a 
+	caseless file system. Each character is mapped to two characters.
+	Lowercase letters get an underscore before the letter, uppercase
+	letters get an underscore after the letter. Trailing spaces are
+	trimmed. Illegal characters are escaped as two hex bytes. If the
+	result starts with a number (as the result of a hex escape), an
+	extra underscore is prepended. Examples: 
+		'glyf' -> '_g_l_y_f'
+		'cvt ' -> '_c_v_t'
+		'OS/2' -> 'O_S_2f_2'
+	"""
+	import re
+	assert len(tag) == 4, "tag should be 4 characters long"
+	while len(tag) > 1 and tag[-1] == ' ':
+		tag = tag[:-1]
+	ident = ""
+	for c in tag:
+		ident = ident + _escapechar(c)
+	if re.match("[0-9]", ident):
+		ident = "_" + ident
+	return ident
+
+
+def identifier2tag(ident):
+	"""the opposite of tag2identifier()"""
+	import string
+	if len(ident) % 2 and ident[0] == "_":
+		ident = ident[1:]
+	assert not (len(ident) % 2)
+	tag = ""
+	for i in range(0, len(ident), 2):
+		if ident[i] == "_":
+			tag = tag + ident[i+1]
+		elif ident[i+1] == "_":
+			tag = tag + ident[i]
+		else:
+			# assume hex
+			tag = tag + chr(string.atoi(ident[i:i+2], 16))
+	# append trailing spaces
+	tag = tag + (4 - len(tag)) * ' '
+	return tag
+
+
+def tag2xmltag(tag):
+	"""Similarly to tag2identifier(), this converts a TT tag
+	to a valid XML element name. Since XML element names are
+	case sensitive, this is a fairly simple/readable translation.
+	"""
+	import string, re
+	if tag == "OS/2":
+		return "OS_2"
+	if re.match("[A-Za-z_][A-Za-z_0-9]* *$", tag):
+		return string.strip(tag)
+	else:
+		return tag2identifier(tag)
+
+
+def xmltag2tag(tag):
+	"""The opposite of tag2xmltag()"""
+	if tag == "OS_2":
+		return "OS/2"
+	if len(tag) == 8:
+		return identifier2tag(tag)
+	else:
+		return tag + " " * (4 - len(tag))
+	return tag
+
+
+def debugmsg(msg):
+	import time
+	print msg + time.strftime("  (%H:%M:%S)", time.localtime(time.time()))
+
+
diff --git a/Lib/fontTools/ttLib/macUtils.py b/Lib/fontTools/ttLib/macUtils.py
new file mode 100644
index 0000000..bace3b4
--- /dev/null
+++ b/Lib/fontTools/ttLib/macUtils.py
@@ -0,0 +1,212 @@
+"""ttLib.macUtils.py -- Various Mac-specific stuff."""
+
+
+import os
+if os.name <> "mac":
+	raise ImportError, "This module is Mac-only!"
+
+import Res, macfs
+import cStringIO
+
+
+def getSFNTResIndices(path):
+	"""Determine whether a file has a resource fork or not."""
+	fss = macfs.FSSpec(path)
+	try:
+		resref = Res.FSpOpenResFile(fss, 1)  # read only
+	except Res.Error:
+		return []
+	Res.UseResFile(resref)
+	numSFNTs = Res.Count1Resources('sfnt')
+	Res.CloseResFile(resref)
+	return range(1, numSFNTs + 1)
+
+
+def openTTFonts(path):
+	"""Given a pathname, return a list of TTFont objects. In the case 
+	of a flat TTF/OTF file, the list will contain just one font object;
+	but in the case of a Mac font suitcase it will contain as many
+	font objects as there are sfnt resources in the file.
+	"""
+	from fontTools import ttLib
+	fonts = []
+	sfnts = getSFNTResIndices(path)
+	if not sfnts:
+		fonts.append(ttLib.TTFont(path))
+	else:
+		for index in sfnts:
+			fonts.append(ttLib.TTFont(path, index))
+		if not fonts:
+			raise ttLib.TTLibError, "no fonts found in file '%s'" % path
+	return fonts
+
+
+class ProgressBar:
+	
+	def __init__(self, title, maxval=100):
+		import EasyDialogs
+		self.bar = EasyDialogs.ProgressBar(title, maxval=maxval)
+	
+	def set(self, val, maxval=None):
+		if maxval <> None:
+			self.bar.maxval = maxval
+		self.bar.set(val)
+	
+	def increment(self, val=1):
+		self.bar.inc(val)
+	
+	def setlabel(self, text):
+		self.bar.label(text)
+	
+	def close(self):
+		self.bar.d.HideWindow()
+		del self.bar
+
+
+class SFNTResourceReader:
+	
+	"""Simple (Mac-only) read-only file wrapper for 'sfnt' resources."""
+	
+	def __init__(self, path, res_name_or_index):
+		fss = macfs.FSSpec(path)
+		resref = Res.FSpOpenResFile(fss, 1)  # read-only
+		Res.UseResFile(resref)
+		if type(res_name_or_index) == type(""):
+			res = Res.Get1NamedResource('sfnt', res_name_or_index)
+		else:
+			res = Res.Get1IndResource('sfnt', res_name_or_index)
+		self.file = cStringIO.StringIO(res.data)
+		Res.CloseResFile(resref)
+		self.name = path
+	
+	def __getattr__(self, attr):
+		# cheap inheritance
+		return getattr(self.file, attr)
+
+
+class SFNTResourceWriter:
+	
+	"""Simple (Mac-only) file wrapper for 'sfnt' resources."""
+	
+	def __init__(self, path, ttFont, res_id=None):
+		self.file = cStringIO.StringIO()
+		self.name = path
+		self.closed = 0
+		fullname = ttFont['name'].getname(4, 1, 0) # Full name, mac, default encoding
+		familyname = ttFont['name'].getname(1, 1, 0) # Fam. name, mac, default encoding
+		psname = ttFont['name'].getname(6, 1, 0) # PostScript name, etc.
+		if fullname is None or fullname is None or psname is None:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "can't make 'sfnt' resource, no Macintosh 'name' table found"
+		self.fullname = fullname.string
+		self.familyname = familyname.string
+		self.psname = psname.string
+		if self.familyname <> self.psname[:len(self.familyname)]:
+			# ugh. force fam name to be the same as first part of ps name,
+			# fondLib otherwise barfs.
+			for i in range(min(len(self.psname), len(self.familyname))):
+				if self.familyname[i] <> self.psname[i]:
+					break
+			self.familyname = self.psname[:i]
+		
+		self.ttFont = ttFont
+		self.res_id = res_id
+		fss = macfs.FSSpec(self.name)
+		if os.path.exists(self.name):
+			os.remove(self.name)
+		Res.FSpCreateResFile(fss, 'DMOV', 'FFIL', 0)
+		self.resref = Res.FSpOpenResFile(fss, 3)  # exclusive read/write permission
+	
+	def close(self):
+		if self.closed:
+			return
+		Res.UseResFile(self.resref)
+		try:
+			res = Res.Get1NamedResource('sfnt', self.fullname)
+		except Res.Error:
+			pass
+		else:
+			res.RemoveResource()
+		res = Res.Resource(self.file.getvalue())
+		if self.res_id is None:
+			self.res_id = Res.Unique1ID('sfnt')
+		res.AddResource('sfnt', self.res_id, self.fullname)
+		res.ChangedResource()
+		
+		self.createFond()
+		del self.ttFont
+		Res.CloseResFile(self.resref)
+		self.file.close()
+		self.closed = 1
+	
+	def createFond(self):
+		fond_res = Res.Resource("")
+		fond_res.AddResource('FOND', self.res_id, self.fullname)
+		
+		from fontTools import fondLib
+		fond = fondLib.FontFamily(fond_res, "w")
+		
+		fond.ffFirstChar = 0
+		fond.ffLastChar = 255
+		fond.fondClass = 0
+		fond.fontAssoc = [(0, 0, self.res_id)]
+		fond.ffFlags = 20480	# XXX ???
+		fond.ffIntl = (0, 0)
+		fond.ffLeading = 0
+		fond.ffProperty = (0, 0, 0, 0, 0, 0, 0, 0, 0)
+		fond.ffVersion = 0
+		fond.glyphEncoding = {}
+		if self.familyname == self.psname:
+			fond.styleIndices = (1,) * 48  # uh-oh, fondLib is too dumb.
+		else:
+			fond.styleIndices = (2,) * 48
+		fond.styleStrings = []
+		fond.boundingBoxes = None
+		fond.ffFamID = self.res_id
+		fond.changed = 1
+		fond.glyphTableOffset = 0
+		fond.styleMappingReserved = 0
+		
+		# calc:
+		scale = 4096.0 / self.ttFont['head'].unitsPerEm
+		fond.ffAscent = scale * self.ttFont['hhea'].ascent
+		fond.ffDescent = scale * self.ttFont['hhea'].descent
+		fond.ffWidMax = scale * self.ttFont['hhea'].advanceWidthMax
+		
+		fond.ffFamilyName = self.familyname
+		fond.psNames = {0: self.psname}
+		
+		fond.widthTables = {}
+		fond.kernTables = {}
+		cmap = self.ttFont['cmap'].getcmap(1, 0)
+		if cmap:
+			names = {}
+			for code, name in cmap.cmap.items():
+				names[name] = code
+			if self.ttFont.has_key('kern'):
+				kern = self.ttFont['kern'].getkern(0)
+				if kern:
+					fondkerning = []
+					for (left, right), value in kern.kernTable.items():
+						if names.has_key(left) and names.has_key(right):
+							fondkerning.append((names[left], names[right], scale * value))
+					fondkerning.sort()
+					fond.kernTables = {0: fondkerning}
+			if self.ttFont.has_key('hmtx'):
+				hmtx = self.ttFont['hmtx']
+				fondwidths = [2048] * 256 + [0, 0]  # default width, + plus two zeros.
+				for name, (width, lsb) in hmtx.metrics.items():
+					if names.has_key(name):
+						fondwidths[names[name]] = scale * width
+				fond.widthTables = {0: fondwidths}
+		fond.save()
+	
+	def __del__(self):
+		if not self.closed:
+			self.close()
+	
+	def __getattr__(self, attr):
+		# cheap inheritance
+		return getattr(self.file, attr)
+	
+
diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py
new file mode 100644
index 0000000..2fdeb99
--- /dev/null
+++ b/Lib/fontTools/ttLib/sfnt.py
@@ -0,0 +1,230 @@
+"""ttLib/sfnt.py -- low-level module to deal with the sfnt file format.
+
+Defines two public classes:
+	SFNTReader
+	SFNTWriter
+
+(Normally you don't have to use these classes explicitly; they are 
+used automatically by ttLib.TTFont.)
+
+The reading and writing of sfnt files is separated in two distinct 
+classes, since whenever to number of tables changes or whenever
+a table's length chages you need to rewrite the whole file anyway.
+"""
+
+import struct, sstruct
+import Numeric
+import os
+
+class SFNTReader:
+	
+	def __init__(self, file, checkchecksums=1):
+		self.file = file
+		self.checkchecksums = checkchecksums
+		data = self.file.read(sfntDirectorySize)
+		if len(data) <> sfntDirectorySize:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "Not a TrueType or OpenType font (not enough data)"
+		sstruct.unpack(sfntDirectoryFormat, data, self)
+		if self.sfntVersion not in ("\000\001\000\000", "OTTO", "true"):
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "Not a TrueType or OpenType font (bad sfntVersion)"
+		self.tables = {}
+		for i in range(self.numTables):
+			entry = SFNTDirectoryEntry()
+			entry.fromfile(self.file)
+			self.tables[entry.tag] = entry
+	
+	def has_key(self, tag):
+		return self.tables.has_key(tag)
+	
+	def keys(self):
+		return self.tables.keys()
+	
+	def __getitem__(self, tag):
+		"""Fetch the raw table data."""
+		entry = self.tables[tag]
+		self.file.seek(entry.offset)
+		data = self.file.read(entry.length)
+		if self.checkchecksums:
+			if tag == 'head':
+				# Beh: we have to special-case the 'head' table.
+				checksum = calcchecksum(data[:8] + '\0\0\0\0' + data[12:])
+			else:
+				checksum = calcchecksum(data)
+			if self.checkchecksums > 1:
+				# Be obnoxious, and barf when it's wrong
+				assert checksum == entry.checksum, "bad checksum for '%s' table" % tag
+			elif checksum <> entry.checkSum:
+				# Be friendly, and just print a warning.
+				print "bad checksum for '%s' table" % tag
+		return data
+	
+	def close(self):
+		self.file.close()
+
+
+class SFNTWriter:
+	
+	def __init__(self, file, numTables, sfntVersion="\000\001\000\000"):
+		self.file = file
+		self.numTables = numTables
+		self.sfntVersion = sfntVersion
+		self.searchRange, self.entrySelector, self.rangeShift = getsearchrange(numTables)
+		self.nextTableOffset = sfntDirectorySize + numTables * sfntDirectoryEntrySize
+		# clear out directory area
+		self.file.seek(self.nextTableOffset)
+		# make sure we're actually where we want to be. (XXX old cStringIO bug)
+		self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
+		self.tables = {}
+	
+	def __setitem__(self, tag, data):
+		"""Write raw table data to disk."""
+		if self.tables.has_key(tag):
+			# We've written this table to file before. If the length
+			# of the data is still the same, we allow overwritng it.
+			entry = self.tables[tag]
+			if len(data) <> entry.length:
+				from fontTools import ttLib
+				raise ttLib.TTLibError, "cannot rewrite '%s' table: length does not match directory entry" % tag
+		else:
+			entry = SFNTDirectoryEntry()
+			entry.tag = tag
+			entry.offset = self.nextTableOffset
+			entry.length = len(data)
+			self.nextTableOffset = self.nextTableOffset + ((len(data) + 3) & ~3)
+		self.file.seek(entry.offset)
+		self.file.write(data)
+		self.file.seek(self.nextTableOffset)
+		# make sure we're actually where we want to be. (XXX old cStringIO bug)
+		self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
+		
+		if tag == 'head':
+			entry.checkSum = calcchecksum(data[:8] + '\0\0\0\0' + data[12:])
+		else:
+			entry.checkSum = calcchecksum(data)
+		self.tables[tag] = entry
+	
+	def close(self):
+		"""All tables must have been written to disk. Now write the
+		directory.
+		"""
+		tables = self.tables.items()
+		tables.sort()
+		if len(tables) <> self.numTables:
+			from fontTools import ttLib
+			raise ttLib.TTLibError, "wrong number of tables; expected %d, found %d" % (self.numTables, len(tables))
+		
+		directory = sstruct.pack(sfntDirectoryFormat, self)
+		
+		self.file.seek(sfntDirectorySize)
+		for tag, entry in tables:
+			directory = directory + entry.tostring()
+		self.calcmasterchecksum(directory)
+		self.file.seek(0)
+		self.file.write(directory)
+		self.file.close()
+	
+	def calcmasterchecksum(self, directory):
+		# calculate checkSumAdjustment
+		tags = self.tables.keys()
+		checksums = Numeric.zeros(len(tags)+1)
+		for i in range(len(tags)):
+			checksums[i] = self.tables[tags[i]].checkSum
+		
+		directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize
+		assert directory_end == len(directory)
+		
+		checksums[-1] = calcchecksum(directory)
+		checksum = Numeric.add.reduce(checksums)
+		# BiboAfba!
+		checksumadjustment = Numeric.array(0xb1b0afba) - checksum
+		# write the checksum to the file
+		self.file.seek(self.tables['head'].offset + 8)
+		self.file.write(struct.pack("l", checksumadjustment))
+		
+
+# -- sfnt directory helpers and cruft
+
+sfntDirectoryFormat = """
+		> # big endian
+		sfntVersion: 	4s
+		numTables:		H		# number of tables
+		searchRange:	H		# (max2 <= numTables)*16
+		entrySelector:	H		# log2(max2 <= numTables)
+		rangeShift:		H		# numTables*16-searchRange
+"""
+
+sfntDirectorySize = sstruct.calcsize(sfntDirectoryFormat)
+
+sfntDirectoryEntryFormat = """
+		> # big endian
+		tag:		4s
+		checkSum:	l
+		offset:		l
+		length:		l
+"""
+
+sfntDirectoryEntrySize = sstruct.calcsize(sfntDirectoryEntryFormat)
+
+class SFNTDirectoryEntry:
+	
+	def fromfile(self, file):
+		sstruct.unpack(sfntDirectoryEntryFormat, 
+				file.read(sfntDirectoryEntrySize), self)
+	
+	def fromstring(self, str):
+		sstruct.unpack(sfntDirectoryEntryFormat, str, self)
+	
+	def tostring(self):
+		return sstruct.pack(sfntDirectoryEntryFormat, self)
+	
+	def __repr__(self):
+		if hasattr(self, "tag"):
+			return "<SFNTDirectoryEntry '%s' at %x>" % (self.tag, id(self))
+		else:
+			return "<SFNTDirectoryEntry at %x>" % id(self)
+
+
+def calcchecksum(data, start=0):
+	"""Calculate the checksum for an arbitrary block of data.
+	Optionally takes a 'start' argument, which allows you to
+	calculate a checksum in chunks by feeding it a previous
+	result.
+	
+	If the data length is not a multiple of four, it assumes
+	it is to be padded with null byte. 
+	"""
+	from fontTools import ttLib
+	remainder = len(data) % 4
+	if remainder:
+		data = data + '\0' * (4-remainder)
+	a = Numeric.fromstring(struct.pack(">l", start) + data, Numeric.Int32)
+	if ttLib.endian <> "big":
+		a = a.byteswapped()
+	return Numeric.add.reduce(a)
+
+
+def maxpoweroftwo(x):
+	"""Return the highest exponent of two, so that
+	(2 ** exponent) <= x
+	"""
+	exponent = 0
+	while x:
+		x = x >> 1
+		exponent = exponent + 1
+	return exponent - 1
+
+
+def getsearchrange(n):
+	"""Calculate searchRange, entrySelector, rangeShift for the
+	sfnt directory. 'n' is the number of tables.
+	"""
+	# This stuff needs to be stored in the file, because?
+	import math
+	exponent = maxpoweroftwo(n)
+	searchRange = (2 ** exponent) * 16
+	entrySelector = exponent
+	rangeShift = n * 16 - searchRange
+	return searchRange, entrySelector, rangeShift
+
diff --git a/Lib/fontTools/ttLib/standardGlyphOrder.py b/Lib/fontTools/ttLib/standardGlyphOrder.py
new file mode 100644
index 0000000..4ef2eb4
--- /dev/null
+++ b/Lib/fontTools/ttLib/standardGlyphOrder.py
@@ -0,0 +1,263 @@
+# XXX is this *really* correct?
+
+standardGlyphOrder = [
+	".notdef",				# 0 
+	".null",				# 1 
+	"nonmarkingreturn",		# 2 
+	"space",				# 3 
+	"exclam",				# 4 
+	"quotedbl",				# 5 
+	"numbersign",			# 6 
+	"dollar",				# 7 
+	"percent",				# 8 
+	"ampersand",			# 9 
+	"quotesingle",			# 10 
+	"parenleft",			# 11 
+	"parenright",			# 12 
+	"asterisk",				# 13 
+	"plus",					# 14 
+	"comma",				# 15 
+	"hyphen",				# 16 
+	"period",				# 17 
+	"slash",				# 18 
+	"zero",					# 19 
+	"one",					# 20 
+	"two",					# 21 
+	"three",				# 22 
+	"four",					# 23 
+	"five",					# 24 
+	"six",					# 25 
+	"seven",				# 26 
+	"eight",				# 27 
+	"nine",					# 28 
+	"colon",				# 29 
+	"semicolon",			# 30 
+	"less",					# 31 
+	"equal",				# 32 
+	"greater",				# 33 
+	"question",				# 34 
+	"at",					# 35 
+	"A",					# 36 
+	"B",					# 37 
+	"C",					# 38 
+	"D",					# 39 
+	"E",					# 40 
+	"F",					# 41 
+	"G",					# 42 
+	"H",					# 43 
+	"I",					# 44 
+	"J",					# 45 
+	"K",					# 46 
+	"L",					# 47 
+	"M",					# 48 
+	"N",					# 49 
+	"O",					# 50 
+	"P",					# 51 
+	"Q",					# 52 
+	"R",					# 53 
+	"S",					# 54 
+	"T",					# 55 
+	"U",					# 56 
+	"V",					# 57 
+	"W",					# 58 
+	"X",					# 59 
+	"Y",					# 60 
+	"Z",					# 61 
+	"bracketleft",			# 62 
+	"backslash",			# 63 
+	"bracketright",			# 64 
+	"asciicircum",			# 65 
+	"underscore",			# 66 
+	"grave",				# 67 
+	"a",					# 68 
+	"b",					# 69 
+	"c",					# 70 
+	"d",					# 71 
+	"e",					# 72 
+	"f",					# 73 
+	"g",					# 74 
+	"h",					# 75 
+	"i",					# 76 
+	"j",					# 77 
+	"k",					# 78 
+	"l",					# 79 
+	"m",					# 80 
+	"n",					# 81 
+	"o",					# 82 
+	"p",					# 83 
+	"q",					# 84 
+	"r",					# 85 
+	"s",					# 86 
+	"t",					# 87 
+	"u",					# 88 
+	"v",					# 89 
+	"w",					# 90 
+	"x",					# 91 
+	"y",					# 92 
+	"z",					# 93 
+	"braceleft",			# 94 
+	"bar",					# 95 
+	"braceright",			# 96 
+	"asciitilde",			# 97 
+	"Adieresis",			# 98 
+	"Aring",				# 99 
+	"Ccedilla",				# 100 
+	"Eacute",				# 101 
+	"Ntilde",				# 102 
+	"Odieresis",			# 103 
+	"Udieresis",			# 104 
+	"aacute",				# 105 
+	"agrave",				# 106 
+	"acircumflex",			# 107 
+	"adieresis",			# 108 
+	"atilde",				# 109 
+	"aring",				# 110 
+	"ccedilla",				# 111 
+	"eacute",				# 112 
+	"egrave",				# 113 
+	"ecircumflex",			# 114 
+	"edieresis",			# 115 
+	"iacute",				# 116 
+	"igrave",				# 117 
+	"icircumflex",			# 118 
+	"idieresis",			# 119 
+	"ntilde",				# 120 
+	"oacute",				# 121 
+	"ograve",				# 122 
+	"ocircumflex",			# 123 
+	"odieresis",			# 124 
+	"otilde",				# 125 
+	"uacute",				# 126 
+	"ugrave",				# 127 
+	"ucircumflex",			# 128 
+	"udieresis",			# 129 
+	"dagger",				# 130 
+	"degree",				# 131 
+	"cent",					# 132 
+	"sterling",				# 133 
+	"section",				# 134 
+	"bullet",				# 135 
+	"paragraph",			# 136 
+	"germandbls",			# 137 
+	"registered",			# 138 
+	"copyright",			# 139 
+	"trademark",			# 140 
+	"acute",				# 141 
+	"dieresis",				# 142 
+	"notequal",				# 143 
+	"AE",					# 144 
+	"Oslash",				# 145 
+	"infinity",				# 146 
+	"plusminus",			# 147 
+	"lessequal",			# 148 
+	"greaterequal",			# 149 
+	"yen",					# 150 
+	"mu",					# 151 
+	"partialdiff",			# 152 
+	"summation",			# 153 
+	"product",				# 154 
+	"pi",					# 155 
+	"integral",				# 156 
+	"ordfeminine",			# 157 
+	"ordmasculine",			# 158 
+	"Omega",				# 159 
+	"ae",					# 160 
+	"oslash",				# 161 
+	"questiondown",			# 162 
+	"exclamdown",			# 163 
+	"logicalnot",			# 164 
+	"radical",				# 165 
+	"florin",				# 166 
+	"approxequal",			# 167 
+	"Delta",				# 168 
+	"guillemotleft",		# 169 
+	"guillemotright",		# 170 
+	"ellipsis",				# 171 
+	"nonbreakingspace",		# 172 
+	"Agrave",				# 173 
+	"Atilde",				# 174 
+	"Otilde",				# 175 
+	"OE",					# 176 
+	"oe",					# 177 
+	"endash",				# 178 
+	"emdash",				# 179 
+	"quotedblleft",			# 180 
+	"quotedblright",		# 181 
+	"quoteleft",			# 182 
+	"quoteright",			# 183 
+	"divide",				# 184 
+	"lozenge",				# 185 
+	"ydieresis",			# 186 
+	"Ydieresis",			# 187 
+	"fraction",				# 188
+	"currency",				# 189
+	"guilsinglleft",		# 190 
+	"guilsinglright",		# 191 
+	"fi",					# 192 
+	"fl",					# 193 
+	"daggerdbl",			# 194 
+	"periodcentered",		# 195 
+	"quotesinglbase",		# 196 
+	"quotedblbase",			# 197 
+	"perthousand",			# 198 
+	"Acircumflex",			# 199 
+	"Ecircumflex",			# 200 
+	"Aacute",				# 201 
+	"Edieresis",			# 202 
+	"Egrave",				# 203 
+	"Iacute",				# 204 
+	"Icircumflex",			# 205 
+	"Idieresis",			# 206 
+	"Igrave",				# 207 
+	"Oacute",				# 208 
+	"Ocircumflex",			# 209 
+	"apple",				# 210 
+	"Ograve",				# 211 
+	"Uacute",				# 212 
+	"Ucircumflex",			# 213 
+	"Ugrave",				# 214 
+	"dotlessi",				# 215 
+	"circumflex",			# 216 
+	"tilde",				# 217 
+	"macron",				# 218 
+	"breve",				# 219 
+	"dotaccent",			# 220 
+	"ring",					# 221 
+	"cedilla",				# 222 
+	"hungarumlaut",			# 223 
+	"ogonek",				# 224 
+	"caron",				# 225 
+	"Lslash",				# 226 
+	"lslash",				# 227 
+	"Scaron",				# 228 
+	"scaron",				# 229 
+	"Zcaron",				# 230 
+	"zcaron",				# 231 
+	"brokenbar",			# 232 
+	"Eth",					# 233 
+	"eth",					# 234 
+	"Yacute",				# 235 
+	"yacute",				# 236 
+	"Thorn",				# 237 
+	"thorn",				# 238 
+	"minus",				# 239 
+	"multiply",				# 240 
+	"onesuperior",			# 241 
+	"twosuperior",			# 242 
+	"threesuperior",		# 243 
+	"onehalf",				# 244 
+	"onequarter",			# 245 
+	"threequarters",		# 246 
+	"franc",				# 247 
+	"Gbreve",				# 248 
+	"gbreve",				# 249 
+	"Idotaccent",			# 250 
+	"Scedilla",				# 251 
+	"scedilla",				# 252 
+	"Cacute",				# 253 
+	"cacute",				# 254 
+	"Ccaron",				# 255 
+	"ccaron",				# 256 
+	"dcroat"				# 257 
+]
+
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
+
diff --git a/Lib/fontTools/ttLib/test/ttBrowser.py b/Lib/fontTools/ttLib/test/ttBrowser.py
new file mode 100644
index 0000000..1c54375
--- /dev/null
+++ b/Lib/fontTools/ttLib/test/ttBrowser.py
@@ -0,0 +1,332 @@
+from fontTools import ttLib
+from fontTools.ttLib import macUtils
+import macfs
+import PyBrowser
+import W, Lists
+import os
+import ATM
+import Numeric
+import Qd
+from rf.views.wGlyphList import GlyphList
+
+
+class TableBrowser:
+	
+	def __init__(self, path=None, ttFont=None, res_index=None):
+		W.SetCursor('watch')
+		if path is None:
+			self.ttFont = ttFont
+			self.filename = "????"
+		else:
+			self.ttFont = ttLib.TTFont(path, res_index)
+			if res_index is None:
+				self.filename = os.path.basename(path)
+			else:
+				self.filename = os.path.basename(path) + " - " + str(res_index)
+		self.currentglyph = None
+		self.glyphs = {}
+		self.buildinterface()
+	
+	def buildinterface(self):
+		buttonwidth = 120
+		glyphlistwidth = 150
+		hmargin = 10
+		vmargin = 8
+		title = self.filename
+		tables = self.ttFont.keys()
+		tables.sort()
+		self.w = w = W.Window((500, 300), title, minsize = (400, 200))
+		w.browsetablebutton = W.Button((hmargin, 32, buttonwidth, 16), "Browse tableŠ", 
+				self.browsetable)
+		w.browsefontbutton = W.Button((hmargin, vmargin, buttonwidth, 16), "Browse fontŠ", 
+				self.browsefont)
+		w.tablelist = W.List((hmargin, 56, buttonwidth, -128), tables, self.tablelisthit)
+		
+		w.divline1 = W.VerticalLine((buttonwidth + 2 * hmargin, vmargin, 1, -vmargin))
+		
+		gleft = buttonwidth + 3 * hmargin + 1
+		
+		hasGlyfTable = self.ttFont.has_key('glyf')
+		
+		glyphnames = self.ttFont.getGlyphNames2()  # caselessly sorted glyph names
+		
+		if hasGlyfTable:
+			w.glyphlist = GlyphList((gleft, 56, glyphlistwidth, -vmargin), 
+					glyphnames, self.glyphlisthit)
+			
+			w.divline2 = W.VerticalLine((buttonwidth + glyphlistwidth + 4 * hmargin + 2, 
+					vmargin, 1, -vmargin))
+			
+			yMin = self.ttFont['head'].yMin
+			yMax = self.ttFont['head'].yMax
+			w.gviewer = GlyphViewer((buttonwidth + glyphlistwidth + 5 * hmargin + 3, 
+					vmargin, -hmargin, -vmargin), yMin, yMax)
+			
+			w.showpoints = W.CheckBox((gleft, vmargin, glyphlistwidth, 16), "Show points", 
+					self.w.gviewer.toggleshowpoints)
+			w.showpoints.set(self.w.gviewer.showpoints)
+			w.showlines = W.CheckBox((gleft, vmargin + 24, glyphlistwidth, 16), "Show lines",
+					self.w.gviewer.toggleshowlines)
+			w.showlines.set(self.w.gviewer.showlines)
+		else:
+			w.glyphlist = GlyphList((gleft, 56, glyphlistwidth, -vmargin), 
+					glyphnames)
+			w.noGlyphTable = W.TextBox((gleft, vmargin, -20, 20), "no 'glyf' table found")
+		
+		
+		w.setdefaultbutton(w.browsetablebutton)
+		
+		w.tocurrentfont = W.Button((hmargin, -120, buttonwidth, 16), "Copy to current font", self.copytocurrentfont)
+		w.fromcurrentfont = W.Button((hmargin, -96, buttonwidth, 16), "Copy from current font", self.copyfromcurrentfont)
+		w.saveflat = W.Button((hmargin, -72, buttonwidth, 16), "Save as flat fileŠ", self.saveflat)
+		w.savesuitcasebutton = W.Button((hmargin, -48, buttonwidth, 16), "Save as suitcaseŠ", self.savesuitcase)
+		w.savexmlbutton = W.Button((hmargin, -24, buttonwidth, 16), "Save as XMLŠ", self.saveXML)
+		
+		w.open()
+		w.browsetablebutton.enable(0)
+	
+	def browsetable(self):
+		self.tablelisthit(1)
+	
+	def browsefont(self):
+		PyBrowser.Browser(self.ttFont)
+	
+	def copytocurrentfont(self):
+		pass
+		
+	def copyfromcurrentfont(self):
+		pass
+		
+	def saveflat(self):
+		path = putfile("Save font as flat file:", self.filename, ".TTF")
+		if path:
+			W.SetCursor('watch')
+			self.ttFont.save(path)
+	
+	def savesuitcase(self):
+		path = putfile("Save font as suitcase:", self.filename, ".suit")
+		if path:
+			W.SetCursor('watch')
+			self.ttFont.save(path, 1)
+	
+	def saveXML(self):
+		path = putfile("Save font as XML text file:", self.filename, ".xml")
+		if path:
+			W.SetCursor('watch')
+			pb = macUtils.ProgressBar("Saving %s as XMLŠ" % self.filename)
+			try:
+				self.ttFont.saveXML(path, pb)
+			finally:
+				pb.close()
+	
+	def glyphlisthit(self, isDbl):
+		sel = self.w.glyphlist.getselectedobjects()
+		if not sel or sel[0] == self.currentglyph:
+			return
+		self.currentglyph = sel[0]
+		if self.glyphs.has_key(self.currentglyph):
+			g = self.glyphs[self.currentglyph]
+		else:
+			g = Glyph(self.ttFont, self.currentglyph)
+			self.glyphs[self.currentglyph] = g
+		self.w.gviewer.setglyph(g)
+	
+	def tablelisthit(self, isdbl):
+		if isdbl:
+			for tag in self.w.tablelist.getselectedobjects():
+				table = self.ttFont[tag]
+				if tag == 'glyf':
+					W.SetCursor('watch')
+					for glyphname in self.ttFont.getGlyphOrder():
+						try:
+							glyph = table[glyphname]
+						except KeyError:
+							pass # incomplete font, oh well.
+				PyBrowser.Browser(table)
+		else:
+			sel = self.w.tablelist.getselection()
+			if sel:
+				self.w.browsetablebutton.enable(1)
+			else:
+				self.w.browsetablebutton.enable(0)
+
+
+class Glyph:
+	
+	def __init__(self, ttFont, glyphName):
+		ttglyph = ttFont['glyf'][glyphName]
+		self.iscomposite = ttglyph.numberOfContours == -1
+		self.width, self.lsb = ttFont['hmtx'][glyphName]
+		if ttglyph.numberOfContours == 0:
+			self.xMin = 0
+			self.contours = []
+			return
+		self.xMin = ttglyph.xMin
+		coordinates, endPts, flags = ttglyph.getCoordinates(ttFont['glyf'])
+		self.contours = []
+		self.flags = []
+		startpt = 0
+		for endpt in endPts:
+			self.contours.append(Numeric.array(coordinates[startpt:endpt+1]))
+			self.flags.append(flags[startpt:endpt+1])
+			startpt = endpt + 1
+	
+	def getcontours(self, scale, move):
+		contours = []
+		for i in range(len(self.contours)):
+			contours.append((self.contours[i] * Numeric.array(scale) + move), self.flags[i])
+		return contours
+
+
+class GlyphViewer(W.Widget):
+	
+	def __init__(self, possize, yMin, yMax):
+		W.Widget.__init__(self, possize)
+		self.glyph = None
+		extra = 0.02 * (yMax-yMin)
+		self.yMin, self.yMax = yMin - extra, yMax + extra
+		self.showpoints = 1
+		self.showlines = 1
+	
+	def toggleshowpoints(self, onoff):
+		self.showpoints = onoff
+		self.SetPort()
+		self.draw()
+	
+	def toggleshowlines(self, onoff):
+		self.showlines = onoff
+		self.SetPort()
+		self.draw()
+	
+	def setglyph(self, glyph):
+		self.glyph = glyph
+		self.SetPort()
+		self.draw()
+		
+	def draw(self, visRgn=None):
+		# This a HELL of a routine, but it's pretty damn fast...
+		import Qd
+		if not self._visible:
+			return
+		Qd.EraseRect(Qd.InsetRect(self._bounds, 1, 1))
+		cliprgn = Qd.NewRgn()
+		savergn = Qd.NewRgn()
+		Qd.RectRgn(cliprgn, self._bounds)
+		Qd.GetClip(savergn)
+		Qd.SetClip(cliprgn)
+		try:
+			if self.glyph:
+				l, t, r, b = Qd.InsetRect(self._bounds, 1, 1)
+				height = b - t
+				scale = float(height) / (self.yMax - self.yMin)
+				topoffset = t + scale * self.yMax
+				width = scale * self.glyph.width
+				lsb = scale * self.glyph.lsb
+				xMin = scale * self.glyph.xMin
+				# XXXX this is not correct when USE_MY_METRICS is set in component!
+				leftoffset = l + 0.5 * (r - l - width)
+				gleftoffset = leftoffset - xMin + lsb
+				if self.showlines:
+					Qd.RGBForeColor((0xafff, 0xafff, 0xafff))
+					# left sidebearing
+					Qd.MoveTo(leftoffset, t)
+					Qd.LineTo(leftoffset, b - 1)
+					# right sidebearing
+					Qd.MoveTo(leftoffset + width, t)
+					Qd.LineTo(leftoffset + width, b - 1)
+					# baseline
+					Qd.MoveTo(l, topoffset)
+					Qd.LineTo(r - 1, topoffset)
+					
+					# origin
+					Qd.RGBForeColor((0x5fff, 0, 0))
+					Qd.MoveTo(gleftoffset, topoffset - 16)
+					Qd.LineTo(gleftoffset, topoffset + 16)
+					# reset color
+					Qd.RGBForeColor((0, 0, 0))
+				
+				if self.glyph.iscomposite:
+					Qd.RGBForeColor((0x7fff, 0x7fff, 0x7fff))
+				
+				ATM.startFillATM()
+				contours = self.glyph.getcontours((scale, -scale), (gleftoffset, topoffset))
+				for contour, flags in contours:
+					currentpoint = None
+					done_moveto = 0
+					i = 0
+					nPoints = len(contour)
+					while i < nPoints:
+						pt = contour[i]
+						if flags[i]:
+							# onCurve
+							currentpoint = lineto(pt, done_moveto)
+						else:
+							if not currentpoint:
+								if not flags[i-1]:
+									currentpoint = 0.5 * (contour[i-1] + pt)
+								else:
+									currentpoint = contour[i-1]
+							if not flags[(i+1) % nPoints]:
+								endPt = 0.5 * (pt + contour[(i+1) % nPoints])
+							else:
+								endPt = contour[(i+1) % nPoints]
+								i = i + 1
+							# offCurve
+							currentpoint = qcurveto(currentpoint, 
+									pt, endPt, done_moveto)
+						done_moveto = 1
+						i = i + 1
+					ATM.fillClosePathATM()
+				ATM.endFillATM()
+				# draw point markers
+				if self.showpoints:
+					for contour, flags in contours:
+						Qd.RGBForeColor((0, 0xffff, 0))
+						for i in range(len(contour)):
+							(x, y) = contour[i]
+							onCurve = flags[i] & 0x1
+							if onCurve:
+								Qd.PaintRect(Qd.InsetRect((x, y, x, y), -2, -2))
+							else:
+								Qd.PaintOval(Qd.InsetRect((x, y, x, y), -2, -2))
+							Qd.RGBForeColor((0xffff, 0, 0))
+						Qd.RGBForeColor((0, 0, 0))
+			Qd.FrameRect(self._bounds)
+		finally:
+			Qd.SetClip(savergn)
+			Qd.DisposeRgn(cliprgn)
+			Qd.DisposeRgn(savergn)
+
+
+extensions = [".suit", ".xml", ".TTF", ".ttf"]
+
+def putfile(prompt, filename, newextension):
+	for ext in extensions:
+		if filename[-len(ext):] == ext:
+			filename = filename[:-len(ext)] + newextension
+			break
+	else:
+		filename = filename + newextension
+	fss, ok = macfs.StandardPutFile(prompt, filename)
+	if ok:
+		return fss.as_pathname()
+
+
+def lineto(pt, done_moveto):
+	x, y = pt
+	if done_moveto:
+		ATM.fillLineToATM((x, y))
+	else:
+		ATM.fillMoveToATM((x, y))
+	return pt
+
+def qcurveto(pt0, pt1, pt2, done_moveto):
+	if not done_moveto:
+		x0, y0 = pt0
+		ATM.fillMoveToATM((x0, y0))
+	x1a, y1a = pt0 + 0.6666666666667 * (pt1 - pt0)
+	x1b, y1b = pt2 + 0.6666666666667 * (pt1 - pt2)
+	x2, y2 = pt2
+	ATM.fillCurveToATM((x1a, y1a), (x1b, y1b), (x2, y2))
+	return pt2
+
diff --git a/Lib/fontTools/ttLib/xmlImport.py b/Lib/fontTools/ttLib/xmlImport.py
new file mode 100644
index 0000000..92d3b42
--- /dev/null
+++ b/Lib/fontTools/ttLib/xmlImport.py
@@ -0,0 +1,196 @@
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+import types
+import string
+import Numeric, array
+from xml.parsers.xmlproc import xmlproc
+
+
+xmlerror = "xmlerror"
+xml_parse_error = "XML parse error"
+
+
+class UnicodeString:
+	
+	def __init__(self, value):
+		if isinstance(value, UnicodeString):
+			self.value = value.value
+		else:
+			if type(value) == types.StringType:
+				# Since Numeric interprets char codes as *signed*,
+				# we feed it through the array module.
+				value = array.array("B", value)
+			self.value = Numeric.array(value, Numeric.Int16)
+	
+	def __len__(self):
+		return len(self.value)
+	
+	#def __hash__(self):
+	#	return hash(self.value.tostring())
+	#
+	#def __cmp__(self, other):
+	#	if not isinstance(other, UnicodeString):
+	#		return 1
+	#	else:
+	#		return not Numeric.alltrue(
+	#				Numeric.equal(self.value, other.value))
+	
+	def __add__(self, other):
+		if not isinstance(other, UnicodeString):
+			other = self.__class__(other)
+		return self.__class__(Numeric.concatenate((self.value, other.value)))
+	
+	def __radd__(self, other):
+		if not isinstance(other, UnicodeString):
+			other = self.__class__(other)
+		return self.__class__(Numeric.concatenate((other.value, self.value)))
+	
+	def __getslice__(self, i, j):
+		return self.__class__(self.value[i:j])
+	
+	def __getitem__(self, i):
+		return self.__class__(self.value[i:i+1])
+	
+	def tostring(self):
+		value = self.value
+		if ttLib.endian <> "big":
+			value = value.byteswapped()
+		return value.tostring()
+	
+	def stripped(self):
+		value = self.value
+		i = 0
+		for i in range(len(value)):
+			if value[i] not in (0xa, 0xd, 0x9, 0x20):
+				break
+		value = value[i:]
+		i = 0
+		for i in range(len(value)-1, -1, -1):
+			if value[i] not in (0xa, 0xd, 0x9, 0x20):
+				break
+		value = value[:i+1]
+		return self.__class__(value)
+	
+	def __repr__(self):
+		return "<%s %s at %x>" % (self.__class__.__name__, `self.value.tostring()`, id(self))
+
+
+class UnicodeProcessor(xmlproc.XMLProcessor):
+	
+	def parse_charref(self):
+		"Parses a character reference."
+		
+		if self.now_at("x"):
+			digs=unhex(self.get_match(xmlproc.reg_hex_digits))
+		else:
+			try:
+				digs=string.atoi(self.get_match(xmlproc.reg_digits))
+			except ValueError,e:
+				self.report_error(3027)
+				digs=None
+		if digs == 169:
+			pass
+		if not self.now_at(";"): self.report_error(3005,";")
+		if digs==None: return
+		
+		if not (digs==9 or digs==10 or digs==13 or \
+				(digs>=32 and digs<=255)):
+			if digs>255:
+				self.app.handle_data(UnicodeString([digs]),0,1)
+			else:
+				# hrm, I need to let some null bytes go through...
+				self.app.handle_data(chr(digs),0,1)
+				#self.report_error(3018,digs)
+		else:
+			if self.stack==[]:
+				self.report_error(3028)
+			self.app.handle_data(chr(digs),0,1)
+
+
+class XMLErrorHandler(xmlproc.ErrorHandler):
+	
+	def fatal(self, msg):
+		"Handles a fatal error message."
+		# we don't want no stinkin' sys.exit(1)
+		raise xml_parse_error, msg
+
+
+class XMLApplication(xmlproc.Application):
+	
+	def __init__(self, ttFont, progress=None):
+		self.ttFont = ttFont
+		self.progress = progress
+		self.root = None
+		self.content_stack = []
+		self.lastpos = 0
+	
+	def handle_start_tag(self, name, attrs):
+		if self.progress:
+			pos = self.locator.pos + self.locator.block_offset
+			if (pos - self.lastpos) > 4000:
+				self.progress.set(pos / 100)
+				self.lastpos = pos
+		stack = self.locator.stack
+		stacksize = len(stack)
+		if not stacksize:
+			if name <> "ttFont":
+				raise xml_parse_error, "illegal root tag: %s" % name
+			sfntVersion = attrs.get("sfntVersion", "\000\001\000\000")
+			if len(sfntVersion) <> 4:
+				sfntVersion = safeEval('"' + sfntVersion + '"')
+			self.ttFont.sfntVersion = sfntVersion
+			self.content_stack.append([])
+		elif stacksize == 1:
+			msg = "Parsing '%s' table..." % ttLib.xmltag2tag(name)
+			if self.progress:
+				self.progress.setlabel(msg)
+			elif self.ttFont.verbose:
+				ttLib.debugmsg(msg)
+			else:
+				print msg
+			tag = ttLib.xmltag2tag(name)
+			tableclass = ttLib.getTableClass(tag)
+			if tableclass is None:
+				from fontTools.ttLib.tables.DefaultTable import DefaultTable
+				tableclass = DefaultTable
+			self.current_table = tableclass(tag)
+			self.ttFont[tag] = self.current_table
+			self.content_stack.append([])
+		elif stacksize == 2:
+			self.content_stack.append([])
+			self.root = (name, attrs, self.content_stack[-1])
+		else:
+			list = []
+			self.content_stack[-1].append(name, attrs, list)
+			self.content_stack.append(list)
+	
+	def handle_data(self, data, start, end):
+		if len(self.locator.stack) > 1:
+			self.content_stack[-1].append(data[start:end])
+	
+	def handle_end_tag(self, name):
+		del self.content_stack[-1]
+		stack = self.locator.stack
+		stacksize = len(stack)
+		if stacksize == 1:
+			self.root = None
+		elif stacksize == 2:
+			self.current_table.fromXML(self.root, self.ttFont)
+			self.root = None
+
+
+class ProgressPrinter:
+	
+	def __init__(self, title, maxval=100):
+		print title
+	
+	def set(self, val, maxval=None):
+		pass
+	
+	def increment(self, val=1):
+		pass
+	
+	def setlabel(self, text):
+		print text
+
+