1 /* 2 * Copyright © 2007,2008,2009 Red Hat, Inc. 3 * Copyright © 2012 Google, Inc. 4 * 5 * This is part of HarfBuzz, a text shaping library. 6 * 7 * Permission is hereby granted, without written agreement and without 8 * license or royalty fees, to use, copy, modify, and distribute this 9 * software and its documentation for any purpose, provided that the 10 * above copyright notice and the following two paragraphs appear in 11 * all copies of this software. 12 * 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 17 * DAMAGE. 18 * 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 * 25 * Red Hat Author(s): Behdad Esfahbod 26 * Google Author(s): Behdad Esfahbod 27 */ 28 29 #ifndef HB_OPEN_FILE_HH 30 #define HB_OPEN_FILE_HH 31 32 #include "hb-open-type.hh" 33 #include "hb-ot-head-table.hh" 34 35 36 namespace OT { 37 38 39 /* 40 * 41 * The OpenType Font File 42 * 43 */ 44 45 46 /* 47 * Organization of an OpenType Font 48 */ 49 50 struct OpenTypeFontFile; 51 struct OffsetTable; 52 struct TTCHeader; 53 54 55 typedef struct TableRecord 56 { 57 int cmp (Tag t) const { return -t.cmp (tag); } 58 59 static int cmp (const void *pa, const void *pb) 60 { 61 const TableRecord *a = (const TableRecord *) pa; 62 const TableRecord *b = (const TableRecord *) pb; 63 return b->cmp (a->tag); 64 } 65 66 bool sanitize (hb_sanitize_context_t *c) const 67 { 68 TRACE_SANITIZE (this); 69 return_trace (c->check_struct (this)); 70 } 71 72 Tag tag; /* 4-byte identifier. */ 73 CheckSum checkSum; /* CheckSum for this table. */ 74 Offset32 offset; /* Offset from beginning of TrueType font 75 * file. */ 76 HBUINT32 length; /* Length of this table. */ 77 public: 78 DEFINE_SIZE_STATIC (16); 79 } OpenTypeTable; 80 81 typedef struct OffsetTable 82 { 83 friend struct OpenTypeFontFile; 84 85 unsigned int get_table_count () const { return tables.len; } 86 const TableRecord& get_table (unsigned int i) const 87 { return tables[i]; } 88 unsigned int get_table_tags (unsigned int start_offset, 89 unsigned int *table_count, /* IN/OUT */ 90 hb_tag_t *table_tags /* OUT */) const 91 { 92 if (table_count) 93 { 94 if (start_offset >= tables.len) 95 *table_count = 0; 96 else 97 *table_count = MIN<unsigned int> (*table_count, tables.len - start_offset); 98 99 const TableRecord *sub_tables = tables.arrayZ + start_offset; 100 unsigned int count = *table_count; 101 for (unsigned int i = 0; i < count; i++) 102 table_tags[i] = sub_tables[i].tag; 103 } 104 return tables.len; 105 } 106 bool find_table_index (hb_tag_t tag, unsigned int *table_index) const 107 { 108 Tag t; 109 t.set (tag); 110 return tables.bfind (t, table_index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); 111 } 112 const TableRecord& get_table_by_tag (hb_tag_t tag) const 113 { 114 unsigned int table_index; 115 find_table_index (tag, &table_index); 116 return get_table (table_index); 117 } 118 119 public: 120 121 template <typename item_t> 122 bool serialize (hb_serialize_context_t *c, 123 hb_tag_t sfnt_tag, 124 hb_array_t<item_t> items) 125 { 126 TRACE_SERIALIZE (this); 127 /* Alloc 12 for the OTHeader. */ 128 if (unlikely (!c->extend_min (*this))) return_trace (false); 129 /* Write sfntVersion (bytes 0..3). */ 130 sfnt_version.set (sfnt_tag); 131 /* Take space for numTables, searchRange, entrySelector, RangeShift 132 * and the TableRecords themselves. */ 133 if (unlikely (!tables.serialize (c, items.length))) return_trace (false); 134 135 const char *dir_end = (const char *) c->head; 136 HBUINT32 *checksum_adjustment = nullptr; 137 138 /* Write OffsetTables, alloc for and write actual table blobs. */ 139 for (unsigned int i = 0; i < tables.len; i++) 140 { 141 TableRecord &rec = tables.arrayZ[i]; 142 hb_blob_t *blob = items[i].blob; 143 rec.tag.set (items[i].tag); 144 rec.length.set (hb_blob_get_length (blob)); 145 rec.offset.serialize (c, this); 146 147 /* Allocate room for the table and copy it. */ 148 char *start = (char *) c->allocate_size<void> (rec.length); 149 if (unlikely (!start)) {return false;} 150 151 memcpy (start, hb_blob_get_data (blob, nullptr), rec.length); 152 153 /* 4-byte alignment. */ 154 c->align (4); 155 const char *end = (const char *) c->head; 156 157 if (items[i].tag == HB_OT_TAG_head && 158 (unsigned) (end - start) >= head::static_size) 159 { 160 head *h = (head *) start; 161 checksum_adjustment = &h->checkSumAdjustment; 162 checksum_adjustment->set (0); 163 } 164 165 rec.checkSum.set_for_data (start, end - start); 166 } 167 168 tables.qsort (); 169 170 if (checksum_adjustment) 171 { 172 CheckSum checksum; 173 174 /* The following line is a slower version of the following block. */ 175 //checksum.set_for_data (this, (const char *) c->head - (const char *) this); 176 checksum.set_for_data (this, dir_end - (const char *) this); 177 for (unsigned int i = 0; i < items.length; i++) 178 { 179 TableRecord &rec = tables.arrayZ[i]; 180 checksum.set (checksum + rec.checkSum); 181 } 182 183 checksum_adjustment->set (0xB1B0AFBAu - checksum); 184 } 185 186 return_trace (true); 187 } 188 189 bool sanitize (hb_sanitize_context_t *c) const 190 { 191 TRACE_SANITIZE (this); 192 return_trace (c->check_struct (this) && tables.sanitize (c)); 193 } 194 195 protected: 196 Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ 197 BinSearchArrayOf<TableRecord> 198 tables; 199 public: 200 DEFINE_SIZE_ARRAY (12, tables); 201 } OpenTypeFontFace; 202 203 204 /* 205 * TrueType Collections 206 */ 207 208 struct TTCHeaderVersion1 209 { 210 friend struct TTCHeader; 211 212 unsigned int get_face_count () const { return table.len; } 213 const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } 214 215 bool sanitize (hb_sanitize_context_t *c) const 216 { 217 TRACE_SANITIZE (this); 218 return_trace (table.sanitize (c, this)); 219 } 220 221 protected: 222 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ 223 FixedVersion<>version; /* Version of the TTC Header (1.0), 224 * 0x00010000u */ 225 LArrayOf<LOffsetTo<OffsetTable> > 226 table; /* Array of offsets to the OffsetTable for each font 227 * from the beginning of the file */ 228 public: 229 DEFINE_SIZE_ARRAY (12, table); 230 }; 231 232 struct TTCHeader 233 { 234 friend struct OpenTypeFontFile; 235 236 private: 237 238 unsigned int get_face_count () const 239 { 240 switch (u.header.version.major) { 241 case 2: /* version 2 is compatible with version 1 */ 242 case 1: return u.version1.get_face_count (); 243 default:return 0; 244 } 245 } 246 const OpenTypeFontFace& get_face (unsigned int i) const 247 { 248 switch (u.header.version.major) { 249 case 2: /* version 2 is compatible with version 1 */ 250 case 1: return u.version1.get_face (i); 251 default:return Null(OpenTypeFontFace); 252 } 253 } 254 255 bool sanitize (hb_sanitize_context_t *c) const 256 { 257 TRACE_SANITIZE (this); 258 if (unlikely (!u.header.version.sanitize (c))) return_trace (false); 259 switch (u.header.version.major) { 260 case 2: /* version 2 is compatible with version 1 */ 261 case 1: return_trace (u.version1.sanitize (c)); 262 default:return_trace (true); 263 } 264 } 265 266 protected: 267 union { 268 struct { 269 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ 270 FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0), 271 * 0x00010000u or 0x00020000u */ 272 } header; 273 TTCHeaderVersion1 version1; 274 } u; 275 }; 276 277 /* 278 * Mac Resource Fork 279 * 280 * http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html 281 */ 282 283 struct ResourceRecord 284 { 285 const OpenTypeFontFace & get_face (const void *data_base) const 286 { return CastR<OpenTypeFontFace> ((data_base+offset).arrayZ); } 287 288 bool sanitize (hb_sanitize_context_t *c, 289 const void *data_base) const 290 { 291 TRACE_SANITIZE (this); 292 return_trace (c->check_struct (this) && 293 offset.sanitize (c, data_base) && 294 get_face (data_base).sanitize (c)); 295 } 296 297 protected: 298 HBUINT16 id; /* Resource ID. */ 299 HBINT16 nameOffset; /* Offset from beginning of resource name list 300 * to resource name, -1 means there is none. */ 301 HBUINT8 attrs; /* Resource attributes */ 302 NNOffsetTo<LArrayOf<HBUINT8>, HBUINT24> 303 offset; /* Offset from beginning of data block to 304 * data for this resource */ 305 HBUINT32 reserved; /* Reserved for handle to resource */ 306 public: 307 DEFINE_SIZE_STATIC (12); 308 }; 309 310 #define HB_TAG_sfnt HB_TAG ('s','f','n','t') 311 312 struct ResourceTypeRecord 313 { 314 unsigned int get_resource_count () const 315 { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; } 316 317 bool is_sfnt () const { return tag == HB_TAG_sfnt; } 318 319 const ResourceRecord& get_resource_record (unsigned int i, 320 const void *type_base) const 321 { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; } 322 323 bool sanitize (hb_sanitize_context_t *c, 324 const void *type_base, 325 const void *data_base) const 326 { 327 TRACE_SANITIZE (this); 328 return_trace (c->check_struct (this) && 329 resourcesZ.sanitize (c, type_base, 330 get_resource_count (), 331 data_base)); 332 } 333 334 protected: 335 Tag tag; /* Resource type. */ 336 HBUINT16 resCountM1; /* Number of resources minus 1. */ 337 NNOffsetTo<UnsizedArrayOf<ResourceRecord> > 338 resourcesZ; /* Offset from beginning of resource type list 339 * to reference item list for this type. */ 340 public: 341 DEFINE_SIZE_STATIC (8); 342 }; 343 344 struct ResourceMap 345 { 346 unsigned int get_face_count () const 347 { 348 unsigned int count = get_type_count (); 349 for (unsigned int i = 0; i < count; i++) 350 { 351 const ResourceTypeRecord& type = get_type_record (i); 352 if (type.is_sfnt ()) 353 return type.get_resource_count (); 354 } 355 return 0; 356 } 357 358 const OpenTypeFontFace& get_face (unsigned int idx, 359 const void *data_base) const 360 { 361 unsigned int count = get_type_count (); 362 for (unsigned int i = 0; i < count; i++) 363 { 364 const ResourceTypeRecord& type = get_type_record (i); 365 /* The check for idx < count is here because ResourceRecord is NOT null-safe. 366 * Because an offset of 0 there does NOT mean null. */ 367 if (type.is_sfnt () && idx < type.get_resource_count ()) 368 return type.get_resource_record (idx, &(this+typeList)).get_face (data_base); 369 } 370 return Null (OpenTypeFontFace); 371 } 372 373 bool sanitize (hb_sanitize_context_t *c, const void *data_base) const 374 { 375 TRACE_SANITIZE (this); 376 return_trace (c->check_struct (this) && 377 typeList.sanitize (c, this, 378 &(this+typeList), 379 data_base)); 380 } 381 382 private: 383 unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; } 384 385 const ResourceTypeRecord& get_type_record (unsigned int i) const 386 { return (this+typeList)[i]; } 387 388 protected: 389 HBUINT8 reserved0[16]; /* Reserved for copy of resource header */ 390 HBUINT32 reserved1; /* Reserved for handle to next resource map */ 391 HBUINT16 resreved2; /* Reserved for file reference number */ 392 HBUINT16 attrs; /* Resource fork attribute */ 393 NNOffsetTo<ArrayOfM1<ResourceTypeRecord> > 394 typeList; /* Offset from beginning of map to 395 * resource type list */ 396 Offset16 nameList; /* Offset from beginning of map to 397 * resource name list */ 398 public: 399 DEFINE_SIZE_STATIC (28); 400 }; 401 402 struct ResourceForkHeader 403 { 404 unsigned int get_face_count () const 405 { return (this+map).get_face_count (); } 406 407 const OpenTypeFontFace& get_face (unsigned int idx, 408 unsigned int *base_offset = nullptr) const 409 { 410 const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data)); 411 if (base_offset) 412 *base_offset = (const char *) &face - (const char *) this; 413 return face; 414 } 415 416 bool sanitize (hb_sanitize_context_t *c) const 417 { 418 TRACE_SANITIZE (this); 419 return_trace (c->check_struct (this) && 420 data.sanitize (c, this, dataLen) && 421 map.sanitize (c, this, &(this+data))); 422 } 423 424 protected: 425 LNNOffsetTo<UnsizedArrayOf<HBUINT8> > 426 data; /* Offset from beginning of resource fork 427 * to resource data */ 428 LNNOffsetTo<ResourceMap > 429 map; /* Offset from beginning of resource fork 430 * to resource map */ 431 HBUINT32 dataLen; /* Length of resource data */ 432 HBUINT32 mapLen; /* Length of resource map */ 433 public: 434 DEFINE_SIZE_STATIC (16); 435 }; 436 437 /* 438 * OpenType Font File 439 */ 440 441 struct OpenTypeFontFile 442 { 443 enum { 444 CFFTag = HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */ 445 TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */ 446 TTCTag = HB_TAG ('t','t','c','f'), /* TrueType Collection */ 447 DFontTag = HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */ 448 TrueTag = HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */ 449 Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */ 450 }; 451 452 hb_tag_t get_tag () const { return u.tag; } 453 454 unsigned int get_face_count () const 455 { 456 switch (u.tag) { 457 case CFFTag: /* All the non-collection tags */ 458 case TrueTag: 459 case Typ1Tag: 460 case TrueTypeTag: return 1; 461 case TTCTag: return u.ttcHeader.get_face_count (); 462 case DFontTag: return u.rfHeader.get_face_count (); 463 default: return 0; 464 } 465 } 466 const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const 467 { 468 if (base_offset) 469 *base_offset = 0; 470 switch (u.tag) { 471 /* Note: for non-collection SFNT data we ignore index. This is because 472 * Apple dfont container is a container of SFNT's. So each SFNT is a 473 * non-TTC, but the index is more than zero. */ 474 case CFFTag: /* All the non-collection tags */ 475 case TrueTag: 476 case Typ1Tag: 477 case TrueTypeTag: return u.fontFace; 478 case TTCTag: return u.ttcHeader.get_face (i); 479 case DFontTag: return u.rfHeader.get_face (i, base_offset); 480 default: return Null(OpenTypeFontFace); 481 } 482 } 483 484 template <typename item_t> 485 bool serialize_single (hb_serialize_context_t *c, 486 hb_tag_t sfnt_tag, 487 hb_array_t<item_t> items) 488 { 489 TRACE_SERIALIZE (this); 490 assert (sfnt_tag != TTCTag); 491 if (unlikely (!c->extend_min (*this))) return_trace (false); 492 return_trace (u.fontFace.serialize (c, sfnt_tag, items)); 493 } 494 495 bool sanitize (hb_sanitize_context_t *c) const 496 { 497 TRACE_SANITIZE (this); 498 if (unlikely (!u.tag.sanitize (c))) return_trace (false); 499 switch (u.tag) { 500 case CFFTag: /* All the non-collection tags */ 501 case TrueTag: 502 case Typ1Tag: 503 case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); 504 case TTCTag: return_trace (u.ttcHeader.sanitize (c)); 505 case DFontTag: return_trace (u.rfHeader.sanitize (c)); 506 default: return_trace (true); 507 } 508 } 509 510 protected: 511 union { 512 Tag tag; /* 4-byte identifier. */ 513 OpenTypeFontFace fontFace; 514 TTCHeader ttcHeader; 515 ResourceForkHeader rfHeader; 516 } u; 517 public: 518 DEFINE_SIZE_UNION (4, tag); 519 }; 520 521 522 } /* namespace OT */ 523 524 525 #endif /* HB_OPEN_FILE_HH */