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