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 * The OpenType Font File 41 * 42 */ 43 44 45 /* 46 * Organization of an OpenType Font 47 */ 48 49 struct OpenTypeFontFile; 50 struct OpenTypeOffsetTable; 51 struct TTCHeader; 52 53 54 typedef struct TableRecord 55 { cmpOT::TableRecord56 int cmp (Tag t) const { return -t.cmp (tag); } 57 cmpOT::TableRecord58 HB_INTERNAL static int cmp (const void *pa, const void *pb) 59 { 60 const TableRecord *a = (const TableRecord *) pa; 61 const TableRecord *b = (const TableRecord *) pb; 62 return b->cmp (a->tag); 63 } 64 sanitizeOT::TableRecord65 bool sanitize (hb_sanitize_context_t *c) const 66 { 67 TRACE_SANITIZE (this); 68 return_trace (c->check_struct (this)); 69 } 70 71 Tag tag; /* 4-byte identifier. */ 72 CheckSum checkSum; /* CheckSum for this table. */ 73 Offset32 offset; /* Offset from beginning of TrueType font 74 * file. */ 75 HBUINT32 length; /* Length of this table. */ 76 public: 77 DEFINE_SIZE_STATIC (16); 78 } OpenTypeTable; 79 80 typedef struct OpenTypeOffsetTable 81 { 82 friend struct OpenTypeFontFile; 83 get_table_countOT::OpenTypeOffsetTable84 unsigned int get_table_count () const { return tables.len; } get_tableOT::OpenTypeOffsetTable85 const TableRecord& get_table (unsigned int i) const 86 { return tables[i]; } get_table_tagsOT::OpenTypeOffsetTable87 unsigned int get_table_tags (unsigned int start_offset, 88 unsigned int *table_count, /* IN/OUT */ 89 hb_tag_t *table_tags /* OUT */) const 90 { 91 if (table_count) 92 { 93 + tables.as_array ().sub_array (start_offset, table_count) 94 | hb_map (&TableRecord::tag) 95 | hb_sink (hb_array (table_tags, *table_count)) 96 ; 97 } 98 return tables.len; 99 } find_table_indexOT::OpenTypeOffsetTable100 bool find_table_index (hb_tag_t tag, unsigned int *table_index) const 101 { 102 Tag t; 103 t = tag; 104 /* Use lfind for small fonts; there are fonts that have unsorted table entries; 105 * those tend to work in other tools, so tolerate them. 106 * https://github.com/harfbuzz/harfbuzz/issues/3065 */ 107 if (tables.len < 16) 108 return tables.lfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); 109 else 110 return tables.bfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); 111 } get_table_by_tagOT::OpenTypeOffsetTable112 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 Iterator, 122 hb_requires ((hb_is_source_of<Iterator, hb_pair_t<hb_tag_t, hb_blob_t *>>::value))> serializeOT::OpenTypeOffsetTable123 bool serialize (hb_serialize_context_t *c, 124 hb_tag_t sfnt_tag, 125 Iterator it) 126 { 127 TRACE_SERIALIZE (this); 128 /* Alloc 12 for the OTHeader. */ 129 if (unlikely (!c->extend_min (this))) return_trace (false); 130 /* Write sfntVersion (bytes 0..3). */ 131 sfnt_version = sfnt_tag; 132 /* Take space for numTables, searchRange, entrySelector, RangeShift 133 * and the TableRecords themselves. */ 134 unsigned num_items = hb_len (it); 135 if (unlikely (!tables.serialize (c, num_items))) return_trace (false); 136 137 const char *dir_end = (const char *) c->head; 138 HBUINT32 *checksum_adjustment = nullptr; 139 140 /* Write OffsetTables, alloc for and write actual table blobs. */ 141 unsigned i = 0; 142 for (hb_pair_t<hb_tag_t, hb_blob_t*> entry : it) 143 { 144 hb_blob_t *blob = entry.second; 145 unsigned len = blob->length; 146 147 /* Allocate room for the table and copy it. */ 148 char *start = (char *) c->allocate_size<void> (len, false); 149 if (unlikely (!start)) return false; 150 151 TableRecord &rec = tables.arrayZ[i]; 152 rec.tag = entry.first; 153 rec.length = len; 154 rec.offset = 0; 155 if (unlikely (!c->check_assign (rec.offset, 156 (unsigned) ((char *) start - (char *) this), 157 HB_SERIALIZE_ERROR_OFFSET_OVERFLOW))) 158 return_trace (false); 159 160 if (likely (len)) 161 hb_memcpy (start, blob->data, len); 162 163 /* 4-byte alignment. */ 164 c->align (4); 165 const char *end = (const char *) c->head; 166 167 if (entry.first == HB_OT_TAG_head && 168 (unsigned) (end - start) >= head::static_size) 169 { 170 head *h = (head *) start; 171 checksum_adjustment = &h->checkSumAdjustment; 172 *checksum_adjustment = 0; 173 } 174 175 rec.checkSum.set_for_data (start, end - start); 176 i++; 177 } 178 179 tables.qsort (); 180 181 if (checksum_adjustment) 182 { 183 CheckSum checksum; 184 185 /* The following line is a slower version of the following block. */ 186 //checksum.set_for_data (this, (const char *) c->head - (const char *) this); 187 checksum.set_for_data (this, dir_end - (const char *) this); 188 for (unsigned int i = 0; i < num_items; i++) 189 { 190 TableRecord &rec = tables.arrayZ[i]; 191 checksum = checksum + rec.checkSum; 192 } 193 194 *checksum_adjustment = 0xB1B0AFBAu - checksum; 195 } 196 197 return_trace (true); 198 } 199 sanitizeOT::OpenTypeOffsetTable200 bool sanitize (hb_sanitize_context_t *c) const 201 { 202 TRACE_SANITIZE (this); 203 return_trace (c->check_struct (this) && tables.sanitize (c)); 204 } 205 206 protected: 207 Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ 208 BinSearchArrayOf<TableRecord> 209 tables; 210 public: 211 DEFINE_SIZE_ARRAY (12, tables); 212 } OpenTypeFontFace; 213 214 215 /* 216 * TrueType Collections 217 */ 218 219 struct TTCHeaderVersion1 220 { 221 friend struct TTCHeader; 222 get_face_countOT::TTCHeaderVersion1223 unsigned int get_face_count () const { return table.len; } get_faceOT::TTCHeaderVersion1224 const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } 225 sanitizeOT::TTCHeaderVersion1226 bool sanitize (hb_sanitize_context_t *c) const 227 { 228 TRACE_SANITIZE (this); 229 return_trace (table.sanitize (c, this)); 230 } 231 232 protected: 233 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ 234 FixedVersion<>version; /* Version of the TTC Header (1.0), 235 * 0x00010000u */ 236 Array32Of<Offset32To<OpenTypeOffsetTable>> 237 table; /* Array of offsets to the OffsetTable for each font 238 * from the beginning of the file */ 239 public: 240 DEFINE_SIZE_ARRAY (12, table); 241 }; 242 243 struct TTCHeader 244 { 245 friend struct OpenTypeFontFile; 246 247 private: 248 get_face_countOT::TTCHeader249 unsigned int get_face_count () const 250 { 251 switch (u.header.version.major) { 252 case 2: /* version 2 is compatible with version 1 */ 253 case 1: hb_barrier (); return u.version1.get_face_count (); 254 default:return 0; 255 } 256 } get_faceOT::TTCHeader257 const OpenTypeFontFace& get_face (unsigned int i) const 258 { 259 switch (u.header.version.major) { 260 case 2: /* version 2 is compatible with version 1 */ 261 case 1: hb_barrier (); return u.version1.get_face (i); 262 default:return Null (OpenTypeFontFace); 263 } 264 } 265 sanitizeOT::TTCHeader266 bool sanitize (hb_sanitize_context_t *c) const 267 { 268 TRACE_SANITIZE (this); 269 if (unlikely (!u.header.version.sanitize (c))) return_trace (false); 270 hb_barrier (); 271 switch (u.header.version.major) { 272 case 2: /* version 2 is compatible with version 1 */ 273 case 1: hb_barrier (); return_trace (u.version1.sanitize (c)); 274 default:return_trace (true); 275 } 276 } 277 278 protected: 279 union { 280 struct { 281 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ 282 FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0), 283 * 0x00010000u or 0x00020000u */ 284 } header; 285 TTCHeaderVersion1 version1; 286 } u; 287 }; 288 289 /* 290 * Mac Resource Fork 291 * 292 * http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html 293 */ 294 295 struct ResourceRecord 296 { get_faceOT::ResourceRecord297 const OpenTypeFontFace & get_face (const void *data_base) const 298 { return * reinterpret_cast<const OpenTypeFontFace *> ((data_base+offset).arrayZ); } 299 sanitizeOT::ResourceRecord300 bool sanitize (hb_sanitize_context_t *c, 301 const void *data_base) const 302 { 303 TRACE_SANITIZE (this); 304 return_trace (c->check_struct (this) && 305 offset.sanitize (c, data_base) && 306 hb_barrier () && 307 get_face (data_base).sanitize (c)); 308 } 309 310 protected: 311 HBUINT16 id; /* Resource ID. */ 312 HBINT16 nameOffset; /* Offset from beginning of resource name list 313 * to resource name, -1 means there is none. */ 314 HBUINT8 attrs; /* Resource attributes */ 315 NNOffset24To<Array32Of<HBUINT8>> 316 offset; /* Offset from beginning of data block to 317 * data for this resource */ 318 HBUINT32 reserved; /* Reserved for handle to resource */ 319 public: 320 DEFINE_SIZE_STATIC (12); 321 }; 322 323 #define HB_TAG_sfnt HB_TAG ('s','f','n','t') 324 325 struct ResourceTypeRecord 326 { get_resource_countOT::ResourceTypeRecord327 unsigned int get_resource_count () const 328 { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; } 329 is_sfntOT::ResourceTypeRecord330 bool is_sfnt () const { return tag == HB_TAG_sfnt; } 331 get_resource_recordOT::ResourceTypeRecord332 const ResourceRecord& get_resource_record (unsigned int i, 333 const void *type_base) const 334 { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; } 335 sanitizeOT::ResourceTypeRecord336 bool sanitize (hb_sanitize_context_t *c, 337 const void *type_base, 338 const void *data_base) const 339 { 340 TRACE_SANITIZE (this); 341 return_trace (c->check_struct (this) && 342 hb_barrier () && 343 resourcesZ.sanitize (c, type_base, 344 get_resource_count (), 345 data_base)); 346 } 347 348 protected: 349 Tag tag; /* Resource type. */ 350 HBUINT16 resCountM1; /* Number of resources minus 1. */ 351 NNOffset16To<UnsizedArrayOf<ResourceRecord>> 352 resourcesZ; /* Offset from beginning of resource type list 353 * to reference item list for this type. */ 354 public: 355 DEFINE_SIZE_STATIC (8); 356 }; 357 358 struct ResourceMap 359 { get_face_countOT::ResourceMap360 unsigned int get_face_count () const 361 { 362 unsigned int count = get_type_count (); 363 for (unsigned int i = 0; i < count; i++) 364 { 365 const ResourceTypeRecord& type = get_type_record (i); 366 if (type.is_sfnt ()) 367 return type.get_resource_count (); 368 } 369 return 0; 370 } 371 get_faceOT::ResourceMap372 const OpenTypeFontFace& get_face (unsigned int idx, 373 const void *data_base) const 374 { 375 unsigned int count = get_type_count (); 376 for (unsigned int i = 0; i < count; i++) 377 { 378 const ResourceTypeRecord& type = get_type_record (i); 379 /* The check for idx < count is here because ResourceRecord is NOT null-safe. 380 * Because an offset of 0 there does NOT mean null. */ 381 if (type.is_sfnt () && idx < type.get_resource_count ()) 382 return type.get_resource_record (idx, &(this+typeList)).get_face (data_base); 383 } 384 return Null (OpenTypeFontFace); 385 } 386 sanitizeOT::ResourceMap387 bool sanitize (hb_sanitize_context_t *c, const void *data_base) const 388 { 389 TRACE_SANITIZE (this); 390 return_trace (c->check_struct (this) && 391 hb_barrier () && 392 typeList.sanitize (c, this, 393 &(this+typeList), 394 data_base)); 395 } 396 397 private: get_type_countOT::ResourceMap398 unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; } 399 get_type_recordOT::ResourceMap400 const ResourceTypeRecord& get_type_record (unsigned int i) const 401 { return (this+typeList)[i]; } 402 403 protected: 404 HBUINT8 reserved0[16]; /* Reserved for copy of resource header */ 405 HBUINT32 reserved1; /* Reserved for handle to next resource map */ 406 HBUINT16 resreved2; /* Reserved for file reference number */ 407 HBUINT16 attrs; /* Resource fork attribute */ 408 NNOffset16To<ArrayOfM1<ResourceTypeRecord>> 409 typeList; /* Offset from beginning of map to 410 * resource type list */ 411 Offset16 nameList; /* Offset from beginning of map to 412 * resource name list */ 413 public: 414 DEFINE_SIZE_STATIC (28); 415 }; 416 417 struct ResourceForkHeader 418 { get_face_countOT::ResourceForkHeader419 unsigned int get_face_count () const 420 { return (this+map).get_face_count (); } 421 get_faceOT::ResourceForkHeader422 const OpenTypeFontFace& get_face (unsigned int idx, 423 unsigned int *base_offset = nullptr) const 424 { 425 const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data)); 426 if (base_offset) 427 *base_offset = (const char *) &face - (const char *) this; 428 return face; 429 } 430 sanitizeOT::ResourceForkHeader431 bool sanitize (hb_sanitize_context_t *c) const 432 { 433 TRACE_SANITIZE (this); 434 return_trace (c->check_struct (this) && 435 hb_barrier () && 436 data.sanitize (c, this, dataLen) && 437 map.sanitize (c, this, &(this+data))); 438 } 439 440 protected: 441 NNOffset32To<UnsizedArrayOf<HBUINT8>> 442 data; /* Offset from beginning of resource fork 443 * to resource data */ 444 NNOffset32To<ResourceMap > 445 map; /* Offset from beginning of resource fork 446 * to resource map */ 447 HBUINT32 dataLen; /* Length of resource data */ 448 HBUINT32 mapLen; /* Length of resource map */ 449 public: 450 DEFINE_SIZE_STATIC (16); 451 }; 452 453 /* 454 * OpenType Font File 455 */ 456 457 struct OpenTypeFontFile 458 { 459 enum { 460 CFFTag = HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */ 461 TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */ 462 TTCTag = HB_TAG ('t','t','c','f'), /* TrueType Collection */ 463 DFontTag = HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */ 464 TrueTag = HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */ 465 Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */ 466 }; 467 get_tagOT::OpenTypeFontFile468 hb_tag_t get_tag () const { return u.tag; } 469 get_face_countOT::OpenTypeFontFile470 unsigned int get_face_count () const 471 { 472 switch (u.tag) { 473 case CFFTag: /* All the non-collection tags */ 474 case TrueTag: 475 case Typ1Tag: 476 case TrueTypeTag: return 1; 477 case TTCTag: return u.ttcHeader.get_face_count (); 478 case DFontTag: return u.rfHeader.get_face_count (); 479 default: return 0; 480 } 481 } get_faceOT::OpenTypeFontFile482 const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const 483 { 484 if (base_offset) 485 *base_offset = 0; 486 switch (u.tag) { 487 /* Note: for non-collection SFNT data we ignore index. This is because 488 * Apple dfont container is a container of SFNT's. So each SFNT is a 489 * non-TTC, but the index is more than zero. */ 490 case CFFTag: /* All the non-collection tags */ 491 case TrueTag: 492 case Typ1Tag: 493 case TrueTypeTag: return u.fontFace; 494 case TTCTag: return u.ttcHeader.get_face (i); 495 case DFontTag: return u.rfHeader.get_face (i, base_offset); 496 default: return Null (OpenTypeFontFace); 497 } 498 } 499 500 template <typename Iterator, 501 hb_requires ((hb_is_source_of<Iterator, hb_pair_t<hb_tag_t, hb_blob_t *>>::value))> serialize_singleOT::OpenTypeFontFile502 bool serialize_single (hb_serialize_context_t *c, 503 hb_tag_t sfnt_tag, 504 Iterator items) 505 { 506 TRACE_SERIALIZE (this); 507 assert (sfnt_tag != TTCTag); 508 if (unlikely (!c->extend_min (this))) return_trace (false); 509 return_trace (u.fontFace.serialize (c, sfnt_tag, items)); 510 } 511 sanitizeOT::OpenTypeFontFile512 bool sanitize (hb_sanitize_context_t *c) const 513 { 514 TRACE_SANITIZE (this); 515 if (unlikely (!u.tag.sanitize (c))) return_trace (false); 516 hb_barrier (); 517 switch (u.tag) { 518 case CFFTag: /* All the non-collection tags */ 519 case TrueTag: 520 case Typ1Tag: 521 case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); 522 case TTCTag: return_trace (u.ttcHeader.sanitize (c)); 523 case DFontTag: return_trace (u.rfHeader.sanitize (c)); 524 default: return_trace (true); 525 } 526 } 527 528 protected: 529 union { 530 Tag tag; /* 4-byte identifier. */ 531 OpenTypeFontFace fontFace; 532 TTCHeader ttcHeader; 533 ResourceForkHeader rfHeader; 534 } u; 535 public: 536 DEFINE_SIZE_UNION (4, tag); 537 }; 538 539 540 } /* namespace OT */ 541 542 543 #endif /* HB_OPEN_FILE_HH */ 544