diff options
Diffstat (limited to 'Lib/fontTools/ttLib/tables/M_E_T_A_.py')
-rw-r--r-- | Lib/fontTools/ttLib/tables/M_E_T_A_.py | 307 |
1 files changed, 307 insertions, 0 deletions
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 00000000..60214e84 --- /dev/null +++ b/Lib/fontTools/ttLib/tables/M_E_T_A_.py @@ -0,0 +1,307 @@ +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 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] == "&#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('utf8') + + +def mapUTF8toXML(string): + uString = string.decode('utf8') + 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 + " ]" + |