Merge branch 'merge'
diff --git a/Doc/documentation.html b/Doc/documentation.html
index ac5dfcb..e2821fe 100644
--- a/Doc/documentation.html
+++ b/Doc/documentation.html
@@ -42,7 +42,7 @@
 The following tables are currently supported:
 <BLOCKQUOTE><TT>
 <!-- begin table list -->
-BASE, CBDT, CBLC, CFF, COLR, CPAL, DSIG, EBDT, EBLC, GDEF, GMAP, GPKG, GPOS, GSUB, JSTF, LTSH, META, OS/2, SING, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS, TSIV, VORG, cmap, cvt, fpgm, gasp, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, post, prep, vhea and vmtx
+BASE, CBDT, CBLC, CFF, COLR, CPAL, DSIG, EBDT, EBLC, FFTM, GDEF, GMAP, GPKG, GPOS, GSUB, JSTF, LTSH, META, OS/2, SING, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS, TSIV, VORG, cmap, cvt, fpgm, gasp, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, post, prep, sbix, vhea and vmtx
 <!-- end table list -->
 </TT></BLOCKQUOTE>
 Other tables are dumped as hexadecimal data.
diff --git a/Doc/install.txt b/Doc/install.txt
index 9bb4573..f2f3b0c 100644
--- a/Doc/install.txt
+++ b/Doc/install.txt
@@ -68,9 +68,9 @@
 contribute, you can also subscribe to the fonttools-checkins mailing list.
 
 
-Anonymous SVN-access
+Anonymous VCS access
 
-The FontTools sources are also accessible through SVN, see:
+The FontTools sources are also accessible here:
   http://sourceforge.net/projects/fonttools/
 Let me know if you'd like to become a co-developer.
 
diff --git a/Lib/fontTools/afmLib.py b/Lib/fontTools/afmLib.py
index aa303ad..2104b9e 100644
--- a/Lib/fontTools/afmLib.py
+++ b/Lib/fontTools/afmLib.py
@@ -4,12 +4,9 @@
 # It does not implement the full spec (Adobe Technote 5004, Adobe Font Metrics
 # File Format Specification). Still, it should read most "common" AFM files.
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 import re
-import string
-import types
-
-__version__ = "$Id: afmLib.py,v 1.6 2003-05-24 12:50:47 jvr Exp $"
-
 
 # every single line starts with a "word"
 identifierRE = re.compile("^([A-Za-z]+).*")
@@ -83,7 +80,7 @@
 class error(Exception): pass
 
 
-class AFM:
+class AFM(object):
 	
 	_attrs = None
 	
@@ -112,15 +109,15 @@
 	def read(self, path):
 		lines = readlines(path)
 		for line in lines:
-			if not string.strip(line):
+			if not line.strip():
 				continue
 			m = identifierRE.match(line)
 			if m is None:
-				raise error, "syntax error in AFM file: " + `line`
+				raise error("syntax error in AFM file: " + repr(line))
 			
 			pos = m.regs[1][1]
 			word = line[:pos]
-			rest = string.strip(line[pos:])
+			rest = line[pos:].strip()
 			if word in self._keywords:
 				continue
 			if word == "C":
@@ -135,35 +132,35 @@
 	def parsechar(self, rest):
 		m = charRE.match(rest)
 		if m is None:
-			raise error, "syntax error in AFM file: " + `rest`
+			raise error("syntax error in AFM file: " + repr(rest))
 		things = []
 		for fr, to in m.regs[1:]:
 			things.append(rest[fr:to])
 		charname = things[2]
 		del things[2]
-		charnum, width, l, b, r, t = map(string.atoi, things)
+		charnum, width, l, b, r, t = (int(thing) for thing in things)
 		self._chars[charname] = charnum, width, (l, b, r, t)
 	
 	def parsekernpair(self, rest):
 		m = kernRE.match(rest)
 		if m is None:
-			raise error, "syntax error in AFM file: " + `rest`
+			raise error("syntax error in AFM file: " + repr(rest))
 		things = []
 		for fr, to in m.regs[1:]:
 			things.append(rest[fr:to])
 		leftchar, rightchar, value = things
-		value = string.atoi(value)
+		value = int(value)
 		self._kerning[(leftchar, rightchar)] = value
 	
 	def parseattr(self, word, rest):
 		if word == "FontBBox":
-			l, b, r, t = map(string.atoi, string.split(rest))
+			l, b, r, t = [int(thing) for thing in rest.split()]
 			self._attrs[word] = l, b, r, t
 		elif word == "Comment":
 			self._comments.append(rest)
 		else:
 			try:
-				value = string.atoi(rest)
+				value = int(rest)
 			except (ValueError, OverflowError):
 				self._attrs[word] = rest
 			else:
@@ -172,15 +169,15 @@
 	def parsecomposite(self, rest):
 		m = compositeRE.match(rest)
 		if m is None:
-			raise error, "syntax error in AFM file: " + `rest`
+			raise error("syntax error in AFM file: " + repr(rest))
 		charname = m.group(1)
 		ncomponents = int(m.group(2))
 		rest = rest[m.regs[0][1]:]
 		components = []
-		while 1:
+		while True:
 			m = componentRE.match(rest)
 			if m is None:
-				raise error, "syntax error in AFM file: " + `rest`
+				raise error("syntax error in AFM file: " + repr(rest))
 			basechar = m.group(1)
 			xoffset = int(m.group(2))
 			yoffset = int(m.group(3))
@@ -194,8 +191,7 @@
 	def write(self, path, sep='\r'):
 		import time
 		lines = [	"StartFontMetrics 2.0",
-				"Comment Generated by afmLib, version %s; at %s" % 
-						(string.split(__version__)[2],
+				"Comment Generated by afmLib; at %s" % (
 						time.strftime("%m/%d/%Y %H:%M:%S", 
 						time.localtime(time.time())))]
 		
@@ -208,35 +204,30 @@
 		# a preferred order
 		attrs = self._attrs
 		for attr in preferredAttributeOrder:
-			if attrs.has_key(attr):
+			if attr in attrs:
 				value = attrs[attr]
 				if attr == "FontBBox":
 					value = "%s %s %s %s" % value
 				lines.append(attr + " " + str(value))
 		# then write the attributes we don't know about,
 		# in alphabetical order
-		items = attrs.items()
-		items.sort()
+		items = sorted(attrs.items())
 		for attr, value in items:
 			if attr in preferredAttributeOrder:
 				continue
 			lines.append(attr + " " + str(value))
 		
 		# write char metrics
-		lines.append("StartCharMetrics " + `len(self._chars)`)
-		items = map(lambda (charname, (charnum, width, box)):
-			(charnum, (charname, width, box)),
-			self._chars.items())
+		lines.append("StartCharMetrics " + repr(len(self._chars)))
+		items = [(charnum, (charname, width, box)) for charname, (charnum, width, box) in self._chars.items()]
 		
-		def myCmp(a, b):
-			"""Custom compare function to make sure unencoded chars (-1) 
+		def myKey(a):
+			"""Custom key function to make sure unencoded chars (-1) 
 			end up at the end of the list after sorting."""
 			if a[0] == -1:
 				a = (0xffff,) + a[1:]  # 0xffff is an arbitrary large number
-			if b[0] == -1:
-				b = (0xffff,) + b[1:]
-			return cmp(a, b)
-		items.sort(myCmp)
+			return a
+		items.sort(key=myKey)
 		
 		for charnum, (charname, width, (l, b, r, t)) in items:
 			lines.append("C %d ; WX %d ; N %s ; B %d %d %d %d ;" %
@@ -245,17 +236,15 @@
 		
 		# write kerning info
 		lines.append("StartKernData")
-		lines.append("StartKernPairs " + `len(self._kerning)`)
-		items = self._kerning.items()
-		items.sort()		# XXX is order important?
+		lines.append("StartKernPairs " + repr(len(self._kerning)))
+		items = sorted(self._kerning.items())
 		for (leftchar, rightchar), value in items:
 			lines.append("KPX %s %s %d" % (leftchar, rightchar, value))
 		lines.append("EndKernPairs")
 		lines.append("EndKernData")
 		
 		if self._composites:
-			composites = self._composites.items()
-			composites.sort()
+			composites = sorted(self._composites.items())
 			lines.append("StartComposites %s" % len(self._composites))
 			for charname, components in composites:
 				line = "CC %s %s ;" % (charname, len(components))
@@ -269,16 +258,16 @@
 		writelines(path, lines, sep)
 	
 	def has_kernpair(self, pair):
-		return self._kerning.has_key(pair)
+		return pair in self._kerning
 	
 	def kernpairs(self):
-		return self._kerning.keys()
+		return list(self._kerning.keys())
 	
 	def has_char(self, char):
-		return self._chars.has_key(char)
+		return char in self._chars
 	
 	def chars(self):
-		return self._chars.keys()
+		return list(self._chars.keys())
 	
 	def comments(self):
 		return self._comments
@@ -290,10 +279,10 @@
 		self._composites[glyphName] = components
 	
 	def __getattr__(self, attr):
-		if self._attrs.has_key(attr):
+		if attr in self._attrs:
 			return self._attrs[attr]
 		else:
-			raise AttributeError, attr
+			raise AttributeError(attr)
 	
 	def __setattr__(self, attr, value):
 		# all attrs *not* starting with "_" are consider to be AFM keywords
@@ -308,15 +297,15 @@
 			try:
 				del self.__dict__[attr]
 			except KeyError:
-				raise AttributeError, attr
+				raise AttributeError(attr)
 		else:
 			try:
 				del self._attrs[attr]
 			except KeyError:
-				raise AttributeError, attr
+				raise AttributeError(attr)
 	
 	def __getitem__(self, key):
-		if type(key) == types.TupleType:
+		if isinstance(key, tuple):
 			# key is a tuple, return the kernpair
 			return self._kerning[key]
 		else:
@@ -324,7 +313,7 @@
 			return self._chars[key]
 	
 	def __setitem__(self, key, value):
-		if type(key) == types.TupleType:
+		if isinstance(key, tuple):
 			# key is a tuple, set kernpair
 			self._kerning[key] = value
 		else:
@@ -332,7 +321,7 @@
 			self._chars[key] = value
 	
 	def __delitem__(self, key):
-		if type(key) == types.TupleType:
+		if isinstance(key, tuple):
 			# key is a tuple, del kernpair
 			del self._kerning[key]
 		else:
@@ -356,7 +345,7 @@
 		sep = sep + '\r'	# mac or dos
 	if '\n' in data:
 		sep = sep + '\n'	# unix or dos
-	return string.split(data, sep)
+	return data.split(sep)
 
 def writelines(path, lines, sep='\r'):
 	f = open(path, 'wb')
@@ -373,16 +362,16 @@
 		afm = AFM(path)
 		char = 'A'
 		if afm.has_char(char):
-			print afm[char]	# print charnum, width and boundingbox
+			print(afm[char])	# print charnum, width and boundingbox
 		pair = ('A', 'V')
 		if afm.has_kernpair(pair):
-			print afm[pair]	# print kerning value for pair
-		print afm.Version	# various other afm entries have become attributes
-		print afm.Weight
+			print(afm[pair])	# print kerning value for pair
+		print(afm.Version)	# various other afm entries have become attributes
+		print(afm.Weight)
 		# afm.comments() returns a list of all Comment lines found in the AFM
-		print afm.comments()
+		print(afm.comments())
 		#print afm.chars()
 		#print afm.kernpairs()
-		print afm
+		print(afm)
 		afm.write(path + ".muck")
 
diff --git a/Lib/fontTools/agl.py b/Lib/fontTools/agl.py
index dc371af..597df7d 100644
--- a/Lib/fontTools/agl.py
+++ b/Lib/fontTools/agl.py
@@ -1,103 +1,119 @@
 # The table below is taken from
 # http://www.adobe.com/devnet/opentype/archives/aglfn.txt
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+
 _aglText = """\
-# ###################################################################################
-# Copyright (c) 2003,2005,2006,2007 Adobe Systems Incorporated
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this documentation file to use, copy, publish, distribute,
-# sublicense, and/or sell copies of the documentation, and to permit
-# others to do the same, provided that:
-# - No modification, editing or other alteration of this document is
-# allowed; and
-# - The above copyright notice and this permission notice shall be
-# included in all copies of the documentation.
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this documentation file, to create their own derivative works
-# from the content of this document to use, copy, publish, distribute,
-# sublicense, and/or sell the derivative works, and to permit others to do
-# the same, provided that the derived work is not represented as being a
-# copy or version of this document.
-# 
-# Adobe shall not be liable to any party for any loss of revenue or profit
-# or for indirect, incidental, special, consequential, or other similar
-# damages, whether based on tort (including without limitation negligence
-# or strict liability), contract or other legal or equitable grounds even
-# if Adobe has been advised or had reason to know of the possibility of
-# such damages. The Adobe materials are provided on an "AS IS" basis.
-# Adobe specifically disclaims all express, statutory, or implied
-# warranties relating to the Adobe materials, including but not limited to
-# those concerning merchantability or fitness for a particular purpose or
-# non-infringement of any third party rights regarding the Adobe
-# materials.
-# ###################################################################################
+# -----------------------------------------------------------
+# Copyright 2003, 2005-2008, 2010 Adobe Systems Incorporated.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the
+# following conditions are met:
+#
+# Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# Neither the name of Adobe Systems Incorporated nor the names
+# of its contributors may be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -----------------------------------------------------------
 # Name:          Adobe Glyph List For New Fonts
-# Table version: 1.6
-# Date:          30 Januaury 2006
+# Table version: 1.7
+# Date:          November 6, 2008
+# URL:           http://sourceforge.net/adobe/aglfn/
 #
 # Description:
 #
-#   The Adobe Glyph List For New Fonts (AGLFN) is meant to provide a list of 
-#   base glyph names which are compatible with the AGL specification at
-#   http://partners.adobe.com/asn/developer/type/unicodegn.html.
-#   and which can be used as described in section 6 of that document.
+# AGLFN (Adobe Glyph List For New Fonts) provides a list of base glyph
+# names that are recommended for new fonts, which are compatible with
+# the AGL (Adobe Glyph List) Specification, and which should be used
+# as described in Section 6 of that document. AGLFN comprises the set
+# of glyph names from AGL that map via the AGL Specification rules to
+# the semantically correct UV (Unicode Value). For example, "Asmall"
+# is omitted because AGL maps this glyph name to the PUA (Private Use
+# Area) value U+F761, rather than to the UV that maps from the glyph
+# name "A." Also omitted is "ffi," because AGL maps this to the
+# Alphabetic Presentation Forms value U+FB03, rather than decomposing
+# it into the following sequence of three UVs: U+0066, U+0066, and
+# U+0069. The name "arrowvertex" has been omitted because this glyph
+# now has a real UV, and AGL is now incorrect in mapping it to the PUA
+# value U+F8E6. If you do not find an appropriate name for your glyph
+# in this list, then please refer to Section 6 of the AGL
+# Specification.
 #
-#   This list comprises the set of glyph names from the AGLv2,0 which map
-#   to via the AGL rules to the semanticly correct Unicode value. For
-#   example, Asmall is omitted as the AGL maps this to the Unicode
-#   Private Use Area value F761, rather than to the Unicode value for the
-#   character "A". "ffi" is also omitted, as the AGL maps this to the
-#   Alphabetic Presentation Forms Area value FB03, rather than
-#   decomposing it to the three-value Unicode sequence 0066,0066,0069.
-#    See section 7.1 of the Unicode Standard 4.0 on this issue.
-#   "arrowvertex" is omitted becuase this now has a real Unicode
-#   character value, and the AGL is now incorrect in mapping this to the 
-#   Private Use Area value  F8E6.
-#
-#  If you do not find an appropriate name for your glyph in this list,
-#  then please refer to section 6 of the document:
-#   http://partners.adobe.com/asn/developer/typeforum/unicodegn.html.
-#
-#	The Unicode values and names are given for convenience.
-#
-# Format: Semicolon-delimited fields:
-#
-#   (1) Standard UV or CUS UV. (4 uppercase hexadecimal digits)
-#
-#   (2) Glyph name. (upper- and lowercase letters, digits)
-#
+# Format: three semicolon-delimited fields:
+#   (1) Standard UV or CUS UV--four uppercase hexadecimal digits
+#   (2) Glyph name--upper/lowercase letters and digits
 #   (3) Character names: Unicode character names for standard UVs, and
-#       descriptive names for CUS UVs. (uppercase letters, hyphen, space)
+#       descriptive names for CUS UVs--uppercase letters, hyphen, and
+#       space
 #
-#   The entries are sorted by glyph name in increasing ASCII order; entries
-#   with the same glyph name are sorted in decreasing priority order.
+# The records are sorted by glyph name in increasing ASCII order,
+# entries with the same glyph name are sorted in decreasing priority
+# order, the UVs and Unicode character names are provided for
+# convenience, lines starting with "#" are comments, and blank lines
+# should be ignored.
 #
-#   Lines starting with "#" are comments; blank lines should be ignored.
+# Revision History:
 #
-#   1.6  [30 January 2006]
-#	- Completed work intended in 1.5
+# 1.7 [6 November 2008]
+# - Reverted to the original 1.4 and earlier mappings for Delta,
+#   Omega, and mu.
+# - Removed mappings for "afii" names. These should now be assigned
+#   "uni" names.
+# - Removed mappings for "commaaccent" names. These should now be
+#   assigned "uni" names.
 #
-#   1.5  [23 November 2005]
-#      - removed duplicated block at end of file
-#      - changed mappings:
-#            2206;Delta;INCREMENT changed to 0394;Delta;GREEK CAPITAL LETTER DELTA
-#            2126;Omega;OHM SIGN changed to 03A9;Omega;GREEK CAPITAL LETTER OMEGA
-#            03BC;mu;MICRO SIGN changed to 03BC;mu;GREEK SMALL LETTER MU
-#      - corrected statement above about why ffi is omitted.
-
-#   1.4  [24 September 2003]  Changed version to 1.4, to avoid confusion 
-#		with the AGL 1.3
-#			fixed spelling errors in the header
-#			fully removed arrowvertex, as it is mapped only to a PUA Unicode value in some fonts.
+# 1.6 [30 January 2006]
+# - Completed work intended in 1.5.
 #
-#   1.1  [17 April 2003]  Renamed [Tt]cedilla back to [Tt]commaaccent:
+# 1.5 [23 November 2005]
+# - Removed duplicated block at end of file.
+# - Changed mappings:
+#   2206;Delta;INCREMENT changed to 0394;Delta;GREEK CAPITAL LETTER DELTA
+#   2126;Omega;OHM SIGN changed to 03A9;Omega;GREEK CAPITAL LETTER OMEGA
+#   03BC;mu;MICRO SIGN changed to 03BC;mu;GREEK SMALL LETTER MU
+# - Corrected statement above about why "ffi" is omitted.
 #
-#   1.0  [31 Jan 2003]  Original version. Derived from the AGLv1.2 by:
-#	-  removing the PUA area codes
-#	- removing duplicate Unicode mappings, and 
-#	- renaming tcommaaccent to tcedilla and Tcommaaccent to Tcedilla 
+# 1.4 [24 September 2003]
+# - Changed version to 1.4, to avoid confusion with the AGL 1.3.
+# - Fixed spelling errors in the header.
+# - Fully removed "arrowvertex," as it is mapped only to a PUA Unicode
+#   value in some fonts.
+#
+# 1.1 [17 April 2003]
+# - Renamed [Tt]cedilla back to [Tt]commaaccent.
+#
+# 1.0 [31 January 2003]
+# - Original version.
+# - Derived from the AGLv1.2 by:
+#   removing the PUA area codes;
+#   removing duplicate Unicode mappings; and
+#   renaming "tcommaaccent" to "tcedilla" and "Tcommaaccent" to "Tcedilla"
 #
 0041;A;LATIN CAPITAL LETTER A
 00C6;AE;LATIN CAPITAL LETTER AE
@@ -126,7 +142,7 @@
 0044;D;LATIN CAPITAL LETTER D
 010E;Dcaron;LATIN CAPITAL LETTER D WITH CARON
 0110;Dcroat;LATIN CAPITAL LETTER D WITH STROKE
-0394;Delta;GREEK CAPITAL LETTER DELTA
+2206;Delta;INCREMENT
 0045;E;LATIN CAPITAL LETTER E
 00C9;Eacute;LATIN CAPITAL LETTER E WITH ACUTE
 0114;Ebreve;LATIN CAPITAL LETTER E WITH BREVE
@@ -150,7 +166,6 @@
 011E;Gbreve;LATIN CAPITAL LETTER G WITH BREVE
 01E6;Gcaron;LATIN CAPITAL LETTER G WITH CARON
 011C;Gcircumflex;LATIN CAPITAL LETTER G WITH CIRCUMFLEX
-0122;Gcommaaccent;LATIN CAPITAL LETTER G WITH CEDILLA
 0120;Gdotaccent;LATIN CAPITAL LETTER G WITH DOT ABOVE
 0048;H;LATIN CAPITAL LETTER H
 25CF;H18533;BLACK CIRCLE
@@ -178,12 +193,10 @@
 0134;Jcircumflex;LATIN CAPITAL LETTER J WITH CIRCUMFLEX
 004B;K;LATIN CAPITAL LETTER K
 039A;Kappa;GREEK CAPITAL LETTER KAPPA
-0136;Kcommaaccent;LATIN CAPITAL LETTER K WITH CEDILLA
 004C;L;LATIN CAPITAL LETTER L
 0139;Lacute;LATIN CAPITAL LETTER L WITH ACUTE
 039B;Lambda;GREEK CAPITAL LETTER LAMDA
 013D;Lcaron;LATIN CAPITAL LETTER L WITH CARON
-013B;Lcommaaccent;LATIN CAPITAL LETTER L WITH CEDILLA
 013F;Ldot;LATIN CAPITAL LETTER L WITH MIDDLE DOT
 0141;Lslash;LATIN CAPITAL LETTER L WITH STROKE
 004D;M;LATIN CAPITAL LETTER M
@@ -191,7 +204,6 @@
 004E;N;LATIN CAPITAL LETTER N
 0143;Nacute;LATIN CAPITAL LETTER N WITH ACUTE
 0147;Ncaron;LATIN CAPITAL LETTER N WITH CARON
-0145;Ncommaaccent;LATIN CAPITAL LETTER N WITH CEDILLA
 00D1;Ntilde;LATIN CAPITAL LETTER N WITH TILDE
 039D;Nu;GREEK CAPITAL LETTER NU
 004F;O;LATIN CAPITAL LETTER O
@@ -204,7 +216,7 @@
 01A0;Ohorn;LATIN CAPITAL LETTER O WITH HORN
 0150;Ohungarumlaut;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
 014C;Omacron;LATIN CAPITAL LETTER O WITH MACRON
-03A9;Omega;GREEK CAPITAL LETTER OMEGA
+2126;Omega;OHM SIGN
 038F;Omegatonos;GREEK CAPITAL LETTER OMEGA WITH TONOS
 039F;Omicron;GREEK CAPITAL LETTER OMICRON
 038C;Omicrontonos;GREEK CAPITAL LETTER OMICRON WITH TONOS
@@ -219,7 +231,6 @@
 0052;R;LATIN CAPITAL LETTER R
 0154;Racute;LATIN CAPITAL LETTER R WITH ACUTE
 0158;Rcaron;LATIN CAPITAL LETTER R WITH CARON
-0156;Rcommaaccent;LATIN CAPITAL LETTER R WITH CEDILLA
 211C;Rfraktur;BLACK-LETTER CAPITAL R
 03A1;Rho;GREEK CAPITAL LETTER RHO
 0053;S;LATIN CAPITAL LETTER S
@@ -267,13 +278,11 @@
 0160;Scaron;LATIN CAPITAL LETTER S WITH CARON
 015E;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA
 015C;Scircumflex;LATIN CAPITAL LETTER S WITH CIRCUMFLEX
-0218;Scommaaccent;LATIN CAPITAL LETTER S WITH COMMA BELOW
 03A3;Sigma;GREEK CAPITAL LETTER SIGMA
 0054;T;LATIN CAPITAL LETTER T
 03A4;Tau;GREEK CAPITAL LETTER TAU
 0166;Tbar;LATIN CAPITAL LETTER T WITH STROKE
 0164;Tcaron;LATIN CAPITAL LETTER T WITH CARON
-0162;Tcommaaccent;LATIN CAPITAL LETTER T WITH CEDILLA
 0398;Theta;GREEK CAPITAL LETTER THETA
 00DE;Thorn;LATIN CAPITAL LETTER THORN
 0055;U;LATIN CAPITAL LETTER U
@@ -319,241 +328,6 @@
 00E4;adieresis;LATIN SMALL LETTER A WITH DIAERESIS
 00E6;ae;LATIN SMALL LETTER AE
 01FD;aeacute;LATIN SMALL LETTER AE WITH ACUTE
-2015;afii00208;HORIZONTAL BAR
-0410;afii10017;CYRILLIC CAPITAL LETTER A
-0411;afii10018;CYRILLIC CAPITAL LETTER BE
-0412;afii10019;CYRILLIC CAPITAL LETTER VE
-0413;afii10020;CYRILLIC CAPITAL LETTER GHE
-0414;afii10021;CYRILLIC CAPITAL LETTER DE
-0415;afii10022;CYRILLIC CAPITAL LETTER IE
-0401;afii10023;CYRILLIC CAPITAL LETTER IO
-0416;afii10024;CYRILLIC CAPITAL LETTER ZHE
-0417;afii10025;CYRILLIC CAPITAL LETTER ZE
-0418;afii10026;CYRILLIC CAPITAL LETTER I
-0419;afii10027;CYRILLIC CAPITAL LETTER SHORT I
-041A;afii10028;CYRILLIC CAPITAL LETTER KA
-041B;afii10029;CYRILLIC CAPITAL LETTER EL
-041C;afii10030;CYRILLIC CAPITAL LETTER EM
-041D;afii10031;CYRILLIC CAPITAL LETTER EN
-041E;afii10032;CYRILLIC CAPITAL LETTER O
-041F;afii10033;CYRILLIC CAPITAL LETTER PE
-0420;afii10034;CYRILLIC CAPITAL LETTER ER
-0421;afii10035;CYRILLIC CAPITAL LETTER ES
-0422;afii10036;CYRILLIC CAPITAL LETTER TE
-0423;afii10037;CYRILLIC CAPITAL LETTER U
-0424;afii10038;CYRILLIC CAPITAL LETTER EF
-0425;afii10039;CYRILLIC CAPITAL LETTER HA
-0426;afii10040;CYRILLIC CAPITAL LETTER TSE
-0427;afii10041;CYRILLIC CAPITAL LETTER CHE
-0428;afii10042;CYRILLIC CAPITAL LETTER SHA
-0429;afii10043;CYRILLIC CAPITAL LETTER SHCHA
-042A;afii10044;CYRILLIC CAPITAL LETTER HARD SIGN
-042B;afii10045;CYRILLIC CAPITAL LETTER YERU
-042C;afii10046;CYRILLIC CAPITAL LETTER SOFT SIGN
-042D;afii10047;CYRILLIC CAPITAL LETTER E
-042E;afii10048;CYRILLIC CAPITAL LETTER YU
-042F;afii10049;CYRILLIC CAPITAL LETTER YA
-0490;afii10050;CYRILLIC CAPITAL LETTER GHE WITH UPTURN
-0402;afii10051;CYRILLIC CAPITAL LETTER DJE
-0403;afii10052;CYRILLIC CAPITAL LETTER GJE
-0404;afii10053;CYRILLIC CAPITAL LETTER UKRAINIAN IE
-0405;afii10054;CYRILLIC CAPITAL LETTER DZE
-0406;afii10055;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
-0407;afii10056;CYRILLIC CAPITAL LETTER YI
-0408;afii10057;CYRILLIC CAPITAL LETTER JE
-0409;afii10058;CYRILLIC CAPITAL LETTER LJE
-040A;afii10059;CYRILLIC CAPITAL LETTER NJE
-040B;afii10060;CYRILLIC CAPITAL LETTER TSHE
-040C;afii10061;CYRILLIC CAPITAL LETTER KJE
-040E;afii10062;CYRILLIC CAPITAL LETTER SHORT U
-0430;afii10065;CYRILLIC SMALL LETTER A
-0431;afii10066;CYRILLIC SMALL LETTER BE
-0432;afii10067;CYRILLIC SMALL LETTER VE
-0433;afii10068;CYRILLIC SMALL LETTER GHE
-0434;afii10069;CYRILLIC SMALL LETTER DE
-0435;afii10070;CYRILLIC SMALL LETTER IE
-0451;afii10071;CYRILLIC SMALL LETTER IO
-0436;afii10072;CYRILLIC SMALL LETTER ZHE
-0437;afii10073;CYRILLIC SMALL LETTER ZE
-0438;afii10074;CYRILLIC SMALL LETTER I
-0439;afii10075;CYRILLIC SMALL LETTER SHORT I
-043A;afii10076;CYRILLIC SMALL LETTER KA
-043B;afii10077;CYRILLIC SMALL LETTER EL
-043C;afii10078;CYRILLIC SMALL LETTER EM
-043D;afii10079;CYRILLIC SMALL LETTER EN
-043E;afii10080;CYRILLIC SMALL LETTER O
-043F;afii10081;CYRILLIC SMALL LETTER PE
-0440;afii10082;CYRILLIC SMALL LETTER ER
-0441;afii10083;CYRILLIC SMALL LETTER ES
-0442;afii10084;CYRILLIC SMALL LETTER TE
-0443;afii10085;CYRILLIC SMALL LETTER U
-0444;afii10086;CYRILLIC SMALL LETTER EF
-0445;afii10087;CYRILLIC SMALL LETTER HA
-0446;afii10088;CYRILLIC SMALL LETTER TSE
-0447;afii10089;CYRILLIC SMALL LETTER CHE
-0448;afii10090;CYRILLIC SMALL LETTER SHA
-0449;afii10091;CYRILLIC SMALL LETTER SHCHA
-044A;afii10092;CYRILLIC SMALL LETTER HARD SIGN
-044B;afii10093;CYRILLIC SMALL LETTER YERU
-044C;afii10094;CYRILLIC SMALL LETTER SOFT SIGN
-044D;afii10095;CYRILLIC SMALL LETTER E
-044E;afii10096;CYRILLIC SMALL LETTER YU
-044F;afii10097;CYRILLIC SMALL LETTER YA
-0491;afii10098;CYRILLIC SMALL LETTER GHE WITH UPTURN
-0452;afii10099;CYRILLIC SMALL LETTER DJE
-0453;afii10100;CYRILLIC SMALL LETTER GJE
-0454;afii10101;CYRILLIC SMALL LETTER UKRAINIAN IE
-0455;afii10102;CYRILLIC SMALL LETTER DZE
-0456;afii10103;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
-0457;afii10104;CYRILLIC SMALL LETTER YI
-0458;afii10105;CYRILLIC SMALL LETTER JE
-0459;afii10106;CYRILLIC SMALL LETTER LJE
-045A;afii10107;CYRILLIC SMALL LETTER NJE
-045B;afii10108;CYRILLIC SMALL LETTER TSHE
-045C;afii10109;CYRILLIC SMALL LETTER KJE
-045E;afii10110;CYRILLIC SMALL LETTER SHORT U
-040F;afii10145;CYRILLIC CAPITAL LETTER DZHE
-0462;afii10146;CYRILLIC CAPITAL LETTER YAT
-0472;afii10147;CYRILLIC CAPITAL LETTER FITA
-0474;afii10148;CYRILLIC CAPITAL LETTER IZHITSA
-045F;afii10193;CYRILLIC SMALL LETTER DZHE
-0463;afii10194;CYRILLIC SMALL LETTER YAT
-0473;afii10195;CYRILLIC SMALL LETTER FITA
-0475;afii10196;CYRILLIC SMALL LETTER IZHITSA
-04D9;afii10846;CYRILLIC SMALL LETTER SCHWA
-200E;afii299;LEFT-TO-RIGHT MARK
-200F;afii300;RIGHT-TO-LEFT MARK
-200D;afii301;ZERO WIDTH JOINER
-066A;afii57381;ARABIC PERCENT SIGN
-060C;afii57388;ARABIC COMMA
-0660;afii57392;ARABIC-INDIC DIGIT ZERO
-0661;afii57393;ARABIC-INDIC DIGIT ONE
-0662;afii57394;ARABIC-INDIC DIGIT TWO
-0663;afii57395;ARABIC-INDIC DIGIT THREE
-0664;afii57396;ARABIC-INDIC DIGIT FOUR
-0665;afii57397;ARABIC-INDIC DIGIT FIVE
-0666;afii57398;ARABIC-INDIC DIGIT SIX
-0667;afii57399;ARABIC-INDIC DIGIT SEVEN
-0668;afii57400;ARABIC-INDIC DIGIT EIGHT
-0669;afii57401;ARABIC-INDIC DIGIT NINE
-061B;afii57403;ARABIC SEMICOLON
-061F;afii57407;ARABIC QUESTION MARK
-0621;afii57409;ARABIC LETTER HAMZA
-0622;afii57410;ARABIC LETTER ALEF WITH MADDA ABOVE
-0623;afii57411;ARABIC LETTER ALEF WITH HAMZA ABOVE
-0624;afii57412;ARABIC LETTER WAW WITH HAMZA ABOVE
-0625;afii57413;ARABIC LETTER ALEF WITH HAMZA BELOW
-0626;afii57414;ARABIC LETTER YEH WITH HAMZA ABOVE
-0627;afii57415;ARABIC LETTER ALEF
-0628;afii57416;ARABIC LETTER BEH
-0629;afii57417;ARABIC LETTER TEH MARBUTA
-062A;afii57418;ARABIC LETTER TEH
-062B;afii57419;ARABIC LETTER THEH
-062C;afii57420;ARABIC LETTER JEEM
-062D;afii57421;ARABIC LETTER HAH
-062E;afii57422;ARABIC LETTER KHAH
-062F;afii57423;ARABIC LETTER DAL
-0630;afii57424;ARABIC LETTER THAL
-0631;afii57425;ARABIC LETTER REH
-0632;afii57426;ARABIC LETTER ZAIN
-0633;afii57427;ARABIC LETTER SEEN
-0634;afii57428;ARABIC LETTER SHEEN
-0635;afii57429;ARABIC LETTER SAD
-0636;afii57430;ARABIC LETTER DAD
-0637;afii57431;ARABIC LETTER TAH
-0638;afii57432;ARABIC LETTER ZAH
-0639;afii57433;ARABIC LETTER AIN
-063A;afii57434;ARABIC LETTER GHAIN
-0640;afii57440;ARABIC TATWEEL
-0641;afii57441;ARABIC LETTER FEH
-0642;afii57442;ARABIC LETTER QAF
-0643;afii57443;ARABIC LETTER KAF
-0644;afii57444;ARABIC LETTER LAM
-0645;afii57445;ARABIC LETTER MEEM
-0646;afii57446;ARABIC LETTER NOON
-0648;afii57448;ARABIC LETTER WAW
-0649;afii57449;ARABIC LETTER ALEF MAKSURA
-064A;afii57450;ARABIC LETTER YEH
-064B;afii57451;ARABIC FATHATAN
-064C;afii57452;ARABIC DAMMATAN
-064D;afii57453;ARABIC KASRATAN
-064E;afii57454;ARABIC FATHA
-064F;afii57455;ARABIC DAMMA
-0650;afii57456;ARABIC KASRA
-0651;afii57457;ARABIC SHADDA
-0652;afii57458;ARABIC SUKUN
-0647;afii57470;ARABIC LETTER HEH
-06A4;afii57505;ARABIC LETTER VEH
-067E;afii57506;ARABIC LETTER PEH
-0686;afii57507;ARABIC LETTER TCHEH
-0698;afii57508;ARABIC LETTER JEH
-06AF;afii57509;ARABIC LETTER GAF
-0679;afii57511;ARABIC LETTER TTEH
-0688;afii57512;ARABIC LETTER DDAL
-0691;afii57513;ARABIC LETTER RREH
-06BA;afii57514;ARABIC LETTER NOON GHUNNA
-06D2;afii57519;ARABIC LETTER YEH BARREE
-06D5;afii57534;ARABIC LETTER AE
-20AA;afii57636;NEW SHEQEL SIGN
-05BE;afii57645;HEBREW PUNCTUATION MAQAF
-05C3;afii57658;HEBREW PUNCTUATION SOF PASUQ
-05D0;afii57664;HEBREW LETTER ALEF
-05D1;afii57665;HEBREW LETTER BET
-05D2;afii57666;HEBREW LETTER GIMEL
-05D3;afii57667;HEBREW LETTER DALET
-05D4;afii57668;HEBREW LETTER HE
-05D5;afii57669;HEBREW LETTER VAV
-05D6;afii57670;HEBREW LETTER ZAYIN
-05D7;afii57671;HEBREW LETTER HET
-05D8;afii57672;HEBREW LETTER TET
-05D9;afii57673;HEBREW LETTER YOD
-05DA;afii57674;HEBREW LETTER FINAL KAF
-05DB;afii57675;HEBREW LETTER KAF
-05DC;afii57676;HEBREW LETTER LAMED
-05DD;afii57677;HEBREW LETTER FINAL MEM
-05DE;afii57678;HEBREW LETTER MEM
-05DF;afii57679;HEBREW LETTER FINAL NUN
-05E0;afii57680;HEBREW LETTER NUN
-05E1;afii57681;HEBREW LETTER SAMEKH
-05E2;afii57682;HEBREW LETTER AYIN
-05E3;afii57683;HEBREW LETTER FINAL PE
-05E4;afii57684;HEBREW LETTER PE
-05E5;afii57685;HEBREW LETTER FINAL TSADI
-05E6;afii57686;HEBREW LETTER TSADI
-05E7;afii57687;HEBREW LETTER QOF
-05E8;afii57688;HEBREW LETTER RESH
-05E9;afii57689;HEBREW LETTER SHIN
-05EA;afii57690;HEBREW LETTER TAV
-05F0;afii57716;HEBREW LIGATURE YIDDISH DOUBLE VAV
-05F1;afii57717;HEBREW LIGATURE YIDDISH VAV YOD
-05F2;afii57718;HEBREW LIGATURE YIDDISH DOUBLE YOD
-05B4;afii57793;HEBREW POINT HIRIQ
-05B5;afii57794;HEBREW POINT TSERE
-05B6;afii57795;HEBREW POINT SEGOL
-05BB;afii57796;HEBREW POINT QUBUTS
-05B8;afii57797;HEBREW POINT QAMATS
-05B7;afii57798;HEBREW POINT PATAH
-05B0;afii57799;HEBREW POINT SHEVA
-05B2;afii57800;HEBREW POINT HATAF PATAH
-05B1;afii57801;HEBREW POINT HATAF SEGOL
-05B3;afii57802;HEBREW POINT HATAF QAMATS
-05C2;afii57803;HEBREW POINT SIN DOT
-05C1;afii57804;HEBREW POINT SHIN DOT
-05B9;afii57806;HEBREW POINT HOLAM
-05BC;afii57807;HEBREW POINT DAGESH OR MAPIQ
-05BD;afii57839;HEBREW POINT METEG
-05BF;afii57841;HEBREW POINT RAFE
-05C0;afii57842;HEBREW PUNCTUATION PASEQ
-02BC;afii57929;MODIFIER LETTER APOSTROPHE
-2105;afii61248;CARE OF
-2113;afii61289;SCRIPT SMALL L
-2116;afii61352;NUMERO SIGN
-202C;afii61573;POP DIRECTIONAL FORMATTING
-202D;afii61574;LEFT-TO-RIGHT OVERRIDE
-202E;afii61575;RIGHT-TO-LEFT OVERRIDE
-200C;afii61664;ZERO WIDTH NON-JOINER
-066D;afii63167;ARABIC FIVE POINTED STAR
-02BD;afii64937;MODIFIER LETTER REVERSED COMMA
 00E0;agrave;LATIN SMALL LETTER A WITH GRAVE
 2135;aleph;ALEF SYMBOL
 03B1;alpha;GREEK SMALL LETTER ALPHA
@@ -684,7 +458,6 @@
 011F;gbreve;LATIN SMALL LETTER G WITH BREVE
 01E7;gcaron;LATIN SMALL LETTER G WITH CARON
 011D;gcircumflex;LATIN SMALL LETTER G WITH CIRCUMFLEX
-0123;gcommaaccent;LATIN SMALL LETTER G WITH CEDILLA
 0121;gdotaccent;LATIN SMALL LETTER G WITH DOT ABOVE
 00DF;germandbls;LATIN SMALL LETTER SHARP S
 2207;gradient;NABLA
@@ -730,13 +503,11 @@
 0135;jcircumflex;LATIN SMALL LETTER J WITH CIRCUMFLEX
 006B;k;LATIN SMALL LETTER K
 03BA;kappa;GREEK SMALL LETTER KAPPA
-0137;kcommaaccent;LATIN SMALL LETTER K WITH CEDILLA
 0138;kgreenlandic;LATIN SMALL LETTER KRA
 006C;l;LATIN SMALL LETTER L
 013A;lacute;LATIN SMALL LETTER L WITH ACUTE
 03BB;lambda;GREEK SMALL LETTER LAMDA
 013E;lcaron;LATIN SMALL LETTER L WITH CARON
-013C;lcommaaccent;LATIN SMALL LETTER L WITH CEDILLA
 0140;ldot;LATIN SMALL LETTER L WITH MIDDLE DOT
 003C;less;LESS-THAN SIGN
 2264;lessequal;LESS-THAN OR EQUAL TO
@@ -754,7 +525,7 @@
 2642;male;MALE SIGN
 2212;minus;MINUS SIGN
 2032;minute;PRIME
-03BC;mu;GREEK SMALL LETTER MU
+00B5;mu;MICRO SIGN
 00D7;multiply;MULTIPLICATION SIGN
 266A;musicalnote;EIGHTH NOTE
 266B;musicalnotedbl;BEAMED EIGHTH NOTES
@@ -762,7 +533,6 @@
 0144;nacute;LATIN SMALL LETTER N WITH ACUTE
 0149;napostrophe;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
 0148;ncaron;LATIN SMALL LETTER N WITH CARON
-0146;ncommaaccent;LATIN SMALL LETTER N WITH CEDILLA
 0039;nine;DIGIT NINE
 2209;notelement;NOT AN ELEMENT OF
 2260;notequal;NOT EQUAL TO
@@ -837,7 +607,6 @@
 0155;racute;LATIN SMALL LETTER R WITH ACUTE
 221A;radical;SQUARE ROOT
 0159;rcaron;LATIN SMALL LETTER R WITH CARON
-0157;rcommaaccent;LATIN SMALL LETTER R WITH CEDILLA
 2286;reflexsubset;SUBSET OF OR EQUAL TO
 2287;reflexsuperset;SUPERSET OF OR EQUAL TO
 00AE;registered;REGISTERED SIGN
@@ -850,7 +619,6 @@
 0161;scaron;LATIN SMALL LETTER S WITH CARON
 015F;scedilla;LATIN SMALL LETTER S WITH CEDILLA
 015D;scircumflex;LATIN SMALL LETTER S WITH CIRCUMFLEX
-0219;scommaaccent;LATIN SMALL LETTER S WITH COMMA BELOW
 2033;second;DOUBLE PRIME
 00A7;section;SECTION SIGN
 003B;semicolon;SEMICOLON
@@ -873,7 +641,6 @@
 03C4;tau;GREEK SMALL LETTER TAU
 0167;tbar;LATIN SMALL LETTER T WITH STROKE
 0165;tcaron;LATIN SMALL LETTER T WITH CARON
-0163;tcommaaccent;LATIN SMALL LETTER T WITH CEDILLA
 2234;therefore;THEREFORE
 03B8;theta;GREEK SMALL LETTER THETA
 03D1;theta1;GREEK THETA SYMBOL
@@ -934,6 +701,7 @@
 017C;zdotaccent;LATIN SMALL LETTER Z WITH DOT ABOVE
 0030;zero;DIGIT ZERO
 03B6;zeta;GREEK SMALL LETTER ZETA
+#END
 """
 
 
@@ -954,12 +722,12 @@
 			continue
 		m = parseAGL_RE.match(line)
 		if not m:
-			raise AGLError, "syntax error in glyphlist.txt: %s" % repr(line[:20])
+			raise AGLError("syntax error in glyphlist.txt: %s" % repr(line[:20]))
 		unicode = m.group(1)
 		assert len(unicode) == 4
 		unicode = int(unicode, 16)
 		glyphName = m.group(2)
-		if AGL2UV.has_key(glyphName):
+		if glyphName in AGL2UV:
 			# the above table contains identical duplicates
 			assert AGL2UV[glyphName] == unicode
 		else:
diff --git a/Lib/fontTools/cffLib.py b/Lib/fontTools/cffLib.py
index dd0112c..a4a85bd 100644
--- a/Lib/fontTools/cffLib.py
+++ b/Lib/fontTools/cffLib.py
@@ -4,11 +4,12 @@
 # $Id: cffLib.py,v 1.34 2008-03-07 19:56:17 jvr Exp $
 #
 
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-import string
 from fontTools.misc import psCharStrings
 from fontTools.misc.textTools import safeEval
+import struct
 
 DEBUG = 0
 
@@ -20,7 +21,7 @@
 	offSize: B
 """
 
-class CFFFontSet:
+class CFFFontSet(object):
 	
 	def __init__(self):
 		pass
@@ -51,7 +52,7 @@
 		try:
 			index = self.fontNames.index(name)
 		except ValueError:
-			raise KeyError, name
+			raise KeyError(name)
 		return self.topDictIndex[index]
 	
 	def compile(self, file, otFont):
@@ -78,9 +79,8 @@
 		writer.toFile(file)
 	
 	def toXML(self, xmlWriter, progress=None):
-		xmlWriter.newline()
 		for fontName in self.fontNames:
-			xmlWriter.begintag("CFFFont", name=fontName)
+			xmlWriter.begintag("CFFFont", name=tostr(fontName))
 			xmlWriter.newline()
 			font = self[fontName]
 			font.toXML(xmlWriter, progress)
@@ -92,9 +92,8 @@
 		self.GlobalSubrs.toXML(xmlWriter, progress)
 		xmlWriter.endtag("GlobalSubrs")
 		xmlWriter.newline()
-		xmlWriter.newline()
 	
-	def fromXML(self, (name, attrs, content)):
+	def fromXML(self, name, attrs, content):
 		if not hasattr(self, "GlobalSubrs"):
 			self.GlobalSubrs = GlobalSubrsIndex()
 			self.major = 1
@@ -113,18 +112,19 @@
 			for element in content:
 				if isinstance(element, basestring):
 					continue
-				topDict.fromXML(element)
+				name, attrs, content = element
+				topDict.fromXML(name, attrs, content)
 		elif name == "GlobalSubrs":
 			for element in content:
 				if isinstance(element, basestring):
 					continue
 				name, attrs, content = element
 				subr = psCharStrings.T2CharString()
-				subr.fromXML((name, attrs, content))
+				subr.fromXML(name, attrs, content)
 				self.GlobalSubrs.append(subr)
 
 
-class CFFWriter:
+class CFFWriter(object):
 	
 	def __init__(self):
 		self.data = []
@@ -135,9 +135,9 @@
 	def toFile(self, file):
 		lastPosList = None
 		count = 1
-		while 1:
+		while True:
 			if DEBUG:
-				print "CFFWriter.toFile() iteration:", count
+				print("CFFWriter.toFile() iteration:", count)
 			count = count + 1
 			pos = 0
 			posList = [pos]
@@ -154,7 +154,7 @@
 				break
 			lastPosList = posList
 		if DEBUG:
-			print "CFFWriter.toFile() writing to file."
+			print("CFFWriter.toFile() writing to file.")
 		begin = file.tell()
 		posList = [0]
 		for item in self.data:
@@ -178,7 +178,7 @@
 	return offSize
 
 
-class IndexCompiler:
+class IndexCompiler(object):
 	
 	def __init__(self, items, strings, parent):
 		self.items = self.getItems(items, strings)
@@ -224,7 +224,7 @@
 			if hasattr(item, "toFile"):
 				item.toFile(file)
 			else:
-				file.write(item)
+				file.write(tobytes(item, encoding="latin1"))
 
 
 class IndexedStringsCompiler(IndexCompiler):
@@ -301,7 +301,7 @@
 		self.parent.rawDict["CharStrings"] = pos
 
 
-class Index:
+class Index(object):
 	
 	"""This class represents what the CFF spec calls an INDEX."""
 	
@@ -313,7 +313,7 @@
 			self.items = []
 			return
 		if DEBUG:
-			print "loading %s at %s" % (name, file.tell())
+			print("loading %s at %s" % (name, file.tell()))
 		self.file = file
 		count = readCard16(file)
 		self.count = count
@@ -323,10 +323,10 @@
 			return
 		offSize = readCard8(file)
 		if DEBUG:
-			print "    index count: %s offSize: %s" % (count, offSize)
+			print("    index count: %s offSize: %s" % (count, offSize))
 		assert offSize <= 4, "offSize too large: %s" % offSize
 		self.offsets = offsets = []
-		pad = '\0' * (4 - offSize)
+		pad = b'\0' * (4 - offSize)
 		for index in range(count+1):
 			chunk = file.read(offSize)
 			chunk = pad + chunk
@@ -335,7 +335,7 @@
 		self.offsetBase = file.tell() - 1
 		file.seek(self.offsetBase + offsets[-1])  # pretend we've read the whole lot
 		if DEBUG:
-			print "    end of %s at %s" % (name, file.tell())
+			print("    end of %s at %s" % (name, file.tell()))
 	
 	def __len__(self):
 		return len(self.items)
@@ -400,11 +400,11 @@
 			xmlWriter.endtag("CharString")
 			xmlWriter.newline()
 	
-	def fromXML(self, (name, attrs, content)):
-		if name <> "CharString":
+	def fromXML(self, name, attrs, content):
+		if name != "CharString":
 			return
 		subr = psCharStrings.T2CharString()
-		subr.fromXML((name, attrs, content))
+		subr.fromXML(name, attrs, content)
 		self.append(subr)
 	
 	def getItemAndSelector(self, index):
@@ -440,14 +440,15 @@
 	
 	compilerClass = FDArrayIndexCompiler
 
-	def fromXML(self, (name, attrs, content)):
-		if name <> "FontDict":
+	def fromXML(self, name, attrs, content):
+		if name != "FontDict":
 			return
 		fontDict = FontDict()
 		for element in content:
 			if isinstance(element, basestring):
 				continue
-			fontDict.fromXML(element)
+			name, attrs, content = element
+			fontDict.fromXML(name, attrs, content)
 		self.append(fontDict)
 
 
@@ -476,10 +477,10 @@
 						gidArray[glyphID] = fd
 				self.gidArray = gidArray
 			else:
-				assert 0, "unsupported FDSelect format: %s" % format
+				assert False, "unsupported FDSelect format: %s" % format
 		else:
 			# reading from XML. Make empty gidArray,, and leave format as passed in.
-			# format == None will result in the smallest representation being used.
+			# format is None will result in the smallest representation being used.
 			self.format = format
 			self.gidArray = []
 
@@ -497,7 +498,7 @@
 		self.gidArray.append(fdSelectValue)
 	
 
-class CharStrings:
+class CharStrings(object):
 	
 	def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
 		if file is not None:
@@ -511,22 +512,22 @@
 			self.charStringsAreIndexed = 0
 			self.globalSubrs = globalSubrs
 			self.private = private
-			if fdSelect != None:
+			if fdSelect is not None:
 				self.fdSelect = fdSelect
-			if fdArray!= None:
+			if fdArray is not None:
 				self.fdArray = fdArray
 	
 	def keys(self):
-		return self.charStrings.keys()
+		return list(self.charStrings.keys())
 	
 	def values(self):
 		if self.charStringsAreIndexed:
 			return self.charStringsIndex
 		else:
-			return self.charStrings.values()
+			return list(self.charStrings.values())
 	
 	def has_key(self, name):
-		return self.charStrings.has_key(name)
+		return name in self.charStrings
 	
 	def __len__(self):
 		return len(self.charStrings)
@@ -556,8 +557,7 @@
 			return self.charStrings[name], sel
 	
 	def toXML(self, xmlWriter, progress):
-		names = self.keys()
-		names.sort()
+		names = sorted(self.keys())
 		i = 0
 		step = 10
 		numGlyphs = len(names)
@@ -578,15 +578,15 @@
 			xmlWriter.newline()
 			if not i % step and progress is not None:
 				progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
-				progress.increment(step / float(numGlyphs))
+				progress.increment(step / numGlyphs)
 			i = i + 1
 	
-	def fromXML(self, (name, attrs, content)):
+	def fromXML(self, name, attrs, content):
 		for element in content:
 			if isinstance(element, basestring):
 				continue
 			name, attrs, content = element
-			if name <> "CharString":
+			if name != "CharString":
 				continue
 			fdID = -1
 			if hasattr(self, "fdArray"):
@@ -599,27 +599,27 @@
 			charString = psCharStrings.T2CharString(
 					private=private,
 					globalSubrs=self.globalSubrs)
-			charString.fromXML((name, attrs, content))
+			charString.fromXML(name, attrs, content)
 			if fdID >= 0:
 				charString.fdSelectIndex = fdID
 			self[glyphName] = charString
 
 
 def readCard8(file):
-	return ord(file.read(1))
+	return byteord(file.read(1))
 
 def readCard16(file):
 	value, = struct.unpack(">H", file.read(2))
 	return value
 
 def writeCard8(file, value):
-	file.write(chr(value))
+	file.write(bytechr(value))
 
 def writeCard16(file, value):
 	file.write(struct.pack(">H", value))
 
 def packCard8(value):
-	return chr(value)
+	return bytechr(value)
 
 def packCard16(value):
 	return struct.pack(">H", value)
@@ -634,9 +634,9 @@
 	d = {}
 	for op, name, arg, default, conv in table:
 		if isinstance(op, tuple):
-			op = chr(op[0]) + chr(op[1])
+			op = bytechr(op[0]) + bytechr(op[1])
 		else:
-			op = chr(op)
+			op = bytechr(op)
 		d[name] = (op, arg)
 	return d
 
@@ -660,7 +660,7 @@
 	return d
 
 
-class SimpleConverter:
+class SimpleConverter(object):
 	def read(self, parent, value):
 		return value
 	def write(self, parent, value):
@@ -668,13 +668,30 @@
 	def xmlWrite(self, xmlWriter, name, value, progress):
 		xmlWriter.simpletag(name, value=value)
 		xmlWriter.newline()
-	def xmlRead(self, (name, attrs, content), parent):
+	def xmlRead(self, name, attrs, content, parent):
 		return attrs["value"]
 
+class ASCIIConverter(SimpleConverter):
+	def read(self, parent, value):
+		return tostr(value, encoding='ascii')
+	def write(self, parent, value):
+		return tobytes(value, encoding='ascii')
+	def xmlWrite(self, xmlWriter, name, value, progress):
+		xmlWriter.simpletag(name, value=tostr(value, encoding="ascii"))
+		xmlWriter.newline()
+	def xmlRead(self, name, attrs, content, parent):
+		return tobytes(attrs["value"], encoding=("ascii"))
+
 class Latin1Converter(SimpleConverter):
-	def xmlRead(self, (name, attrs, content), parent):
-		s = unicode(attrs["value"], "utf-8")
-		return s.encode("latin-1")
+	def read(self, parent, value):
+		return tostr(value, encoding='latin1')
+	def write(self, parent, value):
+		return tobytes(value, encoding='latin1')
+	def xmlWrite(self, xmlWriter, name, value, progress):
+		xmlWriter.simpletag(name, value=tostr(value, encoding="latin1"))
+		xmlWriter.newline()
+	def xmlRead(self, name, attrs, content, parent):
+		return tobytes(attrs["value"], encoding=("latin1"))
 
 
 def parseNum(s):
@@ -685,17 +702,17 @@
 	return value
 
 class NumberConverter(SimpleConverter):
-	def xmlRead(self, (name, attrs, content), parent):
+	def xmlRead(self, name, attrs, content, parent):
 		return parseNum(attrs["value"])
 
 class ArrayConverter(SimpleConverter):
 	def xmlWrite(self, xmlWriter, name, value, progress):
-		value = map(str, value)
-		xmlWriter.simpletag(name, value=" ".join(value))
+		value = " ".join(map(str, value))
+		xmlWriter.simpletag(name, value=value)
 		xmlWriter.newline()
-	def xmlRead(self, (name, attrs, content), parent):
+	def xmlRead(self, name, attrs, content, parent):
 		values = attrs["value"].split()
-		return map(parseNum, values)
+		return [parseNum(value) for value in values]
 
 class TableConverter(SimpleConverter):
 	def xmlWrite(self, xmlWriter, name, value, progress):
@@ -704,12 +721,13 @@
 		value.toXML(xmlWriter, progress)
 		xmlWriter.endtag(name)
 		xmlWriter.newline()
-	def xmlRead(self, (name, attrs, content), parent):
+	def xmlRead(self, name, attrs, content, parent):
 		ob = self.getClass()()
 		for element in content:
 			if isinstance(element, basestring):
 				continue
-			ob.fromXML(element)
+			name, attrs, content = element
+			ob.fromXML(name, attrs, content)
 		return ob
 
 class PrivateDictConverter(TableConverter):
@@ -721,7 +739,7 @@
 		priv = PrivateDict(parent.strings, file, offset)
 		file.seek(offset)
 		data = file.read(size)
-		len(data) == size
+		assert len(data) == size
 		priv.decompile(data)
 		return priv
 	def write(self, parent, value):
@@ -752,7 +770,7 @@
 		return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
 	def write(self, parent, value):
 		return 0  # dummy value
-	def xmlRead(self, (name, attrs, content), parent):
+	def xmlRead(self, name, attrs, content, parent):
 		if hasattr(parent, "ROS"):
 			# if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray 
 			private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
@@ -760,10 +778,10 @@
 			# if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray. 
 			private, fdSelect, fdArray = parent.Private, None, None
 		charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
-		charStrings.fromXML((name, attrs, content))
+		charStrings.fromXML(name, attrs, content)
 		return charStrings
 
-class CharsetConverter:
+class CharsetConverter(object):
 	def read(self, parent, value):
 		isCID = hasattr(parent, "ROS")
 		if value > 2:
@@ -771,7 +789,7 @@
 			file = parent.file
 			file.seek(value)
 			if DEBUG:
-				print "loading charset at %s" % value
+				print("loading charset at %s" % value)
 			format = readCard8(file)
 			if format == 0:
 				charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
@@ -781,9 +799,9 @@
 				raise NotImplementedError
 			assert len(charset) == numGlyphs
 			if DEBUG:
-				print "    charset end at %s" % file.tell()
+				print("    charset end at %s" % file.tell())
 		else: # offset == 0 -> no charset data.
-			if isCID or not parent.rawDict.has_key("CharStrings"): 
+			if isCID or "CharStrings" not in parent.rawDict: 
 				assert value == 0 # We get here only when processing fontDicts from the FDArray of CFF-CID fonts. Only the real topDict references the chrset.
 				charset = None
 			elif value == 0:
@@ -802,12 +820,12 @@
 		##xmlWriter.simpletag("charset")
 		xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
 		xmlWriter.newline()
-	def xmlRead(self, (name, attrs, content), parent):
+	def xmlRead(self, name, attrs, content, parent):
 		if 0:
 			return safeEval(attrs["value"])
 
 
-class CharsetCompiler:
+class CharsetCompiler(object):
 	
 	def __init__(self, strings, charset, parent):
 		assert charset[0] == '.notdef'
@@ -837,8 +855,8 @@
 	return strings.getSID(name)
 
 def packCharset0(charset, isCID, strings):
-	format = 0
-	data = [packCard8(format)]
+	fmt = 0
+	data = [packCard8(fmt)]
 	if isCID:
 		getNameID = getCIDfromName
 	else:
@@ -846,11 +864,11 @@
 
 	for name in charset[1:]:
 		data.append(packCard16(getNameID(name,strings)))
-	return "".join(data)
+	return bytesjoin(data)
 
 
 def packCharset(charset, isCID, strings):
-	format = 1
+	fmt = 1
 	ranges = []
 	first = None
 	end = 0
@@ -863,43 +881,43 @@
 		SID = getNameID(name, strings)
 		if first is None:
 			first = SID
-		elif end + 1 <> SID:
+		elif end + 1 != SID:
 			nLeft = end - first
 			if nLeft > 255:
-				format = 2
+				fmt = 2
 			ranges.append((first, nLeft))
 			first = SID
 		end = SID
 	nLeft = end - first
 	if nLeft > 255:
-		format = 2
+		fmt = 2
 	ranges.append((first, nLeft))
 	
-	data = [packCard8(format)]
-	if format == 1:
+	data = [packCard8(fmt)]
+	if fmt == 1:
 		nLeftFunc = packCard8
 	else:
 		nLeftFunc = packCard16
 	for first, nLeft in ranges:
 		data.append(packCard16(first) + nLeftFunc(nLeft))
-	return "".join(data)
+	return bytesjoin(data)
 
 def parseCharset0(numGlyphs, file, strings, isCID):
 	charset = [".notdef"]
 	if isCID:
 		for i in range(numGlyphs - 1):
 			CID = readCard16(file)
-			charset.append("cid" + string.zfill(str(CID), 5) )
+			charset.append("cid" + str(CID).zfill(5))
 	else:
 		for i in range(numGlyphs - 1):
 			SID = readCard16(file)
 			charset.append(strings[SID])
 	return charset
 
-def parseCharset(numGlyphs, file, strings, isCID, format):
+def parseCharset(numGlyphs, file, strings, isCID, fmt):
 	charset = ['.notdef']
 	count = 1
-	if format == 1:
+	if fmt == 1:
 		nLeftFunc = readCard8
 	else:
 		nLeftFunc = readCard16
@@ -908,7 +926,7 @@
 		nLeft = nLeftFunc(file)
 		if isCID:
 			for CID in range(first, first+nLeft+1):
-				charset.append("cid" + string.zfill(str(CID), 5) )
+				charset.append("cid" + str(CID).zfill(5))
 		else:
 			for SID in range(first, first+nLeft+1):
 				charset.append(strings[SID])
@@ -916,7 +934,7 @@
 	return charset
 
 
-class EncodingCompiler:
+class EncodingCompiler(object):
 
 	def __init__(self, strings, encoding, parent):
 		assert not isinstance(encoding, basestring)
@@ -950,16 +968,16 @@
 			file = parent.file
 			file.seek(value)
 			if DEBUG:
-				print "loading Encoding at %s" % value
-			format = readCard8(file)
-			haveSupplement = format & 0x80
+				print("loading Encoding at %s" % value)
+			fmt = readCard8(file)
+			haveSupplement = fmt & 0x80
 			if haveSupplement:
-				raise NotImplementedError, "Encoding supplements are not yet supported"
-			format = format & 0x7f
-			if format == 0:
+				raise NotImplementedError("Encoding supplements are not yet supported")
+			fmt = fmt & 0x7f
+			if fmt == 0:
 				encoding = parseEncoding0(parent.charset, file, haveSupplement,
 						parent.strings)
-			elif format == 1:
+			elif fmt == 1:
 				encoding = parseEncoding1(parent.charset, file, haveSupplement,
 						parent.strings)
 			return encoding
@@ -986,8 +1004,8 @@
 		xmlWriter.endtag(name)
 		xmlWriter.newline()
 
-	def xmlRead(self, (name, attrs, content), parent):
-		if attrs.has_key("name"):
+	def xmlRead(self, name, attrs, content, parent):
+		if "name" in attrs:
 			return attrs["name"]
 		encoding = [".notdef"] * 256
 		for element in content:
@@ -1023,7 +1041,7 @@
 	return encoding
 
 def packEncoding0(charset, encoding, strings):
-	format = 0
+	fmt = 0
 	m = {}
 	for code in range(len(encoding)):
 		name = encoding[code]
@@ -1037,15 +1055,15 @@
 	while codes and codes[-1] is None:
 		codes.pop()
 
-	data = [packCard8(format), packCard8(len(codes))]
+	data = [packCard8(fmt), packCard8(len(codes))]
 	for code in codes:
 		if code is None:
 			code = 0
 		data.append(packCard8(code))
-	return "".join(data)
+	return bytesjoin(data)
 
 def packEncoding1(charset, encoding, strings):
-	format = 1
+	fmt = 1
 	m = {}
 	for code in range(len(encoding)):
 		name = encoding[code]
@@ -1058,7 +1076,7 @@
 		code = m.get(name, -1)
 		if first is None:
 			first = code
-		elif end + 1 <> code:
+		elif end + 1 != code:
 			nLeft = end - first
 			ranges.append((first, nLeft))
 			first = code
@@ -1070,12 +1088,12 @@
 	while ranges and ranges[-1][0] == -1:
 		ranges.pop()
 
-	data = [packCard8(format), packCard8(len(ranges))]
+	data = [packCard8(fmt), packCard8(len(ranges))]
 	for first, nLeft in ranges:
 		if first == -1:  # unencoded
 			first = 0
 		data.append(packCard8(first) + packCard8(nLeft))
-	return "".join(data)
+	return bytesjoin(data)
 
 
 class FDArrayConverter(TableConverter):
@@ -1091,16 +1109,17 @@
 	def write(self, parent, value):
 		return 0  # dummy value
 
-	def xmlRead(self, (name, attrs, content), parent):
+	def xmlRead(self, name, attrs, content, parent):
 		fdArray = FDArrayIndex()
 		for element in content:
 			if isinstance(element, basestring):
 				continue
-			fdArray.fromXML(element)
+			name, attrs, content = element
+			fdArray.fromXML(name, attrs, content)
 		return fdArray
 
 
-class FDSelectConverter:
+class FDSelectConverter(object):
 
 	def read(self, parent, value):
 		file = parent.file
@@ -1117,24 +1136,24 @@
 		xmlWriter.simpletag(name, [('format', value.format)])
 		xmlWriter.newline()
 
-	def xmlRead(self, (name, attrs, content), parent):
-		format = safeEval(attrs["format"])
+	def xmlRead(self, name, attrs, content, parent):
+		fmt = safeEval(attrs["format"])
 		file = None
 		numGlyphs = None
-		fdSelect = FDSelect(file, numGlyphs, format)
+		fdSelect = FDSelect(file, numGlyphs, fmt)
 		return fdSelect
 		
 
 def packFDSelect0(fdSelectArray):
-	format = 0
-	data = [packCard8(format)]
+	fmt = 0
+	data = [packCard8(fmt)]
 	for index in fdSelectArray:
 		data.append(packCard8(index))
-	return "".join(data)
+	return bytesjoin(data)
 
 
 def packFDSelect3(fdSelectArray):
-	format = 3
+	fmt = 3
 	fdRanges = []
 	first = None
 	end = 0
@@ -1147,23 +1166,23 @@
 			lastFDIndex = fdIndex
 	sentinelGID = i + 1
 		
-	data = [packCard8(format)]
+	data = [packCard8(fmt)]
 	data.append(packCard16( len(fdRanges) ))
 	for fdRange in fdRanges:
 		data.append(packCard16(fdRange[0]))
 		data.append(packCard8(fdRange[1]))
 	data.append(packCard16(sentinelGID))
-	return "".join(data)
+	return bytesjoin(data)
 
 
-class FDSelectCompiler:
+class FDSelectCompiler(object):
 	
 	def __init__(self, fdSelect, parent):
-		format = fdSelect.format
+		fmt = fdSelect.format
 		fdSelectArray = fdSelect.gidArray
-		if format == 0:
+		if fmt == 0:
 			self.data = packFDSelect0(fdSelectArray)
-		elif format == 3:
+		elif fmt == 3:
 			self.data = packFDSelect3(fdSelectArray)
 		else:
 			# choose smaller of the two formats
@@ -1192,11 +1211,11 @@
 
 	def xmlWrite(self, xmlWriter, name, value, progress):
 		registry, order, supplement = value
-		xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
+		xmlWriter.simpletag(name, [('Registry', tostr(registry)), ('Order', tostr(order)),
 			('Supplement', supplement)])
 		xmlWriter.newline()
 
-	def xmlRead(self, (name, attrs, content), parent):
+	def xmlRead(self, name, attrs, content, parent):
 		return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
 
 
@@ -1277,9 +1296,9 @@
 		elif arg == "number":
 			conv = NumberConverter()
 		elif arg == "SID":
-			conv = SimpleConverter()
+			conv = ASCIIConverter()
 		else:
-			assert 0
+			assert False
 		table[i] = op, name, arg, default, conv
 
 addConverters(privateDictOperators)
@@ -1294,7 +1313,7 @@
 	operators = buildOperatorDict(privateDictOperators)
 
 
-class DictCompiler:
+class DictCompiler(object):
 	
 	def __init__(self, dictObj, strings, parent):
 		assert isinstance(strings, IndexedStrings)
@@ -1321,8 +1340,8 @@
 	
 	def compile(self, reason):
 		if DEBUG:
-			print "-- compiling %s for %s" % (self.__class__.__name__, reason)
-			print "in baseDict: ", self
+			print("-- compiling %s for %s" % (self.__class__.__name__, reason))
+			print("in baseDict: ", self)
 		rawDict = self.rawDict
 		data = []
 		for name in self.dictObj.order:
@@ -1342,7 +1361,7 @@
 				arghandler = getattr(self, "arg_" + argType)
 				data.append(arghandler(value))
 			data.append(op)
-		return "".join(data)
+		return bytesjoin(data)
 	
 	def toFile(self, file):
 		file.write(self.compile("toFile"))
@@ -1355,7 +1374,7 @@
 		data = []
 		for num in value:
 			data.append(encodeNumber(num))
-		return "".join(data)
+		return bytesjoin(data)
 	def arg_delta(self, value):
 		out = []
 		last = 0
@@ -1365,7 +1384,7 @@
 		data = []
 		for num in out:
 			data.append(encodeNumber(num))
-		return "".join(data)
+		return bytesjoin(data)
 
 
 def encodeNumber(num):
@@ -1396,7 +1415,6 @@
 			if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
 				charStrings = self.dictObj.CharStrings
 				for name in self.dictObj.charset:
-					charstring = charStrings[name]
 					fdSelect.append(charStrings[name].fdSelectIndex)
 			fdSelectComp = FDSelectCompiler(fdSelect, self)
 			children.append(fdSelectComp)
@@ -1450,12 +1468,12 @@
 		return children
 
 
-class BaseDict:
+class BaseDict(object):
 	
 	def __init__(self, strings=None, file=None, offset=None):
 		self.rawDict = {}
 		if DEBUG:
-			print "loading %s at %s" % (self.__class__.__name__, offset)
+			print("loading %s at %s" % (self.__class__.__name__, offset))
 		self.file = file
 		self.offset = offset
 		self.strings = strings
@@ -1463,7 +1481,7 @@
 	
 	def decompile(self, data):
 		if DEBUG:
-			print "    length %s is %s" % (self.__class__.__name__, len(data))
+			print("    length %s is %s" % (self.__class__.__name__, len(data)))
 		dec = self.decompilerClass(self.strings)
 		dec.decompile(data)
 		self.rawDict = dec.getDict()
@@ -1480,7 +1498,7 @@
 		if value is None:
 			value = self.defaults.get(name)
 		if value is None:
-			raise AttributeError, name
+			raise AttributeError(name)
 		conv = self.converters[name]
 		value = conv.read(self, value)
 		setattr(self, name, value)
@@ -1496,9 +1514,9 @@
 			conv = self.converters[name]
 			conv.xmlWrite(xmlWriter, name, value, progress)
 	
-	def fromXML(self, (name, attrs, content)):
+	def fromXML(self, name, attrs, content):
 		conv = self.converters[name]
-		value = conv.xmlRead((name, attrs, content), self)
+		value = conv.xmlRead(name, attrs, content, self)
 		setattr(self, name, value)
 
 
@@ -1544,10 +1562,10 @@
 			try:
 				charString.decompile()
 			except:
-				print "Error in charstring ", i
+				print("Error in charstring ", i)
 				import sys
-				type, value = sys. exc_info()[0:2]
-				raise type(value)
+				typ, value = sys.exc_info()[0:2]
+				raise typ(value)
 			if not i % 30 and progress:
 				progress.increment(0)  # update
 			i = i + 1
@@ -1582,7 +1600,7 @@
 	compilerClass = PrivateDictCompiler
 
 
-class IndexedStrings:
+class IndexedStrings(object):
 	
 	"""SID -> string mapping."""
 	
@@ -1590,7 +1608,7 @@
 		if file is None:
 			strings = []
 		else:
-			strings = list(Index(file))
+			strings = [tostr(s, encoding="latin1") for s in Index(file)]
 		self.strings = strings
 	
 	def getCompiler(self):
@@ -1608,9 +1626,9 @@
 	def getSID(self, s):
 		if not hasattr(self, "stringMapping"):
 			self.buildStringMapping()
-		if cffStandardStringMapping.has_key(s):
+		if s in cffStandardStringMapping:
 			SID = cffStandardStringMapping[s]
-		elif self.stringMapping.has_key(s):
+		elif s in self.stringMapping:
 			SID = self.stringMapping[s]
 		else:
 			SID = len(self.strings) + cffStandardStringCount
diff --git a/Lib/fontTools/fondLib.py b/Lib/fontTools/fondLib.py
deleted file mode 100644
index 74ae13b..0000000
--- a/Lib/fontTools/fondLib.py
+++ /dev/null
@@ -1,554 +0,0 @@
-import os
-import struct
-from fontTools.misc import sstruct
-import string
-try:
-	from Carbon import Res
-except ImportError:
-	import Res
-
-
-error = "fondLib.error"
-
-DEBUG = 0
-
-headerformat = """
-	>
-	ffFlags:	h
-	ffFamID:	h
-	ffFirstChar:	h
-	ffLastChar:	h
-	ffAscent:	h
-	ffDescent:	h
-	ffLeading:	h
-	ffWidMax:	h
-	ffWTabOff:	l
-	ffKernOff:	l
-	ffStylOff:	l
-"""
-
-FONDheadersize = 52
-
-class FontFamily:
-	
-	def __init__(self, theRes, mode = 'r'):
-		self.ID, type, self.name = theRes.GetResInfo()
-		if type <> 'FOND':
-			raise ValueError, "FOND resource required"
-		self.FOND = theRes
-		self.mode = mode
-		self.changed = 0
-		
-		if DEBUG:
-			self.parsedthings = []
-	
-	def parse(self):
-		self._getheader()
-		self._getfontassociationtable()
-		self._getoffsettable()
-		self._getboundingboxtable()
-		self._getglyphwidthtable()
-		self._getstylemappingtable()
-		self._getglyphencodingsubtable()
-		self._getkerningtables()
-	
-	def minimalparse(self):
-		self._getheader()
-		self._getglyphwidthtable()
-		self._getstylemappingtable()
-	
-	def __repr__(self):
-		return "<FontFamily instance of %s>" % self.name
-	
-	def getflags(self):
-		return self.fondClass
-	
-	def setflags(self, flags):
-		self.changed = 1
-		self.fondClass = flags
-	
-	def save(self, destresfile = None):
-		if self.mode <> 'w':
-			raise error, "can't save font: no write permission"
-		self._buildfontassociationtable()
-		self._buildoffsettable()
-		self._buildboundingboxtable()
-		self._buildglyphwidthtable()
-		self._buildkerningtables()
-		self._buildstylemappingtable()
-		self._buildglyphencodingsubtable()
-		rawnames = [	"_rawheader", 
-					"_rawfontassociationtable", 
-					"_rawoffsettable", 
-					"_rawglyphwidthtable", 
-					"_rawstylemappingtable", 
-					"_rawglyphencodingsubtable",
-					"_rawkerningtables"
-				]
-		for name in rawnames[1:]:	# skip header
-			data = getattr(self, name)
-			if len(data) & 1:
-				setattr(self, name, data + '\0')
-		
-		self.ffWTabOff = FONDheadersize + len(self._rawfontassociationtable) + len(self._rawoffsettable)
-		self.ffStylOff = self.ffWTabOff + len(self._rawglyphwidthtable)
-		self.ffKernOff = self.ffStylOff + len(self._rawstylemappingtable) + len(self._rawglyphencodingsubtable)
-		self.glyphTableOffset = len(self._rawstylemappingtable)
-		
-		if not self._rawglyphwidthtable:
-			self.ffWTabOff = 0
-		if not self._rawstylemappingtable:
-			self.ffStylOff = 0
-		if not self._rawglyphencodingsubtable:
-			self.glyphTableOffset = 0
-		if not self._rawkerningtables:
-			self.ffKernOff = 0
-		
-		self._buildheader()
-		
-		# glyphTableOffset has only just been calculated
-		self._updatestylemappingtable()
-		
-		newdata = ""
-		for name in rawnames:
-			newdata = newdata + getattr(self, name)
-		if destresfile is None:
-			self.FOND.data = newdata
-			self.FOND.ChangedResource()
-			self.FOND.WriteResource()
-		else:
-			ID, type, name = self.FOND.GetResInfo()
-			self.FOND.DetachResource()
-			self.FOND.data = newdata
-			saveref = Res.CurResFile()
-			Res.UseResFile(destresfile)
-			self.FOND.AddResource(type, ID, name)
-			Res.UseResFile(saveref)
-		self.changed = 0
-	
-	def _getheader(self):
-		data = self.FOND.data
-		sstruct.unpack(headerformat, data[:28], self)
-		self.ffProperty = struct.unpack(">9h", data[28:46])
-		self.ffIntl = struct.unpack(">hh", data[46:50])
-		self.ffVersion, = struct.unpack(">h", data[50:FONDheadersize])
-		
-		if DEBUG:
-			self._rawheader = data[:FONDheadersize]
-			self.parsedthings.append((0, FONDheadersize, 'header'))
-	
-	def _buildheader(self):
-		header = sstruct.pack(headerformat, self)
-		header = header + apply(struct.pack, (">9h",) + self.ffProperty)
-		header = header + apply(struct.pack, (">hh",) + self.ffIntl)
-		header = header + struct.pack(">h", self.ffVersion)
-		if DEBUG:
-			print "header is the same?", self._rawheader == header and 'yes.' or 'no.'
-			if self._rawheader <> header:
-				print len(self._rawheader), len(header)
-		self._rawheader = header
-	
-	def _getfontassociationtable(self):
-		data = self.FOND.data
-		offset = FONDheadersize
-		numberofentries, = struct.unpack(">h", data[offset:offset+2])
-		numberofentries = numberofentries + 1
-		size = numberofentries * 6
-		self.fontAssoc = []
-		for i in range(offset + 2, offset + size, 6):
-			self.fontAssoc.append(struct.unpack(">3h", data[i:i+6]))
-		
-		self._endoffontassociationtable = offset + size + 2
-		if DEBUG:
-			self._rawfontassociationtable = data[offset:self._endoffontassociationtable]
-			self.parsedthings.append((offset, self._endoffontassociationtable, 'fontassociationtable'))
-	
-	def _buildfontassociationtable(self):
-		data = struct.pack(">h", len(self.fontAssoc) - 1)
-		for size, stype, ID in self.fontAssoc:
-			data = data + struct.pack(">3h", size, stype, ID)
-		
-		if DEBUG:
-			print "font association table is the same?", self._rawfontassociationtable == data and 'yes.' or 'no.'
-			if self._rawfontassociationtable <> data:
-				print len(self._rawfontassociationtable), len(data)
-		self._rawfontassociationtable = data
-	
-	def _getoffsettable(self):
-		if self.ffWTabOff == 0:
-			self._rawoffsettable = ""
-			return
-		data = self.FOND.data
-		# Quick'n'Dirty. What's the spec anyway? Can't find it...
-		offset = self._endoffontassociationtable
-		count = self.ffWTabOff
-		self._rawoffsettable = data[offset:count]
-		if DEBUG:
-			self.parsedthings.append((offset, count, 'offsettable&bbtable'))
-	
-	def _buildoffsettable(self):
-		if not hasattr(self, "_rawoffsettable"):
-			self._rawoffsettable = ""
-	
-	def _getboundingboxtable(self):
-		self.boundingBoxes = None
-		if self._rawoffsettable[:6] <> '\0\0\0\0\0\6':  # XXX ????
-			return
-		boxes = {}
-		data = self._rawoffsettable[6:]
-		numstyles = struct.unpack(">h", data[:2])[0] + 1
-		data = data[2:]
-		for i in range(numstyles):
-			style, l, b, r, t = struct.unpack(">hhhhh", data[:10])
-			boxes[style] = (l, b, r, t)
-			data = data[10:]
-		self.boundingBoxes = boxes
-	
-	def _buildboundingboxtable(self):
-		if self.boundingBoxes and self._rawoffsettable[:6] == '\0\0\0\0\0\6':
-			boxes = self.boundingBoxes.items()
-			boxes.sort()
-			data = '\0\0\0\0\0\6' + struct.pack(">h", len(boxes) - 1)
-			for style, (l, b, r, t) in boxes:
-				data = data + struct.pack(">hhhhh", style, l, b, r, t)
-			self._rawoffsettable = data
-	
-	def _getglyphwidthtable(self):
-		self.widthTables = {}
-		if self.ffWTabOff == 0:
-			return
-		data = self.FOND.data
-		offset = self.ffWTabOff
-		numberofentries, = struct.unpack(">h", data[offset:offset+2])
-		numberofentries = numberofentries + 1
-		count = offset + 2
-		for i in range(numberofentries):
-			stylecode, = struct.unpack(">h", data[count:count+2])
-			widthtable = self.widthTables[stylecode] = []
-			count = count + 2
-			for j in range(3 + self.ffLastChar - self.ffFirstChar):
-				width, = struct.unpack(">h", data[count:count+2])
-				widthtable.append(width)
-				count = count + 2
-		
-		if DEBUG:
-			self._rawglyphwidthtable = data[offset:count]
-			self.parsedthings.append((offset, count, 'glyphwidthtable'))
-	
-	def _buildglyphwidthtable(self):
-		if not self.widthTables:
-			self._rawglyphwidthtable = ""
-			return
-		numberofentries = len(self.widthTables)
-		data = struct.pack('>h', numberofentries - 1)
-		tables = self.widthTables.items()
-		tables.sort()
-		for stylecode, table in tables:
-			data = data + struct.pack('>h', stylecode)
-			if len(table) <> (3 + self.ffLastChar - self.ffFirstChar):
-				raise error, "width table has wrong length"
-			for width in table:
-				data = data + struct.pack('>h', width)
-		if DEBUG:
-			print "glyph width table is the same?", self._rawglyphwidthtable == data and 'yes.' or 'no.'
-		self._rawglyphwidthtable = data
-	
-	def _getkerningtables(self):
-		self.kernTables = {}
-		if self.ffKernOff == 0:
-			return
-		data = self.FOND.data
-		offset = self.ffKernOff
-		numberofentries, = struct.unpack(">h", data[offset:offset+2])
-		numberofentries = numberofentries + 1
-		count = offset + 2
-		for i in range(numberofentries):
-			stylecode, = struct.unpack(">h", data[count:count+2])
-			count = count + 2
-			numberofpairs, = struct.unpack(">h", data[count:count+2])
-			count = count + 2
-			kerntable = self.kernTables[stylecode] = []
-			for j in range(numberofpairs):
-				firstchar, secondchar, kerndistance = struct.unpack(">cch", data[count:count+4])
-				kerntable.append((ord(firstchar), ord(secondchar), kerndistance))
-				count = count + 4
-		
-		if DEBUG:
-			self._rawkerningtables = data[offset:count]
-			self.parsedthings.append((offset, count, 'kerningtables'))
-	
-	def _buildkerningtables(self):
-		if self.kernTables == {}:
-			self._rawkerningtables = ""
-			self.ffKernOff = 0
-			return
-		numberofentries = len(self.kernTables)
-		data = [struct.pack('>h', numberofentries - 1)]
-		tables = self.kernTables.items()
-		tables.sort()
-		for stylecode, table in tables:
-			data.append(struct.pack('>h', stylecode))
-			data.append(struct.pack('>h', len(table)))  # numberofpairs
-			for firstchar, secondchar, kerndistance in table:
-				data.append(struct.pack(">cch", chr(firstchar), chr(secondchar), kerndistance))
-		
-		data = string.join(data, '')
-		
-		if DEBUG:
-			print "kerning table is the same?", self._rawkerningtables == data and 'yes.' or 'no.'
-			if self._rawkerningtables <> data:
-				print len(self._rawkerningtables), len(data)
-		self._rawkerningtables = data
-	
-	def _getstylemappingtable(self):
-		offset = self.ffStylOff
-		self.styleStrings = []
-		self.styleIndices = ()
-		self.glyphTableOffset = 0
-		self.fondClass = 0
-		if offset == 0:
-			return
-		data = self.FOND.data
-		self.fondClass, self.glyphTableOffset, self.styleMappingReserved, = \
-				struct.unpack(">hll", data[offset:offset+10])
-		self.styleIndices = struct.unpack('>48b', data[offset + 10:offset + 58])
-		stringcount, = struct.unpack('>h', data[offset+58:offset+60])
-		
-		count = offset + 60
-		for i in range(stringcount):
-			str_len = ord(data[count])
-			self.styleStrings.append(data[count + 1:count + 1 + str_len])
-			count = count + 1 + str_len
-		
-		self._unpackstylestrings()
-		
-		data = data[offset:count]
-		if len(data) % 2:
-			data = data + '\0'
-		if DEBUG:
-			self._rawstylemappingtable = data
-			self.parsedthings.append((offset, count, 'stylemappingtable'))
-	
-	def _buildstylemappingtable(self):
-		if not self.styleIndices:
-			self._rawstylemappingtable = ""
-			return
-		data = struct.pack(">hll", self.fondClass, self.glyphTableOffset, 
-					self.styleMappingReserved)
-		
-		self._packstylestrings()
-		data = data + apply(struct.pack, (">48b",) + self.styleIndices)
-		
-		stringcount = len(self.styleStrings)
-		data = data + struct.pack(">h", stringcount)
-		for string in self.styleStrings:
-			data = data + chr(len(string)) + string
-		
-		if len(data) % 2:
-			data = data + '\0'
-		
-		if DEBUG:
-			print "style mapping table is the same?", self._rawstylemappingtable == data and 'yes.' or 'no.'
-		self._rawstylemappingtable = data
-	
-	def _unpackstylestrings(self):
-		psNames = {}
-		self.ffFamilyName = self.styleStrings[0]
-		for i in self.widthTables.keys():
-			index = self.styleIndices[i]
-			if index == 1:
-				psNames[i] = self.styleStrings[0]
-			else:
-				style = self.styleStrings[0]
-				codes = map(ord, self.styleStrings[index - 1])
-				for code in codes:
-					style = style + self.styleStrings[code - 1]
-				psNames[i] = style
-		self.psNames = psNames
-	
-	def _packstylestrings(self):
-		nameparts = {}
-		splitnames = {}
-		for style, name in self.psNames.items():
-			split = splitname(name, self.ffFamilyName)
-			splitnames[style] = split
-			for part in split:
-				nameparts[part] = None
-		del nameparts[self.ffFamilyName]
-		nameparts = nameparts.keys()
-		nameparts.sort()
-		items = splitnames.items()
-		items.sort()
-		numindices = 0
-		for style, split in items:
-			if len(split) > 1:
-				numindices = numindices + 1
-		numindices = max(numindices, max(self.styleIndices) - 1)
-		styleStrings = [self.ffFamilyName] + numindices * [""] + nameparts
-		# XXX the next bit goes wrong for MM fonts.
-		for style, split in items:
-			if len(split) == 1:
-				continue
-			indices = ""
-			for part in split[1:]:
-				indices = indices + chr(nameparts.index(part) + numindices + 2)
-			styleStrings[self.styleIndices[style] - 1] = indices
-		self.styleStrings = styleStrings
-	
-	def _updatestylemappingtable(self):
-		# Update the glyphTableOffset field.
-		# This is necessary since we have to build this table to 
-		# know what the glyphTableOffset will be.
-		# And we don't want to build it twice, do we?
-		data = self._rawstylemappingtable
-		if not data:
-			return
-		data = data[:2] + struct.pack(">l", self.glyphTableOffset) + data[6:]
-		self._rawstylemappingtable = data
-	
-	def _getglyphencodingsubtable(self):
-		glyphEncoding = self.glyphEncoding = {}
-		if not self.glyphTableOffset:
-			return
-		offset = self.ffStylOff + self.glyphTableOffset
-		data = self.FOND.data
-		numberofentries, = struct.unpack(">h", data[offset:offset+2])
-		count = offset + 2
-		for i in range(numberofentries):
-			glyphcode = ord(data[count])
-			count = count + 1
-			strlen = ord(data[count])
-			count = count + 1
-			glyphname = data[count:count+strlen]
-			glyphEncoding[glyphcode] = glyphname
-			count = count + strlen
-		
-		if DEBUG:
-			self._rawglyphencodingsubtable = data[offset:count]
-			self.parsedthings.append((offset, count, 'glyphencodingsubtable'))
-	
-	def _buildglyphencodingsubtable(self):
-		if not self.glyphEncoding:
-			self._rawglyphencodingsubtable = ""
-			return
-		numberofentries = len(self.glyphEncoding)
-		data = struct.pack(">h", numberofentries)
-		items = self.glyphEncoding.items()
-		items.sort()
-		for glyphcode, glyphname in items:
-			data = data + chr(glyphcode) + chr(len(glyphname)) + glyphname
-		self._rawglyphencodingsubtable = data
-	
-
-uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-
-def splitname(name, famname = None):
-	# XXX this goofs up MM font names: but how should it be done??
-	if famname:
-		if name[:len(famname)] <> famname:
-			raise error, "first part of name should be same as family name"
-		name = name[len(famname):]
-		split = [famname]
-	else:
-		split = []
-	current = ""
-	for c in name:
-		if c == '-' or c in uppercase:
-			if current:
-				split.append(current)
-				current = ""
-		current = current + c
-	if current:
-		split.append(current)
-	return split
-
-def makeLWFNfilename(name):
-	split = splitname(name)
-	lwfnname = split[0][:5]
-	for part in split[1:]:
-		if part <> '-':
-			lwfnname = lwfnname + part[:3]
-	return lwfnname
-
-class BitmapFontFile:
-	
-	def __init__(self, path, mode='r'):
-		if mode == 'r':
-			permission = 1	# read only
-		elif mode == 'w':
-			permission = 3	# exclusive r/w
-		else:
-			raise error, 'mode should be either "r" or "w"'
-		self.mode = mode
-		self.resref = Res.FSOpenResFile(path, permission)
-		Res.UseResFile(self.resref)
-		self.path = path
-		self.fonds = []
-		self.getFONDs()
-	
-	def getFONDs(self):
-		FONDcount = Res.Count1Resources('FOND')
-		for i in range(FONDcount):
-			fond = FontFamily(Res.Get1IndResource('FOND', i + 1), self.mode)
-			self.fonds.append(fond)
-	
-	def parse(self):
-		self.fondsbyname = {}
-		for fond in self.fonds:
-			fond.parse()
-			if hasattr(fond, "psNames") and fond.psNames:
-				psNames = fond.psNames.values()
-				psNames.sort()
-				self.fondsbyname[psNames[0]] = fond
-	
-	def minimalparse(self):
-		for fond in self.fonds:
-			fond.minimalparse()
-	
-	def close(self):
-		if self.resref <> None:
-			try:
-				Res.CloseResFile(self.resref)
-			except Res.Error:
-				pass
-			self.resref = None
-
-
-class FondSelector:
-	
-	def __init__(self, fondlist):
-		import W
-		if not fondlist:
-			raise ValueError, "expected at least one FOND entry"
-		if len(fondlist) == 1:
-			self.choice = 0
-			return
-		fonds = []
-		for fond in fondlist:
-			fonds.append(fond.name)
-		self.w = W.ModalDialog((200, 200), "aaa")
-		self.w.donebutton = W.Button((-70, -26, 60, 16), "Done", self.close)
-		self.w.l = W.List((10, 10, -10, -36), fonds, self.listhit)
-		self.w.setdefaultbutton(self.w.donebutton)
-		self.w.l.setselection([0])
-		self.w.open()
-	
-	def close(self):
-		self.checksel()
-		sel = self.w.l.getselection()
-		self.choice = sel[0]
-		self.w.close()
-	
-	def listhit(self, isDbl):
-		if isDbl:
-			self.w.donebutton.push()
-		else:
-			self.checksel()
-	
-	def checksel(self):
-		sel = self.w.l.getselection()
-		if not sel:
-			self.w.l.setselection([0])
-		elif len(sel) <> 1:
-			self.w.l.setselection([sel[0]])
-			
diff --git a/Lib/fontTools/inspect.py b/Lib/fontTools/inspect.py
index 4726e11..00ecfbe 100644
--- a/Lib/fontTools/inspect.py
+++ b/Lib/fontTools/inspect.py
@@ -5,13 +5,14 @@
 """GUI font inspector.
 """
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools import misc, ttLib, cffLib
 import pygtk
 pygtk.require('2.0')
 import gtk
 import sys
-import array
 
-from fontTools import misc, ttLib, cffLib
 
 
 class Row(object):
@@ -74,7 +75,7 @@
 		# Make sure item is decompiled
 		try:
 			value["asdf"]
-		except (AttributeError, KeyError, ttLib.TTLibError):
+		except (AttributeError, KeyError, TypeError, ttLib.TTLibError):
 			pass
 		if isinstance(value, ttLib.getTableModule('glyf').Glyph):
 			# Glyph type needs explicit expanding to be useful
@@ -118,7 +119,7 @@
 			return len(self._children)
 		if hasattr(self, '_items'):
 			return len(self._items)
-		assert 0
+		assert False
 
 	def _ensure_children(self):
 		if hasattr(self, '_children'):
@@ -217,7 +218,7 @@
 	def on_iter_parent(self, rowref):
 		return rowref.get_parent()
 
-class Inspect:
+class Inspect(object):
 
 	def _delete_event(self, widget, event, data=None):
 		gtk.main_quit()
@@ -233,7 +234,7 @@
 		self.scrolled_window = gtk.ScrolledWindow()
 		self.window.add(self.scrolled_window)
 
-		self.font = ttLib.TTFont(fontfile)
+		self.font = ttLib.TTFont(fontfile, lazy=True)
 		self.treemodel = FontTreeModel(self.font)
 		self.treeview = gtk.TreeView(self.treemodel)
 		#self.treeview.set_reorderable(True)
@@ -254,7 +255,7 @@
 
 def main(args):
 	if len(args) < 1:
-		print >>sys.stderr, "usage: pyftinspect font..."
+		print("usage: pyftinspect font...", file=sys.stderr)
 		sys.exit(1)
 	for arg in args:
 		Inspect(arg)
diff --git a/Lib/fontTools/misc/arrayTools.py b/Lib/fontTools/misc/arrayTools.py
index 3f39e7e..c05e195 100644
--- a/Lib/fontTools/misc/arrayTools.py
+++ b/Lib/fontTools/misc/arrayTools.py
@@ -4,6 +4,8 @@
 #
 
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 import math
 
 def calcBounds(array):
@@ -16,13 +18,27 @@
     ys = [y for x, y in array]
     return min(xs), min(ys), max(xs), max(ys)
 
-def updateBounds(bounds, (x, y), min=min, max=max):
+def calcIntBounds(array):
+    """Return the integer bounding rectangle of a 2D points array as a
+    tuple: (xMin, yMin, xMax, yMax)
+    """
+    xMin, yMin, xMax, yMax = calcBounds(array)
+    xMin = int(math.floor(xMin))
+    xMax = int(math.ceil(xMax))
+    yMin = int(math.floor(yMin))
+    yMax = int(math.ceil(yMax))
+    return xMin, yMin, xMax, yMax
+
+
+def updateBounds(bounds, p, min=min, max=max):
     """Return the bounding recangle of rectangle bounds and point (x, y)."""
+    (x, y) = p
     xMin, yMin, xMax, yMax = bounds
     return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
 
-def pointInRect((x, y), rect):
+def pointInRect(p, rect):
     """Return True when point (x, y) is inside rect."""
+    (x, y) = p
     xMin, yMin, xMax, yMax = rect
     return (xMin <= x <= xMax) and (yMin <= y <= yMax)
 
@@ -45,53 +61,62 @@
     return [int(math.floor(i+0.5)) for i in array]
     
 
-def normRect((xMin, yMin, xMax, yMax)):
+def normRect(rect):
     """Normalize the rectangle so that the following holds:
         xMin <= xMax and yMin <= yMax
     """
+    (xMin, yMin, xMax, yMax) = rect
     return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
 
-def scaleRect((xMin, yMin, xMax, yMax), x, y):
+def scaleRect(rect, x, y):
     """Scale the rectangle by x, y."""
+    (xMin, yMin, xMax, yMax) = rect
     return xMin * x, yMin * y, xMax * x, yMax * y
 
-def offsetRect((xMin, yMin, xMax, yMax), dx, dy):
+def offsetRect(rect, dx, dy):
     """Offset the rectangle by dx, dy."""
+    (xMin, yMin, xMax, yMax) = rect
     return xMin+dx, yMin+dy, xMax+dx, yMax+dy
 
-def insetRect((xMin, yMin, xMax, yMax), dx, dy):
+def insetRect(rect, dx, dy):
     """Inset the rectangle by dx, dy on all sides."""
+    (xMin, yMin, xMax, yMax) = rect
     return xMin+dx, yMin+dy, xMax-dx, yMax-dy
 
-def sectRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
+def sectRect(rect1, rect2):
     """Return a boolean and a rectangle. If the input rectangles intersect, return
     True and the intersecting rectangle. Return False and (0, 0, 0, 0) if the input
     rectangles don't intersect.
     """
+    (xMin1, yMin1, xMax1, yMax1) = rect1
+    (xMin2, yMin2, xMax2, yMax2) = rect2
     xMin, yMin, xMax, yMax = (max(xMin1, xMin2), max(yMin1, yMin2),
                               min(xMax1, xMax2), min(yMax1, yMax2))
     if xMin >= xMax or yMin >= yMax:
-        return 0, (0, 0, 0, 0)
-    return 1, (xMin, yMin, xMax, yMax)
+        return False, (0, 0, 0, 0)
+    return True, (xMin, yMin, xMax, yMax)
 
-def unionRect((xMin1, yMin1, xMax1, yMax1), (xMin2, yMin2, xMax2, yMax2)):
+def unionRect(rect1, rect2):
     """Return the smallest rectangle in which both input rectangles are fully
     enclosed. In other words, return the total bounding rectangle of both input
     rectangles.
     """
+    (xMin1, yMin1, xMax1, yMax1) = rect1
+    (xMin2, yMin2, xMax2, yMax2) = rect2
     xMin, yMin, xMax, yMax = (min(xMin1, xMin2), min(yMin1, yMin2),
                               max(xMax1, xMax2), max(yMax1, yMax2))
     return (xMin, yMin, xMax, yMax)
 
-def rectCenter((xMin, yMin, xMax, yMax)):
+def rectCenter(rect0):
     """Return the center of the rectangle as an (x, y) coordinate."""
+    (xMin, yMin, xMax, yMax) = rect0
     return (xMin+xMax)/2, (yMin+yMax)/2
 
-def intRect((xMin, yMin, xMax, yMax)):
+def intRect(rect1):
     """Return the rectangle, rounded off to integer values, but guaranteeing that
     the resulting rectangle is NOT smaller than the original.
     """
-    import math
+    (xMin, yMin, xMax, yMax) = rect1
     xMin = int(math.floor(xMin))
     yMin = int(math.floor(yMin))
     xMax = int(math.ceil(xMax))
@@ -147,9 +172,9 @@
     >>> unionRect((0, 10, 20, 30), (0, 40, 20, 50))
     (0, 10, 20, 50)
     >>> rectCenter((0, 0, 100, 200))
-    (50, 100)
+    (50.0, 100.0)
     >>> rectCenter((0, 0, 100, 199.0))
-    (50, 99.5)
+    (50.0, 99.5)
     >>> intRect((0.9, 2.9, 3.1, 4.1))
     (0, 2, 4, 5)
     """
diff --git a/Lib/fontTools/misc/bezierTools.py b/Lib/fontTools/misc/bezierTools.py
index 4c897d5..9e38422 100644
--- a/Lib/fontTools/misc/bezierTools.py
+++ b/Lib/fontTools/misc/bezierTools.py
@@ -1,6 +1,8 @@
 """fontTools.misc.bezierTools.py -- tools for working with bezier path segments.
 """
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 
 __all__ = [
     "calcQuadraticBounds",
@@ -86,19 +88,17 @@
     """
     pt1x, pt1y = pt1
     pt2x, pt2y = pt2
-    
+
     ax = (pt2x - pt1x)
     ay = (pt2y - pt1y)
-    
+
     bx = pt1x
     by = pt1y
-    
-    ax1 = (ax, ay)[isHorizontal]
-    
+
     if ax == 0:
         return [(pt1, pt2)]
-        
-    t = float(where - (bx, by)[isHorizontal]) / ax
+
+    t = (where - (bx, by)[isHorizontal]) / ax
     if 0 <= t < 1:
         midPt = ax * t + bx, ay * t + by
         return [(pt1, midPt), (midPt, pt2)]
@@ -132,8 +132,7 @@
     a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
     solutions = solveQuadratic(a[isHorizontal], b[isHorizontal],
         c[isHorizontal] - where)
-    solutions = [t for t in solutions if 0 <= t < 1]
-    solutions.sort()
+    solutions = sorted([t for t in solutions if 0 <= t < 1])
     if not solutions:
         return [(pt1, pt2, pt3)]
     return _splitQuadraticAtT(a, b, c, *solutions)
@@ -157,8 +156,7 @@
     a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
     solutions = solveCubic(a[isHorizontal], b[isHorizontal], c[isHorizontal],
         d[isHorizontal] - where)
-    solutions = [t for t in solutions if 0 <= t < 1]
-    solutions.sort()
+    solutions = sorted([t for t in solutions if 0 <= t < 1])
     if not solutions:
         return [(pt1, pt2, pt3, pt4)]
     return _splitCubicAtT(a, b, c, d, *solutions)
@@ -281,8 +279,7 @@
     return roots
 
 
-def solveCubic(a, b, c, d,
-        abs=abs, pow=pow, sqrt=sqrt, cos=cos, acos=acos, pi=pi):
+def solveCubic(a, b, c, d):
     """Solve a cubic equation where a, b, c and d are real.
         a*x*x*x + b*x*x + c*x + d = 0
     This function returns a list of roots. Note that the returned list
@@ -402,7 +399,7 @@
     segments on a single line as a tuple.
     """
     for segment in segments:
-        print _segmentrepr(segment)
+        print(_segmentrepr(segment))
 
 if __name__ == "__main__":
     import doctest
diff --git a/Lib/fontTools/misc/eexec.py b/Lib/fontTools/misc/eexec.py
index ade0789..396f56e 100644
--- a/Lib/fontTools/misc/eexec.py
+++ b/Lib/fontTools/misc/eexec.py
@@ -2,36 +2,28 @@
 charstring encryption algorithm as used by PostScript Type 1 fonts.
 """
 
-# Warning: Although a Python implementation is provided here, 
-# all four public functions get overridden by the *much* faster 
-# C extension module eexecOp, if available.
-
-import string
-
-error = "eexec.error"
-
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 
 def _decryptChar(cipher, R):
-	cipher = ord(cipher)
+	cipher = byteord(cipher)
 	plain = ( (cipher ^ (R>>8)) ) & 0xFF
-	R = ( (cipher + R) * 52845L + 22719L ) & 0xFFFF
-	return chr(plain), R
+	R = ( (cipher + R) * 52845 + 22719 ) & 0xFFFF
+	return bytechr(plain), R
 
 def _encryptChar(plain, R):
-	plain = ord(plain)
+	plain = byteord(plain)
 	cipher = ( (plain ^ (R>>8)) ) & 0xFF
-	R = ( (cipher + R) * 52845L + 22719L ) & 0xFFFF
-	return chr(cipher), R
+	R = ( (cipher + R) * 52845 + 22719 ) & 0xFFFF
+	return bytechr(cipher), R
 
 
 def decrypt(cipherstring, R):
-	# I could probably speed this up by inlining _decryptChar,
-	# but... we've got eexecOp, so who cares ;-)
 	plainList = []
 	for cipher in cipherstring:
 		plain, R = _decryptChar(cipher, R)
 		plainList.append(plain)
-	plainstring = string.join(plainList, '')
+	plainstring = strjoin(plainList)
 	return plainstring, int(R)
 
 def encrypt(plainstring, R):
@@ -39,7 +31,7 @@
 	for plain in plainstring:
 		cipher, R = _encryptChar(plain, R)
 		cipherList.append(cipher)
-	cipherstring = string.join(cipherList, '')
+	cipherstring = strjoin(cipherList)
 	return cipherstring, int(R)
 
 
@@ -49,25 +41,15 @@
 
 def deHexString(h):
 	import binascii
-	h = "".join(h.split())
+	h = strjoin(h.split())
 	return binascii.unhexlify(h)
 
 
 def _test():
-	import fontTools.misc.eexecOp as eexecOp
 	testStr = "\0\0asdadads asds\265"
-	print decrypt, decrypt(testStr, 12321)
-	print eexecOp.decrypt, eexecOp.decrypt(testStr, 12321)
-	print encrypt, encrypt(testStr, 12321)
-	print eexecOp.encrypt, eexecOp.encrypt(testStr, 12321)
+	print(decrypt, decrypt(testStr, 12321))
+	print(encrypt, encrypt(testStr, 12321))
 
 
 if __name__ == "__main__":
 	_test()
-
-
-try:
-	from fontTools.misc.eexecOp import *
-except ImportError:
-	pass # Use the slow Python versions
-
diff --git a/Lib/fontTools/misc/fixedTools.py b/Lib/fontTools/misc/fixedTools.py
new file mode 100644
index 0000000..037a0e2
--- /dev/null
+++ b/Lib/fontTools/misc/fixedTools.py
@@ -0,0 +1,65 @@
+"""fontTools.misc.fixedTools.py -- tools for working with fixed numbers.
+"""
+
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+
+__all__ = [
+    "fixedToFloat",
+    "floatToFixed",
+]
+
+def fixedToFloat(value, precisionBits):
+	"""Converts a fixed-point number to a float, choosing the float
+	that has the shortest decimal reprentation.  Eg. to convert a
+	fixed number in a 2.14 format, use precisionBits=14.  This is
+	pretty slow compared to a simple division.  Use sporadically.
+	
+	>>> fixedToFloat(13107, 14)
+	0.8
+	>>> fixedToFloat(0, 14)
+	0.0
+	>>> fixedToFloat(0x4000, 14)
+	1.0
+	"""
+
+	if not value: return 0.0
+
+	scale = 1 << precisionBits
+	value /= scale
+	eps = .5 / scale
+	digits = (precisionBits + 2) // 3
+	fmt = "%%.%df" % digits
+	lo = fmt % (value - eps)
+	hi = fmt % (value + eps)
+	out = []
+	length = min(len(lo), len(hi))
+	for i in range(length):
+		if lo[i] != hi[i]:
+			break;
+		out.append(lo[i])
+	outlen = len(out)
+	if outlen < length:
+		out.append(max(lo[outlen], hi[outlen]))
+	return float(strjoin(out))
+
+def floatToFixed(value, precisionBits):
+	"""Converts a float to a fixed-point number given the number of
+	precisionBits.  Ie. int(round(value * (1<<precisionBits))).
+
+	>>> floatToFixed(0.8, 14)
+	13107
+	>>> floatToFixed(1.0, 14)
+	16384
+	>>> floatToFixed(1, 14)
+	16384
+	>>> floatToFixed(0, 14)
+	0
+	"""
+
+	return int(round(value * (1<<precisionBits)))
+
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
diff --git a/Lib/fontTools/misc/homeResFile.py b/Lib/fontTools/misc/homeResFile.py
index b9463e4..dc61c2f 100644
--- a/Lib/fontTools/misc/homeResFile.py
+++ b/Lib/fontTools/misc/homeResFile.py
@@ -1,5 +1,7 @@
 """Mac-only module to find the home file of a resource."""
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 import array
 import calldll
@@ -49,7 +51,7 @@
 	ioFCBParID:   l
 """
 
-class ParamBlock:
+class ParamBlock(object):
 	
 	"""Wrapper for the very low level FCBPB record."""
 	
@@ -70,14 +72,14 @@
 		ptr = buf.buffer_info()[0]
 		err = _getInfo(ptr)
 		if err:
-			raise Res.Error, ("can't get file info", err)
+			raise Res.Error("can't get file info", err)
 		sstruct.unpack(_FCBPBFormat, buf.tostring(), self)
 		self.__haveInfo = 1
 	
 	def getFileName(self):
 		self.getInfo()
 		data = self.__fileName.tostring()
-		return data[1:ord(data[0])+1]
+		return data[1:byteord(data[0])+1]
 	
 	def getFSSpec(self):
 		self.getInfo()
@@ -91,4 +93,4 @@
 
 if __name__ == "__main__":
 	fond = Res.GetNamedResource("FOND", "Helvetica")
-	print HomeResFile(fond)
+	print(HomeResFile(fond))
diff --git a/Lib/fontTools/misc/macCreatorType.py b/Lib/fontTools/misc/macCreatorType.py
index 57d158c..bcbc4cb 100644
--- a/Lib/fontTools/misc/macCreatorType.py
+++ b/Lib/fontTools/misc/macCreatorType.py
@@ -1,13 +1,16 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 import sys
 try:
 	import MacOS
 except ImportError:
 	MacOS = None
+from .py23 import *
 
 def _reverseString(s):
 	s = list(s)
 	s.reverse()
-	return "".join(s)
+	return strjoin(s)
 
 
 def getMacCreatorAndType(path):
diff --git a/Lib/fontTools/misc/psCharStrings.py b/Lib/fontTools/misc/psCharStrings.py
index ccb7e1c..571bc89 100644
--- a/Lib/fontTools/misc/psCharStrings.py
+++ b/Lib/fontTools/misc/psCharStrings.py
@@ -2,9 +2,9 @@
 CFF dictionary data and Type1/Type2 CharStrings.
 """
 
-import types
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 import struct
-import string
 
 
 DEBUG = 0
@@ -35,38 +35,35 @@
 	realNibblesDict[realNibbles[_i]] = _i
 
 
-class ByteCodeBase:
+class ByteCodeBase(object):
 	
 	def read_byte(self, b0, data, index):
 		return b0 - 139, index
 	
 	def read_smallInt1(self, b0, data, index):
-		b1 = ord(data[index])
+		b1 = byteord(data[index])
 		return (b0-247)*256 + b1 + 108, index+1
 	
 	def read_smallInt2(self, b0, data, index):
-		b1 = ord(data[index])
+		b1 = byteord(data[index])
 		return -(b0-251)*256 - b1 - 108, index+1
 	
 	def read_shortInt(self, b0, data, index):
-		bin = data[index] + data[index+1]
-		value, = struct.unpack(">h", bin)
+		value, = struct.unpack(">h", data[index:index+2])
 		return value, index+2
 	
 	def read_longInt(self, b0, data, index):
-		bin = data[index] + data[index+1] + data[index+2] + data[index+3]
-		value, = struct.unpack(">l", bin)
+		value, = struct.unpack(">l", data[index:index+4])
 		return value, index+4
 	
 	def read_fixed1616(self, b0, data, index):
-		bin = data[index] + data[index+1] + data[index+2] + data[index+3]
-		value, = struct.unpack(">l", bin)
-		return value / 65536.0, index+4
+		value, = struct.unpack(">l", data[index:index+4])
+		return value / 65536, index+4
 	
 	def read_realNumber(self, b0, data, index):
 		number = ''
-		while 1:
-			b = ord(data[index])
+		while True:
+			b = byteord(data[index])
 			index = index + 1
 			nibble0 = (b & 0xf0) >> 4
 			nibble1 = b & 0x0f
@@ -87,7 +84,7 @@
 			oper[item[0]] = item[1]
 		else:
 			oper[item[0]] = item[1:]
-		if type(item[0]) == types.TupleType:
+		if isinstance(item[0], tuple):
 			opc[item[1]] = item[0]
 		else:
 			opc[item[1]] = (item[0],)
@@ -154,27 +151,27 @@
 
 def getIntEncoder(format):
 	if format == "cff":
-		fourByteOp = chr(29)
+		fourByteOp = bytechr(29)
 	elif format == "t1":
-		fourByteOp = chr(255)
+		fourByteOp = bytechr(255)
 	else:
 		assert format == "t2"
 		fourByteOp = None
 	
-	def encodeInt(value, fourByteOp=fourByteOp, chr=chr,
+	def encodeInt(value, fourByteOp=fourByteOp, bytechr=bytechr,
 			pack=struct.pack, unpack=struct.unpack):
 		if -107 <= value <= 107:
-			code = chr(value + 139)
+			code = bytechr(value + 139)
 		elif 108 <= value <= 1131:
 			value = value - 108
-			code = chr((value >> 8) + 247) + chr(value & 0xFF)
+			code = bytechr((value >> 8) + 247) + bytechr(value & 0xFF)
 		elif -1131 <= value <= -108:
 			value = -value - 108
-			code = chr((value >> 8) + 251) + chr(value & 0xFF)
+			code = bytechr((value >> 8) + 251) + bytechr(value & 0xFF)
 		elif fourByteOp is None:
 			# T2 only supports 2 byte ints
 			if -32768 <= value <= 32767:
-				code = chr(28) + pack(">h", value)
+				code = bytechr(28) + pack(">h", value)
 			else:
 				# Backwards compatible hack: due to a previous bug in FontTools,
 				# 16.16 fixed numbers were written out as 4-byte ints. When
@@ -188,7 +185,7 @@
 				sys.stderr.write("Warning: 4-byte T2 number got passed to the "
 					"IntType handler. This should happen only when reading in "
 					"old XML files.\n")
-				code = chr(255) + pack(">l", value)
+				code = bytechr(255) + pack(">l", value)
 		else:
 			code = fourByteOp + pack(">l", value)
 		return code
@@ -202,7 +199,7 @@
 
 def encodeFixed(f, pack=struct.pack):
 	# For T2 only
-	return "\xff" + pack(">l", int(round(f * 65536)))
+	return b"\xff" + pack(">l", int(round(f * 65536)))
 
 def encodeFloat(f):
 	# For CFF only, used in cffLib
@@ -222,9 +219,9 @@
 	nibbles.append(0xf)
 	if len(nibbles) % 2:
 		nibbles.append(0xf)
-	d = chr(30)
+	d = bytechr(30)
 	for i in range(0, len(nibbles), 2):
-		d = d + chr(nibbles[i] << 4 | nibbles[i+1])
+		d = d + bytechr(nibbles[i] << 4 | nibbles[i+1])
 	return d
 
 
@@ -242,7 +239,7 @@
 		self.bytecode = bytecode
 		self.program = program
 		self.private = private
-		self.globalSubrs = globalSubrs
+		self.globalSubrs = globalSubrs if globalSubrs is not None else []
 	
 	def __repr__(self):
 		if self.bytecode is None:
@@ -287,24 +284,24 @@
 			token = program[i]
 			i = i + 1
 			tp = type(token)
-			if tp == types.StringType:
+			if issubclass(tp, basestring):
 				try:
-					bytecode.extend(map(chr, opcodes[token]))
+					bytecode.extend(bytechr(b) for b in opcodes[token])
 				except KeyError:
-					raise CharStringCompileError, "illegal operator: %s" % token
+					raise CharStringCompileError("illegal operator: %s" % token)
 				if token in ('hintmask', 'cntrmask'):
 					bytecode.append(program[i])  # hint mask
 					i = i + 1
-			elif tp == types.IntType:
+			elif tp == int:
 				bytecode.append(encodeInt(token))
-			elif tp == types.FloatType:
+			elif tp == float:
 				bytecode.append(encodeFixed(token))
 			else:
 				assert 0, "unsupported type: %s" % tp
 		try:
-			bytecode = "".join(bytecode)
+			bytecode = bytesjoin(bytecode)
 		except TypeError:
-			print bytecode
+			print(bytecode)
 			raise
 		self.setBytecode(bytecode)
 	
@@ -320,11 +317,11 @@
 		self.program = None
 	
 	def getToken(self, index, 
-			len=len, ord=ord, getattr=getattr, type=type, StringType=types.StringType):
+			len=len, byteord=byteord, getattr=getattr, type=type, StringType=str):
 		if self.bytecode is not None:
 			if index >= len(self.bytecode):
 				return None, 0, 0
-			b0 = ord(self.bytecode[index])
+			b0 = byteord(self.bytecode[index])
 			index = index + 1
 			code = self.operandEncoding[b0]
 			handler = getattr(self, code)
@@ -334,7 +331,7 @@
 				return None, 0, 0
 			token = self.program[index]
 			index = index + 1
-		isOperator = type(token) == StringType
+		isOperator = isinstance(token, StringType)
 		return token, isOperator, index
 	
 	def getBytes(self, index, nBytes):
@@ -350,7 +347,7 @@
 	
 	def do_operator(self, b0, data, index):
 		if b0 == 12:
-			op = (b0, ord(data[index]))
+			op = (b0, byteord(data[index]))
 			index = index+1
 		else:
 			op = b0
@@ -364,33 +361,33 @@
 		else:
 			index = 0
 			args = []
-			while 1:
+			while True:
 				token, isOperator, index = self.getToken(index)
 				if token is None:
 					break
 				if isOperator:
-					args = map(str, args)
+					args = [str(arg) for arg in args]
 					if token in ('hintmask', 'cntrmask'):
 						hintMask, isOperator, index = self.getToken(index)
 						bits = []
 						for byte in hintMask:
-							bits.append(num2binary(ord(byte), 8))
-						hintMask = string.join(bits, "")
-						line = string.join(args + [token, hintMask], " ")
+							bits.append(num2binary(byteord(byte), 8))
+						hintMask = strjoin(bits)
+						line = ' '.join(args + [token, hintMask])
 					else:
-						line = string.join(args + [token], " ")
+						line = ' '.join(args + [token])
 					xmlWriter.write(line)
 					xmlWriter.newline()
 					args = []
 				else:
 					args.append(token)
 	
-	def fromXML(self, (name, attrs, content)):
+	def fromXML(self, name, attrs, content):
 		from fontTools.misc.textTools import binary2num, readHex
 		if attrs.get("raw"):
 			self.setBytecode(readHex(content))
 			return
-		content = "".join(content)
+		content = strjoin(content)
 		content = content.split()
 		program = []
 		end = len(content)
@@ -407,9 +404,9 @@
 					program.append(token)
 					if token in ('hintmask', 'cntrmask'):
 						mask = content[i]
-						maskBytes = ""
+						maskBytes = b""
 						for j in range(0, len(mask), 8):
-							maskBytes = maskBytes + chr(binary2num(mask[j:j+8]))
+							maskBytes = maskBytes + bytechr(binary2num(mask[j:j+8]))
 						program.append(maskBytes)
 						i = i + 1
 				else:
@@ -468,11 +465,11 @@
 			raise TypeError("Type 1 charstrings don't support floating point operands")
 
 	def decompile(self):
-		if self.program is not None:
+		if self.bytecode is None:
 			return
 		program = []
 		index = 0
-		while 1:
+		while True:
 			token, isOperator, index = self.getToken(index)
 			if token is None:
 				break
@@ -485,7 +482,7 @@
 		self.width = extractor.width
 
 
-class SimpleT2Decompiler:
+class SimpleT2Decompiler(object):
 	
 	def __init__(self, localSubrs, globalSubrs):
 		self.localSubrs = localSubrs
@@ -510,7 +507,7 @@
 			pushToProgram = lambda x: None
 		pushToStack = self.operandStack.append
 		index = 0
-		while 1:
+		while True:
 			token, isOperator, index = charString.getToken(index)
 			if token is None:
 				break  # we're done!
@@ -579,7 +576,7 @@
 	def op_hintmask(self, index):
 		if not self.hintMaskBytes:
 			self.countHints()
-			self.hintMaskBytes = (self.hintCount + 7) / 8
+			self.hintMaskBytes = (self.hintCount + 7) // 8
 		hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes)
 		return hintMaskBytes, index
 	
@@ -587,7 +584,7 @@
 	
 	def countHints(self):
 		args = self.popall()
-		self.hintCount = self.hintCount + len(args) / 2
+		self.hintCount = self.hintCount + len(args) // 2
 
 	# misc
 	def op_and(self, index):
@@ -695,7 +692,7 @@
 	
 	def countHints(self):
 		args = self.popallWidth()
-		self.hintCount = self.hintCount + len(args) / 2
+		self.hintCount = self.hintCount + len(args) // 2
 	
 	#
 	# hint operators
@@ -862,7 +859,7 @@
 	# MultipleMaster. Well...
 	#
 	def op_blend(self, index):
-		args = self.popall()
+		self.popall()
 	
 	# misc
 	def op_and(self, index):
@@ -882,8 +879,8 @@
 	def op_div(self, index):
 		num2 = self.pop()
 		num1 = self.pop()
-		d1 = num1/num2
-		d2 = float(num1)/num2
+		d1 = num1//num2
+		d2 = num1/num2
 		if d1 == d2:
 			self.push(d1)
 		else:
@@ -918,7 +915,7 @@
 		raise NotImplementedError
 	
 	#
-	# miscelaneous helpers
+	# miscellaneous helpers
 	#
 	def alternatingLineto(self, isHorizontal):
 		args = self.popall()
@@ -1114,7 +1111,7 @@
 		lenData = len(data)
 		push = self.stack.append
 		while index < lenData:
-			b0 = ord(data[index])
+			b0 = byteord(data[index])
 			index = index + 1
 			code = self.operandEncoding[b0]
 			handler = getattr(self, code)
@@ -1128,13 +1125,13 @@
 		return value
 	
 	def popall(self):
-		all = self.stack[:]
+		args = self.stack[:]
 		del self.stack[:]
-		return all
+		return args
 	
 	def do_operator(self, b0, data, index):
 		if b0 == 12:
-			op = (b0, ord(data[index]))
+			op = (b0, byteord(data[index]))
 			index = index+1
 		else:
 			op = b0
@@ -1143,7 +1140,7 @@
 		return None, index
 	
 	def handle_operator(self, operator, argType):
-		if type(argType) == type(()):
+		if isinstance(argType, type(())):
 			value = ()
 			for i in range(len(argType)-1, -1, -1):
 				arg = argType[i]
diff --git a/Lib/fontTools/misc/psLib.py b/Lib/fontTools/misc/psLib.py
index 3c7bd37..5b99cb3 100644
--- a/Lib/fontTools/misc/psLib.py
+++ b/Lib/fontTools/misc/psLib.py
@@ -1,10 +1,10 @@
-import re
-import types
-from string import whitespace
-import StringIO
-
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import eexec
-from psOperators import *
+from .psOperators import *
+import re
+import collections
+from string import whitespace
 
 
 ps_special = '()<>[]{}%'	# / is one too, but we take care of that one differently
@@ -38,9 +38,9 @@
 class PSError(Exception): pass
 
 
-class PSTokenizer(StringIO.StringIO):
+class PSTokenizer(StringIO):
 	
-	def getnexttoken(self, 
+	def getnexttoken(self,
 			# localize some stuff, for performance
 			len=len,
 			ps_special=ps_special,
@@ -69,18 +69,18 @@
 				tokentype = 'do_string'
 				m = stringmatch(buf, pos)
 				if m is None:
-					raise PSTokenError, 'bad string at character %d' % pos
+					raise PSTokenError('bad string at character %d' % pos)
 				_, nextpos = m.span()
 				token = buf[pos:nextpos]
 			elif char == '<':
 				tokentype = 'do_hexstring'
 				m = hexstringmatch(buf, pos)
 				if m is None:
-					raise PSTokenError, 'bad hexstring at character %d' % pos
+					raise PSTokenError('bad hexstring at character %d' % pos)
 				_, nextpos = m.span()
 				token = buf[pos:nextpos]
 			else:
-				raise PSTokenError, 'bad token at character %d' % pos
+				raise PSTokenError('bad token at character %d' % pos)
 		else:
 			if char == '/':
 				tokentype = 'do_literal'
@@ -89,7 +89,7 @@
 				tokentype = ''
 				m = endmatch(buf, pos)
 			if m is None:
-				raise PSTokenError, 'bad token at character %d' % pos
+				raise PSTokenError('bad token at character %d' % pos)
 			_, nextpos = m.span()
 			token = buf[pos:nextpos]
 		self.pos = pos + len(token)
@@ -143,7 +143,7 @@
 	def suckoperators(self, systemdict, klass):
 		for name in dir(klass):
 			attr = getattr(self, name)
-			if callable(attr) and name[:3] == 'ps_':
+			if isinstance(attr, collections.Callable) and name[:3] == 'ps_':
 				name = name[3:]
 				systemdict[name] = ps_operator(name, attr)
 		for baseclass in klass.__bases__:
@@ -172,15 +172,15 @@
 		finally:
 			if self.tokenizer is not None:
 				if 0:
-					print 'ps error:\n- - - - - - -'
-					print self.tokenizer.buf[self.tokenizer.pos-50:self.tokenizer.pos]
-					print '>>>'
-					print self.tokenizer.buf[self.tokenizer.pos:self.tokenizer.pos+50]
-					print '- - - - - - -'
+					print('ps error:\n- - - - - - -')
+					print(self.tokenizer.buf[self.tokenizer.pos-50:self.tokenizer.pos])
+					print('>>>')
+					print(self.tokenizer.buf[self.tokenizer.pos:self.tokenizer.pos+50])
+					print('- - - - - - -')
 	
 	def handle_object(self, object):
 		if not (self.proclevel or object.literal or object.type == 'proceduretype'):
-			if object.type <> 'operatortype':
+			if object.type != 'operatortype':
 				object = self.resolve_name(object.value)
 			if object.literal:
 				self.push(object)
@@ -200,9 +200,9 @@
 	def resolve_name(self, name):
 		dictstack = self.dictstack
 		for i in range(len(dictstack)-1, -1, -1):
-			if dictstack[i].has_key(name):
+			if name in dictstack[i]:
 				return dictstack[i][name]
-		raise PSError, 'name error: ' + str(name)
+		raise PSError('name error: ' + str(name))
 	
 	def do_token(self, token,
 				int=int, 
@@ -270,7 +270,7 @@
 		elif token == ']':
 			return ps_name(']')
 		else:
-			raise PSTokenError, 'huh?'
+			raise PSTokenError('huh?')
 	
 	def push(self, object):
 		self.stack.append(object)
@@ -278,11 +278,11 @@
 	def pop(self, *types):
 		stack = self.stack
 		if not stack:
-			raise PSError, 'stack underflow'
+			raise PSError('stack underflow')
 		object = stack[-1]
 		if types:
 			if object.type not in types:
-				raise PSError, 'typecheck, expected %s, found %s' % (`types`, object.type)
+				raise PSError('typecheck, expected %s, found %s' % (repr(types), object.type))
 		del stack[-1]
 		return object
 	
@@ -304,11 +304,11 @@
 
 def unpack_item(item):
 	tp = type(item.value)
-	if tp == types.DictionaryType:
+	if tp == dict:
 		newitem = {}
 		for key, value in item.value.items():
 			newitem[key] = unpack_item(value)
-	elif tp == types.ListType:
+	elif tp == list:
 		newitem = [None] * len(item.value)
 		for i in range(len(item.value)):
 			newitem[i] = unpack_item(item.value[i])
@@ -320,20 +320,20 @@
 
 def suckfont(data):
 	import re
-	m = re.search(r"/FontName\s+/([^ \t\n\r]+)\s+def", data)
+	m = re.search(br"/FontName\s+/([^ \t\n\r]+)\s+def", data)
 	if m:
 		fontName = m.group(1)
 	else:
 		fontName = None
 	interpreter = PSInterpreter()
-	interpreter.interpret("/Helvetica 4 dict dup /Encoding StandardEncoding put definefont pop")
+	interpreter.interpret(b"/Helvetica 4 dict dup /Encoding StandardEncoding put definefont pop")
 	interpreter.interpret(data)
 	fontdir = interpreter.dictstack[0]['FontDirectory'].value
-	if fontdir.has_key(fontName):
+	if fontName in fontdir:
 		rawfont = fontdir[fontName]
 	else:
 		# fall back, in case fontName wasn't found
-		fontNames = fontdir.keys()
+		fontNames = list(fontdir.keys())
 		if len(fontNames) > 1:
 			fontNames.remove("Helvetica")
 		fontNames.sort()
diff --git a/Lib/fontTools/misc/psOperators.py b/Lib/fontTools/misc/psOperators.py
index 9fb3723..c26830d 100644
--- a/Lib/fontTools/misc/psOperators.py
+++ b/Lib/fontTools/misc/psOperators.py
@@ -1,3 +1,6 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+
 _accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
 
 
@@ -116,13 +119,12 @@
 	psstring = "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n"
 	for i in range(256):
 		name = encoding[i].value
-		if name <> '.notdef':
+		if name != '.notdef':
 			psstring = psstring + "dup %d /%s put\n" % (i, name)
 	return psstring + access + "def\n"
 
 def _type1_CharString_repr(charstrings):
-	items = charstrings.items()
-	items.sort()
+	items = sorted(charstrings.items())
 	return 'xxx'
 
 class ps_font(ps_object):
@@ -135,8 +137,7 @@
 				pass
 			else:
 				psstring = psstring + _type1_item_repr(key, value)
-		items = self.value.items()
-		items.sort()
+		items = sorted(self.value.items())
 		for key, value in items:
 			if key not in _type1_pre_eexec_order + _type1_post_eexec_order:
 				psstring = psstring + _type1_item_repr(key, value)
@@ -159,9 +160,7 @@
 class ps_dict(ps_object):
 	def __str__(self):
 		psstring = "%d dict dup begin\n" % len(self.value)
-		items = self.value.items()
-		items.sort()
-		dictrepr = "%d dict dup begin\n" % len(items)
+		items = sorted(self.value.items())
 		for key, value in items:
 			access = _accessstrings[value.access]
 			if access:
@@ -194,23 +193,23 @@
 
 class ps_string(ps_object):
 	def __str__(self):
-		return "(%s)" % `self.value`[1:-1]
+		return "(%s)" % repr(self.value)[1:-1]
 
 class ps_integer(ps_object):
 	def __str__(self):
-		return `self.value`
+		return repr(self.value)
 
 class ps_real(ps_object):
 	def __str__(self):
-		return `self.value`
+		return repr(self.value)
 
 
 class PSOperators:
 	
 	def ps_def(self):
-		object = self.pop()
+		obj = self.pop()
 		name = self.pop()
-		self.dictstack[-1][name.value] = object
+		self.dictstack[-1][name.value] = obj
 	
 	def ps_bind(self):
 		proc = self.pop('proceduretype')
@@ -225,16 +224,16 @@
 			else:
 				if not item.literal:
 					try:
-						object = self.resolve_name(item.value)
+						obj = self.resolve_name(item.value)
 					except:
 						pass
 					else:
-						if object.type == 'operatortype':
-							proc.value[i] = object
+						if obj.type == 'operatortype':
+							proc.value[i] = obj
 	
 	def ps_exch(self):
 		if len(self.stack) < 2:
-			raise RuntimeError, 'stack underflow'
+			raise RuntimeError('stack underflow')
 		obj1 = self.pop()
 		obj2 = self.pop()
 		self.push(obj1)
@@ -242,15 +241,15 @@
 	
 	def ps_dup(self):
 		if not self.stack:
-			raise RuntimeError, 'stack underflow'
+			raise RuntimeError('stack underflow')
 		self.push(self.stack[-1])
 	
 	def ps_exec(self):
-		object = self.pop()
-		if object.type == 'proceduretype':
-			self.call_procedure(object)
+		obj = self.pop()
+		if obj.type == 'proceduretype':
+			self.call_procedure(obj)
 		else:
-			self.handle_object(object)
+			self.handle_object(obj)
 	
 	def ps_count(self):
 		self.push(ps_integer(len(self.stack)))
@@ -263,7 +262,7 @@
 	def ps_ne(self):
 		any1 = self.pop()
 		any2 = self.pop()
-		self.push(ps_boolean(any1.value <> any2.value))
+		self.push(ps_boolean(any1.value != any2.value))
 	
 	def ps_cvx(self):
 		obj = self.pop()
@@ -287,7 +286,7 @@
 		key = self.pop()
 		name = key.value
 		for i in range(len(self.dictstack)-1, -1, -1):
-			if self.dictstack[i].has_key(name):
+			if name in self.dictstack[i]:
 				self.dictstack[i][name] = value
 				break
 		self.dictstack[-1][name] = value
@@ -310,17 +309,17 @@
 		self.push(ps_file(self.tokenizer))
 	
 	def ps_eexec(self):
-		file = self.pop('filetype').value
-		file.starteexec()
+		f = self.pop('filetype').value
+		f.starteexec()
 	
 	def ps_closefile(self):
-		file = self.pop('filetype').value
-		file.skipwhite()
-		file.stopeexec()
+		f = self.pop('filetype').value
+		f.skipwhite()
+		f.stopeexec()
 	
 	def ps_cleartomark(self):
 		obj = self.pop()
-		while obj <> self.mark:
+		while obj != self.mark:
 			obj = self.pop()
 	
 	def ps_readstring(self,
@@ -328,31 +327,29 @@
 				len = len):
 		s = self.pop('stringtype')
 		oldstr = s.value
-		file = self.pop('filetype')
+		f = self.pop('filetype')
 		#pad = file.value.read(1)
 		# for StringIO, this is faster
-		file.value.pos = file.value.pos + 1
-		newstr = file.value.read(len(oldstr))
+		f.value.pos = f.value.pos + 1
+		newstr = f.value.read(len(oldstr))
 		s.value = newstr
 		self.push(s)
 		self.push(ps_boolean(len(oldstr) == len(newstr)))
 	
 	def ps_known(self):
 		key = self.pop()
-		dict = self.pop('dicttype', 'fonttype')
-		self.push(ps_boolean(dict.value.has_key(key.value)))
+		d = self.pop('dicttype', 'fonttype')
+		self.push(ps_boolean(key.value in d.value))
 	
 	def ps_if(self):
 		proc = self.pop('proceduretype')
-		bool = self.pop('booleantype')
-		if bool.value:
+		if self.pop('booleantype').value:
 			self.call_procedure(proc)
 	
 	def ps_ifelse(self):
 		proc2 = self.pop('proceduretype')
 		proc1 = self.pop('proceduretype')
-		bool = self.pop('booleantype')
-		if bool.value:
+		if self.pop('booleantype').value:
 			self.call_procedure(proc1)
 		else:
 			self.call_procedure(proc2)
@@ -384,7 +381,7 @@
 	
 	def ps_print(self):
 		str = self.pop('stringtype')
-		print 'PS output --->', str.value
+		print('PS output --->', str.value)
 	
 	def ps_anchorsearch(self):
 		seek = self.pop('stringtype')
@@ -411,8 +408,7 @@
 	
 	def ps_load(self):
 		name = self.pop()
-		object = self.resolve_name(name.value)
-		self.push(object)
+		self.push(self.resolve_name(name.value))
 	
 	def ps_put(self):
 		obj1 = self.pop()
@@ -440,7 +436,7 @@
 		elif tp == 'stringtype':
 			self.push(ps_integer(ord(obj2.value[obj1.value])))
 		else:
-			assert 0, "shouldn't get here"
+			assert False, "shouldn't get here"
 	
 	def ps_getinterval(self):
 		obj1 = self.pop('integertype')
@@ -466,13 +462,12 @@
 			obj3.value = newstr
 	
 	def ps_cvn(self):
-		str = self.pop('stringtype')
-		self.push(ps_name(str.value))
+		self.push(ps_name(self.pop('stringtype').value))
 	
 	def ps_index(self):
 		n = self.pop('integertype').value
 		if n < 0:
-			raise RuntimeError, 'index may not be negative'
+			raise RuntimeError('index may not be negative')
 		self.push(self.stack[-1-n])
 	
 	def ps_for(self):
@@ -528,21 +523,19 @@
 		self.pop()
 	
 	def ps_dict(self):
-		num = self.pop('integertype')
-		dict = ps_dict({})
-		self.push(dict)
+		self.pop('integertype')
+		self.push(ps_dict({}))
 	
 	def ps_begin(self):
-		dict = self.pop('dicttype')
-		self.dictstack.append(dict.value)
+		self.dictstack.append(self.pop('dicttype').value)
 	
 	def ps_end(self):
 		if len(self.dictstack) > 2:
 			del self.dictstack[-1]
 		else:
-			raise RuntimeError, 'dictstack underflow'
+			raise RuntimeError('dictstack underflow')
 	
 notdef = '.notdef'
 from fontTools.encodings.StandardEncoding import StandardEncoding
-ps_StandardEncoding = map(ps_name, StandardEncoding)
+ps_StandardEncoding = list(map(ps_name, StandardEncoding))
 
diff --git a/Lib/fontTools/misc/py23.py b/Lib/fontTools/misc/py23.py
new file mode 100644
index 0000000..0e5e45f
--- /dev/null
+++ b/Lib/fontTools/misc/py23.py
@@ -0,0 +1,83 @@
+"""Python 2/3 compat layer."""
+
+from __future__ import print_function, division
+
+try:
+	basestring
+except NameError:
+	basestring = str
+
+try:
+	unicode
+except NameError:
+	unicode = str
+
+try:
+	unichr
+	bytechr = chr
+	byteord = ord
+except:
+	unichr = chr
+	def bytechr(n):
+		return bytes([n])
+	def byteord(c):
+		return c if isinstance(c, int) else ord(c)
+
+try:
+	from StringIO import StringIO
+except ImportError:
+	from io import BytesIO as StringIO
+
+def strjoin(iterable):
+	return ''.join(iterable)
+if str == bytes:
+	class Tag(str):
+		def tobytes(self):
+			if isinstance(self, bytes):
+				return self
+			else:
+				return self.encode('latin1')
+
+	def tostr(s, encoding='ascii'):
+		if not isinstance(s, str):
+			return s.encode(encoding)
+		else:
+			return s
+	tobytes = tostr
+
+	bytesjoin = strjoin
+else:
+	class Tag(str):
+
+		@staticmethod
+		def transcode(blob):
+			if not isinstance(blob, str):
+				blob = blob.decode('latin-1')
+			return blob
+
+		def __new__(self, content):
+			return str.__new__(self, self.transcode(content))
+		def __ne__(self, other):
+			return not self.__eq__(other)
+		def __eq__(self, other):
+			return str.__eq__(self, self.transcode(other))
+
+		def __hash__(self):
+			return str.__hash__(self)
+
+		def tobytes(self):
+			return self.encode('latin-1')
+
+	def tostr(s, encoding='ascii'):
+		if not isinstance(s, str):
+			return s.decode(encoding)
+		else:
+			return s
+	def tobytes(s, encoding='ascii'):
+		if not isinstance(s, bytes):
+			return s.encode(encoding)
+		else:
+			return s
+
+	def bytesjoin(iterable):
+		return b''.join(tobytes(item) for item in iterable)
diff --git a/Lib/fontTools/misc/sstruct.py b/Lib/fontTools/misc/sstruct.py
index 7bc4772..e8e11cf 100644
--- a/Lib/fontTools/misc/sstruct.py
+++ b/Lib/fontTools/misc/sstruct.py
@@ -5,14 +5,14 @@
 struct, except the objects passed and returned are not tuples 
 (or argument lists), but dictionaries or instances. 
 
-Just like struct, we use format strings to describe a data 
+Just like struct, we use fmt strings to describe a data 
 structure, except we use one line per element. Lines are 
 separated by newlines or semi-colons. Each line contains 
 either one of the special struct characters ('@', '=', '<', 
 '>' or '!') or a 'name:formatchar' combo (eg. 'myFloat:f'). 
 Repetitions, like the struct module offers them are not useful 
 in this context, except for fixed length strings  (eg. 'myInt:5h' 
-is not allowed but 'myString:5s' is). The 'x' format character 
+is not allowed but 'myString:5s' is). The 'x' fmt character 
 (pad byte) is treated as 'special', since it is by definition 
 anonymous. Extra whitespace is allowed everywhere.
 
@@ -22,14 +22,14 @@
 the number of bits after the point. Fixed point numbers get 
 converted to floats.
 
-pack(format, object):
+pack(fmt, object):
 	'object' is either a dictionary or an instance (or actually
 	anything that has a __dict__ attribute). If it is a dictionary, 
 	its keys are used for names. If it is an instance, it's 
 	attributes are used to grab struct elements from. Returns
 	a string containing the data.
 
-unpack(format, data, object=None)
+unpack(fmt, data, object=None)
 	If 'object' is omitted (or None), a new dictionary will be 
 	returned. If 'object' is a dictionary, it will be used to add 
 	struct elements to. If it is an instance (or in fact anything
@@ -37,67 +37,74 @@
 	each struct element. In the latter two cases, 'object' itself 
 	is returned.
 
-unpack2(format, data, object=None)
+unpack2(fmt, data, object=None)
 	Convenience function. Same as unpack, except data may be longer 
 	than needed. The returned value is a tuple: (object, leftoverdata).
 
-calcsize(format)
-	like struct.calcsize(), but uses our own format strings:
+calcsize(fmt)
+	like struct.calcsize(), but uses our own fmt strings:
 	it returns the size of the data in bytes.
 """
 
-# XXX I would like to support pascal strings, too, but I'm not
-# sure if that's wise. Would be nice if struct supported them
-# "properly", but that would certainly break calcsize()...
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+import struct
+import re
 
 __version__ = "1.2"
 __copyright__ = "Copyright 1998, Just van Rossum <just@letterror.com>"
 
-import struct
-import re
-import types
 
+class Error(Exception):
+	pass
 
-error = "sstruct.error"
-
-def pack(format, object):
-	formatstring, names, fixes = getformat(format)
+def pack(fmt, obj):
+	formatstring, names, fixes = getformat(fmt)
 	elements = []
-	if type(object) is not types.DictType:
-		object = object.__dict__
+	if not isinstance(obj, dict):
+		obj = obj.__dict__
 	for name in names:
-		value = object[name]
-		if fixes.has_key(name):
+		value = obj[name]
+		if name in fixes:
 			# fixed point conversion
-			value = int(round(value*fixes[name]))
+			value = fl2fi(value, fixes[name])
+		elif isinstance(value, basestring):
+			value = tobytes(value)
 		elements.append(value)
-	data = apply(struct.pack, (formatstring,) + tuple(elements))
+	data = struct.pack(*(formatstring,) + tuple(elements))
 	return data
 
-def unpack(format, data, object=None):
-	if object is None:
-		object = {}
-	formatstring, names, fixes = getformat(format)
-	if type(object) is types.DictType:
-		dict = object
+def unpack(fmt, data, obj=None):
+	if obj is None:
+		obj = {}
+	data = tobytes(data)
+	formatstring, names, fixes = getformat(fmt)
+	if isinstance(obj, dict):
+		d = obj
 	else:
-		dict = object.__dict__
+		d = obj.__dict__
 	elements = struct.unpack(formatstring, data)
 	for i in range(len(names)):
 		name = names[i]
 		value = elements[i]
-		if fixes.has_key(name):
+		if name in fixes:
 			# fixed point conversion
-			value = value / fixes[name]
-		dict[name] = value
-	return object
+			value = fi2fl(value, fixes[name])
+		elif isinstance(value, bytes):
+			try:
+				value = tostr(value)
+			except UnicodeDecodeError:
+				pass
+		d[name] = value
+	return obj
 
-def unpack2(format, data, object=None):
-	length = calcsize(format)
-	return unpack(format, data[:length], object), data[length:]
+def unpack2(fmt, data, obj=None):
+	length = calcsize(fmt)
+	return unpack(fmt, data[:length], obj), data[length:]
 
-def calcsize(format):
-	formatstring, names, fixes = getformat(format)
+def calcsize(fmt):
+	formatstring, names, fixes = getformat(fmt)
 	return struct.calcsize(formatstring)
 
 
@@ -106,13 +113,13 @@
 		"\s*"							# whitespace
 		"([A-Za-z_][A-Za-z_0-9]*)"		# name (python identifier)
 		"\s*:\s*"						# whitespace : whitespace
-		"([cbBhHiIlLfd]|[0-9]+[ps]|"	# formatchar...
+		"([cbBhHiIlLqQfd]|[0-9]+[ps]|"	# formatchar...
 			"([0-9]+)\.([0-9]+)(F))"	# ...formatchar
 		"\s*"							# whitespace
 		"(#.*)?$"						# [comment] + end of string
 	)
 
-# matches the special struct format chars and 'x' (pad byte)
+# matches the special struct fmt chars and 'x' (pad byte)
 _extraRE = re.compile("\s*([x@=<>!])\s*(#.*)?$")
 
 # matches an "empty" string, possibly containing whitespace and/or a comment
@@ -125,11 +132,11 @@
 
 _formatcache = {}
 
-def getformat(format):
+def getformat(fmt):
 	try:
-		formatstring, names, fixes = _formatcache[format]
+		formatstring, names, fixes = _formatcache[fmt]
 	except KeyError:
-		lines = re.split("[\n;]", format)
+		lines = re.split("[\n;]", fmt)
 		formatstring = ""
 		names = []
 		fixes = {}
@@ -139,12 +146,12 @@
 			m = _extraRE.match(line)
 			if m:
 				formatchar = m.group(1)
-				if formatchar <> 'x' and formatstring:
-					raise error, "a special format char must be first"
+				if formatchar != 'x' and formatstring:
+					raise Error("a special fmt char must be first")
 			else:
 				m = _elementRE.match(line)
 				if not m:
-					raise error, "syntax error in format: '%s'" % line
+					raise Error("syntax error in fmt: '%s'" % line)
 				name = m.group(1)
 				names.append(name)
 				formatchar = m.group(2)
@@ -154,16 +161,16 @@
 					after = int(m.group(4))
 					bits = before + after
 					if bits not in [8, 16, 32]:
-						raise error, "fixed point must be 8, 16 or 32 bits long"
+						raise Error("fixed point must be 8, 16 or 32 bits long")
 					formatchar = _fixedpointmappings[bits]
 					assert m.group(5) == "F"
-					fixes[name] = float(1 << after)
+					fixes[name] = after
 			formatstring = formatstring + formatchar
-		_formatcache[format] = formatstring, names, fixes
+		_formatcache[fmt] = formatstring, names, fixes
 	return formatstring, names, fixes
 
 def _test():
-	format = """
+	fmt = """
 		# comments are allowed
 		>  # big endian (see documentation for struct)
 		# empty lines are allowed:
@@ -177,9 +184,9 @@
 		afixed: 16.16F
 	"""
 	
-	print 'size:', calcsize(format)
+	print('size:', calcsize(fmt))
 	
-	class foo:
+	class foo(object):
 		pass
 	
 	i = foo()
@@ -193,12 +200,12 @@
 	i.adouble = 0.5
 	i.afixed = 1.5
 	
-	data = pack(format, i)
-	print 'data:', `data`
-	print unpack(format, data)
+	data = pack(fmt, i)
+	print('data:', repr(data))
+	print(unpack(fmt, data))
 	i2 = foo()
-	unpack(format, data, i2)
-	print vars(i2)
+	unpack(fmt, data, i2)
+	print(vars(i2))
 
 if __name__ == "__main__":
 	_test()
diff --git a/Lib/fontTools/misc/textTools.py b/Lib/fontTools/misc/textTools.py
index 2fd5d0b..4d0f05a 100644
--- a/Lib/fontTools/misc/textTools.py
+++ b/Lib/fontTools/misc/textTools.py
@@ -1,28 +1,29 @@
-"""fontTools.misc.textTools.py -- miscelaneous routines."""
+"""fontTools.misc.textTools.py -- miscellaneous routines."""
 
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 import string
 
 
 def safeEval(data, eval=eval):
 	"""A (kindof) safe replacement for eval."""
-	return eval(data, {"__builtins__":{}}, {})
+	return eval(data, {"__builtins__":{}})
 
 
 def readHex(content):
 	"""Convert a list of hex strings to binary data."""
-	return deHexStr(''.join([ chunk for chunk in content if isinstance(chunk,str) ]))
+	return deHexStr(strjoin(chunk for chunk in content if isinstance(chunk, basestring)))
 
 def deHexStr(hexdata):
 	"""Convert a hex string to binary data."""
-	parts = string.split(hexdata)
-	hexdata = string.join(parts, "")
+	hexdata = strjoin(hexdata.split())
 	if len(hexdata) % 2:
 		hexdata = hexdata + "0"
 	data = []
 	for i in range(0, len(hexdata), 2):
-		data.append(chr(string.atoi(hexdata[i:i+2], 16)))
-	return "".join(data)
+		data.append(bytechr(int(hexdata[i:i+2], 16)))
+	return bytesjoin(data)
 
 
 def hexStr(data):
@@ -30,36 +31,36 @@
 	h = string.hexdigits
 	r = ''
 	for c in data:
-		i = ord(c)
+		i = byteord(c)
 		r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
 	return r
 
 
 def num2binary(l, bits=32):
-	all = []
-	bin = ""
+	items = []
+	binary = ""
 	for i in range(bits):
 		if l & 0x1:
-			bin = "1" + bin
+			binary = "1" + binary
 		else:
-			bin = "0" + bin
+			binary = "0" + binary
 		l = l >> 1
 		if not ((i+1) % 8):
-			all.append(bin)
-			bin = ""
-	if bin:
-		all.append(bin)
-	all.reverse()
+			items.append(binary)
+			binary = ""
+	if binary:
+		items.append(binary)
+	items.reverse()
 	assert l in (0, -1), "number doesn't fit in number of bits"
-	return string.join(all, " ")
+	return ' '.join(items)
 
 
 def binary2num(bin):
-	bin = string.join(string.split(bin), "")
+	bin = strjoin(bin.split())
 	l = 0
 	for digit in bin:
 		l = l << 1
-		if digit <> "0":
+		if digit != "0":
 			l = l | 0x1
 	return l
 
@@ -70,19 +71,7 @@
 	"""
 	
 	try:
-		# turn ['FOO',  'aaBc', 'ABcD'] into 
-		# [('foo', 'FOO'), ('aabc', 'aaBc'), ('abcd', 'ABcD')], 
-		# but only if all elements are strings
-		tupledlist = map(lambda item, lower = string.lower: 
-			(lower(item), item), alist)
+		return sorted(alist, key=lambda a: (a.lower(), a))
 	except TypeError:
-		# at least one element in alist is not a string, proceed the normal way...
-		alist = alist[:]
-		alist.sort()
-		return alist
-	else:
-		tupledlist.sort()
-		# turn [('aabc', 'aaBc'), ('abcd', 'ABcD'), ('foo', 'FOO')] into 
-		# ['aaBc', 'ABcD', 'FOO']
-		return map(lambda x: x[1], tupledlist)
+		return sorted(alist)
 
diff --git a/Lib/fontTools/misc/transform.py b/Lib/fontTools/misc/transform.py
index 769475f..a6bbbcb 100644
--- a/Lib/fontTools/misc/transform.py
+++ b/Lib/fontTools/misc/transform.py
@@ -45,6 +45,8 @@
 	>>>
 """
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 
 __all__ = ["Transform", "Identity", "Offset", "Scale"]
 
@@ -64,7 +66,7 @@
 	return v
 
 
-class Transform:
+class Transform(object):
 
 	"""2x2 transformation matrix plus offset, a.k.a. Affine transform.
 	Transform instances are immutable: all transforming methods, eg.
@@ -96,7 +98,7 @@
 		"""
 		self.__affine = xx, xy, yx, yy, dx, dy
 
-	def transformPoint(self, (x, y)):
+	def transformPoint(self, p):
 		"""Transform a point.
 
 		Example:
@@ -105,6 +107,7 @@
 			>>> t.transformPoint((100, 100))
 			(250.0, 550.0)
 		"""
+		(x, y) = p
 		xx, xy, yx, yy, dx, dy = self.__affine
 		return (xx*x + yx*y + dx, xy*x + yy*y + dy)
 
@@ -233,7 +236,7 @@
 		if self.__affine == (1, 0, 0, 1, 0, 0):
 			return self
 		xx, xy, yx, yy, dx, dy = self.__affine
-		det = float(xx*yy - yx*xy)
+		det = xx*yy - yx*xy
 		xx, xy, yx, yy = yy/det, -xy/det, -yx/det, xx/det
 		dx, dy = -xx*dx - yx*dy, -xy*dx - yy*dy
 		return self.__class__(xx, xy, yx, yy, dx, dy)
@@ -265,19 +268,9 @@
 		"""
 		return self.__affine[index]
 
-	def __getslice__(self, i, j):
-		"""Transform instances also behave like sequences and even support
-		slicing:
-			>>> t = Offset(100, 200)
-			>>> t
-			<Transform [1 0 0 1 100 200]>
-			>>> t[4:]
-			(100, 200)
-			>>>
-		"""
-		return self.__affine[i:j]
-
-	def __cmp__(self, other):
+	def __ne__(self, other):
+		return not self.__eq__(other)
+	def __eq__(self, other):
 		"""Transform instances are comparable:
 			>>> t1 = Identity.scale(2, 3).translate(4, 6)
 			>>> t2 = Identity.translate(8, 18).scale(2, 3)
@@ -298,8 +291,8 @@
 		"""
 		xx1, xy1, yx1, yy1, dx1, dy1 = self.__affine
 		xx2, xy2, yx2, yy2, dx2, dy2 = other
-		return cmp((xx1, xy1, yx1, yy1, dx1, dy1),
-				(xx2, xy2, yx2, yy2, dx2, dy2))
+		return (xx1, xy1, yx1, yy1, dx1, dy1) == \
+				(xx2, xy2, yx2, yy2, dx2, dy2)
 
 	def __hash__(self):
 		"""Transform instances are hashable, meaning you can use them as
@@ -328,7 +321,7 @@
 		return hash(self.__affine)
 
 	def __repr__(self):
-		return "<%s [%s %s %s %s %s %s]>" % ((self.__class__.__name__,)
+		return "<%s [%s %s %s %s %s %s]>" % ((self.__class__.__name__,) \
 				 + tuple(map(str, self.__affine)))
 
 
@@ -358,9 +351,6 @@
 	return Transform(x, 0, 0, y, 0, 0)
 
 
-def _test():
-	import doctest, transform
-	return doctest.testmod(transform)
-
 if __name__ == "__main__":
-	_test()
+	import doctest
+	doctest.testmod()
diff --git a/Lib/fontTools/ttLib/xmlImport.py b/Lib/fontTools/misc/xmlReader.py
similarity index 63%
rename from Lib/fontTools/ttLib/xmlImport.py
rename to Lib/fontTools/misc/xmlReader.py
index f110c8b..41315c1 100644
--- a/Lib/fontTools/ttLib/xmlImport.py
+++ b/Lib/fontTools/misc/xmlReader.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools import ttLib
 from fontTools.misc.textTools import safeEval
 from fontTools.ttLib.tables.DefaultTable import DefaultTable
@@ -9,49 +11,52 @@
 BUFSIZE = 0x4000
 
 
-class ExpatParser:
+class XMLReader(object):
 	
-	def __init__(self, ttFont, fileName, progress=None):
+	def __init__(self, fileName, ttFont, progress=None, quiet=False):
 		self.ttFont = ttFont
 		self.fileName = fileName
 		self.progress = progress
+		self.quiet = quiet
 		self.root = None
 		self.contentStack = []
 		self.stackSize = 0
 	
-	def parse(self):
+	def read(self):
+		if self.progress:
+			import stat
+			self.progress.set(0, os.stat(self.fileName)[stat.ST_SIZE] // 100 or 1)
 		file = open(self.fileName)
-		self.parseFile(file)
+		self._parseFile(file)
 		file.close()
 	
-	def parseFile(self, file):
+	def _parseFile(self, file):
 		from xml.parsers.expat import ParserCreate
 		parser = ParserCreate()
-		parser.returns_unicode = 0
-		parser.StartElementHandler = self.startElementHandler
-		parser.EndElementHandler = self.endElementHandler
-		parser.CharacterDataHandler = self.characterDataHandler
+		parser.StartElementHandler = self._startElementHandler
+		parser.EndElementHandler = self._endElementHandler
+		parser.CharacterDataHandler = self._characterDataHandler
 		
 		pos = 0
-		while 1:
+		while True:
 			chunk = file.read(BUFSIZE)
 			if not chunk:
 				parser.Parse(chunk, 1)
 				break
 			pos = pos + len(chunk)
 			if self.progress:
-				self.progress.set(pos / 100)
+				self.progress.set(pos // 100)
 			parser.Parse(chunk, 0)
 	
-	def startElementHandler(self, name, attrs):
+	def _startElementHandler(self, name, attrs):
 		stackSize = self.stackSize
 		self.stackSize = stackSize + 1
 		if not stackSize:
-			if name <> "ttFont":
-				raise TTXParseError, "illegal root tag: %s" % name
+			if name != "ttFont":
+				raise TTXParseError("illegal root tag: %s" % name)
 			sfntVersion = attrs.get("sfntVersion")
 			if sfntVersion is not None:
-				if len(sfntVersion) <> 4:
+				if len(sfntVersion) != 4:
 					sfntVersion = safeEval('"' + sfntVersion + '"')
 				self.ttFont.sfntVersion = sfntVersion
 			self.contentStack.append([])
@@ -59,7 +64,8 @@
 			subFile = attrs.get("src")
 			if subFile is not None:
 				subFile = os.path.join(os.path.dirname(self.fileName), subFile)
-				importXML(self.ttFont, subFile, self.progress)
+				subReader = XMLReader(subFile, self.ttFont, self.progress, self.quiet)
+				subReader.read()
 				self.contentStack.append([])
 				return
 			tag = ttLib.xmlToTag(name)
@@ -69,16 +75,17 @@
 			elif self.ttFont.verbose:
 				ttLib.debugmsg(msg)
 			else:
-				print msg
+				if not self.quiet:
+					print(msg)
 			if tag == "GlyphOrder":
 				tableClass = ttLib.GlyphOrder
-			elif attrs.has_key("ERROR"):
+			elif "ERROR" in attrs:
 				tableClass = DefaultTable
 			else:
 				tableClass = ttLib.getTableClass(tag)
 				if tableClass is None:
 					tableClass = DefaultTable
-			if tag == 'loca' and self.ttFont.has_key(tag):
+			if tag == 'loca' and tag in self.ttFont:
 				# Special-case the 'loca' table as we need the
 				#    original if the 'glyf' table isn't recompiled.
 				self.currentTable = self.ttFont[tag]
@@ -90,28 +97,29 @@
 			self.contentStack.append([])
 			self.root = (name, attrs, self.contentStack[-1])
 		else:
-			list = []
-			self.contentStack[-1].append((name, attrs, list))
-			self.contentStack.append(list)
+			l = []
+			self.contentStack[-1].append((name, attrs, l))
+			self.contentStack.append(l)
 	
-	def characterDataHandler(self, data):
+	def _characterDataHandler(self, data):
 		if self.stackSize > 1:
 			self.contentStack[-1].append(data)
 	
-	def endElementHandler(self, name):
+	def _endElementHandler(self, name):
 		self.stackSize = self.stackSize - 1
 		del self.contentStack[-1]
 		if self.stackSize == 1:
 			self.root = None
 		elif self.stackSize == 2:
-			self.currentTable.fromXML(self.root, self.ttFont)
+			name, attrs, content = self.root
+			self.currentTable.fromXML(name, attrs, content, self.ttFont)
 			self.root = None
 
 
-class ProgressPrinter:
+class ProgressPrinter(object):
 	
 	def __init__(self, title, maxval=100):
-		print title
+		print(title)
 	
 	def set(self, val, maxval=None):
 		pass
@@ -120,16 +128,5 @@
 		pass
 	
 	def setLabel(self, text):
-		print text
-
-
-def importXML(ttFont, fileName, progress=None):
-	"""Import a TTX file (an XML-based text format), so as to recreate
-	a font object.
-	"""
-	if progress:
-		import stat
-		progress.set(0, os.stat(fileName)[stat.ST_SIZE] / 100 or 1)
-	p = ExpatParser(ttFont, fileName, progress)
-	p.parse()
+		print(text)
 
diff --git a/Lib/fontTools/misc/xmlWriter.py b/Lib/fontTools/misc/xmlWriter.py
index aa70f13..93f57e3 100644
--- a/Lib/fontTools/misc/xmlWriter.py
+++ b/Lib/fontTools/misc/xmlWriter.py
@@ -1,17 +1,22 @@
 """xmlWriter.py -- Simple XML authoring class"""
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+import sys
 import string
-import struct
-import os
 
 INDENT = "  "
 
 
-class XMLWriter:
+class XMLWriter(object):
 	
-	def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None, encoding="utf-8"):
+	def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None):
 		if not hasattr(fileOrPath, "write"):
-			self.file = open(fileOrPath, "w")
+			try:
+				# Python3 has encoding support.
+				self.file = open(fileOrPath, "w", encoding="utf-8")
+			except TypeError:
+				self.file = open(fileOrPath, "w")
 		else:
 			# assume writable file object
 			self.file = fileOrPath
@@ -21,32 +26,43 @@
 		self.needindent = 1
 		self.idlefunc = idlefunc
 		self.idlecounter = 0
-		if encoding:
-			self.writeraw('<?xml version="1.0" encoding="%s"?>' % encoding)
-		else:
-			self.writeraw('<?xml version="1.0"?>')
+		self._writeraw('<?xml version="1.0" encoding="utf-8"?>')
 		self.newline()
 	
 	def close(self):
 		self.file.close()
 	
-	def write(self, data):
-		self.writeraw(escape(data))
+	def write(self, string, indent=True):
+		"""Writes text."""
+		self._writeraw(escape(string), indent=indent)
+
+	def writecdata(self, string):
+		"""Writes text in a CDATA section."""
+		self._writeraw("<![CDATA[" + string + "]]>")
+
+	def write8bit(self, data, strip=False):
+		"""Writes a bytes() sequence into the XML, escaping
+		non-ASCII bytes.  When this is read in xmlReader,
+		the original bytes can be recovered by encoding to
+		'latin-1'."""
+		self._writeraw(escape8bit(data), strip=strip)
+
+	def write16bit(self, data, strip=False):
+		self._writeraw(escape16bit(data), strip=strip)
 	
-	def write_noindent(self, data):
-		self.file.write(escape(data))
+	def write_noindent(self, string):
+		"""Writes text without indentation."""
+		self._writeraw(escape(string), indent=False)
 	
-	def write8bit(self, data):
-		self.writeraw(escape8bit(data))
-	
-	def write16bit(self, data):
-		self.writeraw(escape16bit(data))
-	
-	def writeraw(self, data):
-		if self.needindent:
+	def _writeraw(self, data, indent=True, strip=False):
+		"""Writes bytes, possibly indented."""
+		if indent and self.needindent:
 			self.file.write(self.indentlevel * self.indentwhite)
 			self.needindent = 0
-		self.file.write(data)
+		s = tostr(data, encoding="utf-8")
+		if (strip):
+			s = s.strip()
+		self.file.write(s)
 	
 	def newline(self):
 		self.file.write("\n")
@@ -58,22 +74,22 @@
 	
 	def comment(self, data):
 		data = escape(data)
-		lines = string.split(data, "\n")
-		self.writeraw("<!-- " + lines[0])
+		lines = data.split("\n")
+		self._writeraw("<!-- " + lines[0])
 		for line in lines[1:]:
 			self.newline()
-			self.writeraw("     " + line)
-		self.writeraw(" -->")
+			self._writeraw("     " + line)
+		self._writeraw(" -->")
 	
 	def simpletag(self, _TAG_, *args, **kwargs):
-		attrdata = apply(self.stringifyattrs, args, kwargs)
+		attrdata = self.stringifyattrs(*args, **kwargs)
 		data = "<%s%s/>" % (_TAG_, attrdata)
-		self.writeraw(data)
+		self._writeraw(data)
 	
 	def begintag(self, _TAG_, *args, **kwargs):
-		attrdata = apply(self.stringifyattrs, args, kwargs)
+		attrdata = self.stringifyattrs(*args, **kwargs)
 		data = "<%s%s>" % (_TAG_, attrdata)
-		self.writeraw(data)
+		self._writeraw(data)
 		self.stack.append(_TAG_)
 		self.indent()
 	
@@ -82,7 +98,7 @@
 		del self.stack[-1]
 		self.dedent()
 		data = "</%s>" % _TAG_
-		self.writeraw(data)
+		self._writeraw(data)
 	
 	def dumphex(self, data):
 		linelength = 16
@@ -95,7 +111,7 @@
 			for j in range(0, hexlinelength, chunksize):
 				line = line + white + hexline[j:j+chunksize]
 				white = " "
-			self.writeraw(line)
+			self._writeraw(line)
 			self.newline()
 	
 	def indent(self):
@@ -108,8 +124,7 @@
 	def stringifyattrs(self, *args, **kwargs):
 		if kwargs:
 			assert not args
-			attributes = kwargs.items()
-			attributes.sort()
+			attributes = sorted(kwargs.items())
 		elif args:
 			assert len(args) == 1
 			attributes = args[0]
@@ -122,39 +137,34 @@
 	
 
 def escape(data):
-	data = string.replace(data, "&", "&amp;")
-	data = string.replace(data, "<", "&lt;")
+	data = tostr(data, 'utf-8')
+	data = data.replace("&", "&amp;")
+	data = data.replace("<", "&lt;")
+	data = data.replace(">", "&gt;")
 	return data
 
 def escapeattr(data):
-	data = string.replace(data, "&", "&amp;")
-	data = string.replace(data, "<", "&lt;")
-	data = string.replace(data, '"', "&quot;")
+	data = escape(data)
+	data = data.replace('"', "&quot;")
 	return data
 
 def escape8bit(data):
+	"""Input is Unicode string."""
 	def escapechar(c):
 		n = ord(c)
-		if c in "<&":
-			if c == "&":
-				return "&amp;"
-			else:
-				return "&lt;"
-		elif 32 <= n <= 127:
+		if 32 <= n <= 127 and c not in "<&>":
 			return c
 		else:
-			return "&#" + `n` + ";"
-	return string.join(map(escapechar, data), "")
-
-needswap = struct.pack("h", 1) == "\001\000"
+			return "&#" + repr(n) + ";"
+	return strjoin(map(escapechar, data.decode('latin-1')))
 
 def escape16bit(data):
 	import array
 	a = array.array("H")
 	a.fromstring(data)
-	if needswap:
+	if sys.byteorder != "big":
 		a.byteswap()
-	def escapenum(n, amp=ord("&"), lt=ord("<")):
+	def escapenum(n, amp=byteord("&"), lt=byteord("<")):
 		if n == amp:
 			return "&amp;"
 		elif n == lt:
@@ -162,15 +172,14 @@
 		elif 32 <= n <= 127:
 			return chr(n)
 		else:
-			return "&#" + `n` + ";"
-	return string.join(map(escapenum, a), "")
+			return "&#" + repr(n) + ";"
+	return strjoin(map(escapenum, a))
 
 
 def hexStr(s):
 	h = string.hexdigits
 	r = ''
 	for c in s:
-		i = ord(c)
+		i = byteord(c)
 		r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
 	return r
-
diff --git a/Lib/fontTools/nfntLib.py b/Lib/fontTools/nfntLib.py
deleted file mode 100644
index c834fc5..0000000
--- a/Lib/fontTools/nfntLib.py
+++ /dev/null
@@ -1,304 +0,0 @@
-import struct
-from fontTools.misc import sstruct
-import string
-import types
-
-
-# FontRec header
-nfntHeaderFormat = """
-	>  # big endian
-	fontType:    h     # font type
-	firstChar:   h     # ASCII code of first character
-	lastChar:    h     # ASCII code of last character
-	widMax:      h     # maximum character width
-	kernMax:     h     # negative of maximum character kern
-	nDescent:    h     # negative of descent
-	fRectWidth:  h     # width of font rectangle
-	fRectHeight: h     # height of font rectangle
-	owTLoc:      H     # offset to offset/width table (in words from _this_ point)
-	ascent:      h     # ascent
-	descent:     h     # descent
-	leading:     h     # leading
-	rowWords:    h     # row width of bit image / 2
-"""
-headerSize = sstruct.calcsize(nfntHeaderFormat)
-assert headerSize == 26
-
-
-class NFNT:
-	
-	def __init__(self, data=None):
-		if data is not None:
-			self.decompile(data)
-	
-	def decompile(self, data):
-		# header; FontRec
-		sstruct.unpack(nfntHeaderFormat, data[:headerSize], self)
-		
-		#assert self.fRectHeight == (self.ascent + self.descent)
-		
-		# rest
-		tableSize = 2 * (self.lastChar - self.firstChar + 3)
-		bitmapSize = 2 * self.rowWords * self.fRectHeight
-		
-		self.bits = data[headerSize:headerSize + bitmapSize]
-		
-		# XXX deal with self.nDescent being a positive number
-		assert (headerSize + bitmapSize + tableSize - 16) / 2 == self.owTLoc  # ugh...
-		
-		locTable = data[headerSize + bitmapSize:headerSize + bitmapSize + tableSize]
-		if len(locTable) <> tableSize:
-			raise ValueError, 'invalid NFNT format'
-		
-		owTable = data[headerSize + bitmapSize + tableSize:headerSize + bitmapSize + 2 * tableSize]
-		if len(owTable) <> tableSize:
-			raise ValueError, 'invalid NFNT format'
-		
-		# fill tables
-		self.offsetTable = []
-		self.widthTable = []
-		self.locTable = []
-		for i in range(0, tableSize, 2):
-			self.offsetTable.append(ord(owTable[i]))
-			self.widthTable.append(ord(owTable[i+1]))
-			loc, = struct.unpack("h", locTable[i:i+2])
-			self.locTable.append(loc)
-	
-	def compile(self):
-		header = sstruct.pack(nfntHeaderFormat, self)
-		nEntries = len(self.widthTable)
-		owTable = [None] * nEntries
-		locTable = [None] * nEntries
-		for i in range(nEntries):
-			owTable[i] = chr(self.offsetTable[i]) + chr(self.widthTable[i])
-			locTable[i] = struct.pack("h", self.locTable[i])
-		owTable = string.join(owTable, "")
-		locTable = string.join(locTable, "")
-		assert len(locTable) == len(owTable) == 2 * (self.lastChar - self.firstChar + 3)
-		return header + self.bits + locTable + owTable
-	
-	def unpackGlyphs(self):
-		import numpy
-		nGlyphs = len(self.locTable) - 1
-		self.glyphs = [None] * nGlyphs
-		
-		rowBytes = self.rowWords * 2
-		imageWidth = self.rowWords * 16
-		imageHeight = self.fRectHeight
-		bits = self.bits
-		bitImage = numpy.zeros((imageWidth, imageHeight), numpy.int8)
-		
-		for y in range(imageHeight):
-			for xByte in range(rowBytes):
-				byte = bits[y * rowBytes + xByte]
-				for xBit in range(8):
-					x = 8 * xByte + xBit
-					bit = (ord(byte) >> (7 - xBit)) & 0x01
-					bitImage[x, y] = bit
-		
-		for i in range(nGlyphs):
-			width = self.widthTable[i]
-			offset = self.offsetTable[i]
-			if width == 255 and offset == 255:
-				self.glyphs[i] = None
-			else:
-				imageL = self.locTable[i]
-				imageR = self.locTable[i+1]
-				imageWidth = imageR - imageL
-				offset = offset + self.kernMax
-				self.glyphs[i] = glyph = Glyph(width, offset, bitImage[imageL:imageR])
-	
-	def packGlyphs(self):
-		import numpy
-		imageWidth = 0
-		kernMax = 0
-		imageHeight = None
-		widMax = 0
-		fRectWidth = 0
-		for glyph in self.glyphs:
-			if glyph is None:
-				continue
-			if imageHeight is None:
-				imageHeight = glyph.pixels.shape[1]
-			else:
-				assert imageHeight == glyph.pixels.shape[1]
-			imageWidth = imageWidth + glyph.pixels.shape[0]
-			kernMax = min(kernMax, glyph.offset)
-			widMax = max(widMax, glyph.width)
-			fRectWidth = max(fRectWidth, glyph.pixels.shape[0] + glyph.offset)
-		
-		fRectWidth = fRectWidth - kernMax
-		imageWidth = 16 * ((imageWidth - 1) / 16 + 1)
-		rowBytes = imageWidth / 8
-		rowWords = rowBytes / 2
-		bitImage = numpy.zeros((imageWidth, imageHeight), numpy.int8)
-		locTable = []
-		widthTable = []
-		offsetTable = []
-		loc = 0
-		for glyph in self.glyphs:
-			locTable.append(loc)
-			if glyph is None:
-				widthTable.append(255)
-				offsetTable.append(255)
-				continue
-			widthTable.append(glyph.width)
-			offsetTable.append(glyph.offset - kernMax)
-			imageWidth = glyph.pixels.shape[0]
-			bitImage[loc:loc+imageWidth] = glyph.pixels
-			loc = loc + imageWidth
-		
-		locTable.append(loc)
-		widthTable.append(255)
-		offsetTable.append(255)
-		
-		bits = []
-		for y in range(imageHeight):
-			for xByte in range(rowBytes):
-				byte = 0
-				for x in range(8):
-					byte = byte | ((bitImage[8 * xByte + x, y] & 0x01) << (7 - x))
-				bits.append(chr(byte))
-		bits = string.join(bits, "")
-		
-		# assign values
-		self.fontType = 0x9000
-		self.lastChar = self.firstChar + len(self.glyphs) - 2
-		self.widMax = widMax
-		self.kernMax = kernMax
-		self.descent = imageHeight - self.ascent
-		self.nDescent = -self.descent
-		self.fRectWidth = fRectWidth
-		self.fRectHeight = imageHeight
-		self.rowWords = rowWords
-		
-		tableSize = 2 * (self.lastChar - self.firstChar + 3)
-		self.owTLoc = (headerSize + len(bits) + tableSize - 16) / 2
-		
-		self.bits = bits
-		self.locTable = locTable
-		self.widthTable = widthTable
-		self.offsetTable = offsetTable
-	
-	def getMissing(self):
-		return self.glyphs[-1]
-	
-	def __getitem__(self, charNum):
-		if charNum > self.lastChar or charNum < 0:
-			raise IndexError, "no such character"
-		index = charNum - self.firstChar
-		if index < 0:
-			return None
-		return self.glyphs[index]
-	
-	def __setitem__(self, charNum, glyph):
-		if charNum > self.lastChar or charNum < 0:
-			raise IndexError, "no such character"
-		index = charNum - self.firstChar
-		if index < 0:
-			raise IndexError, "no such character"
-		self.glyphs[index] = glyph
-	
-	def __len__(self):
-		return len(self.locTable) - 2 + self.firstChar
-	
-	#
-	# XXX old cruft
-	#
-	
-	def createQdBitImage(self):
-		import Qd
-		self.bitImage = Qd.BitMap(self.bits, 2 * self.rowWords, (0, 0, self.rowWords * 16, self.fRectHeight))
-	
-	def drawstring(self, astring, destbits, xOffset=0, yOffset=0):
-		drawchar = self.drawchar
-		for ch in astring:
-			xOffset = drawchar(ch, destbits, xOffset, yOffset)
-		return xOffset
-	
-	def drawchar(self, ch, destbits, xOffset, yOffset=0):
-		import Qd
-		width, bounds, destbounds = self.getcharbounds(ch)
-		destbounds = Qd.OffsetRect(destbounds, xOffset, yOffset)
-		Qd.CopyBits(self.bitImage, destbits, bounds, destbounds, 1, None)
-		return xOffset + width
-	
-	def stringwidth(self, astring):
-		charwidth = self.charwidth
-		width = 0
-		for ch in astring:
-			width = width + charwidth(ch)
-		return width
-	
-	def charwidth(self, ch):
-		cindex = ord(ch) - self.firstChar
-		if cindex > self.lastChar or 	\
-				(self.offsetTable[cindex] == 255 and self.widthTable[cindex] == 255):
-			cindex = -2		# missing char
-		return self.widthTable[cindex]
-	
-	def getcharbounds(self, ch):
-		cindex = ord(ch) - self.firstChar
-		if cindex > self.lastChar or 	\
-				(self.offsetTable[cindex] == 255 and self.widthTable[cindex] == 255):
-			return self.getcharboundsindex(-2)	# missing char
-		return self.getcharboundsindex(cindex)
-	
-	def getcharboundsindex(self, cindex):
-		offset = self.offsetTable[cindex]
-		width = self.widthTable[cindex]
-		if offset == 255 and width == 255:
-			raise ValueError, "character not defined"
-		location0 = self.locTable[cindex]
-		location1 = self.locTable[cindex + 1]
-		srcbounds = (location0, 0, location1, self.fRectHeight)
-		destbounds = (	offset + self.kernMax, 
-					0, 
-					offset + self.kernMax + location1 - location0, 
-					self.fRectHeight	)
-		return width, srcbounds, destbounds
-
-
-class Glyph:
-	
-	def __init__(self, width, offset, pixels=None, pixelDepth=1):
-		self.width = width
-		self.offset = offset
-		self.pixelDepth = pixelDepth
-		self.pixels = pixels
-
-
-def dataFromFile(pathOrFSSpec, nameOrID="", resType='NFNT'):
-	from Carbon import Res
-	resref = Res.FSOpenResFile(pathOrFSSpec, 1)	# readonly
-	try:
-		Res.UseResFile(resref)
-		if not nameOrID:
-			# just take the first in the file
-			res = Res.Get1IndResource(resType, 1)
-		elif type(nameOrID) == types.IntType:
-			res = Res.Get1Resource(resType, nameOrID)
-		else:
-			res = Res.Get1NamedResource(resType, nameOrID)
-		theID, theType, name = res.GetResInfo()
-		data = res.data
-	finally:
-		Res.CloseResFile(resref)
-	return data
-
-
-def fromFile(pathOrFSSpec, nameOrID="", resType='NFNT'):
-	data = dataFromFile(pathOrFSSpec, nameOrID, resType)
-	return NFNT(data)
-
-
-if __name__ == "__main__":
-	import EasyDialogs
-	path = EasyDialogs.AskFileForOpen()
-	if path:
-		data = dataFromFile(path)
-		font = NFNT(data)
-		font.unpackGlyphs()
-		font.packGlyphs()
-		data2 = font.compile()
-		print "xxxxx", data == data2, len(data) == len(data2)
diff --git a/Lib/fontTools/pens/basePen.py b/Lib/fontTools/pens/basePen.py
index 04da75a..0ad866c 100644
--- a/Lib/fontTools/pens/basePen.py
+++ b/Lib/fontTools/pens/basePen.py
@@ -36,6 +36,8 @@
 sequence of length 2 will do.
 """
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 
 __all__ = ["AbstractPen", "NullPen", "BasePen",
            "decomposeSuperBezierSegment", "decomposeQuadraticSegment"]
@@ -248,7 +250,7 @@
 		elif n == 0:
 			self.lineTo(points[0])
 		else:
-			raise AssertionError, "can't get there from here"
+			raise AssertionError("can't get there from here")
 
 	def qCurveTo(self, *points):
 		n = len(points) - 1  # 'n' is the number of control points
@@ -296,9 +298,8 @@
 	for i in range(2, n+1):
 		# calculate points in between control points.
 		nDivisions = min(i, 3, n-i+2)
-		d = float(nDivisions)
 		for j in range(1, nDivisions):
-			factor = j / d
+			factor = j / nDivisions
 			temp1 = points[i-1]
 			temp2 = points[i-2]
 			temp = (temp2[0] + factor * (temp1[0] - temp2[0]),
@@ -339,14 +340,14 @@
 class _TestPen(BasePen):
 	"""Test class that prints PostScript to stdout."""
 	def _moveTo(self, pt):
-		print "%s %s moveto" % (pt[0], pt[1])
+		print("%s %s moveto" % (pt[0], pt[1]))
 	def _lineTo(self, pt):
-		print "%s %s lineto" % (pt[0], pt[1])
+		print("%s %s lineto" % (pt[0], pt[1]))
 	def _curveToOne(self, bcp1, bcp2, pt):
-		print "%s %s %s %s %s %s curveto" % (bcp1[0], bcp1[1],
-				bcp2[0], bcp2[1], pt[0], pt[1])
+		print("%s %s %s %s %s %s curveto" % (bcp1[0], bcp1[1],
+				bcp2[0], bcp2[1], pt[0], pt[1]))
 	def _closePath(self):
-		print "closepath"
+		print("closepath")
 
 
 if __name__ == "__main__":
diff --git a/Lib/fontTools/pens/boundsPen.py b/Lib/fontTools/pens/boundsPen.py
index 3fde6e3..144acc4 100644
--- a/Lib/fontTools/pens/boundsPen.py
+++ b/Lib/fontTools/pens/boundsPen.py
@@ -1,6 +1,8 @@
-from fontTools.pens.basePen import BasePen
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect
 from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds
+from fontTools.pens.basePen import BasePen
 
 
 __all__ = ["BoundsPen", "ControlBoundsPen"]
@@ -86,8 +88,8 @@
 
 	pen = ControlBoundsPen(None)
 	draw(pen)
-	print pen.bounds
+	print(pen.bounds)
 
 	pen = BoundsPen(None)
 	draw(pen)
-	print pen.bounds
+	print(pen.bounds)
diff --git a/Lib/fontTools/pens/cocoaPen.py b/Lib/fontTools/pens/cocoaPen.py
index ef3bf03..59a4c81 100644
--- a/Lib/fontTools/pens/cocoaPen.py
+++ b/Lib/fontTools/pens/cocoaPen.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.pens.basePen import BasePen
 
 
@@ -13,14 +15,14 @@
 			path = NSBezierPath.bezierPath()
 		self.path = path
 
-	def _moveTo(self, (x, y)):
-		self.path.moveToPoint_((x, y))
+	def _moveTo(self, p):
+		self.path.moveToPoint_(p)
 
-	def _lineTo(self, (x, y)):
-		self.path.lineToPoint_((x, y))
+	def _lineTo(self, p):
+		self.path.lineToPoint_(p)
 
-	def _curveToOne(self, (x1, y1), (x2, y2), (x3, y3)):
-		self.path.curveToPoint_controlPoint1_controlPoint2_((x3, y3), (x1, y1), (x2, y2))
+	def _curveToOne(self, p1, p2, p3):
+		self.path.curveToPoint_controlPoint1_controlPoint2_(p3, p1, p2)
 
 	def _closePath(self):
 		self.path.closePath()
diff --git a/Lib/fontTools/pens/pointInsidePen.py b/Lib/fontTools/pens/pointInsidePen.py
index 131c75f..4966815 100644
--- a/Lib/fontTools/pens/pointInsidePen.py
+++ b/Lib/fontTools/pens/pointInsidePen.py
@@ -2,6 +2,8 @@
 for shapes.
 """
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.pens.basePen import BasePen
 from fontTools.misc.bezierTools import solveQuadratic, solveCubic
 
@@ -96,7 +98,7 @@
 
 		dx = x2 - x1
 		dy = y2 - y1
-		t = float(y - y1) / dy
+		t = (y - y1) / dy
 		ix = dx * t + x1
 		if ix < x:
 			return
@@ -120,8 +122,7 @@
 		cy = (y2 - dy) * 3.0
 		by = (y3 - y2) * 3.0 - cy
 		ay = y4 - dy - cy - by
-		solutions = solveCubic(ay, by, cy, dy - y)
-		solutions.sort()
+		solutions = sorted(solveCubic(ay, by, cy, dy - y))
 		solutions = [t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON]
 		if not solutions:
 			return
@@ -176,8 +177,7 @@
 		c = y1
 		b = (y2 - c) * 2.0
 		a = y3 - c - b
-		solutions = solveQuadratic(a, b, c - y)
-		solutions.sort()
+		solutions = sorted(solveQuadratic(a, b, c - y))
 		solutions = [t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON]
 		if not solutions:
 			return
diff --git a/Lib/fontTools/pens/reportLabPen.py b/Lib/fontTools/pens/reportLabPen.py
index 1c3fcf8..d383100 100644
--- a/Lib/fontTools/pens/reportLabPen.py
+++ b/Lib/fontTools/pens/reportLabPen.py
@@ -1,4 +1,7 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.pens.basePen import BasePen
+from reportlab.graphics.shapes import Path
 
 
 class ReportLabPen(BasePen):
@@ -8,17 +11,21 @@
 	def __init__(self, glyphSet, path=None):
 		BasePen.__init__(self, glyphSet)
 		if path is None:
-			from reportlab.graphics.shapes import Path
 			path = Path()
 		self.path = path
 
-	def _moveTo(self, (x,y)):
+	def _moveTo(self, p):
+		(x,y) = p
 		self.path.moveTo(x,y)
 
-	def _lineTo(self, (x,y)):
+	def _lineTo(self, p):
+		(x,y) = p
 		self.path.lineTo(x,y)
 
-	def _curveToOne(self, (x1,y1), (x2,y2), (x3,y3)):
+	def _curveToOne(self, p1, p2, p3):
+		(x1,y1) = p1
+		(x2,y2) = p2
+		(x3,y3) = p3
 		self.path.curveTo(x1, y1, x2, y2, x3, y3)
 
 	def _closePath(self):
@@ -28,15 +35,14 @@
 if __name__=="__main__":
 	import sys
 	if len(sys.argv) < 3:
-		print "Usage: reportLabPen.py <OTF/TTF font> <glyphname> [<image file to create>]"
-		print "  If no image file name is created, by default <glyphname>.png is created."
-		print "  example: reportLabPen.py Arial.TTF R test.png"
-		print "  (The file format will be PNG, regardless of the image file name supplied)"
+		print("Usage: reportLabPen.py <OTF/TTF font> <glyphname> [<image file to create>]")
+		print("  If no image file name is created, by default <glyphname>.png is created.")
+		print("  example: reportLabPen.py Arial.TTF R test.png")
+		print("  (The file format will be PNG, regardless of the image file name supplied)")
 		sys.exit(0)
 
 	from fontTools.ttLib import TTFont
 	from reportlab.lib import colors
-	from reportlab.graphics.shapes import Path
 
 	path = sys.argv[1]
 	glyphName = sys.argv[2]
diff --git a/Lib/fontTools/pens/transformPen.py b/Lib/fontTools/pens/transformPen.py
index 63c9323..8069ecd 100644
--- a/Lib/fontTools/pens/transformPen.py
+++ b/Lib/fontTools/pens/transformPen.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.pens.basePen import AbstractPen
 
 
diff --git a/Lib/fontTools/subset.py b/Lib/fontTools/subset.py
index ca66942..8ce37f2 100644
--- a/Lib/fontTools/subset.py
+++ b/Lib/fontTools/subset.py
@@ -7,15 +7,16 @@
 Later grown into full OpenType subsetter, supporting all standard tables.
 """
 
-import sys
-import struct
-import time
-import array
-
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools import ttLib
 from fontTools.ttLib.tables import otTables
 from fontTools.misc import psCharStrings
 from fontTools.pens import basePen
+import sys
+import struct
+import time
+import array
 
 
 def _add_method(*clazzes):
@@ -24,10 +25,10 @@
   def wrapper(method):
     for clazz in clazzes:
       assert clazz.__name__ != 'DefaultTable', 'Oops, table class not found.'
-      assert not hasattr(clazz, method.func_name), \
+      assert not hasattr(clazz, method.__name__), \
           "Oops, class '%s' has method '%s'." % (clazz.__name__,
-                                                 method.func_name)
-      setattr(clazz, method.func_name, method)
+                                                 method.__name__)
+      setattr(clazz, method.__name__, method)
     return None
   return wrapper
 
@@ -68,25 +69,25 @@
   "Returns ascending list of matching class values."
   return _uniq_sort(
      ([0] if any(g not in self.classDefs for g in glyphs) else []) +
-      [v for g,v in self.classDefs.iteritems() if g in glyphs])
+      [v for g,v in self.classDefs.items() if g in glyphs])
 
 @_add_method(otTables.ClassDef)
 def intersect_class(self, glyphs, klass):
   "Returns set of glyphs matching class."
   if klass == 0:
     return set(g for g in glyphs if g not in self.classDefs)
-  return set(g for g,v in self.classDefs.iteritems()
+  return set(g for g,v in self.classDefs.items()
               if v == klass and g in glyphs)
 
 @_add_method(otTables.ClassDef)
 def subset(self, glyphs, remap=False):
   "Returns ascending list of remaining classes."
-  self.classDefs = dict((g,v) for g,v in self.classDefs.iteritems() if g in glyphs)
+  self.classDefs = dict((g,v) for g,v in self.classDefs.items() if g in glyphs)
   # Note: while class 0 has the special meaning of "not matched",
   # if no glyph will ever /not match/, we can optimize class 0 out too.
   indices = _uniq_sort(
      ([0] if any(g not in self.classDefs for g in glyphs) else []) +
-      self.classDefs.values())
+      list(self.classDefs.values()))
   if remap:
     self.remap(indices)
   return indices
@@ -95,97 +96,73 @@
 def remap(self, class_map):
   "Remaps classes."
   self.classDefs = dict((g,class_map.index(v))
-                         for g,v in self.classDefs.iteritems())
+                         for g,v in self.classDefs.items())
 
 @_add_method(otTables.SingleSubst)
 def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs == None: cur_glyphs = s.glyphs
-  if self.Format in [1, 2]:
-    s.glyphs.update(v for g,v in self.mapping.iteritems() if g in cur_glyphs)
-  else:
-    assert 0, "unknown format: %s" % self.Format
+  if cur_glyphs is None: cur_glyphs = s.glyphs
+  s.glyphs.update(v for g,v in self.mapping.items() if g in cur_glyphs)
 
 @_add_method(otTables.SingleSubst)
 def subset_glyphs(self, s):
-  if self.Format in [1, 2]:
-    self.mapping = dict((g,v) for g,v in self.mapping.iteritems()
-                        if g in s.glyphs and v in s.glyphs)
-    return bool(self.mapping)
-  else:
-    assert 0, "unknown format: %s" % self.Format
+  self.mapping = dict((g,v) for g,v in self.mapping.items()
+                      if g in s.glyphs and v in s.glyphs)
+  return bool(self.mapping)
 
 @_add_method(otTables.MultipleSubst)
 def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs == None: cur_glyphs = s.glyphs
-  if self.Format == 1:
-    indices = self.Coverage.intersect(cur_glyphs)
-    _set_update(s.glyphs, *(self.Sequence[i].Substitute for i in indices))
-  else:
-    assert 0, "unknown format: %s" % self.Format
+  if cur_glyphs is None: cur_glyphs = s.glyphs
+  indices = self.Coverage.intersect(cur_glyphs)
+  _set_update(s.glyphs, *(self.Sequence[i].Substitute for i in indices))
 
 @_add_method(otTables.MultipleSubst)
 def subset_glyphs(self, s):
-  if self.Format == 1:
-    indices = self.Coverage.subset(s.glyphs)
-    self.Sequence = [self.Sequence[i] for i in indices]
-    # Now drop rules generating glyphs we don't want
-    indices = [i for i,seq in enumerate(self.Sequence)
-         if all(sub in s.glyphs for sub in seq.Substitute)]
-    self.Sequence = [self.Sequence[i] for i in indices]
-    self.Coverage.remap(indices)
-    self.SequenceCount = len(self.Sequence)
-    return bool(self.SequenceCount)
-  else:
-    assert 0, "unknown format: %s" % self.Format
+  indices = self.Coverage.subset(s.glyphs)
+  self.Sequence = [self.Sequence[i] for i in indices]
+  # Now drop rules generating glyphs we don't want
+  indices = [i for i,seq in enumerate(self.Sequence)
+       if all(sub in s.glyphs for sub in seq.Substitute)]
+  self.Sequence = [self.Sequence[i] for i in indices]
+  self.Coverage.remap(indices)
+  self.SequenceCount = len(self.Sequence)
+  return bool(self.SequenceCount)
 
 @_add_method(otTables.AlternateSubst)
 def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs == None: cur_glyphs = s.glyphs
-  if self.Format == 1:
-    _set_update(s.glyphs, *(vlist for g,vlist in self.alternates.iteritems()
-                            if g in cur_glyphs))
-  else:
-    assert 0, "unknown format: %s" % self.Format
+  if cur_glyphs is None: cur_glyphs = s.glyphs
+  _set_update(s.glyphs, *(vlist for g,vlist in self.alternates.items()
+                          if g in cur_glyphs))
 
 @_add_method(otTables.AlternateSubst)
 def subset_glyphs(self, s):
-  if self.Format == 1:
-    self.alternates = dict((g,vlist)
-                           for g,vlist in self.alternates.iteritems()
-                           if g in s.glyphs and
-                              all(v in s.glyphs for v in vlist))
-    return bool(self.alternates)
-  else:
-    assert 0, "unknown format: %s" % self.Format
+  self.alternates = dict((g,vlist)
+                         for g,vlist in self.alternates.items()
+                         if g in s.glyphs and
+                            all(v in s.glyphs for v in vlist))
+  return bool(self.alternates)
 
 @_add_method(otTables.LigatureSubst)
 def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs == None: cur_glyphs = s.glyphs
-  if self.Format == 1:
-    _set_update(s.glyphs, *([seq.LigGlyph for seq in seqs
-                             if all(c in s.glyphs for c in seq.Component)]
-                            for g,seqs in self.ligatures.iteritems()
-                            if g in cur_glyphs))
-  else:
-    assert 0, "unknown format: %s" % self.Format
+  if cur_glyphs is None: cur_glyphs = s.glyphs
+  _set_update(s.glyphs, *([seq.LigGlyph for seq in seqs
+                           if all(c in s.glyphs for c in seq.Component)]
+                          for g,seqs in self.ligatures.items()
+                          if g in cur_glyphs))
 
 @_add_method(otTables.LigatureSubst)
 def subset_glyphs(self, s):
-  if self.Format == 1:
-    self.ligatures = dict((g,v) for g,v in self.ligatures.iteritems()
-                          if g in s.glyphs)
-    self.ligatures = dict((g,[seq for seq in seqs
-                              if seq.LigGlyph in s.glyphs and
-                                 all(c in s.glyphs for c in seq.Component)])
-                           for g,seqs in self.ligatures.iteritems())
-    self.ligatures = dict((g,v) for g,v in self.ligatures.iteritems() if v)
-    return bool(self.ligatures)
-  else:
-    assert 0, "unknown format: %s" % self.Format
+  self.ligatures = dict((g,v) for g,v in self.ligatures.items()
+                        if g in s.glyphs)
+  self.ligatures = dict((g,[seq for seq in seqs
+                            if seq.LigGlyph in s.glyphs and
+                               all(c in s.glyphs for c in seq.Component)])
+                         for g,seqs in self.ligatures.items())
+  self.ligatures = dict((g,v) for g,v in self.ligatures.items() if v)
+  return bool(self.ligatures)
 
 @_add_method(otTables.ReverseChainSingleSubst)
 def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs == None: cur_glyphs = s.glyphs
+  if cur_glyphs is None: cur_glyphs = s.glyphs
   if self.Format == 1:
     indices = self.Coverage.intersect(cur_glyphs)
     if(not indices or
@@ -279,6 +256,7 @@
 @_add_method(otTables.Anchor)
 def prune_hints(self):
   # Drop device tables / contour anchor point
+  self.ensureDecompiled()
   self.Format = 1
 
 @_add_method(otTables.CursivePos)
@@ -317,10 +295,12 @@
 def prune_post_subset(self, options):
     if not options.hinting:
       for m in self.MarkArray.MarkRecord:
-        m.MarkAnchor.prune_hints()
+        if m.MarkAnchor:
+          m.MarkAnchor.prune_hints()
       for b in self.BaseArray.BaseRecord:
         for a in b.BaseAnchor:
-          a.prune_hints()
+          if a:
+            a.prune_hints()
     return True
 
 @_add_method(otTables.MarkLigPos)
@@ -352,11 +332,13 @@
 def prune_post_subset(self, options):
     if not options.hinting:
       for m in self.MarkArray.MarkRecord:
-        m.MarkAnchor.prune_hints()
+        if m.MarkAnchor:
+          m.MarkAnchor.prune_hints()
       for l in self.LigatureArray.LigatureAttach:
         for c in l.ComponentRecord:
           for a in c.LigatureAnchor:
-            a.prune_hints()
+            if a:
+              a.prune_hints()
     return True
 
 @_add_method(otTables.MarkMarkPos)
@@ -388,10 +370,12 @@
     if not options.hinting:
       # Drop device tables or contour anchor point
       for m in self.Mark1Array.MarkRecord:
-        m.MarkAnchor.prune_hints()
+        if m.MarkAnchor:
+          m.MarkAnchor.prune_hints()
       for b in self.Mark2Array.Mark2Record:
         for m in b.Mark2Anchor:
-          m.prune_hints()
+          if m:
+            m.prune_hints()
     return True
 
 @_add_method(otTables.SingleSubst,
@@ -551,6 +535,7 @@
         self.Intersect = lambda glyphs, c, r: c.intersect_class(glyphs, r)
 
         self.ClassDef = 'InputClassDef' if Chain else 'ClassDef'
+        self.ClassDefIndex = 1 if Chain else 0
         self.Input = 'Input' if Chain else 'Class'
 
   if self.Format not in [1, 2, 3]:
@@ -565,7 +550,7 @@
 @_add_method(otTables.ContextSubst,
              otTables.ChainContextSubst)
 def closure_glyphs(self, s, cur_glyphs=None):
-  if cur_glyphs == None: cur_glyphs = s.glyphs
+  if cur_glyphs is None: cur_glyphs = s.glyphs
   c = self.__classify_context()
 
   indices = c.Coverage(self).intersect(s.glyphs)
@@ -576,8 +561,9 @@
   if self.Format == 1:
     ContextData = c.ContextData(self)
     rss = getattr(self, c.RuleSet)
+    rssCount = getattr(self, c.RuleSetCount)
     for i in indices:
-      if not rss[i]: continue
+      if i >= rssCount or not rss[i]: continue
       for r in getattr(rss[i], c.Rule):
         if not r: continue
         if all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
@@ -601,8 +587,9 @@
     indices = ClassDef.intersect(cur_glyphs)
     ContextData = c.ContextData(self)
     rss = getattr(self, c.RuleSet)
+    rssCount = getattr(self, c.RuleSetCount)
     for i in indices:
-      if not rss[i]: continue
+      if i >= rssCount or not rss[i]: continue
       for r in getattr(rss[i], c.Rule):
         if not r: continue
         if all(all(c.Intersect(s.glyphs, cd, k) for k in klist)
@@ -670,12 +657,21 @@
   elif self.Format == 2:
     if not self.Coverage.subset(s.glyphs):
       return False
-    indices = getattr(self, c.ClassDef).subset(self.Coverage.glyphs,
-                                                 remap=False)
-    rss = getattr(self, c.RuleSet)
-    rss = [rss[i] for i in indices]
     ContextData = c.ContextData(self)
     klass_maps = [x.subset(s.glyphs, remap=True) for x in ContextData]
+
+    # Keep rulesets for class numbers that survived.
+    indices = klass_maps[c.ClassDefIndex]
+    rss = getattr(self, c.RuleSet)
+    rssCount = getattr(self, c.RuleSetCount)
+    rss = [rss[i] for i in indices if i < rssCount]
+    del rssCount
+    # Delete, but not renumber, unreachable rulesets.
+    indices = getattr(self, c.ClassDef).intersect(self.Coverage.glyphs)
+    rss = [rss if i in indices else None for i,rss in enumerate(rss)]
+    while rss and rss[-1] is None:
+      del rss[-1]
+
     for rs in rss:
       if not rs: continue
       ss = getattr(rs, c.Rule)
@@ -689,10 +685,6 @@
       for r in ss:
         c.SetRuleData(r, [[klass_map.index(k) for k in klist]
                for klass_map,klist in zip(klass_maps, c.RuleData(r))])
-    # Prune empty subrulesets
-    rss = [rs for rs in rss if rs and getattr(rs, c.Rule)]
-    setattr(self, c.RuleSet, rss)
-    setattr(self, c.RuleSetCount, len(rss))
     return bool(rss)
   elif self.Format == 3:
     return all(x.subset(s.glyphs) for x in c.RuleData(self))
@@ -864,6 +856,7 @@
 
 @_add_method(otTables.LookupList)
 def subset_lookups(self, lookup_indices):
+  self.ensureDecompiled()
   self.Lookup = [self.Lookup[i] for i in lookup_indices
                  if i < self.LookupCount]
   self.LookupCount = len(self.Lookup)
@@ -893,7 +886,7 @@
   self.LookupListIndex = [lookup_indices.index(l)
                           for l in self.LookupListIndex]
   self.LookupCount = len(self.LookupListIndex)
-  return self.LookupCount
+  return self.LookupCount or self.FeatureParams
 
 @_add_method(otTables.Feature)
 def collect_lookups(self):
@@ -902,8 +895,13 @@
 @_add_method(otTables.FeatureList)
 def subset_lookups(self, lookup_indices):
   "Returns the indices of nonempty features."
+  # Note: Never ever drop feature 'pref', even if it's empty.
+  # HarfBuzz chooses shaper for Khmer based on presence of this
+  # feature.  See thread at:
+  # http://lists.freedesktop.org/archives/harfbuzz/2012-November/002660.html
   feature_indices = [i for i,f in enumerate(self.FeatureRecord)
-                     if f.Feature.subset_lookups(lookup_indices)]
+                     if (f.Feature.subset_lookups(lookup_indices) or
+                         f.FeatureTag == 'pref')]
   self.subset_features(feature_indices)
   return feature_indices
 
@@ -915,6 +913,7 @@
 
 @_add_method(otTables.FeatureList)
 def subset_features(self, feature_indices):
+  self.ensureDecompiled()
   self.FeatureRecord = [self.FeatureRecord[i] for i in feature_indices]
   self.FeatureCount = len(self.FeatureRecord)
   return bool(self.FeatureCount)
@@ -973,23 +972,33 @@
 @_add_method(ttLib.getTableClass('GSUB'))
 def closure_glyphs(self, s):
   s.table = self.table
-  feature_indices = self.table.ScriptList.collect_features()
-  lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
-  while True:
-    orig_glyphs = s.glyphs.copy()
-    for i in lookup_indices:
-      if i >= self.table.LookupList.LookupCount: continue
-      if not self.table.LookupList.Lookup[i]: continue
-      self.table.LookupList.Lookup[i].closure_glyphs(s)
-    if orig_glyphs == s.glyphs:
-      break
+  if self.table.ScriptList:
+    feature_indices = self.table.ScriptList.collect_features()
+  else:
+    feature_indices = []
+  if self.table.FeatureList:
+    lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
+  else:
+    lookup_indices = []
+  if self.table.LookupList:
+    while True:
+      orig_glyphs = s.glyphs.copy()
+      for i in lookup_indices:
+        if i >= self.table.LookupList.LookupCount: continue
+        if not self.table.LookupList.Lookup[i]: continue
+        self.table.LookupList.Lookup[i].closure_glyphs(s)
+      if orig_glyphs == s.glyphs:
+        break
   del s.table
 
 @_add_method(ttLib.getTableClass('GSUB'),
              ttLib.getTableClass('GPOS'))
 def subset_glyphs(self, s):
   s.glyphs = s.glyphs_gsubed
-  lookup_indices = self.table.LookupList.subset_glyphs(s)
+  if self.table.LookupList:
+    lookup_indices = self.table.LookupList.subset_glyphs(s)
+  else:
+    lookup_indices = []
   self.subset_lookups(lookup_indices)
   self.prune_lookups()
   return True
@@ -997,43 +1006,139 @@
 @_add_method(ttLib.getTableClass('GSUB'),
              ttLib.getTableClass('GPOS'))
 def subset_lookups(self, lookup_indices):
-  """Retrains specified lookups, then removes empty features, language
+  """Retains specified lookups, then removes empty features, language
      systems, and scripts."""
-  self.table.LookupList.subset_lookups(lookup_indices)
-  feature_indices = self.table.FeatureList.subset_lookups(lookup_indices)
-  self.table.ScriptList.subset_features(feature_indices)
+  if self.table.LookupList:
+    self.table.LookupList.subset_lookups(lookup_indices)
+  if self.table.FeatureList:
+    feature_indices = self.table.FeatureList.subset_lookups(lookup_indices)
+  else:
+    feature_indices = []
+  if self.table.ScriptList:
+    self.table.ScriptList.subset_features(feature_indices)
 
 @_add_method(ttLib.getTableClass('GSUB'),
              ttLib.getTableClass('GPOS'))
 def prune_lookups(self):
   "Remove unreferenced lookups"
-  feature_indices = self.table.ScriptList.collect_features()
-  lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
-  lookup_indices = self.table.LookupList.closure_lookups(lookup_indices)
+  if self.table.ScriptList:
+    feature_indices = self.table.ScriptList.collect_features()
+  else:
+    feature_indices = []
+  if self.table.FeatureList:
+    lookup_indices = self.table.FeatureList.collect_lookups(feature_indices)
+  else:
+    lookup_indices = []
+  if self.table.LookupList:
+    lookup_indices = self.table.LookupList.closure_lookups(lookup_indices)
+  else:
+    lookup_indices = []
   self.subset_lookups(lookup_indices)
 
 @_add_method(ttLib.getTableClass('GSUB'),
              ttLib.getTableClass('GPOS'))
 def subset_feature_tags(self, feature_tags):
-  feature_indices = [i for i,f in
-                     enumerate(self.table.FeatureList.FeatureRecord)
-                     if f.FeatureTag in feature_tags]
-  self.table.FeatureList.subset_features(feature_indices)
-  self.table.ScriptList.subset_features(feature_indices)
+  if self.table.FeatureList:
+    feature_indices = [i for i,f in
+                       enumerate(self.table.FeatureList.FeatureRecord)
+                       if f.FeatureTag in feature_tags]
+    self.table.FeatureList.subset_features(feature_indices)
+  else:
+    feature_indices = []
+  if self.table.ScriptList:
+    self.table.ScriptList.subset_features(feature_indices)
+
+@_add_method(ttLib.getTableClass('GSUB'),
+             ttLib.getTableClass('GPOS'))
+def prune_features(self):
+  "Remove unreferenced featurs"
+  if self.table.ScriptList:
+    feature_indices = self.table.ScriptList.collect_features()
+  else:
+    feature_indices = []
+  if self.table.FeatureList:
+    self.table.FeatureList.subset_features(feature_indices)
+  if self.table.ScriptList:
+    self.table.ScriptList.subset_features(feature_indices)
 
 @_add_method(ttLib.getTableClass('GSUB'),
              ttLib.getTableClass('GPOS'))
 def prune_pre_subset(self, options):
+  # Drop undesired features
   if '*' not in options.layout_features:
     self.subset_feature_tags(options.layout_features)
+  # Drop unreferenced lookups
   self.prune_lookups()
-  self.table.LookupList.prune_pre_subset(options);
+  # Prune lookups themselves
+  if self.table.LookupList:
+    self.table.LookupList.prune_pre_subset(options);
   return True
 
 @_add_method(ttLib.getTableClass('GSUB'),
              ttLib.getTableClass('GPOS'))
+def remove_redundant_langsys(self):
+  table = self.table
+  if not table.ScriptList or not table.FeatureList:
+    return
+
+  features = table.FeatureList.FeatureRecord
+
+  for s in table.ScriptList.ScriptRecord:
+    d = s.Script.DefaultLangSys
+    if not d:
+      continue
+    for lr in s.Script.LangSysRecord[:]:
+      l = lr.LangSys
+      # Compare d and l
+      if len(d.FeatureIndex) != len(l.FeatureIndex):
+        continue
+      if (d.ReqFeatureIndex == 65535) != (l.ReqFeatureIndex == 65535):
+        continue
+
+      if d.ReqFeatureIndex != 65535:
+        if features[d.ReqFeatureIndex] != features[l.ReqFeatureIndex]:
+          continue
+
+      for i in range(len(d.FeatureIndex)):
+        if features[d.FeatureIndex[i]] != features[l.FeatureIndex[i]]:
+          break
+      else:
+        # LangSys and default are equal; delete LangSys
+        s.Script.LangSysRecord.remove(lr)
+
+@_add_method(ttLib.getTableClass('GSUB'),
+             ttLib.getTableClass('GPOS'))
 def prune_post_subset(self, options):
-  self.table.LookupList.prune_post_subset(options);
+  table = self.table
+
+  # LookupList looks good.  Just prune lookups themselves
+  if table.LookupList:
+    table.LookupList.prune_post_subset(options);
+    # XXX Next two lines disabled because OTS is stupid and
+    # doesn't like NULL offsetse here.
+    #if not table.LookupList.Lookup:
+    #  table.LookupList = None
+
+  if not table.LookupList:
+    table.FeatureList = None
+
+  if table.FeatureList:
+    self.remove_redundant_langsys()
+    # Remove unreferenced features
+    self.prune_features()
+
+  # XXX Next two lines disabled because OTS is stupid and
+  # doesn't like NULL offsetse here.
+  #if table.FeatureList and not table.FeatureList.FeatureRecord:
+  #  table.FeatureList = None
+
+  # Never drop scripts themselves as them just being available
+  # holds semantic significance.
+  # XXX Next two lines disabled because OTS is stupid and
+  # doesn't like NULL offsetse here.
+  #if table.ScriptList and not table.ScriptList.ScriptRecord:
+  #  table.ScriptList = None
+
   return True
 
 @_add_method(ttLib.getTableClass('GDEF'))
@@ -1045,33 +1150,52 @@
     table.LigCaretList.LigGlyph = [table.LigCaretList.LigGlyph[i]
                                    for i in indices]
     table.LigCaretList.LigGlyphCount = len(table.LigCaretList.LigGlyph)
-    if not table.LigCaretList.LigGlyphCount:
-      table.LigCaretList = None
   if table.MarkAttachClassDef:
     table.MarkAttachClassDef.classDefs = dict((g,v) for g,v in
                                               table.MarkAttachClassDef.
-                                                classDefs.iteritems()
+                                                classDefs.items()
                                               if g in glyphs)
-    if not table.MarkAttachClassDef.classDefs:
-      table.MarkAttachClassDef = None
   if table.GlyphClassDef:
     table.GlyphClassDef.classDefs = dict((g,v) for g,v in
                                          table.GlyphClassDef.
-                                           classDefs.iteritems()
+                                           classDefs.items()
                                          if g in glyphs)
-    if not table.GlyphClassDef.classDefs:
-      table.GlyphClassDef = None
   if table.AttachList:
     indices = table.AttachList.Coverage.subset(glyphs)
+    GlyphCount = table.AttachList.GlyphCount
     table.AttachList.AttachPoint = [table.AttachList.AttachPoint[i]
-                                    for i in indices]
+                                    for i in indices
+                                    if i < GlyphCount]
     table.AttachList.GlyphCount = len(table.AttachList.AttachPoint)
-    if not table.AttachList.GlyphCount:
-      table.AttachList = None
+  if hasattr(table, "MarkGlyphSetsDef") and table.MarkGlyphSetsDef:
+    for coverage in table.MarkGlyphSetsDef.Coverage:
+      coverage.subset(glyphs)
+    # TODO: The following is disabled.  If enabling, we need to go fixup all
+    # lookups that use MarkFilteringSet and map their set.
+    #indices = table.MarkGlyphSetsDef.Coverage = [c for c in table.MarkGlyphSetsDef.Coverage if c.glyphs]
+  return True
+
+@_add_method(ttLib.getTableClass('GDEF'))
+def prune_post_subset(self, options):
+  table = self.table
+  # XXX check these against OTS
+  if table.LigCaretList and not table.LigCaretList.LigGlyphCount:
+    table.LigCaretList = None
+  if table.MarkAttachClassDef and not table.MarkAttachClassDef.classDefs:
+    table.MarkAttachClassDef = None
+  if table.GlyphClassDef and not table.GlyphClassDef.classDefs:
+    table.GlyphClassDef = None
+  if table.AttachList and not table.AttachList.GlyphCount:
+    table.AttachList = None
+  if hasattr(table, "MarkGlyphSetsDef") and table.MarkGlyphSetsDef and not table.MarkGlyphSetsDef.Coverage:
+    table.MarkGlyphSetsDef = None
+    if table.Version == 0x00010002/0x10000:
+      table.Version = 1.0
   return bool(table.LigCaretList or
-               table.MarkAttachClassDef or
-               table.GlyphClassDef or
-               table.AttachList)
+              table.MarkAttachClassDef or
+              table.GlyphClassDef or
+              table.AttachList or
+              (table.Version >= 0x00010002/0x10000 and table.MarkGlyphSetsDef))
 
 @_add_method(ttLib.getTableClass('kern'))
 def prune_pre_subset(self, options):
@@ -1083,26 +1207,30 @@
 def subset_glyphs(self, s):
   glyphs = s.glyphs_gsubed
   for t in self.kernTables:
-    t.kernTable = dict(((a,b),v) for (a,b),v in t.kernTable.iteritems()
+    t.kernTable = dict(((a,b),v) for (a,b),v in t.kernTable.items()
                        if a in glyphs and b in glyphs)
   self.kernTables = [t for t in self.kernTables if t.kernTable]
   return bool(self.kernTables)
 
-@_add_method(ttLib.getTableClass('vmtx'),
-             ttLib.getTableClass('hmtx'))
+@_add_method(ttLib.getTableClass('vmtx'))
 def subset_glyphs(self, s):
-  self.metrics = dict((g,v) for g,v in self.metrics.iteritems() if g in s.glyphs)
+  self.metrics = dict((g,v) for g,v in self.metrics.items() if g in s.glyphs)
   return bool(self.metrics)
 
+@_add_method(ttLib.getTableClass('hmtx'))
+def subset_glyphs(self, s):
+  self.metrics = dict((g,v) for g,v in self.metrics.items() if g in s.glyphs)
+  return True # Required table
+
 @_add_method(ttLib.getTableClass('hdmx'))
 def subset_glyphs(self, s):
-  self.hdmx = dict((sz,_dict((g,v) for g,v in l.iteritems() if g in s.glyphs))
-                   for sz,l in self.hdmx.iteritems())
+  self.hdmx = dict((sz,dict((g,v) for g,v in l.items() if g in s.glyphs))
+                   for sz,l in self.hdmx.items())
   return bool(self.hdmx)
 
 @_add_method(ttLib.getTableClass('VORG'))
 def subset_glyphs(self, s):
-  self.VOriginRecords = dict((g,v) for g,v in self.VOriginRecords.iteritems()
+  self.VOriginRecords = dict((g,v) for g,v in self.VOriginRecords.items()
                              if g in s.glyphs)
   self.numVertOriginYMetrics = len(self.VOriginRecords)
   return True  # Never drop; has default metrics
@@ -1111,12 +1239,12 @@
 def prune_pre_subset(self, options):
   if not options.glyph_names:
     self.formatType = 3.0
-  return True
+  return True # Required table
 
 @_add_method(ttLib.getTableClass('post'))
 def subset_glyphs(self, s):
   self.extraNames = []  # This seems to do it
-  return True
+  return True # Required table
 
 @_add_method(ttLib.getTableModule('glyf').Glyph)
 def remapComponentsFast(self, indices):
@@ -1173,9 +1301,9 @@
 
 @_add_method(ttLib.getTableClass('glyf'))
 def subset_glyphs(self, s):
-  self.glyphs = dict((g,v) for g,v in self.glyphs.iteritems() if g in s.glyphs)
+  self.glyphs = dict((g,v) for g,v in self.glyphs.items() if g in s.glyphs)
   indices = [i for i,g in enumerate(self.glyphOrder) if g in s.glyphs]
-  for v in self.glyphs.itervalues():
+  for v in self.glyphs.values():
     if hasattr(v, "data"):
       v.remapComponentsFast(indices)
     else:
@@ -1187,7 +1315,7 @@
 @_add_method(ttLib.getTableClass('glyf'))
 def prune_post_subset(self, options):
   if not options.hinting:
-    for v in self.glyphs.itervalues():
+    for v in self.glyphs.values():
       v.removeHinting()
   return True
 
@@ -1230,11 +1358,11 @@
         sel.format = None
         sel.gidArray = [sel.gidArray[i] for i in indices]
       cs.charStrings = dict((g,indices.index(v))
-                            for g,v in cs.charStrings.iteritems()
+                            for g,v in cs.charStrings.items()
                             if g in s.glyphs)
     else:
       cs.charStrings = dict((g,v)
-                            for g,v in cs.charStrings.iteritems()
+                            for g,v in cs.charStrings.items()
                             if g in s.glyphs)
     font.charset = [g for g in font.charset if g in s.glyphs]
     font.numGlyphs = len(font.charset)
@@ -1245,12 +1373,12 @@
 def subset_subroutines(self, subrs, gsubrs):
   p = self.program
   assert len(p)
-  for i in xrange(1, len(p)):
+  for i in range(1, len(p)):
     if p[i] == 'callsubr':
-      assert type(p[i-1]) is int
+      assert isinstance(p[i-1], int)
       p[i-1] = subrs._used.index(p[i-1] + subrs._old_bias) - subrs._new_bias
     elif p[i] == 'callgsubr':
-      assert type(p[i-1]) is int
+      assert isinstance(p[i-1], int)
       p[i-1] = gsubrs._used.index(p[i-1] + gsubrs._old_bias) - gsubrs._new_bias
 
 @_add_method(psCharStrings.T2CharString)
@@ -1274,6 +1402,8 @@
         continue
       i += 1
 
+  # TODO: we currently don't drop calls to "empty" subroutines.
+
   assert len(self.program)
 
   del self._hints
@@ -1298,7 +1428,7 @@
 
 class _DehintingT2Decompiler(psCharStrings.SimpleT2Decompiler):
 
-  class Hints:
+  class Hints(object):
     def __init__(self):
       # Whether calling this charstring produces any hint stems
       self.has_hint = False
@@ -1335,8 +1465,8 @@
 
     if hints.status != 2:
       # Check from last_check, make sure we didn't have any operators.
-      for i in xrange(hints.last_checked, len(charString.program) - 1):
-        if type(charString.program[i]) == str:
+      for i in range(hints.last_checked, len(charString.program) - 1):
+        if isinstance(charString.program[i], str):
           hints.status = 2
           break;
         else:
@@ -1381,15 +1511,15 @@
     hints.has_hintmask = True
     if hints.status != 2 and hints.has_hint:
       # Check from last_check, see if we may be an implicit vstem
-      for i in xrange(hints.last_checked, index - 1):
-        if type(cs.program[i]) == str:
+      for i in range(hints.last_checked, index - 1):
+        if isinstance(cs.program[i], str):
           hints.status = 2
           break;
       if hints.status != 2:
         # We are an implicit vstem
         hints.last_hint = index + 1
         hints.status = 0
-      hints.last_checked = index + 1
+    hints.last_checked = index + 1
 
   def processHint(self, index):
     cs = self.callingStack[-1]
@@ -1424,11 +1554,17 @@
       if hints.status != 2:
         # Check from last_check, make sure we didn't have
         # any operators.
-        for i in xrange(hints.last_checked, index - 1):
-          if type(cs.program[i]) == str:
+        for i in range(hints.last_checked, index - 1):
+          if isinstance(cs.program[i], str):
             hints.status = 2
             break;
         hints.last_checked = index
+      if hints.status != 2:
+        # Decide where to chop off from
+        if subr_hints.status == 0:
+          hints.last_hint = index
+        else:
+          hints.last_hint = index - 2 # Leave the subr call in
 
 @_add_method(ttLib.getTableClass('CFF '))
 def prune_post_subset(self, options):
@@ -1492,9 +1628,9 @@
       else:
         all_privs.append(font.Private)
       for priv in all_privs:
-        priv.BlueValues = []
-        for k in ['OtherBlues', 'StemSnapH', 'StemSnapV', 'StdHW', 'StdVW', \
-                  'FamilyBlues', 'FamilyOtherBlues']:
+        for k in ['BlueValues', 'OtherBlues', 'FamilyBlues', 'FamilyOtherBlues',
+                  'BlueScale', 'BlueShift', 'BlueFuzz',
+                  'StemSnapH', 'StemSnapV', 'StdHW', 'StdVW']:
           if hasattr(priv, k):
             setattr(priv, k, None)
 
@@ -1549,7 +1685,7 @@
       if hasattr(subrs, 'offsets'):
         del subrs.offsets
 
-      for i in xrange (subrs.count):
+      for i in range (subrs.count):
         subrs[i].subset_subroutines (local_subrs, font.GlobalSubrs)
 
     # Cleanup
@@ -1585,7 +1721,7 @@
   # For now, drop format=0 which can't be subset_glyphs easily?
   self.tables = [t for t in self.tables if t.format != 0]
   self.numSubTables = len(self.tables)
-  return bool(self.tables)
+  return True # Required table
 
 @_add_method(ttLib.getTableClass('cmap'))
 def subset_glyphs(self, s):
@@ -1600,10 +1736,10 @@
     if t.format == 14:
       # TODO(behdad) XXX We drop all the default-UVS mappings(g==None).
       t.uvsDict = dict((v,[(u,g) for u,g in l if g in s.glyphs])
-                       for v,l in t.uvsDict.iteritems())
-      t.uvsDict = dict((v,l) for v,l in t.uvsDict.iteritems() if l)
+                       for v,l in t.uvsDict.items())
+      t.uvsDict = dict((v,l) for v,l in t.uvsDict.items() if l)
     else:
-      t.cmap = dict((u,g) for u,g in t.cmap.iteritems()
+      t.cmap = dict((u,g) for u,g in t.cmap.items()
                     if g in s.glyphs_requested or u in s.unicodes_requested)
   self.tables = [t for t in self.tables
                  if (t.cmap if t.format != 14 else t.uvsDict)]
@@ -1612,7 +1748,7 @@
   # In particular, if we have a format=12 without non-BMP
   # characters, either drop format=12 one or convert it
   # to format=4 if there's not one.
-  return bool(self.tables)
+  return True # Required table
 
 @_add_method(ttLib.getTableClass('name'))
 def prune_pre_subset(self, options):
@@ -1623,16 +1759,21 @@
                   if n.platformID == 3 and n.platEncID == 1]
   if '*' not in options.name_languages:
     self.names = [n for n in self.names if n.langID in options.name_languages]
-  return True  # Retain even if empty
+  return True  # Required table
 
 
 # TODO(behdad) OS/2 ulUnicodeRange / ulCodePageRange?
+# TODO(behdad) Drop AAT tables.
 # TODO(behdad) Drop unneeded GSUB/GPOS Script/LangSys entries.
 # TODO(behdad) Drop empty GSUB/GPOS, and GDEF if no GSUB/GPOS left
 # TODO(behdad) Drop GDEF subitems if unused by lookups
 # TODO(behdad) Avoid recursing too much (in GSUB/GPOS and in CFF)
 # TODO(behdad) Text direction considerations.
 # TODO(behdad) Text script / language considerations.
+# TODO(behdad) Optionally drop 'kern' table if GPOS available
+# TODO(behdad) Implement --unicode='*' to choose all cmap'ed
+# TODO(behdad) Drop old-spec Indic scripts
+
 
 class Options(object):
 
@@ -1659,19 +1800,19 @@
     'arabic': ['init', 'medi', 'fina', 'isol', 'med2', 'fin2', 'fin3',
                'cswh', 'mset'],
     'hangul': ['ljmo', 'vjmo', 'tjmo'],
-    'tibetal': ['abvs', 'blws', 'abvm', 'blwm'],
+    'tibetan': ['abvs', 'blws', 'abvm', 'blwm'],
     'indic': ['nukt', 'akhn', 'rphf', 'rkrf', 'pref', 'blwf', 'half',
               'abvf', 'pstf', 'cfar', 'vatu', 'cjct', 'init', 'pres',
               'abvs', 'blws', 'psts', 'haln', 'dist', 'abvm', 'blwm'],
   }
   _layout_features_default = _uniq_sort(sum(
-      _layout_features_groups.itervalues(), []))
+      iter(_layout_features_groups.values()), []))
 
   drop_tables = _drop_tables_default
   no_subset_tables = _no_subset_tables_default
   hinting_tables = _hinting_tables_default
   layout_features = _layout_features_default
-  hinting = False
+  hinting = True
   glyph_names = False
   legacy_cmap = False
   symbol_cmap = False
@@ -1690,7 +1831,7 @@
     self.set(**kwargs)
 
   def set(self, **kwargs):
-    for k,v in kwargs.iteritems():
+    for k,v in kwargs.items():
       if not hasattr(self, k):
         raise self.UnknownOptionError("Unknown option '%s'" % k)
       setattr(self, k, v)
@@ -1721,7 +1862,7 @@
         v = a[i+1:]
       k = k.replace('-', '_')
       if not hasattr(self, k):
-        if ignore_unknown == True or k in ignore_unknown:
+        if ignore_unknown is True or k in ignore_unknown:
           ret.append(orig_a)
           continue
         else:
@@ -1748,7 +1889,7 @@
             if x in v:
               v.remove(x)
         else:
-          assert 0
+          assert False
 
       opts[k] = v
     self.set(**opts)
@@ -1773,7 +1914,7 @@
 
   def populate(self, glyphs=[], unicodes=[], text=""):
     self.unicodes_requested.update(unicodes)
-    if isinstance(text, str):
+    if isinstance(text, bytes):
       text = text.decode("utf8")
     for u in text:
       self.unicodes_requested.add(ord(u))
@@ -1807,10 +1948,13 @@
 
   def _closure_glyphs(self, font):
 
+    realGlyphs = set(font.getGlyphOrder())
+
     self.glyphs = self.glyphs_requested.copy()
 
     if 'cmap' in font:
       font['cmap'].closure_glyphs(self)
+      self.glyphs.intersection_update(realGlyphs)
     self.glyphs_cmaped = self.glyphs
 
     if self.options.notdef_glyph:
@@ -1822,7 +1966,7 @@
         self.log("Added .notdef to subset")
     if self.options.recommended_glyphs:
       if 'glyf' in font:
-        for i in range(4):
+        for i in range(min(4, len(font.getGlyphOrder()))):
           self.glyphs.add(font.getGlyphName(i))
         self.log("Added first four glyphs to subset")
 
@@ -1831,6 +1975,7 @@
                 len(self.glyphs))
       self.log.glyphs(self.glyphs, font=font)
       font['GSUB'].closure_glyphs(self)
+      self.glyphs.intersection_update(realGlyphs)
       self.log("Closed  glyph list over 'GSUB': %d glyphs after" %
                 len(self.glyphs))
       self.log.glyphs(self.glyphs, font=font)
@@ -1842,6 +1987,7 @@
                 len(self.glyphs))
       self.log.glyphs(self.glyphs, font=font)
       font['glyf'].closure_glyphs(self)
+      self.glyphs.intersection_update(realGlyphs)
       self.log("Closed  glyph list over 'glyf': %d glyphs after" %
                 len(self.glyphs))
       self.log.glyphs(self.glyphs, font=font)
@@ -1852,6 +1998,9 @@
 
     self.log("Retaining %d glyphs: " % len(self.glyphs_all))
 
+    del self.glyphs
+
+
   def _subset_glyphs(self, font):
     for tag in font.keys():
       if tag == 'GlyphOrder': continue
@@ -1863,7 +2012,7 @@
         table = font[tag]
         self.glyphs = self.glyphs_all
         retain = table.subset_glyphs(self)
-        self.glyphs = self.glyphs_all
+        del self.glyphs
         self.log.lapse("subset '%s'" % tag)
         if not retain:
           self.log(tag, "subsetted to empty; dropped")
@@ -1921,17 +2070,19 @@
   def __call__(self, *things):
     if not self.verbose:
       return
-    print ' '.join(str(x) for x in things)
+    print(' '.join(str(x) for x in things))
 
   def lapse(self, *things):
     if not self.timing:
       return
     new_time = time.time()
-    print "Took %0.3fs to %s" %(new_time - self.last_time,
-                                 ' '.join(str(x) for x in things))
+    print("Took %0.3fs to %s" %(new_time - self.last_time,
+                                 ' '.join(str(x) for x in things)))
     self.last_time = new_time
 
   def glyphs(self, glyphs, font=None):
+    if not self.verbose:
+      return
     self("Names: ", sorted(glyphs))
     if font:
       reverseGlyphMap = font.getReverseGlyphMap()
@@ -1942,7 +2093,6 @@
       return
     from fontTools.misc import xmlWriter
     writer = xmlWriter.XMLWriter(file)
-    font.disassembleInstructions = False  # Work around ttLib bug
     for tag in font.keys():
       writer.begintag(tag)
       writer.newline()
@@ -1953,12 +2103,16 @@
 
 def load_font(fontFile,
               options,
+              allowVID=False,
               checkChecksums=False,
-              dontLoadGlyphNames=False):
+              dontLoadGlyphNames=False,
+              lazy=True):
 
   font = ttLib.TTFont(fontFile,
-                                checkChecksums=checkChecksums,
-                                recalcBBoxes=options.recalc_bounds)
+                      allowVID=allowVID,
+                      checkChecksums=checkChecksums,
+                      recalcBBoxes=options.recalc_bounds,
+                      lazy=lazy)
 
   # Hack:
   #
@@ -1995,7 +2149,7 @@
   args = options.parse_opts(args, ignore_unknown=['text'])
 
   if len(args) < 2:
-    print >>sys.stderr, "usage: pyftsubset font-file glyph... [--text=ABC]... [--option=value]..."
+    print("usage: pyftsubset font-file glyph... [--text=ABC]... [--option=value]...", file=sys.stderr)
     sys.exit(1)
 
   fontfile = args[0]
@@ -2040,7 +2194,7 @@
       elif g.startswith('glyph') and len(g) > 5:
         g = g[5:]
       try:
-        glyphs.append(font.getGlyphName(int(g), requireReal=1))
+        glyphs.append(font.getGlyphName(int(g), requireReal=True))
       except ValueError:
         raise Exception("Invalid glyph identifier: %s" % g)
       continue
diff --git a/Lib/fontTools/t1Lib.py b/Lib/fontTools/t1Lib.py
index 42d95c2..abd4f32 100644
--- a/Lib/fontTools/t1Lib.py
+++ b/Lib/fontTools/t1Lib.py
@@ -8,24 +8,24 @@
 	to by 'path'. 
 	Raises an error when the file does not contain valid Type 1 data.
 
-write(path, data, kind='OTHER', dohex=0)
+write(path, data, kind='OTHER', dohex=False)
 	writes raw Type 1 data to the file pointed to by 'path'. 
 	'kind' can be one of 'LWFN', 'PFB' or 'OTHER'; it defaults to 'OTHER'.
 	'dohex' is a flag which determines whether the eexec encrypted
 	part should be written as hexadecimal or binary, but only if kind
 	is 'LWFN' or 'PFB'.
 """
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import eexec
+from fontTools.misc.macCreatorType import getMacCreatorAndType
+import os
+import re
 
 __author__ = "jvr"
 __version__ = "1.0b2"
 DEBUG = 0
 
-from fontTools.misc import eexec
-from fontTools.misc.macCreatorType import getMacCreatorAndType
-import string
-import re
-import os
-
 
 try:
 	try:
@@ -42,7 +42,7 @@
 class T1Error(Exception): pass
 
 
-class T1Font:
+class T1Font(object):
 	
 	"""Type 1 font class.
 	
@@ -100,20 +100,20 @@
 
 # low level T1 data read and write functions
 
-def read(path, onlyHeader=0):
+def read(path, onlyHeader=False):
 	"""reads any Type 1 font file, returns raw data"""
-	normpath = string.lower(path)
-	creator, type = getMacCreatorAndType(path)
-	if type == 'LWFN':
+	normpath = path.lower()
+	creator, typ = getMacCreatorAndType(path)
+	if typ == 'LWFN':
 		return readLWFN(path, onlyHeader), 'LWFN'
 	if normpath[-4:] == '.pfb':
 		return readPFB(path, onlyHeader), 'PFB'
 	else:
 		return readOther(path), 'OTHER'
 
-def write(path, data, kind='OTHER', dohex=0):
+def write(path, data, kind='OTHER', dohex=False):
 	assertType1(data)
-	kind = string.upper(kind)
+	kind = kind.upper()
 	try:
 		os.remove(path)
 	except os.error:
@@ -141,7 +141,7 @@
 HEXLINELENGTH = 80
 
 
-def readLWFN(path, onlyHeader=0):
+def readLWFN(path, onlyHeader=False):
 	"""reads an LWFN font file, returns raw data"""
 	resRef = Res.FSOpenResFile(path, 1)  # read-only
 	try:
@@ -150,9 +150,9 @@
 		data = []
 		for i in range(501, 501 + n):
 			res = Res.Get1Resource('POST', i)
-			code = ord(res.data[0])
-			if ord(res.data[1]) <> 0:
-				raise T1Error, 'corrupt LWFN file'
+			code = byteord(res.data[0])
+			if byteord(res.data[1]) != 0:
+				raise T1Error('corrupt LWFN file')
 			if code in [1, 2]:
 				if onlyHeader and code == 2:
 					break
@@ -166,21 +166,21 @@
 			elif code == 0:
 				pass # comment, ignore
 			else:
-				raise T1Error, 'bad chunk code: ' + `code`
+				raise T1Error('bad chunk code: ' + repr(code))
 	finally:
 		Res.CloseResFile(resRef)
-	data = string.join(data, '')
+	data = bytesjoin(data)
 	assertType1(data)
 	return data
 
-def readPFB(path, onlyHeader=0):
+def readPFB(path, onlyHeader=False):
 	"""reads a PFB font file, returns raw data"""
 	f = open(path, "rb")
 	data = []
-	while 1:
-		if f.read(1) <> chr(128):
-			raise T1Error, 'corrupt PFB file'
-		code = ord(f.read(1))
+	while True:
+		if f.read(1) != bytechr(128):
+			raise T1Error('corrupt PFB file')
+		code = byteord(f.read(1))
 		if code in [1, 2]:
 			chunklen = stringToLong(f.read(4))
 			chunk = f.read(chunklen)
@@ -189,11 +189,11 @@
 		elif code == 3:
 			break
 		else:
-			raise T1Error, 'bad chunk code: ' + `code`
+			raise T1Error('bad chunk code: ' + repr(code))
 		if onlyHeader:
 			break
 	f.close()
-	data = string.join(data, '')
+	data = bytesjoin(data)
 	assertType1(data)
 	return data
 
@@ -211,7 +211,7 @@
 			data.append(deHexString(chunk))
 		else:
 			data.append(chunk)
-	return string.join(data, '')
+	return bytesjoin(data)
 
 # file writing tools
 
@@ -228,11 +228,11 @@
 			else:
 				code = 1
 			while chunk:
-				res = Res.Resource(chr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2])
+				res = Res.Resource(bytechr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2])
 				res.AddResource('POST', resID, '')
 				chunk = chunk[LWFNCHUNKSIZE - 2:]
 				resID = resID + 1
-		res = Res.Resource(chr(5) + '\0')
+		res = Res.Resource(bytechr(5) + '\0')
 		res.AddResource('POST', resID, '')
 	finally:
 		Res.CloseResFile(resRef)
@@ -246,18 +246,18 @@
 				code = 2
 			else:
 				code = 1
-			f.write(chr(128) + chr(code))
+			f.write(bytechr(128) + bytechr(code))
 			f.write(longToString(len(chunk)))
 			f.write(chunk)
-		f.write(chr(128) + chr(3))
+		f.write(bytechr(128) + bytechr(3))
 	finally:
 		f.close()
 
-def writeOther(path, data, dohex = 0):
+def writeOther(path, data, dohex=False):
 	chunks = findEncryptedChunks(data)
 	f = open(path, "wb")
 	try:
-		hexlinelen = HEXLINELENGTH / 2
+		hexlinelen = HEXLINELENGTH // 2
 		for isEncrypted, chunk in chunks:
 			if isEncrypted:
 				code = 2
@@ -297,9 +297,9 @@
 				chunk = deHexString(chunk)
 			decrypted, R = eexec.decrypt(chunk, 55665)
 			decrypted = decrypted[4:]
-			if decrypted[-len(EEXECINTERNALEND)-1:-1] <> EEXECINTERNALEND \
-					and decrypted[-len(EEXECINTERNALEND)-2:-2] <> EEXECINTERNALEND:
-				raise T1Error, "invalid end of eexec part"
+			if decrypted[-len(EEXECINTERNALEND)-1:-1] != EEXECINTERNALEND \
+					and decrypted[-len(EEXECINTERNALEND)-2:-2] != EEXECINTERNALEND:
+				raise T1Error("invalid end of eexec part")
 			decrypted = decrypted[:-len(EEXECINTERNALEND)-2] + '\r'
 			data.append(EEXECBEGINMARKER + decrypted + EEXECENDMARKER)
 		else:
@@ -307,25 +307,25 @@
 				data.append(chunk[:-len(EEXECBEGIN)-1])
 			else:
 				data.append(chunk)
-	return string.join(data, '')
+	return bytesjoin(data)
 
 def findEncryptedChunks(data):
 	chunks = []
-	while 1:
-		eBegin = string.find(data, EEXECBEGIN)
+	while True:
+		eBegin = data.find(EEXECBEGIN)
 		if eBegin < 0:
 			break
 		eBegin = eBegin + len(EEXECBEGIN) + 1
-		eEnd = string.find(data, EEXECEND, eBegin)
+		eEnd = data.find(EEXECEND, eBegin)
 		if eEnd < 0:
-			raise T1Error, "can't find end of eexec part"
+			raise T1Error("can't find end of eexec part")
 		cypherText = data[eBegin:eEnd + 2]
 		if isHex(cypherText[:4]):
 			cypherText = deHexString(cypherText)
 		plainText, R = eexec.decrypt(cypherText, 55665)
-		eEndLocal = string.find(plainText, EEXECINTERNALEND)
+		eEndLocal = plainText.find(EEXECINTERNALEND)
 		if eEndLocal < 0:
-			raise T1Error, "can't find end of eexec part"
+			raise T1Error("can't find end of eexec part")
 		chunks.append((0, data[:eBegin]))
 		chunks.append((1, cypherText[:eEndLocal + len(EEXECINTERNALEND) + 1]))
 		data = data[eEnd:]
@@ -333,23 +333,23 @@
 	return chunks
 
 def deHexString(hexstring):
-	return eexec.deHexString(string.join(string.split(hexstring), ""))
+	return eexec.deHexString(strjoin(hexstring.split()))
 
 
 # Type 1 assertion
 
-_fontType1RE = re.compile(r"/FontType\s+1\s+def")
+_fontType1RE = re.compile(br"/FontType\s+1\s+def")
 
 def assertType1(data):
-	for head in ['%!PS-AdobeFont', '%!FontType1']:
+	for head in [b'%!PS-AdobeFont', b'%!FontType1']:
 		if data[:len(head)] == head:
 			break
 	else:
-		raise T1Error, "not a PostScript font"
+		raise T1Error("not a PostScript font")
 	if not _fontType1RE.search(data):
-		raise T1Error, "not a Type 1 font"
-	if string.find(data, "currentfile eexec") < 0:
-		raise T1Error, "not an encrypted Type 1 font"
+		raise T1Error("not a Type 1 font")
+	if data.find(b"currentfile eexec") < 0:
+		raise T1Error("not an encrypted Type 1 font")
 	# XXX what else?
 	return data
 
@@ -357,16 +357,16 @@
 # pfb helpers
 
 def longToString(long):
-	str = ""
+	s = ""
 	for i in range(4):
-		str = str + chr((long & (0xff << (i * 8))) >> i * 8)
-	return str
+		s += bytechr((long & (0xff << (i * 8))) >> i * 8)
+	return s
 
-def stringToLong(str):
-	if len(str) <> 4:
-		raise ValueError, 'string must be 4 bytes long'
-	long = 0
+def stringToLong(s):
+	if len(s) != 4:
+		raise ValueError('string must be 4 bytes long')
+	l = 0
 	for i in range(4):
-		long = long + (ord(str[i]) << (i * 8))
-	return long
+		l += byteord(s[i]) << (i * 8)
+	return l
 
diff --git a/Lib/fontTools/ttLib/__init__.py b/Lib/fontTools/ttLib/__init__.py
index b1456d9..59c12fd 100644
--- a/Lib/fontTools/ttLib/__init__.py
+++ b/Lib/fontTools/ttLib/__init__.py
@@ -45,9 +45,10 @@
 # $Id: __init__.py,v 1.51 2009-02-22 08:55:00 pabs3 Exp $
 #
 
-import sys
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 import os
-import string
+import sys
 
 haveMacSupport = 0
 if sys.platform == "mac":
@@ -60,7 +61,7 @@
 class TTLibError(Exception): pass
 
 
-class TTFont:
+class TTFont(object):
 	
 	"""The main font object. It manages file input and output, and offers
 	a convenient way of accessing tables. 
@@ -69,9 +70,9 @@
 	"""
 	
 	def __init__(self, file=None, res_name_or_index=None,
-			sfntVersion="\000\001\000\000", flavor=None, checkChecksums=0,
-			verbose=0, recalcBBoxes=1, allowVID=0, ignoreDecompileErrors=False,
-			fontNumber=-1):
+			sfntVersion="\000\001\000\000", flavor=None, checkChecksums=False,
+			verbose=False, recalcBBoxes=True, allowVID=False, ignoreDecompileErrors=False,
+			fontNumber=-1, lazy=False, quiet=False):
 		
 		"""The constructor can be called with a few different arguments.
 		When reading a font from disk, 'file' should be either a pathname
@@ -109,7 +110,7 @@
 		supported. Asking for a glyph ID with a glyph name or GID that is not in
 		the font will return a virtual GID.   This is valid for GSUB and cmap
 		tables. For SING glyphlets, the cmap table is used to specify Unicode
-		values for virtual GI's used in GSUB/GPOS rules. If the gid Nis requested
+		values for virtual GI's used in GSUB/GPOS rules. If the gid N is requested
 		and does not exist in the font, or the glyphname has the form glyphN
 		and does not exist in the font, then N is used as the virtual GID.
 		Else, the first virtual GID is assigned as 0x1000 -1; for subsequent new
@@ -119,10 +120,15 @@
 		individual tables during decompilation will be ignored, falling
 		back to the DefaultTable implementation, which simply keeps the
 		binary data.
+
+		If lazy is set to True, many data structures are loaded lazily, upon
+		access only.
 		"""
 		
 		from fontTools.ttLib import sfnt
 		self.verbose = verbose
+		self.quiet = quiet
+		self.lazy = lazy
 		self.recalcBBoxes = recalcBBoxes
 		self.tables = {}
 		self.reader = None
@@ -143,7 +149,7 @@
 			# assume file is a string
 			if haveMacSupport and res_name_or_index is not None:
 				# on the mac, we deal with sfnt resources as well as flat files
-				import macUtils
+				from . import macUtils
 				if res_name_or_index == 0:
 					if macUtils.getSFNTResIndices(file):
 						# get the first available sfnt font.
@@ -166,7 +172,7 @@
 		if self.reader is not None:
 			self.reader.close()
 	
-	def save(self, file, makeSuitcase=0, reorderTables=1):
+	def save(self, file, makeSuitcase=False, reorderTables=True):
 		"""Save the font to disk. Similarly to the constructor, 
 		the 'file' argument can be either a pathname or a writable
 		file object.
@@ -178,7 +184,7 @@
 		if not hasattr(file, "write"):
 			closeStream = 1
 			if os.name == "mac" and makeSuitcase:
-				import macUtils
+				from . import macUtils
 				file = macUtils.SFNTResourceWriter(file, self)
 			else:
 				file = open(file, "wb")
@@ -189,7 +195,7 @@
 			# assume "file" is a writable file object
 			closeStream = 0
 		
-		tags = self.keys()
+		tags = list(self.keys())
 		if "GlyphOrder" in tags:
 			tags.remove("GlyphOrder")
 		numTables = len(tags)
@@ -215,8 +221,8 @@
 		if closeStream:
 			file.close()
 	
-	def saveXML(self, fileOrPath, progress=None, 
-			tables=None, skipTables=None, splitTables=0, disassembleInstructions=1,
+	def saveXML(self, fileOrPath, progress=None, quiet=False,
+			tables=None, skipTables=None, splitTables=False, disassembleInstructions=True,
 			bitmapGlyphDataFormat='raw'):
 		"""Export the font as TTX (an XML-based text file), or as a series of text
 		files when splitTables is true. In the latter case, the 'fileOrPath'
@@ -231,7 +237,7 @@
 		self.disassembleInstructions = disassembleInstructions
 		self.bitmapGlyphDataFormat = bitmapGlyphDataFormat
 		if not tables:
-			tables = self.keys()
+			tables = list(self.keys())
 			if "GlyphOrder" not in tables:
 				tables = ["GlyphOrder"] + tables
 			if skipTables:
@@ -246,7 +252,7 @@
 			idlefunc = None
 		
 		writer = xmlWriter.XMLWriter(fileOrPath, idlefunc=idlefunc)
-		writer.begintag("ttFont", sfntVersion=`self.sfntVersion`[1:-1], 
+		writer.begintag("ttFont", sfntVersion=repr(self.sfntVersion)[1:-1], 
 				ttLibVersion=version)
 		writer.newline()
 		
@@ -271,7 +277,7 @@
 				writer.newline()
 			else:
 				tableWriter = writer
-			self._tableToXML(tableWriter, tag, progress)
+			self._tableToXML(tableWriter, tag, progress, quiet)
 			if splitTables:
 				tableWriter.endtag("ttFont")
 				tableWriter.newline()
@@ -284,8 +290,8 @@
 		if self.verbose:
 			debugmsg("Done dumping TTX")
 	
-	def _tableToXML(self, writer, tag, progress):
-		if self.has_key(tag):
+	def _tableToXML(self, writer, tag, progress, quiet):
+		if tag in self:
 			table = self[tag]
 			report = "Dumping '%s' table..." % tag
 		else:
@@ -295,8 +301,9 @@
 		elif self.verbose:
 			debugmsg(report)
 		else:
-			print report
-		if not self.has_key(tag):
+			if not quiet:
+				print(report)
+		if tag not in self:
 			return
 		xmlTag = tagToXML(tag)
 		if hasattr(table, "ERROR"):
@@ -312,40 +319,43 @@
 		writer.newline()
 		writer.newline()
 	
-	def importXML(self, file, progress=None):
+	def importXML(self, file, progress=None, quiet=False):
 		"""Import a TTX file (an XML-based text format), so as to recreate
 		a font object.
 		"""
-		if self.has_key("maxp") and self.has_key("post"):
+		if "maxp" in self and "post" in self:
 			# Make sure the glyph order is loaded, as it otherwise gets
 			# lost if the XML doesn't contain the glyph order, yet does
 			# contain the table which was originally used to extract the
 			# glyph names from (ie. 'post', 'cmap' or 'CFF ').
 			self.getGlyphOrder()
-		import xmlImport
-		xmlImport.importXML(self, file, progress)
+
+		from fontTools.misc import xmlReader
+
+		reader = xmlReader.XMLReader(file, self, progress, quiet)
+		reader.read()
 	
 	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)
+		return tag in self.tables
 	
 	def has_key(self, tag):
 		if self.isLoaded(tag):
-			return 1
-		elif self.reader and self.reader.has_key(tag):
-			return 1
+			return True
+		elif self.reader and tag in self.reader:
+			return True
 		elif tag == "GlyphOrder":
-			return 1
+			return True
 		else:
-			return 0
+			return False
 	
 	__contains__ = has_key
 	
 	def keys(self):
-		keys = self.tables.keys()
+		keys = list(self.tables.keys())
 		if self.reader:
-			for key in self.reader.keys():
+			for key in list(self.reader.keys()):
 				if key not in keys:
 					keys.append(key)
 
@@ -355,9 +365,10 @@
 		return ["GlyphOrder"] + keys
 	
 	def __len__(self):
-		return len(self.keys())
+		return len(list(self.keys()))
 	
 	def __getitem__(self, tag):
+		tag = Tag(tag)
 		try:
 			return self.tables[tag]
 		except KeyError:
@@ -381,10 +392,9 @@
 					if not self.ignoreDecompileErrors:
 						raise
 					# fall back to DefaultTable, retaining the binary table data
-					print "An exception occurred during the decompilation of the '%s' table" % tag
-					from tables.DefaultTable import DefaultTable
-					import StringIO
-					file = StringIO.StringIO()
+					print("An exception occurred during the decompilation of the '%s' table" % tag)
+					from .tables.DefaultTable import DefaultTable
+					file = StringIO()
 					traceback.print_exc(file=file)
 					table = DefaultTable(tag)
 					table.ERROR = file.getvalue()
@@ -392,17 +402,17 @@
 					table.decompile(data, self)
 				return table
 			else:
-				raise KeyError, "'%s' table not found" % tag
+				raise KeyError("'%s' table not found" % tag)
 	
 	def __setitem__(self, tag, table):
-		self.tables[tag] = table
+		self.tables[Tag(tag)] = table
 	
 	def __delitem__(self, tag):
-		if not self.has_key(tag):
-			raise KeyError, "'%s' table not found" % tag
-		if self.tables.has_key(tag):
+		if tag not in self:
+			raise KeyError("'%s' table not found" % tag)
+		if tag in self.tables:
 			del self.tables[tag]
-		if self.reader and self.reader.has_key(tag):
+		if self.reader and tag in self.reader:
 			del self.reader[tag]
 	
 	def setGlyphOrder(self, glyphOrder):
@@ -413,10 +423,10 @@
 			return self.glyphOrder
 		except AttributeError:
 			pass
-		if self.has_key('CFF '):
+		if 'CFF ' in self:
 			cff = self['CFF ']
 			self.glyphOrder = cff.getGlyphOrder()
-		elif self.has_key('post'):
+		elif 'post' in self:
 			# TrueType font
 			glyphOrder = self['post'].getGlyphOrder()
 			if glyphOrder is None:
@@ -476,24 +486,23 @@
 			cmap = tempcmap.cmap
 			# create a reverse cmap dict
 			reversecmap = {}
-			for unicode, name in cmap.items():
+			for unicode, name in list(cmap.items()):
 				reversecmap[name] = unicode
 			allNames = {}
 			for i in range(numGlyphs):
 				tempName = glyphOrder[i]
-				if reversecmap.has_key(tempName):
+				if tempName in reversecmap:
 					unicode = reversecmap[tempName]
-					if agl.UV2AGL.has_key(unicode):
+					if unicode in agl.UV2AGL:
 						# get name from the Adobe Glyph List
 						glyphName = agl.UV2AGL[unicode]
 					else:
 						# create uni<CODE> name
-						glyphName = "uni" + string.upper(string.zfill(
-								hex(unicode)[2:], 4))
+						glyphName = "uni%04X" % unicode
 					tempName = glyphName
 					n = 1
-					while allNames.has_key(tempName):
-						tempName = glyphName + "#" + `n`
+					while tempName in allNames:
+						tempName = glyphName + "#" + repr(n)
 						n = n + 1
 					glyphOrder[i] = tempName
 					allNames[tempName] = 1
@@ -510,8 +519,7 @@
 	
 	def getGlyphNames(self):
 		"""Get a list of glyph names, sorted alphabetically."""
-		glyphNames = self.getGlyphOrder()[:]
-		glyphNames.sort()
+		glyphNames = sorted(self.getGlyphOrder()[:])
 		return glyphNames
 	
 	def getGlyphNames2(self):
@@ -521,7 +529,7 @@
 		from fontTools.misc import textTools
 		return textTools.caselessSort(self.getGlyphOrder())
 	
-	def getGlyphName(self, glyphID, requireReal=0):
+	def getGlyphName(self, glyphID, requireReal=False):
 		try:
 			return self.getGlyphOrder()[glyphID]
 		except IndexError:
@@ -540,18 +548,25 @@
 					self.VIDDict[glyphID] = glyphName
 				return glyphName
 
-	def getGlyphID(self, glyphName, requireReal = 0):
+	def getGlyphID(self, glyphName, requireReal=False):
 		if not hasattr(self, "_reverseGlyphOrderDict"):
 			self._buildReverseGlyphOrderDict()
 		glyphOrder = self.getGlyphOrder()
 		d = self._reverseGlyphOrderDict
-		if not d.has_key(glyphName):
+		if glyphName not in d:
 			if glyphName in glyphOrder:
 				self._buildReverseGlyphOrderDict()
 				return self.getGlyphID(glyphName)
 			else:
-				if requireReal or not self.allowVID:
-					raise KeyError, glyphName
+				if requireReal:
+					raise KeyError(glyphName)
+				elif not self.allowVID:
+					# Handle glyphXXX only
+					if glyphName[:5] == "glyph":
+						try:
+							return int(glyphName[5:])
+						except (NameError, ValueError):
+							raise KeyError(glyphName)
 				else:
 					# user intends virtual GID support 	
 					try:
@@ -563,7 +578,7 @@
 								glyphID = int(glyphName[5:])
 							except (NameError, ValueError):
 								glyphID = None
-						if glyphID == None:
+						if glyphID is None:
 							glyphID = self.last_vid -1
 							self.last_vid = glyphID
 						self.reverseVIDDict[glyphName] = glyphID
@@ -571,12 +586,12 @@
 					return glyphID
 
 		glyphID = d[glyphName]
-		if glyphName <> glyphOrder[glyphID]:
+		if glyphName != glyphOrder[glyphID]:
 			self._buildReverseGlyphOrderDict()
 			return self.getGlyphID(glyphName)
 		return glyphID
 
-	def getReverseGlyphMap(self, rebuild=0):
+	def getReverseGlyphMap(self, rebuild=False):
 		if rebuild or not hasattr(self, "_reverseGlyphOrderDict"):
 			self._buildReverseGlyphOrderDict()
 		return self._reverseGlyphOrderDict
@@ -596,7 +611,7 @@
 		tableClass = getTableClass(tag)
 		for masterTable in tableClass.dependencies:
 			if masterTable not in done:
-				if self.has_key(masterTable):
+				if masterTable in self:
 					self._writeTable(masterTable, writer, done)
 				else:
 					done.append(masterTable)
@@ -609,18 +624,19 @@
 	def getTableData(self, tag):
 		"""Returns raw table data, whether compiled or directly read from disk.
 		"""
+		tag = Tag(tag)
 		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):
+		elif self.reader and tag in self.reader:
 			if self.verbose:
 				debugmsg("Reading '%s' table from disk" % tag)
 			return self.reader[tag]
 		else:
-			raise KeyError, tag
+			raise KeyError(tag)
 	
-	def getGlyphSet(self, preferCFF=1):
+	def getGlyphSet(self, preferCFF=True):
 		"""Return a generic GlyphSet, which is a dict-like object
 		mapping glyph names to glyph objects. The returned glyph objects
 		have a .draw() method that supports the Pen protocol, and will
@@ -632,16 +648,16 @@
 		If the font contains both a 'CFF ' and a 'glyf' table, you can use
 		the 'preferCFF' argument to specify which one should be taken.
 		"""
-		if preferCFF and self.has_key("CFF "):
-			return self["CFF "].cff.values()[0].CharStrings
-		if self.has_key("glyf"):
+		if preferCFF and "CFF " in self:
+			return list(self["CFF "].cff.values())[0].CharStrings
+		if "glyf" in self:
 			return _TTGlyphSet(self)
-		if self.has_key("CFF "):
-			return self["CFF "].cff.values()[0].CharStrings
-		raise TTLibError, "Font contains no outlines"
+		if "CFF " in self:
+			return list(self["CFF "].cff.values())[0].CharStrings
+		raise TTLibError("Font contains no outlines")
 
 
-class _TTGlyphSet:
+class _TTGlyphSet(object):
 	
 	"""Generic dict-like GlyphSet class, meant as a TrueType counterpart
 	to CFF's CharString dict. See TTFont.getGlyphSet().
@@ -655,10 +671,10 @@
 		self._ttFont = ttFont
 	
 	def keys(self):
-		return self._ttFont["glyf"].keys()
+		return list(self._ttFont["glyf"].keys())
 	
 	def has_key(self, glyphName):
-		return self._ttFont["glyf"].has_key(glyphName)
+		return glyphName in self._ttFont["glyf"]
 	
 	__contains__ = has_key
 
@@ -672,7 +688,7 @@
 			return default
 
 
-class _TTGlyph:
+class _TTGlyph(object):
 	
 	"""Wrapper for a TrueType glyph that supports the Pen protocol, meaning
 	that it has a .draw() method that takes a pen object as its only
@@ -733,7 +749,7 @@
 				pen.closePath()
 
 
-class GlyphOrder:
+class GlyphOrder(object):
 	
 	"""A pseudo table. The glyph order isn't in the font as a separate
 	table, but it's nice to present it as such in the TTX format.
@@ -752,7 +768,7 @@
 			writer.simpletag("GlyphID", id=i, name=glyphName)
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "glyphOrder"):
 			self.glyphOrder = []
 			ttFont.setGlyphOrder(self.glyphOrder)
@@ -764,11 +780,11 @@
 	"""Fetch the packer/unpacker module for a table. 
 	Return None when no module is found.
 	"""
-	import tables
+	from . import tables
 	pyTag = tagToIdentifier(tag)
 	try:
 		__import__("fontTools.ttLib.tables." + pyTag)
-	except ImportError, err:
+	except ImportError as err:
 		# If pyTag is found in the ImportError message,
 		# means table is not implemented.  If it's not
 		# there, then some other module is missing, don't
@@ -787,7 +803,7 @@
 	"""
 	module = getTableModule(tag)
 	if module is None:
-		from tables.DefaultTable import DefaultTable
+		from .tables.DefaultTable import DefaultTable
 		return DefaultTable
 	pyTag = tagToIdentifier(tag)
 	tableClass = getattr(module, "table_" + pyTag)
@@ -808,7 +824,7 @@
 	elif re.match("[A-Z]", c):
 		return c + "_"
 	else:
-		return hex(ord(c))[2:]
+		return hex(byteord(c))[2:]
 
 
 def tagToIdentifier(tag):
@@ -825,6 +841,7 @@
 		'OS/2' -> 'O_S_2f_2'
 	"""
 	import re
+	tag = Tag(tag)
 	if tag == "GlyphOrder":
 		return tag
 	assert len(tag) == 4, "tag should be 4 characters long"
@@ -853,10 +870,10 @@
 			tag = tag + ident[i]
 		else:
 			# assume hex
-			tag = tag + chr(int(ident[i:i+2], 16))
+			tag = tag + bytechr(int(ident[i:i+2], 16))
 	# append trailing spaces
 	tag = tag + (4 - len(tag)) * ' '
-	return tag
+	return Tag(tag)
 
 
 def tagToXML(tag):
@@ -865,12 +882,13 @@
 	case sensitive, this is a fairly simple/readable translation.
 	"""
 	import re
+	tag = Tag(tag)
 	if tag == "OS/2":
 		return "OS_2"
 	elif tag == "GlyphOrder":
 		return tag
 	if re.match("[A-Za-z_][A-Za-z_0-9]* *$", tag):
-		return string.strip(tag)
+		return tag.strip()
 	else:
 		return tagToIdentifier(tag)
 
@@ -878,17 +896,16 @@
 def xmlToTag(tag):
 	"""The opposite of tagToXML()"""
 	if tag == "OS_2":
-		return "OS/2"
+		return Tag("OS/2")
 	if len(tag) == 8:
 		return identifierToTag(tag)
 	else:
-		return tag + " " * (4 - len(tag))
-	return tag
+		return Tag(tag + " " * (4 - len(tag)))
 
 
 def debugmsg(msg):
 	import time
-	print msg + time.strftime("  (%H:%M:%S)", time.localtime(time.time()))
+	print(msg + time.strftime("  (%H:%M:%S)", time.localtime(time.time())))
 
 
 # Table order as recommended in the OpenType specification 1.4
@@ -904,8 +921,7 @@
 	specification, or according to a custom tableOrder. If given and not
 	None, tableOrder needs to be a list of tag names.
 	"""
-	tagList = list(tagList)
-	tagList.sort()
+	tagList = sorted(tagList)
 	if tableOrder is None:
 		if "DSIG" in tagList:
 			# DSIG should be last (XXX spec reference?)
@@ -924,14 +940,14 @@
 	return orderedTables
 
 
-def reorderFontTables(inFile, outFile, tableOrder=None, checkChecksums=0):
+def reorderFontTables(inFile, outFile, tableOrder=None, checkChecksums=False):
 	"""Rewrite a font file, ordering the tables as recommended by the
 	OpenType specification 1.4.
 	"""
 	from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
 	reader = SFNTReader(inFile, checkChecksums=checkChecksums)
-	writer = SFNTWriter(outFile, reader.numTables, reader.sfntVersion, reader.flavor, reader.flavorData)
-	tables = reader.keys()
+	writer = SFNTWriter(outFile, len(reader.tables), reader.sfntVersion, reader.flavor, reader.flavorData)
+	tables = list(reader.keys())
 	for tag in sortedTagList(tables, tableOrder):
 		writer[tag] = reader[tag]
 	writer.close()
diff --git a/Lib/fontTools/ttLib/macUtils.py b/Lib/fontTools/ttLib/macUtils.py
index 2d5aedb..c7c261d 100644
--- a/Lib/fontTools/ttLib/macUtils.py
+++ b/Lib/fontTools/ttLib/macUtils.py
@@ -1,24 +1,25 @@
 """ttLib.macUtils.py -- Various Mac-specific stuff."""
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 import sys
 import os
 if sys.platform not in ("mac", "darwin"):
-	raise ImportError, "This module is Mac-only!"
-
-import cStringIO
+	raise ImportError("This module is Mac-only!")
 try:
 	from Carbon import Res
 except ImportError:
 	import Res
 
 
+
 def MyOpenResFile(path):
 	mode = 1  # read only
 	try:
 		resref = Res.FSOpenResFile(path, mode)
 	except Res.Error:
 		# try data fork
-		resref = Res.FSOpenResourceFile(path, u'', mode)
+		resref = Res.FSOpenResourceFile(path, unicode(), mode)
 	return resref
 
 
@@ -31,7 +32,7 @@
 	Res.UseResFile(resref)
 	numSFNTs = Res.Count1Resources('sfnt')
 	Res.CloseResFile(resref)
-	return range(1, numSFNTs + 1)
+	return list(range(1, numSFNTs + 1))
 
 
 def openTTFonts(path):
@@ -49,22 +50,22 @@
 		for index in sfnts:
 			fonts.append(ttLib.TTFont(path, index))
 		if not fonts:
-			raise ttLib.TTLibError, "no fonts found in file '%s'" % path
+			raise ttLib.TTLibError("no fonts found in file '%s'" % path)
 	return fonts
 
 
-class SFNTResourceReader:
+class SFNTResourceReader(object):
 	
 	"""Simple (Mac-only) read-only file wrapper for 'sfnt' resources."""
 	
 	def __init__(self, path, res_name_or_index):
 		resref = MyOpenResFile(path)
 		Res.UseResFile(resref)
-		if type(res_name_or_index) == type(""):
+		if isinstance(res_name_or_index, basestring):
 			res = Res.Get1NamedResource('sfnt', res_name_or_index)
 		else:
 			res = Res.Get1IndResource('sfnt', res_name_or_index)
-		self.file = cStringIO.StringIO(res.data)
+		self.file = StringIO(res.data)
 		Res.CloseResFile(resref)
 		self.name = path
 	
@@ -73,12 +74,12 @@
 		return getattr(self.file, attr)
 
 
-class SFNTResourceWriter:
+class SFNTResourceWriter(object):
 	
 	"""Simple (Mac-only) file wrapper for 'sfnt' resources."""
 	
 	def __init__(self, path, ttFont, res_id=None):
-		self.file = cStringIO.StringIO()
+		self.file = StringIO()
 		self.name = path
 		self.closed = 0
 		fullname = ttFont['name'].getName(4, 1, 0) # Full name, mac, default encoding
@@ -86,15 +87,15 @@
 		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"
+			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)]:
+		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]:
+				if self.familyname[i] != self.psname[i]:
 					break
 			self.familyname = self.psname[:i]
 		
@@ -157,7 +158,7 @@
 		fond.styleMappingReserved = 0
 		
 		# calc:
-		scale = 4096.0 / self.ttFont['head'].unitsPerEm
+		scale = 4096 / 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
@@ -172,20 +173,20 @@
 			names = {}
 			for code, name in cmap.cmap.items():
 				names[name] = code
-			if self.ttFont.has_key('kern'):
+			if 'kern' in self.ttFont:
 				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):
+						if left in names and right in names:
 							fondkerning.append((names[left], names[right], scale * value))
 					fondkerning.sort()
 					fond.kernTables = {0: fondkerning}
-			if self.ttFont.has_key('hmtx'):
+			if 'hmtx' in self.ttFont:
 				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):
+					if name in names:
 						fondwidths[names[name]] = scale * width
 				fond.widthTables = {0: fondwidths}
 		fond.save()
diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py
index 82e3e04..6291510 100644
--- a/Lib/fontTools/ttLib/sfnt.py
+++ b/Lib/fontTools/ttLib/sfnt.py
@@ -12,13 +12,13 @@
 a table's length chages you need to rewrite the whole file anyway.
 """
 
-import sys
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-import os
+import struct
 
 
-class SFNTReader:
+class SFNTReader(object):
 	
 	def __init__(self, file, checkChecksums=1, fontNumber=-1):
 		self.file = file
@@ -29,33 +29,34 @@
 		self.DirectoryEntry = SFNTDirectoryEntry
 		self.sfntVersion = self.file.read(4)
 		self.file.seek(0)
-		if self.sfntVersion == "ttcf":
+		if self.sfntVersion == b"ttcf":
 			sstruct.unpack(ttcHeaderFormat, self.file.read(ttcHeaderSize), self)
 			assert self.Version == 0x00010000 or self.Version == 0x00020000, "unrecognized TTC version 0x%08x" % self.Version
 			if not 0 <= fontNumber < self.numFonts:
 				from fontTools import ttLib
-				raise ttLib.TTLibError, "specify a font number between 0 and %d (inclusive)" % (self.numFonts - 1)
+				raise ttLib.TTLibError("specify a font number between 0 and %d (inclusive)" % (self.numFonts - 1))
 			offsetTable = struct.unpack(">%dL" % self.numFonts, self.file.read(self.numFonts * 4))
 			if self.Version == 0x00020000:
 				pass # ignoring version 2.0 signatures
 			self.file.seek(offsetTable[fontNumber])
 			sstruct.unpack(sfntDirectoryFormat, self.file.read(sfntDirectorySize), self)
-		elif self.sfntVersion == "wOFF":
+		elif self.sfntVersion == b"wOFF":
 			self.flavor = "woff"
 			self.DirectoryEntry = WOFFDirectoryEntry
 			sstruct.unpack(woffDirectoryFormat, self.file.read(woffDirectorySize), self)
 		else:
 			sstruct.unpack(sfntDirectoryFormat, self.file.read(sfntDirectorySize), self)
+		self.sfntVersion = Tag(self.sfntVersion)
 
-		if self.sfntVersion not in ("\000\001\000\000", "OTTO", "true"):
+		if self.sfntVersion not in ("\x00\x01\x00\x00", "OTTO", "true"):
 			from fontTools import ttLib
-			raise ttLib.TTLibError, "Not a TrueType or OpenType font (bad sfntVersion)"
+			raise ttLib.TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")
 		self.tables = {}
 		for i in range(self.numTables):
 			entry = self.DirectoryEntry()
 			entry.fromFile(self.file)
 			if entry.length > 0:
-				self.tables[entry.tag] = entry
+				self.tables[Tag(entry.tag)] = entry
 			else:
 				# Ignore zero-length tables. This doesn't seem to be documented,
 				# yet it's apparently how the Windows TT rasterizer behaves.
@@ -68,43 +69,45 @@
 			self.flavorData = WOFFFlavorData(self)
 
 	def has_key(self, tag):
-		return self.tables.has_key(tag)
+		return tag in self.tables
+
+	__contains__ = has_key
 	
 	def keys(self):
 		return self.tables.keys()
 	
 	def __getitem__(self, tag):
 		"""Fetch the raw table data."""
-		entry = self.tables[tag]
+		entry = self.tables[Tag(tag)]
 		data = entry.loadData (self.file)
 		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:])
+				checksum = calcChecksum(data[:8] + b'\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:
+			elif checksum != entry.checkSum:
 				# Be friendly, and just print a warning.
-				print "bad checksum for '%s' table" % tag
+				print("bad checksum for '%s' table" % tag)
 		return data
 	
 	def __delitem__(self, tag):
-		del self.tables[tag]
+		del self.tables[Tag(tag)]
 	
 	def close(self):
 		self.file.close()
 
 
-class SFNTWriter:
+class SFNTWriter(object):
 	
 	def __init__(self, file, numTables, sfntVersion="\000\001\000\000",
 		     flavor=None, flavorData=None):
 		self.file = file
 		self.numTables = numTables
-		self.sfntVersion = sfntVersion
+		self.sfntVersion = Tag(sfntVersion)
 		self.flavor = flavor
 		self.flavorData = flavorData
 
@@ -126,27 +129,27 @@
 		# clear out directory area
 		self.file.seek(self.nextTableOffset)
 		# make sure we're actually where we want to be. (old cStringIO bug)
-		self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
+		self.file.write(b'\0' * (self.nextTableOffset - self.file.tell()))
 		self.tables = {}
 	
 	def __setitem__(self, tag, data):
 		"""Write raw table data to disk."""
 		reuse = False
-		if self.tables.has_key(tag):
+		if tag in self.tables:
 			# We've written this table to file before. If the length
 			# of the data is still the same, we allow overwriting it.
 			entry = self.tables[tag]
 			assert not hasattr(entry.__class__, 'encodeData')
-			if len(data) <> entry.length:
+			if len(data) != entry.length:
 				from fontTools import ttLib
-				raise ttLib.TTLibError, "cannot rewrite '%s' table: length does not match directory entry" % tag
+				raise ttLib.TTLibError("cannot rewrite '%s' table: length does not match directory entry" % tag)
 			reuse = True
 		else:
 			entry = self.DirectoryEntry()
 			entry.tag = tag
 
 		if tag == 'head':
-			entry.checkSum = calcChecksum(data[:8] + '\0\0\0\0' + data[12:])
+			entry.checkSum = calcChecksum(data[:8] + b'\0\0\0\0' + data[12:])
 			self.headTable = data
 			entry.uncompressed = True
 		else:
@@ -162,7 +165,7 @@
 		# Don't depend on f.seek() as we need to add the padding even if no
 		# subsequent write follows (seek is lazy), ie. after the final table
 		# in the font.
-		self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
+		self.file.write(b'\0' * (self.nextTableOffset - self.file.tell()))
 		assert self.nextTableOffset == self.file.tell()
 		
 		self.tables[tag] = entry
@@ -171,14 +174,13 @@
 		"""All tables must have been written to disk. Now write the
 		directory.
 		"""
-		tables = self.tables.items()
-		tables.sort()
-		if len(tables) <> self.numTables:
+		tables = sorted(self.tables.items())
+		if len(tables) != self.numTables:
 			from fontTools import ttLib
-			raise ttLib.TTLibError, "wrong number of tables; expected %d, found %d" % (self.numTables, len(tables))
+			raise ttLib.TTLibError("wrong number of tables; expected %d, found %d" % (self.numTables, len(tables)))
 
 		if self.flavor == "woff":
-			self.signature = "wOFF"
+			self.signature = b"wOFF"
 			self.reserved = 0
 
 			self.totalSfntSize = 12
@@ -187,7 +189,7 @@
 				self.totalSfntSize += (entry.origLength + 3) & ~3
 
 			data = self.flavorData if self.flavorData else WOFFFlavorData()
-			if data.majorVersion != None and data.minorVersion != None:
+			if data.majorVersion is not None and data.minorVersion is not None:
 				self.majorVersion = data.majorVersion
 				self.minorVersion = data.minorVersion
 			else:
@@ -199,6 +201,7 @@
 				self.metaOrigLength = len(data.metaData)
 				self.file.seek(0,2)
 				self.metaOffset = self.file.tell()
+				import zlib
 				compressedMetaData = zlib.compress(data.metaData)
 				self.metaLength = len(compressedMetaData)
 				self.file.write(compressedMetaData)
@@ -237,7 +240,7 @@
 
 	def _calcMasterChecksum(self, directory):
 		# calculate checkSumAdjustment
-		tags = self.tables.keys()
+		tags = list(self.tables.keys())
 		checksums = []
 		for i in range(len(tags)):
 			checksums.append(self.tables[tags[i]].checkSum)
@@ -248,8 +251,7 @@
 			# Create a SFNT directory for checksum calculation purposes
 			self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(self.numTables)
 			directory = sstruct.pack(sfntDirectoryFormat, self)
-			tables = self.tables.items()
-			tables.sort()
+			tables = sorted(self.tables.items())
 			for tag, entry in tables:
 				sfntEntry = SFNTDirectoryEntry()
 				for item in ['tag', 'checkSum', 'offset', 'length']:
@@ -339,7 +341,7 @@
 woffDirectoryEntrySize = sstruct.calcsize(woffDirectoryEntryFormat)
 
 
-class DirectoryEntry:
+class DirectoryEntry(object):
 	
 	def __init__(self):
 		self.uncompressed = False # if True, always embed entry raw
@@ -429,14 +431,15 @@
 			self.minorVersion = reader.minorVersion
 			if reader.metaLength:
 				reader.file.seek(reader.metaOffset)
-				rawData = read.file.read(reader.metaLength)
+				rawData = reader.file.read(reader.metaLength)
 				assert len(rawData) == reader.metaLength
+				import zlib
 				data = zlib.decompress(rawData)
 				assert len(data) == reader.metaOrigLength
 				self.metaData = data
 			if reader.privLength:
 				reader.file.seek(reader.privOffset)
-				data = read.file.read(reader.privLength)
+				data = reader.file.read(reader.privLength)
 				assert len(data) == reader.privLength
 				self.privData = data
 
@@ -450,18 +453,18 @@
 	If the data length is not a multiple of four, it assumes
 	it is to be padded with null byte. 
 
-		>>> print calcChecksum("abcd")
+		>>> print calcChecksum(b"abcd")
 		1633837924
-		>>> print calcChecksum("abcdxyz")
+		>>> print calcChecksum(b"abcdxyz")
 		3655064932
 	"""
 	remainder = len(data) % 4
 	if remainder:
-		data += "\0" * (4 - remainder)
+		data += b"\0" * (4 - remainder)
 	value = 0
 	blockSize = 4096
 	assert blockSize % 4 == 0
-	for i in xrange(0, len(data), blockSize):
+	for i in range(0, len(data), blockSize):
 		block = data[i:i+blockSize]
 		longs = struct.unpack(">%dL" % (len(block) // 4), block)
 		value = (value + sum(longs)) & 0xffffffff
@@ -484,7 +487,6 @@
 	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
diff --git a/Lib/fontTools/ttLib/tables/B_A_S_E_.py b/Lib/fontTools/ttLib/tables/B_A_S_E_.py
index 53975c8..9551e2c 100644
--- a/Lib/fontTools/ttLib/tables/B_A_S_E_.py
+++ b/Lib/fontTools/ttLib/tables/B_A_S_E_.py
@@ -1,4 +1,4 @@
-from otBase import BaseTTXConverter
+from .otBase import BaseTTXConverter
 
 
 class table_B_A_S_E_(BaseTTXConverter):
diff --git a/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py b/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
index 71514e1..6e5e1f0 100644
--- a/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
+++ b/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
@@ -1,7 +1,8 @@
 # Since bitmap glyph metrics are shared between EBLC and EBDT
 # this class gets its own python file.
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-from types import TupleType
 from fontTools.misc.textTools import safeEval
 
 
@@ -26,7 +27,7 @@
   Advance:  B
 """
 
-class BitmapGlyphMetrics:
+class BitmapGlyphMetrics(object):
 
 	def toXML(self, writer, ttFont):
 		writer.begintag(self.__class__.__name__)
@@ -37,17 +38,17 @@
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		metricNames = set(sstruct.getformat(self.__class__.binaryFormat)[1])
 		for element in content:
-			if type(element) != TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
 			# Make sure this is a metric that is needed by GlyphMetrics.
 			if name in metricNames:
 				vars(self)[name] = safeEval(attrs['value'])
 			else:
-				print "Warning: unknown name '%s' being ignored in %s." % name, self.__class__.__name__
+				print("Warning: unknown name '%s' being ignored in %s." % name, self.__class__.__name__)
 
 
 class BigGlyphMetrics(BitmapGlyphMetrics):
diff --git a/Lib/fontTools/ttLib/tables/C_B_D_T_.py b/Lib/fontTools/ttLib/tables/C_B_D_T_.py
index 2289594..cd2a975 100644
--- a/Lib/fontTools/ttLib/tables/C_B_D_T_.py
+++ b/Lib/fontTools/ttLib/tables/C_B_D_T_.py
@@ -3,12 +3,13 @@
 # Google Author(s): Matt Fontaine
 
 
-import E_B_D_T_
-import string
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-from BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
-from E_B_D_T_ import BitmapGlyph, BitmapPlusSmallMetricsMixin, BitmapPlusBigMetricsMixin
+from . import E_B_D_T_
+from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+from .E_B_D_T_ import BitmapGlyph, BitmapPlusSmallMetricsMixin, BitmapPlusBigMetricsMixin
+import struct
 
 class table_C_B_D_T_(E_B_D_T_.table_E_B_D_T_):
 
@@ -51,7 +52,7 @@
 		dataList.append(sstruct.pack(smallGlyphMetricsFormat, self.metrics))
 		dataList.append(struct.pack(">L", len(self.imageData)))
 		dataList.append(self.imageData)
-		return string.join(dataList, "")
+		return bytesjoin(dataList)
 
 class cbdt_bitmap_format_18(BitmapPlusBigMetricsMixin, ColorBitmapGlyph):
 
@@ -70,7 +71,7 @@
 		dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
 		dataList.append(struct.pack(">L", len(self.imageData)))
 		dataList.append(self.imageData)
-		return string.join(dataList, "")
+		return bytesjoin(dataList)
 
 class cbdt_bitmap_format_19(ColorBitmapGlyph):
 
diff --git a/Lib/fontTools/ttLib/tables/C_B_L_C_.py b/Lib/fontTools/ttLib/tables/C_B_L_C_.py
index 886f50c..2f78571 100644
--- a/Lib/fontTools/ttLib/tables/C_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/C_B_L_C_.py
@@ -2,7 +2,7 @@
 #
 # Google Author(s): Matt Fontaine
 
-import E_B_L_C_
+from . import E_B_L_C_
 
 class table_C_B_L_C_(E_B_L_C_.table_E_B_L_C_):
 
diff --git a/Lib/fontTools/ttLib/tables/C_F_F_.py b/Lib/fontTools/ttLib/tables/C_F_F_.py
index 6a083c3..6c86947 100644
--- a/Lib/fontTools/ttLib/tables/C_F_F_.py
+++ b/Lib/fontTools/ttLib/tables/C_F_F_.py
@@ -1,5 +1,7 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools import cffLib
+from . import DefaultTable
 
 
 class table_C_F_F_(DefaultTable.DefaultTable):
@@ -7,30 +9,28 @@
 	def __init__(self, tag):
 		DefaultTable.DefaultTable.__init__(self, tag)
 		self.cff = cffLib.CFFFontSet()
-		self._gaveGlyphOrder = 0
+		self._gaveGlyphOrder = False
 	
 	def decompile(self, data, otFont):
-		from cStringIO import StringIO
 		self.cff.decompile(StringIO(data), otFont)
 		assert len(self.cff) == 1, "can't deal with multi-font CFF tables."
 	
 	def compile(self, otFont):
-		from cStringIO import StringIO
 		f = StringIO()
 		self.cff.compile(f, otFont)
 		return f.getvalue()
 	
 	def haveGlyphNames(self):
 		if hasattr(self.cff[self.cff.fontNames[0]], "ROS"):
-			return 0  # CID-keyed font
+			return False  # CID-keyed font
 		else:
-			return 1
+			return True
 	
 	def getGlyphOrder(self):
 		if self._gaveGlyphOrder:
 			from fontTools import ttLib
-			raise ttLib.TTLibError, "illegal use of getGlyphOrder()"
-		self._gaveGlyphOrder = 1
+			raise ttLib.TTLibError("illegal use of getGlyphOrder()")
+		self._gaveGlyphOrder = True
 		return self.cff[self.cff.fontNames[0]].getGlyphOrder()
 	
 	def setGlyphOrder(self, glyphOrder):
@@ -41,8 +41,8 @@
 	def toXML(self, writer, otFont, progress=None):
 		self.cff.toXML(writer, progress)
 	
-	def fromXML(self, (name, attrs, content), otFont):
+	def fromXML(self, name, attrs, content, otFont):
 		if not hasattr(self, "cff"):
 			self.cff = cffLib.CFFFontSet()
-		self.cff.fromXML((name, attrs, content))
+		self.cff.fromXML(name, attrs, content)
 
diff --git a/Lib/fontTools/ttLib/tables/C_O_L_R_.py b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
index b39a4a0..0fdf133 100644
--- a/Lib/fontTools/ttLib/tables/C_O_L_R_.py
+++ b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
@@ -2,12 +2,12 @@
 #
 # Google Author(s): Behdad Esfahbod
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
 import operator
-import DefaultTable
 import struct
-from fontTools.ttLib import sfnt
-from fontTools.misc.textTools import safeEval, readHex
-from types import IntType, StringType
 
 
 class table_C_O_L_R_(DefaultTable.DefaultTable):
@@ -44,17 +44,17 @@
 
 		self.ColorLayers = colorLayerLists = {}
 		try:
-			names = map(operator.getitem, [glyphOrder]*numBaseGlyphRecords, gids)
+			names = list(map(operator.getitem, [glyphOrder]*numBaseGlyphRecords, gids))
 		except IndexError:
 			getGlyphName = self.getGlyphName
-			names = map(getGlyphName, gids )
+			names = list(map(getGlyphName, gids ))
 
-		map(operator.setitem, [colorLayerLists]*numBaseGlyphRecords, names, layerLists)
+		list(map(operator.setitem, [colorLayerLists]*numBaseGlyphRecords, names, layerLists))
 
 
 	def compile(self, ttFont):
 		ordered = []
-		ttFont.getReverseGlyphMap(rebuild=1)
+		ttFont.getReverseGlyphMap(rebuild=True)
 		glyphNames = self.ColorLayers.keys()
 		for glyphName in glyphNames:
 			try:
@@ -74,7 +74,7 @@
 		dataList = [struct.pack(">HHLLH", self.version, len(glyphMap), 14, 14+6*len(glyphMap), len(layerMap))]
 		dataList.extend(glyphMap)
 		dataList.extend(layerMap)
-		data = "".join(dataList)
+		data = bytesjoin(dataList)
 		return data
 
 	def toXML(self, writer, ttFont):
@@ -97,49 +97,48 @@
 			writer.endtag("ColorGlyph")
 			writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "ColorLayers"):
 			self.ColorLayers = {}
 		self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID
 		if name == "ColorGlyph":
 			glyphName = attrs["name"]
 			for element in content:
-				if isinstance(element, StringType):
+				if isinstance(element, basestring):
 					continue
 			layers = []
 			for element in content:
-				if isinstance(element, StringType):
+				if isinstance(element, basestring):
 					continue
 				layer = LayerRecord()
-				layer.fromXML(element, ttFont)
+				layer.fromXML(element[0], element[1], element[2], ttFont)
 				layers.append (layer)
 			operator.setitem(self, glyphName, layers)
-		elif attrs.has_key("value"):
-			value =  safeEval(attrs["value"])
-			setattr(self, name, value)
+		elif "value" in attrs:
+			setattr(self, name, safeEval(attrs["value"]))
 
 
 	def __getitem__(self, glyphSelector):
-		if type(glyphSelector) == IntType:
+		if isinstance(glyphSelector, int):
 			# its a gid, convert to glyph name
 			glyphSelector = self.getGlyphName(glyphSelector)
 
-		if not self.ColorLayers.has_key(glyphSelector):
+		if glyphSelector not in self.ColorLayers:
 			return None
 			
 		return self.ColorLayers[glyphSelector]
 
 	def __setitem__(self, glyphSelector, value):
-		if type(glyphSelector) == IntType:
+		if isinstance(glyphSelector, int):
 			# its a gid, convert to glyph name
 			glyphSelector = self.getGlyphName(glyphSelector)
 
 		if  value:
 			self.ColorLayers[glyphSelector] = value
-		elif self.ColorLayers.has_key(glyphSelector):
+		elif glyphSelector in self.ColorLayers:
 			del self.ColorLayers[glyphSelector]
 
-class LayerRecord:
+class LayerRecord(object):
 
 	def __init__(self, name = None, colorID = None):
 		self.name = name
@@ -149,15 +148,11 @@
 		writer.simpletag("layer", name=self.name, colorID=self.colorID)
 		writer.newline()
 
-	def fromXML(self, (eltname, attrs, content), ttFont):
+	def fromXML(self, eltname, attrs, content, ttFont):
 		for (name, value) in attrs.items():
 			if name == "name":
-				if type(value) == IntType:
+				if isinstance(value, int):
 					value = ttFont.getGlyphName(value)
 				setattr(self, name, value)
 			else:
-				try:
-					value = safeEval(value)
-				except OverflowError:
-					value = long(value)
-				setattr(self, name, value)
+				setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/C_P_A_L_.py b/Lib/fontTools/ttLib/tables/C_P_A_L_.py
index 1e5a878..fc96caa 100644
--- a/Lib/fontTools/ttLib/tables/C_P_A_L_.py
+++ b/Lib/fontTools/ttLib/tables/C_P_A_L_.py
@@ -2,12 +2,11 @@
 #
 # Google Author(s): Behdad Esfahbod
 
-import operator
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
 import struct
-from fontTools.ttLib import sfnt
-from fontTools.misc.textTools import safeEval, readHex
-from types import IntType, StringType
 
 
 class table_C_P_A_L_(DefaultTable.DefaultTable):
@@ -36,7 +35,7 @@
 			assert(len(palette) == self.numPaletteEntries)
 			for color in palette:
 				dataList.append(struct.pack(">BBBB", color.blue,color.green,color.red,color.alpha))
-		data = "".join(dataList)
+		data = bytesjoin(dataList)
 		return data
 
 	def toXML(self, writer, ttFont):
@@ -53,27 +52,27 @@
 			writer.endtag("palette")
 			writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "palettes"):
 			self.palettes = []
 		if name == "palette":
 			palette = []
 			for element in content:
-				if isinstance(element, StringType):
+				if isinstance(element, basestring):
 					continue
 			palette = []
 			for element in content:
-				if isinstance(element, StringType):
+				if isinstance(element, basestring):
 					continue
 				color = Color()
-				color.fromXML(element, ttFont)
+				color.fromXML(element[0], element[1], element[2], ttFont)
 				palette.append (color)
 			self.palettes.append(palette)
-		elif attrs.has_key("value"):
+		elif "value" in attrs:
 			value =  safeEval(attrs["value"])
 			setattr(self, name, value)
 
-class Color:
+class Color(object):
 
 	def __init__(self, blue=None, green=None, red=None, alpha=None):
 		self.blue  = blue
@@ -91,7 +90,7 @@
 		writer.simpletag("color", value=self.hex(), index=index)
 		writer.newline()
 
-	def fromXML(self, (eltname, attrs, content), ttFont):
+	def fromXML(self, eltname, attrs, content, ttFont):
 		value = attrs["value"]
 		if value[0] == '#':
 			value = value[1:]
diff --git a/Lib/fontTools/ttLib/tables/D_S_I_G_.py b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
index 995069d..20f14ee 100644
--- a/Lib/fontTools/ttLib/tables/D_S_I_G_.py
+++ b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
@@ -1,6 +1,9 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
 from fontTools.misc import sstruct
+from . import DefaultTable
+import base64
 
 DSIG_HeaderFormat = """
 	> # big endian
@@ -69,7 +72,10 @@
 			sigrec.ulOffset = offset
 			headers.append(sstruct.pack(DSIG_SignatureFormat, sigrec))
 			offset += sigrec.ulLength
-		return ''.join(headers+data)
+		if offset % 2:
+			# Pad to even bytes
+			data.append(b'\0')
+		return bytesjoin(headers+data)
 	
 	def toXML(self, xmlWriter, ttFont):
 		xmlWriter.comment("note that the Digital Signature will be invalid after recompilation!")
@@ -80,7 +86,7 @@
 			sigrec.toXML(xmlWriter, ttFont)
 		xmlWriter.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == "tableHeader":
 			self.signatureRecords = []
 			self.ulVersion = safeEval(attrs["version"])
@@ -89,14 +95,24 @@
 			return
 		if name == "SignatureRecord":
 			sigrec = SignatureRecord()
-			sigrec.fromXML((name, attrs, content), ttFont)
+			sigrec.fromXML(name, attrs, content, ttFont)
 			self.signatureRecords.append(sigrec)
 
 pem_spam = lambda l, spam = {
 	"-----BEGIN PKCS7-----": True, "-----END PKCS7-----": True, "": True
 }: not spam.get(l.strip())
 
-class SignatureRecord:
+def b64encode(b):
+	s = base64.b64encode(b)
+	# Line-break at 76 chars.
+	items = []
+	while s:
+		items.append(tostr(s[:76]))
+		items.append('\n')
+		s = s[76:]
+	return strjoin(items)
+
+class SignatureRecord(object):
 	def __repr__(self):
 		return "<%s: %s>" % (self.__class__.__name__, self.__dict__)
 	
@@ -104,12 +120,12 @@
 		writer.begintag(self.__class__.__name__, format=self.ulFormat)
 		writer.newline()
 		writer.write_noindent("-----BEGIN PKCS7-----\n")
-		writer.write_noindent(self.pkcs7.encode('base64'))
+		writer.write_noindent(b64encode(self.pkcs7))
 		writer.write_noindent("-----END PKCS7-----\n")
 		writer.endtag(self.__class__.__name__)
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.ulFormat = safeEval(attrs["format"])
 		self.usReserved1 = safeEval(attrs.get("reserved1", "0"))
 		self.usReserved2 = safeEval(attrs.get("reserved2", "0"))
-		self.pkcs7 = "".join(filter(pem_spam, content)).decode('base64')
+		self.pkcs7 = base64.b64decode(tobytes(strjoin(filter(pem_spam, content))))
diff --git a/Lib/fontTools/ttLib/tables/DefaultTable.py b/Lib/fontTools/ttLib/tables/DefaultTable.py
index ff45bc3..f5a6d00 100644
--- a/Lib/fontTools/ttLib/tables/DefaultTable.py
+++ b/Lib/fontTools/ttLib/tables/DefaultTable.py
@@ -1,12 +1,12 @@
-import string
-import sys
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 
-class DefaultTable:
+class DefaultTable(object):
 	
 	dependencies = []
 	
 	def __init__(self, tag):
-		self.tableTag = tag
+		self.tableTag = Tag(tag)
 	
 	def decompile(self, data, ttFont):
 		self.data = data
@@ -14,7 +14,7 @@
 	def compile(self, ttFont):
 		return self.data
 	
-	def toXML(self, writer, ttFont):
+	def toXML(self, writer, ttFont, progress=None):
 		if hasattr(self, "ERROR"):
 			writer.comment("An error occurred during the decompilation of this table")
 			writer.newline()
@@ -26,20 +26,19 @@
 		writer.endtag("hexdata")
 		writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	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
+		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):
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
-
-		return cmp(self.__dict__, other.__dict__)
-
+	def __ne__(self, other):
+		return not self.__eq__(other)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			return NotImplemented
+		return self.__dict__ == other.__dict__
diff --git a/Lib/fontTools/ttLib/tables/E_B_D_T_.py b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
index 93d3009..35ab65c 100644
--- a/Lib/fontTools/ttLib/tables/E_B_D_T_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
@@ -1,13 +1,12 @@
-
-import DefaultTable
-import os
-import string
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-import itertools
-from types import TupleType
 from fontTools.misc.textTools import safeEval, readHex, hexStr, deHexStr
-from BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+from . import DefaultTable
+import itertools
+import os
+import struct
 
 ebdtTableVersionFormat = """
 	> # big endian
@@ -52,7 +51,7 @@
 			bitmapGlyphDict = {}
 			self.strikeData.append(bitmapGlyphDict)
 			for indexSubTable in curStrike.indexSubTables:
-				dataIter = itertools.izip(indexSubTable.names, indexSubTable.locations)
+				dataIter = zip(indexSubTable.names, indexSubTable.locations)
 				for curName, curLoc in dataIter:
 					# Don't create duplicate data entries for the same glyphs.
 					# Instead just use the structures that already exist if they exist.
@@ -82,7 +81,7 @@
 		# recalculation is defered to the EblcIndexSubTable class and just
 		# pass what is known about bitmap glyphs from this particular table.
 		locator = ttFont[self.__class__.locatorName]
-		for curStrike, curGlyphDict in itertools.izip(locator.strikes, self.strikeData):
+		for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
 			for curIndexSubTable in curStrike.indexSubTables:
 				dataLocations = []
 				for curName in curIndexSubTable.names:
@@ -114,7 +113,7 @@
 				# of any of the problems in the convertion that may arise.
 				curIndexSubTable.locations = dataLocations
 
-		return string.join(dataList, "")
+		return bytesjoin(dataList)
 
 	def toXML(self, writer, ttFont):
 		# When exporting to XML if one of the data export formats
@@ -122,7 +121,7 @@
 		# In this case populate the bitmaps with "export metrics".
 		if ttFont.bitmapGlyphDataFormat in ('row', 'bitwise'):
 			locator = ttFont[self.__class__.locatorName]
-			for curStrike, curGlyphDict in itertools.izip(locator.strikes, self.strikeData):
+			for curStrike, curGlyphDict in zip(locator.strikes, self.strikeData):
 				for curIndexSubTable in curStrike.indexSubTables:
 					for curName in curIndexSubTable.names:
 						glyph = curGlyphDict[curName]
@@ -145,7 +144,7 @@
 			writer.endtag('strikedata')
 			writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == 'header':
 			self.version = safeEval(attrs['version'])
 		elif name == 'strikedata':
@@ -155,7 +154,7 @@
 
 			bitmapGlyphDict = {}
 			for element in content:
-				if type(element) != TupleType:
+				if not isinstance(element, tuple):
 					continue
 				name, attrs, content = element
 				if name[4:].startswith(_bitmapGlyphSubclassPrefix[4:]):
@@ -163,20 +162,20 @@
 					glyphName = attrs['name']
 					imageFormatClass = self.getImageFormatClass(imageFormat)
 					curGlyph = imageFormatClass(None, None)
-					curGlyph.fromXML(element, ttFont)
+					curGlyph.fromXML(name, attrs, content, ttFont)
 					assert glyphName not in bitmapGlyphDict, "Duplicate glyphs with the same name '%s' in the same strike." % glyphName
 					bitmapGlyphDict[glyphName] = curGlyph
 				else:
-					print "Warning: %s being ignored by %s", name, self.__class__.__name__
+					print("Warning: %s being ignored by %s", name, self.__class__.__name__)
 
 			# Grow the strike data array to the appropriate size. The XML
 			# format allows the strike index value to be out of order.
 			if strikeIndex >= len(self.strikeData):
 				self.strikeData += [None] * (strikeIndex + 1 - len(self.strikeData))
-			assert self.strikeData[strikeIndex] == None, "Duplicate strike EBDT indices."
+			assert self.strikeData[strikeIndex] is None, "Duplicate strike EBDT indices."
 			self.strikeData[strikeIndex] = bitmapGlyphDict
 
-class EbdtComponent:
+class EbdtComponent(object):
 
 	def toXML(self, writer, ttFont):
 		writer.begintag('ebdtComponent', [('name', self.name)])
@@ -187,45 +186,45 @@
 		writer.endtag('ebdtComponent')
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.name = attrs['name']
 		componentNames = set(sstruct.getformat(ebdtComponentFormat)[1][1:])
 		for element in content:
-			if type(element) != TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
 			if name in componentNames:
 				vars(self)[name] = safeEval(attrs['value'])
 			else:
-				print "Warning: unknown name '%s' being ignored by EbdtComponent." % name
+				print("Warning: unknown name '%s' being ignored by EbdtComponent." % name)
 
 # Helper functions for dealing with binary.
 
 def _data2binary(data, numBits):
 	binaryList = []
 	for curByte in data:
-		value = ord(curByte)
+		value = byteord(curByte)
 		numBitsCut = min(8, numBits)
-		for i in xrange(numBitsCut):
+		for i in range(numBitsCut):
 			if value & 0x1:
 				binaryList.append('1')
 			else:
 				binaryList.append('0')
 			value = value >> 1
 		numBits -= numBitsCut
-	return string.join(binaryList, "")
+	return strjoin(binaryList)
 
 def _binary2data(binary):
 	byteList = []
-	for bitLoc in xrange(0, len(binary), 8):
+	for bitLoc in range(0, len(binary), 8):
 		byteString = binary[bitLoc:bitLoc+8]
 		curByte = 0
 		for curBit in reversed(byteString):
 			curByte = curByte << 1
 			if curBit == '1':
 				curByte |= 1
-		byteList.append(chr(curByte))
-	return string.join(byteList, "")
+		byteList.append(bytechr(curByte))
+	return bytesjoin(byteList)
 
 def _memoize(f):
 	class memodict(dict):
@@ -243,14 +242,14 @@
 @_memoize
 def _reverseBytes(data):
 	if len(data) != 1:
-		return string.join(map(_reverseBytes, data), "")
-	byte = ord(data)
+		return bytesjoin(map(_reverseBytes, data))
+	byte = byteord(data)
 	result = 0
-	for i in xrange(8):
+	for i in range(8):
 		result = result << 1
 		result |= byte & 1
 		byte = byte >> 1
-	return chr(result)
+	return bytechr(result)
 
 # This section of code is for reading and writing image data to/from XML.
 
@@ -261,7 +260,7 @@
 	writer.endtag('rawimagedata')
 	writer.newline()
 
-def _readRawImageData(bitmapObject, (name, attrs, content), ttFont):
+def _readRawImageData(bitmapObject, name, attrs, content, ttFont):
 	bitmapObject.imageData = readHex(content)
 
 def _writeRowImageData(strikeIndex, glyphName, bitmapObject, writer, ttFont):
@@ -272,14 +271,14 @@
 
 	writer.begintag('rowimagedata', bitDepth=bitDepth, width=metrics.width, height=metrics.height)
 	writer.newline()
-	for curRow in xrange(metrics.height):
+	for curRow in range(metrics.height):
 		rowData = bitmapObject.getRow(curRow, bitDepth=bitDepth, metrics=metrics)
 		writer.simpletag('row', value=hexStr(rowData))
 		writer.newline()
 	writer.endtag('rowimagedata')
 	writer.newline()
 
-def _readRowImageData(bitmapObject, (name, attrs, content), ttFont):
+def _readRowImageData(bitmapObject, name, attrs, content, ttFont):
 	bitDepth = safeEval(attrs['bitDepth'])
 	metrics = SmallGlyphMetrics()
 	metrics.width = safeEval(attrs['width'])
@@ -287,7 +286,7 @@
 
 	dataRows = []
 	for element in content:
-		if type(element) != TupleType:
+		if not isinstance(element, tuple):
 			continue
 		name, attr, content = element
 		# Chop off 'imagedata' from the tag to get just the option.
@@ -306,17 +305,17 @@
 
 	writer.begintag('bitwiseimagedata', bitDepth=bitDepth, width=metrics.width, height=metrics.height)
 	writer.newline()
-	for curRow in xrange(metrics.height):
+	for curRow in range(metrics.height):
 		rowData = bitmapObject.getRow(curRow, bitDepth=1, metrics=metrics, reverseBytes=True)
 		rowData = _data2binary(rowData, metrics.width)
 		# Make the output a readable ASCII art form.
-		rowData = string.join(map(binaryConv.get, rowData), "")
+		rowData = strjoin(map(binaryConv.get, rowData))
 		writer.simpletag('row', value=rowData)
 		writer.newline()
 	writer.endtag('bitwiseimagedata')
 	writer.newline()
 
-def _readBitwiseImageData(bitmapObject, (name, attrs, content), ttFont):
+def _readBitwiseImageData(bitmapObject, name, attrs, content, ttFont):
 	bitDepth = safeEval(attrs['bitDepth'])
 	metrics = SmallGlyphMetrics()
 	metrics.width = safeEval(attrs['width'])
@@ -328,12 +327,12 @@
 
 	dataRows = []
 	for element in content:
-		if type(element) != TupleType:
+		if not isinstance(element, tuple):
 			continue
 		name, attr, content = element
 		if name == 'row':
-			mapParams = itertools.izip(attr['value'], itertools.repeat('1'))
-			rowData = string.join(itertools.starmap(binaryConv.get, mapParams), "")
+			mapParams = zip(attr['value'], itertools.repeat('1'))
+			rowData = strjoin(itertools.starmap(binaryConv.get, mapParams))
 			dataRows.append(_binary2data(rowData))
 
 	bitmapObject.setRows(dataRows, bitDepth=bitDepth, metrics=metrics, reverseBytes=True)
@@ -354,7 +353,7 @@
 	with open(fullPath, "wb") as file:
 		file.write(bitmapObject.imageData)
 
-def _readExtFileImageData(bitmapObject, (name, attrs, content), ttFont):
+def _readExtFileImageData(bitmapObject, name, attrs, content, ttFont):
 	fullPath = attrs['value']
 	with open(fullPath, "rb") as file:
 		bitmapObject.imageData = file.read()
@@ -365,7 +364,7 @@
 # in XML.
 _bitmapGlyphSubclassPrefix = 'ebdt_bitmap_format_'
 
-class BitmapGlyph:
+class BitmapGlyph(object):
 
 	# For the external file format. This can be changed in subclasses. This way
 	# when the extfile option is turned on files have the form: glyphName.ext
@@ -383,15 +382,19 @@
 	def __init__(self, data, ttFont):
 		self.data = data
 		self.ttFont = ttFont
+		# TODO Currently non-lazy decompilation is untested here...
+		#if not ttFont.lazy:
+		#	self.decompile()
+		#	del self.data
 
 	def __getattr__(self, attr):
 		# Allow lazy decompile.
 		if attr[:2] == '__':
-			raise AttributeError, attr
-		if self.data == None:
-			raise AttributeError, attr
+			raise AttributeError(attr)
+		if not hasattr(self, "data"):
+			raise AttributeError(attr)
 		self.decompile()
-		self.data = None
+		del self.data
 		return getattr(self, attr)
 
 	# Not a fan of this but it is needed for safer safety checking.
@@ -409,16 +412,18 @@
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
-		self.readMetrics((name, attrs, content), ttFont)
+	def fromXML(self, name, attrs, content, ttFont):
+		self.readMetrics(name, attrs, content, ttFont)
 		for element in content:
-			if type(element) != TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attr, content = element
+			if not name.endswith('imagedata'):
+				continue
 			# Chop off 'imagedata' from the tag to get just the option.
 			option = name[:-len('imagedata')]
-			if option in self.__class__.xmlDataFunctions:
-				self.readData(element, ttFont)
+			assert option in self.__class__.xmlDataFunctions
+			self.readData(name, attrs, content, ttFont)
 
 	# Some of the glyphs have the metrics. This allows for metrics to be
 	# added if the glyph format has them. Default behavior is to do nothing.
@@ -426,7 +431,7 @@
 		pass
 
 	# The opposite of write metrics.
-	def readMetrics(self, (name, attrs, content), ttFont):
+	def readMetrics(self, name, attrs, content, ttFont):
 		pass
 
 	def writeData(self, strikeIndex, glyphName, writer, ttFont):
@@ -436,11 +441,11 @@
 			writeFunc = _writeRawImageData
 		writeFunc(strikeIndex, glyphName, self, writer, ttFont)
 
-	def readData(self, (name, attrs, content), ttFont):
+	def readData(self, name, attrs, content, ttFont):
 		# Chop off 'imagedata' from the tag to get just the option.
 		option = name[:-len('imagedata')]
 		writeFunc, readFunc = self.__class__.xmlDataFunctions[option]
-		readFunc(self, (name, attrs, content), ttFont)
+		readFunc(self, name, attrs, content, ttFont)
 
 
 # A closure for creating a mixin for the two types of metrics handling.
@@ -454,21 +459,21 @@
 	metricsId = metricStrings.index(curMetricsName)
 	oppositeMetricsName = metricStrings[1-metricsId]
 
-	class BitmapPlusMetricsMixin:
+	class BitmapPlusMetricsMixin(object):
 
 		def writeMetrics(self, writer, ttFont):
 			self.metrics.toXML(writer, ttFont)
 
-		def readMetrics(self, (name, attrs, content), ttFont):
+		def readMetrics(self, name, attrs, content, ttFont):
 			for element in content:
-				if type(element) != TupleType:
+				if not isinstance(element, tuple):
 					continue
 				name, attrs, content = element
 				if name == curMetricsName:
 					self.metrics = metricsClass()
-					self.metrics.fromXML(element, ttFont)
+					self.metrics.fromXML(name, attrs, content, ttFont)
 				elif name == oppositeMetricsName:
-					print "Warning: %s being ignored in format %d." % oppositeMetricsName, self.getFormat()
+					print("Warning: %s being ignored in format %d." % oppositeMetricsName, self.getFormat())
 
 	return BitmapPlusMetricsMixin
 
@@ -479,7 +484,7 @@
 # Data that is bit aligned can be tricky to deal with. These classes implement
 # helper functionality for dealing with the data and getting a particular row
 # of bitwise data. Also helps implement fancy data export/import in XML.
-class BitAlignedBitmapMixin:
+class BitAlignedBitmapMixin(object):
 
 	def _getBitRange(self, row, bitDepth, metrics):
 		rowBits = (bitDepth * metrics.width)
@@ -487,7 +492,7 @@
 		return (bitOffset, bitOffset+rowBits)
 
 	def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
-		if metrics == None:
+		if metrics is None:
 			metrics = self.metrics
 		assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
 
@@ -509,55 +514,55 @@
 		dataList = []
 		bitRange = self._getBitRange(row, bitDepth, metrics)
 		stepRange = bitRange + (8,)
-		for curBit in xrange(*stepRange):
+		for curBit in range(*stepRange):
 			endBit = min(curBit+8, bitRange[1])
 			numBits = endBit - curBit
 			cutPoint = curBit % 8
-			firstByteLoc = curBit / 8
-			secondByteLoc = endBit / 8
+			firstByteLoc = curBit // 8
+			secondByteLoc = endBit // 8
 			if firstByteLoc < secondByteLoc:
 				numBitsCut = 8 - cutPoint
 			else:
 				numBitsCut = endBit - curBit
 			curByte = _reverseBytes(self.imageData[firstByteLoc])
-			firstHalf = ord(curByte) >> cutPoint
+			firstHalf = byteord(curByte) >> cutPoint
 			firstHalf = ((1<<numBitsCut)-1) & firstHalf
 			newByte = firstHalf
 			if firstByteLoc < secondByteLoc and secondByteLoc < len(self.imageData):
 				curByte = _reverseBytes(self.imageData[secondByteLoc])
-				secondHalf = ord(curByte) << numBitsCut
+				secondHalf = byteord(curByte) << numBitsCut
 				newByte = (firstHalf | secondHalf) & ((1<<numBits)-1)
-			dataList.append(chr(newByte))
+			dataList.append(bytechr(newByte))
 
 		# The way the data is kept is opposite the algorithm used.
-		data = string.join(dataList, "")
+		data = bytesjoin(dataList)
 		if not reverseBytes:
 			data = _reverseBytes(data)
 		return data
 
 	def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
-		if metrics == None:
+		if metrics is None:
 			metrics = self.metrics
 		if not reverseBytes:
-			dataRows = map(_reverseBytes, dataRows)
+			dataRows = list(map(_reverseBytes, dataRows))
 
 		# Keep track of a list of ordinal values as they are easier to modify
 		# than a list of strings. Map to actual strings later.
-		numBytes = (self._getBitRange(len(dataRows), bitDepth, metrics)[0] + 7) / 8
+		numBytes = (self._getBitRange(len(dataRows), bitDepth, metrics)[0] + 7) // 8
 		ordDataList = [0] * numBytes
 		for row, data in enumerate(dataRows):
 			bitRange = self._getBitRange(row, bitDepth, metrics)
 			stepRange = bitRange + (8,)
-			for curBit, curByte in itertools.izip(xrange(*stepRange), data):
+			for curBit, curByte in zip(range(*stepRange), data):
 				endBit = min(curBit+8, bitRange[1])
 				cutPoint = curBit % 8
-				firstByteLoc = curBit / 8
-				secondByteLoc = endBit / 8
+				firstByteLoc = curBit // 8
+				secondByteLoc = endBit // 8
 				if firstByteLoc < secondByteLoc:
 					numBitsCut = 8 - cutPoint
 				else:
 					numBitsCut = endBit - curBit
-				curByte = ord(curByte)
+				curByte = byteord(curByte)
 				firstByte = curByte & ((1<<numBitsCut)-1)
 				ordDataList[firstByteLoc] |= (firstByte << cutPoint)
 				if firstByteLoc < secondByteLoc and secondByteLoc < numBytes:
@@ -565,17 +570,17 @@
 					ordDataList[secondByteLoc] |= secondByte
 
 		# Save the image data with the bits going the correct way.
-		self.imageData = _reverseBytes(string.join(map(chr, ordDataList), ""))
+		self.imageData = _reverseBytes(bytesjoin(map(bytechr, ordDataList)))
 
-class ByteAlignedBitmapMixin:
+class ByteAlignedBitmapMixin(object):
 
 	def _getByteRange(self, row, bitDepth, metrics):
-		rowBytes = (bitDepth * metrics.width + 7) / 8
+		rowBytes = (bitDepth * metrics.width + 7) // 8
 		byteOffset = row * rowBytes
 		return (byteOffset, byteOffset+rowBytes)
 
 	def getRow(self, row, bitDepth=1, metrics=None, reverseBytes=False):
-		if metrics == None:
+		if metrics is None:
 			metrics = self.metrics
 		assert 0 <= row and row < metrics.height, "Illegal row access in bitmap"
 		byteRange = self._getByteRange(row, bitDepth, metrics)
@@ -585,11 +590,11 @@
 		return data
 
 	def setRows(self, dataRows, bitDepth=1, metrics=None, reverseBytes=False):
-		if metrics == None:
+		if metrics is None:
 			metrics = self.metrics
 		if reverseBytes:
 			dataRows = map(_reverseBytes, dataRows)
-		self.imageData = string.join(dataRows, "")
+		self.imageData = bytesjoin(dataRows)
 
 class ebdt_bitmap_format_1(ByteAlignedBitmapMixin, BitmapPlusSmallMetricsMixin, BitmapGlyph):
 
@@ -665,24 +670,24 @@
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
-		self.readMetrics((name, attrs, content), ttFont)
+	def fromXML(self, name, attrs, content, ttFont):
+		self.readMetrics(name, attrs, content, ttFont)
 		for element in content:
-			if type(element) != TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attr, content = element
 			if name == 'components':
 				self.componentArray = []
 				for compElement in content:
-					if type(compElement) != TupleType:
+					if not isinstance(compElement, tuple):
 						continue
-					name, attr, content = compElement
+					name, attrs, content = compElement
 					if name == 'ebdtComponent':
 						curComponent = EbdtComponent()
-						curComponent.fromXML(compElement, ttFont)
+						curComponent.fromXML(name, attrs, content, ttFont)
 						self.componentArray.append(curComponent)
 					else:
-						print "Warning: '%s' being ignored in component array." % name
+						print("Warning: '%s' being ignored in component array." % name)
 
 
 class ebdt_bitmap_format_8(BitmapPlusSmallMetricsMixin, ComponentBitmapGlyph):
@@ -695,7 +700,7 @@
 		(numComponents,) = struct.unpack(">H", data[:2])
 		data = data[2:]
 		self.componentArray = []
-		for i in xrange(numComponents):
+		for i in range(numComponents):
 			curComponent = EbdtComponent()
 			dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
 			curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
@@ -704,12 +709,12 @@
 	def compile(self, ttFont):
 		dataList = []
 		dataList.append(sstruct.pack(smallGlyphMetricsFormat, self.metrics))
-		dataList.append('\0')
+		dataList.append(b'\0')
 		dataList.append(struct.pack(">H", len(self.componentArray)))
 		for curComponent in self.componentArray:
 			curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
 			dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
-		return string.join(dataList, "")
+		return bytesjoin(dataList)
 
 
 class ebdt_bitmap_format_9(BitmapPlusBigMetricsMixin, ComponentBitmapGlyph):
@@ -720,7 +725,7 @@
 		(numComponents,) = struct.unpack(">H", data[:2])
 		data = data[2:]
 		self.componentArray = []
-		for i in xrange(numComponents):
+		for i in range(numComponents):
 			curComponent = EbdtComponent()
 			dummy, data = sstruct.unpack2(ebdtComponentFormat, data, curComponent)
 			curComponent.name = self.ttFont.getGlyphName(curComponent.glyphCode)
@@ -733,7 +738,7 @@
 		for curComponent in self.componentArray:
 			curComponent.glyphCode = ttFont.getGlyphID(curComponent.name)
 			dataList.append(sstruct.pack(ebdtComponentFormat, curComponent))
-		return string.join(dataList, "")
+		return bytesjoin(dataList)
 
 
 # Dictionary of bitmap formats to the class representing that format
diff --git a/Lib/fontTools/ttLib/tables/E_B_L_C_.py b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
index 500e833..f885318 100644
--- a/Lib/fontTools/ttLib/tables/E_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
@@ -1,13 +1,12 @@
-
-import DefaultTable
-import string
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-import itertools
-from types import TupleType
-from collections import deque
+from . import DefaultTable
 from fontTools.misc.textTools import safeEval
-from BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
+import struct
+import itertools
+from collections import deque
 
 eblcHeaderFormat = """
 	> # big endian
@@ -76,7 +75,7 @@
 		dummy, data = sstruct.unpack2(eblcHeaderFormat, data, self)
 
 		self.strikes = []
-		for curStrikeIndex in xrange(self.numSizes):
+		for curStrikeIndex in range(self.numSizes):
 			curStrike = Strike()
 			self.strikes.append(curStrike)
 			curTable = curStrike.bitmapSizeTable
@@ -89,7 +88,7 @@
 
 		for curStrike in self.strikes:
 			curTable = curStrike.bitmapSizeTable
-			for subtableIndex in xrange(curTable.numberOfIndexSubTables):
+			for subtableIndex in range(curTable.numberOfIndexSubTables):
 				lowerBound = curTable.indexSubTableArrayOffset + subtableIndex * indexSubTableArraySize
 				upperBound = lowerBound + indexSubTableArraySize
 				data = origData[lowerBound:upperBound]
@@ -171,7 +170,7 @@
 			indexSubTableDataList = []
 			for indexSubTable in curStrike.indexSubTables:
 				indexSubTable.additionalOffsetToIndexSubtable = dataSize - curTable.indexSubTableArrayOffset
-				glyphIds = map(ttFont.getGlyphID, indexSubTable.names)
+				glyphIds = list(map(ttFont.getGlyphID, indexSubTable.names))
 				indexSubTable.firstGlyphIndex = min(glyphIds)
 				indexSubTable.lastGlyphIndex = max(glyphIds)
 				data = indexSubTable.compile(ttFont)
@@ -198,7 +197,7 @@
 			dataList.append(data)
 		dataList.extend(indexSubTablePairDataList)
 
-		return string.join(dataList, "")
+		return bytesjoin(dataList)
 
 	def toXML(self, writer, ttFont):
 		writer.simpletag('header', [('version', self.version)])
@@ -206,7 +205,7 @@
 		for curIndex, curStrike in enumerate(self.strikes):
 			curStrike.toXML(curIndex, writer, ttFont)
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == 'header':
 			self.version = safeEval(attrs['version'])
 		elif name == 'strike':
@@ -214,16 +213,16 @@
 				self.strikes = []
 			strikeIndex = safeEval(attrs['index'])
 			curStrike = Strike()
-			curStrike.fromXML((name, attrs, content), ttFont, self)
+			curStrike.fromXML(name, attrs, content, ttFont, self)
 
 			# Grow the strike array to the appropriate size. The XML format
 			# allows for the strike index value to be out of order.
 			if strikeIndex >= len(self.strikes):
 				self.strikes += [None] * (strikeIndex + 1 - len(self.strikes))
-			assert self.strikes[strikeIndex] == None, "Duplicate strike EBLC indices."
+			assert self.strikes[strikeIndex] is None, "Duplicate strike EBLC indices."
 			self.strikes[strikeIndex] = curStrike
 
-class Strike:
+class Strike(object):
 
 	def __init__(self):
 		self.bitmapSizeTable = BitmapSizeTable()
@@ -240,23 +239,23 @@
 		writer.endtag('strike')
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont, locator):
+	def fromXML(self, name, attrs, content, ttFont, locator):
 		for element in content:
-			if type(element) != TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
 			if name == 'bitmapSizeTable':
-				self.bitmapSizeTable.fromXML(element, ttFont)
+				self.bitmapSizeTable.fromXML(name, attrs, content, ttFont)
 			elif name.startswith(_indexSubTableSubclassPrefix):
 				indexFormat = safeEval(name[len(_indexSubTableSubclassPrefix):])
 				indexFormatClass = locator.getIndexFormatClass(indexFormat)
 				indexSubTable = indexFormatClass(None, None)
 				indexSubTable.indexFormat = indexFormat
-				indexSubTable.fromXML(element, ttFont)
+				indexSubTable.fromXML(name, attrs, content, ttFont)
 				self.indexSubTables.append(indexSubTable)
 
 
-class BitmapSizeTable:
+class BitmapSizeTable(object):
 
 	# Returns all the simple metric names that bitmap size table
 	# cares about in terms of XML creation.
@@ -277,27 +276,27 @@
 		writer.endtag('bitmapSizeTable')
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		# Create a lookup for all the simple names that make sense to
 		# bitmap size table. Only read the information from these names.
 		dataNames = set(self._getXMLMetricNames())
 		for element in content:
-			if type(element) != TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
 			if name == 'sbitLineMetrics':
 				direction = attrs['direction']
 				assert direction in ('hori', 'vert'), "SbitLineMetrics direction specified invalid."
 				metricObj = SbitLineMetrics()
-				metricObj.fromXML(element, ttFont)
+				metricObj.fromXML(name, attrs, content, ttFont)
 				vars(self)[direction] = metricObj
 			elif name in dataNames:
 				vars(self)[name] = safeEval(attrs['value'])
 			else:
-				print "Warning: unknown name '%s' being ignored in BitmapSizeTable." % name
+				print("Warning: unknown name '%s' being ignored in BitmapSizeTable." % name)
 
 
-class SbitLineMetrics:
+class SbitLineMetrics(object):
 
 	def toXML(self, name, writer, ttFont):
 		writer.begintag('sbitLineMetrics', [('direction', name)])
@@ -308,10 +307,10 @@
 		writer.endtag('sbitLineMetrics')
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		metricNames = set(sstruct.getformat(sbitLineMetricsFormat)[1])
 		for element in content:
-			if type(element) != TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
 			if name in metricNames:
@@ -320,21 +319,24 @@
 # Important information about the naming scheme. Used for identifying subtables.
 _indexSubTableSubclassPrefix = 'eblc_index_sub_table_'
 
-class EblcIndexSubTable:
+class EblcIndexSubTable(object):
 
 	def __init__(self, data, ttFont):
 		self.data = data
 		self.ttFont = ttFont
+		# TODO Currently non-lazy decompiling doesn't work for this class...
+		#if not ttFont.lazy:
+		#	self.decompile()
+		#	del self.data, self.ttFont
 
 	def __getattr__(self, attr):
 		# Allow lazy decompile.
 		if attr[:2] == '__':
-			raise AttributeError, attr
-		if self.data == None:
-			raise AttributeError, attr
+			raise AttributeError(attr)
+		if not hasattr(self, "data"):
+			raise AttributeError(attr)
 		self.decompile()
-		self.data = None
-		self.ttFont = None
+		del self.data, self.ttFont
 		return getattr(self, attr)
 
 	# This method just takes care of the indexSubHeader. Implementing subclasses
@@ -358,13 +360,13 @@
 		# For font debugging of consecutive formats the ids are also written.
 		# The ids are not read when moving from the XML format.
 		glyphIds = map(ttFont.getGlyphID, self.names)
-		for glyphName, glyphId in itertools.izip(self.names, glyphIds):
+		for glyphName, glyphId in zip(self.names, glyphIds):
 			writer.simpletag('glyphLoc', name=glyphName, id=glyphId)
 			writer.newline()
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		# Read all the attributes. Even though the glyph indices are
 		# recalculated, they are still read in case there needs to
 		# be an immediate export of the data.
@@ -372,11 +374,11 @@
 		self.firstGlyphIndex = safeEval(attrs['firstGlyphIndex'])
 		self.lastGlyphIndex = safeEval(attrs['lastGlyphIndex'])
 
-		self.readMetrics((name, attrs, content), ttFont)
+		self.readMetrics(name, attrs, content, ttFont)
 
 		self.names = []
 		for element in content:
-			if type(element) != TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
 			if name == 'glyphLoc':
@@ -389,7 +391,7 @@
 		pass
 
 	# A helper method that is the inverse of writeMetrics.
-	def readMetrics(self, (name, attrs, content), ttFont):
+	def readMetrics(self, name, attrs, content, ttFont):
 		pass
 
 	# This method is for fixed glyph data sizes. There are formats where
@@ -406,11 +408,12 @@
 	def removeSkipGlyphs(self):
 		# Determines if a name, location pair is a valid data location.
 		# Skip glyphs are marked when the size is equal to zero.
-		def isValidLocation((name, (startByte, endByte))):
+		def isValidLocation(args):
+			(name, (startByte, endByte)) = args
 			return startByte < endByte
 		# Remove all skip glyphs.
-		dataPairs = filter(isValidLocation, zip(self.names, self.locations))
-		self.names, self.locations = map(list, zip(*dataPairs))
+		dataPairs = list(filter(isValidLocation, zip(self.names, self.locations)))
+		self.names, self.locations = list(map(list, zip(*dataPairs)))
 
 # A closure for creating a custom mixin. This is done because formats 1 and 3
 # are very similar. The only difference between them is the size per offset
@@ -421,37 +424,37 @@
 	dataFormat = '>'+formatStringForDataType
 	offsetDataSize = struct.calcsize(dataFormat)
 
-	class OffsetArrayIndexSubTableMixin:
+	class OffsetArrayIndexSubTableMixin(object):
 
 		def decompile(self):
 
 			numGlyphs = self.lastGlyphIndex - self.firstGlyphIndex + 1
-			indexingOffsets = [glyphIndex * offsetDataSize for glyphIndex in xrange(numGlyphs+2)]
+			indexingOffsets = [glyphIndex * offsetDataSize for glyphIndex in range(numGlyphs+2)]
 			indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
 			offsetArray = [struct.unpack(dataFormat, self.data[slice(*loc)])[0] for loc in indexingLocations]
 
-			glyphIds = range(self.firstGlyphIndex, self.lastGlyphIndex+1)
+			glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
 			modifiedOffsets = [offset + self.imageDataOffset for offset in offsetArray]
-			self.locations = zip(modifiedOffsets, modifiedOffsets[1:])
+			self.locations = list(zip(modifiedOffsets, modifiedOffsets[1:]))
 
-			self.names = map(self.ttFont.getGlyphName, glyphIds)
+			self.names = list(map(self.ttFont.getGlyphName, glyphIds))
 			self.removeSkipGlyphs()
 
 		def compile(self, ttFont):
 			# First make sure that all the data lines up properly. Formats 1 and 3
 			# must have all its data lined up consecutively. If not this will fail.
-			for curLoc, nxtLoc in itertools.izip(self.locations, self.locations[1:]):
+			for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
 				assert curLoc[1] == nxtLoc[0], "Data must be consecutive in indexSubTable offset formats"
 
-			glyphIds = map(ttFont.getGlyphID, self.names)
+			glyphIds = list(map(ttFont.getGlyphID, self.names))
 			# Make sure that all ids are sorted strictly increasing.
-			assert all(glyphIds[i] < glyphIds[i+1] for i in xrange(len(glyphIds)-1))
+			assert all(glyphIds[i] < glyphIds[i+1] for i in range(len(glyphIds)-1))
 
 			# Run a simple algorithm to add skip glyphs to the data locations at
 			# the places where an id is not present.
 			idQueue = deque(glyphIds)
 			locQueue = deque(self.locations)
-			allGlyphIds = range(self.firstGlyphIndex, self.lastGlyphIndex+1)
+			allGlyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
 			allLocations = []
 			for curId in allGlyphIds:
 				if curId != idQueue[0]:
@@ -475,38 +478,38 @@
 			# Take care of any padding issues. Only occurs in format 3.
 			if offsetDataSize * len(dataList) % 4 != 0:
 				dataList.append(struct.pack(dataFormat, 0))
-			return string.join(dataList, "")
+			return bytesjoin(dataList)
 
 	return OffsetArrayIndexSubTableMixin
 
 # A Mixin for functionality shared between the different kinds
 # of fixed sized data handling. Both kinds have big metrics so
 # that kind of special processing is also handled in this mixin.
-class FixedSizeIndexSubTableMixin:
+class FixedSizeIndexSubTableMixin(object):
 
 	def writeMetrics(self, writer, ttFont):
 		writer.simpletag('imageSize', value=self.imageSize)
 		writer.newline()
 		self.metrics.toXML(writer, ttFont)
 
-	def readMetrics(self, (name, attrs, content), ttFont):
+	def readMetrics(self, name, attrs, content, ttFont):
 		for element in content:
-			if type(element) != TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
 			if name == 'imageSize':
 				self.imageSize = safeEval(attrs['value'])
 			elif name == BigGlyphMetrics.__name__:
 				self.metrics = BigGlyphMetrics()
-				self.metrics.fromXML(element, ttFont)
+				self.metrics.fromXML(name, attrs, content, ttFont)
 			elif name == SmallGlyphMetrics.__name__:
-				print "Warning: SmallGlyphMetrics being ignored in format %d." % self.indexFormat
+				print("Warning: SmallGlyphMetrics being ignored in format %d." % self.indexFormat)
 
 	def padBitmapData(self, data):
 		# Make sure that the data isn't bigger than the fixed size.
 		assert len(data) <= self.imageSize, "Data in indexSubTable format %d must be less than the fixed size." % self.indexFormat
 		# Pad the data so that it matches the fixed size.
-		pad = (self.imageSize - len(data)) * '\0'
+		pad = (self.imageSize - len(data)) * b'\0'
 		return data + pad
 
 class eblc_index_sub_table_1(_createOffsetArrayIndexSubTableMixin('L'), EblcIndexSubTable):
@@ -518,21 +521,21 @@
 		(self.imageSize,) = struct.unpack(">L", self.data[:4])
 		self.metrics = BigGlyphMetrics()
 		sstruct.unpack2(bigGlyphMetricsFormat, self.data[4:], self.metrics)
-		glyphIds = range(self.firstGlyphIndex, self.lastGlyphIndex+1)
-		offsets = [self.imageSize * i + self.imageDataOffset for i in xrange(len(glyphIds)+1)]
-		self.locations = zip(offsets, offsets[1:])
-		self.names = map(self.ttFont.getGlyphName, glyphIds)
+		glyphIds = list(range(self.firstGlyphIndex, self.lastGlyphIndex+1))
+		offsets = [self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds)+1)]
+		self.locations = list(zip(offsets, offsets[1:]))
+		self.names = list(map(self.ttFont.getGlyphName, glyphIds))
 
 	def compile(self, ttFont):
-		glyphIds = map(ttFont.getGlyphID, self.names)
+		glyphIds = list(map(ttFont.getGlyphID, self.names))
 		# Make sure all the ids are consecutive. This is required by Format 2.
-		assert glyphIds == range(self.firstGlyphIndex, self.lastGlyphIndex+1), "Format 2 ids must be consecutive."
+		assert glyphIds == list(range(self.firstGlyphIndex, self.lastGlyphIndex+1)), "Format 2 ids must be consecutive."
 		self.imageDataOffset = min(zip(*self.locations)[0])
 
 		dataList = [EblcIndexSubTable.compile(self, ttFont)]
 		dataList.append(struct.pack(">L", self.imageSize))
 		dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
-		return string.join(dataList, "")
+		return bytesjoin(dataList)
 
 class eblc_index_sub_table_3(_createOffsetArrayIndexSubTableMixin('H'), EblcIndexSubTable):
 	pass
@@ -543,21 +546,21 @@
 
 		(numGlyphs,) = struct.unpack(">L", self.data[:4])
 		data = self.data[4:]
-		indexingOffsets = [glyphIndex * codeOffsetPairSize for glyphIndex in xrange(numGlyphs+2)]
+		indexingOffsets = [glyphIndex * codeOffsetPairSize for glyphIndex in range(numGlyphs+2)]
 		indexingLocations = zip(indexingOffsets, indexingOffsets[1:])
 		glyphArray = [struct.unpack(codeOffsetPairFormat, data[slice(*loc)]) for loc in indexingLocations]
-		glyphIds, offsets = map(list, zip(*glyphArray))
+		glyphIds, offsets = list(map(list, zip(*glyphArray)))
 		# There are one too many glyph ids. Get rid of the last one.
 		glyphIds.pop()
 
 		offsets = [offset + self.imageDataOffset for offset in offsets]
-		self.locations = zip(offsets, offsets[1:])
-		self.names = map(self.ttFont.getGlyphName, glyphIds)
+		self.locations = list(zip(offsets, offsets[1:]))
+		self.names = list(map(self.ttFont.getGlyphName, glyphIds))
 
 	def compile(self, ttFont):
 		# First make sure that all the data lines up properly. Format 4
 		# must have all its data lined up consecutively. If not this will fail.
-		for curLoc, nxtLoc in itertools.izip(self.locations, self.locations[1:]):
+		for curLoc, nxtLoc in zip(self.locations, self.locations[1:]):
 			assert curLoc[1] == nxtLoc[0], "Data must be consecutive in indexSubTable format 4"
 
 		offsets = list(self.locations[0]) + [loc[1] for loc in self.locations[1:]]
@@ -566,15 +569,15 @@
 		# and allows imageDataOffset to not be required to be in the XML version.
 		self.imageDataOffset = min(offsets)
 		offsets = [offset - self.imageDataOffset for offset in offsets]
-		glyphIds = map(ttFont.getGlyphID, self.names)
+		glyphIds = list(map(ttFont.getGlyphID, self.names))
 		# Create an iterator over the ids plus a padding value.
 		idsPlusPad = list(itertools.chain(glyphIds, [0]))
 
 		dataList = [EblcIndexSubTable.compile(self, ttFont)]
 		dataList.append(struct.pack(">L", len(glyphIds)))
-		tmp = [struct.pack(codeOffsetPairFormat, *cop) for cop in itertools.izip(idsPlusPad, offsets)]
+		tmp = [struct.pack(codeOffsetPairFormat, *cop) for cop in zip(idsPlusPad, offsets)]
 		dataList += tmp
-		data = string.join(dataList, "")
+		data = bytesjoin(dataList)
 		return data
 
 class eblc_index_sub_table_5(FixedSizeIndexSubTableMixin, EblcIndexSubTable):
@@ -586,23 +589,23 @@
 		self.metrics, data = sstruct.unpack2(bigGlyphMetricsFormat, data, BigGlyphMetrics())
 		(numGlyphs,) = struct.unpack(">L", data[:4])
 		data = data[4:]
-		glyphIds = [struct.unpack(">H", data[2*i:2*(i+1)])[0] for i in xrange(numGlyphs)]
+		glyphIds = [struct.unpack(">H", data[2*i:2*(i+1)])[0] for i in range(numGlyphs)]
 
-		offsets = [self.imageSize * i + self.imageDataOffset for i in xrange(len(glyphIds)+1)]
-		self.locations = zip(offsets, offsets[1:])
-		self.names = map(self.ttFont.getGlyphName, glyphIds)
+		offsets = [self.imageSize * i + self.imageDataOffset for i in range(len(glyphIds)+1)]
+		self.locations = list(zip(offsets, offsets[1:]))
+		self.names = list(map(self.ttFont.getGlyphName, glyphIds))
 
 	def compile(self, ttFont):
 		self.imageDataOffset = min(zip(*self.locations)[0])
 		dataList = [EblcIndexSubTable.compile(self, ttFont)]
 		dataList.append(struct.pack(">L", self.imageSize))
 		dataList.append(sstruct.pack(bigGlyphMetricsFormat, self.metrics))
-		glyphIds = map(ttFont.getGlyphID, self.names)
+		glyphIds = list(map(ttFont.getGlyphID, self.names))
 		dataList.append(struct.pack(">L", len(glyphIds)))
 		dataList += [struct.pack(">H", curId) for curId in glyphIds]
 		if len(glyphIds) % 2 == 1:
 			dataList.append(struct.pack(">H", 0))
-		return string.join(dataList, "")
+		return bytesjoin(dataList)
 
 # Dictionary of indexFormat to the class representing that format.
 eblc_sub_table_classes = {
diff --git a/Lib/fontTools/ttLib/tables/F_F_T_M_.py b/Lib/fontTools/ttLib/tables/F_F_T_M_.py
new file mode 100644
index 0000000..e8b1d29
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/F_F_T_M_.py
@@ -0,0 +1,46 @@
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import safeEval
+from ._h_e_a_d import mac_epoch_diff
+from . import DefaultTable
+import time
+import calendar
+
+FFTMFormat = """
+		>	# big endian
+		version:        I
+		FFTimeStamp:    Q
+		sourceCreated:  Q
+		sourceModified: Q
+"""
+
+class table_F_F_T_M_(DefaultTable.DefaultTable):
+
+  def decompile(self, data, ttFont):
+    dummy, rest = sstruct.unpack2(FFTMFormat, data, self)
+
+  def compile(self, ttFont):
+    data = sstruct.pack(FFTMFormat, self)
+    return data
+
+  def toXML(self, writer, ttFont):
+    writer.comment("FontForge's timestamp, font source creation and modification dates")
+    writer.newline()
+    formatstring, names, fixes = sstruct.getformat(FFTMFormat)
+    for name in names:
+      value = getattr(self, name)
+      if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
+        try:
+          value = time.asctime(time.gmtime(max(0, value + mac_epoch_diff)))
+        except ValueError:
+          value = time.asctime(time.gmtime(0))
+      writer.simpletag(name, value=value)
+      writer.newline()
+
+  def fromXML(self, name, attrs, content, ttFont):
+    value = attrs["value"]
+    if name in ("FFTimeStamp", "sourceCreated", "sourceModified"):
+      value = calendar.timegm(time.strptime(value)) - mac_epoch_diff
+    else:
+      value = safeEval(value)
+    setattr(self, name, value)
\ No newline at end of file
diff --git a/Lib/fontTools/ttLib/tables/G_D_E_F_.py b/Lib/fontTools/ttLib/tables/G_D_E_F_.py
index d763a5d..d4a5741 100644
--- a/Lib/fontTools/ttLib/tables/G_D_E_F_.py
+++ b/Lib/fontTools/ttLib/tables/G_D_E_F_.py
@@ -1,4 +1,4 @@
-from otBase import BaseTTXConverter
+from .otBase import BaseTTXConverter
 
 
 class table_G_D_E_F_(BaseTTXConverter):
diff --git a/Lib/fontTools/ttLib/tables/G_M_A_P_.py b/Lib/fontTools/ttLib/tables/G_M_A_P_.py
index c4f64f9..57eb8f1 100644
--- a/Lib/fontTools/ttLib/tables/G_M_A_P_.py
+++ b/Lib/fontTools/ttLib/tables/G_M_A_P_.py
@@ -1,7 +1,8 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-from types import StringType
-from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
 
 GMAPFormat = """
 		>	# big endian
@@ -26,7 +27,7 @@
 		
 
 
-class GMAPRecord:
+class GMAPRecord(object):
 	def __init__(self, uv = 0, cid = 0, gid = 0, ggid = 0, name = ""):
 		self.UV = uv
 		self.cid = cid
@@ -51,20 +52,16 @@
 		writer.newline()
 
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		value = attrs["value"]
 		if name == "GlyphletName":
 			self.name = value
 		else:
-			try:
-				value = safeEval(value)
-			except OverflowError:
-				value = long(value)
-			setattr(self, name, value)
+			setattr(self, name, safeEval(value))
 		
 
 	def compile(self, ttFont):
-		if 	self.UV == None:
+		if 	self.UV is None:
 			self.UV = 0
 		nameLen =  len(self.name)
 		if nameLen < 32:
@@ -82,7 +79,7 @@
 	
 	def decompile(self, data, ttFont):
 		dummy, newData = sstruct.unpack2(GMAPFormat, data, self)
-		self.psFontName = newData[:self.fontNameLength]
+		self.psFontName = tostr(newData[:self.fontNameLength])
 		assert (self.recordsOffset % 4) == 0, "GMAP error: recordsOffset is not 32 bit aligned."
 		newData = data[self.recordsOffset:]
 		self.gmapRecords = []
@@ -95,10 +92,10 @@
 	def compile(self, ttFont):
 		self.recordsCount = len(self.gmapRecords)
 		self.fontNameLength = len(self.psFontName)
-		self.recordsOffset = 4 *(((self.fontNameLength + 12)  + 3) /4)
+		self.recordsOffset = 4 *(((self.fontNameLength + 12)  + 3) // 4)
 		data = sstruct.pack(GMAPFormat, self)
-		data = data + self.psFontName
-		data = data + "\0" * (self.recordsOffset - len(data))
+		data = data + tobytes(self.psFontName)
+		data = data + b"\0" * (self.recordsOffset - len(data))
 		for record in self.gmapRecords:
 			data = data + record.compile(ttFont)
 		return data
@@ -117,23 +114,20 @@
 		for gmapRecord in self.gmapRecords:
 			gmapRecord.toXML(writer, ttFont)
 		
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == "GMAPRecord":
 			if not hasattr(self, "gmapRecords"):
 				self.gmapRecords = []
 			gmapRecord = GMAPRecord()
 			self.gmapRecords.append(gmapRecord)
 			for element in content:
-				if isinstance(element, StringType):
+				if isinstance(element, basestring):
 					continue
-				gmapRecord.fromXML(element, ttFont)
+				name, attrs, content = element
+				gmapRecord.fromXML(name, attrs, content, ttFont)
 		else:
 			value = attrs["value"]
 			if name == "PSFontName":
 				self.psFontName = value
 			else:	
-				try:
-					value = safeEval(value)
-				except OverflowError:
-					value = long(value)
-				setattr(self, name, value)
+				setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/G_P_K_G_.py b/Lib/fontTools/ttLib/tables/G_P_K_G_.py
index 6321093..4acccaa 100644
--- a/Lib/fontTools/ttLib/tables/G_P_K_G_.py
+++ b/Lib/fontTools/ttLib/tables/G_P_K_G_.py
@@ -1,10 +1,10 @@
-import sys
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-import array
-from types import StringType
 from fontTools.misc.textTools import safeEval, readHex
-from fontTools import ttLib
+from . import DefaultTable
+import sys
+import array
 
 GPKGFormat = """
 		>	# big endian
@@ -25,7 +25,7 @@
 		GMAPoffsets = array.array("I")
 		endPos = (self.numGMAPs+1) * 4
 		GMAPoffsets.fromstring(newData[:endPos])
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			GMAPoffsets.byteswap()
 		self.GMAPs = []
 		for i in range(self.numGMAPs):
@@ -36,7 +36,7 @@
 		endPos = pos + (self.numGlyplets + 1)*4
 		glyphletOffsets = array.array("I")
 		glyphletOffsets.fromstring(newData[pos:endPos])
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			glyphletOffsets.byteswap()
 		self.glyphlets = []
 		for i in range(self.numGlyplets):
@@ -59,7 +59,7 @@
 			pos += len(self.GMAPs[i-1])
 			GMAPoffsets[i] = pos
 		gmapArray = array.array("I", GMAPoffsets)
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			gmapArray.byteswap()
 		dataList.append(gmapArray.tostring())
 
@@ -68,12 +68,12 @@
 			pos += len(self.glyphlets[i-1])
 			glyphletOffsets[i] = pos
 		glyphletArray = array.array("I", glyphletOffsets)
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			glyphletArray.byteswap()
 		dataList.append(glyphletArray.tostring())
 		dataList += self.GMAPs
 		dataList += self.glyphlets
-		data = "".join(dataList)
+		data = bytesjoin(dataList)
 		return data
 	
 	def toXML(self, writer, ttFont):
@@ -107,12 +107,12 @@
 		writer.endtag("glyphlets")
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == "GMAPs":
 			if not hasattr(self, "GMAPs"):
 				self.GMAPs = []
 			for element in content:
-				if isinstance(element, StringType):
+				if isinstance(element, basestring):
 					continue
 				itemName, itemAttrs, itemContent = element
 				if itemName == "hexdata":
@@ -121,15 +121,10 @@
 			if not hasattr(self, "glyphlets"):
 				self.glyphlets = []
 			for element in content:
-				if isinstance(element, StringType):
+				if isinstance(element, basestring):
 					continue
 				itemName, itemAttrs, itemContent = element
 				if itemName == "hexdata":
 					self.glyphlets.append(readHex(itemContent))
 		else:	
-			value = attrs["value"]
-			try:
-				value = safeEval(value)
-			except OverflowError:
-				value = long(value)
-			setattr(self, name, value)
+			setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/G_P_O_S_.py b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
index 52f44d4..013c820 100644
--- a/Lib/fontTools/ttLib/tables/G_P_O_S_.py
+++ b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
@@ -1,4 +1,4 @@
-from otBase import BaseTTXConverter
+from .otBase import BaseTTXConverter
 
 
 class table_G_P_O_S_(BaseTTXConverter):
diff --git a/Lib/fontTools/ttLib/tables/G_S_U_B_.py b/Lib/fontTools/ttLib/tables/G_S_U_B_.py
index a523c59..4403649 100644
--- a/Lib/fontTools/ttLib/tables/G_S_U_B_.py
+++ b/Lib/fontTools/ttLib/tables/G_S_U_B_.py
@@ -1,4 +1,4 @@
-from otBase import BaseTTXConverter
+from .otBase import BaseTTXConverter
 
 
 class table_G_S_U_B_(BaseTTXConverter):
diff --git a/Lib/fontTools/ttLib/tables/J_S_T_F_.py b/Lib/fontTools/ttLib/tables/J_S_T_F_.py
index 8ff395e..ddf5405 100644
--- a/Lib/fontTools/ttLib/tables/J_S_T_F_.py
+++ b/Lib/fontTools/ttLib/tables/J_S_T_F_.py
@@ -1,4 +1,4 @@
-from otBase import BaseTTXConverter
+from .otBase import BaseTTXConverter
 
 
 class table_J_S_T_F_(BaseTTXConverter):
diff --git a/Lib/fontTools/ttLib/tables/L_T_S_H_.py b/Lib/fontTools/ttLib/tables/L_T_S_H_.py
index 6cd5aad..453f08b 100644
--- a/Lib/fontTools/ttLib/tables/L_T_S_H_.py
+++ b/Lib/fontTools/ttLib/tables/L_T_S_H_.py
@@ -1,7 +1,9 @@
-import DefaultTable
-import array
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import struct
+import array
 
 # 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
@@ -24,7 +26,7 @@
 	
 	def compile(self, ttFont):
 		version = 0
-		names = self.yPels.keys()
+		names = list(self.yPels.keys())
 		numGlyphs = len(names)
 		yPels = [0] * numGlyphs
 		# ouch: the assertion is not true in Chicago!
@@ -35,16 +37,15 @@
 		return struct.pack(">HH", version, numGlyphs) + yPels.tostring()
 	
 	def toXML(self, writer, ttFont):
-		names = self.yPels.keys()
-		names.sort()
+		names = sorted(self.yPels.keys())
 		for name in names:
 			writer.simpletag("yPel", name=name, value=self.yPels[name])
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "yPels"):
 			self.yPels = {}
-		if name <> "yPel":
+		if name != "yPel":
 			return # ignore unknown tags
 		self.yPels[attrs["name"]] = safeEval(attrs["value"])
 
diff --git a/Lib/fontTools/ttLib/tables/M_A_T_H_.py b/Lib/fontTools/ttLib/tables/M_A_T_H_.py
new file mode 100644
index 0000000..d894c08
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/M_A_T_H_.py
@@ -0,0 +1,5 @@
+from .otBase import BaseTTXConverter
+
+
+class table_M_A_T_H_(BaseTTXConverter):
+	pass
diff --git a/Lib/fontTools/ttLib/tables/M_E_T_A_.py b/Lib/fontTools/ttLib/tables/M_E_T_A_.py
index 492ed60..0a3a708 100644
--- a/Lib/fontTools/ttLib/tables/M_E_T_A_.py
+++ b/Lib/fontTools/ttLib/tables/M_E_T_A_.py
@@ -1,10 +1,11 @@
-import DefaultTable
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
-import string
-from types import FloatType, ListType, StringType, TupleType
-import sys
+from . import DefaultTable
+import struct
+
+
 METAHeaderFormat = """
 		>	# big endian
 		tableVersionMajor:			H
@@ -166,28 +167,24 @@
 		for glyphRec in self.glyphRecords:
 			glyphRec.toXML(writer, ttFont)
 		
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == "GlyphRecord":
 			if not hasattr(self, "glyphRecords"):
 				self.glyphRecords = []
 			glyphRec = GlyphRecord()
 			self.glyphRecords.append(glyphRec)
 			for element in content:
-				if isinstance(element, StringType):
+				if isinstance(element, basestring):
 					continue
-				glyphRec.fromXML(element, ttFont)
+				name, attrs, content = element
+				glyphRec.fromXML(name, attrs, content, ttFont)
 			glyphRec.offset = -1
 			glyphRec.nMetaEntry = len(glyphRec.stringRecs)
 		else:			
-			value = attrs["value"]
-			try:
-				value = safeEval(value)
-			except OverflowError:
-				value = long(value)
-			setattr(self, name, value)
+			setattr(self, name, safeEval(attrs["value"]))
 
 
-class GlyphRecord:
+class GlyphRecord(object):
 	def __init__(self):
 		self.glyphID = -1
 		self.nMetaEntry = -1
@@ -207,22 +204,17 @@
 		writer.newline()
 
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == "StringRecord":
 			stringRec = StringRecord()
 			self.stringRecs.append(stringRec)
 			for element in content:
-				if isinstance(element, StringType):
+				if isinstance(element, basestring):
 					continue
-				stringRec.fromXML(element, ttFont)
+				stringRec.fromXML(name, attrs, content, ttFont)
 			stringRec.stringLen = len(stringRec.string)
 		else:			
-			value = attrs["value"]
-			try:
-				value = safeEval(value)
-			except OverflowError:
-				value = long(value)
-			setattr(self, name, value)
+			setattr(self, name, safeEval(attrs["value"]))
 
 	def compile(self, parentTable):
 		data = sstruct.pack(METAGlyphRecordFormat, self)
@@ -232,24 +224,14 @@
 			datum = struct.pack(">L", self.offset)
 		data = data + datum
 		return data
-
-
-	def __cmp__(self, other):
-		"""Compare method, so a list of NameRecords can be sorted
-		according to the spec by just sorting it..."""
-
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
-
-		return cmp(self.glyphID, other.glyphID)
 	
 	def __repr__(self):
 		return "GlyphRecord[ glyphID: " + str(self.glyphID) + ", nMetaEntry: " + str(self.nMetaEntry) + ", offset: " + str(self.offset) + " ]"
 
+# XXX The following two functions are really broken around UTF-8 vs Unicode
 
 def mapXMLToUTF8(string):
-	uString = u""
+	uString = unicode()
 	strLen = len(string)
 	i = 0
 	while i < strLen:
@@ -267,7 +249,7 @@
 			
 			uString = uString + unichr(eval('0x' + valStr))
 		else:
-			uString = uString + unichr(ord(string[i]))
+			uString = uString + unichr(byteord(string[i]))
 		i = i +1
 			
 	return uString.encode('utf8')
@@ -279,18 +261,13 @@
 	for uChar in uString:
 		i = ord(uChar)
 		if (i < 0x80) and (i > 0x1F):
-			string = string + chr(i)
+			string = string + uChar
 		else:
 			string = string + "&#x" + hex(i)[2:] + ";"
 	return string
 
 
-class StringRecord:
-	def __init__(self):
-		self.labelID = -1
-		self.string = ""
-		self.stringLen = -1
-		self.offset = -1
+class StringRecord(object):
 
 	def toXML(self, writer, ttFont):
 		writer.begintag("StringRecord")
@@ -304,16 +281,16 @@
 		writer.endtag("StringRecord")
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
-		value = attrs["value"]
-		if name == "string":
-			self.string = mapXMLToUTF8(value)
-		else:
-			try:
-				value = safeEval(value)
-			except OverflowError:
-				value = long(value)
-			setattr(self, name, value)
+	def fromXML(self, name, attrs, content, ttFont):
+		for element in content:
+			if isinstance(element, basestring):
+				continue
+			name, attrs, content = element
+			value = attrs["value"]
+			if name == "string":
+				self.string = mapXMLToUTF8(value)
+			else:
+				setattr(self, name, safeEval(value))
 
 	def compile(self, parentTable):
 		data = sstruct.pack(METAStringRecordFormat, self)
@@ -323,16 +300,6 @@
 			datum = struct.pack(">L", self.offset)
 		data = data + datum
 		return data
-
-	def __cmp__(self, other):
-		"""Compare method, so a list of NameRecords can be sorted
-		according to the spec by just sorting it..."""
-
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
-
-		return cmp(self.labelID, other.labelID)
 	
 	def __repr__(self):
 		return "StringRecord [ labelID: " + str(self.labelID) + " aka " + getLabelString(self.labelID) \
diff --git a/Lib/fontTools/ttLib/tables/O_S_2f_2.py b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
index 06d8291..f678c17 100644
--- a/Lib/fontTools/ttLib/tables/O_S_2f_2.py
+++ b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
@@ -1,7 +1,9 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval, num2binary, binary2num
-from types import TupleType
+from . import DefaultTable
+import warnings
 
 
 # panose classification
@@ -19,7 +21,7 @@
 	bXHeight:           B
 """
 
-class Panose:
+class Panose(object):
 	
 	def toXML(self, writer, ttFont):
 		formatstring, names, fixes = sstruct.getformat(panoseFormat)
@@ -27,7 +29,7 @@
 			writer.simpletag(name, value=getattr(self, name))
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		setattr(self, name, safeEval(attrs["value"]))
 
 
@@ -80,12 +82,19 @@
 	usMaxContex:        H
 """
 
+OS2_format_5_addition =  OS2_format_2_addition + """
+	usLowerOpticalPointSize:    H
+	usUpperOpticalPointSize:    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_5 = OS2_format_0 + OS2_format_5_addition
 OS2_format_1_addition = bigendian + OS2_format_1_addition
 OS2_format_2_addition = bigendian + OS2_format_2_addition
+OS2_format_5_addition = bigendian + OS2_format_5_addition
 
 
 class table_O_S_2f_2(DefaultTable.DefaultTable):
@@ -94,24 +103,21 @@
 	
 	def decompile(self, data, ttFont):
 		dummy, data = sstruct.unpack2(OS2_format_0, data, self)
-		# workarounds for buggy fonts (Apple, mona)
-		if not data:
-			self.version = 0
-		elif len(data) == sstruct.calcsize(OS2_format_1_addition):
-			self.version = 1
-		elif len(data) == sstruct.calcsize(OS2_format_2_addition):
-			if self.version not in (2, 3, 4):
-				self.version = 1
-		else:
-			from fontTools import ttLib
-			raise ttLib.TTLibError, "unknown format for OS/2 table (incorrect length): version %s" % (self.version, len(data))
+
 		if self.version == 1:
-			sstruct.unpack2(OS2_format_1_addition, data, self)
+			dummy, data = sstruct.unpack2(OS2_format_1_addition, data, self)
 		elif self.version in (2, 3, 4):
-			sstruct.unpack2(OS2_format_2_addition, data, self)
-		elif self.version <> 0:
+			dummy, data = sstruct.unpack2(OS2_format_2_addition, data, self)
+		elif self.version == 5:
+			dummy, data = sstruct.unpack2(OS2_format_5_addition, data, self)
+			self.usLowerOpticalPointSize /= 20
+			self.usUpperOpticalPointSize /= 20
+		elif self.version != 0:
 			from fontTools import ttLib
-			raise ttLib.TTLibError, "unknown format for OS/2 table: version %s" % self.version
+			raise ttLib.TTLibError("unknown format for OS/2 table: version %s" % self.version)
+		if len(data):
+			warnings.warn("too much 'OS/2' table data")
+
 		self.panose = sstruct.unpack(panoseFormat, self.panose, Panose())
 	
 	def compile(self, ttFont):
@@ -123,9 +129,14 @@
 			data = sstruct.pack(OS2_format_1, self)
 		elif self.version in (2, 3, 4):
 			data = sstruct.pack(OS2_format_2, self)
+		elif self.version == 5:
+			d = self.__dict__.copy()
+			d['usLowerOpticalPointSize'] = int(round(self.usLowerOpticalPointSize * 20))
+			d['usUpperOpticalPointSize'] = int(round(self.usUpperOpticalPointSize * 20))
+			data = sstruct.pack(OS2_format_5, d)
 		else:
 			from fontTools import ttLib
-			raise ttLib.TTLibError, "unknown format for OS/2 table: version %s" % self.version
+			raise ttLib.TTLibError("unknown format for OS/2 table: version %s" % self.version)
 		self.panose = panose
 		return data
 	
@@ -134,13 +145,13 @@
 			format = OS2_format_1
 		elif self.version in (2, 3, 4):
 			format = OS2_format_2
+		elif self.version == 5:
+			format = OS2_format_5
 		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()
@@ -158,12 +169,13 @@
 				writer.simpletag(name, value=value)
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == "panose":
 			self.panose = panose = Panose()
 			for element in content:
-				if type(element) == TupleType:
-					panose.fromXML(element, ttFont)
+				if isinstance(element, tuple):
+					name, attrs, content = element
+					panose.fromXML(name, attrs, content, ttFont)
 		elif name in ("ulUnicodeRange1", "ulUnicodeRange2", 
 				"ulUnicodeRange3", "ulUnicodeRange4",
 				"ulCodePageRange1", "ulCodePageRange2",
diff --git a/Lib/fontTools/ttLib/tables/S_I_N_G_.py b/Lib/fontTools/ttLib/tables/S_I_N_G_.py
index 3f76c93..6d9a412 100644
--- a/Lib/fontTools/ttLib/tables/S_I_N_G_.py
+++ b/Lib/fontTools/ttLib/tables/S_I_N_G_.py
@@ -1,9 +1,8 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-import struct
-import time
-import string
-from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
 
 SINGFormat = """
 		>	# big endian
@@ -30,52 +29,53 @@
 	def decompile(self, data, ttFont):
 		dummy, rest = sstruct.unpack2(SINGFormat, data, self)
 		self.uniqueName = self.decompileUniqueName(self.uniqueName)
-		self.nameLength = ord(self.nameLength)
+		self.nameLength = byteord(self.nameLength)
 		assert len(rest) == self.nameLength
-		self.baseGlyphName = rest
+		self.baseGlyphName = tostr(rest)
 		
 		rawMETAMD5 = self.METAMD5
-		self.METAMD5 = "[" + hex(ord(self.METAMD5[0]))
+		self.METAMD5 = "[" + hex(byteord(self.METAMD5[0]))
 		for char in rawMETAMD5[1:]:
-			self.METAMD5 = self.METAMD5 + ", " + hex(ord(char))
+			self.METAMD5 = self.METAMD5 + ", " + hex(byteord(char))
 		self.METAMD5 = self.METAMD5 + "]"
 		
 	def decompileUniqueName(self, data):
 		name = ""
 		for char in data:
-			val = ord(char)
+			val = byteord(char)
 			if val == 0:
 				break
 			if (val > 31) or (val < 128):
-				name = name + char
+				name += chr(val)
 			else:
 				octString = oct(val)
 				if len(octString) > 3:
 					octString = octString[1:] # chop off that leading zero.
 				elif len(octString) < 3:
 					octString.zfill(3)
-				name = name + "\\" + octString
+				name += "\\" + octString
 		return name
 		
 		
 	def compile(self, ttFont):
-		self.nameLength = chr(len(self.baseGlyphName))
-		self.uniqueName = self.compilecompileUniqueName(self.uniqueName, 28)
+		d = self.__dict__.copy()
+		d["nameLength"] = bytechr(len(self.baseGlyphName))
+		d["uniqueName"] = self.compilecompileUniqueName(self.uniqueName, 28)
 		METAMD5List = eval(self.METAMD5)
-		self.METAMD5 = ""
+		d["METAMD5"] = b""
 		for val in METAMD5List:
-			self.METAMD5 = self.METAMD5 + chr(val)
-		assert (len(self.METAMD5) == 16), "Failed to pack 16 byte MD5 hash in SING table"
-		data = sstruct.pack(SINGFormat, self)
-		data = data + self.baseGlyphName
+			d["METAMD5"] += bytechr(val)
+		assert (len(d["METAMD5"]) == 16), "Failed to pack 16 byte MD5 hash in SING table"
+		data = sstruct.pack(SINGFormat, d)
+		data = data + tobytes(self.baseGlyphName)
 		return data
 	
 	def compilecompileUniqueName(self, name, length):
 		nameLen = len(name)
 		if length <= nameLen:
-			name[:length-1] + "\000"
+			name = name[:length-1] + "\000"
 		else:
-			name.join( (nameLen - length)* "\000")
+			name += (nameLen - length) * "\000"
 		return name
 
 
@@ -90,13 +90,9 @@
 		writer.simpletag("baseGlyphName", value=self.baseGlyphName)
 		writer.newline()
 		
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		value = attrs["value"]
 		if name in ["uniqueName", "METAMD5", "baseGlyphName"]:
 			setattr(self, name, value)
 		else:
-			try:
-				value = safeEval(value)
-			except OverflowError:
-				value = long(value)
-			setattr(self, name, value)
+			setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/S_V_G_.py b/Lib/fontTools/ttLib/tables/S_V_G_.py
index 0763435..e4d600c 100644
--- a/Lib/fontTools/ttLib/tables/S_V_G_.py
+++ b/Lib/fontTools/ttLib/tables/S_V_G_.py
@@ -1,3 +1,14 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from . import DefaultTable
+try:
+    import xml.etree.cElementTree as ET
+except ImportError:
+    import xml.etree.ElementTree as ET
+import struct
+import re
+
 __doc__="""
 Compiles/decompiles version 0 and 1 SVG tables from/to XML.
 
@@ -42,18 +53,6 @@
 
 """
 
-import DefaultTable
-import struct
-from fontTools.misc import sstruct
-from fontTools.misc.textTools import safeEval
-try:
-    import xml.etree.cElementTree as ET
-except ImportError:
-    import xml.etree.ElementTree as ET
-import string
-import types
-import re
-
 XML = ET.XML
 XMLElement = ET.Element
 xmlToString = ET.tostring
@@ -105,16 +104,14 @@
 			self.decompile_format_1(data, ttFont)
 		else:
 			if self.version != 0:
-				print "Unknown SVG table version '%s'. Decompiling as version 0." % (self.version)
+				print("Unknown SVG table version '%s'. Decompiling as version 0." % (self.version))
 			self.decompile_format_0(data, ttFont)
 
 
 	def decompile_format_0(self, data, ttFont):
 		dummy, data2 = sstruct.unpack2(SVG_format_0, data, self)
 		# read in SVG Documents Index
-		pos = self.offsetToSVGDocIndex
-		self.numEntries = numEntries = struct.unpack(">H", data[pos:pos+2])[0]
-		self.decompileEntryList(data, pos+2)
+		self.decompileEntryList(data)
 
 		# read in colorPalettes table.
 		self.colorPalettes = colorPalettes = ColorPalettes()
@@ -156,8 +153,11 @@
 		pos += 2
 		self.decompileEntryList(data, pos)
 
-	def decompileEntryList(self, data, pos):
+	def decompileEntryList(self, data):
 		# data starts with the first entry of the entry list.
+		pos = subTableStart = self.offsetToSVGDocIndex
+		self.numEntries = numEntries = struct.unpack(">H", data[pos:pos+2])[0]
+		pos += 2
 		if self.numEntries > 0:
 			data2 = data[pos:]
 			self.docList = []
@@ -169,9 +169,9 @@
 				i += 1
 
 			for entry in entries:
-				start = entry.svgDocOffset
+				start = entry.svgDocOffset + subTableStart
 				end = start + entry.svgDocLength
-				doc = data[start:end]
+				doc = tostr(data[start:end], "utf-8")
 				self.docList.append( [doc, entry.startGlyphID, entry.endGlyphID] )
 
 	def compile(self, ttFont):
@@ -184,26 +184,25 @@
 	def compileFormat0(self, ttFont):
 		version = 0
 		offsetToSVGDocIndex = SVG_format_0Size # I start the SVGDocIndex right after the header.
-
 		# get SGVDoc info.
 		docList = []
 		entryList = []
 		numEntries = len(self.docList)
 		datum = struct.pack(">H",numEntries)
 		entryList.append(datum)
-		curOffset = offsetToSVGDocIndex + len(datum) + doc_index_entry_format_0Size*numEntries
+		curOffset = len(datum) + doc_index_entry_format_0Size*numEntries
 		for doc, startGlyphID, endGlyphID in self.docList:
 			docOffset = curOffset
 			docLength = len(doc)
 			curOffset += docLength
-		 	entry = struct.pack(">HHLL", startGlyphID, endGlyphID, docOffset, docLength)
-		 	entryList.append(entry)
-		 	docList.append(doc)
+			entry = struct.pack(">HHLL", startGlyphID, endGlyphID, docOffset, docLength)
+			entryList.append(entry)
+			docList.append(tobytes(doc, encoding="utf-8"))
 		entryList.extend(docList)
-		svgDocData = "".join(entryList)
+		svgDocData = bytesjoin(entryList)
 
 		# get colorpalette info.
-		if self.colorPalettes == None:
+		if self.colorPalettes is None:
 			offsetToColorPalettes = 0
 			palettesData = ""
 		else:
@@ -224,11 +223,11 @@
 				for colorRecord in colorPalette.paletteColors:
 					data = struct.pack(">BBBB", colorRecord.red, colorRecord.green, colorRecord.blue, colorRecord.alpha)
 					dataList.append(data)
-			palettesData = "".join(dataList)
+			palettesData = bytesjoin(dataList)
 
 		header = struct.pack(">HLL", version, offsetToSVGDocIndex, offsetToColorPalettes)
 		data = [header, svgDocData, palettesData]
-		data = "".join(data)
+		data = bytesjoin(data)
 		return data
 
 	def compileFormat1(self, ttFont):
@@ -242,11 +241,11 @@
 			docOffset = curOffset
 			docLength = len(doc)
 			curOffset += docLength
-		 	entry = struct.pack(">HHLL", startGlyphID, endGlyphID, docOffset, docLength)
-		 	dataList.append(entry)
-		 	docList.append(doc)
+			entry = struct.pack(">HHLL", startGlyphID, endGlyphID, docOffset, docLength)
+			dataList.append(entry)
+			docList.append(tobytes(doc, encoding="utf-8"))
 		dataList.extend(docList)
-		data = "".join(dataList)
+		data = bytesjoin(dataList)
 		return data
 
 	def toXML(self, writer, ttFont):
@@ -254,12 +253,12 @@
 		for doc, startGID, endGID in self.docList:
 			writer.begintag("svgDoc", startGlyphID=startGID, endGlyphID=endGID)
 			writer.newline()
-			writer.writeraw("<![CDATA["+ doc + "]]>")
+			writer.writecdata(doc)
 			writer.newline()
 			writer.endtag("svgDoc")
 			writer.newline()
 
-		if self.colorPalettes.numColorParams != None:
+		if (self.colorPalettes is not None) and (self.colorPalettes.numColorParams is not None):
 			writer.begintag("colorPalettes")
 			writer.newline()
 			for uiNameID in self.colorPalettes.colorParamUINameIDs:
@@ -290,25 +289,25 @@
 			writer.endtag("colorPalettes")
 			writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		import re
 		if name == "svgDoc":
 			if not hasattr(self, "docList"):
 				self.docList = []
-			doc = "".join(content)
+			doc = strjoin(content)
 			doc = doc.strip()
 			startGID = int(attrs["startGlyphID"])
 			endGID = int(attrs["endGlyphID"])
 			self.docList.append( [doc, startGID, endGID] )
 		elif  name == "colorPalettes":
 			self.colorPalettes = ColorPalettes()
-			self.colorPalettes.fromXML((name, attrs, content), ttFont)
+			self.colorPalettes.fromXML(name, attrs, content, ttFont)
 			if self.colorPalettes.numColorParams == 0:
 				self.colorPalettes = None
 		else:
-			print "Unknown", name, content
+			print("Unknown", name, content)
 
-class DocumentIndexEntry:
+class DocumentIndexEntry(object):
 	def __init__(self):
 		self.startGlyphID = None # USHORT
 		self.endGlyphID = None # USHORT
@@ -318,16 +317,16 @@
 	def __repr__(self):
 		return "startGlyphID: %s, endGlyphID: %s, svgDocOffset: %s, svgDocLength: %s" % (self.startGlyphID, self.endGlyphID, self.svgDocOffset, self.svgDocLength)
 
-class ColorPalettes:
+class ColorPalettes(object):
 	def __init__(self):
 		self.numColorParams = None # USHORT
 		self.colorParamUINameIDs = [] # list of name table name ID values that provide UI description of each color palette.
 		self.numColorPalettes = None # USHORT
 		self.colorPaletteList = [] # list of ColorPalette records
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		for element in content:
-			if type(element) == type(""):
+			if isinstance(element, type("")):
 				continue
 			name, attrib, content = element
 			if name == "colorParamUINameID":
@@ -344,15 +343,15 @@
 			if len(colorPalette.paletteColors) != self.numColorParams:
 				raise ValueError("Number of color records in a colorPalette ('%s') does not match the number of colorParamUINameIDs elements ('%s')." % (len(colorPalette.paletteColors), self.numColorParams))
 
-class ColorPalette:
+class ColorPalette(object):
 	def __init__(self):
 		self.uiNameID = None # USHORT. name table ID that describes user interface strings associated with this color palette. 
 		self.paletteColors = [] # list of ColorRecords
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.uiNameID = int(attrs["uiNameID"])
 		for element in content:
-			if type(element) == type(""):
+			if isinstance(element, type("")):
 				continue
 			name, attrib, content = element
 			if name == "colorRecord":
@@ -363,7 +362,7 @@
 				colorRecord.blue = eval(attrib["blue"])
 				colorRecord.alpha = eval(attrib["alpha"])
 
-class ColorRecord:
+class ColorRecord(object):
 	def __init__(self):
 		self.red = 255 # all are one byte values.
 		self.green = 255
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_B_.py b/Lib/fontTools/ttLib/tables/T_S_I_B_.py
index 31608f8..5cc54e2 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_B_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_B_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
 
 class table_T_S_I_B_(asciiTable.asciiTable):
 	pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_D_.py b/Lib/fontTools/ttLib/tables/T_S_I_D_.py
index 0388405..8228f8a 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_D_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_D_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
 
 class table_T_S_I_D_(asciiTable.asciiTable):
 	pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_J_.py b/Lib/fontTools/ttLib/tables/T_S_I_J_.py
index 8f6ed8b..0983b57 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_J_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_J_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
 
 class table_T_S_I_J_(asciiTable.asciiTable):
 	pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_P_.py b/Lib/fontTools/ttLib/tables/T_S_I_P_.py
index f0de28d..e34a18c 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_P_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_P_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
 
 class table_T_S_I_P_(asciiTable.asciiTable):
 	pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_S_.py b/Lib/fontTools/ttLib/tables/T_S_I_S_.py
index fc98317..56373e6 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_S_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_S_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
 
 class table_T_S_I_S_(asciiTable.asciiTable):
 	pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_V_.py b/Lib/fontTools/ttLib/tables/T_S_I_V_.py
index 928b7ce..a87e3f7 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_V_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_V_.py
@@ -1,4 +1,4 @@
-import asciiTable
+from . import asciiTable
 
 class table_T_S_I_V_(asciiTable.asciiTable):
 	pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__0.py b/Lib/fontTools/ttLib/tables/T_S_I__0.py
index c527818..174b192 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__0.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__0.py
@@ -1,9 +1,11 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from . import DefaultTable
 import struct
 
 tsi0Format = '>HHl'
 
-def fixlongs((glyphID, textLength, textOffset)):
+def fixlongs(glyphID, textLength, textOffset):
 	return int(glyphID), int(textLength), textOffset	
 
 
@@ -16,7 +18,7 @@
 		indices = []
 		size = struct.calcsize(tsi0Format)
 		for i in range(numGlyphs + 5):
-			glyphID, textLength, textOffset = fixlongs(struct.unpack(tsi0Format, data[:size]))
+			glyphID, textLength, textOffset = fixlongs(*struct.unpack(tsi0Format, data[:size]))
 			indices.append((glyphID, textLength, textOffset))
 			data = data[size:]
 		assert len(data) == 0
@@ -26,10 +28,10 @@
 	
 	def compile(self, ttFont):
 		if not hasattr(self, "indices"):
-			# We have no corresponging table (TSI1 or TSI3); let's return
+			# We have no corresponding table (TSI1 or TSI3); let's return
 			# no data, which effectively means "ignore us".
 			return ""
-		data = ""
+		data = b""
 		for index, textLength, textOffset in self.indices:
 			data = data + struct.pack(tsi0Format, index, textLength, textOffset)
 		data = data + struct.pack(tsi0Format, 0XFFFE, 0, -1409540300)  # 0xABFC1F34
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__1.py b/Lib/fontTools/ttLib/tables/T_S_I__1.py
index fa6f7fe..bcb4049 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__1.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__1.py
@@ -1,5 +1,6 @@
-import DefaultTable
-import string
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from . import DefaultTable
 
 class table_T_S_I__1(DefaultTable.DefaultTable):
 	
@@ -39,19 +40,19 @@
 		if not hasattr(self, "glyphPrograms"):
 			self.glyphPrograms = {}
 			self.extraPrograms = {}
-		data = ''
+		data = b''
 		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.
+				data = data + b"\015"  # align on 2-byte boundaries, fill with return chars. Yum.
 			name = glyphNames[i]
-			if self.glyphPrograms.has_key(name):
+			if name in self.glyphPrograms:
 				text = self.glyphPrograms[name]
 			else:
-				text = ""
+				text = b""
 			textLength = len(text)
 			if textLength >= 0x8000:
 				textLength = 0x8000  # XXX ???
@@ -59,16 +60,15 @@
 			data = data + text
 		
 		extra_indices = []
-		codes = self.extras.items()
-		codes.sort()
+		codes = sorted(self.extras.items())
 		for i in range(len(codes)):
 			if len(data) % 2:
-				data = data + "\015"  # align on 2-byte boundaries, fill with return chars.
+				data = data + b"\015"  # align on 2-byte boundaries, fill with return chars.
 			code, name = codes[i]
-			if self.extraPrograms.has_key(name):
+			if name in self.extraPrograms:
 				text = self.extraPrograms[name]
 			else:
-				text = ""
+				text = b""
 			textLength = len(text)
 			if textLength >= 0x8000:
 				textLength = 0x8000  # XXX ???
@@ -78,8 +78,7 @@
 		return data
 	
 	def toXML(self, writer, ttFont):
-		names = self.glyphPrograms.keys()
-		names.sort()
+		names = sorted(self.glyphPrograms.keys())
 		writer.newline()
 		for name in names:
 			text = self.glyphPrograms[name]
@@ -87,31 +86,30 @@
 				continue
 			writer.begintag("glyphProgram", name=name)
 			writer.newline()
-			writer.write_noindent(string.replace(text, "\r", "\n"))
+			writer.write_noindent(text.replace("\r", "\n"))
 			writer.newline()
 			writer.endtag("glyphProgram")
 			writer.newline()
 			writer.newline()
-		extra_names = self.extraPrograms.keys()
-		extra_names.sort()
+		extra_names = sorted(self.extraPrograms.keys())
 		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.write_noindent(text.replace("\r", "\n"))
 			writer.newline()
 			writer.endtag("extraProgram")
 			writer.newline()
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	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")
+		lines = strjoin(content).replace("\r", "\n").split("\n")
+		text = '\r'.join(lines[1:-1])
 		if name == "glyphProgram":
 			self.glyphPrograms[attrs["name"]] = text
 		elif name == "extraProgram":
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__5.py b/Lib/fontTools/ttLib/tables/T_S_I__5.py
index f714b82..9d9c3c3 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__5.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__5.py
@@ -1,8 +1,9 @@
-import sys
-import DefaultTable
-import array
-from fontTools import ttLib
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import sys
+import array
 
 
 class table_T_S_I__5(DefaultTable.DefaultTable):
@@ -12,7 +13,7 @@
 		assert len(data) == 2 * numGlyphs
 		a = array.array("H")
 		a.fromstring(data)
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			a.byteswap()
 		self.glyphGrouping = {}
 		for i in range(numGlyphs):
@@ -23,21 +24,20 @@
 		a = array.array("H")
 		for i in range(len(glyphNames)):
 			a.append(self.glyphGrouping[glyphNames[i]])
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			a.byteswap()
 		return a.tostring()
 	
 	def toXML(self, writer, ttFont):
-		names = self.glyphGrouping.keys()
-		names.sort()
+		names = sorted(self.glyphGrouping.keys())
 		for glyphName in names:
 			writer.simpletag("glyphgroup", name=glyphName, value=self.glyphGrouping[glyphName])
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "glyphGrouping"):
 			self.glyphGrouping = {}
-		if name <> "glyphgroup":
+		if name != "glyphgroup":
 			return
 		self.glyphGrouping[attrs["name"]] = safeEval(attrs["value"])
 
diff --git a/Lib/fontTools/ttLib/tables/V_O_R_G_.py b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
index f817048..143b971 100644
--- a/Lib/fontTools/ttLib/tables/V_O_R_G_.py
+++ b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
@@ -1,9 +1,9 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import safeEval
+from . import DefaultTable
 import operator
-import DefaultTable
 import struct
-from fontTools.ttLib import sfnt
-from fontTools.misc.textTools import safeEval, readHex
-from types import IntType, StringType
 
 
 class table_V_O_R_G_(DefaultTable.DefaultTable):
@@ -30,31 +30,31 @@
 		self.VOriginRecords = vOrig = {}
 		glyphOrder = ttFont.getGlyphOrder()
 		try:
-			names = map(operator.getitem, [glyphOrder]*self.numVertOriginYMetrics, gids )
+			names = map(operator.getitem, [glyphOrder]*self.numVertOriginYMetrics, gids)
 		except IndexError:
 			getGlyphName = self.getGlyphName
 			names = map(getGlyphName, gids )
 
-		map(operator.setitem, [vOrig]*self.numVertOriginYMetrics, names, vids)
+		list(map(operator.setitem, [vOrig]*self.numVertOriginYMetrics, names, vids))
 
 
 	def compile(self, ttFont):
-		vorgs = self.VOriginRecords.values()
-		names = self.VOriginRecords.keys()
+		vorgs = list(self.VOriginRecords.values())
+		names = list(self.VOriginRecords.keys())
 		nameMap = ttFont.getReverseGlyphMap()
 		lenRecords = len(vorgs) 
 		try:
 			gids = map(operator.getitem, [nameMap]*lenRecords, names)
 		except KeyError:
-			nameMap = ttFont.getReverseGlyphMap(rebuild=1)
+			nameMap = ttFont.getReverseGlyphMap(rebuild=True)
 			gids = map(operator.getitem, [nameMap]*lenRecords, names)
-		vOriginTable = map(None, gids, vorgs)
+		vOriginTable = list(zip(gids, vorgs))
 		self.numVertOriginYMetrics = lenRecords
 		vOriginTable.sort() # must be in ascending GID order
 		dataList = [ struct.pack(">Hh", rec[0], rec[1]) for rec in vOriginTable]
 		header = struct.pack(">HHhH", self.majorVersion, self.minorVersion, self.defaultVertOriginY, self.numVertOriginYMetrics)
 		dataList.insert(0, header)
-		data = "".join(dataList)
+		data = bytesjoin(dataList)
 		return data
 
 	def toXML(self, writer, ttFont):
@@ -79,46 +79,43 @@
 			vOriginRec = VOriginRecord(entry[1], entry[2])
 			vOriginRec.toXML(writer, ttFont)
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "VOriginRecords"):
 			self.VOriginRecords = {}
 		self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID
 		if name == "VOriginRecord":
-			for element in content:
-				if isinstance(element, StringType):
-					continue
 			vOriginRec = VOriginRecord()
 			for element in content:
-				if isinstance(element, StringType):
+				if isinstance(element, basestring):
 					continue
-				vOriginRec.fromXML(element, ttFont)
+				name, attrs, content = element
+				vOriginRec.fromXML(name, attrs, content, ttFont)
 			self.VOriginRecords[vOriginRec.glyphName] = vOriginRec.vOrigin
-		elif attrs.has_key("value"):
-			value =  safeEval(attrs["value"])
-			setattr(self, name, value)
+		elif "value" in attrs:
+			setattr(self, name, safeEval(attrs["value"]))
 
 
 	def __getitem__(self, glyphSelector):
-		if type(glyphSelector) == IntType:
+		if isinstance(glyphSelector, int):
 			# its a gid, convert to glyph name
 			glyphSelector = self.getGlyphName(glyphSelector)
 
-		if not self.VOriginRecords.has_key(glyphSelector):
+		if glyphSelector not in self.VOriginRecords:
 			return self.defaultVertOriginY
 			
 		return self.VOriginRecords[glyphSelector]
 
 	def __setitem__(self, glyphSelector, value):
-		if type(glyphSelector) == IntType:
+		if isinstance(glyphSelector, int):
 			# its a gid, convert to glyph name
 			glyphSelector = self.getGlyphName(glyphSelector)
 
 		if  value != self.defaultVertOriginY:
 			self.VOriginRecords[glyphSelector] = value
-		elif self.VOriginRecords.has_key(glyphSelector):
+		elif glyphSelector in self.VOriginRecords:
 			del self.VOriginRecords[glyphSelector]
 
-class VOriginRecord:
+class VOriginRecord(object):
 
 	def __init__(self, name = None, vOrigin = None):
 		self.glyphName = name
@@ -134,13 +131,9 @@
 		writer.endtag("VOriginRecord")
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		value = attrs["value"]
 		if name == "glyphName":
 			setattr(self, name, value)
 		else:
-			try:
-				value = safeEval(value)
-			except OverflowError:
-				value = long(value)
-			setattr(self, name, value)
+			setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/__init__.py b/Lib/fontTools/ttLib/tables/__init__.py
index 205af81..bdf8d96 100644
--- a/Lib/fontTools/ttLib/tables/__init__.py
+++ b/Lib/fontTools/ttLib/tables/__init__.py
@@ -3,52 +3,54 @@
 	"""Dummy function to let modulefinder know what tables may be
 	dynamically imported. Generated by MetaTools/buildTableList.py.
 	"""
-	import B_A_S_E_
-	import C_B_D_T_
-	import C_B_L_C_
-	import C_F_F_
-	import C_O_L_R_
-	import C_P_A_L_
-	import D_S_I_G_
-	import E_B_D_T_
-	import E_B_L_C_
-	import G_D_E_F_
-	import G_M_A_P_
-	import G_P_K_G_
-	import G_P_O_S_
-	import G_S_U_B_
-	import J_S_T_F_
-	import L_T_S_H_
-	import M_E_T_A_
-	import O_S_2f_2
-	import S_I_N_G_
-	import S_V_G_
-	import T_S_I_B_
-	import T_S_I_D_
-	import T_S_I_J_
-	import T_S_I_P_
-	import T_S_I_S_
-	import T_S_I_V_
-	import T_S_I__0
-	import T_S_I__1
-	import T_S_I__2
-	import T_S_I__3
-	import T_S_I__5
-	import V_O_R_G_
-	import _c_m_a_p
-	import _c_v_t
-	import _f_p_g_m
-	import _g_a_s_p
-	import _g_l_y_f
-	import _h_d_m_x
-	import _h_e_a_d
-	import _h_h_e_a
-	import _h_m_t_x
-	import _k_e_r_n
-	import _l_o_c_a
-	import _m_a_x_p
-	import _n_a_m_e
-	import _p_o_s_t
-	import _p_r_e_p
-	import _v_h_e_a
-	import _v_m_t_x
+	from . import B_A_S_E_
+	from . import C_B_D_T_
+	from . import C_B_L_C_
+	from . import C_F_F_
+	from . import C_O_L_R_
+	from . import C_P_A_L_
+	from . import D_S_I_G_
+	from . import E_B_D_T_
+	from . import E_B_L_C_
+	from . import F_F_T_M_
+	from . import G_D_E_F_
+	from . import G_M_A_P_
+	from . import G_P_K_G_
+	from . import G_P_O_S_
+	from . import G_S_U_B_
+	from . import J_S_T_F_
+	from . import L_T_S_H_
+	from . import M_E_T_A_
+	from . import O_S_2f_2
+	from . import S_I_N_G_
+	from . import S_V_G_
+	from . import T_S_I_B_
+	from . import T_S_I_D_
+	from . import T_S_I_J_
+	from . import T_S_I_P_
+	from . import T_S_I_S_
+	from . import T_S_I_V_
+	from . import T_S_I__0
+	from . import T_S_I__1
+	from . import T_S_I__2
+	from . import T_S_I__3
+	from . import T_S_I__5
+	from . import V_O_R_G_
+	from . import _c_m_a_p
+	from . import _c_v_t
+	from . import _f_p_g_m
+	from . import _g_a_s_p
+	from . import _g_l_y_f
+	from . import _h_d_m_x
+	from . import _h_e_a_d
+	from . import _h_h_e_a
+	from . import _h_m_t_x
+	from . import _k_e_r_n
+	from . import _l_o_c_a
+	from . import _m_a_x_p
+	from . import _n_a_m_e
+	from . import _p_o_s_t
+	from . import _p_r_e_p
+	from . import _s_b_i_x
+	from . import _v_h_e_a
+	from . import _v_m_t_x
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
index 7574431..b72d98d 100644
--- a/Lib/fontTools/ttLib/tables/_c_m_a_p.py
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -1,11 +1,11 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc.textTools import safeEval, readHex
+from . import DefaultTable
 import sys
-import DefaultTable
 import struct
 import array
 import operator
-from fontTools import ttLib
-from fontTools.misc.textTools import safeEval, readHex
-from types import TupleType
 
 
 class table__c_m_a_p(DefaultTable.DefaultTable):
@@ -27,15 +27,15 @@
 					">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 format in [8,10,12]:
+			if format in [8,10,12,13]:
 				format, reserved, length = struct.unpack(">HHL", data[offset:offset+8])
 			elif format in [14]:
 				format, length = struct.unpack(">HL", data[offset:offset+6])
 				
 			if not length:
-				print "Error: cmap subtable is reported as having zero length: platformID %s, platEncID %s,  format %s offset %s. Skipping table." % (platformID, platEncID,format, offset)
+				print("Error: cmap subtable is reported as having zero length: platformID %s, platEncID %s,  format %s offset %s. Skipping table." % (platformID, platEncID,format, offset))
 				continue
-			if not cmap_classes.has_key(format):
+			if format not in cmap_classes:
 				table = cmap_format_unknown(format)
 			else:
 				table = cmap_classes[format](format)
@@ -45,18 +45,18 @@
 			# any other data gets decompiled only when an attribute of the
 			# subtable is referenced.
 			table.decompileHeader(data[offset:offset+int(length)], ttFont)
-			if seenOffsets.has_key(offset):
+			if offset in seenOffsets:
 				table.cmap = tables[seenOffsets[offset]].cmap
 			else:
 				seenOffsets[offset] = i
 			tables.append(table)
 	
 	def compile(self, ttFont):
-		self.tables.sort()    # sort according to the spec; see CmapSubtable.__cmp__()
+		self.tables.sort()    # sort according to the spec; see CmapSubtable.__lt__()
 		numSubTables = len(self.tables)
 		totalOffset = 4 + 8 * numSubTables
 		data = struct.pack(">HH", self.tableVersion, numSubTables)
-		tableData = ""
+		tableData = b""
 		seen = {}  # Some tables are the same object reference. Don't compile them twice.
 		done = {}  # Some tables are different objects, but compile to the same data chunk
 		for table in self.tables:
@@ -64,7 +64,7 @@
 				offset = seen[id(table.cmap)]
 			except KeyError:
 				chunk = table.compile(ttFont)
-				if done.has_key(chunk):
+				if chunk in done:
 					offset = done[chunk]
 				else:
 					offset = seen[id(table.cmap)] = done[chunk] = totalOffset + len(tableData)
@@ -78,26 +78,26 @@
 		for table in self.tables:
 			table.toXML(writer, ttFont)
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == "tableVersion":
 			self.tableVersion = safeEval(attrs["version"])
 			return
-		if name[:12] <> "cmap_format_":
+		if name[:12] != "cmap_format_":
 			return
 		if not hasattr(self, "tables"):
 			self.tables = []
 		format = safeEval(name[12:])
-		if not cmap_classes.has_key(format):
+		if format not in cmap_classes:
 			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)
+		table.fromXML(name, attrs, content, ttFont)
 		self.tables.append(table)
 
 
-class CmapSubtable:
+class CmapSubtable(object):
 	
 	def __init__(self, format):
 		self.format = format
@@ -107,9 +107,9 @@
 	def __getattr__(self, attr):
 		# allow lazy decompilation of subtables.
 		if attr[:2] == '__': # don't handle requests for member functions like '__lt__'
-			raise AttributeError, attr
-		if self.data == None:
-			raise AttributeError, attr
+			raise AttributeError(attr)
+		if self.data is None:
+			raise AttributeError(attr)
 		self.decompile(None, None) # use saved data.
 		self.data = None # Once this table has been decompiled, make sure we don't
 						# just return the original data. Also avoids recursion when
@@ -132,8 +132,7 @@
 				("language", self.language),
 				])
 		writer.newline()
-		codes = self.cmap.items()
-		codes.sort()
+		codes = sorted(self.cmap.items())
 		self._writeCodes(codes, writer)
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
@@ -150,23 +149,22 @@
 				writer.comment(Unicode[code])
 			writer.newline()
 	
-	def __cmp__(self, other):
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
+	def __lt__(self, other):
+		if not isinstance(other, CmapSubtable):
+			return NotImplemented
 
-		# implemented so that list.sort() sorts according to the cmap spec.
+		# implemented so that list.sort() sorts according to the spec.
 		selfTuple = (
-					self.platformID,
-					self.platEncID,
-					self.language,
-					self.__dict__)
+			getattr(self, "platformID", None),
+			getattr(self, "platEncID", None),
+			getattr(self, "language", None),
+			self.__dict__)
 		otherTuple = (
-					other.platformID,
-					other.platEncID,
-					other.language,
-					other.__dict__)
-		return cmp(selfTuple, otherTuple)
+			getattr(other, "platformID", None),
+			getattr(other, "platEncID", None),
+			getattr(other, "language", None),
+			other.__dict__)
+		return selfTuple < otherTuple
 
 
 class cmap_format_0(CmapSubtable):
@@ -174,30 +172,29 @@
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
-		if data != None and ttFont != None:
+		if data is not None and ttFont is not None:
 			self.decompileHeader(data[offset:offset+int(length)], ttFont)
 		else:
-			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 		data = self.data # decompileHeader assigns the data after the header to self.data
 		assert 262 == self.length, "Format 0 cmap subtable not 262 bytes"
 		glyphIdArray = array.array("B")
 		glyphIdArray.fromstring(self.data)
 		self.cmap = cmap = {}
 		lenArray = len(glyphIdArray)
-		charCodes = range(lenArray)
+		charCodes = list(range(lenArray))
 		names = map(self.ttFont.getGlyphName, glyphIdArray)
-		map(operator.setitem, [cmap]*lenArray, charCodes, names)
+		list(map(operator.setitem, [cmap]*lenArray, charCodes, names))
 
 	
 	def compile(self, ttFont):
 		if self.data:
 			return struct.pack(">HHH", 0, 262, self.language) + self.data
 
-		charCodeList = self.cmap.items()
-		charCodeList.sort()
+		charCodeList = sorted(self.cmap.items())
 		charCodes = [entry[0] for entry in charCodeList]
 		valueList = [entry[1] for entry in charCodeList]
-		assert charCodes == range(256)
+		assert charCodes == list(range(256))
 		valueList = map(ttFont.getGlyphID, valueList)
 
 		glyphIdArray = array.array("B", valueList)
@@ -205,22 +202,22 @@
 		assert len(data) == 262
 		return data
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.language = safeEval(attrs["language"])
 		if not hasattr(self, "cmap"):
 			self.cmap = {}
 		cmap = self.cmap
 		for element in content:
-			if type(element) <> TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
-			if name <> "map":
+			if name != "map":
 				continue
 			cmap[safeEval(attrs["code"])] = attrs["name"]
 
 
 subHeaderFormat = ">HHhH"
-class SubHeader:
+class SubHeader(object):
 	def __init__(self):
 		self.firstCode = None
 		self.entryCount = None
@@ -262,10 +259,10 @@
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
-		if data != None and ttFont != None:
+		if data is not None and ttFont is not None:
 			self.decompileHeader(data[offset:offset+int(length)], ttFont)
 		else:
-			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 
 		data = self.data # decompileHeader assigns the data after the header to self.data
 		subHeaderKeys = []
@@ -274,9 +271,9 @@
 		allKeys = array.array("H")
 		allKeys.fromstring(data[:512])
 		data = data[512:]
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			allKeys.byteswap()
-		subHeaderKeys = [ key/8 for key in allKeys]
+		subHeaderKeys = [ key//8 for key in allKeys]
 		maxSubHeaderindex = max(subHeaderKeys)
 	
 		#Load subHeaders
@@ -290,7 +287,7 @@
 			giDataPos = pos + subHeader.idRangeOffset-2
 			giList = array.array("H")
 			giList.fromstring(data[giDataPos:giDataPos + subHeader.entryCount*2])
-			if sys.byteorder <> "big":
+			if sys.byteorder != "big":
 				giList.byteswap()
 			subHeader.glyphIndexArray = giList
 			subHeaderList.append(subHeader)
@@ -331,7 +328,7 @@
 		# add it to the glyphID to get the final glyphIndex
 		# value. In this case the final glyph index = 3+ 42 -> 45 for the final glyphIndex. Whew!
 		
-		self.data = ""
+		self.data = b""
 		self.cmap = cmap = {}
 		notdefGI = 0
 		for firstByte in range(256):
@@ -365,15 +362,15 @@
 				# same as mapping it to .notdef.
 		# cmap values are GID's.
 		glyphOrder = self.ttFont.getGlyphOrder()
-		gids = cmap.values()
-		charCodes = cmap.keys()
+		gids = list(cmap.values())
+		charCodes = list(cmap.keys())
 		lenCmap = len(gids)
 		try:
-			names = map(operator.getitem, [glyphOrder]*lenCmap, gids )
+			names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
 		except IndexError:
 			getGlyphName = self.ttFont.getGlyphName
-			names = map(getGlyphName, gids )
-		map(operator.setitem, [cmap]*lenCmap, charCodes, names)
+			names = list(map(getGlyphName, gids ))
+		list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
 	
 		
 	def compile(self, ttFont):
@@ -382,18 +379,17 @@
 		kEmptyTwoCharCodeRange = -1
 		notdefGI = 0
 
-		items = self.cmap.items()
-		items.sort()
+		items = sorted(self.cmap.items())
 		charCodes = [item[0] for item in items]
 		names = [item[1] for item in items]
 		nameMap = ttFont.getReverseGlyphMap()
 		lenCharCodes = len(charCodes) 
 		try:
-			gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+			gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
 		except KeyError:
-			nameMap = ttFont.getReverseGlyphMap(rebuild=1)
+			nameMap = ttFont.getReverseGlyphMap(rebuild=True)
 			try:
-				gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
 			except KeyError:
 				# allow virtual GIDs in format 2 tables
 				gids = []
@@ -519,22 +515,22 @@
 		for subhead in 	subHeaderList[:-1]:
 			for gi in subhead.glyphIndexArray:
 				dataList.append(struct.pack(">H", gi))
-		data = "".join(dataList)
+		data = bytesjoin(dataList)
 		assert (len(data) == length), "Error: cmap format 2 is not same length as calculated! actual: " + str(len(data))+ " calc : " + str(length)
 		return data
 
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.language = safeEval(attrs["language"])
 		if not hasattr(self, "cmap"):
 			self.cmap = {}
 		cmap = self.cmap
 
 		for element in content:
-			if type(element) <> TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
-			if name <> "map":
+			if name != "map":
 				continue
 			cmap[safeEval(attrs["code"])] = attrs["name"]
 
@@ -632,22 +628,22 @@
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
-		if data != None and ttFont != None:
+		if data is not None and ttFont is not None:
 			self.decompileHeader(self.data[offset:offset+int(length)], ttFont)
 		else:
-			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 
 		data = self.data # decompileHeader assigns the data after the header to self.data
 		(segCountX2, searchRange, entrySelector, rangeShift) = \
 					struct.unpack(">4H", data[:8])
 		data = data[8:]
-		segCount = segCountX2 / 2
+		segCount = segCountX2 // 2
 		
 		allCodes = array.array("H")
 		allCodes.fromstring(data)
 		self.data = data = None
 
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			allCodes.byteswap()
 		
 		# divide the data
@@ -665,7 +661,7 @@
 		charCodes = []
 		gids = []
 		for i in range(len(startCode) - 1):	# don't do 0xffff!
-			rangeCharCodes = range(startCode[i], endCode[i] + 1)
+			rangeCharCodes = list(range(startCode[i], endCode[i] + 1))
 			charCodes = charCodes + rangeCharCodes
 			for charCode in rangeCharCodes:
 				rangeOffset = idRangeOffset[i]
@@ -673,9 +669,9 @@
 					glyphID = charCode + idDelta[i]
 				else:
 					# *someone* needs to get killed.
-					index = idRangeOffset[i] / 2 + (charCode - startCode[i]) + i - len(idRangeOffset)
+					index = idRangeOffset[i] // 2 + (charCode - startCode[i]) + i - len(idRangeOffset)
 					assert (index < lenGIArray), "In format 4 cmap, range (%d), the calculated index (%d) into the glyph index array  is not less than the length of the array (%d) !" % (i, index, lenGIArray)
-					if glyphIndexArray[index] <> 0:  # if not missing glyph
+					if glyphIndexArray[index] != 0:  # if not missing glyph
 						glyphID = glyphIndexArray[index] + idDelta[i]
 					else:
 						glyphID = 0  # missing glyph
@@ -685,11 +681,11 @@
 		lenCmap = len(gids)
 		glyphOrder = self.ttFont.getGlyphOrder()
 		try:
-			names = map(operator.getitem, [glyphOrder]*lenCmap, gids )
+			names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
 		except IndexError:
 			getGlyphName = self.ttFont.getGlyphName
-			names = map(getGlyphName, gids )
-		map(operator.setitem, [cmap]*lenCmap, charCodes, names)
+			names = list(map(getGlyphName, gids ))
+		list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
 		
 
 
@@ -720,21 +716,21 @@
 
 		from fontTools.ttLib.sfnt import maxPowerOfTwo
 		
-		charCodes = self.cmap.keys()
+		charCodes = list(self.cmap.keys())
 		lenCharCodes = len(charCodes)
 		if lenCharCodes == 0:
 			startCode = [0xffff]
 			endCode = [0xffff]
 		else:
 			charCodes.sort()
-			names = map(operator.getitem, [self.cmap]*lenCharCodes, charCodes)
+			names = list(map(operator.getitem, [self.cmap]*lenCharCodes, charCodes))
 			nameMap = ttFont.getReverseGlyphMap()
 			try:
-				gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
 			except KeyError:
-				nameMap = ttFont.getReverseGlyphMap(rebuild=1)
+				nameMap = ttFont.getReverseGlyphMap(rebuild=True)
 				try:
-					gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+					gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
 				except KeyError:
 					# allow virtual GIDs in format 4 tables
 					gids = []
@@ -752,7 +748,7 @@
 	
 						gids.append(gid)
 			cmap = {}  # code:glyphID mapping
-			map(operator.setitem, [cmap]*len(charCodes), charCodes, gids)
+			list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
 		
 			# Build startCode and endCode lists.
 			# Split the char codes in ranges of consecutive char codes, then split
@@ -782,7 +778,7 @@
 			indices = []
 			for charCode in range(startCode[i], endCode[i] + 1):
 				indices.append(cmap[charCode])
-			if  (indices == range(indices[0], indices[0] + len(indices))):
+			if  (indices == list(range(indices[0], indices[0] + len(indices)))):
 				idDeltaTemp = self.setIDDelta(indices[0] - startCode[i])
 				idDelta.append( idDeltaTemp)
 				idRangeOffset.append(0)
@@ -805,7 +801,7 @@
 		charCodeArray = array.array("H", endCode + [0] + startCode)
 		idDeltaeArray = array.array("h", idDelta)
 		restArray = array.array("H", idRangeOffset + glyphIndexArray)
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			charCodeArray.byteswap()
 			idDeltaeArray.byteswap()
 			restArray.byteswap()
@@ -816,17 +812,17 @@
 				segCountX2, searchRange, entrySelector, rangeShift)
 		return header + data
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.language = safeEval(attrs["language"])
 		if not hasattr(self, "cmap"):
 			self.cmap = {}
 		cmap = self.cmap
 
 		for element in content:
-			if type(element) <> TupleType:
+			if not isinstance(element, tuple):
 				continue
 			nameMap, attrsMap, dummyContent = element
-			if nameMap <> "map":
+			if nameMap != "map":
 				assert 0, "Unrecognized keyword in cmap subtable"
 			cmap[safeEval(attrsMap["code"])] = attrsMap["name"]
 
@@ -836,10 +832,10 @@
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
-		if data != None and ttFont != None:
+		if data is not None and ttFont is not None:
 			self.decompileHeader(data[offset:offset+int(length)], ttFont)
 		else:
-			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 
 		data = self.data # decompileHeader assigns the data after the header to self.data
 		firstCode, entryCount = struct.unpack(">HH", data[:4])
@@ -848,61 +844,59 @@
 		#assert len(data) == 2 * entryCount  # XXX not true in Apple's Helvetica!!!
 		glyphIndexArray = array.array("H")
 		glyphIndexArray.fromstring(data[:2 * int(entryCount)])
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			glyphIndexArray.byteswap()
 		self.data = data = None
 
 		self.cmap = cmap = {}
 
 		lenArray = len(glyphIndexArray)
-		charCodes = range(firstCode, firstCode + lenArray )
+		charCodes = list(range(firstCode, firstCode + lenArray))
 		glyphOrder = self.ttFont.getGlyphOrder()
 		try:
-			names = map(operator.getitem, [glyphOrder]*lenArray, glyphIndexArray )
+			names = list(map(operator.getitem, [glyphOrder]*lenArray, glyphIndexArray ))
 		except IndexError:
 			getGlyphName = self.ttFont.getGlyphName
-			names = map(getGlyphName, glyphIndexArray )
-		map(operator.setitem, [cmap]*lenArray, charCodes, names)
+			names = list(map(getGlyphName, glyphIndexArray ))
+		list(map(operator.setitem, [cmap]*lenArray, charCodes, names))
 	
 	def compile(self, ttFont):
 		if self.data:
 			return struct.pack(">HHH", self.format, self.length, self.language) + self.data
 		cmap = self.cmap
-		codes = cmap.keys()
+		codes = list(cmap.keys())
 		if codes: # yes, there are empty cmap tables.
-			codes.sort()
-			lenCodes = len(codes)
-			assert codes == range(codes[0], codes[0] + lenCodes)
+			codes = list(range(codes[0], codes[-1] + 1))
 			firstCode = codes[0]
-			valueList = map(operator.getitem, [cmap]*lenCodes, codes)
+			valueList = [cmap.get(code, ".notdef") for code in codes]
 			valueList = map(ttFont.getGlyphID, valueList)
 			glyphIndexArray = array.array("H", valueList)
-			if sys.byteorder <> "big":
+			if sys.byteorder != "big":
 				glyphIndexArray.byteswap()
 			data = glyphIndexArray.tostring()
 		else:
-			data = ""
+			data = b""
 			firstCode = 0
 		header = struct.pack(">HHHHH", 
 				6, len(data) + 10, self.language, firstCode, len(codes))
 		return header + data
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.language = safeEval(attrs["language"])
 		if not hasattr(self, "cmap"):
 			self.cmap = {}
 		cmap = self.cmap
 
 		for element in content:
-			if type(element) <> TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
-			if name <> "map":
+			if name != "map":
 				continue
 			cmap[safeEval(attrs["code"])] = attrs["name"]
 
 
-class cmap_format_12(CmapSubtable):
+class cmap_format_12_or_13(CmapSubtable):
 	
 	def __init__(self, format):
 		self.format = format
@@ -912,7 +906,7 @@
 
 	def decompileHeader(self, data, ttFont):
 		format, reserved, length, language, nGroups = struct.unpack(">HHLLL", data[:16])
-		assert len(data) == (16 + nGroups*12) == (length), "corrupt cmap table format 12 (data length: %d, header length: %d)" % (len(data), length)
+		assert len(data) == (16 + nGroups*12) == (length), "corrupt cmap table format %d (data length: %d, header length: %d)" % (format, len(data), length)
 		self.format = format
 		self.reserved = reserved
 		self.length = length
@@ -924,10 +918,10 @@
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
-		if data != None and ttFont != None:
+		if data is not None and ttFont is not None:
 			self.decompileHeader(data[offset:offset+int(length)], ttFont)
 		else:
-			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 
 		data = self.data # decompileHeader assigns the data after the header to self.data
 		charCodes = []
@@ -937,32 +931,32 @@
 			startCharCode, endCharCode, glyphID = struct.unpack(">LLL",data[pos:pos+12] )
 			pos += 12
 			lenGroup = 1 + endCharCode - startCharCode
-			charCodes += range(startCharCode, endCharCode +1)
-			gids += range(glyphID, glyphID + lenGroup)
+			charCodes += list(range(startCharCode, endCharCode +1))
+			gids += self._computeGIDs(glyphID, lenGroup)
 		self.data = data = None
 		self.cmap = cmap = {}
 		lenCmap = len(gids)
 		glyphOrder = self.ttFont.getGlyphOrder()
 		try:
-			names = map(operator.getitem, [glyphOrder]*lenCmap, gids )
+			names = list(map(operator.getitem, [glyphOrder]*lenCmap, gids ))
 		except IndexError:
 			getGlyphName = self.ttFont.getGlyphName
-			names = map(getGlyphName, gids )
-		map(operator.setitem, [cmap]*lenCmap, charCodes, names)
+			names = list(map(getGlyphName, gids ))
+		list(map(operator.setitem, [cmap]*lenCmap, charCodes, names))
 	
 	def compile(self, ttFont):
 		if self.data:
-			return struct.pack(">HHLLL", self.format, self.reserved , self.length, self.language, self.nGroups) + self.data
-		charCodes = self.cmap.keys()
+			return struct.pack(">HHLLL", self.format, self.reserved, self.length, self.language, self.nGroups) + self.data
+		charCodes = list(self.cmap.keys())
 		lenCharCodes = len(charCodes) 
-		names = self.cmap.values()
+		names = list(self.cmap.values())
 		nameMap = ttFont.getReverseGlyphMap()
 		try:
-			gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+			gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
 		except KeyError:
-			nameMap = ttFont.getReverseGlyphMap(rebuild=1)
+			nameMap = ttFont.getReverseGlyphMap(rebuild=True)
 			try:
-				gids = map(operator.getitem, [nameMap]*lenCharCodes, names)
+				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
 			except KeyError:
 				# allow virtual GIDs in format 12 tables
 				gids = []
@@ -981,13 +975,13 @@
 					gids.append(gid)
 		
 		cmap = {}  # code:glyphID mapping
-		map(operator.setitem, [cmap]*len(charCodes), charCodes, gids)
+		list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
 
 		charCodes.sort()
 		index = 0
 		startCharCode = charCodes[0]
 		startGlyphID = cmap[startCharCode]
-		lastGlyphID =  startGlyphID - 1
+		lastGlyphID = startGlyphID - self._format_step
 		lastCharCode = startCharCode - 1
 		nGroups = 0
 		dataList =  []
@@ -995,7 +989,7 @@
 		for index in range(maxIndex):
 			charCode = charCodes[index]
 			glyphID = cmap[charCode]
-			if (glyphID != 1 + lastGlyphID) or (charCode != 1 + lastCharCode):
+			if not self._IsInSameRun(glyphID, lastGlyphID, charCode, lastCharCode):
 				dataList.append(struct.pack(">LLL", startCharCode, lastCharCode, startGlyphID))
 				startCharCode = charCode
 				startGlyphID = glyphID
@@ -1004,7 +998,7 @@
 			lastCharCode = charCode
 		dataList.append(struct.pack(">LLL", startCharCode, lastCharCode, startGlyphID))
 		nGroups = nGroups + 1
-		data = "".join(dataList)
+		data = bytesjoin(dataList)
 		lengthSubtable = len(data) +16
 		assert len(data) == (nGroups*12) == (lengthSubtable-16) 
 		return struct.pack(">HHLLL", self.format, self.reserved , lengthSubtable, self.language, nGroups) + data
@@ -1020,13 +1014,12 @@
 				("nGroups", self.nGroups),
 				])
 		writer.newline()
-		codes = self.cmap.items()
-		codes.sort()
+		codes = sorted(self.cmap.items())
 		self._writeCodes(codes, writer)
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.format = safeEval(attrs["format"])
 		self.reserved = safeEval(attrs["reserved"])
 		self.length = safeEval(attrs["length"])
@@ -1037,44 +1030,49 @@
 		cmap = self.cmap
 
 		for element in content:
-			if type(element) <> TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
-			if name <> "map":
+			if name != "map":
 				continue
 			cmap[safeEval(attrs["code"])] = attrs["name"]
 
 
+class cmap_format_12(cmap_format_12_or_13):
+	def __init__(self, format):
+		cmap_format_12_or_13.__init__(self, format)
+		self._format_step = 1
+
+	def _computeGIDs(self, startingGlyph, numberOfGlyphs):
+		return list(range(startingGlyph, startingGlyph + numberOfGlyphs))
+
+	def _IsInSameRun(self, glyphID, lastGlyphID, charCode, lastCharCode):
+		return (glyphID == 1 + lastGlyphID) and (charCode == 1 + lastCharCode)
+
+
+class cmap_format_13(cmap_format_12_or_13):
+	def __init__(self, format):
+		cmap_format_12_or_13.__init__(self, format)
+		self._format_step = 0
+
+	def _computeGIDs(self, startingGlyph, numberOfGlyphs):
+		return [startingGlyph] * numberOfGlyphs
+
+	def _IsInSameRun(self, glyphID, lastGlyphID, charCode, lastCharCode):
+		return (glyphID == lastGlyphID) and (charCode == 1 + lastCharCode)
+
+
 def  cvtToUVS(threeByteString):
-	if sys.byteorder <> "big":
-		data = "\0" +threeByteString
-	else:
-		data = threeByteString + "\0"
+	data = b"\0" + threeByteString
 	val, = struct.unpack(">L", data)
 	return val
 
 def  cvtFromUVS(val):
-	if sys.byteorder <> "big":
-		threeByteString = struct.pack(">L", val)[1:]
-	else:
-		threeByteString = struct.pack(">L", val)[:3]
-	return threeByteString
+	assert 0 <= val < 0x1000000
+	fourByteString = struct.pack(">L", val)
+	return fourByteString[1:]
 
-def cmpUVSListEntry(first, second):
-	uv1, glyphName1 = first
-	uv2, glyphName2 = second
-	
-	if (glyphName1 == None) and (glyphName2 != None):
-		return -1
-	elif (glyphName2 == None) and (glyphName1 != None):
-		return 1
-		
-	ret = cmp(uv1, uv2)
-	if ret:
-		return ret
-	return cmp(glyphName1, glyphName2)
-		
-		
+
 class cmap_format_14(CmapSubtable):
 
 	def decompileHeader(self, data, ttFont):
@@ -1086,10 +1084,10 @@
 		self.language = 0xFF # has no language.
 
 	def decompile(self, data, ttFont):
-		if data != None and ttFont != None:
+		if data is not None and ttFont is not None and ttFont.lazy:
 			self.decompileHeader(data, ttFont)
 		else:
-			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 		data = self.data
 		
 		self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail.
@@ -1108,13 +1106,13 @@
 					startOffset += 4
 					firstBaseUV = cvtToUVS(uv)
 					cnt = addtlCnt+1
-					baseUVList = range(firstBaseUV, firstBaseUV+cnt)
+					baseUVList = list(range(firstBaseUV, firstBaseUV+cnt))
 					glyphList = [None]*cnt
 					localUVList = zip(baseUVList, glyphList)
 					try:
 						uvsDict[varUVS].extend(localUVList)
 					except KeyError:
-						uvsDict[varUVS] = localUVList
+						uvsDict[varUVS] = list(localUVList)
 				
 			if nonDefUVSOffset:
 				startOffset = nonDefUVSOffset  - 10
@@ -1144,13 +1142,12 @@
 				])
 		writer.newline()
 		uvsDict = self.uvsDict
-		uvsList = uvsDict.keys()
-		uvsList.sort()
+		uvsList = sorted(uvsDict.keys())
 		for uvs in uvsList:
 			uvList = uvsDict[uvs]
-			uvList.sort(cmpUVSListEntry)
+			uvList.sort(key=lambda item: (item[1] is not None, item[0], item[1]))
 			for uv, gname in uvList:
-				if gname == None:
+				if gname is None:
 					gname = "None"
 				# I use the arg rather than th keyword syntax in order to preserve the attribute order.
 				writer.simpletag("map", [ ("uvs",hex(uvs)), ("uv",hex(uv)), ("name", gname)]  )
@@ -1158,11 +1155,11 @@
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
 
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.format = safeEval(attrs["format"])
 		self.length = safeEval(attrs["length"])
 		self.numVarSelectorRecords = safeEval(attrs["numVarSelectorRecords"])
-		self.language = 0xFF # provide a value so that  CmapSubtable.__cmp__() won't fail
+		self.language = 0xFF # provide a value so that  CmapSubtable.__lt__() won't fail
 		if not hasattr(self, "cmap"):
 			self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail.
 		if not hasattr(self, "uvsDict"):
@@ -1170,10 +1167,10 @@
 			uvsDict = self.uvsDict 
 
 		for element in content:
-			if type(element) <> TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
-			if name <> "map":
+			if name != "map":
 				continue
 			uvs = safeEval(attrs["uvs"])
 			uv = safeEval(attrs["uv"])
@@ -1191,8 +1188,7 @@
 			return struct.pack(">HLL", self.format, self.length , self.numVarSelectorRecords) + self.data
 
 		uvsDict = self.uvsDict
-		uvsList = uvsDict.keys()
-		uvsList.sort()
+		uvsList = sorted(uvsDict.keys())
 		self.numVarSelectorRecords = len(uvsList)
 		offset = 10 + self.numVarSelectorRecords*11 # current value is end of VarSelectorRecords block.
 		data = []
@@ -1200,9 +1196,9 @@
 		for uvs in uvsList:
 			entryList = uvsDict[uvs]
 
-			defList = filter(lambda entry: entry[1] == None, entryList)
+			defList = [entry for entry in entryList if entry[1] is None]
 			if defList:
-				defList = map(lambda entry: entry[0], defList)
+				defList = [entry[0] for entry in defList]
 				defOVSOffset = offset
 				defList.sort()
 
@@ -1227,7 +1223,7 @@
 			else:
 				defOVSOffset = 0
 
-			ndefList = filter(lambda entry: entry[1] != None, entryList)
+			ndefList = [entry for entry in entryList if entry[1] is not None]
 			if ndefList:
 				nonDefUVSOffset = offset
 				ndefList.sort()
@@ -1245,7 +1241,7 @@
 			vrec = struct.pack(">3sLL", cvtFromUVS(uvs), defOVSOffset, nonDefUVSOffset)
 			varSelectorRecords.append(vrec)
 				
-		data = "".join(varSelectorRecords) + "".join(data)
+		data = bytesjoin(varSelectorRecords) + bytesjoin(data)
 		self.length = 10 + len(data)
 		headerdata = struct.pack(">HLL", self.format, self.length , self.numVarSelectorRecords)
 		self.data = headerdata + data
@@ -1266,7 +1262,7 @@
 		writer.endtag(cmapName)
 		writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.data = readHex(content)
 		self.cmap = {}
 	
@@ -1277,10 +1273,10 @@
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
 		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
-		if data != None and ttFont != None:
+		if data is not None and ttFont is not None:
 			self.decompileHeader(data[offset:offset+int(length)], ttFont)
 		else:
-			assert (data == None and ttFont == None), "Need both data and ttFont arguments"
+			assert (data is None and ttFont is None), "Need both data and ttFont arguments"
 
 	def compile(self, ttFont):
 		if self.data:
@@ -1294,5 +1290,6 @@
 		4: cmap_format_4,
 		6: cmap_format_6,
 		12: cmap_format_12,
+		13: cmap_format_13,
 		14: cmap_format_14,
 		}
diff --git a/Lib/fontTools/ttLib/tables/_c_v_t.py b/Lib/fontTools/ttLib/tables/_c_v_t.py
index be08ca3..fc0ceb7 100644
--- a/Lib/fontTools/ttLib/tables/_c_v_t.py
+++ b/Lib/fontTools/ttLib/tables/_c_v_t.py
@@ -1,21 +1,22 @@
-import sys
-import DefaultTable
-import array
-from fontTools import ttLib
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import sys
+import array
 
 class table__c_v_t(DefaultTable.DefaultTable):
 	
 	def decompile(self, data, ttFont):
 		values = array.array("h")
 		values.fromstring(data)
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			values.byteswap()
 		self.values = values
 	
 	def compile(self, ttFont):
 		values = self.values[:]
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			values.byteswap()
 		return values.tostring()
 	
@@ -25,7 +26,7 @@
 			writer.simpletag("cv", value=value, index=i)
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "values"):
 			self.values = array.array("h")
 		if name == "cv":
diff --git a/Lib/fontTools/ttLib/tables/_f_p_g_m.py b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
index 6f7beee..a6cebae 100644
--- a/Lib/fontTools/ttLib/tables/_f_p_g_m.py
+++ b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
@@ -1,6 +1,7 @@
-import DefaultTable
-import array
-import ttProgram
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from . import DefaultTable
+from . import ttProgram
 
 class table__f_p_g_m(DefaultTable.DefaultTable):
 	
@@ -16,9 +17,9 @@
 		self.program.toXML(writer, ttFont)
 		writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		program = ttProgram.Program()
-		program.fromXML((name, attrs, content), ttFont)
+		program.fromXML(name, attrs, content, ttFont)
 		self.program = program
 	
 	def __len__(self):
diff --git a/Lib/fontTools/ttLib/tables/_g_a_s_p.py b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
index 43f513f..5097a08 100644
--- a/Lib/fontTools/ttLib/tables/_g_a_s_p.py
+++ b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
@@ -1,6 +1,8 @@
-import DefaultTable
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import struct
 
 
 GASP_SYMMETRIC_GRIDFIT = 0x0004
@@ -24,9 +26,8 @@
 	def compile(self, ttFont):
 		version = 0 # ignore self.version
 		numRanges = len(self.gaspRange)
-		data = ""
-		items = self.gaspRange.items()
-		items.sort()
+		data = b""
+		items = sorted(self.gaspRange.items())
 		for rangeMaxPPEM, rangeGaspBehavior in items:
 			data = data + struct.pack(">HH", rangeMaxPPEM, rangeGaspBehavior)
 			if rangeGaspBehavior & ~(GASP_GRIDFIT | GASP_DOGRAY):
@@ -35,16 +36,15 @@
 		return data
 	
 	def toXML(self, writer, ttFont):
-		items = self.gaspRange.items()
-		items.sort()
+		items = sorted(self.gaspRange.items())
 		for rangeMaxPPEM, rangeGaspBehavior in items:
 			writer.simpletag("gaspRange", [
 					("rangeMaxPPEM", rangeMaxPPEM),
 					("rangeGaspBehavior", rangeGaspBehavior)])
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
-		if name <> "gaspRange":
+	def fromXML(self, name, attrs, content, ttFont):
+		if name != "gaspRange":
 			return
 		if not hasattr(self, "gaspRange"):
 			self.gaspRange = {}
diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
index 418d450..57a664e 100644
--- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -1,6 +1,21 @@
 """_g_l_y_f.py -- Converter classes for the 'glyf' table."""
 
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools import ttLib
+from fontTools.misc.textTools import safeEval
+from fontTools.misc.arrayTools import calcBounds, calcIntBounds, pointInRect
+from fontTools.misc.bezierTools import calcQuadraticBounds
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from . import DefaultTable
+from . import ttProgram
+import sys
+import struct
+import array
+import warnings
+
 #
 # The Apple and MS rasterizers behave differently for 
 # scaled composite components: one does scale first and then translate
@@ -14,18 +29,6 @@
 SCALE_COMPONENT_OFFSET_DEFAULT = 0   # 0 == MS, 1 == Apple
 
 
-import sys
-import struct
-from fontTools.misc import sstruct
-import DefaultTable
-from fontTools import ttLib
-from fontTools.misc.textTools import safeEval, readHex
-from fontTools.misc.arrayTools import calcBounds
-import ttProgram
-import array
-from types import StringType, TupleType
-import warnings
-
 class table__g_l_y_f(DefaultTable.DefaultTable):
 	
 	def decompile(self, data, ttFont):
@@ -42,8 +45,8 @@
 				glyphName = 'ttxautoglyph%s' % i
 			next = int(loca[i+1])
 			glyphdata = data[last:next]
-			if len(glyphdata) <> (next - last):
-				raise ttLib.TTLibError, "not enough 'glyf' table data"
+			if len(glyphdata) != (next - last):
+				raise ttLib.TTLibError("not enough 'glyf' table data")
 			glyph = Glyph(glyphdata)
 			self.glyphs[glyphName] = glyph
 			last = next
@@ -51,11 +54,13 @@
 			warnings.warn("too much 'glyf' table data")
 		if noname:
 			warnings.warn('%s glyphs have no name' % i)
+		if not ttFont.lazy:
+			for glyph in self.glyphs.values():
+				glyph.expand(self)
 	
 	def compile(self, ttFont):
 		if not hasattr(self, "glyphOrder"):
 			self.glyphOrder = ttFont.getGlyphOrder()
-		import string
 		locations = []
 		currentLocation = 0
 		dataList = []
@@ -67,8 +72,9 @@
 			currentLocation = currentLocation + len(glyphData)
 			dataList.append(glyphData)
 		locations.append(currentLocation)
-		data = string.join(dataList, "")
-		ttFont['loca'].set(locations)
+		data = bytesjoin(dataList)
+		if 'loca' in ttFont:
+			ttFont['loca'].set(locations)
 		ttFont['maxp'].numGlyphs = len(self.glyphs)
 		return data
 	
@@ -84,7 +90,7 @@
 		for glyphName in glyphNames:
 			if not counter % progressStep and progress is not None:
 				progress.setLabel("Dumping 'glyf' table... (%s)" % glyphName)
-				progress.increment(progressStep / float(numGlyphs))
+				progress.increment(progressStep / numGlyphs)
 			counter = counter + 1
 			glyph = self[glyphName]
 			if glyph.numberOfContours:
@@ -105,8 +111,8 @@
 				writer.newline()
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
-		if name <> "TTGlyph":
+	def fromXML(self, name, attrs, content, ttFont):
+		if name != "TTGlyph":
 			return
 		if not hasattr(self, "glyphs"):
 			self.glyphs = {}
@@ -120,9 +126,10 @@
 			setattr(glyph, attr, safeEval(attrs.get(attr, '0')))
 		self.glyphs[glyphName] = glyph
 		for element in content:
-			if type(element) <> TupleType:
+			if not isinstance(element, tuple):
 				continue
-			glyph.fromXML(element, ttFont)
+			name, attrs, content = element
+			glyph.fromXML(name, attrs, content, ttFont)
 		if not ttFont.recalcBBoxes:
 			glyph.compact(self, 0)
 	
@@ -140,7 +147,7 @@
 		return self.glyphs.keys()
 	
 	def has_key(self, glyphName):
-		return self.glyphs.has_key(glyphName)
+		return glyphName in self.glyphs
 	
 	__contains__ = has_key
 	
@@ -198,7 +205,7 @@
 UNSCALED_COMPONENT_OFFSET  = 0x1000  # composite designed not to have the component offset scaled (designed for MS) 
 
 
-class Glyph:
+class Glyph(object):
 	
 	def __init__(self, data=""):
 		if not data:
@@ -207,7 +214,7 @@
 			return
 		self.data = data
 	
-	def compact(self, glyfTable, recalcBBoxes=1):
+	def compact(self, glyfTable, recalcBBoxes=True):
 		data = self.compile(glyfTable, recalcBBoxes)
 		self.__dict__.clear()
 		self.data = data
@@ -227,7 +234,7 @@
 		else:
 			self.decompileCoordinates(data)
 	
-	def compile(self, glyfTable, recalcBBoxes=1):
+	def compile(self, glyfTable, recalcBBoxes=True):
 		if hasattr(self, "data"):
 			return self.data
 		if self.numberOfContours == 0:
@@ -245,7 +252,7 @@
 		if len(data) % 4:
 			# add pad bytes
 			nPadBytes = 4 - (len(data) % 4)
-			data = data + "\0" * nPadBytes
+			data = data + b"\0" * nPadBytes
 		return data
 	
 	def toXML(self, writer, ttFont):
@@ -277,18 +284,18 @@
 				writer.endtag("instructions")
 				writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == "contour":
 			if self.numberOfContours < 0:
-				raise ttLib.TTLibError, "can't mix composites and contours in glyph"
+				raise ttLib.TTLibError("can't mix composites and contours in glyph")
 			self.numberOfContours = self.numberOfContours + 1
 			coordinates = GlyphCoordinates()
 			flags = []
 			for element in content:
-				if type(element) <> TupleType:
+				if not isinstance(element, tuple):
 					continue
 				name, attrs, content = element
-				if name <> "pt":
+				if name != "pt":
 					continue  # ignore anything but "pt"
 				coordinates.append((safeEval(attrs["x"]), safeEval(attrs["y"])))
 				flags.append(not not safeEval(attrs["on"]))
@@ -303,19 +310,20 @@
 				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"
+				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)
+			component.fromXML(name, attrs, content, ttFont)
 		elif name == "instructions":
 			self.program = ttProgram.Program()
 			for element in content:
-				if type(element) <> TupleType:
+				if not isinstance(element, tuple):
 					continue
-				self.program.fromXML(element, ttFont)
+				name, attrs, content = element
+				self.program.fromXML(name, attrs, content, ttFont)
 	
 	def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
 		assert self.isComposite()
@@ -358,7 +366,7 @@
 	def decompileCoordinates(self, data):
 		endPtsOfContours = array.array("h")
 		endPtsOfContours.fromstring(data[:2*self.numberOfContours])
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			endPtsOfContours.byteswap()
 		self.endPtsOfContours = endPtsOfContours.tolist()
 		
@@ -419,12 +427,12 @@
 		xFormat = ">" # big endian
 		yFormat = ">" # big endian
 		i = j = 0
-		while 1:
-			flag = ord(data[i])
+		while True:
+			flag = byteord(data[i])
 			i = i + 1
 			repeat = 1
 			if flag & flagRepeat:
-				repeat = ord(data[i]) + 1
+				repeat = byteord(data[i]) + 1
 				i = i + 1
 			for k in range(repeat):
 				if flag & flagXShort:
@@ -451,7 +459,7 @@
 		return flags, xCoordinates, yCoordinates
 	
 	def compileComponents(self, glyfTable):
-		data = ""
+		data = b""
 		lastcomponent = len(self.components) - 1
 		more = 1
 		haveInstructions = 0
@@ -469,9 +477,9 @@
 	
 	def compileCoordinates(self):
 		assert len(self.coordinates) == len(self.flags)
-		data = ""
+		data = b""
 		endPtsOfContours = array.array("h", self.endPtsOfContours)
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			endPtsOfContours.byteswap()
 		data = data + endPtsOfContours.tostring()
 		instructions = self.program.getBytecode()
@@ -521,30 +529,81 @@
 				yPoints.append(y)
 				yFormat = yFormat + 'h'
 			# handle repeating flags
-			if flag == lastflag:
+			if flag == lastflag and repeat != 255:
 				repeat = repeat + 1
 				if repeat == 1:
 					compressedflags.append(flag)
-				elif repeat > 1:
-					compressedflags[-2] = flag | flagRepeat
-					compressedflags[-1] = repeat
 				else:
+					compressedflags[-2] = flag | flagRepeat
 					compressedflags[-1] = repeat
 			else:
 				repeat = 0
 				compressedflags.append(flag)
 			lastflag = flag
 		data = data + array.array("B", compressedflags).tostring()
-		xPoints = map(int, xPoints)  # work around struct >= 2.5 bug
-		yPoints = map(int, yPoints)
-		data = data + apply(struct.pack, (xFormat,)+tuple(xPoints))
-		data = data + apply(struct.pack, (yFormat,)+tuple(yPoints))
+		if coordinates.isFloat():
+			# Warn?
+			xPoints = [int(round(x)) for x in xPoints]
+			yPoints = [int(round(y)) for y in xPoints]
+		data = data + struct.pack(*(xFormat,)+tuple(xPoints))
+		data = data + struct.pack(*(yFormat,)+tuple(yPoints))
 		return data
 	
 	def recalcBounds(self, glyfTable):
-		coordinates, endPts, flags = self.getCoordinates(glyfTable)
-		if len(coordinates) > 0:
-			self.xMin, self.yMin, self.xMax, self.yMax = calcBounds(coordinates)
+		coords, endPts, flags = self.getCoordinates(glyfTable)
+		if len(coords) > 0:
+			if 0:
+				# This branch calculates exact glyph outline bounds
+				# analytically, handling cases without on-curve
+				# extremas, etc.  However, the glyf table header
+				# simply says that the bounds should be min/max x/y
+				# "for coordinate data", so I suppose that means no
+				# fancy thing here, just get extremas of all coord
+				# points (on and off).  As such, this branch is
+				# disabled.
+
+				# Collect on-curve points
+				onCurveCoords = [coords[j] for j in range(len(coords))
+						 if flags[j] & flagOnCurve]
+				# Add implicit on-curve points
+				start = 0
+				for end in endPts:
+					last = end
+					for j in range(start, end + 1):
+						if not ((flags[j] | flags[last]) & flagOnCurve):
+							x = (coords[last][0] + coords[j][0]) / 2
+							y = (coords[last][1] + coords[j][1]) / 2
+							onCurveCoords.append((x,y))
+						last = j
+					start = end + 1
+				# Add bounds for curves without an explicit extrema
+				start = 0
+				for end in endPts:
+					last = end
+					for j in range(start, end + 1):
+						if not (flags[j] & flagOnCurve):
+							next = j + 1 if j < end else start
+							bbox = calcBounds([coords[last], coords[next]])
+							if not pointInRect(coords[j], bbox):
+								# Ouch!
+								warnings.warn("Outline has curve with implicit extrema.")
+								# Ouch!  Find analytical curve bounds.
+								pthis = coords[j]
+								plast = coords[last]
+								if not (flags[last] & flagOnCurve):
+									plast = ((pthis[0]+plast[0])/2, (pthis[1]+plast[1])/2)
+								pnext = coords[next]
+								if not (flags[next] & flagOnCurve):
+									pnext = ((pthis[0]+pnext[0])/2, (pthis[1]+pnext[1])/2)
+								bbox = calcQuadraticBounds(plast, pthis, pnext)
+								onCurveCoords.append((bbox[0],bbox[1]))
+								onCurveCoords.append((bbox[2],bbox[3]))
+						last = j
+					start = end + 1
+
+				self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(onCurveCoords)
+			else:
+				self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords)
 		else:
 			self.xMin, self.yMin, self.xMax, self.yMax = (0, 0, 0, 0)
 	
@@ -557,7 +616,7 @@
 	
 	def __getitem__(self, componentIndex):
 		if not self.isComposite():
-			raise ttLib.TTLibError, "can't use glyph as sequence"
+			raise ttLib.TTLibError("can't use glyph as sequence")
 		return self.components[componentIndex]
 	
 	def getCoordinates(self, glyfTable):
@@ -639,7 +698,7 @@
 
 	def removeHinting(self):
 		if not hasattr(self, "data"):
-			self.program = ttLib.tables.ttProgram.Program()
+			self.program = ttProgram.Program()
 			self.program.fromBytecode([])
 			return
 
@@ -668,7 +727,7 @@
 					# padding.
 					coordBytes = 0
 					j = 0
-					while 1:
+					while True:
 						flag = data[i]
 						i = i + 1
 						repeat = 1
@@ -716,19 +775,19 @@
 		if len(data) % 4:
 			# add pad bytes
 			nPadBytes = 4 - (len(data) % 4)
-			data = data + "\0" * nPadBytes
+			data = data + b"\0" * nPadBytes
 
 		self.data = data
-	
-	def __cmp__(self, other):
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
 
-		return cmp(self.__dict__, other.__dict__)
+	def __ne__(self, other):
+		return not self.__eq__(other)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			return NotImplemented
+		return self.__dict__ == other.__dict__
 
 
-class GlyphComponent:
+class GlyphComponent(object):
 	
 	def __init__(self):
 		pass
@@ -754,7 +813,6 @@
 		self.glyphName = glyfTable.getGlyphName(int(glyphID))
 		#print ">>", reprflag(self.flags)
 		data = data[4:]
-		x4000 = float(0x4000)
 		
 		if self.flags & ARG_1_AND_2_ARE_WORDS:
 			if self.flags & ARGS_ARE_XY_VALUES:
@@ -773,17 +831,17 @@
 		
 		if self.flags & WE_HAVE_A_SCALE:
 			scale, = struct.unpack(">h", data[:2])
-			self.transform = [[scale/x4000, 0], [0, scale/x4000]]  # fixed 2.14
+			self.transform = [[fi2fl(scale,14), 0], [0, fi2fl(scale,14)]]  # 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 = [[xscale/x4000, 0], [0, yscale/x4000]]  # fixed 2.14
+			self.transform = [[fi2fl(xscale,14), 0], [0, fi2fl(yscale,14)]]  # 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 = [[xscale/x4000, scale01/x4000],
-					  [scale10/x4000, yscale/x4000]] # fixed 2.14
+			self.transform = [[fi2fl(xscale,14), fi2fl(scale01,14)],
+					  [fi2fl(scale10,14), fi2fl(yscale,14)]] # fixed 2.14
 			data = data[8:]
 		more = self.flags & MORE_COMPONENTS
 		haveInstructions = self.flags & WE_HAVE_INSTRUCTIONS
@@ -793,7 +851,7 @@
 		return more, haveInstructions, data
 	
 	def compile(self, more, haveInstructions, glyfTable):
-		data = ""
+		data = b""
 		
 		# reset all flags we will calculate ourselves
 		flags = self.flags & (ROUND_XY_TO_GRID | USE_MY_METRICS | 
@@ -819,13 +877,13 @@
 				flags = flags | ARG_1_AND_2_ARE_WORDS
 		
 		if hasattr(self, "transform"):
-			transform = [[int(x * 0x4000 + .5) for x in row] for row in self.transform]
+			transform = [[fl2fi(x,14) for x in row] for row in self.transform]
 			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]:
+			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])
@@ -851,7 +909,7 @@
 						("scalex", transform[0][0]), ("scale01", transform[0][1]),
 						("scale10", transform[1][0]), ("scaley", transform[1][1]),
 						]
-			elif transform[0][0] <> transform[1][1]:
+			elif transform[0][0] != transform[1][1]:
 				attrs = attrs + [
 						("scalex", transform[0][0]), ("scaley", transform[1][1]),
 						]
@@ -861,42 +919,57 @@
 		writer.simpletag("component", attrs)
 		writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.glyphName = attrs["glyphName"]
-		if attrs.has_key("firstPt"):
+		if "firstPt" in attrs:
 			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"):
+		if "scale01" in attrs:
 			scalex = safeEval(attrs["scalex"])
 			scale01 = safeEval(attrs["scale01"])
 			scale10 = safeEval(attrs["scale10"])
 			scaley = safeEval(attrs["scaley"])
 			self.transform = [[scalex, scale01], [scale10, scaley]]
-		elif attrs.has_key("scalex"):
+		elif "scalex" in attrs:
 			scalex = safeEval(attrs["scalex"])
 			scaley = safeEval(attrs["scaley"])
 			self.transform = [[scalex, 0], [0, scaley]]
-		elif attrs.has_key("scale"):
+		elif "scale" in attrs:
 			scale = safeEval(attrs["scale"])
 			self.transform = [[scale, 0], [0, scale]]
 		self.flags = safeEval(attrs["flags"])
 	
-	def __cmp__(self, other):
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
+	def __ne__(self, other):
+		return not self.__eq__(other)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			return NotImplemented
+		return self.__dict__ == other.__dict__
 
-		return cmp(self.__dict__, other.__dict__)
-
-class GlyphCoordinates:
+class GlyphCoordinates(object):
 
 	def __init__(self, iterable=[]):
 		self._a = array.array("h")
 		self.extend(iterable)
 
+	def isFloat(self):
+		return self._a.typecode == 'f'
+
+	def _ensureFloat(self):
+		if self.isFloat():
+			return
+		self._a = array.array("f", self._a)
+
+	def _checkFloat(self, p):
+		if any(isinstance(v, float) for v in p):
+			p = [int(v) if int(v) == v else v for v in p]
+			if any(isinstance(v, float) for v in p):
+				self._ensureFloat()
+		return p
+
 	@staticmethod
 	def zeros(count):
 		return GlyphCoordinates([(0,0)] * count)
@@ -907,43 +980,46 @@
 		return c
 
 	def __len__(self):
-		return len(self._a) / 2
+		return len(self._a) // 2
 
 	def __getitem__(self, k):
 		if isinstance(k, slice):
-			indices = xrange(*k.indices(len(self)))
+			indices = range(*k.indices(len(self)))
 			return [self[i] for i in indices]
 		return self._a[2*k],self._a[2*k+1]
 
 	def __setitem__(self, k, v):
 		if isinstance(k, slice):
-			indices = xrange(*k.indices(len(self)))
+			indices = range(*k.indices(len(self)))
 			for j,i in enumerate(indices):
 				self[i] = v[j]
 			return
+		v = self._checkFloat(v)
 		self._a[2*k],self._a[2*k+1] = v
 
 	def __repr__(self):
 		return 'GlyphCoordinates(['+','.join(str(c) for c in self)+'])'
 
-	def append(self, (x,y)):
-		self._a.extend((x,y))
+	def append(self, p):
+		p = self._checkFloat(p)
+		self._a.extend(tuple(p))
 
 	def extend(self, iterable):
-		for x,y in iterable:
-			self._a.extend((x,y))
+		for p in iterable:
+			p = self._checkFloat(p)
+			self._a.extend(p)
 
 	def relativeToAbsolute(self):
 		a = self._a
 		x,y = 0,0
-		for i in range(len(a) / 2):
+		for i in range(len(a) // 2):
 			a[2*i  ] = x = a[2*i  ] + x
 			a[2*i+1] = y = a[2*i+1] + y
 
 	def absoluteToRelative(self):
 		a = self._a
 		x,y = 0,0
-		for i in range(len(a) / 2):
+		for i in range(len(a) // 2):
 			dx = a[2*i  ] - x
 			dy = a[2*i+1] - y
 			x = a[2*i  ]
@@ -951,25 +1027,34 @@
 			a[2*i  ] = dx
 			a[2*i+1] = dy
 
-	def translate(self, (x,y)):
+	def translate(self, p):
+		(x,y) = p
 		a = self._a
-		for i in range(len(a) / 2):
+		for i in range(len(a) // 2):
 			a[2*i  ] += x
 			a[2*i+1] += y
 
 	def transform(self, t):
 		a = self._a
-		for i in range(len(a) / 2):
+		for i in range(len(a) // 2):
 			x = a[2*i  ]
 			y = a[2*i+1]
-			a[2*i  ] = int(.5 + x * t[0][0] + y * t[1][0])
-			a[2*i+1] = int(.5 + x * t[0][1] + y * t[1][1])
+			px = x * t[0][0] + y * t[1][0]
+			py = x * t[0][1] + y * t[1][1]
+			self[i] = (px, py)
+
+	def __ne__(self, other):
+		return not self.__eq__(other)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			return NotImplemented
+		return self._a == other._a
 
 
 def reprflag(flag):
 	bin = ""
-	if type(flag) == StringType:
-		flag = ord(flag)
+	if isinstance(flag, str):
+		flag = byteord(flag)
 	while flag:
 		if flag & 0x01:
 			bin = "1" + bin
diff --git a/Lib/fontTools/ttLib/tables/_h_d_m_x.py b/Lib/fontTools/ttLib/tables/_h_d_m_x.py
index 592f426..f9fd557 100644
--- a/Lib/fontTools/ttLib/tables/_h_d_m_x.py
+++ b/Lib/fontTools/ttLib/tables/_h_d_m_x.py
@@ -1,6 +1,7 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-import string
+from . import DefaultTable
 
 hdmxHeaderFormat = """
 	>   # big endian!
@@ -17,11 +18,11 @@
 		dummy, data = sstruct.unpack2(hdmxHeaderFormat, data, self)
 		self.hdmx = {}
 		for i in range(self.numRecords):
-			ppem = ord(data[0])
-			maxSize = ord(data[1])
+			ppem = byteord(data[0])
+			maxSize = byteord(data[1])
 			widths = {}
 			for glyphID in range(numGlyphs):
-				widths[glyphOrder[glyphID]] = ord(data[glyphID+2])
+				widths[glyphOrder[glyphID]] = byteord(data[glyphID+2])
 			self.hdmx[ppem] = widths
 			data = data[self.recordSize:]
 		assert len(data) == 0, "too much hdmx data"
@@ -30,25 +31,23 @@
 		self.version = 0
 		numGlyphs = ttFont['maxp'].numGlyphs
 		glyphOrder = ttFont.getGlyphOrder()
-		self.recordSize = 4 * ((2 + numGlyphs + 3) / 4)
-		pad = (self.recordSize - 2 - numGlyphs) * "\0"
+		self.recordSize = 4 * ((2 + numGlyphs + 3) // 4)
+		pad = (self.recordSize - 2 - numGlyphs) * b"\0"
 		self.numRecords = len(self.hdmx)
 		data = sstruct.pack(hdmxHeaderFormat, self)
-		items = self.hdmx.items()
-		items.sort()
+		items = sorted(self.hdmx.items())
 		for ppem, widths in items:
-			data = data + chr(ppem) + chr(max(widths.values()))
+			data = data + bytechr(ppem) + bytechr(max(widths.values()))
 			for glyphID in range(len(glyphOrder)):
 				width = widths[glyphOrder[glyphID]]
-				data = data + chr(width)
+				data = data + bytechr(width)
 			data = data + pad
 		return data
 	
 	def toXML(self, writer, ttFont):
 		writer.begintag("hdmxData")
 		writer.newline()
-		ppems = self.hdmx.keys()
-		ppems.sort()
+		ppems = sorted(self.hdmx.keys())
 		records = []
 		format = ""
 		for ppem in ppems:
@@ -58,7 +57,7 @@
 		glyphNames = ttFont.getGlyphOrder()[:]
 		glyphNames.sort()
 		maxNameLen = max(map(len, glyphNames))
-		format = "%" + `maxNameLen` + 's:' + format + ' ;'
+		format = "%" + repr(maxNameLen) + 's:' + format + ' ;'
 		writer.write(format % (("ppem",) + tuple(ppems)))
 		writer.newline()
 		writer.newline()
@@ -74,18 +73,18 @@
 		writer.endtag("hdmxData")
 		writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
-		if name <> "hdmxData":
+	def fromXML(self, name, attrs, content, ttFont):
+		if name != "hdmxData":
 			return
-		content = string.join(content, "")
-		lines = string.split(content, ";")
-		topRow = string.split(lines[0])
+		content = strjoin(content)
+		lines = content.split(";")
+		topRow = lines[0].split()
 		assert topRow[0] == "ppem:", "illegal hdmx format"
-		ppems = map(int, topRow[1:])
+		ppems = list(map(int, topRow[1:]))
 		self.hdmx = hdmx = {}
 		for ppem in ppems:
 			hdmx[ppem] = {}
-		lines = map(string.split, lines[1:])
+		lines = (line.split() for line in lines[1:])
 		for line in lines:
 			if not line:
 				continue
@@ -94,7 +93,7 @@
 			if "\\" in glyphName:
 				from fontTools.misc.textTools import safeEval
 				glyphName = safeEval('"""' + glyphName + '"""')
-			line = map(int, line[1:])
+			line = list(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
index 060930f..1c2e366 100644
--- a/Lib/fontTools/ttLib/tables/_h_e_a_d.py
+++ b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
@@ -1,8 +1,10 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-import time
-import string
 from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from . import DefaultTable
+import time
+import calendar
 
 
 headFormat = """
@@ -13,8 +15,8 @@
 		magicNumber:        I
 		flags:              H
 		unitsPerEm:         H
-		created:            8s
-		modified:           8s
+		created:            Q
+		modified:           Q
 		xMin:               h
 		yMin:               h
 		xMax:               h
@@ -35,25 +37,12 @@
 		if rest:
 			# this is quite illegal, but there seem to be fonts out there that do this
 			assert rest == "\0\0"
-		self.unitsPerEm = int(self.unitsPerEm)
-		self.flags = int(self.flags)
-		self.strings2dates()
 	
 	def compile(self, ttFont):
-		self.modified = long(time.time() - mac_epoch_diff)
-		self.dates2strings()
+		self.modified = int(time.time() - mac_epoch_diff)
 		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()
@@ -67,7 +56,7 @@
 					value = time.asctime(time.gmtime(0))
 			if name in ("magicNumber", "checkSumAdjustment"):
 				if value < 0:
-					value = value + 0x100000000L
+					value = value + 0x100000000
 				value = hex(value)
 				if value[-1:] == "L":
 					value = value[:-1]
@@ -76,81 +65,16 @@
 			writer.simpletag(name, value=value)
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		value = attrs["value"]
 		if name in ("created", "modified"):
-			value = parse_date(value) - mac_epoch_diff
+			value = calendar.timegm(time.strptime(value)) - mac_epoch_diff
 		elif name in ("macStyle", "flags"):
 			value = binary2num(value)
 		else:
-			try:
-				value = safeEval(value)
-			except OverflowError:
-				value = long(value)
+			value = safeEval(value)
 		setattr(self, name, value)
-	
-	def __cmp__(self, other):
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(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 = (1972, 1, 1, 0, 0, 0, 0, 0, 0)
-	safe_epoch = time.mktime(safe_epoch_t) - time.timezone
-	# This assert fails in certain time zones, with certain daylight settings
-	#assert time.gmtime(safe_epoch)[:6] == safe_epoch_t[:6]
-	seconds1904to1972 = 60 * 60 * 24 * (365 * (1972-1904) + 17) # thanks, Laurence!
-	return long(safe_epoch - seconds1904to1972)
-
-mac_epoch_diff = calc_mac_epoch_diff()
-
-
-_months = ['   ', 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug',
-		'sep', 'oct', 'nov', 'dec']
-_weekdays = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
-
-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)
-	try:
-		return long(time.mktime(t) - time.timezone)
-	except OverflowError:
-		return 0L
-
-
-def bin2long(data):
-	# thanks </F>!
-	v = 0L
-	for i in map(ord, data):
-	    v = v<<8 | i
-	return v
-
-def long2bin(v, bytes=8):
-	mask = long("FF" * bytes, 16)
-	data = ""
-	while v:
-		data = chr(v & 0xff) + data
-		v = (v >> 8) & mask
-	data = (bytes - len(data)) * "\0" + data
-	assert len(data) == 8, "long too long"
-	return data
-
+# Difference between the original Mac epoch (1904) to the epoch on this machine.
+mac_epoch_diff = calendar.timegm((1904, 1, 1, 0, 0, 0, 0, 0, 0))
diff --git a/Lib/fontTools/ttLib/tables/_h_h_e_a.py b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
index a8decfd..0f0dfbc 100644
--- a/Lib/fontTools/ttLib/tables/_h_h_e_a.py
+++ b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
@@ -1,6 +1,8 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
+from . import DefaultTable
 
 
 hheaFormat = """
@@ -39,24 +41,36 @@
 	
 	def recalc(self, ttFont):
 		hmtxTable = ttFont['hmtx']
-		if ttFont.has_key('glyf'):
+		if 'glyf' in ttFont:
 			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
+			INFINITY = 100000
+			advanceWidthMax = 0
+			minLeftSideBearing = +INFINITY  # arbitrary big number
+			minRightSideBearing = +INFINITY # arbitrary big number
+			xMaxExtent = -INFINITY          # 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)
+				g = glyfTable[name]
+				if g.numberOfContours == 0:
+					continue
+				if g.numberOfContours < 0 and not hasattr(g, "xMax"):
+					# Composite glyph without extents set.
+					# Calculate those.
+					g.recalcBounds(glyfTable)
 				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)
+
+			if xMaxExtent == -INFINITY:
+				# No glyph has outlines.
+				minLeftSideBearing = 0
+				minRightSideBearing = 0
+				xMaxExtent = 0
+
 			self.advanceWidthMax = advanceWidthMax
 			self.minLeftSideBearing = minLeftSideBearing
 			self.minRightSideBearing = minRightSideBearing
@@ -69,11 +83,9 @@
 		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):
+	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
index 475a93f..a17215f 100644
--- a/Lib/fontTools/ttLib/tables/_h_m_t_x.py
+++ b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
@@ -1,8 +1,9 @@
-import sys
-import DefaultTable
-import array
-from fontTools import ttLib
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
+from . import DefaultTable
+import sys
+import array
 import warnings
 
 
@@ -20,17 +21,17 @@
 			numberOfMetrics = numGlyphs # We warn later.
 		# Note: advanceWidth is unsigned, but we read/write as signed.
 		metrics = array.array("h", data[:4 * numberOfMetrics])
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			metrics.byteswap()
 		data = data[4 * numberOfMetrics:]
 		numberOfSideBearings = numGlyphs - numberOfMetrics
 		sideBearings = array.array("h", data[:2 * numberOfSideBearings])
 		data = data[2 * numberOfSideBearings:]
 
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			sideBearings.byteswap()
 		if data:
-			sys.stderr.write("too much data for hmtx/vmtx table\n")
+			warnings.warn("too much 'hmtx'/'vmtx' table data")
 		self.metrics = {}
 		for i in range(numberOfMetrics):
 			glyphName = ttFont.getGlyphName(i)
@@ -57,21 +58,22 @@
 		metrics = metrics[:lastIndex]
 		setattr(ttFont[self.headerTag], self.numberOfMetricsName, len(metrics))
 		
-		metrics = sum(metrics,[])
-		metrics = array.array("h", metrics)
-		if sys.byteorder <> "big":
-			metrics.byteswap()
-		data = metrics.tostring()
+		allMetrics = []
+		for item in metrics:
+			allMetrics.extend(item)
+		allMetrics = array.array("h", allMetrics)
+		if sys.byteorder != "big":
+			allMetrics.byteswap()
+		data = allMetrics.tostring()
 		
 		additionalMetrics = array.array("h", additionalMetrics)
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			additionalMetrics.byteswap()
 		data = data + additionalMetrics.tostring()
 		return data
 	
 	def toXML(self, writer, ttFont):
-		names = self.metrics.keys()
-		names.sort()
+		names = sorted(self.metrics.keys())
 		for glyphName in names:
 			advance, sb = self.metrics[glyphName]
 			writer.simpletag("mtx", [
@@ -81,7 +83,7 @@
 					])
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if not hasattr(self, "metrics"):
 			self.metrics = {}
 		if name == "mtx":
@@ -91,6 +93,6 @@
 	def __getitem__(self, glyphName):
 		return self.metrics[glyphName]
 	
-	def __setitem__(self, glyphName, (advance, sb)):
-		self.metrics[glyphName] = advance, sb
+	def __setitem__(self, glyphName, advance_sb_pair):
+		self.metrics[glyphName] = tuple(advance_sb_pair)
 
diff --git a/Lib/fontTools/ttLib/tables/_k_e_r_n.py b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
index 005d1ed..4ee9fc4 100644
--- a/Lib/fontTools/ttLib/tables/_k_e_r_n.py
+++ b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
@@ -1,8 +1,10 @@
-import DefaultTable
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.ttLib import sfnt
 from fontTools.misc.textTools import safeEval, readHex
-from types import TupleType
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from . import DefaultTable
+import struct
 import warnings
 
 
@@ -20,7 +22,7 @@
 		if (len(data) >= 8) and (version == 1):
 			# AAT Apple's "new" format. Hm.
 			version, nTables = struct.unpack(">LL", data[:8])
-			self.version = version / float(0x10000)
+			self.version = fi2fl(version, 16)
 			data = data[8:]
 			apple = True
 		else:
@@ -36,7 +38,7 @@
 			else:
 				version, length = struct.unpack(">HH", data[:4])
 			length = int(length)
-			if not kern_classes.has_key(version):
+			if version not in kern_classes:
 				subtable = KernTable_format_unkown(version)
 			else:
 				subtable = kern_classes[version]()
@@ -52,7 +54,7 @@
 			nTables = 0
 		if self.version == 1.0:
 			# AAT Apple's "new" format.
-			data = struct.pack(">ll", self.version * 0x10000, nTables)
+			data = struct.pack(">ll", fl2fi(self.version, 16), nTables)
 		else:
 			data = struct.pack(">HH", self.version, nTables)
 		if hasattr(self, "kernTables"):
@@ -66,24 +68,24 @@
 		for subtable in self.kernTables:
 			subtable.toXML(writer, ttFont)
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == "version":
 			self.version = safeEval(attrs["value"])
 			return
-		if name <> "kernsubtable":
+		if name != "kernsubtable":
 			return
 		if not hasattr(self, "kernTables"):
 			self.kernTables = []
 		format = safeEval(attrs["format"])
-		if not kern_classes.has_key(format):
+		if format not in kern_classes:
 			subtable = KernTable_format_unkown(format)
 		else:
 			subtable = kern_classes[format]()
 		self.kernTables.append(subtable)
-		subtable.fromXML((name, attrs, content), ttFont)
+		subtable.fromXML(name, attrs, content, ttFont)
 
 
-class KernTable_format_0:
+class KernTable_format_0(object):
 	
 	def decompile(self, data, ttFont):
 		version, length, coverage = (0,0,0)
@@ -103,7 +105,7 @@
 		for k in range(nPairs):
 			if len(data) < 6:
 				# buggy kern table
-				data = ""
+				data = b""
 				break
 			left, right, value = struct.unpack(">HHh", data[:6])
 			data = data[6:]
@@ -120,10 +122,8 @@
 		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()
+		getGlyphID = ttFont.getGlyphID
+		kernTable = sorted((getGlyphID(left), getGlyphID(right), value) for ((left,right),value) in self.kernTable.items())
 		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
@@ -131,8 +131,7 @@
 	def toXML(self, writer, ttFont):
 		writer.begintag("kernsubtable", coverage=self.coverage, format=0)
 		writer.newline()
-		items = self.kernTable.items()
-		items.sort()
+		items = sorted(self.kernTable.items())
 		for (left, right), value in items:
 			writer.simpletag("pair", [
 					("l", left),
@@ -143,13 +142,13 @@
 		writer.endtag("kernsubtable")
 		writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	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) <> TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
 			self.kernTable[(attrs["l"], attrs["r"])] = safeEval(attrs["v"])
@@ -162,16 +161,9 @@
 	
 	def __delitem__(self, pair):
 		del self.kernTable[pair]
-	
-	def __cmp__(self, other):
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
-
-		return cmp(self.__dict__, other.__dict__)
 
 
-class KernTable_format_2:
+class KernTable_format_2(object):
 	
 	def decompile(self, data, ttFont):
 		self.data = data
@@ -186,11 +178,11 @@
 		writer.endtag("kernsubtable")
 		writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.decompile(readHex(content), ttFont)
 
 
-class KernTable_format_unkown:
+class KernTable_format_unkown(object):
 	
 	def __init__(self, format):
 		self.format = format
@@ -210,7 +202,7 @@
 		writer.endtag("kernsubtable")
 		writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		self.decompile(readHex(content), ttFont)
 
 
diff --git a/Lib/fontTools/ttLib/tables/_l_o_c_a.py b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
index 761ab9b..daa9d03 100644
--- a/Lib/fontTools/ttLib/tables/_l_o_c_a.py
+++ b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
@@ -1,8 +1,8 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from . import DefaultTable
 import sys
-import DefaultTable
 import array
-from fontTools import ttLib
-import struct
 import warnings
 
 class table__l_o_c_a(DefaultTable.DefaultTable):
@@ -17,7 +17,7 @@
 			format = "H"
 		locations = array.array(format)
 		locations.fromstring(data)
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			locations.byteswap()
 		if not longFormat:
 			l = array.array("I")
@@ -29,15 +29,20 @@
 		self.locations = locations
 	
 	def compile(self, ttFont):
-		if max(self.locations) < 0x20000:
+		try:
+			max_location = max(self.locations)
+		except AttributeError:
+			self.set([])
+			max_location = 0
+		if max_location < 0x20000:
 			locations = array.array("H")
 			for i in range(len(self.locations)):
-				locations.append(self.locations[i] / 2)
+				locations.append(self.locations[i] // 2)
 			ttFont['head'].indexToLocFormat = 0
 		else:
 			locations = array.array("I", self.locations)
 			ttFont['head'].indexToLocFormat = 1
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			locations.byteswap()
 		return locations.tostring()
 	
@@ -53,11 +58,4 @@
 	
 	def __len__(self):
 		return len(self.locations)
-	
-	def __cmp__(self, other):
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
-
-		return cmp(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
index 0f0d3f3..2f42ae5 100644
--- a/Lib/fontTools/ttLib/tables/_m_a_x_p.py
+++ b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
@@ -1,6 +1,8 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
+from . import DefaultTable
 
 maxpFormat_0_5 = """
 		>	# big endian
@@ -38,7 +40,7 @@
 		assert len(data) == 0
 	
 	def compile(self, ttFont):
-		if ttFont.has_key('glyf'):
+		if 'glyf' in ttFont:
 			if ttFont.isLoaded('glyf') and ttFont.recalcBBoxes:
 				self.recalc(ttFont)
 		else:
@@ -60,10 +62,11 @@
 		hmtxTable = ttFont['hmtx']
 		headTable = ttFont['head']
 		self.numGlyphs = len(glyfTable)
-		xMin = 100000
-		yMin = 100000
-		xMax = -100000
-		yMax = -100000
+		INFINITY = 100000
+		xMin = +INFINITY
+		yMin = +INFINITY
+		xMax = -INFINITY
+		yMax = -INFINITY
 		maxPoints = 0
 		maxContours = 0
 		maxCompositePoints = 0
@@ -74,7 +77,7 @@
 		for glyphName in ttFont.getGlyphOrder():
 			g = glyfTable[glyphName]
 			if g.numberOfContours:
-				if hmtxTable[glyphName][1] <> g.xMin:
+				if hmtxTable[glyphName][1] != g.xMin:
 					allXMaxIsLsb = 0
 				xMin = min(xMin, g.xMin)
 				yMin = min(yMin, g.yMin)
@@ -90,10 +93,16 @@
 					maxCompositeContours = max(maxCompositeContours, nContours)
 					maxComponentElements = max(maxComponentElements, len(g.components))
 					maxComponentDepth = max(maxComponentDepth, componentDepth)
-		headTable.xMin = xMin
-		headTable.yMin = yMin
-		headTable.xMax = xMax
-		headTable.yMax = yMax
+		if xMin == +INFINITY:
+			headTable.xMin = 0
+			headTable.yMin = 0
+			headTable.xMax = 0
+			headTable.yMax = 0
+		else:
+		    headTable.xMin = xMin
+		    headTable.yMin = yMin
+		    headTable.xMax = xMax
+		    headTable.yMax = yMax
 		self.maxPoints = maxPoints
 		self.maxContours = maxContours
 		self.maxCompositePoints = maxCompositePoints
@@ -105,12 +114,11 @@
 			headTable.flags = headTable.flags & ~0x2
 	
 	def testrepr(self):
-		items = self.__dict__.items()
-		items.sort()
-		print ". . . . . . . . ."
+		items = sorted(self.__dict__.items())
+		print(". . . . . . . . .")
 		for combo in items:
-			print "  %s: %s" % combo
-		print ". . . . . . . . ."
+			print("  %s: %s" % combo)
+		print(". . . . . . . . .")
 	
 	def toXML(self, writer, ttFont):
 		if self.tableVersion != 0x00005000:
@@ -122,14 +130,12 @@
 			names = names + names_1_0
 		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):
+	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
index 307fbb5..b45ecc9 100644
--- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py
+++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
@@ -1,9 +1,9 @@
-import DefaultTable
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
-import string
-import types
+from . import DefaultTable
+import struct
 
 nameRecordFormat = """
 		>	# big endian
@@ -25,8 +25,7 @@
 		expectedStringOffset = 6 + n * nameRecordSize
 		if stringOffset != expectedStringOffset:
 			# XXX we need a warn function
-			print "Warning: 'name' table stringOffset incorrect.",
-			print "Expected: %s; Actual: %s" % (expectedStringOffset, stringOffset)
+			print("Warning: 'name' table stringOffset incorrect. Expected: %s; Actual: %s" % (expectedStringOffset, stringOffset))
 		stringData = data[stringOffset:]
 		data = data[6:]
 		self.names = []
@@ -49,8 +48,8 @@
 			# only happens when there are NO name table entries read
 			# from the TTX file
 			self.names = []
-		self.names.sort()  # sort according to the spec; see NameRecord.__cmp__()
-		stringData = ""
+		self.names.sort()  # sort according to the spec; see NameRecord.__lt__()
+		stringData = b""
 		format = 0
 		n = len(self.names)
 		stringOffset = 6 + n * sstruct.calcsize(nameRecordFormat)
@@ -58,7 +57,7 @@
 		lastoffset = 0
 		done = {}  # remember the data so we can reuse the "pointers"
 		for name in self.names:
-			if done.has_key(name.string):
+			if name.string in done:
 				name.offset, name.length = done[name.string]
 			else:
 				name.offset, name.length = done[name.string] = len(stringData), len(name.string)
@@ -70,14 +69,14 @@
 		for name in self.names:
 			name.toXML(writer, ttFont)
 	
-	def fromXML(self, (name, attrs, content), ttFont):
-		if name <> "namerecord":
+	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)
+		name.fromXML(name, attrs, content, ttFont)
 	
 	def getName(self, nameID, platformID, platEncID, langID=None):
 		for namerecord in self.names:
@@ -87,16 +86,9 @@
 				if langID is None or namerecord.langID == langID:
 					return namerecord
 		return None # not found
-	
-	def __cmp__(self, other):
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
 
-		return cmp(self.names, other.names)
-	
 
-class NameRecord:
+class NameRecord(object):
 	
 	def toXML(self, writer, ttFont):
 		writer.begintag("namerecord", [
@@ -110,50 +102,47 @@
 			if len(self.string) % 2:
 				# no, shouldn't happen, but some of the Apple
 				# tools cause this anyway :-(
-				writer.write16bit(self.string + "\0")
+				writer.write16bit(self.string + b"\0", strip=True)
 			else:
-				writer.write16bit(self.string)
+				writer.write16bit(self.string, strip=True)
 		else:
-			writer.write8bit(self.string)
+			writer.write8bit(self.string, strip=True)
 		writer.newline()
 		writer.endtag("namerecord")
 		writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	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"])
+		s = strjoin(content).strip()
 		if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)):
-			s = ""
-			for element in content:
-				s = s + element
-			s = unicode(s, "utf8")
-			s = s.strip()
 			self.string = s.encode("utf_16_be")
 		else:
-			s = string.strip(string.join(content, ""))
-			self.string = unicode(s, "utf8").encode("latin1")
+			# This is the inverse of write8bit...
+			self.string = s.encode("latin1")
 	
-	def __cmp__(self, other):
-		"""Compare method, so a list of NameRecords can be sorted
-		according to the spec by just sorting it..."""
+	def __lt__(self, other):
+		if type(self) != type(other):
+			return NotImplemented
 
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
-
-		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)
+		# implemented so that list.sort() sorts according to the spec.
+		selfTuple = (
+			getattr(self, "platformID", None),
+			getattr(self, "platEncID", None),
+			getattr(self, "langID", None),
+			getattr(self, "nameID", None),
+			getattr(self, "string", None),
+		)
+		otherTuple = (
+			getattr(other, "platformID", None),
+			getattr(other, "platEncID", None),
+			getattr(other, "langID", None),
+			getattr(other, "nameID", None),
+			getattr(other, "string", None),
+		)
+		return selfTuple < otherTuple
 	
 	def __repr__(self):
 		return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
diff --git a/Lib/fontTools/ttLib/tables/_p_o_s_t.py b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
index 82e91bc..abaaa09 100644
--- a/Lib/fontTools/ttLib/tables/_p_o_s_t.py
+++ b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
@@ -1,12 +1,13 @@
-import sys
-from fontTools.ttLib.standardGlyphOrder import standardGlyphOrder
-import DefaultTable
-import struct
-from fontTools.misc import sstruct
-import array
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools import ttLib
+from fontTools.ttLib.standardGlyphOrder import standardGlyphOrder
+from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval, readHex
-from types import TupleType
+from . import DefaultTable
+import sys
+import struct
+import array
 
 
 postFormat = """
@@ -38,7 +39,7 @@
 			self.decode_format_3_0(data, ttFont)
 		else:
 			# supported format
-			raise ttLib.TTLibError, "'post' table format %f not supported" % self.formatType
+			raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType)
 	
 	def compile(self, ttFont):
 		data = sstruct.pack(postFormat, self)
@@ -50,7 +51,7 @@
 			pass # we're done
 		else:
 			# supported format
-			raise ttLib.TTLibError, "'post' table format %f not supported" % self.formatType
+			raise ttLib.TTLibError("'post' table format %f not supported" % self.formatType)
 		return data
 	
 	def getGlyphOrder(self):
@@ -59,7 +60,7 @@
 		or its relatives instead!
 		"""
 		if not hasattr(self, "glyphOrder"):
-			raise ttLib.TTLibError, "illegal use of getGlyphOrder()"
+			raise ttLib.TTLibError("illegal use of getGlyphOrder()")
 		glyphOrder = self.glyphOrder
 		del self.glyphOrder
 		return glyphOrder
@@ -79,7 +80,7 @@
 		data = data[2:]
 		indices = array.array("H")
 		indices.fromstring(data[:2*numGlyphs])
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			indices.byteswap()
 		data = data[2*numGlyphs:]
 		self.extraNames = extraNames = unpackPStrings(data)
@@ -105,11 +106,11 @@
 		allNames = {}
 		for i in range(ttFont['maxp'].numGlyphs):
 			glyphName = psName = self.glyphOrder[i]
-			if allNames.has_key(glyphName):
+			if glyphName in allNames:
 				# make up a new glyphName that's unique
 				n = allNames[glyphName]
 				allNames[glyphName] = n + 1
-				glyphName = glyphName + "#" + `n`
+				glyphName = glyphName + "#" + repr(n)
 				self.glyphOrder[i] = glyphName
 				mapping[glyphName] = psName
 			else:
@@ -132,11 +133,11 @@
 			extraDict[extraNames[i]] = i
 		for glyphID in range(numGlyphs):
 			glyphName = glyphOrder[glyphID]
-			if self.mapping.has_key(glyphName):
+			if glyphName in self.mapping:
 				psName = self.mapping[glyphName]
 			else:
 				psName = glyphName
-			if extraDict.has_key(psName):
+			if psName in extraDict:
 				index = 258 + extraDict[psName]
 			elif psName in standardGlyphOrder:
 				index = standardGlyphOrder.index(psName)
@@ -145,7 +146,7 @@
 				extraDict[psName] = len(extraNames)
 				extraNames.append(psName)
 			indices.append(index)
-		if sys.byteorder <> "big":
+		if sys.byteorder != "big":
 			indices.byteswap()
 		return struct.pack(">H", numGlyphs) + indices.tostring() + packPStrings(extraNames)
 	
@@ -165,8 +166,7 @@
 						"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()
+			items = sorted(self.mapping.items())
 			for name, psName in items:
 				writer.simpletag("psName", name=name, psName=psName)
 				writer.newline()
@@ -189,13 +189,13 @@
 			writer.endtag("hexdata")
 			writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	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) <> TupleType:
+				if not isinstance(element, tuple):
 					continue
 				name, attrs, content = element
 				if name == "psName":
@@ -203,7 +203,7 @@
 		elif name == "extraNames":
 			self.extraNames = []
 			for element in content:
-				if type(element) <> TupleType:
+				if not isinstance(element, tuple):
 					continue
 				name, attrs, content = element
 				if name == "psName":
@@ -217,15 +217,15 @@
 	index = 0
 	dataLen = len(data)
 	while index < dataLen:
-		length = ord(data[index])
-		strings.append(data[index+1:index+1+length])
+		length = byteord(data[index])
+		strings.append(tostr(data[index+1:index+1+length], encoding="latin1"))
 		index = index + 1 + length
 	return strings
 
 
 def packPStrings(strings):
-	data = ""
+	data = b""
 	for s in strings:
-		data = data + chr(len(s)) + s
+		data = data + bytechr(len(s)) + tobytes(s, encoding="latin1")
 	return data
 
diff --git a/Lib/fontTools/ttLib/tables/_s_b_i_x.py b/Lib/fontTools/ttLib/tables/_s_b_i_x.py
new file mode 100644
index 0000000..35c7e50
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/_s_b_i_x.py
@@ -0,0 +1,142 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import readHex
+from . import DefaultTable
+from .sbixBitmap import *
+from .sbixBitmapSet import *
+import struct
+
+"""
+sbix Table organization:
+
+USHORT        version?
+USHORT        version?
+USHORT        count                    number of bitmap sets
+offsetEntry   offsetEntry[count]       offsetEntries
+(Variable)    storage for bitmap sets
+
+
+offsetEntry:
+
+ULONG         offset                   offset from table start to bitmap set
+
+
+bitmap set:
+
+USHORT        size                     height and width in pixels
+USHORT        resolution               ?
+offsetRecord  offsetRecord[]
+(Variable)    storage for bitmaps
+
+
+offsetRecord:
+
+ULONG         bitmapOffset             offset from start of bitmap set to individual bitmap
+
+
+bitmap:
+
+ULONG         reserved                 00 00 00 00
+char[4]       format                   data type, e.g. "png "
+(Variable)    bitmap data
+"""
+
+sbixHeaderFormat = """
+	>
+	usVal1:          H    # 00 01
+	usVal2:          H    #       00 01
+	numSets:         L    # 00 00 00 02 # number of bitmap sets
+"""
+sbixHeaderFormatSize = sstruct.calcsize(sbixHeaderFormat)
+
+
+sbixBitmapSetOffsetFormat = """
+	>
+	offset:          L    # 00 00 00 10 # offset from table start to each bitmap set
+"""
+sbixBitmapSetOffsetFormatSize = sstruct.calcsize(sbixBitmapSetOffsetFormat)
+
+
+class table__s_b_i_x(DefaultTable.DefaultTable):
+	def __init__(self, tag):
+		self.tableTag = tag
+		self.usVal1 = 1
+		self.usVal2 = 1
+		self.numSets = 0
+		self.bitmapSets = {}
+		self.bitmapSetOffsets = []
+
+	def decompile(self, data, ttFont):
+		# read table header
+		sstruct.unpack(sbixHeaderFormat, data[ : sbixHeaderFormatSize], self)
+		# collect offsets to individual bitmap sets in self.bitmapSetOffsets
+		for i in range(self.numSets):
+			myOffset = sbixHeaderFormatSize + i * sbixBitmapSetOffsetFormatSize
+			offsetEntry = sbixBitmapSetOffset()
+			sstruct.unpack(sbixBitmapSetOffsetFormat, \
+				data[myOffset : myOffset+sbixBitmapSetOffsetFormatSize], \
+				offsetEntry)
+			self.bitmapSetOffsets.append(offsetEntry.offset)
+
+		# decompile BitmapSets
+		for i in range(self.numSets-1, -1, -1):
+			myBitmapSet = BitmapSet(rawdata=data[self.bitmapSetOffsets[i]:])
+			data = data[:self.bitmapSetOffsets[i]]
+			myBitmapSet.decompile(ttFont)
+			#print "  BitmapSet length: %xh" % len(bitmapSetData)
+			#print "Number of Bitmaps:", myBitmapSet.numBitmaps
+			if myBitmapSet.size in self.bitmapSets:
+				from fontTools import ttLib
+				raise ttLib.TTLibError("Pixel 'size' must be unique for each BitmapSet")
+			self.bitmapSets[myBitmapSet.size] = myBitmapSet
+
+		# after the bitmaps have been extracted, we don't need the offsets anymore
+		del self.bitmapSetOffsets
+
+	def compile(self, ttFont):
+		sbixData = ""
+		self.numSets = len(self.bitmapSets)
+		sbixHeader = sstruct.pack(sbixHeaderFormat, self)
+
+		# calculate offset to start of first bitmap set
+		setOffset = sbixHeaderFormatSize + sbixBitmapSetOffsetFormatSize * self.numSets
+
+		for si in sorted(self.bitmapSets.keys()):
+			myBitmapSet = self.bitmapSets[si]
+			myBitmapSet.compile(ttFont)
+			# append offset to this bitmap set to table header
+			myBitmapSet.offset = setOffset
+			sbixHeader += sstruct.pack(sbixBitmapSetOffsetFormat, myBitmapSet)
+			setOffset += sbixBitmapSetHeaderFormatSize + len(myBitmapSet.data)
+			sbixData += myBitmapSet.data
+
+		return sbixHeader + sbixData
+
+	def toXML(self, xmlWriter, ttFont):
+		xmlWriter.simpletag("usVal1", value=self.usVal1)
+		xmlWriter.newline()
+		xmlWriter.simpletag("usVal2", value=self.usVal2)
+		xmlWriter.newline()
+		for i in sorted(self.bitmapSets.keys()):
+			self.bitmapSets[i].toXML(xmlWriter, ttFont)
+
+	def fromXML(self, name, attrs, content, ttFont):
+		if name in ["usVal1", "usVal2"]:
+			setattr(self, name, int(attrs["value"]))
+		elif name == "bitmapSet":
+			myBitmapSet = BitmapSet()
+			for element in content:
+				if isinstance(element, tuple):
+					name, attrs, content = element
+					myBitmapSet.fromXML(name, attrs, content, ttFont)
+			self.bitmapSets[myBitmapSet.size] = myBitmapSet
+		else:
+			from fontTools import ttLib
+			raise ttLib.TTLibError("can't handle '%s' element" % name)
+
+
+# Helper classes
+
+class sbixBitmapSetOffset(object):
+	pass
diff --git a/Lib/fontTools/ttLib/tables/_v_h_e_a.py b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
index 9adc63b..0ed0b7a 100644
--- a/Lib/fontTools/ttLib/tables/_v_h_e_a.py
+++ b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
@@ -1,6 +1,8 @@
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
+from . import DefaultTable
 
 vheaFormat = """
 		>	# big endian
@@ -36,7 +38,7 @@
 	
 	def recalc(self, ttFont):
 		vtmxTable = ttFont['vmtx']
-		if ttFont.has_key('glyf'):
+		if 'glyf' in ttFont:
 			if not ttFont.isLoaded('glyf'):
 				return
 			glyfTable = ttFont['glyf']
@@ -68,11 +70,9 @@
 		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):
+	def fromXML(self, name, attrs, content, ttFont):
 		setattr(self, name, safeEval(attrs["value"]))
 
diff --git a/Lib/fontTools/ttLib/tables/asciiTable.py b/Lib/fontTools/ttLib/tables/asciiTable.py
index ee9455d..87ef62a 100644
--- a/Lib/fontTools/ttLib/tables/asciiTable.py
+++ b/Lib/fontTools/ttLib/tables/asciiTable.py
@@ -1,22 +1,23 @@
-import string
-import DefaultTable
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from . import DefaultTable
 
 
 class asciiTable(DefaultTable.DefaultTable):
 	
 	def toXML(self, writer, ttFont):
-		data = self.data
+		data = tostr(self.data)
 		# removing null bytes. XXX needed??
-		data = string.split(data, '\0')
-		data = string.join(data, '')
+		data = data.split('\0')
+		data = strjoin(data)
 		writer.begintag("source")
 		writer.newline()
-		writer.write_noindent(string.replace(data, "\r", "\n"))
+		writer.write_noindent(data.replace("\r", "\n"))
 		writer.newline()
 		writer.endtag("source")
 		writer.newline()
 	
-	def fromXML(self, (name, attrs, content), ttFont):
-		lines = string.split(string.replace(string.join(content, ""), "\r", "\n"), "\n")
-		self.data = string.join(lines[1:-1], "\r")
+	def fromXML(self, name, attrs, content, ttFont):
+		lines = strjoin(content).replace("\r", "\n").split("\n")
+		self.data = tobytes("\r".join(lines[1:-1]))
 
diff --git a/Lib/fontTools/ttLib/tables/otBase.py b/Lib/fontTools/ttLib/tables/otBase.py
index 0f6a4e8..6a7a63b 100644
--- a/Lib/fontTools/ttLib/tables/otBase.py
+++ b/Lib/fontTools/ttLib/tables/otBase.py
@@ -1,9 +1,9 @@
-from DefaultTable import DefaultTable
-import otData
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from .DefaultTable import DefaultTable
 import struct
-from types import TupleType
 
-class OverflowErrorRecord:
+class OverflowErrorRecord(object):
 	def __init__(self, overflowTuple):
 		self.tableType = overflowTuple[0]
 		self.LookupListIndex = overflowTuple[1]
@@ -30,22 +30,27 @@
 	"""
 	
 	def decompile(self, data, font):
-		import otTables
-		cachingStats = None
-		reader = OTTableReader(data, self.tableTag, cachingStats=cachingStats)
+		from . import otTables
+		cachingStats = None if True else {}
+		class GlobalState(object):
+			def __init__(self, tableType, cachingStats):
+				self.tableType = tableType
+				self.cachingStats = cachingStats
+		globalState = GlobalState(tableType=self.tableTag,
+					  cachingStats=cachingStats)
+		reader = OTTableReader(data, globalState)
 		tableClass = getattr(otTables, self.tableTag)
 		self.table = tableClass()
 		self.table.decompile(reader, font)
-		if 0:
-			stats = [(v, k) for k, v in cachingStats.items()]
-			stats.sort()
+		if cachingStats:
+			stats = sorted([(v, k) for k, v in cachingStats.items()])
 			stats.reverse()
-			print "cachingsstats for ", self.tableTag
+			print("cachingsstats for ", self.tableTag)
 			for v, k in stats:
 				if v < 2:
 					break
-				print v, k
-			print "---", len(stats)
+				print(v, k)
+			print("---", len(stats))
 	
 	def compile(self, font):
 		""" Create a top-level OTFWriter for the GPOS/GSUB table.
@@ -68,7 +73,11 @@
 
 				If a lookup subtable overflows an offset, we have to start all over. 
 		"""
-		writer = OTTableWriter(self.tableTag)
+		class GlobalState(object):
+			def __init__(self, tableType):
+				self.tableType = tableType
+		globalState = GlobalState(tableType=self.tableTag)
+		writer = OTTableWriter(globalState)
 		writer.parent = None
 		self.table.compile(writer, font)
 		return writer.getAllData()
@@ -76,61 +85,62 @@
 	def toXML(self, writer, font):
 		self.table.toXML2(writer, font)
 	
-	def fromXML(self, (name, attrs, content), font):
-		import otTables
+	def fromXML(self, name, attrs, content, font):
+		from . import otTables
 		if not hasattr(self, "table"):
 			tableClass = getattr(otTables, self.tableTag)
 			self.table = tableClass()
-		self.table.fromXML((name, attrs, content), font)
+		self.table.fromXML(name, attrs, content, font)
 
 
-class OTTableReader:
-	
+class OTTableReader(object):
+
 	"""Helper class to retrieve data from an OpenType table."""
-	
-	def __init__(self, data, tableType, offset=0, valueFormat=None, cachingStats=None):
+
+	__slots__ = ('data', 'offset', 'pos', 'globalState', 'localState')
+
+	def __init__(self, data, globalState={}, localState=None, offset=0):
 		self.data = data
 		self.offset = offset
 		self.pos = offset
-		self.tableType = tableType
-		if valueFormat is None:
-			valueFormat = (ValueRecordFactory(), ValueRecordFactory())
-		self.valueFormat = valueFormat
-		self.cachingStats = cachingStats
-	
+		self.globalState = globalState
+		self.localState = localState
+
 	def getSubReader(self, offset):
 		offset = self.offset + offset
-		if self.cachingStats is not None:
-			try:
-				self.cachingStats[offset] = self.cachingStats[offset] + 1
-			except KeyError:
-				self.cachingStats[offset] = 1
-		
-		subReader = self.__class__(self.data, self.tableType, offset,
-			self.valueFormat, self.cachingStats)
-		return subReader
-	
+		cachingStats = self.globalState.cachingStats
+		if cachingStats is not None:
+			cachingStats[offset] = cachingStats.get(offset, 0) + 1
+		return self.__class__(self.data, self.globalState, self.localState, offset)
+
 	def readUShort(self):
 		pos = self.pos
 		newpos = pos + 2
 		value, = struct.unpack(">H", self.data[pos:newpos])
 		self.pos = newpos
 		return value
-	
+
 	def readShort(self):
 		pos = self.pos
 		newpos = pos + 2
 		value, = struct.unpack(">h", self.data[pos:newpos])
 		self.pos = newpos
 		return value
-	
+
 	def readLong(self):
 		pos = self.pos
 		newpos = pos + 4
 		value, = struct.unpack(">l", self.data[pos:newpos])
 		self.pos = newpos
 		return value
-	
+
+	def readUInt24(self):
+		pos = self.pos
+		newpos = pos + 3
+		value, = struct.unpack(">l", b'\0'+self.data[pos:newpos])
+		self.pos = newpos
+		return value
+
 	def readULong(self):
 		pos = self.pos
 		newpos = pos + 4
@@ -141,41 +151,38 @@
 	def readTag(self):
 		pos = self.pos
 		newpos = pos + 4
-		value = self.data[pos:newpos]
+		value = Tag(self.data[pos:newpos])
 		assert len(value) == 4
 		self.pos = newpos
 		return value
-	
-	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 setValueFormat(self, format, which):
-		self.valueFormat[which].setFormat(format)
-	
-	def readValueRecord(self, font, which):
-		return self.valueFormat[which].readValueRecord(self, font)
+
+	def __setitem__(self, name, value):
+		state = self.localState.copy() if self.localState else dict()
+		state[name] = value
+		self.localState = state
+
+	def __getitem__(self, name):
+		return self.localState[name]
 
 
-class OTTableWriter:
+class OTTableWriter(object):
 	
 	"""Helper class to gather and assemble data for OpenType tables."""
 	
-	def __init__(self, tableType, valueFormat=None):
+	def __init__(self, globalState, localState=None):
 		self.items = []
-		self.tableType = tableType
-		if valueFormat is None:
-			valueFormat = ValueRecordFactory(), ValueRecordFactory()
-		self.valueFormat = valueFormat
 		self.pos = None
-	
+		self.globalState = globalState
+		self.localState = localState
+
+	def __setitem__(self, name, value):
+		state = self.localState.copy() if self.localState else dict()
+		state[name] = value
+		self.localState = state
+
+	def __getitem__(self, name):
+		return self.localState[name]
+
 	# assembler interface
 	
 	def getAllData(self):
@@ -205,18 +212,14 @@
 			tableData = table.getData()
 			data.append(tableData)
 
-		return "".join(data)
+		return bytesjoin(data)
 	
 	def getDataLength(self):
 		"""Return the length of this table in bytes, without subtables."""
 		l = 0
-		if hasattr(self, "Extension"):
-			longOffset = 1
-		else:
-			longOffset = 0
 		for item in self.items:
 			if hasattr(item, "getData") or hasattr(item, "getCountData"):
-				if longOffset:
+				if item.longOffset:
 					l = l + 4  # sizeof(ULong)
 				else:
 					l = l + 2  # sizeof(UShort)
@@ -227,28 +230,22 @@
 	def getData(self):
 		"""Assemble the data for this writer/table, without subtables."""
 		items = list(self.items)  # make a shallow copy
-		if hasattr(self,"Extension"):
-			longOffset = 1
-		else:
-			longOffset = 0
 		pos = self.pos
 		numItems = len(items)
 		for i in range(numItems):
 			item = items[i]
 			
 			if hasattr(item, "getData"):
-				if longOffset:
+				if item.longOffset:
 					items[i] = packULong(item.pos - pos)
 				else:
 					try:
 						items[i] = packUShort(item.pos - pos)
-					except AssertionError:
+					except struct.error:
 						# provide data to fix overflow problem.
-						# If the overflow is to a lookup, or from a lookup to a subtable, 
-						# just report the current item.
-						if self.name in [ 'LookupList', 'Lookup']:
-							overflowErrorRecord = self.getOverflowErrorRecord(item)
-						else:
+						# If the overflow is to a lookup, or from a lookup to a subtable,
+						# just report the current item.  Otherwise...
+						if self.name not in [ 'LookupList', 'Lookup']:
 							# overflow is within a subTable. Life is more complicated.
 							# If we split the sub-table just before the current item, we may still suffer overflow.
 							# This is because duplicate table merging is done only within an Extension subTable tree;
@@ -279,20 +276,20 @@
 						overflowErrorRecord = self.getOverflowErrorRecord(item)
 						
 						
-						raise OTLOffsetOverflowError, overflowErrorRecord
+						raise OTLOffsetOverflowError(overflowErrorRecord)
 
-		return "".join(items)
+		return bytesjoin(items)
 	
 	def __hash__(self):
 		# only works after self._doneWriting() has been called
 		return hash(self.items)
 	
-	def __cmp__(self, other):
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
-
-		return cmp(self.items, other.items)
+	def __ne__(self, other):
+		return not self.__eq__(other)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			return NotImplemented
+		return self.items == other.items
 	
 	def _doneWriting(self, internedTables=None):
 		# Convert CountData references to data string items
@@ -305,7 +302,7 @@
 		if internedTables is None:
 			internedTables = {}
 		items = self.items
-		iRange = range(len(items))
+		iRange = list(range(len(items)))
 		
 		if hasattr(self, "Extension"):
 			newTree = 1
@@ -320,8 +317,9 @@
 					item._doneWriting()
 				else:
 					item._doneWriting(internedTables)
-					if internedTables.has_key(item):
-						items[i] = item = internedTables[item]
+					internedItem = internedTables.get(item)
+					if internedItem:
+						items[i] = item = internedItem
 					else:
 						internedTables[item] = item
 		self.items = tuple(items)
@@ -342,7 +340,7 @@
 		done[self] = 1
 
 		numItems = len(self.items)
-		iRange = range(numItems)
+		iRange = list(range(numItems))
 		iRange.reverse()
 
 		if hasattr(self, "Extension"):
@@ -359,13 +357,12 @@
 				if hasattr(item, "name") and (item.name == "Coverage"):
 					sortCoverageLast = 1
 					break
-			if not done.has_key(item):
+			if item not in done:
 				item._gatherTables(tables, extTables, done)
 			else:
-				index = max(item.parent.keys())
-				item.parent[index + 1] = self
+				# We're a new parent of item
+				pass
 
-		saveItem = None
 		for i in iRange:
 			item = self.items[i]
 			if not hasattr(item, "getData"):
@@ -376,15 +373,15 @@
 				continue
 
 			if appendExtensions:
-				assert extTables != None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
+				assert extTables is not None, "Program or XML editing error. Extension subtables cannot contain extensions subtables"
 				newDone = {}
 				item._gatherTables(extTables, None, newDone)
 
-			elif not done.has_key(item):
+			elif item not in done:
 				item._gatherTables(tables, extTables, done)
 			else:
-				index = max(item.parent.keys())
-				item.parent[index + 1] = self
+				# We're a new parent of item
+				pass
 
 
 		tables.append(self)
@@ -393,10 +390,11 @@
 	# interface for gathering data, as used by table.compile()
 	
 	def getSubWriter(self):
-		subwriter = self.__class__(self.tableType, self.valueFormat)
-		subwriter.parent = {0:self} # because some subtables have idential values, we discard
-									# the duplicates under the getAllData method. Hence some
-									# subtable writers can have more than one parent writer.
+		subwriter = self.__class__(self.globalState, self.localState)
+		subwriter.parent = self # because some subtables have idential values, we discard
+					# the duplicates under the getAllData method. Hence some
+					# subtable writers can have more than one parent writer.
+					# But we just care about first one right now.
 		return subwriter
 	
 	def writeUShort(self, value):
@@ -405,6 +403,11 @@
 	
 	def writeShort(self, value):
 		self.items.append(struct.pack(">h", value))
+
+	def writeUInt24(self, value):
+		assert 0 <= value < 0x1000000
+		b = struct.pack(">L", value)
+		self.items.append(b[1:])
 	
 	def writeLong(self, value):
 		self.items.append(struct.pack(">l", value))
@@ -413,6 +416,7 @@
 		self.items.append(struct.pack(">L", value))
 	
 	def writeTag(self, tag):
+		tag = Tag(tag).tobytes()
 		assert len(tag) == 4
 		self.items.append(tag)
 	
@@ -420,20 +424,16 @@
 		self.items.append(subWriter)
 	
 	def writeCountReference(self, table, name):
-		self.items.append(CountReference(table, name))
+		ref = CountReference(table, name)
+		self.items.append(ref)
+		return ref
 	
 	def writeStruct(self, format, values):
-		data = apply(struct.pack, (format,) + values)
+		data = struct.pack(*(format,) + values)
 		self.items.append(data)
 	
 	def writeData(self, data):
 		self.items.append(data)
-	
-	def setValueFormat(self, format, which):
-		self.valueFormat[which].setFormat(format)
-	
-	def writeValueRecord(self, value, font, which):
-		return self.valueFormat[which].writeValueRecord(self, font, value)
 
 	def	getOverflowErrorRecord(self, item):
 		LookupListIndex = SubTableIndex = itemName = itemIndex = None
@@ -447,39 +447,45 @@
 			if hasattr(item, 'repeatIndex'):
 				itemIndex = item.repeatIndex
 			if self.name == 'SubTable':
-				LookupListIndex = self.parent[0].repeatIndex
+				LookupListIndex = self.parent.repeatIndex
 				SubTableIndex = self.repeatIndex
 			elif self.name == 'ExtSubTable':
-				LookupListIndex = self.parent[0].parent[0].repeatIndex
-				SubTableIndex = self.parent[0].repeatIndex
+				LookupListIndex = self.parent.parent.repeatIndex
+				SubTableIndex = self.parent.repeatIndex
 			else: # who knows how far below the SubTable level we are! Climb back up to the nearest subtable.
-				itemName = ".".join(self.name, item.name)
-				p1 = self.parent[0]
+				itemName = ".".join([self.name, item.name])
+				p1 = self.parent
 				while p1 and p1.name not in ['ExtSubTable', 'SubTable']:
-					itemName = ".".join(p1.name, item.name)
-					p1 = p1.parent[0]
+					itemName = ".".join([p1.name, item.name])
+					p1 = p1.parent
 				if p1:
 					if p1.name == 'ExtSubTable':
-						LookupListIndex = self.parent[0].parent[0].repeatIndex
-						SubTableIndex = self.parent[0].repeatIndex
+						LookupListIndex = p1.parent.parent.repeatIndex
+						SubTableIndex = p1.parent.repeatIndex
 					else:
-						LookupListIndex = self.parent[0].repeatIndex
-						SubTableIndex = self.repeatIndex
+						LookupListIndex = p1.parent.repeatIndex
+						SubTableIndex = p1.repeatIndex
 
-		return OverflowErrorRecord( (self.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
+		return OverflowErrorRecord( (self.globalState.tableType, LookupListIndex, SubTableIndex, itemName, itemIndex) )
 
 
-class CountReference:
+class CountReference(object):
 	"""A reference to a Count value, not a count of references."""
 	def __init__(self, table, name):
 		self.table = table
 		self.name = name
+	def setValue(self, value):
+		table = self.table
+		name = self.name
+		if table[name] is None:
+			table[name] = value
+		else:
+			assert table[name] == value, (name, table[name], value)
 	def getCountData(self):
 		return packUShort(self.table[self.name])
 
 
 def packUShort(value):
-	assert 0 <= value < 0x10000, value
 	return struct.pack(">H", value)
 
 
@@ -488,64 +494,18 @@
 	return struct.pack(">L", value)
 
 
+class BaseTable(object):
 
-class TableStack:
-	"""A stack of table dicts, working as a stack of namespaces so we can
-	retrieve values from (and store values to) tables higher up the stack."""
-	def __init__(self):
-		self.stack = []
-	def push(self, table):
-		self.stack.append(table)
-	def pop(self):
-		self.stack.pop()
-	def getTop(self):
-		return self.stack[-1]
-	def getValue(self, name):
-		return self.__findTable(name)[name]
-	def storeValue(self, name, value):
-		table = self.__findTable(name)
-		if table[name] is None:
-			table[name] = value
-		else:
-			assert table[name] == value, (table[name], value)
-	def __findTable(self, name):
-		for table in reversed(self.stack):
-			if table.has_key(name):
-				return table
-		raise KeyError, name
-
-
-class BaseTable:
-	def __init__(self):
-		self.compileStatus = 0 # 0 means table was created
-									# 1 means the table.read() function was called by a table which is subject
-									# to delayed compilation
-									# 2 means that it was subject to delayed compilation, and 
-									# has been decompiled
-									# 3 means that the start and end fields have been filled out, and that we
-									# can use the data string rather than compiling from the table data.
-
-		self.recurse = 0
-	
 	def __getattr__(self, attr):
-		# we get here only when the table does not have the attribute.
-		# This method ovveride exists so that we can try to de-compile
-		# a table which is subject to delayed decompilation, and then try
-		# to get the value again after decompilation.
-		self.recurse +=1
-		if self.recurse > 2:
-			# shouldn't ever get here - we should only get to two levels of recursion.
-			# this guards against self.decompile NOT setting compileStatus to other than 1.
-			raise AttributeError, attr 
-		if self.compileStatus == 1:
-			# table.read() has been called, but table has not yet been decompiled
-			# This happens only for extension tables.
-			self.decompile(self.reader, self.font)
-			val = getattr(self, attr)
-			self.recurse -=1
-			return val
-			
-		raise AttributeError, attr 
+		reader = self.__dict__.get("reader")
+		if reader:
+			del self.reader
+			font = self.font
+			del self.font
+			self.decompile(reader, font)
+			return getattr(self, attr)
+
+		raise AttributeError(attr)
 
 
 	"""Generic base class for all OpenType (sub)tables."""
@@ -556,66 +516,95 @@
 	def getConverterByName(self, name):
 		return self.convertersByName[name]
 	
-	def decompile(self, reader, font, tableStack=None):
-		self.compileStatus = 2 # table has been decompiled.
-		if tableStack is None:
-			tableStack = TableStack()
+	def decompile(self, reader, font):
 		self.readFormat(reader)
 		table = {}
 		self.__rawTable = table  # for debugging
-		tableStack.push(table)
-		for conv in self.getConverters():
+		converters = self.getConverters()
+		for conv in converters:
 			if conv.name == "SubTable":
-				conv = conv.getConverter(reader.tableType,
+				conv = conv.getConverter(reader.globalState.tableType,
 						table["LookupType"])
 			if conv.name == "ExtSubTable":
-				conv = conv.getConverter(reader.tableType,
+				conv = conv.getConverter(reader.globalState.tableType,
 						table["ExtensionLookupType"])
+			if conv.name == "FeatureParams":
+				conv = conv.getConverter(reader["FeatureTag"])
 			if conv.repeat:
 				l = []
-				for i in range(tableStack.getValue(conv.repeat) + conv.repeatOffset):
-					l.append(conv.read(reader, font, tableStack))
+				if conv.repeat in table:
+					countValue = table[conv.repeat]
+				else:
+					# conv.repeat is a propagated count
+					countValue = reader[conv.repeat]
+				for i in range(countValue + conv.aux):
+					l.append(conv.read(reader, font, table))
 				table[conv.name] = l
 			else:
-				table[conv.name] = conv.read(reader, font, tableStack)
-		tableStack.pop()
+				if conv.aux and not eval(conv.aux, None, table):
+					continue
+				table[conv.name] = conv.read(reader, font, table)
+				if conv.isPropagated:
+					reader[conv.name] = table[conv.name]
+
 		self.postRead(table, font)
+
 		del self.__rawTable  # succeeded, get rid of debugging info
 
-	def preCompile(self):
-		pass # used only by the LookupList class
+	def ensureDecompiled(self):
+		reader = self.__dict__.get("reader")
+		if reader:
+			del self.reader
+			font = self.font
+			del self.font
+			self.decompile(reader, font)
 
-	def compile(self, writer, font, tableStack=None):
-		if tableStack is None:
-			tableStack = TableStack()
+	def compile(self, writer, font):
+		self.ensureDecompiled()
 		table = self.preWrite(font)
 
 		if hasattr(self, 'sortCoverageLast'):
 			writer.sortCoverageLast = 1
 
+		if hasattr(self.__class__, 'LookupType'):
+			writer['LookupType'].setValue(self.__class__.LookupType)
+
 		self.writeFormat(writer)
-		tableStack.push(table)
 		for conv in self.getConverters():
 			value = table.get(conv.name)
 			if conv.repeat:
 				if value is None:
 					value = []
-				tableStack.storeValue(conv.repeat, len(value) - conv.repeatOffset)
+				countValue = len(value) - conv.aux
+				if conv.repeat in table:
+					CountReference(table, conv.repeat).setValue(countValue)
+				else:
+					# conv.repeat is a propagated count
+					writer[conv.repeat].setValue(countValue)
 				for i in range(len(value)):
-					conv.write(writer, font, tableStack, value[i], i)
+					conv.write(writer, font, table, value[i], i)
 			elif conv.isCount:
 				# Special-case Count values.
 				# Assumption: a Count field will *always* precede
-				# the actual array.
+				# the actual array(s).
 				# We need a default value, as it may be set later by a nested
-				# table. TableStack.storeValue() will then find it here.
-				table[conv.name] = None
+				# table. We will later store it here.
 				# We add a reference: by the time the data is assembled
 				# the Count value will be filled in.
-				writer.writeCountReference(table, conv.name)
+				ref = writer.writeCountReference(table, conv.name)
+				table[conv.name] = None
+				if conv.isPropagated:
+					writer[conv.name] = ref
+			elif conv.isLookupType:
+				ref = writer.writeCountReference(table, conv.name)
+				table[conv.name] = None
+				writer['LookupType'] = ref
 			else:
-				conv.write(writer, font, tableStack, value)
-		tableStack.pop()
+				if conv.aux and not eval(conv.aux, None, table):
+					continue
+				conv.write(writer, font, table, value)
+				if conv.isPropagated:
+					writer[conv.name] = value
 	
 	def readFormat(self, reader):
 		pass
@@ -629,8 +618,8 @@
 	def preWrite(self, font):
 		return self.__dict__.copy()
 	
-	def toXML(self, xmlWriter, font, attrs=None):
-		tableName = self.__class__.__name__
+	def toXML(self, xmlWriter, font, attrs=None, name=None):
+		tableName = name if name else self.__class__.__name__
 		if attrs is None:
 			attrs = []
 		if hasattr(self, "Format"):
@@ -646,16 +635,19 @@
 		# This is because in TTX our parent writes our main tag, and in otBase.py we
 		# do it ourselves. I think I'm getting schizophrenic...
 		for conv in self.getConverters():
-			value = getattr(self, conv.name)
 			if conv.repeat:
+				value = getattr(self, conv.name)
 				for i in range(len(value)):
 					item = value[i]
 					conv.xmlWrite(xmlWriter, font, item, conv.name,
 							[("index", i)])
 			else:
+				if conv.aux and not eval(conv.aux, None, vars(self)):
+					continue
+				value = getattr(self, conv.name)
 				conv.xmlWrite(xmlWriter, font, value, conv.name, [])
 	
-	def fromXML(self, (name, attrs, content), font):
+	def fromXML(self, name, attrs, content, font):
 		try:
 			conv = self.getConverterByName(name)
 		except KeyError:
@@ -670,12 +662,16 @@
 		else:
 			setattr(self, conv.name, value)
 	
-	def __cmp__(self, other):
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
+	def __ne__(self, other):
+		return not self.__eq__(other)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			return NotImplemented
 
-		return cmp(self.__dict__, other.__dict__)
+		self.ensureDecompiled()
+		other.ensureDecompiled()
+
+		return self.__dict__ == other.__dict__
 
 
 class FormatSwitchingBaseTable(BaseTable):
@@ -691,11 +687,14 @@
 	
 	def readFormat(self, reader):
 		self.Format = reader.readUShort()
-		assert self.Format <> 0, (self, reader.pos, len(reader.data))
+		assert self.Format != 0, (self, reader.pos, len(reader.data))
 	
 	def writeFormat(self, writer):
 		writer.writeUShort(self.Format)
 
+	def toXML(self, xmlWriter, font, attrs=None, name=None):
+		BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
+
 
 #
 # Support for ValueRecords
@@ -735,11 +734,11 @@
 valueRecordFormatDict = _buildDict()
 
 
-class ValueRecordFactory:
+class ValueRecordFactory(object):
 	
 	"""Given a format code, this object convert ValueRecords."""
-	
-	def setFormat(self, valueFormat):
+
+	def __init__(self, valueFormat):
 		format = []
 		for mask, name, isDevice, signed in valueRecordFormat:
 			if valueFormat & mask:
@@ -758,7 +757,7 @@
 				value = reader.readUShort()
 			if isDevice:
 				if value:
-					import otTables
+					from . import otTables
 					subReader = reader.getSubReader(value)
 					value = getattr(otTables, name)()
 					value.decompile(subReader, font)
@@ -783,7 +782,7 @@
 				writer.writeUShort(value)
 
 
-class ValueRecord:
+class ValueRecord(object):
 	
 	# see ValueRecordFactory
 	
@@ -819,24 +818,25 @@
 			xmlWriter.simpletag(valueName, simpleItems)
 			xmlWriter.newline()
 	
-	def fromXML(self, (name, attrs, content), font):
-		import otTables
+	def fromXML(self, name, attrs, content, font):
+		from . import otTables
 		for k, v in attrs.items():
 			setattr(self, k, int(v))
 		for element in content:
-			if type(element) <> TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
 			value = getattr(otTables, name)()
 			for elem2 in content:
-				if type(elem2) <> TupleType:
+				if not isinstance(elem2, tuple):
 					continue
-				value.fromXML(elem2, font)
+				name2, attrs2, content2 = elem2
+				value.fromXML(name2, attrs2, content2, font)
 			setattr(self, name, value)
 	
-	def __cmp__(self, other):
-		if type(self) != type(other) or \
-		   self.__class__ != other.__class__:
-			return cmp(id(self), id(other))
-
-		return cmp(self.__dict__, other.__dict__)
+	def __ne__(self, other):
+		return not self.__eq__(other)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			return NotImplemented
+		return self.__dict__ == other.__dict__
diff --git a/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py
index 11a8773..433ea8e 100644
--- a/Lib/fontTools/ttLib/tables/otConverters.py
+++ b/Lib/fontTools/ttLib/tables/otConverters.py
@@ -1,5 +1,8 @@
-from types import TupleType
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from .otBase import ValueRecordFactory
 
 
 def buildConverters(tableSpec, tableNamespace):
@@ -8,63 +11,74 @@
 	the results are assigned to the corresponding class in otTables.py."""
 	converters = []
 	convertersByName = {}
-	for tp, name, repeat, repeatOffset, descr in tableSpec:
+	for tp, name, repeat, aux, descr in tableSpec:
+		tableName = name
 		if name.startswith("ValueFormat"):
 			assert tp == "uint16"
 			converterClass = ValueFormat
-		elif name == "DeltaValue":
+		elif name.endswith("Count") or name.endswith("LookupType"):
 			assert tp == "uint16"
-			converterClass = DeltaValue
-		elif name.endswith("Count"):
-			assert tp == "uint16"
-			converterClass = Count
+			converterClass = ComputedUShort
 		elif name == "SubTable":
 			converterClass = SubTable
 		elif name == "ExtSubTable":
 			converterClass = ExtSubTable
+		elif name == "FeatureParams":
+			converterClass = FeatureParams
 		else:
-			converterClass = converterMapping[tp]
-		tableClass = tableNamespace.get(name)
-		conv = converterClass(name, repeat, repeatOffset, tableClass)
+			if not tp in converterMapping:
+				tableName = tp
+				converterClass = Struct
+			else:
+				converterClass = converterMapping[tp]
+		tableClass = tableNamespace.get(tableName)
+		conv = converterClass(name, repeat, aux, tableClass)
 		if name in ["SubTable", "ExtSubTable"]:
 			conv.lookupTypes = tableNamespace['lookupTypes']
 			# also create reverse mapping
 			for t in conv.lookupTypes.values():
 				for cls in t.values():
-					convertersByName[cls.__name__] = Table(name, repeat, repeatOffset, cls)
+					convertersByName[cls.__name__] = Table(name, repeat, aux, cls)
+		if name == "FeatureParams":
+			conv.featureParamTypes = tableNamespace['featureParamTypes']
+			conv.defaultFeatureParams = tableNamespace['FeatureParams']
+			for cls in conv.featureParamTypes.values():
+				convertersByName[cls.__name__] = Table(name, repeat, aux, cls)
 		converters.append(conv)
-		assert not convertersByName.has_key(name)
+		assert name not in convertersByName, name
 		convertersByName[name] = conv
 	return converters, convertersByName
 
 
-class BaseConverter:
+class BaseConverter(object):
 	
 	"""Base class for converter objects. Apart from the constructor, this
 	is an abstract class."""
 	
-	def __init__(self, name, repeat, repeatOffset, tableClass):
+	def __init__(self, name, repeat, aux, tableClass):
 		self.name = name
 		self.repeat = repeat
-		self.repeatOffset = repeatOffset
+		self.aux = aux
 		self.tableClass = tableClass
 		self.isCount = name.endswith("Count")
+		self.isLookupType = name.endswith("LookupType")
+		self.isPropagated = name in ["ClassCount", "Class2Count", "FeatureTag"]
 	
-	def read(self, reader, font, tableStack):
+	def read(self, reader, font, tableDict):
 		"""Read a value from the reader."""
-		raise NotImplementedError, self
+		raise NotImplementedError(self)
 	
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		"""Write a value to the writer."""
-		raise NotImplementedError, self
+		raise NotImplementedError(self)
 	
 	def xmlRead(self, attrs, content, font):
 		"""Read a value from XML."""
-		raise NotImplementedError, self
+		raise NotImplementedError(self)
 	
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
 		"""Write a value to XML."""
-		raise NotImplementedError, self
+		raise NotImplementedError(self)
 
 
 class SimpleValue(BaseConverter):
@@ -76,196 +90,235 @@
 
 class IntValue(SimpleValue):
 	def xmlRead(self, attrs, content, font):
-		return int(attrs["value"])
+		return int(attrs["value"], 0)
 
 class Long(IntValue):
-	def read(self, reader, font, tableStack):
+	def read(self, reader, font, tableDict):
 		return reader.readLong()
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeLong(value)
 
-class Fixed(IntValue):
-	def read(self, reader, font, tableStack):
-		return float(reader.readLong()) / 0x10000
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
-		writer.writeLong(int(round(value * 0x10000)))
+class Version(BaseConverter):
+	def read(self, reader, font, tableDict):
+		value = reader.readLong()
+		assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
+		return  fi2fl(value, 16)
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		if value < 0x10000:
+			value = fl2fi(value, 16)
+		value = int(round(value))
+		assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
+		writer.writeLong(value)
 	def xmlRead(self, attrs, content, font):
-		return float(attrs["value"])
+		value = attrs["value"]
+		value = float(int(value, 0)) if value.startswith("0") else float(value)
+		if value >= 0x10000:
+			value = fi2fl(value, 16)
+		return value
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		if value >= 0x10000:
+			value = fi2fl(value, 16)
+		if value % 1 != 0:
+			# Write as hex
+			value = "0x%08x" % fl2fi(value, 16)
+		xmlWriter.simpletag(name, attrs + [("value", value)])
+		xmlWriter.newline()
 
 class Short(IntValue):
-	def read(self, reader, font, tableStack):
+	def read(self, reader, font, tableDict):
 		return reader.readShort()
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeShort(value)
 
 class UShort(IntValue):
-	def read(self, reader, font, tableStack):
+	def read(self, reader, font, tableDict):
 		return reader.readUShort()
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeUShort(value)
 
-class Count(Short):
+class UInt24(IntValue):
+	def read(self, reader, font, tableDict):
+		return reader.readUInt24()
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		writer.writeUInt24(value)
+
+class ComputedUShort(UShort):
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
 		xmlWriter.comment("%s=%s" % (name, value))
 		xmlWriter.newline()
 
 class Tag(SimpleValue):
-	def read(self, reader, font, tableStack):
+	def read(self, reader, font, tableDict):
 		return reader.readTag()
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeTag(value)
 
 class GlyphID(SimpleValue):
-	def read(self, reader, font, tableStack):
+	def read(self, reader, font, tableDict):
 		value = reader.readUShort()
 		value =  font.getGlyphName(value)
 		return value
 
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		value =  font.getGlyphID(value)
 		writer.writeUShort(value)
 
+class FloatValue(SimpleValue):
+	def xmlRead(self, attrs, content, font):
+		return float(attrs["value"])
+
+class DeciPoints(FloatValue):
+	def read(self, reader, font, tableDict):
+		value = reader.readUShort()
+		return value / 10
+
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		writer.writeUShort(int(round(value * 10)))
 
 class Struct(BaseConverter):
 	
-	def read(self, reader, font, tableStack):
+	def read(self, reader, font, tableDict):
 		table = self.tableClass()
-		table.decompile(reader, font, tableStack)
+		table.decompile(reader, font)
 		return table
 	
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
-		value.compile(writer, font, tableStack)
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		value.compile(writer, font)
 	
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
 		if value is None:
-			pass  # NULL table, ignore
+			if attrs:
+				# If there are attributes (probably index), then
+				# don't drop this even if it's NULL.  It will mess
+				# up the array indices of the containing element.
+				xmlWriter.simpletag(name, attrs + [("empty", True)])
+				xmlWriter.newline()
+			else:
+				pass # NULL table, ignore
 		else:
-			value.toXML(xmlWriter, font, attrs)
+			value.toXML(xmlWriter, font, attrs, name=name)
 	
 	def xmlRead(self, attrs, content, font):
 		table = self.tableClass()
+		if attrs.get("empty"):
+			return None
 		Format = attrs.get("Format")
 		if Format is not None:
 			table.Format = int(Format)
 		for element in content:
-			if type(element) == TupleType:
+			if isinstance(element, tuple):
 				name, attrs, content = element
-				table.fromXML((name, attrs, content), font)
+				table.fromXML(name, attrs, content, font)
 			else:
 				pass
 		return table
 
 
 class Table(Struct):
+
+	longOffset = False
+
+	def readOffset(self, reader):
+		return reader.readUShort()
+
+	def writeNullOffset(self, writer):
+		if self.longOffset:
+			writer.writeULong(0)
+		else:
+			writer.writeUShort(0)
 	
-	def read(self, reader, font, tableStack):
-		offset = reader.readUShort()
+	def read(self, reader, font, tableDict):
+		offset = self.readOffset(reader)
 		if offset == 0:
 			return None
 		if offset <= 3:
 			# XXX hack to work around buggy pala.ttf
-			print "*** Warning: offset is not 0, yet suspiciously low (%s). table: %s" \
-					% (offset, self.tableClass.__name__)
+			print("*** Warning: offset is not 0, yet suspiciously low (%s). table: %s" \
+					% (offset, self.tableClass.__name__))
 			return None
-		subReader = reader.getSubReader(offset)
 		table = self.tableClass()
-		table.decompile(subReader, font, tableStack)
+		reader = reader.getSubReader(offset)
+		if font.lazy:
+			table.reader = reader
+			table.font = font
+		else:
+			table.decompile(reader, font)
 		return table
 	
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		if value is None:
-			writer.writeUShort(0)
+			self.writeNullOffset(writer)
 		else:
 			subWriter = writer.getSubWriter()
+			subWriter.longOffset = self.longOffset
 			subWriter.name = self.name
 			if repeatIndex is not None:
 				subWriter.repeatIndex = repeatIndex
-			value.preCompile()
 			writer.writeSubTable(subWriter)
-			value.compile(subWriter, font, tableStack)
+			value.compile(subWriter, font)
+
+class LTable(Table):
+
+	longOffset = True
+
+	def readOffset(self, reader):
+		return reader.readULong()
+
 
 class SubTable(Table):
 	def getConverter(self, tableType, lookupType):
-		lookupTypes = self.lookupTypes[tableType]
-		tableClass = lookupTypes[lookupType]
-		return SubTable(self.name, self.repeat, self.repeatOffset, tableClass)
+		tableClass = self.lookupTypes[tableType][lookupType]
+		return self.__class__(self.name, self.repeat, self.aux, tableClass)
 
 
-class ExtSubTable(Table):
-	def getConverter(self, tableType, lookupType):
-		lookupTypes = self.lookupTypes[tableType]
-		tableClass = lookupTypes[lookupType]
-		return ExtSubTable(self.name, self.repeat, self.repeatOffset, tableClass)
+class ExtSubTable(LTable, SubTable):
 	
-	def read(self, reader, font, tableStack):
-		offset = reader.readULong()
-		if offset == 0:
-			return None
-		subReader = reader.getSubReader(offset)
-		table = self.tableClass()
-		table.reader = subReader
-		table.font = font
-		table.compileStatus = 1
-		table.start = table.reader.offset
-		return table
-	
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.Extension = 1 # actually, mere presence of the field flags it as an Ext Subtable writer.
-		if value is None:
-			writer.writeULong(0)
-		else:
-			# If the subtable has not yet been decompiled, we need to do so.
-			if  value.compileStatus == 1:
-				value.decompile(value.reader, value.font, tableStack)
-			subWriter = writer.getSubWriter()
-			subWriter.name = self.name
-			writer.writeSubTable(subWriter)
-			# If the subtable has been sorted and we can just write the original
-			# data, then do so.
-			if value.compileStatus == 3:
-				data = value.reader.data[value.start:value.end]
-				subWriter.writeData(data)
-			else:
-				value.compile(subWriter, font, tableStack)
+		Table.write(self, writer, font, tableDict, value, repeatIndex)
+
+class FeatureParams(Table):
+	def getConverter(self, featureTag):
+		tableClass = self.featureParamTypes.get(featureTag, self.defaultFeatureParams)
+		return self.__class__(self.name, self.repeat, self.aux, tableClass)
 
 
 class ValueFormat(IntValue):
-	def __init__(self, name, repeat, repeatOffset, tableClass):
-		BaseConverter.__init__(self, name, repeat, repeatOffset, tableClass)
-		self.which = name[-1] == "2"
-	def read(self, reader, font, tableStack):
+	def __init__(self, name, repeat, aux, tableClass):
+		BaseConverter.__init__(self, name, repeat, aux, tableClass)
+		self.which = "ValueFormat" + ("2" if name[-1] == "2" else "1")
+	def read(self, reader, font, tableDict):
 		format = reader.readUShort()
-		reader.setValueFormat(format, self.which)
+		reader[self.which] = ValueRecordFactory(format)
 		return format
-	def write(self, writer, font, tableStack, format, repeatIndex=None):
+	def write(self, writer, font, tableDict, format, repeatIndex=None):
 		writer.writeUShort(format)
-		writer.setValueFormat(format, self.which)
+		writer[self.which] = ValueRecordFactory(format)
 
 
 class ValueRecord(ValueFormat):
-	def read(self, reader, font, tableStack):
-		return reader.readValueRecord(font, self.which)
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
-		writer.writeValueRecord(value, font, self.which)
+	def read(self, reader, font, tableDict):
+		return reader[self.which].readValueRecord(reader, font)
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		writer[self.which].writeValueRecord(writer, font, value)
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
 		if value is None:
 			pass  # NULL table, ignore
 		else:
 			value.toXML(xmlWriter, font, self.name, attrs)
 	def xmlRead(self, attrs, content, font):
-		from otBase import ValueRecord
+		from .otBase import ValueRecord
 		value = ValueRecord()
-		value.fromXML((None, attrs, content), font)
+		value.fromXML(None, attrs, content, font)
 		return value
 
 
 class DeltaValue(BaseConverter):
 	
-	def read(self, reader, font, tableStack):
-		table = tableStack.getTop()
-		StartSize = table["StartSize"]
-		EndSize = table["EndSize"]
-		DeltaFormat = table["DeltaFormat"]
+	def read(self, reader, font, tableDict):
+		StartSize = tableDict["StartSize"]
+		EndSize = tableDict["EndSize"]
+		DeltaFormat = tableDict["DeltaFormat"]
 		assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat"
 		nItems = EndSize - StartSize + 1
 		nBits = 1 << DeltaFormat
@@ -285,12 +338,11 @@
 			DeltaValue.append(value)
 		return DeltaValue
 	
-	def write(self, writer, font, tableStack, value, repeatIndex=None):
-		table = tableStack.getTop()
-		StartSize = table["StartSize"]
-		EndSize = table["EndSize"]
-		DeltaFormat = table["DeltaFormat"]
-		DeltaValue = table["DeltaValue"]
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		StartSize = tableDict["StartSize"]
+		EndSize = tableDict["EndSize"]
+		DeltaFormat = tableDict["DeltaFormat"]
+		DeltaValue = value
 		assert DeltaFormat in (1, 2, 3), "illegal DeltaFormat"
 		nItems = EndSize - StartSize + 1
 		nBits = 1 << DeltaFormat
@@ -304,11 +356,10 @@
 			if shift == 0:
 				writer.writeUShort(tmp)
 				tmp, shift = 0, 16
-		if shift <> 16:
+		if shift != 16:
 			writer.writeUShort(tmp)
 	
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
-		# XXX this could do with a nicer format
 		xmlWriter.simpletag(name, attrs + [("value", value)])
 		xmlWriter.newline()
 	
@@ -320,17 +371,15 @@
 	# type         class
 	"int16":       Short,
 	"uint16":      UShort,
-	"ULONG":       Long,
-	"Fixed":       Fixed,
+	"uint24":      UInt24,
+	"Version":     Version,
 	"Tag":         Tag,
 	"GlyphID":     GlyphID,
+	"DeciPoints":  DeciPoints,
 	"struct":      Struct,
 	"Offset":      Table,
-	"LOffset":     ExtSubTable,
+	"LOffset":     LTable,
 	"ValueRecord": ValueRecord,
+	"DeltaValue":  DeltaValue,
 }
 
-# equivalents:
-converterMapping["USHORT"] = converterMapping["uint16"]
-converterMapping["fixed32"] = converterMapping["Fixed"]
-
diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py
index 89358e6..10046d8 100644
--- a/Lib/fontTools/ttLib/tables/otData.py
+++ b/Lib/fontTools/ttLib/tables/otData.py
@@ -1,7 +1,7 @@
 otData = [
 
 	#
-	# common (generated from chapter2.htm)
+	# common
 	#
 
 	('ScriptList', [
@@ -48,6 +48,33 @@
 		('uint16', 'LookupListIndex', 'LookupCount', 0, 'Array of LookupList indices for this feature -zero-based (first lookup is LookupListIndex = 0)'),
 	]),
 
+	('FeatureParams', [
+	]),
+
+	('FeatureParamsSize', [
+		('DeciPoints', 'DesignSize', None, None, 'The design size in 720/inch units (decipoints).'),
+		('uint16', 'SubfamilyID', None, None, 'Serves as an identifier that associates fonts in a subfamily.'),
+		('uint16', 'SubfamilyNameID', None, None, 'Subfamily NameID.'),
+		('DeciPoints', 'RangeStart', None, None, 'Small end of recommended usage range (exclusive) in 720/inch units.'),
+		('DeciPoints', 'RangeEnd', None, None, 'Large end of recommended usage range (inclusive) in 720/inch units.'),
+	]),
+
+	('FeatureParamsStylisticSet', [
+		('uint16', 'Version', None, None, 'Set to 0.'),
+		('uint16', 'UINameID', None, None, 'UI NameID.'),
+	]),
+
+	('FeatureParamsCharacterVariants', [
+		('uint16', 'Format', None, None, 'Set to 0.'),
+		('uint16', 'FeatUILabelNameID', None, None, 'Feature UI label NameID.'),
+		('uint16', 'FeatUITooltipTextNameID', None, None, 'Feature UI tooltip text NameID.'),
+		('uint16', 'SampleTextNameID', None, None, 'Sample text NameID.'),
+		('uint16', 'NumNamedParameters', None, None, 'Number of named parameters.'),
+		('uint16', 'FirstParamUILabelNameID', None, None, 'First NameID of UI feature parameters.'),
+		('uint16', 'CharCount', None, None, 'Count of characters this feature provides glyph variants for.'),
+		('uint24', 'Character', 'CharCount', 0, 'Unicode characters for which this feature provides glyph variants.'),
+	]),
+
 	('LookupList', [
 		('uint16', 'LookupCount', None, None, 'Number of lookups in this table'),
 		('Offset', 'Lookup', 'LookupCount', 0, 'Array of offsets to Lookup tables-from beginning of LookupList -zero based (first lookup is Lookup index = 0)'),
@@ -58,6 +85,7 @@
 		('uint16', 'LookupFlag', None, None, 'Lookup qualifiers'),
 		('uint16', 'SubTableCount', None, None, 'Number of SubTables for this lookup'),
 		('Offset', 'SubTable', 'SubTableCount', 0, 'Array of offsets to SubTables-from beginning of Lookup table'),
+		('uint16', 'MarkFilteringSet', None, 'LookupFlag & 0x0010', 'If set, indicates that the lookup table structure is followed by a MarkFilteringSet field. The layout engine skips over all mark glyphs not in the mark filtering set indicated.'),
 	]),
 
 	('CoverageFormat1', [
@@ -101,16 +129,16 @@
 		('uint16', 'StartSize', None, None, 'Smallest size to correct-in ppem'),
 		('uint16', 'EndSize', None, None, 'Largest size to correct-in ppem'),
 		('uint16', 'DeltaFormat', None, None, 'Format of DeltaValue array data: 1, 2, or 3'),
-		('uint16', 'DeltaValue', '', 0, 'Array of compressed data'),
+		('DeltaValue', 'DeltaValue', '', 0, 'Array of compressed data'),
 	]),
 
 
 	#
-	# gpos (generated from gpos.htm)
+	# gpos
 	#
 
 	('GPOS', [
-		('Fixed', 'Version', None, None, 'Version of the GPOS table-initially = 0x00010000'),
+		('Version', 'Version', None, None, 'Version of the GPOS table-initially = 0x00010000'),
 		('Offset', 'ScriptList', None, None, 'Offset to ScriptList table-from beginning of GPOS table'),
 		('Offset', 'FeatureList', None, None, 'Offset to FeatureList table-from beginning of GPOS table'),
 		('Offset', 'LookupList', None, None, 'Offset to LookupList table-from beginning of GPOS table'),
@@ -357,9 +385,9 @@
 	]),
 
 	('ExtensionPosFormat1', [
-		('USHORT', 'ExtFormat', None, None, 'Format identifier. Set to 1.'),
-		('USHORT', 'ExtensionLookupType', None, None, 'Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).'),
-		('LOffset', 'ExtSubTable', None, None, 'Array of offsets to Lookup tables-from beginning of LookupList -zero based (first lookup is Lookup index = 0)'),
+		('uint16', 'ExtFormat', None, None, 'Format identifier. Set to 1.'),
+		('uint16', 'ExtensionLookupType', None, None, 'Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).'),
+		('LOffset', 'ExtSubTable', None, None, 'Offset to SubTable'),
 	]),
 
 	('ValueRecord', [
@@ -406,11 +434,11 @@
 
 
 	#
-	# gsub (generated from gsub.htm)
+	# gsub
 	#
 
 	('GSUB', [
-		('Fixed', 'Version', None, None, 'Version of the GSUB table-initially set to 0x00010000'),
+		('Version', 'Version', None, None, 'Version of the GSUB table-initially set to 0x00010000'),
 		('Offset', 'ScriptList', None, None, 'Offset to ScriptList table-from beginning of GSUB table'),
 		('Offset', 'FeatureList', None, None, 'Offset to FeatureList table-from beginning of GSUB table'),
 		('Offset', 'LookupList', None, None, 'Offset to LookupList table-from beginning of GSUB table'),
@@ -585,8 +613,8 @@
 	]),
 
 	('ExtensionSubstFormat1', [
-		('USHORT', 'ExtFormat', None, None, 'Format identifier. Set to 1.'),
-		('USHORT', 'ExtensionLookupType', None, None, 'Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).'),
+		('uint16', 'ExtFormat', None, None, 'Format identifier. Set to 1.'),
+		('uint16', 'ExtensionLookupType', None, None, 'Lookup type of subtable referenced by ExtensionOffset (i.e. the extension subtable).'),
 		('LOffset', 'ExtSubTable', None, None, 'Array of offsets to Lookup tables-from beginning of LookupList -zero based (first lookup is Lookup index = 0)'),
 	]),
 
@@ -602,15 +630,16 @@
 	]),
 
 	#
-	# gdef (generated from gdef.htm)
+	# gdef
 	#
 
 	('GDEF', [
-		('Fixed', 'Version', None, None, 'Version of the GDEF table-initially 0x00010000'),
+		('Version', 'Version', None, None, 'Version of the GDEF table-initially 0x00010000'),
 		('Offset', 'GlyphClassDef', None, None, 'Offset to class definition table for glyph type-from beginning of GDEF header (may be NULL)'),
 		('Offset', 'AttachList', None, None, 'Offset to list of glyphs with attachment points-from beginning of GDEF header (may be NULL)'),
 		('Offset', 'LigCaretList', None, None, 'Offset to list of positioning points for ligature carets-from beginning of GDEF header (may be NULL)'),
 		('Offset', 'MarkAttachClassDef', None, None, 'Offset to class definition table for mark attachment type-from beginning of GDEF header (may be NULL)'),
+		('Offset', 'MarkGlyphSetsDef', None, 'int(round(Version*0x10000)) >= 0x00010002', 'Offset to the table of mark set definitions-from beginning of GDEF header (may be NULL)'),
 	]),
 
 	('AttachList', [
@@ -651,13 +680,18 @@
 		('Offset', 'DeviceTable', None, None, 'Offset to Device table for X or Y value-from beginning of CaretValue table'),
 	]),
 
+	('MarkGlyphSetsDef', [
+		('uint16', 'MarkSetTableFormat', None, None, 'Format identifier == 1'),
+		('uint16', 'MarkSetCount', None, None, 'Number of mark sets defined'),
+		('LOffset', 'Coverage', 'MarkSetCount', 0, 'Array of offsets to mark set coverage tables.'),
+	]),
 
 	#
-	# base (generated from base.htm)
+	# base
 	#
 
 	('BASE', [
-		('fixed32', 'Version', None, None, 'Version of the BASE table-initially 0x00010000'),
+		('Version', 'Version', None, None, 'Version of the BASE table-initially 0x00010000'),
 		('Offset', 'HorizAxis', None, None, 'Offset to horizontal Axis table-from beginning of BASE table-may be NULL'),
 		('Offset', 'VertAxis', None, None, 'Offset to vertical Axis table-from beginning of BASE table-may be NULL'),
 	]),
@@ -733,11 +767,11 @@
 
 
 	#
-	# jstf (generated from jstf.htm)
+	# jstf
 	#
 
 	('JSTF', [
-		('fixed32', 'Version', None, None, 'Version of the JSTF table-initially set to 0x00010000'),
+		('Version', 'Version', None, None, 'Version of the JSTF table-initially set to 0x00010000'),
 		('uint16', 'JstfScriptCount', None, None, 'Number of JstfScriptRecords in this table'),
 		('struct', 'JstfScriptRecord', 'JstfScriptCount', 0, 'Array of JstfScriptRecords-in alphabetical order, by JstfScriptTag'),
 	]),
@@ -797,5 +831,152 @@
 		('Offset', 'Lookup', 'LookupCount', 0, 'Array of offsets to GPOS-type lookup tables-from beginning of JstfMax table-in design order'),
 	]),
 
-]
+	#
+	# math
+	#
 
+	('MATH', [
+		('Version', 'Version', None, None, 'Version of the MATH table-initially set to 0x00010000.'),
+		('Offset', 'MathConstants', None, None, 'Offset to MathConstants table - from the beginning of MATH table.'),
+		('Offset', 'MathGlyphInfo', None, None, 'Offset to MathGlyphInfo table - from the beginning of MATH table.'),
+		('Offset', 'MathVariants', None, None, 'Offset to MathVariants table - from the beginning of MATH table.'),
+	]),
+
+	('MathValueRecord', [
+		('int16', 'Value', None, None, 'The X or Y value in design units.'),
+		('Offset', 'DeviceTable', None, None, 'Offset to the device table - from the beginning of parent table. May be NULL. Suggested format for device table is 1.'),
+	]),
+
+	('MathConstants', [
+		('int16', 'ScriptPercentScaleDown', None, None, 'Percentage of scaling down for script level 1. Suggested value: 80%.'),
+		('int16', 'ScriptScriptPercentScaleDown', None, None, 'Percentage of scaling down for script level 2 (ScriptScript). Suggested value: 60%.'),
+		('uint16', 'DelimitedSubFormulaMinHeight', None, None, 'Minimum height required for a delimited expression to be treated as a subformula. Suggested value: normal line height x1.5.'),
+		('uint16', 'DisplayOperatorMinHeight', None, None, 'Minimum height of n-ary operators (such as integral and summation) for formulas in display mode.'),
+		('MathValueRecord', 'MathLeading', None, None, 'White space to be left between math formulas to ensure proper line spacing. For example, for applications that treat line gap as a part of line ascender, formulas with ink  going above (os2.sTypoAscender + os2.sTypoLineGap - MathLeading) or with ink going below os2.sTypoDescender will result in increasing line height.'),
+		('MathValueRecord', 'AxisHeight', None, None, 'Axis height of the font.'),
+		('MathValueRecord', 'AccentBaseHeight', None, None, 'Maximum (ink) height of accent base that does not require raising the accents. Suggested: x-height of the font (os2.sxHeight) plus any possible overshots.'),
+		('MathValueRecord', 'FlattenedAccentBaseHeight', None, None, 'Maximum (ink) height of accent base that does not require flattening the accents. Suggested: cap height of the font (os2.sCapHeight).'),
+		('MathValueRecord', 'SubscriptShiftDown', None, None, 'The standard shift down applied to subscript elements. Positive for moving in the downward direction. Suggested: os2.ySubscriptYOffset.'),
+		('MathValueRecord', 'SubscriptTopMax', None, None, 'Maximum allowed height of the (ink) top of subscripts that does not require moving subscripts further down. Suggested: 4/5 x-height.'),
+		('MathValueRecord', 'SubscriptBaselineDropMin', None, None, 'Minimum allowed drop of the baseline of subscripts relative to the (ink) bottom of the base. Checked for bases that are treated as a box or extended shape. Positive for subscript baseline dropped below the base bottom.'),
+		('MathValueRecord', 'SuperscriptShiftUp', None, None, 'Standard shift up applied to superscript elements. Suggested: os2.ySuperscriptYOffset.'),
+		('MathValueRecord', 'SuperscriptShiftUpCramped', None, None, 'Standard shift of superscripts relative to the base, in cramped style.'),
+		('MathValueRecord', 'SuperscriptBottomMin', None, None, 'Minimum allowed height of the (ink) bottom of superscripts that does not require moving subscripts further up. Suggested: 1/4 x-height.'),
+		('MathValueRecord', 'SuperscriptBaselineDropMax', None, None, 'Maximum allowed drop of the baseline of superscripts relative to the (ink) top of the base. Checked for bases that are treated as a box or extended shape. Positive for superscript baseline below the base top.'),
+		('MathValueRecord', 'SubSuperscriptGapMin', None, None, 'Minimum gap between the superscript and subscript ink. Suggested: 4x default rule thickness.'),
+		('MathValueRecord', 'SuperscriptBottomMaxWithSubscript', None, None, 'The maximum level to which the (ink) bottom of superscript can be pushed to increase the gap between superscript and subscript, before subscript starts being moved down. Suggested: 4/5 x-height.'),
+		('MathValueRecord', 'SpaceAfterScript', None, None, 'Extra white space to be added after each subscript and superscript. Suggested: 0.5pt for a 12 pt font.'),
+		('MathValueRecord', 'UpperLimitGapMin', None, None, 'Minimum gap between the (ink) bottom of the upper limit, and the (ink) top of the base operator.'),
+		('MathValueRecord', 'UpperLimitBaselineRiseMin', None, None, 'Minimum distance between baseline of upper limit and (ink) top of the base operator.'),
+		('MathValueRecord', 'LowerLimitGapMin', None, None, 'Minimum gap between (ink) top of the lower limit, and (ink) bottom of the base operator.'),
+		('MathValueRecord', 'LowerLimitBaselineDropMin', None, None, 'Minimum distance between baseline of the lower limit and (ink) bottom of the base operator.'),
+		('MathValueRecord', 'StackTopShiftUp', None, None, 'Standard shift up applied to the top element of a stack.'),
+		('MathValueRecord', 'StackTopDisplayStyleShiftUp', None, None, 'Standard shift up applied to the top element of a stack in display style.'),
+		('MathValueRecord', 'StackBottomShiftDown', None, None, 'Standard shift down applied to the bottom element of a stack. Positive for moving in the downward direction.'),
+		('MathValueRecord', 'StackBottomDisplayStyleShiftDown', None, None, 'Standard shift down applied to the bottom element of a stack in display style. Positive for moving in the downward direction.'),
+		('MathValueRecord', 'StackGapMin', None, None, 'Minimum gap between (ink) bottom of the top element of a stack, and the (ink) top of the bottom element. Suggested: 3x default rule thickness.'),
+		('MathValueRecord', 'StackDisplayStyleGapMin', None, None, 'Minimum gap between (ink) bottom of the top element of a stack, and the (ink) top of the bottom element in display style. Suggested: 7x default rule thickness.'),
+		('MathValueRecord', 'StretchStackTopShiftUp', None, None, 'Standard shift up applied to the top element of the stretch stack.'),
+		('MathValueRecord', 'StretchStackBottomShiftDown', None, None, 'Standard shift down applied to the bottom element of the stretch stack. Positive for moving in the downward direction.'),
+		('MathValueRecord', 'StretchStackGapAboveMin', None, None, 'Minimum gap between the ink of the stretched element, and the (ink) bottom of the element above. Suggested: UpperLimitGapMin'),
+		('MathValueRecord', 'StretchStackGapBelowMin', None, None, 'Minimum gap between the ink of the stretched element, and the (ink) top of the element below. Suggested: LowerLimitGapMin.'),
+		('MathValueRecord', 'FractionNumeratorShiftUp', None, None, 'Standard shift up applied to the numerator.'),
+		('MathValueRecord', 'FractionNumeratorDisplayStyleShiftUp', None, None, 'Standard shift up applied to the numerator in display style. Suggested: StackTopDisplayStyleShiftUp.'),
+		('MathValueRecord', 'FractionDenominatorShiftDown', None, None, 'Standard shift down applied to the denominator. Positive for moving in the downward direction.'),
+		('MathValueRecord', 'FractionDenominatorDisplayStyleShiftDown', None, None, 'Standard shift down applied to the denominator in display style. Positive for moving in the downward direction. Suggested: StackBottomDisplayStyleShiftDown.'),
+		('MathValueRecord', 'FractionNumeratorGapMin', None, None, 'Minimum tolerated gap between the (ink) bottom of the numerator and the ink of the fraction bar. Suggested: default rule thickness'),
+		('MathValueRecord', 'FractionNumDisplayStyleGapMin', None, None, 'Minimum tolerated gap between the (ink) bottom of the numerator and the ink of the fraction bar in display style. Suggested: 3x default rule thickness.'),
+		('MathValueRecord', 'FractionRuleThickness', None, None, 'Thickness of the fraction bar. Suggested: default rule thickness.'),
+		('MathValueRecord', 'FractionDenominatorGapMin', None, None, 'Minimum tolerated gap between the (ink) top of the denominator and the ink of the fraction bar. Suggested: default rule thickness'),
+		('MathValueRecord', 'FractionDenomDisplayStyleGapMin', None, None, 'Minimum tolerated gap between the (ink) top of the denominator and the ink of the fraction bar in display style. Suggested: 3x default rule thickness.'),
+		('MathValueRecord', 'SkewedFractionHorizontalGap', None, None, 'Horizontal distance between the top and bottom elements of a skewed fraction.'),
+		('MathValueRecord', 'SkewedFractionVerticalGap', None, None, 'Vertical distance between the ink of the top and bottom elements of a skewed fraction.'),
+		('MathValueRecord', 'OverbarVerticalGap', None, None, 'Distance between the overbar and the (ink) top of he base. Suggested: 3x default rule thickness.'),
+		('MathValueRecord', 'OverbarRuleThickness', None, None, 'Thickness of overbar. Suggested: default rule thickness.'),
+		('MathValueRecord', 'OverbarExtraAscender', None, None, 'Extra white space reserved above the overbar. Suggested: default rule thickness.'),
+		('MathValueRecord', 'UnderbarVerticalGap', None, None, 'Distance between underbar and (ink) bottom of the base. Suggested: 3x default rule thickness.'),
+		('MathValueRecord', 'UnderbarRuleThickness', None, None, 'Thickness of underbar. Suggested: default rule thickness.'),
+		('MathValueRecord', 'UnderbarExtraDescender', None, None, 'Extra white space reserved below the underbar. Always positive. Suggested: default rule thickness.'),
+		('MathValueRecord', 'RadicalVerticalGap', None, None, 'Space between the (ink) top of the expression and the bar over it. Suggested: 1 1/4 default rule thickness.'),
+		('MathValueRecord', 'RadicalDisplayStyleVerticalGap', None, None, 'Space between the (ink) top of the expression and the bar over it. Suggested: default rule thickness + 1/4 x-height.'),
+		('MathValueRecord', 'RadicalRuleThickness', None, None, 'Thickness of the radical rule. This is the thickness of the rule in designed or constructed radical signs. Suggested: default rule thickness.'),
+		('MathValueRecord', 'RadicalExtraAscender', None, None, 'Extra white space reserved above the radical. Suggested: RadicalRuleThickness.'),
+		('MathValueRecord', 'RadicalKernBeforeDegree', None, None, 'Extra horizontal kern before the degree of a radical, if such is present. Suggested: 5/18 of em.'),
+		('MathValueRecord', 'RadicalKernAfterDegree', None, None, 'Negative kern after the degree of a radical, if such is present. Suggested: 10/18 of em.'),
+		('uint16', 'RadicalDegreeBottomRaisePercent', None, None, 'Height of the bottom of the radical degree, if such is present, in proportion to the ascender of the radical sign. Suggested: 60%.'),
+	]),
+
+	('MathGlyphInfo', [
+		('Offset', 'MathItalicsCorrectionInfo', None, None, 'Offset to MathItalicsCorrectionInfo table - from the beginning of MathGlyphInfo table.'),
+		('Offset', 'MathTopAccentAttachment', None, None, 'Offset to MathTopAccentAttachment table - from the beginning of MathGlyphInfo table.'),
+		('Offset', 'ExtendedShapeCoverage', None, None, 'Offset to coverage table for Extended Shape glyphs - from the  beginning of MathGlyphInfo table. When the left or right glyph of a box is an extended shape variant, the (ink) box (and not the default position defined by values in MathConstants table) should be used for vertical positioning purposes. May be NULL.'),
+		('Offset', 'MathKernInfo', None, None, 'Offset to MathKernInfo table - from the beginning of MathGlyphInfo table.'),
+	]),
+
+	('MathItalicsCorrectionInfo', [
+		('Offset', 'Coverage', None, None, 'Offset to Coverage table - from the beginning of MathItalicsCorrectionInfo table.'),
+		('uint16', 'ItalicsCorrectionCount', None, None, 'Number of italics correction values. Should coincide with the number of covered glyphs.'),
+		('MathValueRecord', 'ItalicsCorrection', 'ItalicsCorrectionCount', 0, 'Array of MathValueRecords defining italics correction values for each covered glyph.'),
+	]),
+
+	('MathTopAccentAttachment', [
+		('Offset', 'TopAccentCoverage', None, None, 'Offset to Coverage table - from the beginning of  MathTopAccentAttachment table.'),
+		('uint16', 'TopAccentAttachmentCount', None, None, 'Number of top accent attachment point values. Should coincide with the number of covered glyphs'),
+		('MathValueRecord', 'TopAccentAttachment', 'TopAccentAttachmentCount', 0, 'Array of MathValueRecords defining top accent attachment points for each covered glyph'),
+	]),
+
+	('MathKernInfo', [
+		('Offset', 'MathKernCoverage', None, None, 'Offset to Coverage table - from the beginning of the MathKernInfo table.'),
+		('uint16', 'MathKernCount', None, None, 'Number of MathKernInfoRecords.'),
+		('MathKernInfoRecord', 'MathKernInfoRecords', 'MathKernCount', 0, 'Array of MathKernInfoRecords, per-glyph information for mathematical positioning of subscripts and superscripts.'),
+	]),
+
+	('MathKernInfoRecord', [
+		('Offset', 'TopRightMathKern', None, None, 'Offset to MathKern table for top right corner - from the beginning of MathKernInfo table. May be NULL.'),
+		('Offset', 'TopLeftMathKern', None, None, 'Offset to MathKern table for the top left corner - from the beginning of MathKernInfo table. May be NULL.'),
+		('Offset', 'BottomRightMathKern', None, None, 'Offset to MathKern table for bottom right corner - from the beginning of MathKernInfo table. May be NULL.'),
+		('Offset', 'BottomLeftMathKern', None, None, 'Offset to MathKern table for bottom left corner - from the beginning of MathKernInfo table. May be NULL.'),
+	]),
+
+	('MathKern', [
+		('uint16', 'HeightCount', None, None, 'Number of heights on which the kern value changes.'),
+		('MathValueRecord', 'CorrectionHeight', 'HeightCount', 0, 'Array of correction heights at which the kern value changes. Sorted by the height value in design units.'),
+		('MathValueRecord', 'KernValue', 'HeightCount', 1, 'Array of kern values corresponding to heights. First value is the kern value for all heights less or equal than the first height in this table.Last value is the value to be applied for all heights greater than the last height in this table. Negative values are interpreted as move glyphs closer to each other.'),
+	]),
+
+	('MathVariants', [
+		('uint16', 'MinConnectorOverlap', None, None, 'Minimum overlap of connecting glyphs during glyph construction,  in design units.'),
+		('Offset', 'VertGlyphCoverage', None, None, 'Offset to Coverage table - from the beginning of MathVariants table.'),
+		('Offset', 'HorizGlyphCoverage', None, None, 'Offset to Coverage table - from the beginning of MathVariants table.'),
+		('uint16', 'VertGlyphCount', None, None, 'Number of glyphs for which information is provided for vertically growing variants.'),
+		('uint16', 'HorizGlyphCount', None, None, 'Number of glyphs for which information is provided for horizontally growing variants.'),
+		('Offset', 'VertGlyphConstruction', 'VertGlyphCount', 0, 'Array of offsets to MathGlyphConstruction tables - from the beginning of the MathVariants table, for shapes growing in vertical direction.'),
+		('Offset', 'HorizGlyphConstruction', 'HorizGlyphCount', 0, 'Array of offsets to MathGlyphConstruction tables - from the beginning of the MathVariants table, for shapes growing in horizontal direction.'),
+	]),
+
+	('MathGlyphConstruction', [
+		('Offset', 'GlyphAssembly', None, None, 'Offset to GlyphAssembly table for this shape - from the beginning of MathGlyphConstruction table. May be NULL'),
+		('uint16', 'VariantCount', None, None, 'Count of glyph growing variants for this glyph.'),
+		('MathGlyphVariantRecord', 'MathGlyphVariantRecord', 'VariantCount', 0, 'MathGlyphVariantRecords for alternative variants of the glyphs.'),
+	]),
+
+	('MathGlyphVariantRecord', [
+		('GlyphID', 'VariantGlyph', None, None, 'Glyph ID for the variant.'),
+		('uint16', 'AdvanceMeasurement', None, None, 'Advance width/height, in design units, of the variant, in the direction of requested glyph extension.'),
+	]),
+
+	('GlyphAssembly', [
+		('MathValueRecord', 'ItalicsCorrection', None, None, 'Italics correction of this GlyphAssembly. Should not depend on the assembly size.'),
+		('uint16', 'PartCount', None, None, 'Number of parts in this assembly.'),
+		('GlyphPartRecord', 'PartRecords', 'PartCount', 0, 'Array of part records, from left to right and bottom to top.'),
+	]),
+
+	('GlyphPartRecord', [
+		('GlyphID', 'glyph', None, None, 'Glyph ID for the part.'),
+		('uint16', 'StartConnectorLength', None, None, 'Advance width/ height of the straight bar connector material, in design units, is at the beginning of the glyph, in the direction of the extension.'),
+		('uint16', 'EndConnectorLength', None, None, 'Advance width/ height of the straight bar connector material, in design units, is at the end of the glyph, in the direction of the extension.'),
+		('uint16', 'FullAdvance', None, None, 'Full advance width/height for this part, in the direction of the extension. In design units.'),
+		('uint16', 'PartFlags', None, None, 'Part qualifiers. PartFlags enumeration currently uses only one bit: 0x0001 fExtender: If set, the part can be skipped or repeated. 0xFFFE Reserved'),
+	]),
+
+]
diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py
index bd02393..5d3d08b 100644
--- a/Lib/fontTools/ttLib/tables/otTables.py
+++ b/Lib/fontTools/ttLib/tables/otTables.py
@@ -4,24 +4,33 @@
 Most are constructed upon import from data in otData.py, all are populated with
 converter objects from otConverters.py.
 """
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from .otBase import BaseTable, FormatSwitchingBaseTable
 import operator
-from otBase import BaseTable, FormatSwitchingBaseTable
-from types import TupleType
+import warnings
 
 
 class LookupOrder(BaseTable):
 	"""Dummy class; this table isn't defined, but is used, and is always NULL."""
 
-
 class FeatureParams(BaseTable):
-	"""This class has been used by Adobe, but but this one implementation was done wrong.
-	No other use has been made, becuase there is no way to know how to interpret
-	the data at the offset.. For now, if we see one, just skip the data on
-	decompiling and dumping to XML. """
-	# XXX The above is no longer true; the 'size' feature uses FeatureParams now.
-	def __init__(self):
-		BaseTable.__init__(self)
-		self.converters = []
+
+	def compile(self, writer, font):
+		assert featureParamTypes.get(writer['FeatureTag']) == self.__class__, "Wrong FeatureParams type for feature '%s': %s" % (writer['FeatureTag'], self.__class__.__name__)
+		BaseTable.compile(self, writer, font)
+
+	def toXML(self, xmlWriter, font, attrs=None, name=None):
+		BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
+
+class FeatureParamsSize(FeatureParams):
+	pass
+
+class FeatureParamsStylisticSet(FeatureParams):
+	pass
+
+class FeatureParamsCharacterVariants(FeatureParams):
+	pass
 
 class Coverage(FormatSwitchingBaseTable):
 	
@@ -29,25 +38,43 @@
 	
 	def postRead(self, rawTable, font):
 		if self.Format == 1:
+			# TODO only allow glyphs that are valid?
 			self.glyphs = rawTable["GlyphArray"]
 		elif self.Format == 2:
 			glyphs = self.glyphs = []
 			ranges = rawTable["RangeRecord"]
 			glyphOrder = font.getGlyphOrder()
+			# Some SIL fonts have coverage entries that don't have sorted
+			# StartCoverageIndex.  If it is so, fixup and warn.  We undo
+			# this when writing font out.
+			sorted_ranges = sorted(ranges, key=lambda a: a.StartCoverageIndex)
+			if ranges != sorted_ranges:
+				warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
+				ranges = sorted_ranges
+			del sorted_ranges
 			for r in ranges:
 				assert r.StartCoverageIndex == len(glyphs), \
 					(r.StartCoverageIndex, len(glyphs))
 				start = r.Start
 				end = r.End
-				startID = font.getGlyphID(start)
-				endID = font.getGlyphID(end)
-				glyphs.append(start)
-				rangeList = [glyphOrder[glyphID] for glyphID in range(startID + 1, endID) ]
-				glyphs += rangeList
-				if start != end:
-					glyphs.append(end)
+				try:
+					startID = font.getGlyphID(start, requireReal=True)
+				except KeyError:
+					warnings.warn("Coverage table has start glyph ID out of range: %s." % start)
+					continue
+				try:
+					endID = font.getGlyphID(end, requireReal=True) + 1
+				except KeyError:
+					# Apparently some tools use 65535 to "match all" the range
+					if end != 'glyph65535':
+						warnings.warn("Coverage table has end glyph ID out of range: %s." % end)
+					# NOTE: We clobber out-of-range things here.  There are legit uses for those,
+					# but none that we have seen in the wild.
+					endID = len(glyphOrder)
+				glyphs.extend(glyphOrder[glyphID] for glyphID in range(startID, endID))
 		else:
 			assert 0, "unknown format: %s" % self.Format
+		del self.Format # Don't need this anymore
 	
 	def preWrite(self, font):
 		glyphs = getattr(self, "glyphs", None)
@@ -59,6 +86,7 @@
 		if glyphs:
 			# find out whether Format 2 is more compact or not
 			glyphIDs = [getGlyphID(glyphName) for glyphName in glyphs ]
+			brokenOrder = sorted(glyphIDs) != glyphIDs
 			
 			last = glyphIDs[0]
 			ranges = [[last]]
@@ -69,17 +97,23 @@
 				last = glyphID
 			ranges[-1].append(last)
 			
-			if len(ranges) * 3 < len(glyphs):  # 3 words vs. 1 word
+			if brokenOrder or len(ranges) * 3 < len(glyphs):  # 3 words vs. 1 word
 				# Format 2 is more compact
 				index = 0
 				for i in range(len(ranges)):
 					start, end = ranges[i]
 					r = RangeRecord()
+					r.StartID = start
 					r.Start = font.getGlyphName(start)
 					r.End = font.getGlyphName(end)
 					r.StartCoverageIndex = index
 					ranges[i] = r
 					index = index + end - start + 1
+				if brokenOrder:
+					warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
+					ranges.sort(key=lambda a: a.StartID)
+				for r in ranges:
+					del r.StartID
 				format = 2
 				rawTable = {"RangeRecord": ranges}
 			#else:
@@ -92,7 +126,7 @@
 			xmlWriter.simpletag("Glyph", value=glyphName)
 			xmlWriter.newline()
 	
-	def fromXML(self, (name, attrs, content), font):
+	def fromXML(self, name, attrs, content, font):
 		glyphs = getattr(self, "glyphs", None)
 		if glyphs is None:
 			glyphs = []
@@ -100,55 +134,6 @@
 		glyphs.append(attrs["value"])
 
 
-class LookupList(BaseTable):
-	def preCompile(self):
-		""" This function is used to optimize writing out extension subtables. This is useful
-		when a font has been read in, modified, and we are now writing out a new version. If the
-		the extension subtables have not been touched (proof being that they have not been decompiled)
-		then we can write them out using the original data, and do not have to recompile them. This can save
-		20-30% of the compile time for fonts with large extension tables, such as Japanese Pro fonts."""
-
-		if hasattr(self, 'LookupCount'): #not defined if loading from xml
-			lookupCount = self.LookupCount
-		else:
-			return # The optimization of not recompiling extension lookup subtables is not possible
-					# when reading from XML.
- 
-		liRange = range(lookupCount)
-		extTables = []
-		for li in liRange:
-			lookup = self.Lookup[li]
-			if hasattr(lookup, 'SubTableCount'): #not defined if loading from xml
-				subtableCount = lookup.SubTableCount
-			else:
-				subtableCount = len(lookup.SubTable)
-			siRange = range(subtableCount)
-			for si in siRange:
-				subtable = lookup.SubTable[si]
-				if hasattr(subtable, 'ExtSubTable'):
-					extTable = subtable.ExtSubTable
-					extTables.append([extTable.start, extTable] )
-
-		# Since offsets in one subtable can and do point forward into later
-		# subtables, we can afford to simply copy data only for the last subtables 
-		# which were not decompiled. So we start figuring out the
-		# data segments starting with the last subtTable, and work our way towards
-		# the first subtable, and then quit as soon as we see a subtable that was decompiled.
-		if  extTables:
-			extTables.sort()
-			extTables.reverse()
-			lastTable = extTables[0][1]
-			if lastTable.compileStatus == 1:
-				lastTable.end = len(lastTable.reader.data)
-				lastTable.compileStatus = 3
-				for i in range(1, len(extTables)):
-					extTable = extTables[i][1]
-					if extTable.compileStatus != 1:
-						break
-					extTable.end = lastTable.start
-					extTable.compileStatus = 3
-					lastTable = extTable
-
 def doModulo(value):
 	if value < 0:
 		return value + 65536
@@ -163,29 +148,28 @@
 		if self.Format == 1:
 			delta = rawTable["DeltaGlyphID"]
 			inputGIDS =  [ font.getGlyphID(name) for name in input ]
-			inputGIDS = map(doModulo, inputGIDS) 
 			outGIDS = [ glyphID + delta for glyphID in inputGIDS ]
-			outGIDS = map(doModulo, outGIDS) 
+			outGIDS = map(doModulo, outGIDS)
 			outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ]
-			map(operator.setitem, [mapping]*lenMapping, input, outNames)
+			list(map(operator.setitem, [mapping]*lenMapping, input, outNames))
 		elif self.Format == 2:
 			assert len(input) == rawTable["GlyphCount"], \
 					"invalid SingleSubstFormat2 table"
 			subst = rawTable["Substitute"]
-			map(operator.setitem, [mapping]*lenMapping, input, subst)
+			list(map(operator.setitem, [mapping]*lenMapping, input, subst))
 		else:
 			assert 0, "unknown format: %s" % self.Format
 		self.mapping = mapping
+		del self.Format # Don't need this anymore
 	
 	def preWrite(self, font):
 		mapping = getattr(self, "mapping", None)
 		if mapping is None:
 			mapping = self.mapping = {}
-		items = mapping.items()
+		items = list(mapping.items())
 		getGlyphID = font.getGlyphID
-		gidItems = [(getGlyphID(item[0]), getGlyphID(item[1])) for item in items]
-		sortableItems = zip(gidItems, items)
-		sortableItems.sort()
+		gidItems = [(getGlyphID(a), getGlyphID(b)) for a,b in items]
+		sortableItems = sorted(zip(gidItems, items))
 
 		# figure out format
 		format = 2
@@ -193,6 +177,10 @@
 		for inID, outID in gidItems:
 			if delta is None:
 				delta = outID - inID
+				if delta < -32768:
+					delta += 65536
+				elif delta > 32767:
+					delta -= 65536
 			else:
 				if delta != outID - inID:
 					break
@@ -214,14 +202,13 @@
 		return rawTable
 	
 	def toXML2(self, xmlWriter, font):
-		items = self.mapping.items()
-		items.sort()
+		items = sorted(self.mapping.items())
 		for inGlyph, outGlyph in items:
 			xmlWriter.simpletag("Substitution",
 					[("in", inGlyph), ("out", outGlyph)])
 			xmlWriter.newline()
 	
-	def fromXML(self, (name, attrs, content), font):
+	def fromXML(self, name, attrs, content, font):
 		mapping = getattr(self, "mapping", None)
 		if mapping is None:
 			mapping = {}
@@ -233,17 +220,25 @@
 	
 	def postRead(self, rawTable, font):
 		classDefs = {}
-		getGlyphName = font.getGlyphName
+		glyphOrder = font.getGlyphOrder()
 
 		if self.Format == 1:
 			start = rawTable["StartGlyph"]
 			classList = rawTable["ClassValueArray"]
-			lenList = len(classList)
-			glyphID = font.getGlyphID(start)
-			gidList = range(glyphID, glyphID + len(classList))
-			keyList = [getGlyphName(glyphID) for glyphID in gidList]
+			try:
+				startID = font.getGlyphID(start, requireReal=True)
+			except KeyError:
+				warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
+				startID = len(glyphOrder)
+			endID = startID + len(classList)
+			if endID > len(glyphOrder):
+				warnings.warn("ClassDef table has entries for out of range glyph IDs: %s,%s." % (start, len(classList)))
+				# NOTE: We clobber out-of-range things here.  There are legit uses for those,
+				# but none that we have seen in the wild.
+				endID = len(glyphOrder)
 
-			map(operator.setitem, [classDefs]*lenList, keyList, classList)
+			for glyphID, cls in zip(range(startID, endID), classList):
+				classDefs[glyphOrder[glyphID]] = cls
 
 		elif self.Format == 2:
 			records = rawTable["ClassRangeRecord"]
@@ -251,21 +246,34 @@
 				start = rec.Start
 				end = rec.End
 				cls = rec.Class
-				classDefs[start] = cls
-				glyphIDs = range(font.getGlyphID(start) + 1, font.getGlyphID(end))
-				lenList = len(glyphIDs)
-				keyList = [getGlyphName(glyphID) for glyphID in glyphIDs]
-				map(operator.setitem,  [classDefs]*lenList, keyList, [cls]*lenList)
-				classDefs[end] = cls
+				try:
+					startID = font.getGlyphID(start, requireReal=True)
+				except KeyError:
+					warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
+					continue
+				try:
+					endID = font.getGlyphID(end, requireReal=True) + 1
+				except KeyError:
+					# Apparently some tools use 65535 to "match all" the range
+					if end != 'glyph65535':
+						warnings.warn("ClassDef table has end glyph ID out of range: %s." % end)
+					# NOTE: We clobber out-of-range things here.  There are legit uses for those,
+					# but none that we have seen in the wild.
+					endID = len(glyphOrder)
+				for glyphID in range(startID, endID):
+					classDefs[glyphOrder[glyphID]] = cls
 		else:
 			assert 0, "unknown format: %s" % self.Format
 		self.classDefs = classDefs
+		del self.Format # Don't need this anymore
 	
 	def preWrite(self, font):
 		classDefs = getattr(self, "classDefs", None)
 		if classDefs is None:
 			classDefs = self.classDefs = {}
-		items = classDefs.items()
+		items = list(classDefs.items())
+		format = 2
+		rawTable = {"ClassRangeRecord": []}
 		getGlyphID = font.getGlyphID
 		for i in range(len(items)):
 			glyphName, cls = items[i]
@@ -273,34 +281,49 @@
 		items.sort()
 		if items:
 			last, lastName, lastCls = items[0]
-			rec = ClassRangeRecord()
-			rec.Start = lastName
-			rec.Class = lastCls
-			ranges = [rec]
+			ranges = [[lastCls, last, lastName]]
 			for glyphID, glyphName, cls in items[1:]:
 				if glyphID != last + 1 or cls != lastCls:
-					rec.End = lastName
-					rec = ClassRangeRecord()
-					rec.Start = glyphName
-					rec.Class = cls
-					ranges.append(rec)
+					ranges[-1].extend([last, lastName])
+					ranges.append([cls, glyphID, glyphName])
 				last = glyphID
 				lastName = glyphName
 				lastCls = cls
-			rec.End = lastName
-		else:
-			ranges = []
-		self.Format = 2  # currently no support for Format 1
-		return {"ClassRangeRecord": ranges}
+			ranges[-1].extend([last, lastName])
+
+			startGlyph = ranges[0][1]
+			endGlyph = ranges[-1][3]
+			glyphCount = endGlyph - startGlyph + 1
+			if len(ranges) * 3 < glyphCount + 1:
+				# Format 2 is more compact
+				for i in range(len(ranges)):
+					cls, start, startName, end, endName = ranges[i]
+					rec = ClassRangeRecord()
+					rec.Start = startName
+					rec.End = endName
+					rec.Class = cls
+					ranges[i] = rec
+				format = 2
+				rawTable = {"ClassRangeRecord": ranges}
+			else:
+				# Format 1 is more compact
+				startGlyphName = ranges[0][2]
+				classes = [0] * glyphCount
+				for cls, start, startName, end, endName in ranges:
+					for g in range(start - startGlyph, end - startGlyph + 1):
+						classes[g] = cls
+				format = 1
+				rawTable = {"StartGlyph": startGlyphName, "ClassValueArray": classes}
+		self.Format = format
+		return rawTable
 	
 	def toXML2(self, xmlWriter, font):
-		items = self.classDefs.items()
-		items.sort()
+		items = sorted(self.classDefs.items())
 		for glyphName, cls in items:
 			xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)])
 			xmlWriter.newline()
 	
-	def fromXML(self, (name, attrs, content), font):
+	def fromXML(self, name, attrs, content, font):
 		classDefs = getattr(self, "classDefs", None)
 		if classDefs is None:
 			classDefs = {}
@@ -322,13 +345,14 @@
 		else:
 			assert 0, "unknown format: %s" % self.Format
 		self.alternates = alternates
+		del self.Format # Don't need this anymore
 	
 	def preWrite(self, font):
 		self.Format = 1
 		alternates = getattr(self, "alternates", None)
 		if alternates is None:
 			alternates = self.alternates = {}
-		items = alternates.items()
+		items = list(alternates.items())
 		for i in range(len(items)):
 			glyphName, set = items[i]
 			items[i] = font.getGlyphID(glyphName), glyphName, set
@@ -350,8 +374,7 @@
 		return {"Coverage": cov, "AlternateSet": alternates}
 	
 	def toXML2(self, xmlWriter, font):
-		items = self.alternates.items()
-		items.sort()
+		items = sorted(self.alternates.items())
 		for glyphName, alternates in items:
 			xmlWriter.begintag("AlternateSet", glyph=glyphName)
 			xmlWriter.newline()
@@ -361,7 +384,7 @@
 			xmlWriter.endtag("AlternateSet")
 			xmlWriter.newline()
 	
-	def fromXML(self, (name, attrs, content), font):
+	def fromXML(self, name, attrs, content, font):
 		alternates = getattr(self, "alternates", None)
 		if alternates is None:
 			alternates = {}
@@ -370,7 +393,7 @@
 		set = []
 		alternates[glyphName] = set
 		for element in content:
-			if type(element) != TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
 			set.append(attrs["glyph"])
@@ -381,7 +404,7 @@
 	def postRead(self, rawTable, font):
 		ligatures = {}
 		if self.Format == 1:
-			input = rawTable["Coverage"].glyphs
+			input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
 			ligSets = rawTable["LigatureSet"]
 			assert len(input) == len(ligSets)
 			for i in range(len(input)):
@@ -389,12 +412,14 @@
 		else:
 			assert 0, "unknown format: %s" % self.Format
 		self.ligatures = ligatures
+		del self.Format # Don't need this anymore
 	
 	def preWrite(self, font):
+		self.Format = 1
 		ligatures = getattr(self, "ligatures", None)
 		if ligatures is None:
 			ligatures = self.ligatures = {}
-		items = ligatures.items()
+		items = list(ligatures.items())
 		for i in range(len(items)):
 			glyphName, set = items[i]
 			items[i] = font.getGlyphID(glyphName), glyphName, set
@@ -417,8 +442,7 @@
 		return {"Coverage": cov, "LigatureSet": ligSets}
 	
 	def toXML2(self, xmlWriter, font):
-		items = self.ligatures.items()
-		items.sort()
+		items = sorted(self.ligatures.items())
 		for glyphName, ligSets in items:
 			xmlWriter.begintag("LigatureSet", glyph=glyphName)
 			xmlWriter.newline()
@@ -429,7 +453,7 @@
 			xmlWriter.endtag("LigatureSet")
 			xmlWriter.newline()
 	
-	def fromXML(self, (name, attrs, content), font):
+	def fromXML(self, name, attrs, content, font):
 		ligatures = getattr(self, "ligatures", None)
 		if ligatures is None:
 			ligatures = {}
@@ -438,7 +462,7 @@
 		ligs = []
 		ligatures[glyphName] = ligs
 		for element in content:
-			if type(element) != TupleType:
+			if not isinstance(element, tuple):
 				continue
 			name, attrs, content = element
 			lig = Ligature()
@@ -459,7 +483,8 @@
 	'LangSys': ('DefaultLangSys',),
 	'Coverage': ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage',
 			'Mark2Coverage', 'BacktrackCoverage', 'InputCoverage',
-			'LookAheadCoverage'),
+			'LookAheadCoverage', 'VertGlyphCoverage', 'HorizGlyphCoverage',
+			'TopAccentCoverage', 'ExtendedShapeCoverage', 'MathKernCoverage'),
 	'ClassDef': ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef',
 			'LookAheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef'),
 	'Anchor': ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor',
@@ -475,6 +500,9 @@
 	'JstfGPOSModList': ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS',
 			'ExtensionDisableGPOS',),
 	'JstfMax': ('ShrinkageJstfMax', 'ExtensionJstfMax',),
+	'MathKern': ('TopRightMathKern', 'TopLeftMathKern', 'BottomRightMathKern',
+			'BottomLeftMathKern'),
+	'MathGlyphConstruction': ('VertGlyphConstruction', 'HorizGlyphConstruction'),
 }
 
 #
@@ -501,14 +529,14 @@
 					SubTable[0] and contents
 					...
 					SubTable[n] and contents
-	If the offset to a lookup overflowed (SubTableIndex == None)
+	If the offset to a lookup overflowed (SubTableIndex is None)
 		we must promote the *previous*	lookup to an Extension type.
 	If the offset from a lookup to subtable overflowed, then we must promote it 
 		to an Extension Lookup type.
 	"""
 	ok = 0
 	lookupIndex = overflowRecord.LookupListIndex
-	if (overflowRecord.SubTableIndex == None):
+	if (overflowRecord.SubTableIndex is None):
 		lookupIndex = lookupIndex - 1
 	if lookupIndex < 0:
 		return ok
@@ -544,14 +572,13 @@
 	if hasattr(oldSubTable, 'sortCoverageLast'):
 		newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast
 	
-	oldAlts = oldSubTable.alternates.items()
-	oldAlts.sort()
+	oldAlts = sorted(oldSubTable.alternates.items())
 	oldLen = len(oldAlts)
 
 	if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']:
 		# Coverage table is written last. overflow is to or within the
 		# the coverage table. We will just cut the subtable in half.
-		newLen = int(oldLen/2)
+		newLen = oldLen//2
 
 	elif overflowRecord.itemName == 'AlternateSet':
 		# We just need to back up by two items 
@@ -573,14 +600,13 @@
 def splitLigatureSubst(oldSubTable, newSubTable, overflowRecord):
 	ok = 1
 	newSubTable.Format = oldSubTable.Format
-	oldLigs = oldSubTable.ligatures.items()
-	oldLigs.sort()
+	oldLigs = sorted(oldSubTable.ligatures.items())
 	oldLen = len(oldLigs)
 
 	if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']:
 		# Coverage table is written last. overflow is to or within the
 		# the coverage table. We will just cut the subtable in half.
-		newLen = int(oldLen/2)
+		newLen = oldLen//2
 
 	elif overflowRecord.itemName == 'LigatureSet':
 		# We just need to back up by two items 
@@ -669,8 +695,8 @@
 
 
 def _buildClasses():
-	import new, re
-	from otData import otData
+	import re
+	from .otData import otData
 	
 	formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$")
 	namespace = globals()
@@ -683,15 +709,15 @@
 			# XxxFormatN subtable, we only add the "base" table
 			name = m.group(1)
 			baseClass = FormatSwitchingBaseTable
-		if not namespace.has_key(name):
+		if name not in namespace:
 			# the class doesn't exist yet, so the base implementation is used.
-			cls = new.classobj(name, (baseClass,), {})
+			cls = type(name, (baseClass,), {})
 			namespace[name] = cls
 	
 	for base, alts in _equivalents.items():
 		base = namespace[base]
 		for alt in alts:
-			namespace[alt] = new.classobj(alt, (base,), {})
+			namespace[alt] = type(alt, (base,), {})
 	
 	global lookupTypes
 	lookupTypes = {
@@ -721,9 +747,18 @@
 	for lookupEnum in lookupTypes.values():
 		for enum, cls in lookupEnum.items():
 			cls.LookupType = enum
+
+	global featureParamTypes
+	featureParamTypes = {
+		'size': FeatureParamsSize,
+	}
+	for i in range(1, 20+1):
+		featureParamTypes['ss%02d' % i] = FeatureParamsStylisticSet
+	for i in range(1, 99+1):
+		featureParamTypes['cv%02d' % i] = FeatureParamsCharacterVariants
 	
 	# add converters to classes
-	from otConverters import buildConverters
+	from .otConverters import buildConverters
 	for name, table in otData:
 		m = formatPat.match(name)
 		if m:
diff --git a/Lib/fontTools/ttLib/tables/sbixBitmap.py b/Lib/fontTools/ttLib/tables/sbixBitmap.py
new file mode 100644
index 0000000..991b547
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/sbixBitmap.py
@@ -0,0 +1,104 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import readHex
+import struct
+
+
+sbixBitmapHeaderFormat = """
+	>
+	usReserved1:     H    # 00 00
+	usReserved2:     H    #       00 00
+	imageFormatTag:  4s   # e.g. "png "
+"""
+
+sbixBitmapHeaderFormatSize = sstruct.calcsize(sbixBitmapHeaderFormat)
+
+
+class Bitmap(object):
+	def __init__(self, glyphName=None, referenceGlyphName=None, usReserved1=0, usReserved2=0, imageFormatTag=None, imageData=None, rawdata=None, gid=0):
+		self.gid = gid
+		self.glyphName = glyphName
+		self.referenceGlyphName = referenceGlyphName
+		self.usReserved1 = usReserved1
+		self.usReserved2 = usReserved2
+		self.rawdata = rawdata
+		self.imageFormatTag = imageFormatTag
+		self.imageData = imageData
+
+	def decompile(self, ttFont):
+		self.glyphName = ttFont.getGlyphName(self.gid)
+		if self.rawdata is None:
+			from fontTools import ttLib
+			raise ttLib.TTLibError("No table data to decompile")
+		if len(self.rawdata) > 0:
+			if len(self.rawdata) < sbixBitmapHeaderFormatSize:
+				from fontTools import ttLib
+				#print "Bitmap %i header too short: Expected %x, got %x." % (self.gid, sbixBitmapHeaderFormatSize, len(self.rawdata))
+				raise ttLib.TTLibError("Bitmap header too short.")
+
+			sstruct.unpack(sbixBitmapHeaderFormat, self.rawdata[:sbixBitmapHeaderFormatSize], self)
+
+			if self.imageFormatTag == "dupe":
+				# bitmap is a reference to another glyph's bitmap
+				gid, = struct.unpack(">H", self.rawdata[sbixBitmapHeaderFormatSize:])
+				self.referenceGlyphName = ttFont.getGlyphName(gid)
+			else:
+				self.imageData = self.rawdata[sbixBitmapHeaderFormatSize:]
+				self.referenceGlyphName = None
+		# clean up
+		del self.rawdata
+		del self.gid
+
+	def compile(self, ttFont):
+		if self.glyphName is None:
+			from fontTools import ttLib
+			raise ttLib.TTLibError("Can't compile bitmap without glyph name")
+			# TODO: if ttFont has no maxp, cmap etc., ignore glyph names and compile by index?
+			# (needed if you just want to compile the sbix table on its own)
+		self.gid = struct.pack(">H", ttFont.getGlyphID(self.glyphName))
+		if self.imageFormatTag is None:
+			self.rawdata = ""
+		else:
+			self.rawdata = sstruct.pack(sbixBitmapHeaderFormat, self) + self.imageData
+
+	def toXML(self, xmlWriter, ttFont):
+		if self.imageFormatTag == None:
+			# TODO: ignore empty bitmaps?
+			# a bitmap entry is required for each glyph,
+			# but empty ones can be calculated at compile time
+			xmlWriter.simpletag("bitmap", glyphname=self.glyphName)
+			xmlWriter.newline()
+			return
+		xmlWriter.begintag("bitmap", format=self.imageFormatTag, glyphname=self.glyphName)
+		xmlWriter.newline()
+		#xmlWriter.simpletag("usReserved1", value=self.usReserved1)
+		#xmlWriter.newline()
+		#xmlWriter.simpletag("usReserved2", value=self.usReserved2)
+		#xmlWriter.newline()
+		if self.imageFormatTag == "dupe":
+			# format == "dupe" is apparently a reference to another glyph id.
+			xmlWriter.simpletag("ref", glyphname=self.referenceGlyphName)
+		else:
+			xmlWriter.begintag("hexdata")
+			xmlWriter.newline()
+			xmlWriter.dumphex(self.imageData)
+			xmlWriter.endtag("hexdata")
+		xmlWriter.newline()
+		xmlWriter.endtag("bitmap")
+		xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content, ttFont):
+		#if name in ["usReserved1", "usReserved2"]:
+		#	setattr(self, name, int(attrs["value"]))
+		#elif
+		if name == "ref":
+			# bitmap is a "dupe", i.e. a reference to another bitmap.
+			# in this case imageData contains the glyph id of the reference glyph
+			# get glyph id from glyphname
+			self.imageData = struct.pack(">H", ttFont.getGlyphID(attrs["glyphname"]))
+		elif name == "hexdata":
+			self.imageData = readHex(content)
+		else:
+			from fontTools import ttLib
+			raise ttLib.TTLibError("can't handle '%s' element" % name)
diff --git a/Lib/fontTools/ttLib/tables/sbixBitmapSet.py b/Lib/fontTools/ttLib/tables/sbixBitmapSet.py
new file mode 100644
index 0000000..9798666
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/sbixBitmapSet.py
@@ -0,0 +1,138 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.misc import sstruct
+from fontTools.misc.textTools import readHex
+from .sbixBitmap import *
+import struct
+
+sbixBitmapSetHeaderFormat = """
+	>
+	size:            H    # 00 28
+	resolution:      H    #       00 48
+"""
+
+sbixBitmapOffsetEntryFormat = """
+	>
+	ulOffset:        L    # 00 00 07 E0 # Offset from start of first offset entry to each bitmap
+"""
+
+sbixBitmapSetHeaderFormatSize = sstruct.calcsize(sbixBitmapSetHeaderFormat)
+sbixBitmapOffsetEntryFormatSize = sstruct.calcsize(sbixBitmapOffsetEntryFormat)
+
+
+class BitmapSet(object):
+	def __init__(self, rawdata=None, size=0, resolution=72):
+		self.data = rawdata
+		self.size = size
+		self.resolution = resolution
+		self.bitmaps = {}
+
+	def decompile(self, ttFont):
+		if self.data is None:
+			from fontTools import ttLib
+			raise ttLib.TTLibError
+		if len(self.data) < sbixBitmapSetHeaderFormatSize:
+			from fontTools import ttLib
+			raise(ttLib.TTLibError, "BitmapSet header too short: Expected %x, got %x.") \
+				% (sbixBitmapSetHeaderFormatSize, len(self.data))
+
+		# read BitmapSet header from raw data
+		sstruct.unpack(sbixBitmapSetHeaderFormat, self.data[:sbixBitmapSetHeaderFormatSize], self)
+
+		# calculate number of bitmaps
+		firstBitmapOffset, = struct.unpack(">L", \
+			self.data[sbixBitmapSetHeaderFormatSize : sbixBitmapSetHeaderFormatSize + sbixBitmapOffsetEntryFormatSize])
+		self.numBitmaps = (firstBitmapOffset - sbixBitmapSetHeaderFormatSize) // sbixBitmapOffsetEntryFormatSize - 1
+		# ^ -1 because there's one more offset than bitmaps
+
+		# build offset list for single bitmap offsets
+		self.bitmapOffsets = []
+		for i in range(self.numBitmaps + 1): # + 1 because there's one more offset than bitmaps
+			start = i * sbixBitmapOffsetEntryFormatSize + sbixBitmapSetHeaderFormatSize
+			myOffset, = struct.unpack(">L", self.data[start : start + sbixBitmapOffsetEntryFormatSize])
+			self.bitmapOffsets.append(myOffset)
+
+		# iterate through offset list and slice raw data into bitmaps
+		for i in range(self.numBitmaps):
+			myBitmap = Bitmap(rawdata=self.data[self.bitmapOffsets[i] : self.bitmapOffsets[i+1]], gid=i)
+			myBitmap.decompile(ttFont)
+			self.bitmaps[myBitmap.glyphName] = myBitmap
+		del self.bitmapOffsets
+		del self.data
+
+	def compile(self, ttFont):
+		self.bitmapOffsets = ""
+		self.bitmapData = ""
+
+		glyphOrder = ttFont.getGlyphOrder()
+
+		# first bitmap starts right after the header
+		bitmapOffset = sbixBitmapSetHeaderFormatSize + sbixBitmapOffsetEntryFormatSize * (len(glyphOrder) + 1)
+		for glyphName in glyphOrder:
+			if glyphName in self.bitmaps:
+				# we have a bitmap for this glyph
+				myBitmap = self.bitmaps[glyphName]
+			else:
+				# must add empty bitmap for this glyph
+				myBitmap = Bitmap(glyphName=glyphName)
+			myBitmap.compile(ttFont)
+			myBitmap.ulOffset = bitmapOffset
+			self.bitmapData += myBitmap.rawdata
+			bitmapOffset += len(myBitmap.rawdata)
+			self.bitmapOffsets += sstruct.pack(sbixBitmapOffsetEntryFormat, myBitmap)
+
+		# add last "offset", really the end address of the last bitmap
+		dummy = Bitmap()
+		dummy.ulOffset = bitmapOffset
+		self.bitmapOffsets += sstruct.pack(sbixBitmapOffsetEntryFormat, dummy)
+
+		# bitmap sets are padded to 4 byte boundaries
+		dataLength = len(self.bitmapOffsets) + len(self.bitmapData)
+		if dataLength % 4 != 0:
+			padding = 4 - (dataLength % 4)
+		else:
+			padding = 0
+
+		# pack header
+		self.data = sstruct.pack(sbixBitmapSetHeaderFormat, self)
+		# add offset, image data and padding after header
+		self.data += self.bitmapOffsets + self.bitmapData + "\0" * padding
+
+	def toXML(self, xmlWriter, ttFont):
+		xmlWriter.begintag("bitmapSet")
+		xmlWriter.newline()
+		xmlWriter.simpletag("size", value=self.size)
+		xmlWriter.newline()
+		xmlWriter.simpletag("resolution", value=self.resolution)
+		xmlWriter.newline()
+		glyphOrder = ttFont.getGlyphOrder()
+		for i in range(len(glyphOrder)):
+			if glyphOrder[i] in self.bitmaps:
+				self.bitmaps[glyphOrder[i]].toXML(xmlWriter, ttFont)
+				# TODO: what if there are more bitmaps than glyphs?
+		xmlWriter.endtag("bitmapSet")
+		xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content, ttFont):
+		if name in ["size", "resolution"]:
+			setattr(self, name, int(attrs["value"]))
+		elif name == "bitmap":
+			if "format" in attrs:
+				myFormat = attrs["format"]
+			else:
+				myFormat = None
+			if "glyphname" in attrs:
+				myGlyphName = attrs["glyphname"]
+			else:
+				from fontTools import ttLib
+				raise ttLib.TTLibError("Bitmap must have a glyph name.")
+			myBitmap = Bitmap(glyphName=myGlyphName, imageFormatTag=myFormat)
+			for element in content:
+				if isinstance(element, tuple):
+					name, attrs, content = element
+					myBitmap.fromXML(name, attrs, content, ttFont)
+					myBitmap.compile(ttFont)
+			self.bitmaps[myBitmap.glyphName] = myBitmap
+		else:
+			from fontTools import ttLib
+			raise ttLib.TTLibError("can't handle '%s' element" % name)
diff --git a/Lib/fontTools/ttLib/tables/ttProgram.py b/Lib/fontTools/ttLib/tables/ttProgram.py
index fa6ea69..17c15f2 100644
--- a/Lib/fontTools/ttLib/tables/ttProgram.py
+++ b/Lib/fontTools/ttLib/tables/ttProgram.py
@@ -1,8 +1,10 @@
 """ttLib.tables.ttProgram.py -- Assembler/disassembler for TrueType bytecode programs."""
 
-import array
-import re, string
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc.textTools import num2binary, binary2num, readHex
+import array
+import re
 
 # first, the list of instructions that eat bytes or words from the instruction stream
 
@@ -161,13 +163,13 @@
 	mnemonicDict = {}
 	for op, mnemonic, argBits, name, pops, pushes in instructionList:
 		assert _mnemonicPat.match(mnemonic)
-		mnemonicDict[mnemonic] = op, argBits, name
+		mnemonicDict[mnemonic] = op, argBits
 		if argBits:
 			argoffset = op
 			for i in range(1 << argBits):
-				opcodeDict[op+i] = mnemonic, argBits, argoffset, name
+				opcodeDict[op+i] = mnemonic, argBits, argoffset
 		else:
-				opcodeDict[op] = mnemonic, 0, 0, name
+				opcodeDict[op] = mnemonic, 0, 0
 	return opcodeDict, mnemonicDict
 
 streamOpcodeDict, streamMnemonicDict = _makeDict(streamInstructions)
@@ -191,14 +193,14 @@
 _pushCountPat = re.compile(r"[A-Z][A-Z0-9]*\s*\[.*?\]\s*/\* ([0-9]*).*?\*/")
 
 
-def _skipWhite(data, pos, _whiteRE=_whiteRE):
+def _skipWhite(data, pos):
 	m = _whiteRE.match(data, pos)
 	newPos = m.regs[0][1]
 	assert newPos >= pos
 	return newPos
 
 
-class Program:
+class Program(object):
 	
 	def __init__(self):
 		pass
@@ -224,7 +226,7 @@
 		return self.assembly
 	
 	def toXML(self, writer, ttFont):
-		if hasattr (ttFont, "disassembleInstructions") and ttFont.disassembleInstructions:
+		if not hasattr (ttFont, "disassembleInstructions") or ttFont.disassembleInstructions:
 			assembly = self.getAssembly()
 			writer.begintag("assembly")
 			writer.newline()
@@ -242,11 +244,11 @@
 					j = 0
 					for j in range(nValues):
 						if j and not (j % 25):
-							writer.write(string.join(line, " "))
+							writer.write(' '.join(line))
 							writer.newline()
 							line = []
 						line.append(assembly[i+j])
-					writer.write(string.join(line, " "))
+					writer.write(' '.join(line))
 					writer.newline()
 					i = i + j + 1
 			writer.endtag("assembly")
@@ -256,39 +258,41 @@
 			writer.dumphex(self.getBytecode())
 			writer.endtag("bytecode")
 	
-	def fromXML(self, (name, attrs, content), ttFont):
+	def fromXML(self, name, attrs, content, ttFont):
 		if name == "assembly":
-			self.fromAssembly(string.join(content, ""))
+			self.fromAssembly(strjoin(content))
 			self._assemble()
 			del self.assembly
 		else:
 			assert name == "bytecode"
 			self.fromBytecode(readHex(content))
 	
-	def _assemble(self, 
-			skipWhite=_skipWhite, mnemonicDict=mnemonicDict, strip=string.strip,
-			binary2num=binary2num):
+	def _assemble(self):
 		assembly = self.assembly
-		if type(assembly) == type([]):
-			assembly = string.join(assembly, " ")
+		if isinstance(assembly, type([])):
+			assembly = ' '.join(assembly)
 		bytecode = []
 		push = bytecode.append
 		lenAssembly = len(assembly)
-		pos = skipWhite(assembly, 0)
+		pos = _skipWhite(assembly, 0)
 		while pos < lenAssembly:
 			m = _tokenRE.match(assembly, pos)
 			if m is None:
-				raise tt_instructions_error, "Syntax error in TT program (%s)" % assembly[pos-5:pos+15]
+				raise tt_instructions_error("Syntax error in TT program (%s)" % assembly[pos-5:pos+15])
 			dummy, mnemonic, arg, number, comment = m.groups()
 			pos = m.regs[0][1]
 			if comment:
 				continue
 			
-			arg = strip(arg)
-			if mnemonic not in ("NPUSHB", "NPUSHW", "PUSHB", "PUSHW"):
-				op, argBits, name = mnemonicDict[mnemonic]
-				if len(arg) <> argBits:
-					raise tt_instructions_error, "Incorrect number of argument bits (%s[%s])" % (mnemonic, arg)
+			arg = arg.strip()
+			if mnemonic.startswith("INSTR"):
+				# Unknown instruction
+				op = int(mnemonic[5:])
+				push(op)
+			elif mnemonic not in ("PUSH", "NPUSHB", "NPUSHW", "PUSHB", "PUSHW"):
+				op, argBits = mnemonicDict[mnemonic]
+				if len(arg) != argBits:
+					raise tt_instructions_error("Incorrect number of argument bits (%s[%s])" % (mnemonic, arg))
 				if arg:
 					arg = binary2num(arg)
 					push(op + arg)
@@ -296,44 +300,90 @@
 					push(op)
 			else:
 				args = []
+				pos = _skipWhite(assembly, pos)
 				while pos < lenAssembly:
-					pos = skipWhite(assembly, pos)
 					m = _tokenRE.match(assembly, pos)
 					if m is None:
-						raise tt_instructions_error, "Syntax error in TT program (%s)" % assembly[pos:pos+15]
-					dummy, mnemonic, arg, number, comment = m.groups()
+						raise tt_instructions_error("Syntax error in TT program (%s)" % assembly[pos:pos+15])
+					dummy, _mnemonic, arg, number, comment = m.groups()
 					if number is None and comment is None:
 						break
 					pos = m.regs[0][1]
+					pos = _skipWhite(assembly, pos)
 					if comment is not None:
 						continue
 					args.append(int(number))
-				if max(args) > 255 or min(args) < 0:
-					words = 1
-					mnemonic = "PUSHW"
-				else:
-					words = 0
-					mnemonic = "PUSHB"
 				nArgs = len(args)
-				if nArgs <= 8:
-					op, argBits, name = streamMnemonicDict[mnemonic]
-					op = op + nArgs - 1
-					push(op)
-				elif nArgs < 256:
-					mnemonic = "N" + mnemonic
-					op, argBits, name = streamMnemonicDict[mnemonic]
-					push(op)
-					push(nArgs)
+				if mnemonic == "PUSH":
+					# Automatically choose the most compact representation
+					nWords = 0
+					while nArgs:
+						while nWords < nArgs and nWords < 255 and not (0 <= args[nWords] <= 255):
+							nWords += 1
+						nBytes = 0
+						while nWords+nBytes < nArgs and nBytes < 255 and 0 <= args[nWords+nBytes] <= 255:
+							nBytes += 1
+						if nBytes < 2 and nWords + nBytes < 255 and nWords + nBytes != nArgs:
+							# Will write bytes as words
+							nWords += nBytes
+							continue
+
+						# Write words
+						if nWords:
+							if nWords <= 8:
+								op, argBits = streamMnemonicDict["PUSHW"]
+								op = op + nWords - 1
+								push(op)
+							else:
+								op, argBits = streamMnemonicDict["NPUSHW"]
+								push(op)
+								push(nWords)
+							for value in args[:nWords]:
+								assert -32768 <= value < 32768, "PUSH value out of range %d" % value
+								push((value >> 8) & 0xff)
+								push(value & 0xff)
+
+						# Write bytes
+						if nBytes:
+							pass
+							if nBytes <= 8:
+								op, argBits = streamMnemonicDict["PUSHB"]
+								op = op + nBytes - 1
+								push(op)
+							else:
+								op, argBits = streamMnemonicDict["NPUSHB"]
+								push(op)
+								push(nBytes)
+							for value in args[nWords:nWords+nBytes]:
+								push(value)
+
+						nTotal = nWords + nBytes
+						args = args[nTotal:]
+						nArgs -= nTotal
+						nWords = 0
 				else:
-					raise tt_instructions_error, "More than 255 push arguments (%s)" % nArgs
-				if words:
-					for value in args:
-						push((value >> 8) & 0xff)
-						push(value & 0xff)
-				else:
-					for value in args:
-						push(value)
-			pos = skipWhite(assembly, pos)
+					# Write exactly what we've been asked to
+					words = mnemonic[-1] == "W"
+					op, argBits = streamMnemonicDict[mnemonic]
+					if mnemonic[0] != "N":
+						assert nArgs <= 8, nArgs
+						op = op + nArgs - 1
+						push(op)
+					else:
+						assert nArgs < 256
+						push(op)
+						push(nArgs)
+					if words:
+						for value in args:
+							assert -32768 <= value < 32768, "PUSHW value out of range %d" % value
+							push((value >> 8) & 0xff)
+							push(value & 0xff)
+					else:
+						for value in args:
+							assert 0 <= value < 256, "PUSHB value out of range %d" % value
+							push(value)
+
+			pos = _skipWhite(assembly, pos)
 		
 		if bytecode:
 			assert max(bytecode) < 256 and min(bytecode) >= 0
@@ -346,44 +396,47 @@
 		numBytecode = len(bytecode)
 		while i < numBytecode:
 			op = bytecode[i]
-			arg = 0
 			try:
-				mnemonic, argBits, argoffset, name = opcodeDict[op]
+				mnemonic, argBits, argoffset = 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
+				if op in streamOpcodeDict:
+					values = []
+
+					# Merge consecutive PUSH operations
+					while bytecode[i] in streamOpcodeDict:
+						op = bytecode[i]
+						mnemonic, argBits, argoffset = streamOpcodeDict[op]
+						words = mnemonic[-1] == "W"
+						if argBits:
+							nValues = op - argoffset + 1
+						else:
+							i = i + 1
+							nValues = bytecode[i]
+						i = i + 1
+						assert nValues > 0
+						if not words:
+							for j in range(nValues):
+								value = bytecode[i]
+								values.append(repr(value))
+								i = i + 1
+						else:
+							for j in range(nValues):
+								# cast to signed int16
+								value = (bytecode[i] << 8) | bytecode[i+1]
+								if value >= 0x8000:
+									value = value - 0x10000
+								values.append(repr(value))
+								i = i + 2
+
+					nValues = len(values)
+					if nValues == 1:
+						assembly.append("PUSH[ ]")
 					else:
-						pushWords = op - argoffset + 1
+						assembly.append("PUSH[ ]  /* %s values pushed */" % nValues)
+					assembly.extend(values)
 				else:
+					assembly.append("INSTR%d[ ]" % op)
 					i = i + 1
-					if mnemonic == "NPUSHB":
-						pushBytes = bytecode[i]
-					else:
-						pushWords = bytecode[i]
-				i = i + 1
-				nValues = pushBytes or pushWords
-				assert nValues > 0
-				if nValues == 1:
-					assembly.append("%s[ ]  /* %s value pushed */" % (mnemonic, nValues))
-				else:
-					assembly.append("%s[ ]  /* %s values pushed */" % (mnemonic, nValues))
-				for j in range(pushBytes):
-					value = bytecode[i]
-					assembly.append(`value`)
-					i = i + 1
-				for j in range(pushWords):
-					# cast to signed int16
-					value = (bytecode[i] << 8) | bytecode[i+1]
-					if value >= 0x8000:
-						value = value - 0x10000
-					assembly.append(`value`)
-					i = i + 2
 			else:
 				if argBits:
 					assembly.append(mnemonic + "[%s]" % num2binary(op - argoffset, argBits))
@@ -400,5 +453,5 @@
 	p.fromBytecode(bc)
 	asm = p.getAssembly()
 	p.fromAssembly(asm)
-	print bc == p.getBytecode()
+	print(bc == p.getBytecode())
 
diff --git a/Lib/fontTools/ttLib/test/__init__.py b/Lib/fontTools/ttLib/test/__init__.py
deleted file mode 100644
index e001bb2..0000000
--- a/Lib/fontTools/ttLib/test/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""Empty __init__.py file to signal Python this directory is a package.
-(It can't be completely empty since WinZip seems to skip empty files.)
-"""
diff --git a/Lib/fontTools/ttLib/test/ttBrowser.py b/Lib/fontTools/ttLib/test/ttBrowser.py
deleted file mode 100644
index 5add6bb..0000000
--- a/Lib/fontTools/ttLib/test/ttBrowser.py
+++ /dev/null
@@ -1,351 +0,0 @@
-"""Mac-OS9-only TrueType browser window, deprecated and no longer maintained."""
-
-from fontTools import ttLib
-from fontTools.ttLib import macUtils
-import macfs
-import PyBrowser
-import W, Lists
-import os
-import ATM
-import numpy
-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, ".ttx")
-		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(numpy.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] * numpy.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", ".ttx", ".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
-
-
-def browseTTFont():
-	fss, ok = macfs.StandardGetFile()
-	if not ok:
-		return
-	path = fss.as_pathname()
-	indices = macUtils.getSFNTResIndices(path)
-	if indices:
-		for i in indices:
-			TableBrowser(path, res_index=i)
-	else:
-		TableBrowser(path)
-
-
-if __name__ == "__main__":
-	browseTTFont()
-
diff --git a/Lib/fontTools/ttx.py b/Lib/fontTools/ttx.py
index 0ed719d..5690e7f 100644
--- a/Lib/fontTools/ttx.py
+++ b/Lib/fontTools/ttx.py
@@ -18,6 +18,8 @@
     -o <outputfile> Specify a file to write the output to.
     -v Verbose: more messages will be written to stdout about what
        is being done.
+    -q Quiet: No messages will be written to stdout about what
+       is being done.
     -a allow virtual glyphs ID's on compile or decompile.
 
     Dump options:
@@ -64,51 +66,52 @@
 """
 
 
-import sys
-import os
-import getopt
-import re
-from fontTools.ttLib import TTFont
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+from fontTools.ttLib import TTFont, TTLibError
 from fontTools.ttLib.tables.otBase import OTLOffsetOverflowError
 from fontTools.ttLib.tables.otTables import fixLookupOverFlows, fixSubTableOverFlows
 from fontTools.misc.macCreatorType import getMacCreatorAndType
-from fontTools import version
+import os
+import sys
+import getopt
+import re
 
 def usage():
-	print __doc__ % version
+	from fontTools import version
+	print(__doc__ % version)
 	sys.exit(2)
 
 	
-numberAddedRE = re.compile("(.*)#\d+$")
+numberAddedRE = re.compile("#\d+$")
 opentypeheaderRE = re.compile('''sfntVersion=['"]OTTO["']''')
 
 def makeOutputFileName(input, outputDir, extension):
-	dir, file = os.path.split(input)
-	file, ext = os.path.splitext(file)
+	dirName, fileName = os.path.split(input)
+	fileName, ext = os.path.splitext(fileName)
 	if outputDir:
-		dir = outputDir
-	output = os.path.join(dir, file + extension)
-	m = numberAddedRE.match(file)
-	if m:
-		file = m.group(1)
+		dirName = outputDir
+	fileName = numberAddedRE.split(fileName)[0]
+	output = os.path.join(dirName, fileName + extension)
 	n = 1
 	while os.path.exists(output):
-		output = os.path.join(dir, file + "#" + repr(n) + extension)
+		output = os.path.join(dirName, fileName + "#" + repr(n) + extension)
 		n = n + 1
 	return output
 
 
-class Options:
+class Options(object):
 
-	listTables = 0
+	listTables = False
 	outputDir = None
 	outputFile = None
-	verbose = 0
-	splitTables = 0
-	disassembleInstructions = 1
+	verbose = False
+	quiet = False
+	splitTables = False
+	disassembleInstructions = True
 	mergeFile = None
-	recalcBBoxes = 1
-	allowVID = 0
+	recalcBBoxes = True
+	allowVID = False
 	ignoreDecompileErrors = True
 	bitmapGlyphDataFormat = 'raw'
 
@@ -119,32 +122,35 @@
 		for option, value in rawOptions:
 			# general options
 			if option == "-h":
-				print __doc__ % version
+				from fontTools import version
+				print(__doc__ % version)
 				sys.exit(0)
 			elif option == "-d":
 				if not os.path.isdir(value):
-					print "The -d option value must be an existing directory"
+					print("The -d option value must be an existing directory")
 					sys.exit(2)
 				self.outputDir = value
 			elif option == "-o":
 				self.outputFile = value
 			elif option == "-v":
-				self.verbose = 1
+				self.verbose = True
+			elif option == "-q":
+				self.quiet = True
 			# dump options
 			elif option == "-l":
-				self.listTables = 1
+				self.listTables = True
 			elif option == "-t":
 				self.onlyTables.append(value)
 			elif option == "-x":
 				self.skipTables.append(value)
 			elif option == "-s":
-				self.splitTables = 1
+				self.splitTables = True
 			elif option == "-i":
-				self.disassembleInstructions = 0
+				self.disassembleInstructions = False
 			elif option == "-z":
 				validOptions = ('raw', 'row', 'bitwise', 'extfile')
 				if value not in validOptions:
-					print "-z does not allow %s as a format. Use %s" % (option, validOptions)
+					print("-z does not allow %s as a format. Use %s" % (option, validOptions))
 					sys.exit(2)
 				self.bitmapGlyphDataFormat = value
 			elif option == "-y":
@@ -153,48 +159,49 @@
 			elif option == "-m":
 				self.mergeFile = value
 			elif option == "-b":
-				self.recalcBBoxes = 0
+				self.recalcBBoxes = False
 			elif option == "-a":
-				self.allowVID = 1
+				self.allowVID = True
 			elif option == "-e":
 				self.ignoreDecompileErrors = False
 		if self.onlyTables and self.skipTables:
-			print "-t and -x options are mutually exclusive"
+			print("-t and -x options are mutually exclusive")
 			sys.exit(2)
 		if self.mergeFile and numFiles > 1:
-			print "Must specify exactly one TTX source file when using -m"
+			print("Must specify exactly one TTX source file when using -m")
 			sys.exit(2)
 
 
 def ttList(input, output, options):
-	import string
-	ttf = TTFont(input, fontNumber=options.fontNumber)
+	ttf = TTFont(input, fontNumber=options.fontNumber, lazy=True)
 	reader = ttf.reader
-	tags = reader.keys()
-	tags.sort()
-	print 'Listing table info for "%s":' % input
+	tags = sorted(reader.keys())
+	print('Listing table info for "%s":' % input)
 	format = "    %4s  %10s  %7s  %7s"
-	print format % ("tag ", "  checksum", " length", " offset")
-	print format % ("----", "----------", "-------", "-------")
+	print(format % ("tag ", "  checksum", " length", " offset"))
+	print(format % ("----", "----------", "-------", "-------"))
 	for tag in tags:
 		entry = reader.tables[tag]
-		checkSum = long(entry.checkSum)
+		checkSum = int(entry.checkSum)
 		if checkSum < 0:
-			checkSum = checkSum + 0x100000000L
-		checksum = "0x" + string.zfill(hex(checkSum)[2:-1], 8)
-		print format % (tag, checksum, entry.length, entry.offset)
-	print
+			checkSum = checkSum + 0x100000000
+		checksum = "0x%08X" % checkSum
+		print(format % (tag, checksum, entry.length, entry.offset))
+	print()
 	ttf.close()
 
 
 def ttDump(input, output, options):
-	print 'Dumping "%s" to "%s"...' % (input, output)
+	if not options.quiet:
+		print('Dumping "%s" to "%s"...' % (input, output))
 	ttf = TTFont(input, 0, verbose=options.verbose, allowVID=options.allowVID,
+			quiet=options.quiet,
 			ignoreDecompileErrors=options.ignoreDecompileErrors,
 			fontNumber=options.fontNumber)
 	ttf.saveXML(output,
+			quiet=options.quiet,
 			tables=options.onlyTables,
-			skipTables=options.skipTables, 
+			skipTables=options.skipTables,
 			splitTables=options.splitTables,
 			disassembleInstructions=options.disassembleInstructions,
 			bitmapGlyphDataFormat=options.bitmapGlyphDataFormat)
@@ -202,22 +209,23 @@
 
 
 def ttCompile(input, output, options):
-	print 'Compiling "%s" to "%s"...' % (input, output)
+	if not options.quiet:
+		print('Compiling "%s" to "%s"...' % (input, output))
 	ttf = TTFont(options.mergeFile,
 			recalcBBoxes=options.recalcBBoxes,
 			verbose=options.verbose, allowVID=options.allowVID)
-	ttf.importXML(input)
+	ttf.importXML(input, quiet=options.quiet)
 	try:
 		ttf.save(output)
-	except OTLOffsetOverflowError, e:
+	except OTLOffsetOverflowError as e:
 		# XXX This shouldn't be here at all, it should be as close to the
 		# OTL code as possible.
 		overflowRecord = e.value
-		print "Attempting to fix OTLOffsetOverflowError", e
+		print("Attempting to fix OTLOffsetOverflowError", e)
 		lastItem = overflowRecord 
-		while 1:
+		while True:
 			ok = 0
-			if overflowRecord.itemName == None:
+			if overflowRecord.itemName is None:
 				ok = fixLookupOverFlows(ttf, overflowRecord)
 			else:
 				ok = fixSubTableOverFlows(ttf, overflowRecord)
@@ -227,15 +235,15 @@
 			try:
 				ttf.save(output)
 				break
-			except OTLOffsetOverflowError, e:
-				print "Attempting to fix OTLOffsetOverflowError", e
+			except OTLOffsetOverflowError as e:
+				print("Attempting to fix OTLOffsetOverflowError", e)
 				overflowRecord = e.value
 				if overflowRecord == lastItem:
 					raise
 
 	if options.verbose:
 		import time
-		print "finished at", time.strftime("%H:%M:%S", time.localtime(time.time()))
+		print("finished at", time.strftime("%H:%M:%S", time.localtime(time.time())))
 
 
 def guessFileType(fileName):
@@ -250,15 +258,19 @@
 	if ext == ".dfont":
 		return "TTF"
 	header = f.read(256)
-	head = header[:4]
+	head = Tag(header[:4])
 	if head == "OTTO":
 		return "OTF"
 	elif head == "ttcf":
 		return "TTC"
 	elif head in ("\0\1\0\0", "true"):
 		return "TTF"
+	elif head == "wOFF":
+		return "WOFF"
 	elif head.lower() == "<?xm":
-		if opentypeheaderRE.match(header):
+		# Use 'latin1' because that can't fail.
+		header = tostr(header, 'latin1')
+		if opentypeheaderRE.search(header):
 			return "OTX"
 		else:
 			return "TTX"
@@ -267,7 +279,7 @@
 
 def parseOptions(args):
 	try:
-		rawOptions, files = getopt.getopt(args, "ld:o:vht:x:sim:z:baey:")
+		rawOptions, files = getopt.getopt(args, "ld:o:vqht:x:sim:z:baey:")
 	except getopt.GetoptError:
 		usage()
 	
@@ -279,7 +291,7 @@
 	
 	for input in files:
 		tp = guessFileType(input)
-		if tp in ("OTF", "TTF", "TTC"):
+		if tp in ("OTF", "TTF", "TTC", "WOFF"):
 			extension = ".ttx"
 			if options.listTables:
 				action = ttList
@@ -292,7 +304,7 @@
 			extension = ".otf"
 			action = ttCompile
 		else:
-			print 'Unknown file type: "%s"' % input
+			print('Unknown file type: "%s"' % input)
 			continue
 		
 		if options.outputFile:
@@ -312,7 +324,7 @@
 	"""Force the DOS Prompt window to stay open so the user gets
 	a chance to see what's wrong."""
 	import msvcrt
-	print '(Hit any key to exit)'
+	print('(Hit any key to exit)')
 	while not msvcrt.kbhit():
 		pass
 
@@ -322,12 +334,14 @@
 	try:
 		process(jobs, options)
 	except KeyboardInterrupt:
-		print "(Cancelled.)"
+		print("(Cancelled.)")
 	except SystemExit:
 		if sys.platform == "win32":
 			waitForKeyPress()
 		else:
 			raise
+	except TTLibError as e:
+		print("Error:",e)
 	except:
 		if sys.platform == "win32":
 			import traceback
diff --git a/Lib/fontTools/unicode.py b/Lib/fontTools/unicode.py
index 669d497..ef61b01 100644
--- a/Lib/fontTools/unicode.py
+++ b/Lib/fontTools/unicode.py
@@ -24471,7 +24471,7 @@
 	return unicodes
 
 
-class _Unicode:
+class _Unicode(object):
 
 	def __init__(self):
 		self.codes = _makeunicodes()
diff --git a/MANIFEST.in b/MANIFEST.in
index e1977d5..db16aa2 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -5,14 +5,9 @@
 include Doc/*.txt
 include Doc/*.html
 include MetaTools/*.py
-include MetaTools/*.diff
-include Mac/TTX.py
-include Mac/README.txt
-include Mac/TTX.rsrc.hqx
 include Windows/mcmillan.bat
 include Windows/ttx.ico
 include Windows/README.TXT
 include Windows/fonttools-win-setup.iss
 include Windows/fonttools-win-setup.txt
 include Lib/fontTools/ttLib/tables/table_API_readme.txt
-include Src/eexecOp/README.txt
diff --git a/Mac/README.txt b/Mac/README.txt
deleted file mode 100644
index 2a15038..0000000
--- a/Mac/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-The stuff in this folder is old and rusty, don't pay too much attention to it...
diff --git a/Mac/TTX.py b/Mac/TTX.py
deleted file mode 100644
index dc3f275..0000000
--- a/Mac/TTX.py
+++ /dev/null
@@ -1,322 +0,0 @@
-"""Main TTX application, Mac-only"""
-
-
-#make sure we don't lose events to SIOUX
-import MacOS
-MacOS.EnableAppswitch(-1)
-
-def SetWatchCursor():
-	import Qd, QuickDraw
-	Qd.SetCursor(Qd.GetCursor(QuickDraw.watchCursor).data)
-
-def SetArrowCursor():
-	import Qd
-	Qd.SetCursor(Qd.qd.arrow)
-
-SetWatchCursor()
-
-# a few constants
-LOGFILENAME = "TTX errors"
-PREFSFILENAME = "TTX preferences"
-DEFAULTXMLOUTPUT = ":XML output"
-DEFAULTTTOUTPUT = ":TrueType output"
-
-
-import FrameWork
-import MiniAEFrame, AppleEvents
-import EasyDialogs
-import Res
-import macfs
-import os
-import sys, time
-import re, string
-import traceback
-from fontTools import ttLib, version
-from fontTools.ttLib import xmlImport
-from fontTools.ttLib.macUtils import ProgressBar
-
-abouttext = """\
-TTX - The free TrueType to XML to TrueType converter
-(version %s)
-Copyright 1999-2001, Just van Rossum (Letterror)
-just@letterror.com""" % version
-
-
-class TTX(FrameWork.Application, MiniAEFrame.AEServer):
-	
-	def __init__(self):
-		FrameWork.Application.__init__(self)
-		MiniAEFrame.AEServer.__init__(self)
-		self.installaehandler(
-			AppleEvents.kCoreEventClass, AppleEvents.kAEOpenApplication, self.do_nothing)
-		self.installaehandler(
-			AppleEvents.kCoreEventClass, AppleEvents.kAEPrintDocuments, self.do_nothing)
-		self.installaehandler(
-			AppleEvents.kCoreEventClass, AppleEvents.kAEOpenDocuments, self.handle_opendocumentsevent)
-		self.installaehandler(
-			AppleEvents.kCoreEventClass, AppleEvents.kAEQuitApplication, self.handle_quitevent)
-	
-	def idle(self, event):
-		SetArrowCursor()
-	
-	def makeusermenus(self):
-		m = FrameWork.Menu(self.menubar, "File")
-		FrameWork.MenuItem(m, "Open...", "O", self.domenu_open)
-		FrameWork.Separator(m)
-		FrameWork.MenuItem(m, "Quit", "Q", self._quit)
-	
-	def do_about(self, *args):
-		EasyDialogs.Message(abouttext)
-	
-	def handle_quitevent(self, *args, **kwargs):
-		self._quit()
-	
-	def domenu_open(self, *args):
-		fss, ok = macfs.StandardGetFile()
-		if ok:
-			self.opendocument(fss.as_pathname())
-	
-	def handle_opendocumentsevent(self, docs, **kwargs):
-		if type(docs) <> type([]):
-			docs = [docs]
-		for doc in docs:
-			fss, a = doc.Resolve()
-			path = fss.as_pathname()
-			self.opendocument(path)
-	
-	def opendocument(self, path):
-		filename = os.path.basename(path)
-		filetype = guessfiletype(path)
-		handler = getattr(self, "handle_%s_file" % filetype)
-		handler(path)
-	
-	def handle_xml_file(self, path):
-		prefs = getprefs()
-		makesuitcase = int(prefs.get("makesuitcases", 0))
-		dstfolder = prefs.get("ttoutput", DEFAULTTTOUTPUT)
-		if not os.path.exists(dstfolder):
-			os.mkdir(dstfolder)
-		srcfilename = dstfilename = os.path.basename(path)
-		if dstfilename[-4:] in (".ttx", ".xml"):
-			dstfilename = dstfilename[:-4]
-		if dstfilename[-4:] not in (".TTF", ".ttf"):
-			dstfilename = dstfilename + ".TTF"
-		dst = os.path.join(dstfolder, dstfilename)
-		
-		if makesuitcase:
-			try:
-				# see if the destination file is writable,
-				# otherwise we'll get an error waaay at the end of
-				# the parse procedure
-				testref = Res.FSpOpenResFile(macfs.FSSpec(dst), 3)  # read-write
-			except Res.Error, why:
-				if why[0] <> -43: # file not found
-					EasyDialogs.Message("Can't create '%s'; file already open" % dst)
-					return
-			else:
-				Res.CloseResFile(testref)
-		else:
-			try:
-				f = open(dst, "wb")
-			except IOError, why:
-				EasyDialogs.Message("Can't create '%s'; file already open" % dst)
-				return
-			else:
-				f.close()
-		pb = ProgressBar("Reading TTX file '%s'..." % srcfilename)
-		try:
-			tt = ttLib.TTFont()
-			tt.importXML(path, pb)
-			pb.setlabel("Compiling and saving...")
-			tt.save(dst, makesuitcase)
-		finally:
-			pb.close()
-	
-	def handle_datafork_file(self, path):
-		prefs = getprefs()
-		dstfolder = prefs.get("xmloutput", DEFAULTXMLOUTPUT)
-		if not os.path.exists(dstfolder):
-			os.mkdir(dstfolder)
-		filename = os.path.basename(path)
-		pb = ProgressBar("Dumping '%s' to XML..." % filename)
-		if filename[-4:] in (".TTF", ".ttf"):
-			filename = filename[:-4]
-		filename = filename + ".ttx"
-		dst = os.path.join(dstfolder, filename)
-		try:
-			tt = ttLib.TTFont(path)
-			tt.saveXML(dst, pb)
-		finally:
-			pb.close()
-	
-	def handle_resource_file(self, path):
-		prefs = getprefs()
-		dstfolder = prefs.get("xmloutput", DEFAULTXMLOUTPUT)
-		if not os.path.exists(dstfolder):
-			os.mkdir(dstfolder)
-		filename = os.path.basename(path)
-		fss = macfs.FSSpec(path)
-		try:
-			resref = Res.FSpOpenResFile(fss, 1)  # read-only
-		except:
-			return "unknown"
-		Res.UseResFile(resref)
-		pb = None
-		try:
-			n = Res.Count1Resources("sfnt")
-			for i in range(1, n+1):
-				res = Res.Get1IndResource('sfnt', i)
-				resid, restype, resname = res.GetResInfo()
-				if not resname:
-					resname = filename + `i`
-				pb = ProgressBar("Dumping '%s' to XML..." % resname)
-				dst = os.path.join(dstfolder, resname + ".ttx")
-				try:
-					tt = ttLib.TTFont(path, i)
-					tt.saveXML(dst, pb)
-				finally:
-					pb.close()
-		finally:
-			Res.CloseResFile(resref)
-	
-	def handle_python_file(self, path):
-		pass
-		#print "python", path
-	
-	def handle_unknown_file(self, path):
-		EasyDialogs.Message("Cannot open '%s': unknown file kind" % os.path.basename(path))
-	
-	def do_nothing(self, *args, **kwargs):
-		pass
-	
-	def mainloop(self, mask=FrameWork.everyEvent, wait=0):
-		self.quitting = 0
-		while not self.quitting:
-			try:
-				self.do1event(mask, wait)
-			except self.__class__:
-				# D'OH! FrameWork tries to quit us on cmd-.!
-				pass
-			except KeyboardInterrupt:
-				pass
-			except ttLib.xmlImport.xml_parse_error, why:
-				EasyDialogs.Message(
-					"An error occurred while parsing the XML file:\n" + why)
-			except:
-				exc = traceback.format_exception(sys.exc_type, sys.exc_value, None)[0]
-				exc = string.strip(exc)
-				EasyDialogs.Message("An error occurred!\n%s\n[see the logfile '%s' for details]" % 
-						(exc, LOGFILENAME))
-				traceback.print_exc()
-	
-	def do_kHighLevelEvent(self, event):
-		import AE
-		AE.AEProcessAppleEvent(event)
-
-
-
-def guessfiletype(path):
-	#if path[-3:] == ".py":
-	#	return "python"
-	f = open(path, "rb")
-	data = f.read(21)
-	f.close()
-	if data[:5] == "<?xml":
-		return "xml"
-	elif data[:4] in ("\000\001\000\000", "OTTO", "true"):
-		return "datafork"
-	else:
-		# assume res fork font
-		fss = macfs.FSSpec(path)
-		try:
-			resref = Res.FSpOpenResFile(fss, 1)  # read-only
-		except:
-			return "unknown"
-		Res.UseResFile(resref)
-		i = Res.Count1Resources("sfnt")
-		Res.CloseResFile(resref)
-		if i > 0:
-			return "resource"
-	return "unknown"
-
-
-default_prefs = """\
-xmloutput:	":XML output"
-ttoutput:	":TrueType output"
-makesuitcases:	1
-"""
-
-def getprefs(path=PREFSFILENAME):
-	if not os.path.exists(path):
-		f = open(path, "w")
-		f.write(default_prefs)
-		f.close()
-	f = open(path)
-	lines = f.readlines()
-	prefs = {}
-	for line in lines:
-		if line[-1:] == "\n":
-			line = line[:-1]
-		try:
-			name, value = re.split(":", line, 1)
-			prefs[string.strip(name)] = eval(value)
-		except:
-			pass
-	return prefs
-
-
-class dummy_stdin:
-	def readline(self):
-		return ""
-sys.stdin = dummy_stdin()
-
-# redirect all output to a log file
-sys.stdout = sys.stderr = open(LOGFILENAME, "w", 0)  # unbuffered
-print "Starting TTX at " + time.ctime(time.time())
-
-# fire it up!
-ttx = TTX()
-ttx.mainloop()
-
-
-# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-# clues for BuildApplication/MacFreeze. 
-#
-# These modules somehow get imported, but we don't want/have them:
-#
-# macfreeze: exclude msvcrt
-# macfreeze: exclude W
-# macfreeze: exclude SOCKS
-# macfreeze: exclude TERMIOS
-# macfreeze: exclude termios
-# macfreeze: exclude icglue
-# macfreeze: exclude ce
-#
-# these modules are imported dynamically, so MacFreeze won't see them:
-#
-# macfreeze: include fontTools.ttLib.tables._c_m_a_p
-# macfreeze: include fontTools.ttLib.tables._c_v_t
-# macfreeze: include fontTools.ttLib.tables._f_p_g_m
-# macfreeze: include fontTools.ttLib.tables._g_a_s_p
-# macfreeze: include fontTools.ttLib.tables._g_l_y_f
-# macfreeze: include fontTools.ttLib.tables._h_d_m_x
-# macfreeze: include fontTools.ttLib.tables._h_e_a_d
-# macfreeze: include fontTools.ttLib.tables._h_h_e_a
-# macfreeze: include fontTools.ttLib.tables._h_m_t_x
-# macfreeze: include fontTools.ttLib.tables._k_e_r_n
-# macfreeze: include fontTools.ttLib.tables._l_o_c_a
-# macfreeze: include fontTools.ttLib.tables._m_a_x_p
-# macfreeze: include fontTools.ttLib.tables._n_a_m_e
-# macfreeze: include fontTools.ttLib.tables._p_o_s_t
-# macfreeze: include fontTools.ttLib.tables._p_r_e_p
-# macfreeze: include fontTools.ttLib.tables._v_h_e_a
-# macfreeze: include fontTools.ttLib.tables._v_m_t_x
-# macfreeze: include fontTools.ttLib.tables.L_T_S_H_
-# macfreeze: include fontTools.ttLib.tables.O_S_2f_2
-# macfreeze: include fontTools.ttLib.tables.T_S_I__0
-# macfreeze: include fontTools.ttLib.tables.T_S_I__1
-# macfreeze: include fontTools.ttLib.tables.T_S_I__2
-# macfreeze: include fontTools.ttLib.tables.T_S_I__3
-# macfreeze: include fontTools.ttLib.tables.T_S_I__5
-# macfreeze: include fontTools.ttLib.tables.C_F_F_
-
diff --git a/Mac/TTX.rsrc.hqx b/Mac/TTX.rsrc.hqx
deleted file mode 100644
index 0d396b5..0000000
--- a/Mac/TTX.rsrc.hqx
+++ /dev/null
@@ -1,68 +0,0 @@
-(This file must be converted with BinHex 4.0)
-:#&48@#jbFh*M!(*cFQ058d9%!3!!!!!!!!!-5eK3!!!!!!%!!!!+kJ!!#HS!!!&
-KJ'-!!%[qIYf!IJ!)5rjqr6KJ!!")!!!8J*m!!$Kr!!!)9&4B,R*cFQ0c,d0[FQp
-eG'PZCA0c8hPcC'9bC@eLC3),FR0bBe*6483"!!!f!-%!!!!!!!!!!!!!!!!!!!!
-!!!#dKq%9!!!!!!!!$%B!!$L&!'*,rr2T,!-!!%##!!`iB!!!5!!!J)!G!!JS!!!
-!3B)!1)!G!"3S!!!!3B)!*)"LLqJi!!!!N!!$!!#!I3!)JCd!&%J*2df!33!81m-
-!!$J!!!#3!"d!##`Hrrp!JJ!8J'+,f)"M!!",rRi*5!!!+#`H!!""JJ!31(i!!%[
-pE68!!!!39%9B9&)UBfJ!U$!a1$%!!!!!!!`%!!!!!!!!!3%"!!!!!!!"!!!!!!G
-"8&"-!!!!!!!#!2!!!!!!!!!!!!!!!!!!!!$r%4%4%4%4%4%4%4%4%4%Jrr)5%K)
-5%K)5%K)5%K)5X2rr)5%K)5%K)5%K)5%K)E$rrr)5%K)5%K)5%K)5%K+`rrrr)5%
-K)5%K)5%K)5%KX2rrrr)5'lX5%K)5%K)5%V$rrrrr)5rrS5%K)5rk)5'`rrrrmK+
-[rr)5%K,rra)5X2rrrb%Krrrl)5'rrrmK)E$rrr)5'rrrqK)Errrk%K+`rrmK)5V
-krrmK[rrrqL%KX2rb%K)DSUrrS[qUUU)5%V$r)5%K)5'rrrrk)5%K)5'`m4)5%K)
-5(rrrSK)5%K)5X!%K)5%K)5VrqL%K)5%K)E!"%K)5%K)DrrX5%K)5%K+`!5%K)5%
-K,rrk)5%Km5%KX!%5%K)5%[rrra)5%[m5%V!")5'a)5VkrrqUUb(rm5'`!4)DqK+
-[SUrrrrm5rrm5X!%K,rq[qb'rrrrl)IrrmE!"%Krrrl)5[rrrmK,rrrq`!5%[rrS
-K)5Vrrk%Krrrrm!%5(rqL%K)5rrS5%[rrrrm")5'l)5%K)5Ul)5(rrrr`!4)5%K)
-5%K)5%K)5rrrr!!%K)5%K)5%K)5%K)Irrm!!"%K)5%K)5%K)5%K,rr`!!!5%K)5%
-K)5%K)5%Krr!!!!+lZlZlZlZlZlZlZrm!!!!!!!!!!!!!!!!!!!$`!!!!!!!%!2m
-!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!rrm,#`X,#`X,#`X,#`X,#`X
-,#`X,#`X,#`X,#`X,%3$rrrm4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4&I!2r
-rrrm4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%9m!rrrrrrm4%4%4%4%4%4%4%4%
-4%4%4%4%4%4%4%4%4A`$rrrrrrrm4%4%4%4%4%4%4%4%4%4%4%4%4%4%4%4&I!2r
-rrrrrrrm4%4%4AepI04%4%4%4%4%4068e%4%4%9m!rrrrrrrrrrm4%6Arrrq*%4%
-4%4%4%6AJp)N4%4%4A`$rrrrrrrrr%4%4rIrrrrme%4%4%4%ehrrri"%4%4&I!2r
-rrrrrra%4%6Arrrrrrem4%4%4Arlrrrrr%4%4%9m!rrrrrrm4%4%4ArrrrrrrV4%
-4%9rrrrrrrrd4%4%4A`$rrrrr%4%4%4'*rrhrrrrJ04&Ii2rrrrrrV4%4%4&I!2r
-rra%4%4%4%Df*0Dhrrrq*%IlrVB1*rBNe%4%4%9m!rrm4%4%4%4%41c84Arrrrrr
-qrkde%4%4%4%4%4%4A`$r#a%4%4%4%4%4%4%er[rrrrq*04%4%4%4%4%4%4&I!!!
-,%4%4%4%4%4%4%4'YrrrrV684%4%4%4%4%4%4%9m!!!X4%4%4%4%4%4%4%BRrrrp
-I%4%4%4%4%4%4%4%4A`!!#a%4%4%4%4%4%4%er[rrriN4%4%4%4(r%4%4%4&I!!!
-,%4%4%4%4%4%40IlrrrrrrM84%4%4%Irr%4%4%9m!!!X4%4%eAc84%4'Yrkhqrrr
-rLB1YAa%4rrrr%4%4A`!!#a%4%BRdL4%eLIq$%BRrrrrrrrrq04(rrrrr%4&I!!!
-,%4%4r[rqVIlrAa%4Arrrrrrrrem4%Irrrrrr%9m!!!X4%6Arrrrrrem4%4&Irrr
-rrrrI04%4rrrrrrrrA`!!#a%40Irrrrq*%4%4%6@Yrrrrri-4%4(rrrrrrrrr!!!
-,%4%er[rrL4%4%4%4%6[rrrq*04%4%Irrrrrrrrrr!!X4%4%eAeme%4%4%4%4%6Z
-$Ac34%4%4rrrrrrrrr`!!#a%4%4%4%4%4%4%4%4%4%4%4%4%4%4(rrrrrrrm!!!!
-,%4%4%4%4%4%4%4%4%4%4%4%4%4%4%Irrrrrr!!!!!!X4%4%4%4%4%4%4%4%4%4%
-4%4%4%4%4rrrrr`!!!!!!#a%4%4%4%4%4%4%4%4%4%4%4%4%4%4(rrrm!!!!!!!!
-4AepIAepIAepIAepIAepIAepIAepIArrr!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!!!!!!!!!r`!!!!!!!!!!!!%!J!!!!2rrrrlJ!!!#m!!!![J!!!,m!!!#rK`!![m
-H!B,q2J2#r$m(`[Kr$m,`Iar#i'qrJX!2m!,!"q!#3!I!!N!(J!*!"i##3!r!`N)
-Ic1*(1rcb4r2mqNIMq2j(`IMq4i(`rd-!i2j!!!$m3!!!q%!!!2"!!!$JIrrr`!!
-!!)#!!!!!rrrrr[rrrrlrrrrqrrrrr[rrrrlrrrrqrrrrr[rrrrlrrrrqrrrrr[r
-rrrlrrrrqrrrrr[rrrrjrrrrqIrrrrRrrrrjrrrrqIrrrrRrrrrjrrrrqIrrrrRr
-rrrjrrrrrIrrrrRrrrrarrrriIrrrm(rrrq"rrrr!!!!!J!!!!%$rrm!"i!(h'IF
-jlhR,HB2"JiQAVEh[[Hqjci!1J!crq2rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr
-rrrlrr2ri!!!!J2%4%4%4%4%5rb%K)5%K)5[rmK)5%K)5'rrr+rSK,r%VrrmIra,
-rmK[rmErr,rra+rm5U[rrUU)Em5%KrrSK)5X5%K,rmK,b'a%V+[rkXImV%[rrVrr
-brrX4rrX[rr(rra,rXK,r%[rr%5%K)5%Krr!5%K)5%K,r!#ZlZlZlZr!!!!!"!2m
-,#`X,#`X,#`X,#`X,#a(rra%4%4%4%4%4%4%4%4&Irrrr%4%4%4%4%4%4%4%4Arr
-rrrm40Iq*%4%4Ar34%9rrrrrr09rrra%4r[rr%4&Irrrr%9rrrrmei2rrra%4Arr
-r%4'YLIrrrrqYrBN4%9rr%4%4%4(qrrq*%4%4%4&I#a%4%4%4r[rr%4%4ra%4A`X
-409m4VIrrriQY%Irr%9m,%Ilrr[pIrrrrrcArrrpI#a(rrrpI%BRrrpm4rrrrr`X
-4r[pI%4%lrrme%Irrrrm,%4%4%4%4%4%4%4(rrrm!#a%4%4%4%4%4%4%4rrm!!"&
-IAepIAepIAepIArm!!!!!!!!(+LSU+J!"!!!!!#48G&KU!!!!!8C548B!!3!!!)!
-!!3#"5801)`!"!!!!J!!"!!!!!!!+81!!HK)!!"EMB!!!!!T3i!#lI!!!I2`!!!!
-!#P$J!,Ym!!!@if!!!!!Z!3G!!!!!"Na88Lp54L"")'CbC@8J6'9dG'9bFQpb,e*
-[BQp'EfFJF(*[C(9MG!!!!%X""d!!!!!&-5i`B6Bq-5i`B6FJ,5"MEh"jFQPRD(3
-J-6Nj15db-$!`)%TeFh3JGQ&Z)&*[Fh0eE5`J6'9dG'9bFQpb,e*[BQp'EfF!!!%
-!!!!+kJ!!#HS!!!&K"Z5f9%Y)!!!!(!&5!!a(9EG*!!!!DP"[F(3!!!"f3Nj%6!!
-!!)*8G&KU!!!!MNC548B!!3#DD@0X0!!!!,**3diM!!!![QPMFb-!!!$+D@0c0!!
-!!0CTBh-i!!!!iRCPFR-!!3$Z8dPD43!#!3CTBf`i!!!"+LJ"rrm!!!!!!!!!!!$
-Prrm!!!!8!!!!!!#!rrm!!!NA!!!!!!!!!!!!!!!N!!!!!!#!rrm!!!!T!!!!!!#
-"rrm!!!N-!!!!!!#!rrm!!!!d!!!!!!#!rrm!!!Bm!!!!!!#!rrm!!!G!!!!!!!#
-!rrm!!!H%!!!!!!#!rrm!!!J)!!!!!!!"rrm!!!QE"Z5e,!!#rrm!!!PT"Z5e-2r
-rrrm!!!Nr"Z5e*!!"rrm!!!P0"Z5e+!!!rrm!!!PE"Z5e)!#!rrm!!!)i!!!!!!j
-2GfjPFL"bCA0[GA*MCHPB:
diff --git a/MetaTools/buildChangeLog.py b/MetaTools/buildChangeLog.py
index 2abdc1f..d29e379 100755
--- a/MetaTools/buildChangeLog.py
+++ b/MetaTools/buildChangeLog.py
@@ -6,5 +6,5 @@
 		os.path.join(os.getcwd(), sys.argv[0]))))
 
 os.chdir(fontToolsDir)
-os.system("svn2cl -o Doc/ChangeLog https://svn.code.sf.net/p/fonttools/code/trunk")
-print "done."
+os.system("git2cl > Doc/ChangeLog")
+print("done.")
diff --git a/MetaTools/buildTableList.py b/MetaTools/buildTableList.py
index bf2df69..1e77492 100755
--- a/MetaTools/buildTableList.py
+++ b/MetaTools/buildTableList.py
@@ -37,7 +37,7 @@
 file.write('\tdynamically imported. Generated by MetaTools/buildTableList.py.\n')
 file.write('\t"""\n')
 for module in modules:
-	file.write("\timport %s\n" % module)
+	file.write("\tfrom . import %s\n" % module)
 
 file.close()
 
diff --git a/MetaTools/build_otData.py b/MetaTools/build_otData.py
deleted file mode 100755
index a58c677..0000000
--- a/MetaTools/build_otData.py
+++ /dev/null
@@ -1,158 +0,0 @@
-#! /usr/bin/env python
-
-
-"""This script builds the Lib/fontTools/ttLib/tables/otData.py file
-from the OpenType HTML documentation. However, it depends on a slightly
-patched version the the HTML, as there are some inconsistencies in the
-markup and the naming of certain fields. See doco.diff for differences,
-but this is probably against a slightly older version of the documentation
-than what is currently online. The documentation was taken from this URL:
-	http://www.microsoft.com/typography/otspec/default.htm
-"""
-
-
-from sgmllib import SGMLParser
-
-
-class HTMLParser(SGMLParser):
-	
-	def __init__(self):
-		SGMLParser.__init__(self)
-		self.data = None
-		self.currenttable = None
-		self.lastcaption = None
-	
-	def handle_data(self, data):
-		if self.data is not None:
-			self.data.append(data)
-	
-	def start_i(self, attrs):
-		if self.currenttable is None:
-			self.data = []
-	def end_i(self):
-		if self.currenttable is None:
-			self.lastcaption = " ".join(self.data)
-			self.data = None
-	
-	def start_b(self, attrs):
-		if self.currenttable is None:
-			self.data = []
-	def end_b(self):
-		if self.currenttable is None:
-			self.lastcaption = " ".join(self.data)
-			self.data = None
-	
-	def start_table(self, attrs):
-		attrs = dict(attrs)
-		if attrs.get('width') in ('455', '460'):
-			#print "---", attrs
-			self.currenttable = []
-		else:
-			self.currenttable = None
-	def end_table(self):
-		if self.currenttable is not None and self.lastcaption is not None:
-			if self.currenttable[0] == ['Type', 'Name', 'Description'] or \
-					self.currenttable[0] == ['Value', 'Type', 'Description']:
-				caption = self.lastcaption.split()
-				name = caption[0]
-				if name == "LookupType" or name == "LookupFlag":
-					self.currenttable = None
-					return
-				elif name == "Device":
-					if "Tables" in caption:
-						# XXX skip this one
-						self.currenttable = None
-						return
-				buildTable(name, self.currenttable[1:], self.lastcaption)
-		self.currenttable = None
-	
-	def start_tr(self, attrs):
-		if self.currenttable is not None:
-			self.currenttable.append([])
-	def end_tr(self):
-		pass
-	
-	def start_td(self, attrs):
-		self.data = []
-	def end_td(self):
-		if self.currenttable is not None and self.data is not None:
-			self.currenttable[-1].append(" ".join(self.data))
-			self.data = None
-
-
-globalDups = {}
-localDups = {}
-not3 = []
-
-def buildTable(name, table, caption):
-	if globalDups.has_key(name):
-		globalDups[name].append(caption)
-	else:
-		globalDups[name] = [caption]
-	print "\t(%s, [" % repr(name)
-	allFields = {}
-	for row in table:
-		row = [" ".join(x.split()) for x in row]
-		if len(row) <> 3:
-			not3.append(row)
-		row = makeRow(row)
-		fieldName = row[1]
-		if allFields.has_key(fieldName):
-			key = (name, fieldName)
-			localDups[key] = 1
-		allFields[fieldName] = 1
-		print "\t\t%s," % (tuple(row),)
-	print "\t]),"
-	print
-
-
-def makeRow(rawRow):
-	tp, name = rawRow[:2]
-	name = name.strip()
-	rest = tuple(rawRow[2:])
-	if '[' in name:
-		name, repeat = name.split("[")
-		name = name.strip()
-		assert repeat[-1] == "]"
-		repeat = repeat[:-1].split()
-		if repeat[1:]:
-			repeatOffset = int("".join(repeat[1:]))
-		else:
-			repeatOffset = 0
-		if not repeat:
-			repeat = ""
-		else:
-			repeat = repeat[0]
-	else:
-		repeat = None
-		repeatOffset = None
-	row = (tp, name, repeat, repeatOffset) + rest
-	return row
-
-
-if __name__ == "__main__":
-	import sys, os
-	if "-" not in sys.argv:
-		sys.stdout = open("otData.py", "w")
-	print "otData = ["
-	for file in ["chapter2.htm", "gpos.htm", "gsub.htm", "gdef.htm", "base.htm", "jstf.htm"]:
-		name = os.path.splitext(file)[0]
-		if name == "chapter2":
-			name = "common"
-		print
-		print "\t#"
-		print "\t# %s (generated from %s)" % (name, file)
-		print "\t#"
-		print 
-		p = HTMLParser()
-		p.feed(open(file).read())
-		p.close()
-	print "]"
-	print
-	for k, v in globalDups.items():
-		if len(v) > 1:
-			print "# XXX duplicate table name:", k, v
-	for (name, fieldName), v in localDups.items():
-		print "# XXX duplicate field name '%s' in table '%s'" % (fieldName, name)
-	for n in not3:
-		print "#XXX", not3
diff --git a/MetaTools/doco.diff b/MetaTools/doco.diff
deleted file mode 100644
index 007ec28..0000000
--- a/MetaTools/doco.diff
+++ /dev/null
@@ -1,108 +0,0 @@
---- htmlorig/gpos.htm   Fri Apr  5 23:55:58 2002
-+++ htmlbak/gpos.htm    Tue May  7 09:53:30 2002
-@@ -270,7 +270,7 @@
- 
- <P>Example 2 at the end of this chapter shows a SinglePosFormat1 subtable used to adjust the placement of subscript glyphs.
- 
--<P>SinglePosFormat1 subtable: Single positioning value
-+<P><I>SinglePosFormat1 subtable: Single positioning value</I>
- <P>
- 
- <TABLE BGCOLOR="#F0F0F0" WIDTH=460 BORDER=0 CELLPADDING=3>
-@@ -312,7 +312,7 @@
- 
- <P>Example 3 at the end of this chapter shows how to adjust the spacing of three dash glyphs with a SinglePosFormat2 subtable.
- 
--<P>SinglePosFormat2 subtable: Array of positioning values
-+<P><I>SinglePosFormat2 subtable: Array of positioning values</I>
- <P>
- 
- <TABLE BGCOLOR="#F0F0F0" WIDTH=460 BORDER=0 CELLPADDING=3>
-@@ -392,8 +392,8 @@
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>PairSetCount</TD>
- <TD CLASS=tab>Number of PairSet tables</TD></TR>
--<TR><TD CLASS=tab VALIGN=TOP>ValueRecord</TD>
--<TD CLASS=tab VALIGN=TOP>PairSet<BR>[Offset]</TD>
-+<TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
-+<TD CLASS=tab VALIGN=TOP>PairSet<BR>[PairSetCount]</TD>
- <TD CLASS=tab>Array of offsets to PairSet tables-from beginning of PairPos subtable-ordered by Coverage Index</TD></TR>
- </TABLE>
- 
-@@ -855,7 +855,8 @@
- <TD CLASS=tab>Offset to Base Mark Coverage table-from beginning of MarkMarkPos subtable</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>ClassCount</TD>
--<TD CLASS=tab>Number of Combining Mark classes defined<TR>
-+<TD CLASS=tab>Number of Combining Mark classes defined</TD>
-+<TR>
- <TD CLASS=tab VALIGN=TOP>Offset</TD>
- <TD CLASS=tab VALIGN=TOP>Mark1Array</TD>
- <TD CLASS=tab VALIGN=TOP>Offset to MarkArray table for Mark1-from beginning of MarkMarkPos subtable</TD></TR>
-@@ -1386,19 +1387,19 @@
- <TD CLASS=tab VALIGN=TOP>BacktrackGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in the backtracking sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[BacktrackGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>BacktrackCoverage[BacktrackGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in backtracking sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>InputGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in input sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[InputGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>InputCoverage[InputGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in input sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>LookaheadGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in lookahead sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[LookaheadGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>LookaheadCoverage[LookaheadGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in lookahead sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>PosCount</TD>
-diff -u htmlorig/gsub.htm htmlbak/gsub.htm
---- htmlorig/gsub.htm   Fri Apr  5 23:55:58 2002
-+++ htmlbak/gsub.htm    Tue May  7 09:53:17 2002
-@@ -758,7 +758,7 @@
- 
- <A HREF="#EX9"><P>Example 9</A> at the end of this chapter substitutes swash glyphs for two out of three glyphs in a sequence. 
- 
--<P><BR><I>ChainContextSubstFormat3 subtable: Coverage-based context glyph substitution</I><P>
-+<P><BR><I>ContextSubstFormat3 subtable: Coverage-based context glyph substitution</I><P>
- 
- <TABLE BGCOLOR="#F0F0F0" WIDTH=460 BORDER=0 CELLPADDING=3>
- <TR>
-@@ -880,7 +880,7 @@
- <TD CLASS=tab VALIGN=TOP>LookaheadGlyphCount</TD>
- <TD CLASS=tab>Total number of glyphs in the look ahead sequence (number of glyphs to be matched after the input sequence)</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>GlyphID</TD>
--<TD CLASS=tab VALIGN=TOP>LookAhead<BR>[LookAheadGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>Lookahead<BR>[LookAheadGlyphCount]</TD>
- <TD CLASS=tab VALIGN=TOP>Array of lookahead GlyphID's (to be matched after the input sequence)</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>SubstCount</TD>
-@@ -1023,19 +1023,19 @@
- <TD CLASS=tab VALIGN=TOP>BacktrackGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in the backtracking sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[BacktrackGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>BacktrackCoverage[BacktrackGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in backtracking sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>InputGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in input sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[InputGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>InputCoverage[InputGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in input sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>LookaheadGlyphCount</TD>
- <TD CLASS=tab>Number of glyphs in lookahead sequence</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>Offset</TD>
--<TD CLASS=tab VALIGN=TOP>Coverage[LookaheadGlyphCount]</TD>
-+<TD CLASS=tab VALIGN=TOP>LookaheadCoverage[LookaheadGlyphCount]</TD>
- <TD CLASS=tab>Array of offsets to coverage tables in lookahead sequence, in glyph sequence order</TD></TR>
- <TR><TD CLASS=tab VALIGN=TOP>uint16</TD>
- <TD CLASS=tab VALIGN=TOP>SubstCount</TD>
diff --git a/MetaTools/roundTrip.py b/MetaTools/roundTrip.py
index 9438f0b..122b39b 100755
--- a/MetaTools/roundTrip.py
+++ b/MetaTools/roundTrip.py
@@ -25,7 +25,7 @@
 
 
 def usage():
-	print __doc__
+	print(__doc__)
 	sys.exit(2)
 
 
@@ -46,7 +46,7 @@
 		diffcmd = 'diff -U2 -I ".*modified value\|checkSumAdjustment.*" "%s" "%s"' % (xmlFile1, xmlFile2)
 		output = os.popen(diffcmd, "r", 1)
 		lines = []
-		while 1:
+		while True:
 			line = output.readline()
 			if not line:
 				break
@@ -58,7 +58,7 @@
 			report.write("-------------------------------------------------------------\n")
 			report.writelines(lines)
 		else:
-			print "(TTX files are the same)"
+			print("(TTX files are the same)")
 	finally:
 		for tmpFile in (xmlFile1, ttFile2, xmlFile2):
 			if os.path.exists(tmpFile):
@@ -80,10 +80,10 @@
 		try:
 			roundTrip(ttFile, options, report)
 		except KeyboardInterrupt:
-			print "(Cancelled)"
+			print("(Cancelled)")
 			break
 		except:
-			print "*** round tripping aborted ***"
+			print("*** round tripping aborted ***")
 			traceback.print_exc()
 			report.write("=============================================================\n")
 			report.write("  An exception occurred while round tripping")
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c2f7d0c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,34 @@
+### What it is ?
+
+Quoting from [TTX/FontTools Sourceforge Project](http://sourceforge.net/projects/fonttools/) 
+> a tool to convert OpenType and TrueType fonts to and from XML. FontTools is a library for manipulating fonts, written in Python. It supports TrueType, OpenType, AFM and to an extent Type 1 and some Mac-specific formats.   
+
+### Quick start
+
+```python setup.py install```
+
+From your command line type the above command to get fontools installed on your system.
+
+### Installation
+
+See [install.txt](https://github.com/behdad/fonttools/blob/master/Doc/install.txt) in the 'Doc' subdirectory for instructions on how to build and install TTX/FontTools from the sources.
+
+
+### Documentation
+
+#### What is TTX ?
+
+See [documentation.html](https://github.com/behdad/fonttools/blob/master/Doc/documentation.html) in the "Doc" subdirectory for TTX usage instructions and information about the TTX file format.
+
+### Community
+* https://groups.google.com/d/forum/fonttools
+
+### License
+
+See "LICENSE.txt" for licensing information.
+
+
+
+Have fun!
+
+Just van Rossum <just@letterror.com>
diff --git a/README.txt b/README.txt
deleted file mode 100644
index 019a547..0000000
--- a/README.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-See the file "documentation.html" in the "Doc" subdirectory for TTX
-usage instructions and information about the TTX file format.
-
-See the file "install.txt" in the "Doc" subdirectory for instructions
-how to build and install TTX/FontTools from the sources.
-
-Quick start: run python setup.py install from the command line.
-
-See the file "LICENSE.txt" for licensing info.
-
-Have fun!
-
-Just van Rossum <just@letterror.com>
diff --git a/Src/eexecOp/README.txt b/Src/eexecOp/README.txt
deleted file mode 100644
index 76c2506..0000000
--- a/Src/eexecOp/README.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-eexecOp is imported by the fontTools.misc.eexec module, and the latter
-provides a (slow) Python implementation in case eexecOp isn't there.
-It is designed to be a shared library, to be placed in 
-    FontTools/Lib/fontTools/misc/
-but it should also work as a (possibly statically linked) top level module.
-
-It is built automatically when you run
-
-   python setup.py build
-or
-   python setup.py install
-
-in the top level FontTools directory.
-
-Just
diff --git a/Src/eexecOp/eexecOpmodule.c b/Src/eexecOp/eexecOpmodule.c
deleted file mode 100644
index 3befda7..0000000
--- a/Src/eexecOp/eexecOpmodule.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
-**  Copyright 1996-2001 by Letterror: Just van Rossum, The Netherlands.
-**	
-**  Open source.
-**
-**  Module implementing the eexec and charstring encryption algorithm as 
-**  used by PostScript Type 1 fonts.
-**	
-*/
-
-#include "Python.h"
-#include <ctype.h>
-
-static PyObject *ErrorObject;
-
-/* ----------------------------------------------------- */
-
-static char eexec_decrypt__doc__[] =
-""
-;
-
-static PyObject *
-eexec_decrypt(PyObject *self, PyObject *args)
-{
-	PyObject *_res = NULL;
-	unsigned short R;
-	int tempR;  /* can't portably use unsigned shorts between Python versions */
-	unsigned short c1 = 52845;
-	unsigned short c2 = 22719;
-	unsigned char * inbuf;
-	unsigned char * outbuf;
-	unsigned long counter, insize;
-	
-	if (!PyArg_ParseTuple(args, "s#i", &inbuf, &insize, &tempR))
-		return NULL;
-	
-	R = (unsigned short)tempR;
-	
-	if ((outbuf = malloc(insize)) == NULL)
-	{
-		PyErr_NoMemory();
-		return NULL;
-	}
-	for(counter = 0;counter < insize; counter++) {
-		outbuf[counter] = (inbuf[counter] ^ (R>>8));
-		R = (inbuf[counter] + R) * c1 + c2;
-	}
-	
-	_res = Py_BuildValue("s#l", outbuf, insize, (unsigned long)R);
-	free(outbuf);
-	return _res;
-}
-
-static char eexec_encrypt__doc__[] =
-""
-;
-
-static PyObject *
-eexec_encrypt(PyObject *self, PyObject *args)
-{
-	PyObject *_res = NULL;
-	unsigned short R;
-	int tempR;  /* can't portably use unsigned shorts between Python versions */
-	unsigned short c1 = 52845;
-	unsigned short c2 = 22719;
-	unsigned char * inbuf;
-	unsigned char * outbuf;
-	unsigned long counter, insize;
-	
-	if (!PyArg_ParseTuple(args, "s#i", &inbuf, &insize, &tempR))
-		return NULL;
-	
-	R = (unsigned short)tempR;
-	
-	if ((outbuf = malloc(insize)) == NULL)
-	{
-		PyErr_NoMemory();
-		return NULL;
-	}
-	for(counter = 0;counter < insize; counter++) {
-		outbuf[counter] = (inbuf[counter] ^ (R>>8));
-		R = (outbuf[counter] + R) * c1 + c2;
-	}
-	
-	_res = Py_BuildValue("s#l", outbuf, insize, (unsigned long)R);
-	free(outbuf);
-	return _res;
-}
-
-static char eexec_hexString__doc__[] =
-""
-;
-
-static PyObject *
-eexec_hexString(PyObject *self, PyObject *args)
-{
-	PyObject *_res = NULL;
-	unsigned char * inbuf;
-	unsigned char * outbuf;
-	static const unsigned char hexchars[] = "0123456789ABCDEF";
-	unsigned long i, insize;
-	
-	if (!PyArg_ParseTuple(args, "s#", &inbuf, &insize))
-		return NULL;
-	
-	outbuf = malloc(2 * insize);
-	if (outbuf == NULL) {
-		PyErr_NoMemory();
-		return NULL;
-	}
-	
-	for (i = 0; i < insize; i++) {
-		outbuf[2 * i] = hexchars[(inbuf[i] >> 4) & 0xF];
-		outbuf[2 * i + 1] = hexchars[inbuf[i] & 0xF];
-	}
-	_res = Py_BuildValue("s#", outbuf, 2 * insize);
-	free(outbuf);
-	return _res;
-}
-
-
-#define HEX2DEC(c) ((c) >= 'A' ? ((c) - 'A' + 10) : ((c) - '0'))
-
-static char eexec_deHexString__doc__[] =
-""
-;
-
-static PyObject *
-eexec_deHexString(PyObject *self, PyObject *args)
-{
-	PyObject *_res = NULL;
-	unsigned char * inbuf;
-	unsigned char * outbuf;
-	unsigned char c1, c2;
-	unsigned long insize, i;
-	
-	if (!PyArg_ParseTuple(args, "s#", &inbuf, &insize))
-		return NULL;
-	
-	if (insize % 2) {
-		PyErr_SetString(ErrorObject, "hex string must have even length");
-		return NULL;
-	}
-	
-	outbuf = malloc(insize / 2);
-	if (outbuf == NULL) {
-		PyErr_NoMemory();
-		return NULL;
-	}
-	
-	for ( i = 0; i < insize; i += 2) {
-		c1 = toupper(inbuf[i]);
-		c2 = toupper(inbuf[i+1]);
-		if (!isxdigit(c1) || !isxdigit(c1)) {
-			PyErr_SetString(ErrorObject, "non-hex character found");
-			goto error;
-		}
-		outbuf[i/2] = (HEX2DEC(c2)) | (HEX2DEC(c1) << 4);
-	}
-	_res = Py_BuildValue("s#", outbuf, insize / 2);
-error:
-	free(outbuf);
-	return _res;
-}
-
-/* List of methods defined in the module */
-
-static struct PyMethodDef eexec_methods[] = {
-	{"decrypt",	(PyCFunction)eexec_decrypt,		METH_VARARGS,	eexec_decrypt__doc__},
-	{"encrypt",	(PyCFunction)eexec_encrypt,		METH_VARARGS,	eexec_encrypt__doc__},
- 	{"hexString",	(PyCFunction)eexec_hexString,		METH_VARARGS,	eexec_hexString__doc__},
-	{"deHexString",	(PyCFunction)eexec_deHexString,	METH_VARARGS,	eexec_deHexString__doc__},
-	{NULL, (PyCFunction)NULL, 0, NULL}		/* sentinel */
-};
-
-
-/* Initialization function for the module (*must* be called initeexec) */
-
-static char eexec_module_documentation[] = 
-""
-;
-
-void initeexecOp(void); /* prototype to shut up the compiler */
-
-void initeexecOp(void)
-{
-	PyObject *m, *d;
-
-	/* Create the module and add the functions */
-	m = Py_InitModule4("eexecOp", eexec_methods,
-		eexec_module_documentation,
-		(PyObject*)NULL,PYTHON_API_VERSION);
-
-	/* Add some symbolic constants to the module */
-	d = PyModule_GetDict(m);
-	ErrorObject = PyString_FromString("eexec.error");
-	PyDict_SetItemString(d, "error", ErrorObject);
-
-	/* Check for errors */
-	if (PyErr_Occurred())
-		Py_FatalError("can't initialize module eexec");
-}
-
diff --git a/Tools/fontTools b/Tools/fontTools
new file mode 120000
index 0000000..9a21c02
--- /dev/null
+++ b/Tools/fontTools
@@ -0,0 +1 @@
+../Lib/fontTools
\ No newline at end of file
diff --git a/Windows/README.TXT b/Windows/README.TXT
index b16b80d..13f1971 100644
--- a/Windows/README.TXT
+++ b/Windows/README.TXT
@@ -15,7 +15,7 @@
 3. Install InnoSetup 4: http://www.jrsoftware.org/
 4. Download the latest released source code of TTX/FontTools at
    http://sourceforge.net/projects/fonttools/
-   Or alternatively grab the sources from SVN:
+   Or alternatively grab the sources from the VCS:
    http://fonttools.sourceforge.net/
 5. Unzip the source code of TTX/FontTools into a folder.
 6. In the folder where you unzipped TTX/FontTools, type:
@@ -36,7 +36,7 @@
 5. Put UPX somewhere within your PATH: http://upx.sourceforge.net/
 6. Download the latest released source code of TTX/FontTools at
    http://sourceforge.net/projects/fonttools/
-   Or alternatively grab the sources from SVN:
+   Or alternatively grab the sources from the VCS:
    http://fonttools.sourceforge.net/
 7. Unzip the source code of TTX/FontTools into a folder.
 8. In the folder where you unzipped TTX/FontTools, type:
diff --git a/setup.py b/setup.py
index e37bf26..b3ca286 100755
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,6 @@
 #! /usr/bin/env python
 
+from __future__ import print_function
 import os, sys
 from distutils.core import setup, Extension
 from distutils.command.build_ext import build_ext
@@ -13,8 +14,8 @@
 try:
 	import xml.parsers.expat
 except ImportError:
-	print "*** Warning: FontTools needs PyXML, see:"
-	print "        http://sourceforge.net/projects/pyxml/"
+	print("*** Warning: FontTools needs PyXML, see:")
+	print("        http://sourceforge.net/projects/pyxml/")
 
 
 class build_ext_optional(build_ext):
@@ -70,29 +71,16 @@
 		long_description = long_description,
 		
 		packages = [
-			"",
 			"fontTools",
 			"fontTools.encodings",
 			"fontTools.misc",
 			"fontTools.pens",
 			"fontTools.ttLib",
 			"fontTools.ttLib.tables",
-			"fontTools.ttLib.test",
 		],
 		package_dir = {'': 'Lib'},
 		extra_path = 'FontTools',
-		ext_modules = [
-			Extension(
-				"fontTools.misc.eexecOp",
-				["Src/eexecOp/eexecOpmodule.c"],
-				include_dirs=[],
-				define_macros=[],
-				library_dirs=[],
-				libraries=[],
-			)
-		],
 		scripts = ["Tools/ttx", "Tools/pyftsubset", "Tools/pyftinspect"],
-		console = ["Tools/ttx", "Tools/pyftsubset"],
 		cmdclass = {"build_ext": build_ext_optional},
 		data_files = [('share/man/man1', ["Doc/ttx.1"])],
 		**classifiers