123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- 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
- + " ]"
- )
|