Merge pull request #103 from olivierberten/post4

'post' format 4.0 support
diff --git a/Lib/fontTools/merge.py b/Lib/fontTools/merge.py
index c3ae1cc..54e91e3 100644
--- a/Lib/fontTools/merge.py
+++ b/Lib/fontTools/merge.py
@@ -33,9 +33,10 @@
 # General utility functions for merging values from different fonts
 
 def equal(lst):
+	lst = list(lst)
 	t = iter(lst)
 	first = next(t)
-	assert all(item == first for item in t)
+	assert all(item == first for item in t), "Expected all items to be equal: %s" % lst
 	return first
 
 def first(lst):
@@ -57,7 +58,7 @@
 	lst = list(lst)
 	return sum(lst) // len(lst)
 
-def implementedFilter(func):
+def onlyExisting(func):
 	"""Returns a filter func that when called with a list,
 	only calls func on the non-NotImplemented items of the list,
 	and only so if there's at least one item remaining.
@@ -82,14 +83,19 @@
 	return d
 
 def mergeObjects(lst):
-	lst = [item for item in lst if item is not None and item is not NotImplemented]
+	lst = [item for item in lst if item is not NotImplemented]
 	if not lst:
-		return None # Not all can be NotImplemented
+		return NotImplemented
+	lst = [item for item in lst if item is not None]
+	if not lst:
+		return None
 
 	clazz = lst[0].__class__
 	assert all(type(item) == clazz for item in lst), lst
+
 	logic = clazz.mergeMap
 	returnTable = clazz()
+	returnDict = {}
 
 	allKeys = set.union(set(), *(vars(table).keys() for table in lst))
 	for key in allKeys:
@@ -105,25 +111,31 @@
 			continue
 		value = mergeLogic(getattr(table, key, NotImplemented) for table in lst)
 		if value is not NotImplemented:
-			setattr(returnTable, key, value)
+			returnDict[key] = value
+
+	returnTable.__dict__ = returnDict
 
 	return returnTable
 
-def mergeBits(logic, lst):
-	lst = list(lst)
-	returnValue = 0
-	for bitNumber in range(logic['size']):
-		try:
-			mergeLogic = logic[bitNumber]
-		except KeyError:
+def mergeBits(bitmap):
+
+	def wrapper(lst):
+		lst = list(lst)
+		returnValue = 0
+		for bitNumber in range(bitmap['size']):
 			try:
-				mergeLogic = logic['*']
+				mergeLogic = bitmap[bitNumber]
 			except KeyError:
-				raise Exception("Don't know how to merge bit %s" % bitNumber)
-		shiftedBit = 1 << bitNumber
-		mergedValue = mergeLogic(bool(item & shiftedBit) for item in lst)
-		returnValue |= mergedValue << bitNumber
-	return returnValue
+				try:
+					mergeLogic = bitmap['*']
+				except KeyError:
+					raise Exception("Don't know how to merge bit %s" % bitNumber)
+			shiftedBit = 1 << bitNumber
+			mergedValue = mergeLogic(bool(item & shiftedBit) for item in lst)
+			returnValue |= mergedValue << bitNumber
+		return returnValue
+
+	return wrapper
 
 
 @_add_method(DefaultTable, allowDefaultTable=True)
@@ -152,7 +164,7 @@
 	# maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions
 }
 
-headFlagsMergeMap = {
+headFlagsMergeBitMap = {
 	'size': 16,
 	'*': bitwise_or,
 	1: bitwise_and, # Baseline at y = 0
@@ -172,7 +184,7 @@
 	'fontRevision': max,
 	'checkSumAdjustment': lambda lst: 0, # We need *something* here
 	'magicNumber': equal,
-	'flags': lambda lst: mergeBits(headFlagsMergeMap, lst),
+	'flags': mergeBits(headFlagsMergeBitMap),
 	'unitsPerEm': equal,
 	'created': current_time,
 	'modified': current_time,
@@ -204,7 +216,7 @@
 	'numberOfHMetrics': recalculate,
 }
 
-os2FsTypeMergeMap = {
+os2FsTypeMergeBitMap = {
 	'size': 16,
 	'*': lambda bit: 0,
 	1: bitwise_or, # no embedding permitted
@@ -231,7 +243,7 @@
 		elif lst[i] == 0:
 			lst[i] = 0x000C
 
-	fsType = mergeBits(os2FsTypeMergeMap, lst)
+	fsType = mergeBits(os2FsTypeMergeBitMap)(lst)
 	# unset bits 2 and 3 if bit 1 is set (some font is "no embedding")
 	if fsType & 0x0002:
 		fsType &= ~0x000C
@@ -256,9 +268,10 @@
 	'sTypoLineGap': max,
 	'usWinAscent': max,
 	'usWinDescent': max,
-	'ulCodePageRange1': bitwise_or,
-	'ulCodePageRange2': bitwise_or,
-	'usMaxContex': max,
+	# Version 2,3,4
+	'ulCodePageRange1': onlyExisting(bitwise_or),
+	'ulCodePageRange2': onlyExisting(bitwise_or),
+	'usMaxContex': onlyExisting(max),
 	# TODO version 5
 }
 
@@ -285,7 +298,7 @@
 	'maxMemType42': lambda lst: 0,
 	'minMemType1': max,
 	'maxMemType1': lambda lst: 0,
-	'mapping': implementedFilter(sumDicts),
+	'mapping': onlyExisting(sumDicts),
 	'extraNames': lambda lst: [],
 }
 
@@ -337,21 +350,30 @@
 @_add_method(ttLib.getTableClass('cmap'))
 def merge(self, m, tables):
 	# TODO Handle format=14.
-	cmapTables = [t for table in tables for t in table.tables
-		      if t.platformID == 3 and t.platEncID in [1, 10]]
+	cmapTables = [(t,fontIdx) for fontIdx,table in enumerate(tables) for t in table.tables
+		      if t.isUnicode()]
 	# TODO Better handle format-4 and format-12 coexisting in same font.
 	# TODO Insert both a format-4 and format-12 if needed.
 	module = ttLib.getTableModule('cmap')
-	assert all(t.format in [4, 12] for t in cmapTables)
-	format = max(t.format for t in cmapTables)
+	assert all(t.format in [4, 12] for t,_ in cmapTables)
+	format = max(t.format for t,_ in cmapTables)
 	cmapTable = module.cmap_classes[format](format)
 	cmapTable.cmap = {}
 	cmapTable.platformID = 3
-	cmapTable.platEncID = max(t.platEncID for t in cmapTables)
+	cmapTable.platEncID = max(t.platEncID for t,_ in cmapTables)
 	cmapTable.language = 0
-	for table in cmapTables:
+	cmap = cmapTable.cmap
+	for table,fontIdx in cmapTables:
 		# TODO handle duplicates.
-		cmapTable.cmap.update(table.cmap)
+		for uni,gid in table.cmap.items():
+			oldgid = cmap.get(uni, None)
+			if oldgid is None:
+				cmap[uni] = gid
+			elif oldgid != gid:
+				# Char previously mapped to oldgid, now to gid.
+				# Record, to fix up in GSUB 'locl' later.
+				assert m.duplicateGlyphsPerFont[fontIdx].get(oldgid, gid) == gid
+				m.duplicateGlyphsPerFont[fontIdx][oldgid] = gid
 	self.tableVersion = 0
 	self.tables = [cmapTable]
 	self.numSubTables = len(self.tables)
@@ -417,10 +439,155 @@
 ttLib.getTableClass('JSTF').mergeMap = \
 ttLib.getTableClass('MATH').mergeMap = \
 {
-	'tableTag': equal,
+	'tableTag': onlyExisting(equal), # XXX clean me up
 	'table': mergeObjects,
 }
 
+@_add_method(ttLib.getTableClass('GSUB'))
+def merge(self, m, tables):
+
+	assert len(tables) == len(m.duplicateGlyphsPerFont)
+	for i,(table,dups) in enumerate(zip(tables, m.duplicateGlyphsPerFont)):
+		if not dups: continue
+		assert (table is not None and table is not NotImplemented), "Have duplicates to resolve for font %d but no GSUB" % (i + 1)
+		lookupMap = dict((id(v),v) for v in table.table.LookupList.Lookup)
+		featureMap = dict((id(v),v) for v in table.table.FeatureList.FeatureRecord)
+		synthFeature = None
+		synthLookup = None
+		for script in table.table.ScriptList.ScriptRecord:
+			if script.ScriptTag == 'DFLT': continue # XXX
+			for langsys in [script.Script.DefaultLangSys] + [l.LangSys for l in script.Script.LangSysRecord]:
+				feature = [featureMap[v] for v in langsys.FeatureIndex if featureMap[v].FeatureTag == 'locl']
+				assert len(feature) <= 1
+				if feature:
+					feature = feature[0]
+				else:
+					if not synthFeature:
+						synthFeature = otTables.FeatureRecord()
+						synthFeature.FeatureTag = 'locl'
+						f = synthFeature.Feature = otTables.Feature()
+						f.FeatureParams = None
+						f.LookupCount = 0
+						f.LookupListIndex = []
+						langsys.FeatureIndex.append(id(synthFeature))
+						featureMap[id(synthFeature)] = synthFeature
+						langsys.FeatureIndex.sort(key=lambda v: featureMap[v].FeatureTag)
+						table.table.FeatureList.FeatureRecord.append(synthFeature)
+						table.table.FeatureList.FeatureCount += 1
+					feature = synthFeature
+
+				if not synthLookup:
+					subtable = otTables.SingleSubst()
+					subtable.mapping = dups
+					synthLookup = otTables.Lookup()
+					synthLookup.LookupFlag = 0
+					synthLookup.LookupType = 1
+					synthLookup.SubTableCount = 1
+					synthLookup.SubTable = [subtable]
+					table.table.LookupList.Lookup.append(synthLookup)
+					table.table.LookupList.LookupCount += 1
+
+				feature.Feature.LookupListIndex[:0] = [id(synthLookup)]
+				feature.Feature.LookupCount += 1
+
+
+	DefaultTable.merge(self, m, tables)
+	return self
+
+
+
+@_add_method(otTables.SingleSubst,
+             otTables.MultipleSubst,
+             otTables.AlternateSubst,
+             otTables.LigatureSubst,
+             otTables.ReverseChainSingleSubst,
+             otTables.SinglePos,
+             otTables.PairPos,
+             otTables.CursivePos,
+             otTables.MarkBasePos,
+             otTables.MarkLigPos,
+             otTables.MarkMarkPos)
+def mapLookups(self, lookupMap):
+  pass
+
+# Copied and trimmed down from subset.py
+@_add_method(otTables.ContextSubst,
+             otTables.ChainContextSubst,
+             otTables.ContextPos,
+             otTables.ChainContextPos)
+def __classify_context(self):
+
+  class ContextHelper(object):
+    def __init__(self, klass, Format):
+      if klass.__name__.endswith('Subst'):
+        Typ = 'Sub'
+        Type = 'Subst'
+      else:
+        Typ = 'Pos'
+        Type = 'Pos'
+      if klass.__name__.startswith('Chain'):
+        Chain = 'Chain'
+      else:
+        Chain = ''
+      ChainTyp = Chain+Typ
+
+      self.Typ = Typ
+      self.Type = Type
+      self.Chain = Chain
+      self.ChainTyp = ChainTyp
+
+      self.LookupRecord = Type+'LookupRecord'
+
+      if Format == 1:
+        self.Rule = ChainTyp+'Rule'
+        self.RuleSet = ChainTyp+'RuleSet'
+      elif Format == 2:
+        self.Rule = ChainTyp+'ClassRule'
+        self.RuleSet = ChainTyp+'ClassSet'
+
+  if self.Format not in [1, 2, 3]:
+    return None  # Don't shoot the messenger; let it go
+  if not hasattr(self.__class__, "__ContextHelpers"):
+    self.__class__.__ContextHelpers = {}
+  if self.Format not in self.__class__.__ContextHelpers:
+    helper = ContextHelper(self.__class__, self.Format)
+    self.__class__.__ContextHelpers[self.Format] = helper
+  return self.__class__.__ContextHelpers[self.Format]
+
+
+@_add_method(otTables.ContextSubst,
+             otTables.ChainContextSubst,
+             otTables.ContextPos,
+             otTables.ChainContextPos)
+def mapLookups(self, lookupMap):
+  c = self.__classify_context()
+
+  if self.Format in [1, 2]:
+    for rs in getattr(self, c.RuleSet):
+      if not rs: continue
+      for r in getattr(rs, c.Rule):
+        if not r: continue
+        for ll in getattr(r, c.LookupRecord):
+          if not ll: continue
+          ll.LookupListIndex = lookupMap[ll.LookupListIndex]
+  elif self.Format == 3:
+    for ll in getattr(self, c.LookupRecord):
+      if not ll: continue
+      ll.LookupListIndex = lookupMap[ll.LookupListIndex]
+  else:
+    assert 0, "unknown format: %s" % self.Format
+
+@_add_method(otTables.Lookup)
+def mapLookups(self, lookupMap):
+	for st in self.SubTable:
+		if not st: continue
+		st.mapLookups(lookupMap)
+
+@_add_method(otTables.LookupList)
+def mapLookups(self, lookupMap):
+	for l in self.Lookup:
+		if not l: continue
+		l.mapLookups(lookupMap)
 
 @_add_method(otTables.Feature)
 def mapLookups(self, lookupMap):
@@ -563,14 +730,21 @@
 		for font in fonts:
 			self._preMerge(font)
 
+		self.duplicateGlyphsPerFont = [{} for f in fonts]
+
 		allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
 		allTags.remove('GlyphOrder')
+		allTags.remove('cmap')
+		allTags.remove('GSUB')
+		allTags = ['cmap', 'GSUB'] + list(allTags)
 		for tag in allTags:
 
-			clazz = ttLib.getTableClass(tag)
-
 			tables = [font.get(tag, NotImplemented) for font in fonts]
+
+			clazz = ttLib.getTableClass(tag)
 			table = clazz(tag).merge(self, tables)
+			# XXX Clean this up and use:  table = mergeObjects(tables)
+
 			if table is not NotImplemented and table is not False:
 				mega[tag] = table
 				self.log("Merged '%s'." % tag)
@@ -578,6 +752,8 @@
 				self.log("Dropped '%s'." % tag)
 			self.log.lapse("merge '%s'" % tag)
 
+		del self.duplicateGlyphsPerFont
+
 		self._postMerge(mega)
 
 		return mega
@@ -600,9 +776,6 @@
 		# Right now we don't use self at all.  Will use in the future
 		# for options and logging.
 
-		if logic is NotImplemented:
-			return NotImplemented
-
 		allKeys = set.union(set(), *(vars(table).keys() for table in tables if table is not NotImplemented))
 		for key in allKeys:
 			try:
@@ -623,6 +796,8 @@
 
 	def _preMerge(self, font):
 
+		# Map indices to references
+
 		GDEF = font.get('GDEF')
 		GSUB = font.get('GSUB')
 		GPOS = font.get('GPOS')
@@ -630,12 +805,15 @@
 		for t in [GSUB, GPOS]:
 			if not t: continue
 
-			if t.table.LookupList and t.table.FeatureList:
-				lookupMap = {i:id(v) for i,v in enumerate(t.table.LookupList.Lookup)}
-				t.table.FeatureList.mapLookups(lookupMap)
+			if t.table.LookupList:
+				lookupMap = dict((i,id(v)) for i,v in enumerate(t.table.LookupList.Lookup))
+				t.table.LookupList.mapLookups(lookupMap)
+				if t.table.FeatureList:
+					# XXX Handle present FeatureList but absent LookupList
+					t.table.FeatureList.mapLookups(lookupMap)
 
 			if t.table.FeatureList and t.table.ScriptList:
-				featureMap = {i:id(v) for i,v in enumerate(t.table.FeatureList.FeatureRecord)}
+				featureMap = dict((i,id(v)) for i,v in enumerate(t.table.FeatureList.FeatureRecord))
 				t.table.ScriptList.mapFeatures(featureMap)
 
 		# TODO GDEF/Lookup MarkFilteringSets
@@ -643,6 +821,8 @@
 
 	def _postMerge(self, font):
 
+		# Map references back to indices
+
 		GDEF = font.get('GDEF')
 		GSUB = font.get('GSUB')
 		GPOS = font.get('GPOS')
@@ -650,12 +830,16 @@
 		for t in [GSUB, GPOS]:
 			if not t: continue
 
-			if t.table.LookupList and t.table.FeatureList:
-				lookupMap = {id(v):i for i,v in enumerate(t.table.LookupList.Lookup)}
-				t.table.FeatureList.mapLookups(lookupMap)
+			if t.table.LookupList:
+				lookupMap = dict((id(v),i) for i,v in enumerate(t.table.LookupList.Lookup))
+				t.table.LookupList.mapLookups(lookupMap)
+				if t.table.FeatureList:
+					# XXX Handle present FeatureList but absent LookupList
+					t.table.FeatureList.mapLookups(lookupMap)
 
 			if t.table.FeatureList and t.table.ScriptList:
-				featureMap = {id(v):i for i,v in enumerate(t.table.FeatureList.FeatureRecord)}
+				# XXX Handle present ScriptList but absent FeatureList
+				featureMap = dict((id(v),i) for i,v in enumerate(t.table.FeatureList.FeatureRecord))
 				t.table.ScriptList.mapFeatures(featureMap)
 
 		# TODO GDEF/Lookup MarkFilteringSets
diff --git a/Lib/fontTools/misc/xmlReader.py b/Lib/fontTools/misc/xmlReader.py
index 581039d..85dd441 100644
--- a/Lib/fontTools/misc/xmlReader.py
+++ b/Lib/fontTools/misc/xmlReader.py
@@ -79,7 +79,7 @@
 					print(msg)
 			if tag == "GlyphOrder":
 				tableClass = ttLib.GlyphOrder
-			elif "ERROR" in attrs:
+			elif "ERROR" in attrs or ('raw' in attrs and safeEval(attrs['raw'])):
 				tableClass = DefaultTable
 			else:
 				tableClass = ttLib.getTableClass(tag)
diff --git a/Lib/fontTools/subset.py b/Lib/fontTools/subset.py
index f50d206..45bd457 100644
--- a/Lib/fontTools/subset.py
+++ b/Lib/fontTools/subset.py
@@ -218,7 +218,10 @@
       p.PairValueRecord = [r for r in p.PairValueRecord
                            if r.SecondGlyph in s.glyphs]
       p.PairValueCount = len(p.PairValueRecord)
-    self.PairSet = [p for p in self.PairSet if p.PairValueCount]
+    # Remove empty pairsets
+    indices = [i for i,p in enumerate(self.PairSet) if p.PairValueCount]
+    self.Coverage.remap(indices)
+    self.PairSet = [self.PairSet[i] for i in indices]
     self.PairSetCount = len(self.PairSet)
     return bool(self.PairSetCount)
   elif self.Format == 2:
@@ -1696,27 +1699,30 @@
 
 @_add_method(ttLib.getTableClass('cmap'))
 def closure_glyphs(self, s):
-  tables = [t for t in self.tables
-            if t.platformID == 3 and t.platEncID in [1, 10]]
+  tables = [t for t in self.tables if t.isUnicode()]
   for u in s.unicodes_requested:
     found = False
     for table in tables:
-      if u in table.cmap:
-        s.glyphs.add(table.cmap[u])
-        found = True
-        break
+      if table.format == 14:
+        for l in table.uvsDict.values():
+          # TODO(behdad) Speed this up!
+          gids = [g for uc,g in l if u == uc and g is not None]
+          s.glyphs.update(gids)
+          # Intentionally not setting found=True here.
+      else:
+        if u in table.cmap:
+          s.glyphs.add(table.cmap[u])
+          found = True
     if not found:
-      s.log("No glyph for Unicode value %s; skipping." % u)
+      s.log("No default glyph for Unicode %04X found." % u)
 
 @_add_method(ttLib.getTableClass('cmap'))
 def prune_pre_subset(self, options):
   if not options.legacy_cmap:
     # Drop non-Unicode / non-Symbol cmaps
-    self.tables = [t for t in self.tables
-                   if t.platformID == 3 and t.platEncID in [0, 1, 10]]
+    self.tables = [t for t in self.tables if t.isUnicode() or t.isSymbol()]
   if not options.symbol_cmap:
-    self.tables = [t for t in self.tables
-                   if t.platformID == 3 and t.platEncID in [1, 10]]
+    self.tables = [t for t in self.tables if not t.isSymbol()]
   # TODO(behdad) Only keep one subtable?
   # For now, drop format=0 which can't be subset_glyphs easily?
   self.tables = [t for t in self.tables if t.format != 0]
@@ -1734,13 +1740,18 @@
     except AttributeError:
       pass
     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])
+      # TODO(behdad) We drop all the default-UVS mappings for glyphs_requested.
+      # I don't think we care about that...
+      t.uvsDict = dict((v,[(u,g) for u,g in l
+                           if g in s.glyphs or u in s.unicodes_requested])
                        for v,l in t.uvsDict.items())
       t.uvsDict = dict((v,l) for v,l in t.uvsDict.items() if l)
-    else:
+    elif t.isUnicode():
       t.cmap = dict((u,g) for u,g in t.cmap.items()
                     if g in s.glyphs_requested or u in s.unicodes_requested)
+    else:
+      t.cmap = dict((u,g) for u,g in t.cmap.items()
+                    if g in s.glyphs_requested)
   self.tables = [t for t in self.tables
                  if (t.cmap if t.format != 14 else t.uvsDict)]
   self.numSubTables = len(self.tables)
@@ -1755,9 +1766,10 @@
   if '*' not in options.name_IDs:
     self.names = [n for n in self.names if n.nameID in options.name_IDs]
   if not options.name_legacy:
-    self.names = [n for n in self.names
-                  if n.platformID == 3 and n.platEncID == 1]
+    self.names = [n for n in self.names if n.isUnicode()]
+  # TODO(behdad) Option to keep only one platform's
   if '*' not in options.name_languages:
+    # TODO(behdad) This is Windows-platform specific!
     self.names = [n for n in self.names if n.langID in options.name_languages]
   return True  # Required table
 
@@ -1823,6 +1835,7 @@
   notdef_outline = False # No need for notdef to have an outline really
   recommended_glyphs = False  # gid1, gid2, gid3 for TrueType
   recalc_bounds = False # Recalculate font bounding boxes
+  recalc_timestamp = False # Recalculate font modified timestamp
   canonical_order = False # Order tables as recommended
   flavor = None # May be 'woff'
 
@@ -2112,6 +2125,7 @@
                       allowVID=allowVID,
                       checkChecksums=checkChecksums,
                       recalcBBoxes=options.recalc_bounds,
+                      recalcTimestamp=options.recalc_timestamp,
                       lazy=lazy)
 
   # Hack:
diff --git a/Lib/fontTools/ttLib/__init__.py b/Lib/fontTools/ttLib/__init__.py
index 3866451..9879a4e 100644
--- a/Lib/fontTools/ttLib/__init__.py
+++ b/Lib/fontTools/ttLib/__init__.py
@@ -72,7 +72,7 @@
 	def __init__(self, file=None, res_name_or_index=None,
 			sfntVersion="\000\001\000\000", flavor=None, checkChecksums=False,
 			verbose=False, recalcBBoxes=True, allowVID=False, ignoreDecompileErrors=False,
-			fontNumber=-1, lazy=False, quiet=False):
+			recalcTimestamp=True, fontNumber=-1, lazy=False, quiet=False):
 		
 		"""The constructor can be called with a few different arguments.
 		When reading a font from disk, 'file' should be either a pathname
@@ -106,6 +106,9 @@
 		greatly, and therefore should have some impact on the time needed 
 		to parse/compile large fonts.
 
+		If the recalcTimestamp argument is false, the modified timestamp in the
+		'head' table will *not* be recalculated upon save/compile.
+
 		If the allowVID argument is set to true, then virtual GID's are
 		supported. Asking for a glyph ID with a glyph name or GID that is not in
 		the font will return a virtual GID.   This is valid for GSUB and cmap
@@ -130,6 +133,7 @@
 		self.quiet = quiet
 		self.lazy = lazy
 		self.recalcBBoxes = recalcBBoxes
+		self.recalcTimestamp = recalcTimestamp
 		self.tables = {}
 		self.reader = None
 
@@ -761,7 +765,7 @@
 	table, but it's nice to present it as such in the TTX format.
 	"""
 	
-	def __init__(self, tag):
+	def __init__(self, tag=None):
 		pass
 	
 	def toXML(self, writer, ttFont):
@@ -816,6 +820,15 @@
 	return tableClass
 
 
+def getClassTag(klass):
+	"""Fetch the table tag for a class object."""
+	name = klass.__name__
+	assert name[:6] == 'table_'
+	name = name[6:] # Chop 'table_'
+	return identifierToTag(name)
+
+
+
 def newTable(tag):
 	"""Return a new instance of a table."""
 	tableClass = getTableClass(tag)
@@ -876,7 +889,7 @@
 			tag = tag + ident[i]
 		else:
 			# assume hex
-			tag = tag + bytechr(int(ident[i:i+2], 16))
+			tag = tag + chr(int(ident[i:i+2], 16))
 	# append trailing spaces
 	tag = tag + (4 - len(tag)) * ' '
 	return Tag(tag)
diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py
index 0fd12c1..c6bc93a 100644
--- a/Lib/fontTools/ttLib/sfnt.py
+++ b/Lib/fontTools/ttLib/sfnt.py
@@ -55,14 +55,7 @@
 		for i in range(self.numTables):
 			entry = self.DirectoryEntry()
 			entry.fromFile(self.file)
-			if entry.length > 0:
-				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.
-				# Besides, at least one font has been sighted which actually
-				# *has* a zero-length table.
-				pass
+			self.tables[Tag(entry.tag)] = entry
 
 		# Load flavor data if any
 		if self.flavor == "woff":
diff --git a/Lib/fontTools/ttLib/tables/C_O_L_R_.py b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
index d0c8b21..139de3c 100644
--- a/Lib/fontTools/ttLib/tables/C_O_L_R_.py
+++ b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
@@ -138,6 +138,9 @@
 		elif glyphSelector in self.ColorLayers:
 			del self.ColorLayers[glyphSelector]
 
+	def __delitem__(self, glyphSelector):
+		del self.ColorLayers[glyphSelector]
+
 class LayerRecord(object):
 
 	def __init__(self, name = None, colorID = None):
diff --git a/Lib/fontTools/ttLib/tables/DefaultTable.py b/Lib/fontTools/ttLib/tables/DefaultTable.py
index e2e7685..3a6886c 100644
--- a/Lib/fontTools/ttLib/tables/DefaultTable.py
+++ b/Lib/fontTools/ttLib/tables/DefaultTable.py
@@ -1,11 +1,14 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
+from fontTools.ttLib import getClassTag
 
 class DefaultTable(object):
 	
 	dependencies = []
 	
-	def __init__(self, tag):
+	def __init__(self, tag=None):
+		if tag is None:
+			tag = getClassTag(self.__class__)
 		self.tableTag = Tag(tag)
 	
 	def decompile(self, data, ttFont):
diff --git a/Lib/fontTools/ttLib/tables/V_O_R_G_.py b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
index ef80795..19f25b5 100644
--- a/Lib/fontTools/ttLib/tables/V_O_R_G_.py
+++ b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
@@ -115,6 +115,9 @@
 		elif glyphSelector in self.VOriginRecords:
 			del self.VOriginRecords[glyphSelector]
 
+	def __delitem__(self, glyphSelector):
+		del self.VOriginRecords[glyphSelector]
+
 class VOriginRecord(object):
 
 	def __init__(self, name = None, vOrigin = None):
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
index 3ffe026..fbfd2ee 100644
--- a/Lib/fontTools/ttLib/tables/_c_m_a_p.py
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -1,6 +1,7 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval, readHex
+from fontTools.unicode import Unicode
 from . import DefaultTable
 import sys
 import struct
@@ -137,12 +138,15 @@
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
 
+	def isUnicode(self):
+		return (self.platformID == 0 or
+			(self.platformID == 3 and self.platEncID in [1, 10]))
+
+	def isSymbol(self):
+		return self.platformID == 3 and self.platEncID == 0
+
 	def _writeCodes(self, codes, writer):
-		if (self.platformID, self.platEncID) == (3, 1) or (self.platformID, self.platEncID) == (3, 10) or self.platformID == 0:
-			from fontTools.unicode import Unicode
-			isUnicode = 1
-		else:
-			isUnicode = 0
+		isUnicode = self.isUnicode()
 		for code, name in codes:
 			writer.simpletag("map", code=hex(code), name=name)
 			if isUnicode:
@@ -661,21 +665,25 @@
 		charCodes = []
 		gids = []
 		for i in range(len(startCode) - 1):	# don't do 0xffff!
+			start = startCode[i]
+			delta = idDelta[i]
+			rangeOffset = idRangeOffset[i]
+			# *someone* needs to get killed.
+			partial = rangeOffset // 2 - start + i - len(idRangeOffset)
+
 			rangeCharCodes = list(range(startCode[i], endCode[i] + 1))
-			charCodes = charCodes + rangeCharCodes
-			for charCode in rangeCharCodes:
-				rangeOffset = idRangeOffset[i]
-				if rangeOffset == 0:
-					glyphID = charCode + idDelta[i]
-				else:
-					# *someone* needs to get killed.
-					index = idRangeOffset[i] // 2 + (charCode - startCode[i]) + i - len(idRangeOffset)
+			charCodes.extend(rangeCharCodes)
+			if rangeOffset == 0:
+				gids.extend([(charCode + delta) & 0xFFFF for charCode in rangeCharCodes])
+			else:
+				for charCode in rangeCharCodes:
+					index = charCode + partial
 					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
-						glyphID = glyphIndexArray[index] + idDelta[i]
+						glyphID = glyphIndexArray[index] + delta
 					else:
 						glyphID = 0  # missing glyph
-				gids.append(glyphID % 0x10000)
+					gids.append(glyphID & 0xFFFF)
 
 		self.cmap = cmap = {}
 		lenCmap = len(gids)
@@ -931,8 +939,8 @@
 			startCharCode, endCharCode, glyphID = struct.unpack(">LLL",data[pos:pos+12] )
 			pos += 12
 			lenGroup = 1 + endCharCode - startCharCode
-			charCodes += list(range(startCharCode, endCharCode +1))
-			gids += self._computeGIDs(glyphID, lenGroup)
+			charCodes.extend(list(range(startCharCode, endCharCode +1)))
+			gids.extend(self._computeGIDs(glyphID, lenGroup))
 		self.data = data = None
 		self.cmap = cmap = {}
 		lenCmap = len(gids)
diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
index 3434d35..970980b 100644
--- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -991,6 +991,8 @@
 	def __setitem__(self, k, v):
 		if isinstance(k, slice):
 			indices = range(*k.indices(len(self)))
+			# XXX This only works if len(v) == len(indices)
+			# TODO Implement __delitem__
 			for j,i in enumerate(indices):
 				self[i] = v[j]
 			return
diff --git a/Lib/fontTools/ttLib/tables/_h_e_a_d.py b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
index 97dd1a7..bf4116d 100644
--- a/Lib/fontTools/ttLib/tables/_h_e_a_d.py
+++ b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
@@ -39,7 +39,8 @@
 			assert rest == "\0\0"
 	
 	def compile(self, ttFont):
-		self.modified = int(time.time() - mac_epoch_diff)
+		if ttFont.recalcTimestamp:
+			self.modified = int(time.time() - mac_epoch_diff)
 		data = sstruct.pack(headFormat, self)
 		return data
 	
diff --git a/Lib/fontTools/ttLib/tables/_h_m_t_x.py b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
index acb686b..c7b5ee9 100644
--- a/Lib/fontTools/ttLib/tables/_h_m_t_x.py
+++ b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
@@ -33,12 +33,13 @@
 		if data:
 			warnings.warn("too much 'hmtx'/'vmtx' table data")
 		self.metrics = {}
+		glyphOrder = ttFont.getGlyphOrder()
 		for i in range(numberOfMetrics):
-			glyphName = ttFont.getGlyphName(i)
+			glyphName = glyphOrder[i]
 			self.metrics[glyphName] = list(metrics[i*2:i*2+2])
 		lastAdvance = metrics[-2]
 		for i in range(numberOfSideBearings):
-			glyphName = ttFont.getGlyphName(i + numberOfMetrics)
+			glyphName = glyphOrder[i + numberOfMetrics]
 			self.metrics[glyphName] = [lastAdvance, sideBearings[i]]
 	
 	def compile(self, ttFont):
@@ -89,6 +90,9 @@
 		if name == "mtx":
 			self.metrics[attrs["name"]] = [safeEval(attrs[self.advanceName]), 
 					safeEval(attrs[self.sideBearingName])]
+
+	def __delitem__(self, glyphName):
+		del self.metrics[glyphName]
 	
 	def __getitem__(self, glyphName):
 		return self.metrics[glyphName]
diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
index d78b838..53fde4d 100644
--- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py
+++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
@@ -90,6 +90,10 @@
 
 class NameRecord(object):
 	
+	def isUnicode(self):
+		return (self.platformID == 0 or
+			(self.platformID == 3 and self.platEncID in [0, 1, 10]))
+
 	def toXML(self, writer, ttFont):
 		writer.begintag("namerecord", [
 				("nameID", self.nameID),
@@ -98,7 +102,7 @@
 				("langID", hex(self.langID)),
 						])
 		writer.newline()
-		if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)):
+		if self.isUnicode():
 			if len(self.string) % 2:
 				# no, shouldn't happen, but some of the Apple
 				# tools cause this anyway :-(
@@ -117,7 +121,7 @@
 		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)):
+		if self.isUnicode():
 			self.string = s.encode("utf_16_be")
 		else:
 			# This is the inverse of write8bit...
diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py
index 83b8031..2afc2cc 100644
--- a/Lib/fontTools/ttLib/tables/otTables.py
+++ b/Lib/fontTools/ttLib/tables/otTables.py
@@ -548,7 +548,7 @@
 	lookups = ttf[overflowRecord.tableType].table.LookupList.Lookup
 	lookup = lookups[lookupIndex]
 	# If the previous lookup is an extType, look further back. Very unlikely, but possible.
-	while lookup.LookupType == extType:
+	while lookup.SubTable[0].__class__.LookupType == extType:
 		lookupIndex = lookupIndex -1
 		if lookupIndex < 0:
 			return ok
@@ -559,10 +559,8 @@
 		extSubTableClass = lookupTypes[overflowRecord.tableType][extType]
 		extSubTable = extSubTableClass()
 		extSubTable.Format = 1
-		extSubTable.ExtensionLookupType = lookup.LookupType
 		extSubTable.ExtSubTable = subTable
 		lookup.SubTable[si] = extSubTable
-	lookup.LookupType = extType
 	ok = 1
 	return ok
 
@@ -662,20 +660,19 @@
 		# We split the subtable of the Extension table, and add a new Extension table
 		# to contain the new subtable.
 
-		subTableType = subtable.ExtensionLookupType
+		subTableType = subtable.ExtSubTable.__class__.LookupType
 		extSubTable = subtable
 		subtable = extSubTable.ExtSubTable
-		newExtSubTableClass = lookupTypes[overflowRecord.tableType][lookup.LookupType]
+		newExtSubTableClass = lookupTypes[overflowRecord.tableType][subtable.__class__.LookupType]
 		newExtSubTable = newExtSubTableClass()
 		newExtSubTable.Format = extSubTable.Format
-		newExtSubTable.ExtensionLookupType = extSubTable.ExtensionLookupType
 		lookup.SubTable.insert(subIndex + 1, newExtSubTable)
 
 		newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
 		newSubTable = newSubTableClass()
 		newExtSubTable.ExtSubTable = newSubTable
 	else:
-		subTableType = lookup.LookupType
+		subTableType = subtable.__class__.LookupType
 		newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
 		newSubTable = newSubTableClass()
 		lookup.SubTable.insert(subIndex + 1, newSubTable)
diff --git a/Lib/fontTools/unicode.py b/Lib/fontTools/unicode.py
index c72fb9e..b599051 100644
--- a/Lib/fontTools/unicode.py
+++ b/Lib/fontTools/unicode.py
@@ -1,3 +1,5 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
 
 def _makeunicodes(f):
 	import re