_h_e_a_d.py revision 8413c108d21e8cf0e9059bbfffde8d13f2616340
1import DefaultTable 2from fontTools.misc import sstruct 3import time 4import string 5from fontTools.misc.textTools import safeEval, num2binary, binary2num 6 7 8headFormat = """ 9 > # big endian 10 tableVersion: 16.16F 11 fontRevision: 16.16F 12 checkSumAdjustment: I 13 magicNumber: I 14 flags: H 15 unitsPerEm: H 16 created: 8s 17 modified: 8s 18 xMin: h 19 yMin: h 20 xMax: h 21 yMax: h 22 macStyle: H 23 lowestRecPPEM: H 24 fontDirectionHint: h 25 indexToLocFormat: h 26 glyphDataFormat: h 27""" 28 29class table__h_e_a_d(DefaultTable.DefaultTable): 30 31 dependencies = ['maxp', 'loca'] 32 33 def decompile(self, data, ttFont): 34 dummy, rest = sstruct.unpack2(headFormat, data, self) 35 if rest: 36 # this is quite illegal, but there seem to be fonts out there that do this 37 assert rest == "\0\0" 38 self.unitsPerEm = int(self.unitsPerEm) 39 self.flags = int(self.flags) 40 self.strings2dates() 41 42 def compile(self, ttFont): 43 self.modified = long(time.time() - mac_epoch_diff) 44 self.dates2strings() 45 data = sstruct.pack(headFormat, self) 46 self.strings2dates() 47 return data 48 49 def strings2dates(self): 50 self.created = bin2long(self.created) 51 self.modified = bin2long(self.modified) 52 53 def dates2strings(self): 54 self.created = long2bin(self.created) 55 self.modified = long2bin(self.modified) 56 57 def toXML(self, writer, ttFont): 58 writer.comment("Most of this table will be recalculated by the compiler") 59 writer.newline() 60 formatstring, names, fixes = sstruct.getformat(headFormat) 61 for name in names: 62 value = getattr(self, name) 63 if name in ("created", "modified"): 64 try: 65 value = time.asctime(time.gmtime(max(0, value + mac_epoch_diff))) 66 except ValueError: 67 value = time.asctime(time.gmtime(0)) 68 if name in ("magicNumber", "checkSumAdjustment"): 69 if value < 0: 70 value = value + 0x100000000L 71 value = hex(value) 72 if value[-1:] == "L": 73 value = value[:-1] 74 elif name in ("macStyle", "flags"): 75 value = num2binary(value, 16) 76 writer.simpletag(name, value=value) 77 writer.newline() 78 79 def fromXML(self, (name, attrs, content), ttFont): 80 value = attrs["value"] 81 if name in ("created", "modified"): 82 value = parse_date(value) - mac_epoch_diff 83 elif name in ("macStyle", "flags"): 84 value = binary2num(value) 85 else: 86 try: 87 value = safeEval(value) 88 except OverflowError: 89 value = long(value) 90 setattr(self, name, value) 91 92 def __cmp__(self, other): 93 if type(self) != type(other) or \ 94 self.__class__ != other.__class__: 95 return cmp(id(self), id(other)) 96 97 selfdict = self.__dict__.copy() 98 otherdict = other.__dict__.copy() 99 # for testing purposes, compare without the modified and checkSumAdjustment 100 # fields, since they are allowed to be different. 101 for key in ["modified", "checkSumAdjustment"]: 102 del selfdict[key] 103 del otherdict[key] 104 return cmp(selfdict, otherdict) 105 106 107def calc_mac_epoch_diff(): 108 """calculate the difference between the original Mac epoch (1904) 109 to the epoch on this machine. 110 """ 111 safe_epoch_t = (1972, 1, 1, 0, 0, 0, 0, 0, 0) 112 safe_epoch = time.mktime(safe_epoch_t) - time.timezone 113 # This assert fails in certain time zones, with certain daylight settings 114 #assert time.gmtime(safe_epoch)[:6] == safe_epoch_t[:6] 115 seconds1904to1972 = 60 * 60 * 24 * (365 * (1972-1904) + 17) # thanks, Laurence! 116 return long(safe_epoch - seconds1904to1972) 117 118mac_epoch_diff = calc_mac_epoch_diff() 119 120 121_months = [' ', 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 122 'sep', 'oct', 'nov', 'dec'] 123_weekdays = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] 124 125def parse_date(datestring): 126 datestring = string.lower(datestring) 127 weekday, month, day, tim, year = string.split(datestring) 128 weekday = _weekdays.index(weekday) 129 month = _months.index(month) 130 year = int(year) 131 day = int(day) 132 hour, minute, second = map(int, string.split(tim, ":")) 133 t = (year, month, day, hour, minute, second, weekday, 0, 0) 134 try: 135 return long(time.mktime(t) - time.timezone) 136 except OverflowError: 137 return 0L 138 139 140def bin2long(data): 141 # thanks </F>! 142 v = 0L 143 for i in map(ord, data): 144 v = v<<8 | i 145 return v 146 147def long2bin(v, bytes=8): 148 mask = long("FF" * bytes, 16) 149 data = "" 150 while v: 151 data = chr(v & 0xff) + data 152 v = (v >> 8) & mask 153 data = (bytes - len(data)) * "\0" + data 154 assert len(data) == 8, "long too long" 155 return data 156 157