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