Some non-official OT tables from rrboerts. He wrote:

There are also some new files, for SING glyphlet support, that you
may or may not want to add, because they are not in the OpenType spec.

M_E_T_A_.py # SING glyphlet meta data table. see
'http://partners.adobe.com/public/developer/opentype/gdk/topic.html"
S_I_N_G_.py # SING glyphlet basic info. See same web site as for META
data table.

G_M_A_P_.py # Summary of sing glyphlet info that has been stuck into
a parent font. Not documented anywhere yet.
G_P_K_G_.py # Opaque wrapper for SING glyphlet info; travels with
application document. Is also stuck into augmented parent font. Not
documented anywhere yet


git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@507 4cde692c-a291-49d1-8350-778aa11640f8
diff --git a/Doc/documentation.html b/Doc/documentation.html
index 9690415..3a42184 100644
--- a/Doc/documentation.html
+++ b/Doc/documentation.html
@@ -42,7 +42,7 @@
 The following tables are currently supported:
 <BLOCKQUOTE><TT>
 <!-- begin table list -->
-BASE, CFF, DSIG, GDEF, GPOS, GSUB, JSTF, LTSH, OS/2, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS, TSIV, VORG, cmap, cvt, fpgm, gasp, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, post, prep, vhea and vmtx
+BASE, CFF, DSIG, GDEF, GMAP, GPKG, GPOS, GSUB, JSTF, LTSH, META, OS/2, SING, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS, TSIV, VORG, cmap, cvt, fpgm, gasp, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, post, prep, vhea and vmtx
 <!-- end table list -->
 </TT></BLOCKQUOTE>
 Other tables are dumped as hexadecimal data.
diff --git a/Lib/fontTools/ttLib/tables/G_M_A_P_.py b/Lib/fontTools/ttLib/tables/G_M_A_P_.py
new file mode 100644
index 0000000..354798e
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/G_M_A_P_.py
@@ -0,0 +1,139 @@
+import DefaultTable
+import sstruct
+from types import StringType
+from fontTools.misc.textTools import safeEval, num2binary, binary2num
+
+GMAPFormat = """
+		>	# big endian
+		tableVersionMajor:	H
+		tableVersionMinor: 	H
+		flags:	H
+		recordsCount:		H
+		recordsOffset:		H
+		fontNameLength:		H
+"""
+# psFontName is a byte string which follows the record above. This is zero padded 
+# to the beginning of the records array. The recordsOffsst is 32 bit aligned.
+
+GMAPRecordFormat1 = """
+		>	# big endian
+		UV:			L
+		cid:		H
+		gid:		H
+		ggid:		H
+		name:		32s
+"""
+		
+
+
+class GMAPRecord:
+	def __init__(self, uv = 0, cid = 0, gid = 0, ggid = 0, name = ""):
+		self.UV = uv
+		self.cid = cid
+		self.gid = gid
+		self.ggid = ggid
+		self.name = name
+		
+	def toXML(self, writer, ttFont):
+		writer.begintag("GMAPRecord")
+		writer.newline()
+		writer.simpletag("UV", value=self.UV)
+		writer.newline()
+		writer.simpletag("cid", value=self.cid)
+		writer.newline()
+		writer.simpletag("gid", value=self.gid)
+		writer.newline()
+		writer.simpletag("glyphletGid", value=self.gid)
+		writer.newline()
+		writer.simpletag("GlyphletName", value=self.name)
+		writer.newline()
+		writer.endtag("GMAPRecord")
+		writer.newline()
+
+
+	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)
+		
+
+	def compile(self, ttFont):
+		if 	self.UV == None:
+			self.UV = 0
+		nameLen =  len(self.name)
+		if nameLen < 32:
+			self.name = self.name + "\0"*(32 - nameLen)
+		data = sstruct.pack(GMAPRecordFormat1, self)
+		return data
+
+	def __repr__(self):
+		return "GMAPRecord[ UV: " + str(self.UV) + ", cid: " + str(self.cid) + ", gid: " + str(self.gid) + ", ggid: " + str(self.ggid) + ", Glyphlet Name: " + str(self.name) + " ]"
+
+
+class table_G_M_A_P_(DefaultTable.DefaultTable):
+	
+	dependencies = []
+	
+	def decompile(self, data, ttFont):
+		dummy, newData = sstruct.unpack2(GMAPFormat, data, self)
+		self.psFontName = newData[:self.fontNameLength]
+		assert (self.recordsOffset % 4) == 0, "GMAP error: recordsOffset is not 32 bit aligned."
+		newData = data[self.recordsOffset:]
+		self.gmapRecords = []
+		for i in range (self.recordsCount):
+			gmapRecord, newData = sstruct.unpack2(GMAPRecordFormat1, newData, GMAPRecord())
+			gmapRecord.name = gmapRecord.name.strip('\0')
+			self.gmapRecords.append(gmapRecord)
+		
+
+	def compile(self, ttFont):
+		self.recordsCount = len(self.gmapRecords)
+		self.fontNameLength = len(self.psFontName)
+		self.recordsOffset = 4 *(((self.fontNameLength + 12)  + 3) /4)
+		data = sstruct.pack(GMAPFormat, self)
+		data = data + self.psFontName
+		data = data + "\0" * (self.recordsOffset - len(data))
+		for record in self.gmapRecords:
+			data = data + record.compile(ttFont)
+		return data
+	
+
+	def toXML(self, writer, ttFont):
+		writer.comment("Most of this table will be recalculated by the compiler")
+		writer.newline()
+		formatstring, names, fixes = sstruct.getformat(GMAPFormat)
+		for name in names:
+			value = getattr(self, name)
+			writer.simpletag(name, value=value)
+			writer.newline()
+		writer.simpletag("PSFontName", value=self.psFontName)
+		writer.newline()
+		for gmapRecord in self.gmapRecords:
+			gmapRecord.toXML(writer, 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):
+					continue
+				gmapRecord.fromXML(element, 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)
diff --git a/Lib/fontTools/ttLib/tables/G_P_K_G_.py b/Lib/fontTools/ttLib/tables/G_P_K_G_.py
new file mode 100644
index 0000000..42214bf
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/G_P_K_G_.py
@@ -0,0 +1,135 @@
+import DefaultTable
+import sstruct
+import array
+import Numeric
+from types import StringType
+from fontTools.misc.textTools import safeEval, readHex
+from fontTools import ttLib
+
+GPKGFormat = """
+		>	# big endian
+		version:	H
+		flags:	H
+		numGMAPs:		H
+		numGlyplets:		H
+"""
+# psFontName is a byte string which follows the record above. This is zero padded 
+# to the beginning of the records array. The recordsOffsst is 32 bit aligned.
+
+
+class table_G_P_K_G_(DefaultTable.DefaultTable):
+	
+	def decompile(self, data, ttFont):
+		dummy, newData = sstruct.unpack2(GPKGFormat, data, self)
+
+		GMAPoffsets = array.array("L")
+		endPos = (self.numGMAPs+1) * 4
+		GMAPoffsets.fromstring(newData[:endPos])
+		if ttLib.endian <> "big":
+			GMAPoffsets.byteswap()
+		self.GMAPs = []
+		for i in range(self.numGMAPs):
+			start = GMAPoffsets[i]
+			end = GMAPoffsets[i+1]
+			self.GMAPs.append(data[start:end])
+		pos = endPos
+		endPos = pos + (self.numGlyplets + 1)*4
+		glyphletOffsets = array.array("L")
+		glyphletOffsets.fromstring(newData[pos:endPos])
+		if ttLib.endian <> "big":
+			glyphletOffsets.byteswap()
+		self.glyphlets = []
+		for i in range(self.numGlyplets):
+			start = glyphletOffsets[i]
+			end = glyphletOffsets[i+1]
+			self.glyphlets.append(data[start:end])
+
+
+	def compile(self, ttFont):
+		self.numGMAPs = len(self.GMAPs)
+		self.numGlyplets = len(self.glyphlets)
+		GMAPoffsets = [0]*(self.numGMAPs + 1)
+		glyphletOffsets = [0]*(self.numGlyplets + 1)
+
+		dataList =[ sstruct.pack(GPKGFormat, self)]
+
+		pos = len(dataList[0]) + (self.numGMAPs + 1)*4 + (self.numGlyplets + 1)*4
+		GMAPoffsets[0] = pos
+		for i in range(1, self.numGMAPs +1):
+			pos += len(self.GMAPs[i-1])
+			GMAPoffsets[i] = pos
+		gmapArray = Numeric.array(GMAPoffsets, Numeric.UInt32)
+		if ttLib.endian <> "big":
+			gmapArray = gmapArray.byteswapped()
+		dataList.append(gmapArray.tostring())
+
+		glyphletOffsets[0] = pos
+		for i in range(1, self.numGlyplets +1):
+			pos += len(self.glyphlets[i-1])
+			glyphletOffsets[i] = pos
+		glyphletArray = Numeric.array(glyphletOffsets, Numeric.UInt32)
+		if ttLib.endian <> "big":
+			glyphletArray = glyphletArray.byteswapped()
+		dataList.append(glyphletArray.tostring())
+		dataList += self.GMAPs
+		dataList += self.glyphlets
+		data = "".join(dataList)
+		return data
+	
+	def toXML(self, writer, ttFont):
+		writer.comment("Most of this table will be recalculated by the compiler")
+		writer.newline()
+		formatstring, names, fixes = sstruct.getformat(GPKGFormat)
+		for name in names:
+			value = getattr(self, name)
+			writer.simpletag(name, value=value)
+			writer.newline()
+
+		writer.begintag("GMAPs")
+		writer.newline()
+		for gmapData in self.GMAPs:
+			writer.begintag("hexdata")
+			writer.newline()
+			writer.dumphex(gmapData)
+			writer.endtag("hexdata")
+			writer.newline()
+		writer.endtag("GMAPs")
+		writer.newline()
+
+		writer.begintag("glyphlets")
+		writer.newline()
+		for glyphletData in self.glyphlets:
+			writer.begintag("hexdata")
+			writer.newline()
+			writer.dumphex(glyphletData)
+			writer.endtag("hexdata")
+			writer.newline()
+		writer.endtag("glyphlets")
+		writer.newline()
+
+	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):
+					continue
+				itemName, itemAttrs, itemContent = element
+				if itemName == "hexdata":
+					self.GMAPs.append(readHex(itemContent))
+		elif name == "glyphlets":
+			if not hasattr(self, "glyphlets"):
+				self.glyphlets = []
+			for element in content:
+				if isinstance(element, StringType):
+					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)
diff --git a/Lib/fontTools/ttLib/tables/M_E_T_A_.py b/Lib/fontTools/ttLib/tables/M_E_T_A_.py
new file mode 100644
index 0000000..78d5987
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/M_E_T_A_.py
@@ -0,0 +1,329 @@
+import DefaultTable
+import struct, sstruct
+from fontTools.misc.textTools import safeEval
+import string
+from types import FloatType, ListType, StringType, TupleType
+import sys
+METAHeaderFormat = """
+		>	# big endian
+		tableVersionMajor:			H
+		tableVersionMinor:			H
+		metaEntriesVersionMajor:	H
+		metaEntriesVersionMinor:	H
+		unicodeVersion:				L
+		metaFlags:					H
+		nMetaRecs:					H
+"""
+# This record is followed by nMetaRecs of METAGlyphRecordFormat.
+# This in turn is followd by as many METAStringRecordFormat entries
+# as specified by the METAGlyphRecordFormat entries
+# this is followed by the strings specifried in the  METAStringRecordFormat
+METAGlyphRecordFormat = """
+		>	# big endian
+		glyphID:			H
+		nMetaEntry:			H
+"""
+# This record is followd by a variable data length field:
+# 	USHORT or ULONG	hdrOffset	
+# Offset from start of META table to the beginning
+# of this glyphs array of ns Metadata string entries.
+# Size determined by metaFlags field		
+# METAGlyphRecordFormat entries must be sorted by glyph ID
+ 
+METAStringRecordFormat = """
+		>	# big endian
+		labelID:			H
+		stringLen:			H
+"""
+# This record is followd by a variable data length field:
+# 	USHORT or ULONG	stringOffset	
+# METAStringRecordFormat entries must be sorted in order of labelID
+# There may be more than one entry with the same labelID
+# There may be more than one strign with the same content.
+
+# Strings shall be Unicode UTF-8 encoded, and null-terminated.
+
+METALabelDict = {
+	0 : "MojikumiX4051", # An integer in the range 1-20
+	1 : "UNIUnifiedBaseChars",
+	2 : "BaseFontName",
+	3 : "Language",
+	4 : "CreationDate",
+	5 : "FoundryName",
+	6 : "FoundryCopyright",
+	7 : "OwnerURI",
+	8 : "WritingScript",
+	10 : "StrokeCount",
+	11 : "IndexingRadical",
+}
+
+
+def getLabelString(labelID):
+	try:
+		label = METALabelDict[labelID]
+	except KeyError:
+		label = "Unknown label"
+	return str(label)
+
+
+class table_M_E_T_A_(DefaultTable.DefaultTable):
+	
+	dependencies = []
+	
+	def decompile(self, data, ttFont):
+		dummy, newData = sstruct.unpack2(METAHeaderFormat, data, self)
+		self.glyphRecords = []
+		for i in range(self.nMetaRecs):
+			glyphRecord, newData = sstruct.unpack2(METAGlyphRecordFormat, newData, GlyphRecord())
+			if self.metaFlags == 0:
+				[glyphRecord.offset] = struct.unpack(">H", newData[:2])
+				newData = newData[2:]
+			elif self.metaFlags == 1:
+				[glyphRecord.offset] = struct.unpack(">H", newData[:4])
+				newData = newData[4:]
+			else:
+				assert 0, "The metaFlags field in the META table header has a value other than 0 or 1 :" + str(self.metaFlags)
+			glyphRecord.stringRecs = []
+			newData = data[glyphRecord.offset:]
+			for j in range(glyphRecord.nMetaEntry):
+				stringRec, newData = sstruct.unpack2(METAStringRecordFormat, newData, StringRecord())
+				if self.metaFlags == 0:
+					[stringRec.offset] = struct.unpack(">H", newData[:2])
+					newData = newData[2:]
+				else:
+					[stringRec.offset] = struct.unpack(">H", newData[:4])
+					newData = newData[4:]
+				stringRec.string = data[stringRec.offset:stringRec.offset + stringRec.stringLen]
+				glyphRecord.stringRecs.append(stringRec)
+			self.glyphRecords.append(glyphRecord)	
+			
+	def compile(self, ttFont):
+		offsetOK = 0
+		self.nMetaRecs = len(self.glyphRecords)
+		count = 0
+		while ( offsetOK != 1):
+			count = count + 1
+			if count > 4:
+				pdb_set_trace()
+			metaData = sstruct.pack(METAHeaderFormat, self)
+			stringRecsOffset = len(metaData) + self.nMetaRecs * (6 + 2*(self.metaFlags & 1))
+			stringRecSize = (6 + 2*(self.metaFlags & 1))
+			for glyphRec in self.glyphRecords:
+				glyphRec.offset = stringRecsOffset
+				if (glyphRec.offset > 65535) and ((self.metaFlags & 1) == 0):
+					self.metaFlags = self.metaFlags + 1
+					offsetOK = -1
+					break
+				metaData = metaData + glyphRec.compile(self)
+				stringRecsOffset = stringRecsOffset + (glyphRec.nMetaEntry * stringRecSize) 
+				# this will be the String Record offset for the next GlyphRecord.
+			if 	offsetOK == -1:
+				offsetOK = 0
+				continue
+			
+			# metaData now contains the header and all of the GlyphRecords. Its length should bw
+			# the offset to the first StringRecord.
+			stringOffset = stringRecsOffset
+			for glyphRec in self.glyphRecords:
+				assert (glyphRec.offset == len(metaData)), "Glyph record offset did not compile correctly! for rec:" + str(glyphRec)
+				for stringRec in glyphRec.stringRecs:
+					stringRec.offset = stringOffset
+					if (stringRec.offset > 65535) and ((self.metaFlags & 1) == 0):
+						self.metaFlags = self.metaFlags + 1
+						offsetOK = -1
+						break
+					metaData = metaData + stringRec.compile(self)
+					stringOffset = stringOffset + stringRec.stringLen
+			if 	offsetOK == -1:
+				offsetOK = 0
+				continue
+				
+			if ((self.metaFlags & 1) == 1) and (stringOffset < 65536):
+				self.metaFlags = self.metaFlags - 1
+				continue
+			else:
+				offsetOK = 1
+					
+								
+			# metaData now contains the header and all of the GlyphRecords and all of the String Records.
+			# Its length should be the offset to the first string datum.
+			for glyphRec in self.glyphRecords:
+				for stringRec in glyphRec.stringRecs:
+					assert (stringRec.offset == len(metaData)), "String offset did not compile correctly! for string:" + str(stringRec.string)
+					metaData = metaData + stringRec.string
+		
+		return metaData
+	
+	def toXML(self, writer, ttFont):
+		writer.comment("Lengths and number of entries in this table will be recalculated by the compiler")
+		writer.newline()
+		formatstring, names, fixes = sstruct.getformat(METAHeaderFormat)
+		for name in names:
+			value = getattr(self, name)
+			writer.simpletag(name, value=value)
+			writer.newline()
+		for glyphRec in self.glyphRecords:
+			glyphRec.toXML(writer, 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):
+					continue
+				glyphRec.fromXML(element, 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)
+
+
+class GlyphRecord:
+	def __init__(self):
+		self.glyphID = -1
+		self.nMetaEntry = -1
+		self.offset = -1
+		self.stringRecs = []
+		
+	def toXML(self, writer, ttFont):
+		writer.begintag("GlyphRecord")
+		writer.newline()
+		writer.simpletag("glyphID", value=self.glyphID)
+		writer.newline()
+		writer.simpletag("nMetaEntry", value=self.nMetaEntry)
+		writer.newline()
+		for stringRec in self.stringRecs:
+			stringRec.toXML(writer, ttFont)
+		writer.endtag("GlyphRecord")
+		writer.newline()
+
+
+	def fromXML(self, (name, attrs, content), ttFont):
+		if name == "StringRecord":
+			stringRec = StringRecord()
+			self.stringRecs.append(stringRec)
+			for element in content:
+				if isinstance(element, StringType):
+					continue
+				stringRec.fromXML(element, ttFont)
+			stringRec.stringLen = len(stringRec.string)
+		else:			
+			value = attrs["value"]
+			try:
+				value = safeEval(value)
+			except OverflowError:
+				value = long(value)
+			setattr(self, name, value)
+
+	def compile(self, parentTable):
+		data = sstruct.pack(METAGlyphRecordFormat, self)
+		if parentTable.metaFlags == 0:
+			datum = struct.pack(">H", self.offset)
+		elif parentTable.metaFlags == 1:
+			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..."""
+		return cmp(self.glyphID, other.glyphID)
+	
+	def __repr__(self):
+		return "GlyphRecord[ glyphID: " + str(self.glyphID) + ", nMetaEntry: " + str(self.nMetaEntry) + ", offset: " + str(self.offset) + " ]"
+
+
+def mapXMLToUTF8(string):
+	uString = u""
+	strLen = len(string)
+	i = 0
+	while i < strLen:
+		prefixLen = 0
+		if  (string[i:i+3] == "&#x"):
+			prefixLen = 3
+		elif  (string[i:i+7] == "&amp;#x"):
+			prefixLen = 7
+		if prefixLen:
+			i = i+prefixLen
+			j= i
+			while string[i] != ";":
+				i = i+1
+			valStr = string[j:i]
+			
+			uString = uString + unichr(eval('0x' + valStr))
+		else:
+			uString = uString + unichr(ord(string[i]))
+		i = i +1
+			
+	return uString.encode('utf8')
+
+
+def mapUTF8toXML(string):
+	uString = string.decode('utf8')
+	string = ""
+	for uChar in uString:
+		i = ord(uChar)
+		if (i < 0x80) and (i > 0x1F):
+			string = string + chr(i)
+		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
+
+	def toXML(self, writer, ttFont):
+		writer.begintag("StringRecord")
+		writer.newline()
+		writer.simpletag("labelID", value=self.labelID)
+		writer.comment(getLabelString(self.labelID))
+		writer.newline()
+		writer.newline()
+		writer.simpletag("string", value=mapUTF8toXML(self.string))
+		writer.newline()
+		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 compile(self, parentTable):
+		data = sstruct.pack(METAStringRecordFormat, self)
+		if parentTable.metaFlags == 0:
+			datum = struct.pack(">H", self.offset)
+		elif parentTable.metaFlags == 1:
+			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..."""
+		return cmp(self.labelID, other.labelID)
+	
+	def __repr__(self):
+		return "StringRecord [ labelID: " + str(self.labelID) + " aka " + getLabelString(self.labelID) \
+			+ ", offset: " + str(self.offset) + ", length: " + str(self.stringLen) + ", string: " +self.string + " ]"
+
diff --git a/Lib/fontTools/ttLib/tables/S_I_N_G_.py b/Lib/fontTools/ttLib/tables/S_I_N_G_.py
new file mode 100644
index 0000000..6cd0f27
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/S_I_N_G_.py
@@ -0,0 +1,102 @@
+import DefaultTable
+import sstruct
+import struct
+import time
+import string
+from fontTools.misc.textTools import safeEval, num2binary, binary2num
+
+SINGFormat = """
+		>	# big endian
+		tableVersionMajor:	H
+		tableVersionMinor: 	H
+		glyphletVersion:	H
+		permissions:		h
+		mainGID:			H
+		unitsPerEm:			H
+		vertAdvance:		h
+		vertOrigin:			h
+		uniqueName:			28s
+		METAMD5:			16s
+		nameLength:			1s
+"""
+# baseGlyphName is a byte string which follows the record above.
+		
+
+
+class table_S_I_N_G_(DefaultTable.DefaultTable):
+	
+	dependencies = []
+	
+	def decompile(self, data, ttFont):
+		dummy, rest = sstruct.unpack2(SINGFormat, data, self)
+		self.uniqueName = self.decompileUniqueName(self.uniqueName)
+		self.nameLength = ord(self.nameLength)
+		assert len(rest) == self.nameLength
+		self.baseGlyphName = rest
+		
+		rawMETAMD5 = self.METAMD5
+		self.METAMD5 = "[" + hex(ord(self.METAMD5[0]))
+		for char in rawMETAMD5[1:]:
+			self.METAMD5 = self.METAMD5 + ", " + hex(ord(char))
+		self.METAMD5 = self.METAMD5 + "]"
+		
+	def decompileUniqueName(self, data):
+		name = ""
+		for char in data:
+			val = ord(char)
+			if val == 0:
+				break
+			if (val > 31) or (val < 128):
+				name = name + char
+			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
+		return name
+		
+		
+	def compile(self, ttFont):
+		self.nameLength = chr(len(self.baseGlyphName))
+		self.uniqueName = self.compilecompileUniqueName(self.uniqueName, 28)
+		METAMD5List = eval(self.METAMD5)
+		self.METAMD5 = ""
+		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
+		return data
+	
+	def compilecompileUniqueName(self, name, length):
+		nameLen = len(name)
+		if length <= nameLen:
+			name[:length-1] + "\000"
+		else:
+			name.join( (nameLen - length)* "\000")
+		return name
+
+
+	def toXML(self, writer, ttFont):
+		writer.comment("Most of this table will be recalculated by the compiler")
+		writer.newline()
+		formatstring, names, fixes = sstruct.getformat(SINGFormat)
+		for name in names:
+			value = getattr(self, name)
+			writer.simpletag(name, value=value)
+			writer.newline()
+		writer.simpletag("baseGlyphName", value=self.baseGlyphName)
+		writer.newline()
+		
+	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)
diff --git a/Lib/fontTools/ttLib/tables/__init__.py b/Lib/fontTools/ttLib/tables/__init__.py
index 4c56969..84cbf04 100644
--- a/Lib/fontTools/ttLib/tables/__init__.py
+++ b/Lib/fontTools/ttLib/tables/__init__.py
@@ -7,11 +7,15 @@
 	import C_F_F_
 	import D_S_I_G_
 	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 T_S_I_B_
 	import T_S_I_D_
 	import T_S_I_J_