353 lines
12 KiB
Python
353 lines
12 KiB
Python
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):
|
|
"""Glyphlets META table
|
|
|
|
The ``META`` table is used by Adobe's SING Glyphlets.
|
|
|
|
See also https://web.archive.org/web/20080627183635/http://www.adobe.com/devnet/opentype/gdk/topic.html
|
|
"""
|
|
|
|
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
|
|
+ " ]"
|
|
)
|