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