Merge pull request #50 from behdad/2and3

Fully functioning Python 2 and 3
diff --git a/Lib/fontTools/afmLib.py b/Lib/fontTools/afmLib.py
index aa303ad..ac31563 100644
--- a/Lib/fontTools/afmLib.py
+++ b/Lib/fontTools/afmLib.py
@@ -4,9 +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 $"
 
@@ -83,7 +83,7 @@
 class error(Exception): pass
 
 
-class AFM:
+class AFM(object):
 	
 	_attrs = None
 	
@@ -112,15 +112,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 +135,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 +172,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))
@@ -195,7 +195,7 @@
 		import time
 		lines = [	"StartFontMetrics 2.0",
 				"Comment Generated by afmLib, version %s; at %s" % 
-						(string.split(__version__)[2],
+						(__version__.split()[2],
 						time.strftime("%m/%d/%Y %H:%M:%S", 
 						time.localtime(time.time())))]
 		
@@ -208,35 +208,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 +240,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 +262,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 +283,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 +301,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 +317,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 +325,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 +349,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 +366,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 c56ab81..597df7d 100644
--- a/Lib/fontTools/agl.py
+++ b/Lib/fontTools/agl.py
@@ -1,6 +1,9 @@
 # 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 2003, 2005-2008, 2010 Adobe Systems Incorporated.
@@ -719,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 54c9cf2..9265ebc 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):
@@ -80,7 +81,7 @@
 	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)
@@ -94,7 +95,7 @@
 		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 +114,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 +137,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 +156,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 +180,7 @@
 	return offSize
 
 
-class IndexCompiler:
+class IndexCompiler(object):
 	
 	def __init__(self, items, strings, parent):
 		self.items = self.getItems(items, strings)
@@ -224,7 +226,7 @@
 			if hasattr(item, "toFile"):
 				item.toFile(file)
 			else:
-				file.write(item)
+				file.write(tobytes(item))
 
 
 class IndexedStringsCompiler(IndexCompiler):
@@ -301,7 +303,7 @@
 		self.parent.rawDict["CharStrings"] = pos
 
 
-class Index:
+class Index(object):
 	
 	"""This class represents what the CFF spec calls an INDEX."""
 	
@@ -313,7 +315,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 +325,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 +337,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 +402,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 +442,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)
 
 
@@ -497,7 +500,7 @@
 		self.gidArray.append(fdSelectValue)
 	
 
-class CharStrings:
+class CharStrings(object):
 	
 	def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
 		if file is not None:
@@ -517,16 +520,16 @@
 				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 +559,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 +580,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 +601,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 +636,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 +662,7 @@
 	return d
 
 
-class SimpleConverter:
+class SimpleConverter(object):
 	def read(self, parent, value):
 		return value
 	def write(self, parent, value):
@@ -668,18 +670,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 Latin1Converter(SimpleConverter):
+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):
-		# Store as UTF-8
-		value = unicode(value, "latin-1").encode("utf-8")
-		xmlWriter.simpletag(name, value=value)
+		xmlWriter.simpletag(name, value=tostr(value, encoding="ascii"))
 		xmlWriter.newline()
-	def xmlRead(self, (name, attrs, content), parent):
-		s = unicode(attrs["value"], "utf-8")
-		return s.encode("latin-1")
+	def xmlRead(self, name, attrs, content, parent):
+		return tobytes(attrs["value"], encoding=("ascii"))
+
+class Latin1Converter(SimpleConverter):
+	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):
@@ -690,17 +704,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):
@@ -709,12 +723,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):
@@ -757,7 +772,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
@@ -765,10 +780,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:
@@ -776,7 +791,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)
@@ -786,9 +801,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:
@@ -807,12 +822,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'
@@ -851,7 +866,7 @@
 
 	for name in charset[1:]:
 		data.append(packCard16(getNameID(name,strings)))
-	return "".join(data)
+	return bytesjoin(data)
 
 
 def packCharset(charset, isCID, strings):
@@ -868,7 +883,7 @@
 		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
@@ -887,14 +902,14 @@
 		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)
@@ -913,7 +928,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])
@@ -921,7 +936,7 @@
 	return charset
 
 
-class EncodingCompiler:
+class EncodingCompiler(object):
 
 	def __init__(self, strings, encoding, parent):
 		assert not isinstance(encoding, basestring)
@@ -955,11 +970,11 @@
 			file = parent.file
 			file.seek(value)
 			if DEBUG:
-				print "loading Encoding at %s" % value
+				print("loading Encoding at %s" % value)
 			format = readCard8(file)
 			haveSupplement = format & 0x80
 			if haveSupplement:
-				raise NotImplementedError, "Encoding supplements are not yet supported"
+				raise NotImplementedError("Encoding supplements are not yet supported")
 			format = format & 0x7f
 			if format == 0:
 				encoding = parseEncoding0(parent.charset, file, haveSupplement,
@@ -991,8 +1006,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:
@@ -1047,7 +1062,7 @@
 		if code is None:
 			code = 0
 		data.append(packCard8(code))
-	return "".join(data)
+	return bytesjoin(data)
 
 def packEncoding1(charset, encoding, strings):
 	format = 1
@@ -1063,7 +1078,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
@@ -1080,7 +1095,7 @@
 		if first == -1:  # unencoded
 			first = 0
 		data.append(packCard8(first) + packCard8(nLeft))
-	return "".join(data)
+	return bytesjoin(data)
 
 
 class FDArrayConverter(TableConverter):
@@ -1096,16 +1111,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
@@ -1122,7 +1138,7 @@
 		xmlWriter.simpletag(name, [('format', value.format)])
 		xmlWriter.newline()
 
-	def xmlRead(self, (name, attrs, content), parent):
+	def xmlRead(self, name, attrs, content, parent):
 		format = safeEval(attrs["format"])
 		file = None
 		numGlyphs = None
@@ -1135,7 +1151,7 @@
 	data = [packCard8(format)]
 	for index in fdSelectArray:
 		data.append(packCard8(index))
-	return "".join(data)
+	return bytesjoin(data)
 
 
 def packFDSelect3(fdSelectArray):
@@ -1158,10 +1174,10 @@
 		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
@@ -1197,11 +1213,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']))
 
 
@@ -1282,7 +1298,7 @@
 		elif arg == "number":
 			conv = NumberConverter()
 		elif arg == "SID":
-			conv = SimpleConverter()
+			conv = ASCIIConverter()
 		else:
 			assert 0
 		table[i] = op, name, arg, default, conv
@@ -1299,7 +1315,7 @@
 	operators = buildOperatorDict(privateDictOperators)
 
 
-class DictCompiler:
+class DictCompiler(object):
 	
 	def __init__(self, dictObj, strings, parent):
 		assert isinstance(strings, IndexedStrings)
@@ -1326,8 +1342,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:
@@ -1347,7 +1363,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"))
@@ -1360,7 +1376,7 @@
 		data = []
 		for num in value:
 			data.append(encodeNumber(num))
-		return "".join(data)
+		return bytesjoin(data)
 	def arg_delta(self, value):
 		out = []
 		last = 0
@@ -1370,7 +1386,7 @@
 		data = []
 		for num in out:
 			data.append(encodeNumber(num))
-		return "".join(data)
+		return bytesjoin(data)
 
 
 def encodeNumber(num):
@@ -1455,12 +1471,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
@@ -1468,7 +1484,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()
@@ -1485,7 +1501,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)
@@ -1501,9 +1517,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)
 
 
@@ -1549,7 +1565,7 @@
 			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)
@@ -1587,7 +1603,7 @@
 	compilerClass = PrivateDictCompiler
 
 
-class IndexedStrings:
+class IndexedStrings(object):
 	
 	"""SID -> string mapping."""
 	
@@ -1595,7 +1611,7 @@
 		if file is None:
 			strings = []
 		else:
-			strings = list(Index(file))
+			strings = [tostr(s) for s in Index(file)]
 		self.strings = strings
 	
 	def getCompiler(self):
@@ -1613,9 +1629,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
index 74ae13b..c104d9e 100644
--- a/Lib/fontTools/fondLib.py
+++ b/Lib/fontTools/fondLib.py
@@ -1,11 +1,11 @@
-import os
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-import string
 try:
 	from Carbon import Res
 except ImportError:
 	import Res
+import struct
 
 
 error = "fondLib.error"
@@ -29,12 +29,12 @@
 
 FONDheadersize = 52
 
-class FontFamily:
+class FontFamily(object):
 	
 	def __init__(self, theRes, mode = 'r'):
 		self.ID, type, self.name = theRes.GetResInfo()
-		if type <> 'FOND':
-			raise ValueError, "FOND resource required"
+		if type != 'FOND':
+			raise ValueError("FOND resource required")
 		self.FOND = theRes
 		self.mode = mode
 		self.changed = 0
@@ -68,8 +68,8 @@
 		self.fondClass = flags
 	
 	def save(self, destresfile = None):
-		if self.mode <> 'w':
-			raise error, "can't save font: no write permission"
+		if self.mode != 'w':
+			raise error("can't save font: no write permission")
 		self._buildfontassociationtable()
 		self._buildoffsettable()
 		self._buildboundingboxtable()
@@ -139,13 +139,13 @@
 	
 	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(*(">9h",) + self.ffProperty)
+		header = header + 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)
+			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):
@@ -169,9 +169,9 @@
 			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)
+			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):
@@ -192,7 +192,7 @@
 	
 	def _getboundingboxtable(self):
 		self.boundingBoxes = None
-		if self._rawoffsettable[:6] <> '\0\0\0\0\0\6':  # XXX ????
+		if self._rawoffsettable[:6] != '\0\0\0\0\0\6':  # XXX ????
 			return
 		boxes = {}
 		data = self._rawoffsettable[6:]
@@ -206,8 +206,7 @@
 	
 	def _buildboundingboxtable(self):
 		if self.boundingBoxes and self._rawoffsettable[:6] == '\0\0\0\0\0\6':
-			boxes = self.boundingBoxes.items()
-			boxes.sort()
+			boxes = sorted(self.boundingBoxes.items())
 			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)
@@ -241,16 +240,15 @@
 			return
 		numberofentries = len(self.widthTables)
 		data = struct.pack('>h', numberofentries - 1)
-		tables = self.widthTables.items()
-		tables.sort()
+		tables = sorted(self.widthTables.items())
 		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"
+			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.'
+			print("glyph width table is the same?", self._rawglyphwidthtable == data and 'yes.' or 'no.')
 		self._rawglyphwidthtable = data
 	
 	def _getkerningtables(self):
@@ -270,7 +268,7 @@
 			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))
+				kerntable.append((byteord(firstchar), byteord(secondchar), kerndistance))
 				count = count + 4
 		
 		if DEBUG:
@@ -284,20 +282,19 @@
 			return
 		numberofentries = len(self.kernTables)
 		data = [struct.pack('>h', numberofentries - 1)]
-		tables = self.kernTables.items()
-		tables.sort()
+		tables = sorted(self.kernTables.items())
 		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.append(struct.pack(">cch", bytechr(firstchar), bytechr(secondchar), kerndistance))
 		
-		data = string.join(data, '')
+		data = bytesjoin(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)
+			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):
@@ -316,7 +313,7 @@
 		
 		count = offset + 60
 		for i in range(stringcount):
-			str_len = ord(data[count])
+			str_len = byteord(data[count])
 			self.styleStrings.append(data[count + 1:count + 1 + str_len])
 			count = count + 1 + str_len
 		
@@ -337,18 +334,18 @@
 					self.styleMappingReserved)
 		
 		self._packstylestrings()
-		data = data + apply(struct.pack, (">48b",) + self.styleIndices)
+		data = data + 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
+			data = data + bytechr(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.'
+			print("style mapping table is the same?", self._rawstylemappingtable == data and 'yes.' or 'no.')
 		self._rawstylemappingtable = data
 	
 	def _unpackstylestrings(self):
@@ -360,7 +357,7 @@
 				psNames[i] = self.styleStrings[0]
 			else:
 				style = self.styleStrings[0]
-				codes = map(ord, self.styleStrings[index - 1])
+				codes = map(byteord, self.styleStrings[index - 1])
 				for code in codes:
 					style = style + self.styleStrings[code - 1]
 				psNames[i] = style
@@ -375,10 +372,8 @@
 			for part in split:
 				nameparts[part] = None
 		del nameparts[self.ffFamilyName]
-		nameparts = nameparts.keys()
-		nameparts.sort()
-		items = splitnames.items()
-		items.sort()
+		nameparts = sorted(nameparts.keys())
+		items = sorted(splitnames.items())
 		numindices = 0
 		for style, split in items:
 			if len(split) > 1:
@@ -391,7 +386,7 @@
 				continue
 			indices = ""
 			for part in split[1:]:
-				indices = indices + chr(nameparts.index(part) + numindices + 2)
+				indices = indices + bytechr(nameparts.index(part) + numindices + 2)
 			styleStrings[self.styleIndices[style] - 1] = indices
 		self.styleStrings = styleStrings
 	
@@ -415,9 +410,9 @@
 		numberofentries, = struct.unpack(">h", data[offset:offset+2])
 		count = offset + 2
 		for i in range(numberofentries):
-			glyphcode = ord(data[count])
+			glyphcode = byteord(data[count])
 			count = count + 1
-			strlen = ord(data[count])
+			strlen = byteord(data[count])
 			count = count + 1
 			glyphname = data[count:count+strlen]
 			glyphEncoding[glyphcode] = glyphname
@@ -433,10 +428,9 @@
 			return
 		numberofentries = len(self.glyphEncoding)
 		data = struct.pack(">h", numberofentries)
-		items = self.glyphEncoding.items()
-		items.sort()
+		items = sorted(self.glyphEncoding.items())
 		for glyphcode, glyphname in items:
-			data = data + chr(glyphcode) + chr(len(glyphname)) + glyphname
+			data = data + bytechr(glyphcode) + bytechr(len(glyphname)) + glyphname
 		self._rawglyphencodingsubtable = data
 	
 
@@ -445,8 +439,8 @@
 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"
+		if name[:len(famname)] != famname:
+			raise error("first part of name should be same as family name")
 		name = name[len(famname):]
 		split = [famname]
 	else:
@@ -466,11 +460,11 @@
 	split = splitname(name)
 	lwfnname = split[0][:5]
 	for part in split[1:]:
-		if part <> '-':
+		if part != '-':
 			lwfnname = lwfnname + part[:3]
 	return lwfnname
 
-class BitmapFontFile:
+class BitmapFontFile(object):
 	
 	def __init__(self, path, mode='r'):
 		if mode == 'r':
@@ -478,7 +472,7 @@
 		elif mode == 'w':
 			permission = 3	# exclusive r/w
 		else:
-			raise error, 'mode should be either "r" or "w"'
+			raise error('mode should be either "r" or "w"')
 		self.mode = mode
 		self.resref = Res.FSOpenResFile(path, permission)
 		Res.UseResFile(self.resref)
@@ -497,8 +491,7 @@
 		for fond in self.fonds:
 			fond.parse()
 			if hasattr(fond, "psNames") and fond.psNames:
-				psNames = fond.psNames.values()
-				psNames.sort()
+				psNames = sorted(fond.psNames.values())
 				self.fondsbyname[psNames[0]] = fond
 	
 	def minimalparse(self):
@@ -506,7 +499,7 @@
 			fond.minimalparse()
 	
 	def close(self):
-		if self.resref <> None:
+		if self.resref != None:
 			try:
 				Res.CloseResFile(self.resref)
 			except Res.Error:
@@ -514,12 +507,12 @@
 			self.resref = None
 
 
-class FondSelector:
+class FondSelector(object):
 	
 	def __init__(self, fondlist):
 		import W
 		if not fondlist:
-			raise ValueError, "expected at least one FOND entry"
+			raise ValueError("expected at least one FOND entry")
 		if len(fondlist) == 1:
 			self.choice = 0
 			return
@@ -549,6 +542,6 @@
 		sel = self.w.l.getselection()
 		if not sel:
 			self.w.l.setselection([0])
-		elif len(sel) <> 1:
+		elif len(sel) != 1:
 			self.w.l.setselection([sel[0]])
 			
diff --git a/Lib/fontTools/inspect.py b/Lib/fontTools/inspect.py
index d273579..93186f0 100644
--- a/Lib/fontTools/inspect.py
+++ b/Lib/fontTools/inspect.py
@@ -5,13 +5,15 @@
 """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):
@@ -217,7 +219,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()
@@ -254,7 +256,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..acbb02f 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,15 @@
     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 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,52 +49,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)
 
-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.
     """
+    (xMin, yMin, xMax, yMax) = rect1
     import math
     xMin = int(math.floor(xMin))
     yMin = int(math.floor(yMin))
@@ -147,9 +161,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..179ab3c 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",
@@ -98,7 +100,7 @@
     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 +134,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 +158,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)
@@ -402,7 +402,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..1264355 100644
--- a/Lib/fontTools/misc/eexec.py
+++ b/Lib/fontTools/misc/eexec.py
@@ -2,26 +2,27 @@
 charstring encryption algorithm as used by PostScript Type 1 fonts.
 """
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
+
 # 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"
 
 
 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):
@@ -31,7 +32,7 @@
 	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 +40,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,17 +50,17 @@
 
 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(eexecOp.decrypt, eexecOp.decrypt(testStr, 12321))
+	print(encrypt, encrypt(testStr, 12321))
+	print(eexecOp.encrypt, eexecOp.encrypt(testStr, 12321))
 
 
 if __name__ == "__main__":
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 9b2105b..34ace23 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
 
 
@@ -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:
@@ -472,7 +469,7 @@
 			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
@@ -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:
@@ -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)
@@ -1134,7 +1131,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
@@ -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/py23.py b/Lib/fontTools/misc/py23.py
new file mode 100644
index 0000000..2842840
--- /dev/null
+++ b/Lib/fontTools/misc/py23.py
@@ -0,0 +1,84 @@
+"""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 cStringIO import StringIO
+except ImportError:
+	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 __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..ecf495b 100644
--- a/Lib/fontTools/misc/sstruct.py
+++ b/Lib/fontTools/misc/sstruct.py
@@ -46,50 +46,57 @@
 	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
 
-
-error = "sstruct.error"
+class Error(Exception):
+	pass
 
 def pack(format, object):
 	formatstring, names, fixes = getformat(format)
 	elements = []
-	if type(object) is not types.DictType:
+	if not isinstance(object, dict):
 		object = object.__dict__
 	for name in names:
 		value = object[name]
-		if fixes.has_key(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 = {}
+	data = tobytes(data)
 	formatstring, names, fixes = getformat(format)
-	if type(object) is types.DictType:
-		dict = object
+	if isinstance(object, dict):
+		d = object
 	else:
-		dict = object.__dict__
+		d = object.__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
+			value = fi2fl(value, fixes[name])
+		elif isinstance(value, bytes):
+			try:
+				value = tostr(value)
+			except UnicodeDecodeError:
+				pass
+		d[name] = value
 	return object
 
 def unpack2(format, data, object=None):
@@ -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 format 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 format: '%s'" % line)
 				name = m.group(1)
 				names.append(name)
 				formatchar = m.group(2)
@@ -154,10 +161,10 @@
 					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
 	return formatstring, names, fixes
@@ -177,9 +184,9 @@
 		afixed: 16.16F
 	"""
 	
-	print 'size:', calcsize(format)
+	print('size:', calcsize(format))
 	
-	class foo:
+	class foo(object):
 		pass
 	
 	i = foo()
@@ -194,11 +201,11 @@
 	i.afixed = 1.5
 	
 	data = pack(format, i)
-	print 'data:', `data`
-	print unpack(format, data)
+	print('data:', repr(data))
+	print(unpack(format, data))
 	i2 = foo()
 	unpack(format, data, i2)
-	print vars(i2)
+	print(vars(i2))
 
 if __name__ == "__main__":
 	_test()
diff --git a/Lib/fontTools/misc/textTools.py b/Lib/fontTools/misc/textTools.py
index d461b25..f4fe07c 100644
--- a/Lib/fontTools/misc/textTools.py
+++ b/Lib/fontTools/misc/textTools.py
@@ -1,6 +1,8 @@
 """fontTools.misc.textTools.py -- miscelaneous routines."""
 
 
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 import string
 
 
@@ -11,18 +13,17 @@
 
 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,7 +31,7 @@
 	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
 
@@ -51,15 +52,15 @@
 		all.append(bin)
 	all.reverse()
 	assert l in (0, -1), "number doesn't fit in number of bits"
-	return string.join(all, " ")
+	return ' '.join(all)
 
 
 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..531b288 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,7 @@
 		"""
 		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 __lt__(self, other):
 		"""Transform instances are comparable:
 			>>> t1 = Identity.scale(2, 3).translate(4, 6)
 			>>> t2 = Identity.translate(8, 18).scale(2, 3)
@@ -298,8 +289,32 @@
 		"""
 		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 __eq__(self, other):
+		"""Transform instances are comparable:
+			>>> t1 = Identity.scale(2, 3).translate(4, 6)
+			>>> t2 = Identity.translate(8, 18).scale(2, 3)
+			>>> t1 == t2
+			1
+			>>>
+
+		But beware of floating point rounding errors:
+			>>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
+			>>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
+			>>> t1
+			<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
+			>>> t2
+			<Transform [0.2 0.0 0.0 0.3 0.08 0.18]>
+			>>> t1 == t2
+			0
+			>>>
+		"""
+		xx1, xy1, yx1, yy1, dx1, dy1 = self.__affine
+		xx2, xy2, yx2, yy2, dx2, dy2 = other
+		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 +343,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)))
 
 
diff --git a/Lib/fontTools/misc/xmlReader.py b/Lib/fontTools/misc/xmlReader.py
index f01dbcf..085e057 100644
--- a/Lib/fontTools/misc/xmlReader.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,7 +11,7 @@
 BUFSIZE = 0x4000
 
 
-class XMLReader:
+class XMLReader(object):
 	
 	def __init__(self, fileName, ttFont, progress=None, quiet=False):
 		self.ttFont = ttFont
@@ -23,7 +25,7 @@
 	def read(self):
 		if self.progress:
 			import stat
-			self.progress.set(0, os.stat(fileName)[stat.ST_SIZE] / 100 or 1)
+			self.progress.set(0, os.stat(fileName)[stat.ST_SIZE] // 100 or 1)
 		file = open(self.fileName)
 		self._parseFile(file)
 		file.close()
@@ -31,31 +33,30 @@
 	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
 		
 		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):
 		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([])
@@ -75,16 +76,16 @@
 				ttLib.debugmsg(msg)
 			else:
 				if not self.quiet:
-					print msg
+					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]
@@ -110,14 +111,15 @@
 		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
@@ -126,5 +128,5 @@
 		pass
 	
 	def setLabel(self, text):
-		print text
+		print(text)
 
diff --git a/Lib/fontTools/misc/xmlWriter.py b/Lib/fontTools/misc/xmlWriter.py
index aa70f13..6862e91 100644
--- a/Lib/fontTools/misc/xmlWriter.py
+++ b/Lib/fontTools/misc/xmlWriter.py
@@ -1,17 +1,23 @@
 """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")
+			except TypeError:
+				self.file = open(fileOrPath, "w", encoding="utf-8")
 		else:
 			# assume writable file object
 			self.file = fileOrPath
@@ -21,32 +27,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 +75,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 +99,7 @@
 		del self.stack[-1]
 		self.dedent()
 		data = "</%s>" % _TAG_
-		self.writeraw(data)
+		self._writeraw(data)
 	
 	def dumphex(self, data):
 		linelength = 16
@@ -95,7 +112,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 +125,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 +138,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 +173,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
index c834fc5..ea2c9ee 100644
--- a/Lib/fontTools/nfntLib.py
+++ b/Lib/fontTools/nfntLib.py
@@ -1,7 +1,7 @@
-import struct
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
-import string
-import types
+import struct
 
 
 # FontRec header
@@ -25,7 +25,7 @@
 assert headerSize == 26
 
 
-class NFNT:
+class NFNT(object):
 	
 	def __init__(self, data=None):
 		if data is not None:
@@ -44,23 +44,23 @@
 		self.bits = data[headerSize:headerSize + bitmapSize]
 		
 		# XXX deal with self.nDescent being a positive number
-		assert (headerSize + bitmapSize + tableSize - 16) / 2 == self.owTLoc  # ugh...
+		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'
+		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'
+		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]))
+			self.offsetTable.append(byteord(owTable[i]))
+			self.widthTable.append(byteord(owTable[i+1]))
 			loc, = struct.unpack("h", locTable[i:i+2])
 			self.locTable.append(loc)
 	
@@ -70,10 +70,10 @@
 		owTable = [None] * nEntries
 		locTable = [None] * nEntries
 		for i in range(nEntries):
-			owTable[i] = chr(self.offsetTable[i]) + chr(self.widthTable[i])
+			owTable[i] = bytechr(self.offsetTable[i]) + bytechr(self.widthTable[i])
 			locTable[i] = struct.pack("h", self.locTable[i])
-		owTable = string.join(owTable, "")
-		locTable = string.join(locTable, "")
+		owTable = bytesjoin(owTable)
+		locTable = bytesjoin(locTable)
 		assert len(locTable) == len(owTable) == 2 * (self.lastChar - self.firstChar + 3)
 		return header + self.bits + locTable + owTable
 	
@@ -93,7 +93,7 @@
 				byte = bits[y * rowBytes + xByte]
 				for xBit in range(8):
 					x = 8 * xByte + xBit
-					bit = (ord(byte) >> (7 - xBit)) & 0x01
+					bit = (byteord(byte) >> (7 - xBit)) & 0x01
 					bitImage[x, y] = bit
 		
 		for i in range(nGlyphs):
@@ -128,9 +128,9 @@
 			fRectWidth = max(fRectWidth, glyph.pixels.shape[0] + glyph.offset)
 		
 		fRectWidth = fRectWidth - kernMax
-		imageWidth = 16 * ((imageWidth - 1) / 16 + 1)
-		rowBytes = imageWidth / 8
-		rowWords = rowBytes / 2
+		imageWidth = 16 * ((imageWidth - 1) // 16 + 1)
+		rowBytes = imageWidth // 8
+		rowWords = rowBytes // 2
 		bitImage = numpy.zeros((imageWidth, imageHeight), numpy.int8)
 		locTable = []
 		widthTable = []
@@ -158,8 +158,8 @@
 				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, "")
+				bits.append(bytechr(byte))
+		bits = bytesjoin(bits)
 		
 		# assign values
 		self.fontType = 0x9000
@@ -173,7 +173,7 @@
 		self.rowWords = rowWords
 		
 		tableSize = 2 * (self.lastChar - self.firstChar + 3)
-		self.owTLoc = (headerSize + len(bits) + tableSize - 16) / 2
+		self.owTLoc = (headerSize + len(bits) + tableSize - 16) // 2
 		
 		self.bits = bits
 		self.locTable = locTable
@@ -185,7 +185,7 @@
 	
 	def __getitem__(self, charNum):
 		if charNum > self.lastChar or charNum < 0:
-			raise IndexError, "no such character"
+			raise IndexError("no such character")
 		index = charNum - self.firstChar
 		if index < 0:
 			return None
@@ -193,10 +193,10 @@
 	
 	def __setitem__(self, charNum, glyph):
 		if charNum > self.lastChar or charNum < 0:
-			raise IndexError, "no such character"
+			raise IndexError("no such character")
 		index = charNum - self.firstChar
 		if index < 0:
-			raise IndexError, "no such character"
+			raise IndexError("no such character")
 		self.glyphs[index] = glyph
 	
 	def __len__(self):
@@ -231,14 +231,14 @@
 		return width
 	
 	def charwidth(self, ch):
-		cindex = ord(ch) - self.firstChar
+		cindex = byteord(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
+		cindex = byteord(ch) - self.firstChar
 		if cindex > self.lastChar or 	\
 				(self.offsetTable[cindex] == 255 and self.widthTable[cindex] == 255):
 			return self.getcharboundsindex(-2)	# missing char
@@ -248,7 +248,7 @@
 		offset = self.offsetTable[cindex]
 		width = self.widthTable[cindex]
 		if offset == 255 and width == 255:
-			raise ValueError, "character not defined"
+			raise ValueError("character not defined")
 		location0 = self.locTable[cindex]
 		location1 = self.locTable[cindex + 1]
 		srcbounds = (location0, 0, location1, self.fRectHeight)
@@ -259,7 +259,7 @@
 		return width, srcbounds, destbounds
 
 
-class Glyph:
+class Glyph(object):
 	
 	def __init__(self, width, offset, pixels=None, pixelDepth=1):
 		self.width = width
@@ -276,7 +276,7 @@
 		if not nameOrID:
 			# just take the first in the file
 			res = Res.Get1IndResource(resType, 1)
-		elif type(nameOrID) == types.IntType:
+		elif isinstance(nameOrID, int):
 			res = Res.Get1Resource(resType, nameOrID)
 		else:
 			res = Res.Get1NamedResource(resType, nameOrID)
@@ -301,4 +301,4 @@
 		font.unpackGlyphs()
 		font.packGlyphs()
 		data2 = font.compile()
-		print "xxxxx", data == data2, len(data) == len(data2)
+		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..c1c394e 100644
--- a/Lib/fontTools/pens/reportLabPen.py
+++ b/Lib/fontTools/pens/reportLabPen.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.pens.basePen import BasePen
 
 
@@ -12,13 +14,18 @@
 			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,10 +35,10 @@
 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
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 20d2b7c..1a1601d 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,20 +96,20 @@
 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)
+    s.glyphs.update(v for g,v in self.mapping.items() if g in cur_glyphs)
   else:
     assert 0, "unknown format: %s" % self.Format
 
 @_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()
+    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)
   else:
@@ -142,7 +143,7 @@
 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()
+    _set_update(s.glyphs, *(vlist for g,vlist in self.alternates.items()
                             if g in cur_glyphs))
   else:
     assert 0, "unknown format: %s" % self.Format
@@ -151,7 +152,7 @@
 def subset_glyphs(self, s):
   if self.Format == 1:
     self.alternates = dict((g,vlist)
-                           for g,vlist in self.alternates.iteritems()
+                           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)
@@ -164,7 +165,7 @@
   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()
+                            for g,seqs in self.ligatures.items()
                             if g in cur_glyphs))
   else:
     assert 0, "unknown format: %s" % self.Format
@@ -172,13 +173,13 @@
 @_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()
+    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.iteritems())
-    self.ligatures = dict((g,v) for g,v in self.ligatures.iteritems() if v)
+                           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)
   else:
     assert 0, "unknown format: %s" % self.Format
@@ -1091,12 +1092,12 @@
   if table.MarkAttachClassDef:
     table.MarkAttachClassDef.classDefs = dict((g,v) for g,v in
                                               table.MarkAttachClassDef.
-                                                classDefs.iteritems()
+                                                classDefs.items()
                                               if g in glyphs)
   if table.GlyphClassDef:
     table.GlyphClassDef.classDefs = dict((g,v) for g,v in
                                          table.GlyphClassDef.
-                                           classDefs.iteritems()
+                                           classDefs.items()
                                          if g in glyphs)
   if table.AttachList:
     indices = table.AttachList.Coverage.subset(glyphs)
@@ -1126,13 +1127,13 @@
     table.AttachList = None
   if hasattr(table, "MarkGlyphSetsDef") and table.MarkGlyphSetsDef and not table.MarkGlyphSetsDef.Coverage:
     table.MarkGlyphSetsDef = None
-    if table.Version == float(0x00010002)/0x10000:
+    if table.Version == 0x00010002/0x10000:
       table.Version = 1.0
   return bool(table.LigCaretList or
               table.MarkAttachClassDef or
               table.GlyphClassDef or
               table.AttachList or
-              (table.Version >= float(0x00010002)/0x10000 and table.MarkGlyphSetsDef))
+              (table.Version >= 0x00010002/0x10000 and table.MarkGlyphSetsDef))
 
 @_add_method(ttLib.getTableClass('kern'))
 def prune_pre_subset(self, options):
@@ -1144,30 +1145,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'))
 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.iteritems() if g in s.glyphs)
+  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
@@ -1238,9 +1239,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:
@@ -1252,7 +1253,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
 
@@ -1295,11 +1296,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)
@@ -1310,12 +1311,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)
@@ -1365,7 +1366,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
@@ -1402,8 +1403,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:
@@ -1448,8 +1449,8 @@
     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:
@@ -1491,8 +1492,8 @@
       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
@@ -1622,7 +1623,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
@@ -1673,10 +1674,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)]
@@ -1740,7 +1741,7 @@
               '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
@@ -1765,7 +1766,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)
@@ -1848,7 +1849,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))
@@ -1996,14 +1997,14 @@
   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):
@@ -2070,7 +2071,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]
diff --git a/Lib/fontTools/t1Lib.py b/Lib/fontTools/t1Lib.py
index 42d95c2..1a81a99 100644
--- a/Lib/fontTools/t1Lib.py
+++ b/Lib/fontTools/t1Lib.py
@@ -15,17 +15,17 @@
 	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.
 	
@@ -102,7 +102,7 @@
 
 def read(path, onlyHeader=0):
 	"""reads any Type 1 font file, returns raw data"""
-	normpath = string.lower(path)
+	normpath = path.lower()
 	creator, type = getMacCreatorAndType(path)
 	if type == 'LWFN':
 		return readLWFN(path, onlyHeader), 'LWFN'
@@ -113,7 +113,7 @@
 
 def write(path, data, kind='OTHER', dohex=0):
 	assertType1(data)
-	kind = string.upper(kind)
+	kind = kind.upper()
 	try:
 		os.remove(path)
 	except os.error:
@@ -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,10 +166,10 @@
 			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
 
@@ -177,10 +177,10 @@
 	"""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,10 +246,10 @@
 				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()
 
@@ -257,7 +257,7 @@
 	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,7 +333,7 @@
 	return chunks
 
 def deHexString(hexstring):
-	return eexec.deHexString(string.join(string.split(hexstring), ""))
+	return eexec.deHexString(strjoin(hexstring.split()))
 
 
 # Type 1 assertion
@@ -345,11 +345,11 @@
 		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("currentfile eexec") < 0:
+		raise T1Error("not an encrypted Type 1 font")
 	# XXX what else?
 	return data
 
@@ -359,14 +359,14 @@
 def longToString(long):
 	str = ""
 	for i in range(4):
-		str = str + chr((long & (0xff << (i * 8))) >> i * 8)
+		str = str + bytechr((long & (0xff << (i * 8))) >> i * 8)
 	return str
 
 def stringToLong(str):
-	if len(str) <> 4:
-		raise ValueError, 'string must be 4 bytes long'
+	if len(str) != 4:
+		raise ValueError('string must be 4 bytes long')
 	long = 0
 	for i in range(4):
-		long = long + (ord(str[i]) << (i * 8))
+		long = long + (byteord(str[i]) << (i * 8))
 	return long
 
diff --git a/Lib/fontTools/ttLib/__init__.py b/Lib/fontTools/ttLib/__init__.py
index 54abed1..bb9f262 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. 
@@ -148,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.
@@ -183,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")
@@ -194,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)
@@ -236,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:
@@ -251,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()
 		
@@ -290,7 +291,7 @@
 			debugmsg("Done dumping TTX")
 	
 	def _tableToXML(self, writer, tag, progress, quiet):
-		if self.has_key(tag):
+		if tag in self:
 			table = self[tag]
 			report = "Dumping '%s' table..." % tag
 		else:
@@ -301,8 +302,8 @@
 			debugmsg(report)
 		else:
 			if not quiet:
-				print report
-		if not self.has_key(tag):
+				print(report)
+		if tag not in self:
 			return
 		xmlTag = tagToXML(tag)
 		if hasattr(table, "ERROR"):
@@ -322,7 +323,7 @@
 		"""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
@@ -337,12 +338,12 @@
 	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):
+		elif self.reader and tag in self.reader:
 			return 1
 		elif tag == "GlyphOrder":
 			return 1
@@ -352,9 +353,9 @@
 	__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)
 
@@ -364,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:
@@ -390,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()
@@ -401,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):
@@ -422,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:
@@ -485,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
@@ -519,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):
@@ -554,13 +553,13 @@
 			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
+					raise KeyError(glyphName)
 				else:
 					# user intends virtual GID support 	
 					try:
@@ -580,7 +579,7 @@
 					return glyphID
 
 		glyphID = d[glyphName]
-		if glyphName <> glyphOrder[glyphID]:
+		if glyphName != glyphOrder[glyphID]:
 			self._buildReverseGlyphOrderDict()
 			return self.getGlyphID(glyphName)
 		return glyphID
@@ -605,7 +604,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)
@@ -618,16 +617,17 @@
 	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):
 		"""Return a generic GlyphSet, which is a dict-like object
@@ -641,16 +641,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().
@@ -664,10 +664,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
 
@@ -681,7 +681,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
@@ -742,7 +742,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.
@@ -761,7 +761,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)
@@ -773,11 +773,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
@@ -796,7 +796,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)
@@ -817,7 +817,7 @@
 	elif re.match("[A-Z]", c):
 		return c + "_"
 	else:
-		return hex(ord(c))[2:]
+		return hex(byteord(c))[2:]
 
 
 def tagToIdentifier(tag):
@@ -834,6 +834,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"
@@ -862,10 +863,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):
@@ -874,12 +875,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)
 
@@ -892,12 +894,12 @@
 		return identifierToTag(tag)
 	else:
 		return tag + " " * (4 - len(tag))
-	return tag
+	return Tag(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
@@ -913,8 +915,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?)
@@ -940,7 +941,7 @@
 	from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
 	reader = SFNTReader(inFile, checkChecksums=checkChecksums)
 	writer = SFNTWriter(outFile, len(reader.tables), reader.sfntVersion, reader.flavor, reader.flavorData)
-	tables = reader.keys()
+	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..da5c79d 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
@@ -237,7 +239,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 +250,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 +340,7 @@
 woffDirectoryEntrySize = sstruct.calcsize(woffDirectoryEntryFormat)
 
 
-class DirectoryEntry:
+class DirectoryEntry(object):
 	
 	def __init__(self):
 		self.uncompressed = False # if True, always embed entry raw
@@ -450,18 +451,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
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..eb48f75 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):
@@ -10,12 +12,10 @@
 		self._gaveGlyphOrder = 0
 	
 	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()
@@ -29,7 +29,7 @@
 	def getGlyphOrder(self):
 		if self._gaveGlyphOrder:
 			from fontTools import ttLib
-			raise ttLib.TTLibError, "illegal use of getGlyphOrder()"
+			raise ttLib.TTLibError("illegal use of getGlyphOrder()")
 		self._gaveGlyphOrder = 1
 		return self.cff[self.cff.fontNames[0]].getGlyphOrder()
 	
@@ -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..31ef729 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,12 +44,12 @@
 
 		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):
@@ -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 8d68ec0..5951a0b 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
@@ -26,19 +26,17 @@
 		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): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
-
-		return cmp(self.__dict__, other.__dict__)
-
+	def __eq__(self, other):
+		if type(self) != type(other):
+			raise TypeError("unordered types %s() < %s()", type(self), type(other))
+		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 4b033ec..ab2b8f4 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,11 +162,11 @@
 					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.
@@ -176,7 +175,7 @@
 			assert self.strikeData[strikeIndex] == 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,16 +382,17 @@
 	def __init__(self, data, ttFont):
 		self.data = data
 		self.ttFont = ttFont
-		if not ttFont.lazy:
-			self.decompile()
-			del self.data
+		# 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
+			raise AttributeError(attr)
 		if not hasattr(self, "data"):
-			raise AttributeError, attr
+			raise AttributeError(attr)
 		self.decompile()
 		del self.data
 		return getattr(self, attr)
@@ -412,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.
@@ -429,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):
@@ -439,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.
@@ -457,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
 
@@ -482,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)
@@ -512,28 +514,28 @@
 		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
@@ -542,25 +544,25 @@
 		if metrics == 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:
@@ -568,12 +570,12 @@
 					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)
 
@@ -592,7 +594,7 @@
 			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):
 
@@ -668,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):
@@ -698,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)
@@ -707,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):
@@ -723,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)
@@ -736,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 c7e2f89..1856fd7 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,7 +213,7 @@
 				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.
@@ -223,7 +222,7 @@
 			assert self.strikes[strikeIndex] == 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,22 @@
 # 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
-		if not ttFont.lazy:
-			self.decompile()
-			del self.data, self.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
+			raise AttributeError(attr)
 		if not hasattr(self, "data"):
-			raise AttributeError, attr
+			raise AttributeError(attr)
 		self.decompile()
 		del self.data, self.ttFont
 		return getattr(self, attr)
@@ -360,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.
@@ -374,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':
@@ -391,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
@@ -408,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
@@ -423,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]:
@@ -477,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):
@@ -520,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
@@ -545,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:]]
@@ -568,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):
@@ -588,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/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..6e8b322 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,16 +52,12 @@
 		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):
@@ -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_E_T_A_.py b/Lib/fontTools/ttLib/tables/M_E_T_A_.py
index b8fb640..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,23 +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): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
-
-		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:
@@ -266,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')
@@ -278,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")
@@ -303,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)
@@ -322,15 +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): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
-
-		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 eb3cb01..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,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, num2binary, binary2num
-from types import TupleType
+from . import DefaultTable
 import warnings
 
 
@@ -20,7 +21,7 @@
 	bXHeight:           B
 """
 
-class Panose:
+class Panose(object):
 	
 	def toXML(self, writer, ttFont):
 		formatstring, names, fixes = sstruct.getformat(panoseFormat)
@@ -28,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"]))
 
 
@@ -109,11 +110,11 @@
 			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:
+			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")
 
@@ -135,7 +136,7 @@
 			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
 	
@@ -151,8 +152,6 @@
 		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()
@@ -170,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 84e77ad..8d04811 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,7 +104,7 @@
 			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)
 
 
@@ -172,7 +171,7 @@
 			for entry in entries:
 				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):
@@ -196,11 +195,11 @@
 			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:
@@ -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,7 +253,7 @@
 		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()
@@ -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..44214f7 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
@@ -29,7 +31,7 @@
 			# We have no corresponging 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..5f29eb1 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,17 +30,17 @@
 		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:
@@ -48,13 +48,13 @@
 		except KeyError:
 			nameMap = ttFont.getReverseGlyphMap(rebuild=1)
 			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..108aed1 100644
--- a/Lib/fontTools/ttLib/tables/__init__.py
+++ b/Lib/fontTools/ttLib/tables/__init__.py
@@ -3,52 +3,52 @@
 	"""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 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 _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 9ae7987..b08c1ff 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):
@@ -33,9 +33,9 @@
 				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
+			raise AttributeError(attr)
 		if self.data == None:
-			raise AttributeError, attr
+			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,10 +149,11 @@
 				writer.comment(Unicode[code])
 			writer.newline()
 	
-	def __cmp__(self, other):
-		if type(self) != type(other): return cmp(type(self), type(other))
+	def __lt__(self, other):
+		if not isinstance(other, CmapSubtable):
+			raise TypeError("unordered types %s() < %s()", type(self), type(other))
 
-		# implemented so that list.sort() sorts according to the cmap spec.
+		# implemented so that list.sort() sorts according to the spec.
 		selfTuple = (
 			getattr(self, "platformID", None),
 			getattr(self, "platEncID", None),
@@ -164,7 +164,7 @@
 			getattr(other, "platEncID", None),
 			getattr(other, "language", None),
 			other.__dict__)
-		return cmp(selfTuple, otherTuple)
+		return selfTuple < otherTuple
 
 
 class cmap_format_0(CmapSubtable):
@@ -182,20 +182,19 @@
 		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)
@@ -203,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
@@ -272,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
@@ -288,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)
@@ -329,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):
@@ -363,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):
@@ -380,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)
 			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 = []
@@ -517,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"]
 
@@ -639,13 +637,13 @@
 		(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
@@ -663,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]
@@ -671,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
@@ -683,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))
 		
 
 
@@ -718,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)
 				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 = []
@@ -750,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
@@ -780,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)
@@ -803,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()
@@ -814,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"]
 
@@ -846,54 +844,54 @@
 		#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 = range(codes[0], codes[-1] + 1)
+			codes = list(range(codes[0], codes[-1] + 1))
 			firstCode = codes[0]
 			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"]
 
@@ -933,32 +931,32 @@
 			startCharCode, endCharCode, glyphID = struct.unpack(">LLL",data[pos:pos+12] )
 			pos += 12
 			lenGroup = 1 + endCharCode - startCharCode
-			charCodes += range(startCharCode, endCharCode +1)
+			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()
+		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)
 			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 = []
@@ -977,7 +975,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))
 
 		charCodes.sort()
 		index = 0
@@ -1000,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
@@ -1016,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"])
@@ -1033,10 +1030,10 @@
 		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"]
 
@@ -1047,7 +1044,7 @@
 		self._format_step = 1
 
 	def _computeGIDs(self, startingGlyph, numberOfGlyphs):
-		return range(startingGlyph, startingGlyph + numberOfGlyphs)
+		return list(range(startingGlyph, startingGlyph + numberOfGlyphs))
 
 	def _IsInSameRun(self, glyphID, lastGlyphID, charCode, lastCharCode):
 		return (glyphID == 1 + lastGlyphID) and (charCode == 1 + lastCharCode)
@@ -1066,35 +1063,16 @@
 
 
 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):
@@ -1128,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
@@ -1164,11 +1142,10 @@
 				])
 		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] != None, item[0], item[1]))
 			for uv, gname in uvList:
 				if gname == None:
 					gname = "None"
@@ -1178,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"):
@@ -1190,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"])
@@ -1211,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 = []
@@ -1220,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] == None]
 			if defList:
-				defList = map(lambda entry: entry[0], defList)
+				defList = [entry[0] for entry in defList]
 				defOVSOffset = offset
 				defList.sort()
 
@@ -1247,7 +1223,7 @@
 			else:
 				defOVSOffset = 0
 
-			ndefList = filter(lambda entry: entry[1] != None, entryList)
+			ndefList = [entry for entry in entryList if entry[1] != None]
 			if ndefList:
 				nonDefUVSOffset = offset
 				ndefList.sort()
@@ -1265,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
@@ -1286,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 = {}
 	
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 838c5d1..fc47903 100644
--- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -1,6 +1,20 @@
 """_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
+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 +28,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 +44,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
@@ -58,7 +60,6 @@
 	def compile(self, ttFont):
 		if not hasattr(self, "glyphOrder"):
 			self.glyphOrder = ttFont.getGlyphOrder()
-		import string
 		locations = []
 		currentLocation = 0
 		dataList = []
@@ -70,7 +71,7 @@
 			currentLocation = currentLocation + len(glyphData)
 			dataList.append(glyphData)
 		locations.append(currentLocation)
-		data = string.join(dataList, "")
+		data = bytesjoin(dataList)
 		if 'loca' in ttFont:
 			ttFont['loca'].set(locations)
 		ttFont['maxp'].numGlyphs = len(self.glyphs)
@@ -88,7 +89,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:
@@ -109,8 +110,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 = {}
@@ -124,9 +125,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)
 	
@@ -144,7 +146,7 @@
 		return self.glyphs.keys()
 	
 	def has_key(self, glyphName):
-		return self.glyphs.has_key(glyphName)
+		return glyphName in self.glyphs
 	
 	__contains__ = has_key
 	
@@ -202,7 +204,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:
@@ -249,7 +251,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):
@@ -281,18 +283,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"]))
@@ -307,19 +309,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()
@@ -362,7 +365,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()
 		
@@ -423,12 +426,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:
@@ -455,7 +458,7 @@
 		return flags, xCoordinates, yCoordinates
 	
 	def compileComponents(self, glyfTable):
-		data = ""
+		data = b""
 		lastcomponent = len(self.components) - 1
 		more = 1
 		haveInstructions = 0
@@ -473,9 +476,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()
@@ -537,10 +540,10 @@
 				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))
+		xPoints = list(map(int, xPoints))  # work around struct >= 2.5 bug
+		yPoints = list(map(int, yPoints))
+		data = data + struct.pack(*(xFormat,)+tuple(xPoints))
+		data = data + struct.pack(*(yFormat,)+tuple(yPoints))
 		return data
 	
 	def recalcBounds(self, glyfTable):
@@ -559,7 +562,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):
@@ -641,7 +644,7 @@
 
 	def removeHinting(self):
 		if not hasattr(self, "data"):
-			self.program = ttLib.tables.ttProgram.Program()
+			self.program = ttProgram.Program()
 			self.program.fromBytecode([])
 			return
 
@@ -670,7 +673,7 @@
 					# padding.
 					coordBytes = 0
 					j = 0
-					while 1:
+					while True:
 						flag = data[i]
 						i = i + 1
 						repeat = 1
@@ -718,18 +721,17 @@
 		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): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
 
-		return cmp(self.__dict__, other.__dict__)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			raise TypeError("unordered types %s() < %s()", type(self), type(other))
+		return self.__dict__ == other.__dict__
 
 
-class GlyphComponent:
+class GlyphComponent(object):
 	
 	def __init__(self):
 		pass
@@ -755,7 +757,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:
@@ -774,17 +775,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
@@ -794,7 +795,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 | 
@@ -820,13 +821,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])
@@ -852,7 +853,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]),
 						]
@@ -862,36 +863,35 @@
 		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): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			raise TypeError("unordered types %s() < %s()", type(self), type(other))
+		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")
@@ -907,17 +907,17 @@
 		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
@@ -926,8 +926,8 @@
 	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):
+		self._a.extend(tuple(p))
 
 	def extend(self, iterable):
 		for x,y in iterable:
@@ -936,14 +936,14 @@
 	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 +951,31 @@
 			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])
 
+	def __eq__(self, other):
+		if type(self) != type(other):
+			raise TypeError("unordered types %s() < %s()", type(self), type(other))
+		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 61d9a16..182f328 100644
--- a/Lib/fontTools/ttLib/tables/_h_e_a_d.py
+++ b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
@@ -1,8 +1,9 @@
-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
 
 
 headFormat = """
@@ -35,12 +36,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.unitsPerEm = self.unitsPerEm
+		self.flags = self.flags
 		self.strings2dates()
 	
 	def compile(self, ttFont):
-		self.modified = long(time.time() - mac_epoch_diff)
+		self.modified = int(time.time() - mac_epoch_diff)
 		self.dates2strings()
 		data = sstruct.pack(headFormat, self)
 		self.strings2dates()
@@ -67,7 +68,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,31 +77,15 @@
 			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
 		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): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
-
-		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():
@@ -112,7 +97,7 @@
 	# 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)
+	return int(safe_epoch - seconds1904to1972)
 
 mac_epoch_diff = calc_mac_epoch_diff()
 
@@ -122,34 +107,31 @@
 _weekdays = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
 
 def parse_date(datestring):
-	datestring = string.lower(datestring)
-	weekday, month, day, tim, year = string.split(datestring)
+	datestring = datestring.lower()
+	weekday, month, day, tim, year = datestring.split()
 	weekday = _weekdays.index(weekday)
 	month = _months.index(month)
 	year = int(year)
 	day = int(day)
-	hour, minute, second = map(int, string.split(tim, ":"))
+	hour, minute, second = [int(item) for item in tim.split(":")]
 	t = (year, month, day, hour, minute, second, weekday, 0, 0)
-	try:
-		return long(time.mktime(t) - time.timezone)
-	except OverflowError:
-		return 0L
+	return int(time.mktime(t) - time.timezone)
 
 
 def bin2long(data):
 	# thanks </F>!
-	v = 0L
-	for i in map(ord, data):
+	v = 0
+	for i in map(byteord, data):
 	    v = v<<8 | i
 	return v
 
 def long2bin(v, bytes=8):
-	mask = long("FF" * bytes, 16)
-	data = ""
+	mask = int("FF" * bytes, 16)
+	data = b""
 	while v:
-		data = chr(v & 0xff) + data
+		data = bytechr(v & 0xff) + data
 		v = (v >> 8) & mask
-	data = (bytes - len(data)) * "\0" + data
+	data = (bytes - len(data)) * b"\0" + data
 	assert len(data) == 8, "long too long"
 	return data
 
diff --git a/Lib/fontTools/ttLib/tables/_h_h_e_a.py b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
index cf2919e..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,7 +41,7 @@
 	
 	def recalc(self, ttFont):
 		hmtxTable = ttFont['hmtx']
-		if ttFont.has_key('glyf'):
+		if 'glyf' in ttFont:
 			glyfTable = ttFont['glyf']
 			INFINITY = 100000
 			advanceWidthMax = 0
@@ -81,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 1b1e200..f7ea1e6 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,14 +21,14 @@
 			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")
@@ -61,19 +62,18 @@
 		for item in metrics:
 			allMetrics.extend(item)
 		allMetrics = array.array("h", allMetrics)
-		if sys.byteorder <> "big":
+		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", [
@@ -83,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":
@@ -93,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 22c5552..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,15 +161,9 @@
 	
 	def __delitem__(self, pair):
 		del self.kernTable[pair]
-	
-	def __cmp__(self, other):
-		if type(self) != type(other): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
-
-		return cmp(self.__dict__, other.__dict__)
 
 
-class KernTable_format_2:
+class KernTable_format_2(object):
 	
 	def decompile(self, data, ttFont):
 		self.data = data
@@ -185,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
@@ -209,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 21c31ad..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")
@@ -37,12 +37,12 @@
 		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()
 	
@@ -58,10 +58,4 @@
 	
 	def __len__(self):
 		return len(self.locations)
-	
-	def __cmp__(self, other):
-		if type(self) != type(other): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
-
-		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 5aafe90..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:
@@ -75,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)
@@ -112,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:
@@ -129,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 5234ae1..b6cc19b 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:
@@ -88,14 +87,8 @@
 					return namerecord
 		return None # not found
 	
-	def __cmp__(self, other):
-		if type(self) != type(other): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
 
-		return cmp(self.names, other.names)
-	
-
-class NameRecord:
+class NameRecord(object):
 	
 	def toXML(self, writer, ttFont):
 		writer.begintag("namerecord", [
@@ -109,52 +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):
+			raise TypeError("unordered types %s() < %s()", type(self), type(other))
 
-		if type(self) != type(other): return cmp(type(self), type(other))
-
-		selftuple = (
+		# 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 = (
+		otherTuple = (
 			getattr(other, "platformID", None),
 			getattr(other, "platEncID", None),
 			getattr(other, "langID", None),
 			getattr(other, "nameID", None),
 			getattr(other, "string", None),
 		)
-		return cmp(selftuple, othertuple)
+		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..eae5863 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]))
 		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)
 	return data
 
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 d3f4eb3..bf010b5 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,9 +30,9 @@
 	"""
 	
 	def decompile(self, data, font):
-		import otTables
+		from . import otTables
 		cachingStats = None if True else {}
-		class GlobalState:
+		class GlobalState(object):
 			def __init__(self, tableType, cachingStats):
 				self.tableType = tableType
 				self.cachingStats = cachingStats
@@ -43,15 +43,14 @@
 		self.table = tableClass()
 		self.table.decompile(reader, font)
 		if cachingStats:
-			stats = [(v, k) for k, v in cachingStats.items()]
-			stats.sort()
+			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.
@@ -74,7 +73,7 @@
 
 				If a lookup subtable overflows an offset, we have to start all over. 
 		"""
-		class GlobalState:
+		class GlobalState(object):
 			def __init__(self, tableType):
 				self.tableType = tableType
 		globalState = GlobalState(tableType=self.tableTag)
@@ -86,12 +85,12 @@
 	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(object):
@@ -138,8 +137,7 @@
 	def readUInt24(self):
 		pos = self.pos
 		newpos = pos + 3
-		value = (ord(self.data[pos]) << 16) | (ord(self.data[pos+1]) << 8) | ord(self.data[pos+2])
-		value, = struct.unpack(">H", self.data[pos:newpos])
+		value, = struct.unpack(">l", b'\0'+self.data[pos:newpos])
 		self.pos = newpos
 		return value
 
@@ -153,7 +151,7 @@
 	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
@@ -214,7 +212,7 @@
 			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."""
@@ -280,19 +278,18 @@
 						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): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
-
-		return cmp(self.items, other.items)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			raise TypeError("unordered types %s() < %s()", type(self), type(other))
+		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,7 +317,7 @@
 					item._doneWriting()
 				else:
 					item._doneWriting(internedTables)
-					if internedTables.has_key(item):
+					if item in internedTables:
 						items[i] = item = internedTables[item]
 					else:
 						internedTables[item] = item
@@ -342,7 +339,7 @@
 		done[self] = 1
 
 		numItems = len(self.items)
-		iRange = range(numItems)
+		iRange = list(range(numItems))
 		iRange.reverse()
 
 		if hasattr(self, "Extension"):
@@ -359,7 +356,7 @@
 				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())
@@ -380,7 +377,7 @@
 				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())
@@ -408,7 +405,8 @@
 
 	def writeUInt24(self, value):
 		assert 0 <= value < 0x1000000
-		self.items.append(''.join(chr(v) for v in (value>>16, (value>>8)&0xFF, value&0xff)))
+		b = struct.pack(">L", value)
+		self.items.append(b[1:])
 	
 	def writeLong(self, value):
 		self.items.append(struct.pack(">l", value))
@@ -417,6 +415,7 @@
 		self.items.append(struct.pack(">L", value))
 	
 	def writeTag(self, tag):
+		tag = Tag(tag).tobytes()
 		assert len(tag) == 4
 		self.items.append(tag)
 	
@@ -429,7 +428,7 @@
 		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):
@@ -469,7 +468,7 @@
 		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
@@ -514,14 +513,14 @@
 		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 
+			raise AttributeError(attr) 
 		if self.compileStatus == 1:
 			self.ensureDecompiled()
 			val = getattr(self, attr)
 			self.recurse -=1
 			return val
 			
-		raise AttributeError, attr 
+		raise AttributeError(attr) 
 
 
 	"""Generic base class for all OpenType (sub)tables."""
@@ -659,7 +658,7 @@
 				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:
@@ -674,13 +673,14 @@
 		else:
 			setattr(self, conv.name, value)
 	
-	def __cmp__(self, other):
-		if type(self) != type(other): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			raise TypeError("unordered types %s() < %s()", type(self), type(other))
 
 		self.ensureDecompiled()
+		other.ensureDecompiled()
 
-		return cmp(self.__dict__, other.__dict__)
+		return self.__dict__ == other.__dict__
 
 
 class FormatSwitchingBaseTable(BaseTable):
@@ -696,7 +696,7 @@
 	
 	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)
@@ -740,7 +740,7 @@
 valueRecordFormatDict = _buildDict()
 
 
-class ValueRecordFactory:
+class ValueRecordFactory(object):
 	
 	"""Given a format code, this object convert ValueRecords."""
 
@@ -763,7 +763,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)
@@ -788,7 +788,7 @@
 				writer.writeUShort(value)
 
 
-class ValueRecord:
+class ValueRecord(object):
 	
 	# see ValueRecordFactory
 	
@@ -824,23 +824,23 @@
 			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): return cmp(type(self), type(other))
-		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
-
-		return cmp(self.__dict__, other.__dict__)
+	def __eq__(self, other):
+		if type(self) != type(other):
+			raise TypeError("unordered types %s() < %s()", type(self), type(other))
+		return self.__dict__ == other.__dict__
diff --git a/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py
index 0266c71..871ec1d 100644
--- a/Lib/fontTools/ttLib/tables/otConverters.py
+++ b/Lib/fontTools/ttLib/tables/otConverters.py
@@ -1,6 +1,8 @@
-from types import TupleType
+from __future__ import print_function, division
+from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
-from otBase import ValueRecordFactory
+from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from .otBase import ValueRecordFactory
 
 
 def buildConverters(tableSpec, tableNamespace):
@@ -38,7 +40,7 @@
 			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
 		convertersByName[name] = conv
 	return converters, convertersByName
 
@@ -58,19 +60,19 @@
 	
 	def read(self, reader, font, tableDict):
 		"""Read a value from the reader."""
-		raise NotImplementedError, self
+		raise NotImplementedError(self)
 	
 	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):
@@ -94,10 +96,10 @@
 	def read(self, reader, font, tableDict):
 		value = reader.readLong()
 		assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
-		return float(value) / 0x10000
+		return  fi2fl(value, 16)
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		if value < 0x10000:
-			value *= 0x10000
+			value = fl2fi(value, 16)
 		value = int(round(value))
 		assert (value >> 16) == 1, "Unsupported version 0x%08x" % value
 		writer.writeLong(value)
@@ -105,14 +107,14 @@
 		value = attrs["value"]
 		value = float(int(value, 0)) if value.startswith("0") else float(value)
 		if value >= 0x10000:
-			value = float(value) / 0x10000
+			value = fi2fl(value, 16)
 		return value
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
 		if value >= 0x10000:
-			value = float(value) / 0x10000
+			value = fi2fl(value, 16)
 		if value % 1 != 0:
 			# Write as hex
-			value = "0x%08x" % (int(round(value * 0x10000)))
+			value = "0x%08x" % fl2fi(value, 16)
 		xmlWriter.simpletag(name, attrs + [("value", value)])
 		xmlWriter.newline()
 
@@ -162,7 +164,7 @@
 class DeciPoints(FloatValue):
 	def read(self, reader, font, tableDict):
 		value = reader.readUShort()
-		return value / 10.
+		return value / 10
 
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeUShort(int(round(value * 10)))
@@ -198,9 +200,9 @@
 		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
@@ -225,8 +227,8 @@
 			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
 		table = self.tableClass()
 		table.reader = reader.getSubReader(offset)
@@ -298,9 +300,9 @@
 		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
 
 
@@ -347,7 +349,7 @@
 			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):
diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py
index e06bd42..4808dd0 100644
--- a/Lib/fontTools/ttLib/tables/otData.py
+++ b/Lib/fontTools/ttLib/tables/otData.py
@@ -639,7 +639,7 @@
 		('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, 'Version >= 0x00010002 / float(0x10000)', 'Offset to the table of mark set definitions-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', [
diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py
index 584d0c5..54a684f 100644
--- a/Lib/fontTools/ttLib/tables/otTables.py
+++ b/Lib/fontTools/ttLib/tables/otTables.py
@@ -4,9 +4,10 @@
 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
 
 
@@ -42,7 +43,7 @@
 			# 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, cmp=lambda a,b: cmp(a.StartCoverageIndex,b.StartCoverageIndex))
+			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
@@ -105,7 +106,7 @@
 					index = index + end - start + 1
 				if brokenOrder:
 					warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
-					ranges.sort(cmp=lambda a,b: cmp(a.StartID,b.StartID))
+					ranges.sort(key=lambda a: a.StartID)
 				for r in ranges:
 					del r.StartID
 				format = 2
@@ -120,7 +121,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 = []
@@ -142,16 +143,16 @@
 		if self.Format == 1:
 			delta = rawTable["DeltaGlyphID"]
 			inputGIDS =  [ font.getGlyphID(name) for name in input ]
-			inputGIDS = map(doModulo, inputGIDS) 
+			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
@@ -160,11 +161,10 @@
 		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()
+		sortableItems = sorted(zip(gidItems, items))
 
 		# figure out format
 		format = 2
@@ -193,14 +193,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 = {}
@@ -219,10 +218,10 @@
 			classList = rawTable["ClassValueArray"]
 			lenList = len(classList)
 			glyphID = font.getGlyphID(start)
-			gidList = range(glyphID, glyphID + len(classList))
+			gidList = list(range(glyphID, glyphID + len(classList)))
 			keyList = [getGlyphName(glyphID) for glyphID in gidList]
 
-			map(operator.setitem, [classDefs]*lenList, keyList, classList)
+			list(map(operator.setitem, [classDefs]*lenList, keyList, classList))
 
 		elif self.Format == 2:
 			records = rawTable["ClassRangeRecord"]
@@ -231,10 +230,10 @@
 				end = rec.End
 				cls = rec.Class
 				classDefs[start] = cls
-				glyphIDs = range(font.getGlyphID(start) + 1, font.getGlyphID(end))
+				glyphIDs = list(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)
+				list(map(operator.setitem,  [classDefs]*lenList, keyList, [cls]*lenList))
 				classDefs[end] = cls
 		else:
 			assert 0, "unknown format: %s" % self.Format
@@ -244,7 +243,7 @@
 		classDefs = getattr(self, "classDefs", None)
 		if classDefs is None:
 			classDefs = self.classDefs = {}
-		items = classDefs.items()
+		items = list(classDefs.items())
 		getGlyphID = font.getGlyphID
 		for i in range(len(items)):
 			glyphName, cls = items[i]
@@ -273,13 +272,12 @@
 		return {"ClassRangeRecord": ranges}
 	
 	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 = {}
@@ -307,7 +305,7 @@
 		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
@@ -329,8 +327,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()
@@ -340,7 +337,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 = {}
@@ -349,7 +346,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"])
@@ -373,7 +370,7 @@
 		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
@@ -396,8 +393,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()
@@ -408,7 +404,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 = {}
@@ -417,7 +413,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()
@@ -523,14 +519,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 
@@ -552,14 +547,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 
@@ -648,8 +642,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()
@@ -662,15 +656,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 = {
@@ -711,7 +705,7 @@
 		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/ttProgram.py b/Lib/fontTools/ttLib/tables/ttProgram.py
index 647386e..156e4a2 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
 
@@ -198,7 +200,7 @@
 	return newPos
 
 
-class Program:
+class Program(object):
 	
 	def __init__(self):
 		pass
@@ -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,9 +258,9 @@
 			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:
@@ -266,11 +268,11 @@
 			self.fromBytecode(readHex(content))
 	
 	def _assemble(self, 
-			skipWhite=_skipWhite, mnemonicDict=mnemonicDict, strip=string.strip,
+			skipWhite=_skipWhite, mnemonicDict=mnemonicDict,
 			binary2num=binary2num):
 		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)
@@ -278,21 +280,21 @@
 		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)
+			arg = arg.strip()
 			if mnemonic.startswith("INSTR"):
 				# Unknown instruction
 				op = int(mnemonic[5:])
 				push(op)
 			elif mnemonic not in ("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 len(arg) != argBits:
+					raise tt_instructions_error("Incorrect number of argument bits (%s[%s])" % (mnemonic, arg))
 				if arg:
 					arg = binary2num(arg)
 					push(op + arg)
@@ -304,7 +306,7 @@
 				while pos < lenAssembly:
 					m = _tokenRE.match(assembly, pos)
 					if m is None:
-						raise tt_instructions_error, "Syntax error in TT program (%s)" % assembly[pos:pos+15]
+						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
@@ -330,7 +332,7 @@
 					push(op)
 					push(nArgs)
 				else:
-					raise tt_instructions_error, "More than 255 push arguments (%s)" % nArgs
+					raise tt_instructions_error("More than 255 push arguments (%s)" % nArgs)
 				if words:
 					for value in args:
 						push((value >> 8) & 0xff)
@@ -378,14 +380,14 @@
 						assembly.append("%s[ ]  /* %s values pushed */" % (mnemonic, nValues))
 					for j in range(pushBytes):
 						value = bytecode[i]
-						assembly.append(`value`)
+						assembly.append(repr(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`)
+						assembly.append(repr(value))
 						i = i + 2
 				else:
 					assembly.append("INSTR%d[ ]" % op)
@@ -406,5 +408,5 @@
 	p.fromBytecode(bc)
 	asm = p.getAssembly()
 	p.fromAssembly(asm)
-	print bc == p.getBytecode()
+	print(bc == p.getBytecode())
 
diff --git a/Lib/fontTools/ttx.py b/Lib/fontTools/ttx.py
index 08f7694..ae3c115 100644
--- a/Lib/fontTools/ttx.py
+++ b/Lib/fontTools/ttx.py
@@ -66,18 +66,20 @@
 """
 
 
-import sys
-import os
-import getopt
-import re
+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
+	print(__doc__ % version)
 	sys.exit(2)
 
 	
@@ -98,7 +100,7 @@
 	return output
 
 
-class Options:
+class Options(object):
 
 	listTables = False
 	outputDir = None
@@ -120,11 +122,11 @@
 		for option, value in rawOptions:
 			# general options
 			if option == "-h":
-				print __doc__ % 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":
@@ -147,7 +149,7 @@
 			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":
@@ -162,37 +164,35 @@
 			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, 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):
 	if not options.quiet:
-		print 'Dumping "%s" to "%s"...' % (input, output)
+		print('Dumping "%s" to "%s"...' % (input, output))
 	ttf = TTFont(input, 0, verbose=options.verbose, allowVID=options.allowVID,
 			lazy=False,
 			quiet=options.quiet,
@@ -210,7 +210,7 @@
 
 def ttCompile(input, output, options):
 	if not options.quiet:
-		print 'Compiling "%s" to "%s"...' % (input, output)
+		print('Compiling "%s" to "%s"...' % (input, output))
 	ttf = TTFont(options.mergeFile,
 			lazy=False,
 			recalcBBoxes=options.recalcBBoxes,
@@ -218,13 +218,13 @@
 	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:
 				ok = fixLookupOverFlows(ttf, overflowRecord)
@@ -236,15 +236,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):
@@ -259,16 +259,18 @@
 	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 in ("wOFF", "true"):
+	elif head == "wOFF":
 		return "WOFF"
 	elif head.lower() == "<?xm":
+		# Use 'latin1' because that can't fail.
+		header = tostr(header, 'latin1')
 		if opentypeheaderRE.search(header):
 			return "OTX"
 		else:
@@ -303,7 +305,7 @@
 			extension = ".otf"
 			action = ttCompile
 		else:
-			print 'Unknown file type: "%s"' % input
+			print('Unknown file type: "%s"' % input)
 			continue
 		
 		if options.outputFile:
@@ -323,7 +325,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
 
@@ -333,14 +335,14 @@
 	try:
 		process(jobs, options)
 	except KeyboardInterrupt:
-		print "(Cancelled.)"
+		print("(Cancelled.)")
 	except SystemExit:
 		if sys.platform == "win32":
 			waitForKeyPress()
 		else:
 			raise
-	except TTLibError, e:
-		print "Error:",e
+	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/MetaTools/buildChangeLog.py b/MetaTools/buildChangeLog.py
index ec68e83..d29e379 100755
--- a/MetaTools/buildChangeLog.py
+++ b/MetaTools/buildChangeLog.py
@@ -7,4 +7,4 @@
 
 os.chdir(fontToolsDir)
 os.system("git2cl > Doc/ChangeLog")
-print "done."
+print("done.")
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/setup.py b/setup.py
index f00661e..4de9e07 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):