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
This commit is contained in:
jvr 2006-10-21 13:27:25 +00:00
parent 192655c661
commit 4cc0051c08
6 changed files with 710 additions and 1 deletions

View File

@ -42,7 +42,7 @@ When using TTX from the command line there are a bunch of extra options, these a
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.

View File

@ -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)

View File

@ -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)

View File

@ -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 + " ]"

View File

@ -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)

View File

@ -7,11 +7,15 @@ def _moduleFinderHint():
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_