Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 1 | import os |
Behdad Esfahbod | 8413c10 | 2013-09-17 16:59:39 -0400 | [diff] [blame] | 2 | import struct |
| 3 | from fontTools.misc import sstruct |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 4 | import string |
jvr | b4e94d9 | 2002-10-29 15:49:25 +0000 | [diff] [blame] | 5 | try: |
| 6 | from Carbon import Res |
| 7 | except ImportError: |
| 8 | import Res |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 9 | |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 10 | |
| 11 | error = "fondLib.error" |
| 12 | |
| 13 | DEBUG = 0 |
| 14 | |
| 15 | headerformat = """ |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 16 | > |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 17 | ffFlags: h |
| 18 | ffFamID: h |
| 19 | ffFirstChar: h |
| 20 | ffLastChar: h |
| 21 | ffAscent: h |
| 22 | ffDescent: h |
| 23 | ffLeading: h |
| 24 | ffWidMax: h |
| 25 | ffWTabOff: l |
| 26 | ffKernOff: l |
| 27 | ffStylOff: l |
| 28 | """ |
| 29 | |
| 30 | FONDheadersize = 52 |
| 31 | |
| 32 | class FontFamily: |
| 33 | |
| 34 | def __init__(self, theRes, mode = 'r'): |
| 35 | self.ID, type, self.name = theRes.GetResInfo() |
| 36 | if type <> 'FOND': |
| 37 | raise ValueError, "FOND resource required" |
| 38 | self.FOND = theRes |
| 39 | self.mode = mode |
| 40 | self.changed = 0 |
| 41 | |
| 42 | if DEBUG: |
| 43 | self.parsedthings = [] |
| 44 | |
| 45 | def parse(self): |
| 46 | self._getheader() |
| 47 | self._getfontassociationtable() |
| 48 | self._getoffsettable() |
| 49 | self._getboundingboxtable() |
| 50 | self._getglyphwidthtable() |
| 51 | self._getstylemappingtable() |
| 52 | self._getglyphencodingsubtable() |
| 53 | self._getkerningtables() |
| 54 | |
| 55 | def minimalparse(self): |
| 56 | self._getheader() |
| 57 | self._getglyphwidthtable() |
| 58 | self._getstylemappingtable() |
| 59 | |
| 60 | def __repr__(self): |
| 61 | return "<FontFamily instance of %s>" % self.name |
| 62 | |
| 63 | def getflags(self): |
| 64 | return self.fondClass |
| 65 | |
| 66 | def setflags(self, flags): |
| 67 | self.changed = 1 |
| 68 | self.fondClass = flags |
| 69 | |
| 70 | def save(self, destresfile = None): |
| 71 | if self.mode <> 'w': |
| 72 | raise error, "can't save font: no write permission" |
| 73 | self._buildfontassociationtable() |
| 74 | self._buildoffsettable() |
| 75 | self._buildboundingboxtable() |
| 76 | self._buildglyphwidthtable() |
| 77 | self._buildkerningtables() |
| 78 | self._buildstylemappingtable() |
| 79 | self._buildglyphencodingsubtable() |
| 80 | rawnames = [ "_rawheader", |
| 81 | "_rawfontassociationtable", |
| 82 | "_rawoffsettable", |
| 83 | "_rawglyphwidthtable", |
| 84 | "_rawstylemappingtable", |
| 85 | "_rawglyphencodingsubtable", |
| 86 | "_rawkerningtables" |
| 87 | ] |
| 88 | for name in rawnames[1:]: # skip header |
| 89 | data = getattr(self, name) |
| 90 | if len(data) & 1: |
| 91 | setattr(self, name, data + '\0') |
| 92 | |
| 93 | self.ffWTabOff = FONDheadersize + len(self._rawfontassociationtable) + len(self._rawoffsettable) |
| 94 | self.ffStylOff = self.ffWTabOff + len(self._rawglyphwidthtable) |
| 95 | self.ffKernOff = self.ffStylOff + len(self._rawstylemappingtable) + len(self._rawglyphencodingsubtable) |
| 96 | self.glyphTableOffset = len(self._rawstylemappingtable) |
| 97 | |
| 98 | if not self._rawglyphwidthtable: |
| 99 | self.ffWTabOff = 0 |
| 100 | if not self._rawstylemappingtable: |
| 101 | self.ffStylOff = 0 |
| 102 | if not self._rawglyphencodingsubtable: |
| 103 | self.glyphTableOffset = 0 |
| 104 | if not self._rawkerningtables: |
| 105 | self.ffKernOff = 0 |
| 106 | |
| 107 | self._buildheader() |
| 108 | |
| 109 | # glyphTableOffset has only just been calculated |
| 110 | self._updatestylemappingtable() |
| 111 | |
| 112 | newdata = "" |
| 113 | for name in rawnames: |
| 114 | newdata = newdata + getattr(self, name) |
| 115 | if destresfile is None: |
| 116 | self.FOND.data = newdata |
| 117 | self.FOND.ChangedResource() |
| 118 | self.FOND.WriteResource() |
| 119 | else: |
| 120 | ID, type, name = self.FOND.GetResInfo() |
| 121 | self.FOND.DetachResource() |
| 122 | self.FOND.data = newdata |
| 123 | saveref = Res.CurResFile() |
| 124 | Res.UseResFile(destresfile) |
| 125 | self.FOND.AddResource(type, ID, name) |
| 126 | Res.UseResFile(saveref) |
| 127 | self.changed = 0 |
| 128 | |
| 129 | def _getheader(self): |
| 130 | data = self.FOND.data |
| 131 | sstruct.unpack(headerformat, data[:28], self) |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 132 | self.ffProperty = struct.unpack(">9h", data[28:46]) |
| 133 | self.ffIntl = struct.unpack(">hh", data[46:50]) |
| 134 | self.ffVersion, = struct.unpack(">h", data[50:FONDheadersize]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 135 | |
| 136 | if DEBUG: |
| 137 | self._rawheader = data[:FONDheadersize] |
| 138 | self.parsedthings.append((0, FONDheadersize, 'header')) |
| 139 | |
| 140 | def _buildheader(self): |
| 141 | header = sstruct.pack(headerformat, self) |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 142 | header = header + apply(struct.pack, (">9h",) + self.ffProperty) |
| 143 | header = header + apply(struct.pack, (">hh",) + self.ffIntl) |
| 144 | header = header + struct.pack(">h", self.ffVersion) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 145 | if DEBUG: |
| 146 | print "header is the same?", self._rawheader == header and 'yes.' or 'no.' |
| 147 | if self._rawheader <> header: |
| 148 | print len(self._rawheader), len(header) |
| 149 | self._rawheader = header |
| 150 | |
| 151 | def _getfontassociationtable(self): |
| 152 | data = self.FOND.data |
| 153 | offset = FONDheadersize |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 154 | numberofentries, = struct.unpack(">h", data[offset:offset+2]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 155 | numberofentries = numberofentries + 1 |
| 156 | size = numberofentries * 6 |
| 157 | self.fontAssoc = [] |
| 158 | for i in range(offset + 2, offset + size, 6): |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 159 | self.fontAssoc.append(struct.unpack(">3h", data[i:i+6])) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 160 | |
| 161 | self._endoffontassociationtable = offset + size + 2 |
| 162 | if DEBUG: |
| 163 | self._rawfontassociationtable = data[offset:self._endoffontassociationtable] |
| 164 | self.parsedthings.append((offset, self._endoffontassociationtable, 'fontassociationtable')) |
| 165 | |
| 166 | def _buildfontassociationtable(self): |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 167 | data = struct.pack(">h", len(self.fontAssoc) - 1) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 168 | for size, stype, ID in self.fontAssoc: |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 169 | data = data + struct.pack(">3h", size, stype, ID) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 170 | |
| 171 | if DEBUG: |
| 172 | print "font association table is the same?", self._rawfontassociationtable == data and 'yes.' or 'no.' |
| 173 | if self._rawfontassociationtable <> data: |
| 174 | print len(self._rawfontassociationtable), len(data) |
| 175 | self._rawfontassociationtable = data |
| 176 | |
| 177 | def _getoffsettable(self): |
| 178 | if self.ffWTabOff == 0: |
| 179 | self._rawoffsettable = "" |
| 180 | return |
| 181 | data = self.FOND.data |
| 182 | # Quick'n'Dirty. What's the spec anyway? Can't find it... |
| 183 | offset = self._endoffontassociationtable |
| 184 | count = self.ffWTabOff |
| 185 | self._rawoffsettable = data[offset:count] |
| 186 | if DEBUG: |
| 187 | self.parsedthings.append((offset, count, 'offsettable&bbtable')) |
| 188 | |
| 189 | def _buildoffsettable(self): |
| 190 | if not hasattr(self, "_rawoffsettable"): |
| 191 | self._rawoffsettable = "" |
| 192 | |
| 193 | def _getboundingboxtable(self): |
| 194 | self.boundingBoxes = None |
| 195 | if self._rawoffsettable[:6] <> '\0\0\0\0\0\6': # XXX ???? |
| 196 | return |
| 197 | boxes = {} |
| 198 | data = self._rawoffsettable[6:] |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 199 | numstyles = struct.unpack(">h", data[:2])[0] + 1 |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 200 | data = data[2:] |
| 201 | for i in range(numstyles): |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 202 | style, l, b, r, t = struct.unpack(">hhhhh", data[:10]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 203 | boxes[style] = (l, b, r, t) |
| 204 | data = data[10:] |
| 205 | self.boundingBoxes = boxes |
| 206 | |
| 207 | def _buildboundingboxtable(self): |
| 208 | if self.boundingBoxes and self._rawoffsettable[:6] == '\0\0\0\0\0\6': |
| 209 | boxes = self.boundingBoxes.items() |
| 210 | boxes.sort() |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 211 | data = '\0\0\0\0\0\6' + struct.pack(">h", len(boxes) - 1) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 212 | for style, (l, b, r, t) in boxes: |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 213 | data = data + struct.pack(">hhhhh", style, l, b, r, t) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 214 | self._rawoffsettable = data |
| 215 | |
| 216 | def _getglyphwidthtable(self): |
| 217 | self.widthTables = {} |
| 218 | if self.ffWTabOff == 0: |
| 219 | return |
| 220 | data = self.FOND.data |
| 221 | offset = self.ffWTabOff |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 222 | numberofentries, = struct.unpack(">h", data[offset:offset+2]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 223 | numberofentries = numberofentries + 1 |
| 224 | count = offset + 2 |
| 225 | for i in range(numberofentries): |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 226 | stylecode, = struct.unpack(">h", data[count:count+2]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 227 | widthtable = self.widthTables[stylecode] = [] |
| 228 | count = count + 2 |
| 229 | for j in range(3 + self.ffLastChar - self.ffFirstChar): |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 230 | width, = struct.unpack(">h", data[count:count+2]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 231 | widthtable.append(width) |
| 232 | count = count + 2 |
| 233 | |
| 234 | if DEBUG: |
| 235 | self._rawglyphwidthtable = data[offset:count] |
| 236 | self.parsedthings.append((offset, count, 'glyphwidthtable')) |
| 237 | |
| 238 | def _buildglyphwidthtable(self): |
| 239 | if not self.widthTables: |
| 240 | self._rawglyphwidthtable = "" |
| 241 | return |
| 242 | numberofentries = len(self.widthTables) |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 243 | data = struct.pack('>h', numberofentries - 1) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 244 | tables = self.widthTables.items() |
| 245 | tables.sort() |
| 246 | for stylecode, table in tables: |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 247 | data = data + struct.pack('>h', stylecode) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 248 | if len(table) <> (3 + self.ffLastChar - self.ffFirstChar): |
| 249 | raise error, "width table has wrong length" |
| 250 | for width in table: |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 251 | data = data + struct.pack('>h', width) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 252 | if DEBUG: |
| 253 | print "glyph width table is the same?", self._rawglyphwidthtable == data and 'yes.' or 'no.' |
| 254 | self._rawglyphwidthtable = data |
| 255 | |
| 256 | def _getkerningtables(self): |
| 257 | self.kernTables = {} |
| 258 | if self.ffKernOff == 0: |
| 259 | return |
| 260 | data = self.FOND.data |
| 261 | offset = self.ffKernOff |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 262 | numberofentries, = struct.unpack(">h", data[offset:offset+2]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 263 | numberofentries = numberofentries + 1 |
| 264 | count = offset + 2 |
| 265 | for i in range(numberofentries): |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 266 | stylecode, = struct.unpack(">h", data[count:count+2]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 267 | count = count + 2 |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 268 | numberofpairs, = struct.unpack(">h", data[count:count+2]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 269 | count = count + 2 |
| 270 | kerntable = self.kernTables[stylecode] = [] |
| 271 | for j in range(numberofpairs): |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 272 | firstchar, secondchar, kerndistance = struct.unpack(">cch", data[count:count+4]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 273 | kerntable.append((ord(firstchar), ord(secondchar), kerndistance)) |
| 274 | count = count + 4 |
| 275 | |
| 276 | if DEBUG: |
| 277 | self._rawkerningtables = data[offset:count] |
| 278 | self.parsedthings.append((offset, count, 'kerningtables')) |
| 279 | |
| 280 | def _buildkerningtables(self): |
| 281 | if self.kernTables == {}: |
| 282 | self._rawkerningtables = "" |
| 283 | self.ffKernOff = 0 |
| 284 | return |
| 285 | numberofentries = len(self.kernTables) |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 286 | data = [struct.pack('>h', numberofentries - 1)] |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 287 | tables = self.kernTables.items() |
| 288 | tables.sort() |
| 289 | for stylecode, table in tables: |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 290 | data.append(struct.pack('>h', stylecode)) |
| 291 | data.append(struct.pack('>h', len(table))) # numberofpairs |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 292 | for firstchar, secondchar, kerndistance in table: |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 293 | data.append(struct.pack(">cch", chr(firstchar), chr(secondchar), kerndistance)) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 294 | |
| 295 | data = string.join(data, '') |
| 296 | |
| 297 | if DEBUG: |
| 298 | print "kerning table is the same?", self._rawkerningtables == data and 'yes.' or 'no.' |
| 299 | if self._rawkerningtables <> data: |
| 300 | print len(self._rawkerningtables), len(data) |
| 301 | self._rawkerningtables = data |
| 302 | |
| 303 | def _getstylemappingtable(self): |
| 304 | offset = self.ffStylOff |
| 305 | self.styleStrings = [] |
| 306 | self.styleIndices = () |
| 307 | self.glyphTableOffset = 0 |
| 308 | self.fondClass = 0 |
| 309 | if offset == 0: |
| 310 | return |
| 311 | data = self.FOND.data |
| 312 | self.fondClass, self.glyphTableOffset, self.styleMappingReserved, = \ |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 313 | struct.unpack(">hll", data[offset:offset+10]) |
| 314 | self.styleIndices = struct.unpack('>48b', data[offset + 10:offset + 58]) |
| 315 | stringcount, = struct.unpack('>h', data[offset+58:offset+60]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 316 | |
| 317 | count = offset + 60 |
| 318 | for i in range(stringcount): |
| 319 | str_len = ord(data[count]) |
| 320 | self.styleStrings.append(data[count + 1:count + 1 + str_len]) |
| 321 | count = count + 1 + str_len |
| 322 | |
| 323 | self._unpackstylestrings() |
| 324 | |
| 325 | data = data[offset:count] |
| 326 | if len(data) % 2: |
| 327 | data = data + '\0' |
| 328 | if DEBUG: |
| 329 | self._rawstylemappingtable = data |
| 330 | self.parsedthings.append((offset, count, 'stylemappingtable')) |
| 331 | |
| 332 | def _buildstylemappingtable(self): |
| 333 | if not self.styleIndices: |
| 334 | self._rawstylemappingtable = "" |
| 335 | return |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 336 | data = struct.pack(">hll", self.fondClass, self.glyphTableOffset, |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 337 | self.styleMappingReserved) |
| 338 | |
| 339 | self._packstylestrings() |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 340 | data = data + apply(struct.pack, (">48b",) + self.styleIndices) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 341 | |
| 342 | stringcount = len(self.styleStrings) |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 343 | data = data + struct.pack(">h", stringcount) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 344 | for string in self.styleStrings: |
| 345 | data = data + chr(len(string)) + string |
| 346 | |
| 347 | if len(data) % 2: |
| 348 | data = data + '\0' |
| 349 | |
| 350 | if DEBUG: |
| 351 | print "style mapping table is the same?", self._rawstylemappingtable == data and 'yes.' or 'no.' |
| 352 | self._rawstylemappingtable = data |
| 353 | |
| 354 | def _unpackstylestrings(self): |
| 355 | psNames = {} |
| 356 | self.ffFamilyName = self.styleStrings[0] |
| 357 | for i in self.widthTables.keys(): |
| 358 | index = self.styleIndices[i] |
| 359 | if index == 1: |
| 360 | psNames[i] = self.styleStrings[0] |
| 361 | else: |
| 362 | style = self.styleStrings[0] |
| 363 | codes = map(ord, self.styleStrings[index - 1]) |
| 364 | for code in codes: |
| 365 | style = style + self.styleStrings[code - 1] |
| 366 | psNames[i] = style |
| 367 | self.psNames = psNames |
| 368 | |
| 369 | def _packstylestrings(self): |
| 370 | nameparts = {} |
| 371 | splitnames = {} |
| 372 | for style, name in self.psNames.items(): |
| 373 | split = splitname(name, self.ffFamilyName) |
| 374 | splitnames[style] = split |
| 375 | for part in split: |
| 376 | nameparts[part] = None |
| 377 | del nameparts[self.ffFamilyName] |
| 378 | nameparts = nameparts.keys() |
| 379 | nameparts.sort() |
| 380 | items = splitnames.items() |
| 381 | items.sort() |
| 382 | numindices = 0 |
| 383 | for style, split in items: |
| 384 | if len(split) > 1: |
| 385 | numindices = numindices + 1 |
Just | 0d51707 | 2001-06-24 15:10:02 +0000 | [diff] [blame] | 386 | numindices = max(numindices, max(self.styleIndices) - 1) |
Just | dccbd31 | 2000-10-03 10:34:34 +0000 | [diff] [blame] | 387 | styleStrings = [self.ffFamilyName] + numindices * [""] + nameparts |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 388 | # XXX the next bit goes wrong for MM fonts. |
| 389 | for style, split in items: |
| 390 | if len(split) == 1: |
| 391 | continue |
| 392 | indices = "" |
| 393 | for part in split[1:]: |
| 394 | indices = indices + chr(nameparts.index(part) + numindices + 2) |
| 395 | styleStrings[self.styleIndices[style] - 1] = indices |
| 396 | self.styleStrings = styleStrings |
| 397 | |
| 398 | def _updatestylemappingtable(self): |
| 399 | # Update the glyphTableOffset field. |
pabs3 | 1344bc9 | 2010-01-09 09:12:11 +0000 | [diff] [blame] | 400 | # This is necessary since we have to build this table to |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 401 | # know what the glyphTableOffset will be. |
| 402 | # And we don't want to build it twice, do we? |
| 403 | data = self._rawstylemappingtable |
| 404 | if not data: |
| 405 | return |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 406 | data = data[:2] + struct.pack(">l", self.glyphTableOffset) + data[6:] |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 407 | self._rawstylemappingtable = data |
| 408 | |
| 409 | def _getglyphencodingsubtable(self): |
| 410 | glyphEncoding = self.glyphEncoding = {} |
| 411 | if not self.glyphTableOffset: |
| 412 | return |
| 413 | offset = self.ffStylOff + self.glyphTableOffset |
| 414 | data = self.FOND.data |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 415 | numberofentries, = struct.unpack(">h", data[offset:offset+2]) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 416 | count = offset + 2 |
| 417 | for i in range(numberofentries): |
| 418 | glyphcode = ord(data[count]) |
| 419 | count = count + 1 |
| 420 | strlen = ord(data[count]) |
| 421 | count = count + 1 |
| 422 | glyphname = data[count:count+strlen] |
| 423 | glyphEncoding[glyphcode] = glyphname |
| 424 | count = count + strlen |
| 425 | |
| 426 | if DEBUG: |
| 427 | self._rawglyphencodingsubtable = data[offset:count] |
| 428 | self.parsedthings.append((offset, count, 'glyphencodingsubtable')) |
| 429 | |
| 430 | def _buildglyphencodingsubtable(self): |
| 431 | if not self.glyphEncoding: |
| 432 | self._rawglyphencodingsubtable = "" |
| 433 | return |
| 434 | numberofentries = len(self.glyphEncoding) |
jvr | e895dce | 2005-06-24 09:35:16 +0000 | [diff] [blame] | 435 | data = struct.pack(">h", numberofentries) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 436 | items = self.glyphEncoding.items() |
| 437 | items.sort() |
| 438 | for glyphcode, glyphname in items: |
| 439 | data = data + chr(glyphcode) + chr(len(glyphname)) + glyphname |
| 440 | self._rawglyphencodingsubtable = data |
| 441 | |
| 442 | |
| 443 | uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
| 444 | |
| 445 | def splitname(name, famname = None): |
| 446 | # XXX this goofs up MM font names: but how should it be done?? |
| 447 | if famname: |
| 448 | if name[:len(famname)] <> famname: |
| 449 | raise error, "first part of name should be same as family name" |
| 450 | name = name[len(famname):] |
| 451 | split = [famname] |
| 452 | else: |
| 453 | split = [] |
| 454 | current = "" |
| 455 | for c in name: |
| 456 | if c == '-' or c in uppercase: |
| 457 | if current: |
| 458 | split.append(current) |
| 459 | current = "" |
| 460 | current = current + c |
| 461 | if current: |
| 462 | split.append(current) |
| 463 | return split |
| 464 | |
| 465 | def makeLWFNfilename(name): |
| 466 | split = splitname(name) |
| 467 | lwfnname = split[0][:5] |
| 468 | for part in split[1:]: |
| 469 | if part <> '-': |
| 470 | lwfnname = lwfnname + part[:3] |
| 471 | return lwfnname |
| 472 | |
| 473 | class BitmapFontFile: |
| 474 | |
Just | 0d51707 | 2001-06-24 15:10:02 +0000 | [diff] [blame] | 475 | def __init__(self, path, mode='r'): |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 476 | if mode == 'r': |
| 477 | permission = 1 # read only |
| 478 | elif mode == 'w': |
| 479 | permission = 3 # exclusive r/w |
| 480 | else: |
| 481 | raise error, 'mode should be either "r" or "w"' |
| 482 | self.mode = mode |
jvr | 91bca42 | 2012-10-18 12:49:22 +0000 | [diff] [blame] | 483 | self.resref = Res.FSOpenResFile(path, permission) |
Just | 7842e56 | 1999-12-16 21:34:53 +0000 | [diff] [blame] | 484 | Res.UseResFile(self.resref) |
| 485 | self.path = path |
| 486 | self.fonds = [] |
| 487 | self.getFONDs() |
| 488 | |
| 489 | def getFONDs(self): |
| 490 | FONDcount = Res.Count1Resources('FOND') |
| 491 | for i in range(FONDcount): |
| 492 | fond = FontFamily(Res.Get1IndResource('FOND', i + 1), self.mode) |
| 493 | self.fonds.append(fond) |
| 494 | |
| 495 | def parse(self): |
| 496 | self.fondsbyname = {} |
| 497 | for fond in self.fonds: |
| 498 | fond.parse() |
| 499 | if hasattr(fond, "psNames") and fond.psNames: |
| 500 | psNames = fond.psNames.values() |
| 501 | psNames.sort() |
| 502 | self.fondsbyname[psNames[0]] = fond |
| 503 | |
| 504 | def minimalparse(self): |
| 505 | for fond in self.fonds: |
| 506 | fond.minimalparse() |
| 507 | |
| 508 | def close(self): |
| 509 | if self.resref <> None: |
| 510 | try: |
| 511 | Res.CloseResFile(self.resref) |
| 512 | except Res.Error: |
| 513 | pass |
| 514 | self.resref = None |
| 515 | |
| 516 | |
| 517 | class FondSelector: |
| 518 | |
| 519 | def __init__(self, fondlist): |
| 520 | import W |
| 521 | if not fondlist: |
| 522 | raise ValueError, "expected at least one FOND entry" |
| 523 | if len(fondlist) == 1: |
| 524 | self.choice = 0 |
| 525 | return |
| 526 | fonds = [] |
| 527 | for fond in fondlist: |
| 528 | fonds.append(fond.name) |
| 529 | self.w = W.ModalDialog((200, 200), "aaa") |
| 530 | self.w.donebutton = W.Button((-70, -26, 60, 16), "Done", self.close) |
| 531 | self.w.l = W.List((10, 10, -10, -36), fonds, self.listhit) |
| 532 | self.w.setdefaultbutton(self.w.donebutton) |
| 533 | self.w.l.setselection([0]) |
| 534 | self.w.open() |
| 535 | |
| 536 | def close(self): |
| 537 | self.checksel() |
| 538 | sel = self.w.l.getselection() |
| 539 | self.choice = sel[0] |
| 540 | self.w.close() |
| 541 | |
| 542 | def listhit(self, isDbl): |
| 543 | if isDbl: |
| 544 | self.w.donebutton.push() |
| 545 | else: |
| 546 | self.checksel() |
| 547 | |
| 548 | def checksel(self): |
| 549 | sel = self.w.l.getselection() |
| 550 | if not sel: |
| 551 | self.w.l.setselection([0]) |
| 552 | elif len(sel) <> 1: |
| 553 | self.w.l.setselection([sel[0]]) |
| 554 | |