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".
157 lines
4.3 KiB
Python
157 lines
4.3 KiB
Python
import DefaultTable
|
|
from fontTools.misc import sstruct
|
|
import time
|
|
import string
|
|
from fontTools.misc.textTools import safeEval, num2binary, binary2num
|
|
|
|
|
|
headFormat = """
|
|
> # big endian
|
|
tableVersion: 16.16F
|
|
fontRevision: 16.16F
|
|
checkSumAdjustment: I
|
|
magicNumber: I
|
|
flags: H
|
|
unitsPerEm: H
|
|
created: 8s
|
|
modified: 8s
|
|
xMin: h
|
|
yMin: h
|
|
xMax: h
|
|
yMax: h
|
|
macStyle: H
|
|
lowestRecPPEM: H
|
|
fontDirectionHint: h
|
|
indexToLocFormat: h
|
|
glyphDataFormat: h
|
|
"""
|
|
|
|
class table__h_e_a_d(DefaultTable.DefaultTable):
|
|
|
|
dependencies = ['maxp', 'loca']
|
|
|
|
def decompile(self, data, ttFont):
|
|
dummy, rest = sstruct.unpack2(headFormat, data, self)
|
|
if rest:
|
|
# this is quite illegal, but there seem to be fonts out there that do this
|
|
assert rest == "\0\0"
|
|
self.unitsPerEm = int(self.unitsPerEm)
|
|
self.flags = int(self.flags)
|
|
self.strings2dates()
|
|
|
|
def compile(self, ttFont):
|
|
self.modified = long(time.time() - mac_epoch_diff)
|
|
self.dates2strings()
|
|
data = sstruct.pack(headFormat, self)
|
|
self.strings2dates()
|
|
return data
|
|
|
|
def strings2dates(self):
|
|
self.created = bin2long(self.created)
|
|
self.modified = bin2long(self.modified)
|
|
|
|
def dates2strings(self):
|
|
self.created = long2bin(self.created)
|
|
self.modified = long2bin(self.modified)
|
|
|
|
def toXML(self, writer, ttFont):
|
|
writer.comment("Most of this table will be recalculated by the compiler")
|
|
writer.newline()
|
|
formatstring, names, fixes = sstruct.getformat(headFormat)
|
|
for name in names:
|
|
value = getattr(self, name)
|
|
if name in ("created", "modified"):
|
|
try:
|
|
value = time.asctime(time.gmtime(max(0, value + mac_epoch_diff)))
|
|
except ValueError:
|
|
value = time.asctime(time.gmtime(0))
|
|
if name in ("magicNumber", "checkSumAdjustment"):
|
|
if value < 0:
|
|
value = value + 0x100000000L
|
|
value = hex(value)
|
|
if value[-1:] == "L":
|
|
value = value[:-1]
|
|
elif name in ("macStyle", "flags"):
|
|
value = num2binary(value, 16)
|
|
writer.simpletag(name, value=value)
|
|
writer.newline()
|
|
|
|
def fromXML(self, (name, attrs, content), ttFont):
|
|
value = attrs["value"]
|
|
if name in ("created", "modified"):
|
|
value = parse_date(value) - mac_epoch_diff
|
|
elif name in ("macStyle", "flags"):
|
|
value = binary2num(value)
|
|
else:
|
|
try:
|
|
value = safeEval(value)
|
|
except OverflowError:
|
|
value = long(value)
|
|
setattr(self, name, value)
|
|
|
|
def __cmp__(self, other):
|
|
if type(self) != type(other) or \
|
|
self.__class__ != other.__class__:
|
|
return cmp(id(self), id(other))
|
|
|
|
selfdict = self.__dict__.copy()
|
|
otherdict = other.__dict__.copy()
|
|
# for testing purposes, compare without the modified and checkSumAdjustment
|
|
# fields, since they are allowed to be different.
|
|
for key in ["modified", "checkSumAdjustment"]:
|
|
del selfdict[key]
|
|
del otherdict[key]
|
|
return cmp(selfdict, otherdict)
|
|
|
|
|
|
def calc_mac_epoch_diff():
|
|
"""calculate the difference between the original Mac epoch (1904)
|
|
to the epoch on this machine.
|
|
"""
|
|
safe_epoch_t = (1972, 1, 1, 0, 0, 0, 0, 0, 0)
|
|
safe_epoch = time.mktime(safe_epoch_t) - time.timezone
|
|
# This assert fails in certain time zones, with certain daylight settings
|
|
#assert time.gmtime(safe_epoch)[:6] == safe_epoch_t[:6]
|
|
seconds1904to1972 = 60 * 60 * 24 * (365 * (1972-1904) + 17) # thanks, Laurence!
|
|
return long(safe_epoch - seconds1904to1972)
|
|
|
|
mac_epoch_diff = calc_mac_epoch_diff()
|
|
|
|
|
|
_months = [' ', 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug',
|
|
'sep', 'oct', 'nov', 'dec']
|
|
_weekdays = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
|
|
|
|
def parse_date(datestring):
|
|
datestring = string.lower(datestring)
|
|
weekday, month, day, tim, year = string.split(datestring)
|
|
weekday = _weekdays.index(weekday)
|
|
month = _months.index(month)
|
|
year = int(year)
|
|
day = int(day)
|
|
hour, minute, second = map(int, string.split(tim, ":"))
|
|
t = (year, month, day, hour, minute, second, weekday, 0, 0)
|
|
try:
|
|
return long(time.mktime(t) - time.timezone)
|
|
except OverflowError:
|
|
return 0L
|
|
|
|
|
|
def bin2long(data):
|
|
# thanks </F>!
|
|
v = 0L
|
|
for i in map(ord, data):
|
|
v = v<<8 | i
|
|
return v
|
|
|
|
def long2bin(v, bytes=8):
|
|
mask = long("FF" * bytes, 16)
|
|
data = ""
|
|
while v:
|
|
data = chr(v & 0xff) + data
|
|
v = (v >> 8) & mask
|
|
data = (bytes - len(data)) * "\0" + data
|
|
assert len(data) == 8, "long too long"
|
|
return data
|
|
|