Add support for saving WOFF file format
diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py
index 7943e1e..43bf78b 100644
--- a/Lib/fontTools/ttLib/sfnt.py
+++ b/Lib/fontTools/ttLib/sfnt.py
@@ -99,35 +99,64 @@
 
 class SFNTWriter:
 	
-	def __init__(self, file, numTables, sfntVersion="\000\001\000\000"):
+	def __init__(self, file, numTables, sfntVersion="\000\001\000\000",
+		     flavor=None, flavorData=None):
 		self.file = file
 		self.numTables = numTables
 		self.sfntVersion = sfntVersion
-		self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(numTables)
-		self.nextTableOffset = sfntDirectorySize + numTables * sfntDirectoryEntrySize
+		self.flavor = flavor
+		self.flavorData = flavorData
+
+		if self.flavor == "woff":
+			self.directoryFormat = woffDirectoryFormat
+			self.directorySize = woffDirectorySize
+			self.DirectoryEntry = WOFFDirectoryEntry
+
+			self.signature = "wOFF"
+		else:
+			assert not self.flavor,  "Unknown flavor '%s'" % self.flavor
+			self.directoryFormat = sfntDirectoryFormat
+			self.directorySize = sfntDirectorySize
+			self.DirectoryEntry = SFNTDirectoryEntry
+
+			self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(numTables)
+
+		self.nextTableOffset = self.directorySize + numTables * self.DirectoryEntry.formatSize
 		# clear out directory area
 		self.file.seek(self.nextTableOffset)
-		# make sure we're actually where we want to be. (XXX old cStringIO bug)
+		# make sure we're actually where we want to be. (old cStringIO bug)
 		self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
 		self.tables = {}
 	
 	def __setitem__(self, tag, data):
 		"""Write raw table data to disk."""
+		reuse = False
 		if self.tables.has_key(tag):
 			# We've written this table to file before. If the length
 			# of the data is still the same, we allow overwriting it.
 			entry = self.tables[tag]
+			assert not hasattr(entry.__class__, 'encodeData')
 			if len(data) <> entry.length:
 				from fontTools import ttLib
 				raise ttLib.TTLibError, "cannot rewrite '%s' table: length does not match directory entry" % tag
+			reuse = True
 		else:
-			entry = SFNTDirectoryEntry()
+			entry = self.DirectoryEntry()
 			entry.tag = tag
-			entry.offset = self.nextTableOffset
-			entry.length = len(data)
-			self.nextTableOffset = self.nextTableOffset + ((len(data) + 3) & ~3)
-		self.file.seek(entry.offset)
-		self.file.write(data)
+
+		if tag == 'head':
+			entry.checkSum = calcChecksum(data[:8] + '\0\0\0\0' + data[12:])
+			self.headTable = data
+			entry.uncompressed = True
+		else:
+			entry.checkSum = calcChecksum(data)
+
+		entry.offset = self.nextTableOffset
+		entry.saveData (self.file, data)
+
+		if not reuse:
+			self.nextTableOffset = self.nextTableOffset + ((entry.length + 3) & ~3)
+
 		# Add NUL bytes to pad the table data to a 4-byte boundary.
 		# 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
@@ -135,10 +164,6 @@
 		self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
 		assert self.nextTableOffset == self.file.tell()
 		
-		if tag == 'head':
-			entry.checkSum = calcChecksum(data[:8] + '\0\0\0\0' + data[12:])
-		else:
-			entry.checkSum = calcChecksum(data)
 		self.tables[tag] = entry
 	
 	def close(self):
@@ -150,10 +175,55 @@
 		if len(tables) <> self.numTables:
 			from fontTools import ttLib
 			raise ttLib.TTLibError, "wrong number of tables; expected %d, found %d" % (self.numTables, len(tables))
+
+		if self.flavor == "woff":
+			self.signature = "wOFF"
+			self.reserved = 0
+
+			self.totalSfntSize = 12
+			self.totalSfntSize += 16 * len(tables)
+			for tag, entry in tables:
+				self.totalSfntSize += (entry.origLength + 3) & ~3
+
+			data = self.flavorData if self.flavorData else WOFFFlavorData()
+			if data.majorVersion != None and data.minorVersion != None:
+				self.majorVersion = data.majorVersion
+				self.minorVersion = data.minorVersion
+			else:
+				if hasattr(self, 'headTable'):
+					self.majorVersion, self.minorVersion = struct.unpack(">HH", self.headTable[4:8])
+				else:
+					self.majorVersion = self.minorVersion = 0
+			if data.metaData:
+				self.metaOrigLength = len(data.metaData)
+				self.file.seek(0,2)
+				self.metaOffset = self.file.tell()
+				compressedMetaData = zlib.compress(data.metaData)
+				self.metaLength = len(compressedMetaData)
+				self.file.write(compressedMetaData)
+			else:
+				self.metaOffset = self.metaLength = self.metaOrigLength = 0
+			if data.privData:
+				self.file.seek(0,2)
+				off = self.file.tell()
+				paddedOff = (off + 3) & ~3
+				self.file.write('\0' * (paddedOff - off))
+				self.privOffset = self.file.tell()
+				self.privLength = len(data.privData)
+				self.file.write(data.privData)
+			else:
+				self.privOffset = self.privLength = 0
+
+			self.file.seek(0,2)
+			self.length = self.file.tell()
+
+		else:
+			assert not self.flavor,  "Unknown flavor '%s'" % self.flavor
+			pass
 		
-		directory = sstruct.pack(sfntDirectoryFormat, self)
+		directory = sstruct.pack(self.directoryFormat, self)
 		
-		self.file.seek(sfntDirectorySize)
+		self.file.seek(self.directorySize)
 		seenHead = 0
 		for tag, entry in tables:
 			if tag == "head":
@@ -171,6 +241,20 @@
 		for i in range(len(tags)):
 			checksums.append(self.tables[tags[i]].checkSum)
 
+		# TODO(behdad) I'm fairly sure the checksum for woff is not working correctly.
+		# Haven't debugged.
+		if self.DirectoryEntry != SFNTDirectoryEntry:
+			# 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()
+			for tag, entry in tables:
+				sfntEntry = SFNTDirectoryEntry()
+				for item in ['tag', 'checkSum', 'offset', 'length']:
+					setattr(sfntEntry, item, getattr(entry, item))
+				directory = directory + sfntEntry.toString()
+
 		directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize
 		assert directory_end == len(directory)
 
@@ -248,7 +332,7 @@
 		offset:         L
 		length:         L    # compressed length
 		origLength:     L    # original length
-		checksum:       L    # original checksum
+		checkSum:       L    # original checksum
 """
 
 woffDirectoryEntrySize = sstruct.calcsize(woffDirectoryEntryFormat)
@@ -256,6 +340,9 @@
 
 class DirectoryEntry:
 	
+	def __init__(self):
+		self.uncompressed = False # if True, always embed entry raw
+
 	def fromFile(self, file):
 		sstruct.unpack(self.format, file.read(self.formatSize), self)
 	
@@ -275,11 +362,23 @@
 		file.seek(self.offset)
 		data = file.read(self.length)
 		assert len(data) == self.length
-		return self.decodeData (data)
+		if hasattr(self.__class__, 'decodeData'):
+			data = self.decodeData(data)
+		return data
+
+	def saveData(self, file, data):
+		if hasattr(self.__class__, 'encodeData'):
+			data = self.encodeData(data)
+		self.length = len(data)
+		file.seek(self.offset)
+		file.write(data)
 
 	def decodeData(self, rawData):
 		return rawData
 
+	def encodeData(self, data):
+		return data
+
 class SFNTDirectoryEntry(DirectoryEntry):
 
 	format = sfntDirectoryEntryFormat
@@ -289,6 +388,7 @@
 
 	format = woffDirectoryEntryFormat
 	formatSize = woffDirectoryEntrySize
+	zlibCompressionLevel = 6
 
 	def decodeData(self, rawData):
 		import zlib
@@ -300,8 +400,24 @@
 			assert len (data) == self.origLength
 		return data
 
+	def encodeData(self, data):
+		import zlib
+		self.origLength = len(data)
+		if not self.uncompressed:
+			compressedData = zlib.compress(data, self.zlibCompressionLevel)
+		if self.uncompressed or len(compressedData) >= self.origLength:
+			# Encode uncompressed
+			rawData = data
+			self.length = self.origLength
+		else:
+			rawData = compressedData
+			self.length = len(rawData)
+		return rawData
+
 class WOFFFlavorData():
 
+	Flavor = 'woff'
+
 	def __init__(self, reader=None):
 		self.majorVersion = None
 		self.minorVersion = None