The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

DirectoryTable.vala in libbirdfont/OpenFontFormat

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libbirdfont/OpenFontFormat/DirectoryTable.vala.
Generate mac specific truetype fonts
1 /* 2 Copyright (C) 2012, 2013, 2014 Johan Mattsson 3 4 This library is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 3 of the 7 License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Lesser General Public License for more details. 13 */ 14 15 namespace BirdFont { 16 17 /** Table with list of tables sorted by table tag. */ 18 public class DirectoryTable : OtfTable { 19 20 public CmapTable cmap_table; 21 public CvtTable cvt_table; 22 public GaspTable gasp_table; 23 public GdefTable gdef_table; 24 public GlyfTable glyf_table; 25 public GposTable gpos_table; 26 public GsubTable gsub_table; 27 public HeadTable head_table; 28 public HheaTable hhea_table; 29 public HmtxTable hmtx_table; 30 public KernTable kern_table; 31 public MaxpTable maxp_table; 32 public NameTable name_table; 33 public Os2Table os_2_table; 34 public PostTable post_table; 35 public LocaTable loca_table; 36 37 public OffsetTable offset_table; 38 39 Gee.ArrayList<OtfTable> tables; 40 41 public DirectoryTable () { 42 offset_table = new OffsetTable (this); 43 44 loca_table = new LocaTable (); 45 gasp_table = new GaspTable (); 46 gdef_table = new GdefTable (); 47 glyf_table = new GlyfTable (loca_table); 48 gsub_table = new GsubTable (glyf_table); 49 cmap_table = new CmapTable (glyf_table); 50 cvt_table = new CvtTable (); 51 head_table = new HeadTable (glyf_table); 52 hmtx_table = new HmtxTable (head_table, glyf_table); 53 hhea_table = new HheaTable (glyf_table, head_table, hmtx_table); 54 kern_table = new KernTable (glyf_table); 55 gpos_table = new GposTable (); 56 maxp_table = new MaxpTable (glyf_table); 57 name_table = new NameTable (); 58 os_2_table = new Os2Table (); 59 post_table = new PostTable (glyf_table); 60 61 id = "Directory table"; 62 63 tables = new Gee.ArrayList<OtfTable> (); 64 } 65 66 public void process () throws GLib.Error { 67 // generate font data 68 glyf_table.process (); 69 gsub_table.process (); 70 gasp_table.process (); 71 gdef_table.process (); 72 cmap_table.process (glyf_table); 73 cvt_table.process (); 74 hmtx_table.process (); 75 hhea_table.process (); 76 maxp_table.process (); 77 name_table.process (); 78 os_2_table.process (glyf_table, hmtx_table); 79 head_table.process (); 80 loca_table.process (glyf_table, head_table); 81 post_table.process (); 82 kern_table.process (); 83 gpos_table.process (glyf_table); 84 85 offset_table.process (); 86 process_directory (); // this table 87 } 88 89 public Gee.ArrayList<OtfTable> get_tables () { 90 if (tables.size == 0) { 91 tables.add (offset_table); 92 tables.add (this); 93 94 tables.add (gpos_table); 95 tables.add (gsub_table); 96 97 tables.add (os_2_table); 98 99 // tables.append (gdef_table); // invalid table 100 101 tables.add (cmap_table); 102 // tables.append (cvt_table); 103 tables.add (gasp_table); 104 tables.add (glyf_table); 105 tables.add (head_table); 106 107 tables.add (hhea_table); 108 tables.add (hmtx_table); 109 110 // It looks like the old kerning table is no longer needed 111 // since the most browsers uses the GPOS table 112 // but Windows does not accept fonts without a kern table. 113 114 tables.add (kern_table); 115 116 tables.add (loca_table); 117 tables.add (maxp_table); 118 tables.add (name_table); 119 120 tables.add (post_table); 121 } 122 123 return tables; 124 } 125 126 public void set_offset_table (OffsetTable ot) { 127 offset_table = ot; 128 } 129 130 public new void parse (FontData dis, OpenFontFormatReader reader_callback) throws Error { 131 StringBuilder tag = new StringBuilder (); 132 uint32 checksum; 133 uint32 offset; 134 uint32 length; 135 136 return_if_fail (offset_table.num_tables > 0); 137 138 for (uint i = 0; i < offset_table.num_tables; i++) { 139 tag.erase (); 140 141 tag.append_unichar ((unichar) dis.read_byte ()); 142 tag.append_unichar ((unichar) dis.read_byte ()); 143 tag.append_unichar ((unichar) dis.read_byte ()); 144 tag.append_unichar ((unichar) dis.read_byte ()); 145 146 checksum = dis.read_ulong (); 147 offset = dis.read_ulong (); 148 length = dis.read_ulong (); 149 150 printd (@"$(tag.str) \toffset: $offset \tlength: $length \tchecksum: $checksum.\n"); 151 152 if (tag.str == "cvt") { 153 cvt_table.id = tag.str; 154 cvt_table.checksum = checksum; 155 cvt_table.offset = offset; 156 cvt_table.length = length; 157 } else if (tag.str == "hmtx") { 158 hmtx_table.id = tag.str; 159 hmtx_table.checksum = checksum; 160 hmtx_table.offset = offset; 161 hmtx_table.length = length; 162 } else if (tag.str == "hhea") { 163 hhea_table.id = tag.str; 164 hhea_table.checksum = checksum; 165 hhea_table.offset = offset; 166 hhea_table.length = length; 167 } else if (tag.str == "loca") { 168 loca_table.id = tag.str; 169 loca_table.checksum = checksum; 170 loca_table.offset = offset; 171 loca_table.length = length; 172 } else if (tag.str == "cmap") { 173 cmap_table.id = tag.str; 174 cmap_table.checksum = checksum; 175 cmap_table.offset = offset; 176 cmap_table.length = length; 177 } else if (tag.str == "maxp") { 178 maxp_table.id = tag.str; 179 maxp_table.checksum = checksum; 180 maxp_table.offset = offset; 181 maxp_table.length = length; 182 } else if (tag.str == "gasp") { 183 gasp_table.id = tag.str; 184 gasp_table.checksum = checksum; 185 gasp_table.offset = offset; 186 gasp_table.length = length; 187 } else if (tag.str == "gdef") { 188 gdef_table.id = tag.str; 189 gdef_table.checksum = checksum; 190 gdef_table.offset = offset; 191 gdef_table.length = length; 192 } else if (tag.str == "glyf") { 193 glyf_table.id = tag.str; 194 glyf_table.checksum = checksum; 195 glyf_table.offset = offset; 196 glyf_table.length = length; 197 } else if (tag.str == "head") { 198 head_table.id = tag.str; 199 head_table.checksum = checksum; 200 head_table.offset = offset; 201 head_table.length = length; 202 } else if (tag.str == "name") { 203 name_table.id = tag.str; 204 name_table.checksum = checksum; 205 name_table.offset = offset; 206 name_table.length = length; 207 } else if (tag.str == "OS/2") { 208 os_2_table.id = tag.str; 209 os_2_table.checksum = checksum; 210 os_2_table.offset = offset; 211 os_2_table.length = length; 212 } else if (tag.str == "post") { 213 post_table.id = tag.str; 214 post_table.checksum = checksum; 215 post_table.offset = offset; 216 post_table.length = length; 217 } else if (tag.str == "kern") { 218 kern_table.id = tag.str; 219 kern_table.checksum = checksum; 220 kern_table.offset = offset; 221 kern_table.length = length; 222 } else if (tag.str == "GPOS") { 223 gpos_table.id = tag.str; 224 gpos_table.checksum = checksum; 225 gpos_table.offset = offset; 226 gpos_table.length = length; 227 } 228 } 229 } 230 231 public void parse_all_tables (FontData dis, OpenFontFormatReader reader_callback) throws Error { 232 head_table.parse (dis); 233 234 hhea_table.parse (dis); 235 reader_callback.set_limits (); 236 237 name_table.parse (dis); 238 post_table.parse (dis); 239 os_2_table.parse (dis); 240 maxp_table.parse (dis); 241 loca_table.parse (dis, head_table, maxp_table); 242 hmtx_table.parse (dis, hhea_table, loca_table); 243 cmap_table.parse (dis); 244 gpos_table.parse (dis); 245 246 if (kern_table.has_data ()) { 247 kern_table.parse (dis); 248 } 249 250 glyf_table.parse (dis, cmap_table, loca_table, hmtx_table, head_table, post_table, kern_table); 251 252 if (kern_table.has_data ()) { 253 gasp_table.parse (dis); 254 } 255 256 if (kern_table.has_data ()) { 257 cvt_table.parse (dis); 258 } 259 } 260 261 public void parse_kern_table (FontData dis) throws Error { 262 if (kern_table.has_data ()) { 263 kern_table.parse (dis); 264 } else { 265 warning ("Kern table is empty."); 266 } 267 } 268 269 public void parse_cmap_table (FontData dis) throws Error { 270 if (cmap_table.has_data ()) { 271 cmap_table.parse (dis); 272 } else { 273 warning ("Cmap table is empty."); 274 } 275 } 276 277 public void parse_head_table (FontData dis) throws Error { 278 if (head_table.has_data ()) { 279 head_table.parse (dis); 280 } else { 281 warning ("Head table is empty."); 282 } 283 } 284 285 public bool validate_tables (FontData dis, File file) { 286 bool valid = true; 287 288 try { 289 dis.seek (0); 290 291 if (!validate_checksum_for_entire_font (dis, file)) { 292 warning ("file has invalid checksum"); 293 } else { 294 printd ("Font file has valid checksum.\n"); 295 } 296 297 // Skip validation of head table for now it should be simple but it seems to 298 // be broken in some funny way. 299 300 if (!glyf_table.validate (dis)) { 301 warning ("glyf_table has invalid checksum"); 302 valid = false; 303 } 304 305 if (!maxp_table.validate (dis)) { 306 warning ("maxp_table has is invalid checksum"); 307 valid = false; 308 } 309 310 if (!loca_table.validate (dis)) { 311 warning ("loca_table has invalid checksum"); 312 valid = false; 313 } 314 315 if (!cmap_table.validate (dis)) { 316 warning ("cmap_table has invalid checksum"); 317 valid = false; 318 } 319 320 if (!hhea_table.validate (dis)) { 321 warning ("hhea_table has invalid checksum"); 322 valid = false; 323 } 324 325 if (!hmtx_table.validate (dis)) { 326 warning ("hmtx_table has invalid checksum"); 327 valid = false; 328 } 329 330 if (!name_table.validate (dis)) { 331 warning ("name_table has invalid checksum"); 332 valid = false; 333 } 334 335 if (!os_2_table.validate (dis)) { 336 warning ("os_2_table has invalid checksum"); 337 valid = false; 338 } 339 340 if (!post_table.validate (dis)) { 341 warning ("post_table has invalid checksum"); 342 valid = false; 343 } 344 345 if (kern_table.has_data () && !kern_table.validate (dis)) { 346 warning ("kern_table has invalid checksum"); 347 valid = false; 348 } 349 350 if (!gpos_table.validate (dis)) { 351 warning (@"gpos_table has invalid checksum"); 352 353 if (gpos_table.font_data != null) { 354 warning (@"Length: $(((!)gpos_table.font_data).length ())\n"); 355 } else { 356 warning ("font_data is null"); 357 } 358 359 valid = false; 360 } 361 } catch (GLib.Error e) { 362 warning (e.message); 363 valid = false; 364 } 365 366 return valid; 367 } 368 369 bool validate_checksum_for_entire_font (FontData dis, File f) throws GLib.Error { 370 uint p = head_table.offset + head_table.get_checksum_position (); 371 uint32 checksum_font, checksum_head; 372 373 checksum_head = head_table.get_font_checksum (); 374 375 dis.seek (0); 376 377 // zero out checksum entry in head table before validating it 378 dis.write_at (p + 0, 0); 379 dis.write_at (p + 1, 0); 380 dis.write_at (p + 2, 0); 381 dis.write_at (p + 3, 0); 382 383 checksum_font = (uint32) (0xB1B0AFBA - dis.check_sum ()); 384 385 if (checksum_font != checksum_head) { 386 warning (@"Fontfile checksum in head table does not match calculated checksum. checksum_font: $checksum_font checksum_head: $checksum_head"); 387 return false; 388 } 389 390 return true; 391 } 392 393 public long get_font_file_size () { 394 long length = 0; 395 396 foreach (OtfTable t in tables) { 397 length += t.get_font_data ().length_with_padding (); 398 } 399 400 return length; 401 } 402 403 public void process_directory () throws GLib.Error { 404 create_directory (); // create directory without offsets to calculate length of offset table and checksum for entre file 405 create_directory (); // generate a valid directory 406 } 407 408 // Check sum adjustment for the entire font 409 public uint32 get_font_file_checksum () { 410 uint32 check_sum = 0; 411 foreach (OtfTable t in tables) { 412 t.get_font_data ().continous_check_sum (ref check_sum); 413 } 414 return check_sum; 415 } 416 417 public void create_directory () throws GLib.Error { 418 FontData fd; 419 420 uint32 table_offset = 0; 421 uint32 table_length = 0; 422 423 uint32 check_sum = 0; 424 425 fd = new FontData (); 426 427 return_if_fail (offset_table.num_tables > 0); 428 429 table_offset += offset_table.get_font_data ().length_with_padding (); 430 431 if (this.font_data != null) { 432 table_offset += this.get_font_data ().length_with_padding (); 433 } 434 435 head_table.set_check_sum_adjustment (0); // Set this to zero, calculate checksums and update the value 436 head_table.process (); 437 438 // write the directory 439 foreach (OtfTable t in tables) { 440 441 if (t is DirectoryTable || t is OffsetTable) { 442 continue; 443 } 444 445 printd (@"c $(t.id) offset: $(table_offset) len with pad $(t.get_font_data ().length_with_padding ())\n"); 446 447 table_length = t.get_font_data ().length (); // without padding 448 449 fd.add_tag (t.get_id ()); // name of table 450 fd.add_u32 (t.get_font_data ().check_sum ()); 451 fd.add_u32 (table_offset); 452 fd.add_u32 (table_length); 453 454 table_offset += t.get_font_data ().length_with_padding (); 455 } 456 457 // padding 458 fd.pad (); 459 460 this.font_data = fd; 461 462 check_sum = get_font_file_checksum (); 463 head_table.set_check_sum_adjustment ((uint32)(0xB1B0AFBA - check_sum)); 464 head_table.process (); // update the value 465 } 466 467 } 468 469 } 470