Refactored and enhanced table order support:
- Rewrote sorting function, it was really quite buggy.
- Added reorderFontTables() functions, which reorders the
  tables in a font at the sfnt level.
- TTFont.save() will now by default rewrite the font in the
  optimized order. This is done through a temp file since
  our dependency checking logic gets in the way of writing
  the tables in a predefined order directly (if table A depends
  on B, table B will always be compiled and written first, so
  this prevents A from showing up in the file before B).

sfnt.py:
- removed closeStream option from SFNTWriter.close(); it's better
  done by the caller (TTFont).


git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@475 4cde692c-a291-49d1-8350-778aa11640f8
diff --git a/Lib/fontTools/ttLib/__init__.py b/Lib/fontTools/ttLib/__init__.py
index a09c831..dccc055 100644
--- a/Lib/fontTools/ttLib/__init__.py
+++ b/Lib/fontTools/ttLib/__init__.py
@@ -42,7 +42,7 @@
 """
 
 #
-# $Id: __init__.py,v 1.44 2004-11-16 09:12:30 jvr Exp $
+# $Id: __init__.py,v 1.45 2004-11-16 10:37:59 jvr Exp $
 #
 
 import sys
@@ -137,7 +137,7 @@
 		if self.reader is not None:
 			self.reader.close()
 	
-	def save(self, file, makeSuitcase=0):
+	def save(self, file, makeSuitcase=0, reorderTables=1):
 		"""Save the font to disk. Similarly to the constructor, 
 		the 'file' argument can be either a pathname or a writable
 		file object.
@@ -165,13 +165,27 @@
 		if "GlyphOrder" in tags:
 			tags.remove("GlyphOrder")
 		numTables = len(tags)
-		writer = sfnt.SFNTWriter(file, numTables, self.sfntVersion)
+		if reorderTables:
+			import tempfile
+			tmp = tempfile.TemporaryFile(prefix="ttx-fonttools")
+		else:
+			tmp = file
+		writer = sfnt.SFNTWriter(tmp, numTables, self.sfntVersion)
 		
 		done = []
 		for tag in tags:
 			self._writeTable(tag, writer, done)
 		
-		writer.close(closeStream)
+		writer.close()
+
+		if reorderTables:
+			tmp.flush()
+			tmp.seek(0)
+			reorderFontTables(tmp, file)
+			tmp.close()
+
+		if closeStream:
+			file.close()
 	
 	def saveXML(self, fileOrPath, progress=None, 
 			tables=None, skipTables=None, splitTables=0, disassembleInstructions=1):
@@ -305,14 +319,10 @@
 				if key not in keys:
 					keys.append(key)
 
-		if "glyf" in keys:
-			tableSort = sortTTFFont
-		elif "CFF " in keys:
-			tableSort = sortOTFFont
-		else:
-			assert 0, "Font has neither glyf nor CFF table. Table list:" + str(keys)
-		keys.sort(tableSort)
-		return keys
+		if "GlyphOrder" in keys:
+			keys.remove("GlyphOrder")
+		keys = sortedTagList(keys)
+		return ["GlyphOrder"] + keys
 	
 	def __len__(self):
 		return len(self.keys())
@@ -795,7 +805,7 @@
 	if tag == "OS/2":
 		return "OS_2"
 	elif tag == "GlyphOrder":
-		return "GlyphOrder"
+		return tag
 	if re.match("[A-Za-z_][A-Za-z_0-9]* *$", tag):
 		return string.strip(tag)
 	else:
@@ -818,44 +828,47 @@
 	print msg + time.strftime("  (%H:%M:%S)", time.localtime(time.time()))
 
 
+# Table order as recommended in the OpenType specification 1.4
+TTFTableOrder = ["head", "hhea", "maxp", "OS/2", "hmtx", "LTSH", "VDMX",
+                  "hdmx", "cmap", "fpgm", "prep", "cvt ", "loca", "glyf",
+                  "kern", "name", "post", "gasp", "PCLT"]
 
-# Table sorting algorithm pre recommendations in OpenType Spec v1.4
-kTTFTableOrder = ["GlyphOrder", "head", "hhea", "maxp", "OS/2", "hmtx", "LTSH", "VDMX", "hdmx", "cmap", "fpgm", "prep", "cvt", "loca", "glyf", "kern", "name", "post", "gasp", "PCLT"]
-kOTFTableOrder = ["GlyphOrder", "head", "hhea", "maxp", "OS/2", "name", "cmap", "post", "CFF "]
-kNotInTableIndex = 10000 # an arbitrary value larger than will ever be a font.
-def sortFontTables(tag1, tag2, tableOrder):
-	#No need to allow for two tags with the same name.
-	if tag1 == "DSIG":
-		ret = -1
-	elif tag2 == "DSIG":
-		ret = 1
-	else:
-		try:
-			i1 = tableOrder.index(tag1)
-		except ValueError:
-			i1 = kNotInTableIndex
-		try:
-			i2 = tableOrder.index(tag2)
-		except ValueError:
-			i2 = kNotInTableIndex
-		
-		if i1 > i2:
-			ret = 1
-		elif i1 < i2:
-			ret  = -1
+OTFTableOrder = ["head", "hhea", "maxp", "OS/2", "name", "cmap", "post",
+                  "CFF "]
+
+def sortedTagList(tagList, tableOrder=None):
+	"""Return a sorted copy of tagList, sorted according to the OpenType
+	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()
+	if tableOrder is None:
+		if "DSIG" in tagList:
+			# DSIG should be last (XXX spec reference?)
+			tagList.remove("DSIG")
+			tagList.append("DSIG")
+		if "CFF " in tagList:
+			tableOrder = OTFTableOrder
 		else:
-			if tag1 < tag2:
-				ret = 1
-			elif tag1 < tag2:
-				ret = -1
-			else:
-				ret = 0
-	return ret
+			tableOrder = TTFTableOrder
+	orderedTables = []
+	for tag in tableOrder:
+		if tag in tagList:
+			orderedTables.append(tag)
+			tagList.remove(tag)
+	orderedTables.extend(tagList)
+	return orderedTables
 
 
-def sortTTFFont(tag1, tag2):
-	return sortFontTables(tag1, tag2, kTTFTableOrder)
-
-
-def sortOTFFont(tag1, tag2):
-	return sortFontTables(tag1, tag2, kOTFTableOrder)
+def reorderFontTables(inFile, outFile, tableOrder=None, checkChecksums=0):
+	"""Rewrite a font file, ordering the tables as recommended by the
+	OpenType specification 1.4.
+	"""
+	from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
+	reader = SFNTReader(inFile, checkChecksums=checkChecksums)
+	writer = SFNTWriter(outFile, reader.numTables, reader.sfntVersion)
+	tables = reader.keys()
+	for tag in sortedTagList(tables, tableOrder):
+		writer[tag] = reader[tag]
+	writer.close()
diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py
index 0ce0ca0..e94a516 100644
--- a/Lib/fontTools/ttLib/sfnt.py
+++ b/Lib/fontTools/ttLib/sfnt.py
@@ -116,7 +116,7 @@
 			entry.checkSum = calcChecksum(data)
 		self.tables[tag] = entry
 	
-	def close(self, closeStream=1):
+	def close(self):
 		"""All tables must have been written to disk. Now write the
 		directory.
 		"""
@@ -138,8 +138,6 @@
 			self.calcMasterChecksum(directory)
 		self.file.seek(0)
 		self.file.write(directory)
-		if closeStream:
-			self.file.close()
 	
 	def calcMasterChecksum(self, directory):
 		# calculate checkSumAdjustment