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 = it.len (); 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); 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: 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: 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 switch (u.header.version.major) { 271 case 2: /* version 2 is compatible with version 1 */ 272 case 1: return_trace (u.version1.sanitize (c)); 273 default:return_trace (true); 274 } 275 } 276 277 protected: 278 union { 279 struct { 280 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ 281 FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0), 282 * 0x00010000u or 0x00020000u */ 283 } header; 284 TTCHeaderVersion1 version1; 285 } u; 286 }; 287 288 /* 289 * Mac Resource Fork 290 * 291 * http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html 292 */ 293 294 struct ResourceRecord 295 { get_faceOT::ResourceRecord296 const OpenTypeFontFace & get_face (const void *data_base) const 297 { return * reinterpret_cast<const OpenTypeFontFace *> ((data_base+offset).arrayZ); } 298 sanitizeOT::ResourceRecord299 bool sanitize (hb_sanitize_context_t *c, 300 const void *data_base) const 301 { 302 TRACE_SANITIZE (this); 303 return_trace (c->check_struct (this) && 304 offset.sanitize (c, data_base) && 305 get_face (data_base).sanitize (c)); 306 } 307 308 protected: 309 HBUINT16 id; /* Resource ID. */ 310 HBINT16 nameOffset; /* Offset from beginning of resource name list 311 * to resource name, -1 means there is none. */ 312 HBUINT8 attrs; /* Resource attributes */ 313 NNOffset24To<Array32Of<HBUINT8>> 314 offset; /* Offset from beginning of data block to 315 * data for this resource */ 316 HBUINT32 reserved; /* Reserved for handle to resource */ 317 public: 318 DEFINE_SIZE_STATIC (12); 319 }; 320 321 #define HB_TAG_sfnt HB_TAG ('s','f','n','t') 322 323 struct ResourceTypeRecord 324 { get_resource_countOT::ResourceTypeRecord325 unsigned int get_resource_count () const 326 { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; } 327 is_sfntOT::ResourceTypeRecord328 bool is_sfnt () const { return tag == HB_TAG_sfnt; } 329 get_resource_recordOT::ResourceTypeRecord330 const ResourceRecord& get_resource_record (unsigned int i, 331 const void *type_base) const 332 { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; } 333 sanitizeOT::ResourceTypeRecord334 bool sanitize (hb_sanitize_context_t *c, 335 const void *type_base, 336 const void *data_base) const 337 { 338 TRACE_SANITIZE (this); 339 return_trace (c->check_struct (this) && 340 resourcesZ.sanitize (c, type_base, 341 get_resource_count (), 342 data_base)); 343 } 344 345 protected: 346 Tag tag; /* Resource type. */ 347 HBUINT16 resCountM1; /* Number of resources minus 1. */ 348 NNOffset16To<UnsizedArrayOf<ResourceRecord>> 349 resourcesZ; /* Offset from beginning of resource type list 350 * to reference item list for this type. */ 351 public: 352 DEFINE_SIZE_STATIC (8); 353 }; 354 355 struct ResourceMap 356 { get_face_countOT::ResourceMap357 unsigned int get_face_count () const 358 { 359 unsigned int count = get_type_count (); 360 for (unsigned int i = 0; i < count; i++) 361 { 362 const ResourceTypeRecord& type = get_type_record (i); 363 if (type.is_sfnt ()) 364 return type.get_resource_count (); 365 } 366 return 0; 367 } 368 get_faceOT::ResourceMap369 const OpenTypeFontFace& get_face (unsigned int idx, 370 const void *data_base) const 371 { 372 unsigned int count = get_type_count (); 373 for (unsigned int i = 0; i < count; i++) 374 { 375 const ResourceTypeRecord& type = get_type_record (i); 376 /* The check for idx < count is here because ResourceRecord is NOT null-safe. 377 * Because an offset of 0 there does NOT mean null. */ 378 if (type.is_sfnt () && idx < type.get_resource_count ()) 379 return type.get_resource_record (idx, &(this+typeList)).get_face (data_base); 380 } 381 return Null (OpenTypeFontFace); 382 } 383 sanitizeOT::ResourceMap384 bool sanitize (hb_sanitize_context_t *c, const void *data_base) const 385 { 386 TRACE_SANITIZE (this); 387 return_trace (c->check_struct (this) && 388 typeList.sanitize (c, this, 389 &(this+typeList), 390 data_base)); 391 } 392 393 private: get_type_countOT::ResourceMap394 unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; } 395 get_type_recordOT::ResourceMap396 const ResourceTypeRecord& get_type_record (unsigned int i) const 397 { return (this+typeList)[i]; } 398 399 protected: 400 HBUINT8 reserved0[16]; /* Reserved for copy of resource header */ 401 HBUINT32 reserved1; /* Reserved for handle to next resource map */ 402 HBUINT16 resreved2; /* Reserved for file reference number */ 403 HBUINT16 attrs; /* Resource fork attribute */ 404 NNOffset16To<ArrayOfM1<ResourceTypeRecord>> 405 typeList; /* Offset from beginning of map to 406 * resource type list */ 407 Offset16 nameList; /* Offset from beginning of map to 408 * resource name list */ 409 public: 410 DEFINE_SIZE_STATIC (28); 411 }; 412 413 struct ResourceForkHeader 414 { get_face_countOT::ResourceForkHeader415 unsigned int get_face_count () const 416 { return (this+map).get_face_count (); } 417 get_faceOT::ResourceForkHeader418 const OpenTypeFontFace& get_face (unsigned int idx, 419 unsigned int *base_offset = nullptr) const 420 { 421 const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data)); 422 if (base_offset) 423 *base_offset = (const char *) &face - (const char *) this; 424 return face; 425 } 426 sanitizeOT::ResourceForkHeader427 bool sanitize (hb_sanitize_context_t *c) const 428 { 429 TRACE_SANITIZE (this); 430 return_trace (c->check_struct (this) && 431 data.sanitize (c, this, dataLen) && 432 map.sanitize (c, this, &(this+data))); 433 } 434 435 protected: 436 NNOffset32To<UnsizedArrayOf<HBUINT8>> 437 data; /* Offset from beginning of resource fork 438 * to resource data */ 439 NNOffset32To<ResourceMap > 440 map; /* Offset from beginning of resource fork 441 * to resource map */ 442 HBUINT32 dataLen; /* Length of resource data */ 443 HBUINT32 mapLen; /* Length of resource map */ 444 public: 445 DEFINE_SIZE_STATIC (16); 446 }; 447 448 /* 449 * OpenType Font File 450 */ 451 452 struct OpenTypeFontFile 453 { 454 enum { 455 CFFTag = HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */ 456 TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */ 457 TTCTag = HB_TAG ('t','t','c','f'), /* TrueType Collection */ 458 DFontTag = HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */ 459 TrueTag = HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */ 460 Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */ 461 }; 462 get_tagOT::OpenTypeFontFile463 hb_tag_t get_tag () const { return u.tag; } 464 get_face_countOT::OpenTypeFontFile465 unsigned int get_face_count () const 466 { 467 switch (u.tag) { 468 case CFFTag: /* All the non-collection tags */ 469 case TrueTag: 470 case Typ1Tag: 471 case TrueTypeTag: return 1; 472 case TTCTag: return u.ttcHeader.get_face_count (); 473 case DFontTag: return u.rfHeader.get_face_count (); 474 default: return 0; 475 } 476 } get_faceOT::OpenTypeFontFile477 const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const 478 { 479 if (base_offset) 480 *base_offset = 0; 481 switch (u.tag) { 482 /* Note: for non-collection SFNT data we ignore index. This is because 483 * Apple dfont container is a container of SFNT's. So each SFNT is a 484 * non-TTC, but the index is more than zero. */ 485 case CFFTag: /* All the non-collection tags */ 486 case TrueTag: 487 case Typ1Tag: 488 case TrueTypeTag: return u.fontFace; 489 case TTCTag: return u.ttcHeader.get_face (i); 490 case DFontTag: return u.rfHeader.get_face (i, base_offset); 491 default: return Null (OpenTypeFontFace); 492 } 493 } 494 495 template <typename Iterator, 496 hb_requires ((hb_is_source_of<Iterator, hb_pair_t<hb_tag_t, hb_blob_t *>>::value))> serialize_singleOT::OpenTypeFontFile497 bool serialize_single (hb_serialize_context_t *c, 498 hb_tag_t sfnt_tag, 499 Iterator items) 500 { 501 TRACE_SERIALIZE (this); 502 assert (sfnt_tag != TTCTag); 503 if (unlikely (!c->extend_min (this))) return_trace (false); 504 return_trace (u.fontFace.serialize (c, sfnt_tag, items)); 505 } 506 sanitizeOT::OpenTypeFontFile507 bool sanitize (hb_sanitize_context_t *c) const 508 { 509 TRACE_SANITIZE (this); 510 if (unlikely (!u.tag.sanitize (c))) return_trace (false); 511 switch (u.tag) { 512 case CFFTag: /* All the non-collection tags */ 513 case TrueTag: 514 case Typ1Tag: 515 case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); 516 case TTCTag: return_trace (u.ttcHeader.sanitize (c)); 517 case DFontTag: return_trace (u.rfHeader.sanitize (c)); 518 default: return_trace (true); 519 } 520 } 521 522 protected: 523 union { 524 Tag tag; /* 4-byte identifier. */ 525 OpenTypeFontFace fontFace; 526 TTCHeader ttcHeader; 527 ResourceForkHeader rfHeader; 528 } u; 529 public: 530 DEFINE_SIZE_UNION (4, tag); 531 }; 532 533 534 } /* namespace OT */ 535 536 537 #endif /* HB_OPEN_FILE_HH */ 538