Behdad Esfahbod 8413c108d2 Move sstruct under fontTools.misc
Our footprint in the Python module namespace is all under
fontTools now.  User code importing sstruct should be updated
to say "from fontTools.misc import sstruct".
2013-09-17 16:59:39 -04:00

161 lines
4.7 KiB
Python

import DefaultTable
import struct
from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval
import string
import types
nameRecordFormat = """
> # big endian
platformID: H
platEncID: H
langID: H
nameID: H
length: H
offset: H
"""
nameRecordSize = sstruct.calcsize(nameRecordFormat)
class table__n_a_m_e(DefaultTable.DefaultTable):
def decompile(self, data, ttFont):
format, n, stringOffset = struct.unpack(">HHH", data[:6])
expectedStringOffset = 6 + n * nameRecordSize
if stringOffset != expectedStringOffset:
# XXX we need a warn function
print "Warning: 'name' table stringOffset incorrect.",
print "Expected: %s; Actual: %s" % (expectedStringOffset, stringOffset)
stringData = data[stringOffset:]
data = data[6:]
self.names = []
for i in range(n):
if len(data) < 12:
# compensate for buggy font
break
name, data = sstruct.unpack2(nameRecordFormat, data, NameRecord())
name.string = stringData[name.offset:name.offset+name.length]
assert len(name.string) == name.length
#if (name.platEncID, name.platformID) in ((0, 0), (1, 3)):
# if len(name.string) % 2:
# print "2-byte string doesn't have even length!"
# print name.__dict__
del name.offset, name.length
self.names.append(name)
def compile(self, ttFont):
if not hasattr(self, "names"):
# only happens when there are NO name table entries read
# from the TTX file
self.names = []
self.names.sort() # sort according to the spec; see NameRecord.__cmp__()
stringData = ""
format = 0
n = len(self.names)
stringOffset = 6 + n * sstruct.calcsize(nameRecordFormat)
data = struct.pack(">HHH", format, n, stringOffset)
lastoffset = 0
done = {} # remember the data so we can reuse the "pointers"
for name in self.names:
if done.has_key(name.string):
name.offset, name.length = done[name.string]
else:
name.offset, name.length = done[name.string] = len(stringData), len(name.string)
stringData = stringData + name.string
data = data + sstruct.pack(nameRecordFormat, name)
return data + stringData
def toXML(self, writer, ttFont):
for name in self.names:
name.toXML(writer, ttFont)
def fromXML(self, (name, attrs, content), ttFont):
if name <> "namerecord":
return # ignore unknown tags
if not hasattr(self, "names"):
self.names = []
name = NameRecord()
self.names.append(name)
name.fromXML((name, attrs, content), ttFont)
def getName(self, nameID, platformID, platEncID, langID=None):
for namerecord in self.names:
if ( namerecord.nameID == nameID and
namerecord.platformID == platformID and
namerecord.platEncID == platEncID):
if langID is None or namerecord.langID == langID:
return namerecord
return None # not found
def __cmp__(self, other):
if type(self) != type(other) or \
self.__class__ != other.__class__:
return cmp(id(self), id(other))
return cmp(self.names, other.names)
class NameRecord:
def toXML(self, writer, ttFont):
writer.begintag("namerecord", [
("nameID", self.nameID),
("platformID", self.platformID),
("platEncID", self.platEncID),
("langID", hex(self.langID)),
])
writer.newline()
if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)):
if len(self.string) % 2:
# no, shouldn't happen, but some of the Apple
# tools cause this anyway :-(
writer.write16bit(self.string + "\0")
else:
writer.write16bit(self.string)
else:
writer.write8bit(self.string)
writer.newline()
writer.endtag("namerecord")
writer.newline()
def fromXML(self, (name, attrs, content), ttFont):
self.nameID = safeEval(attrs["nameID"])
self.platformID = safeEval(attrs["platformID"])
self.platEncID = safeEval(attrs["platEncID"])
self.langID = safeEval(attrs["langID"])
if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)):
s = ""
for element in content:
s = s + element
s = unicode(s, "utf8")
s = s.strip()
self.string = s.encode("utf_16_be")
else:
s = string.strip(string.join(content, ""))
self.string = unicode(s, "utf8").encode("latin1")
def __cmp__(self, other):
"""Compare method, so a list of NameRecords can be sorted
according to the spec by just sorting it..."""
if type(self) != type(other) or \
self.__class__ != other.__class__:
return cmp(id(self), id(other))
selftuple = (self.platformID,
self.platEncID,
self.langID,
self.nameID,
self.string)
othertuple = (other.platformID,
other.platEncID,
other.langID,
other.nameID,
other.string)
return cmp(selftuple, othertuple)
def __repr__(self):
return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
self.nameID, self.platformID, self.langID)