Spaces:
Runtime error
Runtime error
from fontTools.misc import sstruct | |
from fontTools.misc.textTools import byteord, 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, str): | |
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, str): | |
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 = str() | |
strLen = len(string) | |
i = 0 | |
while i < strLen: | |
prefixLen = 0 | |
if string[i : i + 3] == "&#x": | |
prefixLen = 3 | |
elif string[i : i + 7] == "&#x": | |
prefixLen = 7 | |
if prefixLen: | |
i = i + prefixLen | |
j = i | |
while string[i] != ";": | |
i = i + 1 | |
valStr = string[j:i] | |
uString = uString + chr(eval("0x" + valStr)) | |
else: | |
uString = uString + chr(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, str): | |
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 | |
+ " ]" | |
) | |