blob: 3eb6550da4cdd8e7e4e0e06efe3756daa800c910 [file] [log] [blame]
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval
from . import DefaultTable
import pdb
import struct
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, basestring):
continue
name, attrs, content = element
glyphRec.fromXML(name, attrs, content, ttFont)
glyphRec.offset = -1
glyphRec.nMetaEntry = len(glyphRec.stringRecs)
else:
setattr(self, name, safeEval(attrs["value"]))
class GlyphRecord(object):
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, basestring):
continue
stringRec.fromXML(name, attrs, content, ttFont)
stringRec.stringLen = len(stringRec.string)
else:
setattr(self, name, safeEval(attrs["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 __repr__(self):
return "GlyphRecord[ glyphID: " + str(self.glyphID) + ", nMetaEntry: " + str(self.nMetaEntry) + ", offset: " + str(self.offset) + " ]"
# XXX The following two functions are really broken around UTF-8 vs Unicode
def mapXMLToUTF8(string):
uString = unicode()
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(byteord(string[i]))
i = i +1
return uString.encode('utf_8')
def mapUTF8toXML(string):
uString = string.decode('utf_8')
string = ""
for uChar in uString:
i = ord(uChar)
if (i < 0x80) and (i > 0x1F):
string = string + uChar
else:
string = string + "&#x" + hex(i)[2:] + ";"
return string
class StringRecord(object):
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):
for element in content:
if isinstance(element, basestring):
continue
name, attrs, content = element
value = attrs["value"]
if name == "string":
self.string = mapXMLToUTF8(value)
else:
setattr(self, name, safeEval(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 __repr__(self):
return "StringRecord [ labelID: " + str(self.labelID) + " aka " + getLabelString(self.labelID) \
+ ", offset: " + str(self.offset) + ", length: " + str(self.stringLen) + ", string: " +self.string + " ]"