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 { cmpOT::TableRecord57 int cmp (Tag t) const { return -t.cmp (tag); } 58 cmpOT::TableRecord59 HB_INTERNAL 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 sanitizeOT::TableRecord66 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 get_table_countOT::OffsetTable85 unsigned int get_table_count () const { return tables.len; } get_tableOT::OffsetTable86 const TableRecord& get_table (unsigned int i) const 87 { return tables[i]; } get_table_tagsOT::OffsetTable88 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 = hb_min (*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 } find_table_indexOT::OffsetTable106 bool find_table_index (hb_tag_t tag, unsigned int *table_index) const 107 { 108 Tag t; 109 t = tag; 110 return tables.bfind (t, table_index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); 111 } get_table_by_tagOT::OffsetTable112 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> serializeOT::OffsetTable122 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 = 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 = items[i].tag; 144 rec.length = blob->length; 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 if (likely (rec.length)) 152 memcpy (start, blob->data, rec.length); 153 154 /* 4-byte alignment. */ 155 c->align (4); 156 const char *end = (const char *) c->head; 157 158 if (items[i].tag == HB_OT_TAG_head && 159 (unsigned) (end - start) >= head::static_size) 160 { 161 head *h = (head *) start; 162 checksum_adjustment = &h->checkSumAdjustment; 163 *checksum_adjustment = 0; 164 } 165 166 rec.checkSum.set_for_data (start, end - start); 167 } 168 169 tables.qsort (); 170 171 if (checksum_adjustment) 172 { 173 CheckSum checksum; 174 175 /* The following line is a slower version of the following block. */ 176 //checksum.set_for_data (this, (const char *) c->head - (const char *) this); 177 checksum.set_for_data (this, dir_end - (const char *) this); 178 for (unsigned int i = 0; i < items.length; i++) 179 { 180 TableRecord &rec = tables.arrayZ[i]; 181 checksum = checksum + rec.checkSum; 182 } 183 184 *checksum_adjustment = 0xB1B0AFBAu - checksum; 185 } 186 187 return_trace (true); 188 } 189 sanitizeOT::OffsetTable190 bool sanitize (hb_sanitize_context_t *c) const 191 { 192 TRACE_SANITIZE (this); 193 return_trace (c->check_struct (this) && tables.sanitize (c)); 194 } 195 196 protected: 197 Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ 198 BinSearchArrayOf<TableRecord> 199 tables; 200 public: 201 DEFINE_SIZE_ARRAY (12, tables); 202 } OpenTypeFontFace; 203 204 205 /* 206 * TrueType Collections 207 */ 208 209 struct TTCHeaderVersion1 210 { 211 friend struct TTCHeader; 212 get_face_countOT::TTCHeaderVersion1213 unsigned int get_face_count () const { return table.len; } get_faceOT::TTCHeaderVersion1214 const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } 215 sanitizeOT::TTCHeaderVersion1216 bool sanitize (hb_sanitize_context_t *c) const 217 { 218 TRACE_SANITIZE (this); 219 return_trace (table.sanitize (c, this)); 220 } 221 222 protected: 223 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ 224 FixedVersion<>version; /* Version of the TTC Header (1.0), 225 * 0x00010000u */ 226 LArrayOf<LOffsetTo<OffsetTable>> 227 table; /* Array of offsets to the OffsetTable for each font 228 * from the beginning of the file */ 229 public: 230 DEFINE_SIZE_ARRAY (12, table); 231 }; 232 233 struct TTCHeader 234 { 235 friend struct OpenTypeFontFile; 236 237 private: 238 get_face_countOT::TTCHeader239 unsigned int get_face_count () const 240 { 241 switch (u.header.version.major) { 242 case 2: /* version 2 is compatible with version 1 */ 243 case 1: return u.version1.get_face_count (); 244 default:return 0; 245 } 246 } get_faceOT::TTCHeader247 const OpenTypeFontFace& get_face (unsigned int i) const 248 { 249 switch (u.header.version.major) { 250 case 2: /* version 2 is compatible with version 1 */ 251 case 1: return u.version1.get_face (i); 252 default:return Null(OpenTypeFontFace); 253 } 254 } 255 sanitizeOT::TTCHeader256 bool sanitize (hb_sanitize_context_t *c) const 257 { 258 TRACE_SANITIZE (this); 259 if (unlikely (!u.header.version.sanitize (c))) return_trace (false); 260 switch (u.header.version.major) { 261 case 2: /* version 2 is compatible with version 1 */ 262 case 1: return_trace (u.version1.sanitize (c)); 263 default:return_trace (true); 264 } 265 } 266 267 protected: 268 union { 269 struct { 270 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ 271 FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0), 272 * 0x00010000u or 0x00020000u */ 273 } header; 274 TTCHeaderVersion1 version1; 275 } u; 276 }; 277 278 /* 279 * Mac Resource Fork 280 * 281 * http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html 282 */ 283 284 struct ResourceRecord 285 { get_faceOT::ResourceRecord286 const OpenTypeFontFace & get_face (const void *data_base) const 287 { return CastR<OpenTypeFontFace> ((data_base+offset).arrayZ); } 288 sanitizeOT::ResourceRecord289 bool sanitize (hb_sanitize_context_t *c, 290 const void *data_base) const 291 { 292 TRACE_SANITIZE (this); 293 return_trace (c->check_struct (this) && 294 offset.sanitize (c, data_base) && 295 get_face (data_base).sanitize (c)); 296 } 297 298 protected: 299 HBUINT16 id; /* Resource ID. */ 300 HBINT16 nameOffset; /* Offset from beginning of resource name list 301 * to resource name, -1 means there is none. */ 302 HBUINT8 attrs; /* Resource attributes */ 303 NNOffsetTo<LArrayOf<HBUINT8>, HBUINT24> 304 offset; /* Offset from beginning of data block to 305 * data for this resource */ 306 HBUINT32 reserved; /* Reserved for handle to resource */ 307 public: 308 DEFINE_SIZE_STATIC (12); 309 }; 310 311 #define HB_TAG_sfnt HB_TAG ('s','f','n','t') 312 313 struct ResourceTypeRecord 314 { get_resource_countOT::ResourceTypeRecord315 unsigned int get_resource_count () const 316 { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; } 317 is_sfntOT::ResourceTypeRecord318 bool is_sfnt () const { return tag == HB_TAG_sfnt; } 319 get_resource_recordOT::ResourceTypeRecord320 const ResourceRecord& get_resource_record (unsigned int i, 321 const void *type_base) const 322 { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; } 323 sanitizeOT::ResourceTypeRecord324 bool sanitize (hb_sanitize_context_t *c, 325 const void *type_base, 326 const void *data_base) const 327 { 328 TRACE_SANITIZE (this); 329 return_trace (c->check_struct (this) && 330 resourcesZ.sanitize (c, type_base, 331 get_resource_count (), 332 data_base)); 333 } 334 335 protected: 336 Tag tag; /* Resource type. */ 337 HBUINT16 resCountM1; /* Number of resources minus 1. */ 338 NNOffsetTo<UnsizedArrayOf<ResourceRecord>> 339 resourcesZ; /* Offset from beginning of resource type list 340 * to reference item list for this type. */ 341 public: 342 DEFINE_SIZE_STATIC (8); 343 }; 344 345 struct ResourceMap 346 { get_face_countOT::ResourceMap347 unsigned int get_face_count () const 348 { 349 unsigned int count = get_type_count (); 350 for (unsigned int i = 0; i < count; i++) 351 { 352 const ResourceTypeRecord& type = get_type_record (i); 353 if (type.is_sfnt ()) 354 return type.get_resource_count (); 355 } 356 return 0; 357 } 358 get_faceOT::ResourceMap359 const OpenTypeFontFace& get_face (unsigned int idx, 360 const void *data_base) 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 /* The check for idx < count is here because ResourceRecord is NOT null-safe. 367 * Because an offset of 0 there does NOT mean null. */ 368 if (type.is_sfnt () && idx < type.get_resource_count ()) 369 return type.get_resource_record (idx, &(this+typeList)).get_face (data_base); 370 } 371 return Null (OpenTypeFontFace); 372 } 373 sanitizeOT::ResourceMap374 bool sanitize (hb_sanitize_context_t *c, const void *data_base) const 375 { 376 TRACE_SANITIZE (this); 377 return_trace (c->check_struct (this) && 378 typeList.sanitize (c, this, 379 &(this+typeList), 380 data_base)); 381 } 382 383 private: get_type_countOT::ResourceMap384 unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; } 385 get_type_recordOT::ResourceMap386 const ResourceTypeRecord& get_type_record (unsigned int i) const 387 { return (this+typeList)[i]; } 388 389 protected: 390 HBUINT8 reserved0[16]; /* Reserved for copy of resource header */ 391 HBUINT32 reserved1; /* Reserved for handle to next resource map */ 392 HBUINT16 resreved2; /* Reserved for file reference number */ 393 HBUINT16 attrs; /* Resource fork attribute */ 394 NNOffsetTo<ArrayOfM1<ResourceTypeRecord>> 395 typeList; /* Offset from beginning of map to 396 * resource type list */ 397 Offset16 nameList; /* Offset from beginning of map to 398 * resource name list */ 399 public: 400 DEFINE_SIZE_STATIC (28); 401 }; 402 403 struct ResourceForkHeader 404 { get_face_countOT::ResourceForkHeader405 unsigned int get_face_count () const 406 { return (this+map).get_face_count (); } 407 get_faceOT::ResourceForkHeader408 const OpenTypeFontFace& get_face (unsigned int idx, 409 unsigned int *base_offset = nullptr) const 410 { 411 const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data)); 412 if (base_offset) 413 *base_offset = (const char *) &face - (const char *) this; 414 return face; 415 } 416 sanitizeOT::ResourceForkHeader417 bool sanitize (hb_sanitize_context_t *c) const 418 { 419 TRACE_SANITIZE (this); 420 return_trace (c->check_struct (this) && 421 data.sanitize (c, this, dataLen) && 422 map.sanitize (c, this, &(this+data))); 423 } 424 425 protected: 426 LNNOffsetTo<UnsizedArrayOf<HBUINT8>> 427 data; /* Offset from beginning of resource fork 428 * to resource data */ 429 LNNOffsetTo<ResourceMap > 430 map; /* Offset from beginning of resource fork 431 * to resource map */ 432 HBUINT32 dataLen; /* Length of resource data */ 433 HBUINT32 mapLen; /* Length of resource map */ 434 public: 435 DEFINE_SIZE_STATIC (16); 436 }; 437 438 /* 439 * OpenType Font File 440 */ 441 442 struct OpenTypeFontFile 443 { 444 enum { 445 CFFTag = HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */ 446 TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */ 447 TTCTag = HB_TAG ('t','t','c','f'), /* TrueType Collection */ 448 DFontTag = HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */ 449 TrueTag = HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */ 450 Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */ 451 }; 452 get_tagOT::OpenTypeFontFile453 hb_tag_t get_tag () const { return u.tag; } 454 get_face_countOT::OpenTypeFontFile455 unsigned int get_face_count () const 456 { 457 switch (u.tag) { 458 case CFFTag: /* All the non-collection tags */ 459 case TrueTag: 460 case Typ1Tag: 461 case TrueTypeTag: return 1; 462 case TTCTag: return u.ttcHeader.get_face_count (); 463 case DFontTag: return u.rfHeader.get_face_count (); 464 default: return 0; 465 } 466 } get_faceOT::OpenTypeFontFile467 const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const 468 { 469 if (base_offset) 470 *base_offset = 0; 471 switch (u.tag) { 472 /* Note: for non-collection SFNT data we ignore index. This is because 473 * Apple dfont container is a container of SFNT's. So each SFNT is a 474 * non-TTC, but the index is more than zero. */ 475 case CFFTag: /* All the non-collection tags */ 476 case TrueTag: 477 case Typ1Tag: 478 case TrueTypeTag: return u.fontFace; 479 case TTCTag: return u.ttcHeader.get_face (i); 480 case DFontTag: return u.rfHeader.get_face (i, base_offset); 481 default: return Null(OpenTypeFontFace); 482 } 483 } 484 485 template <typename item_t> serialize_singleOT::OpenTypeFontFile486 bool serialize_single (hb_serialize_context_t *c, 487 hb_tag_t sfnt_tag, 488 hb_array_t<item_t> items) 489 { 490 TRACE_SERIALIZE (this); 491 assert (sfnt_tag != TTCTag); 492 if (unlikely (!c->extend_min (*this))) return_trace (false); 493 return_trace (u.fontFace.serialize (c, sfnt_tag, items)); 494 } 495 sanitizeOT::OpenTypeFontFile496 bool sanitize (hb_sanitize_context_t *c) const 497 { 498 TRACE_SANITIZE (this); 499 if (unlikely (!u.tag.sanitize (c))) return_trace (false); 500 switch (u.tag) { 501 case CFFTag: /* All the non-collection tags */ 502 case TrueTag: 503 case Typ1Tag: 504 case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); 505 case TTCTag: return_trace (u.ttcHeader.sanitize (c)); 506 case DFontTag: return_trace (u.rfHeader.sanitize (c)); 507 default: return_trace (true); 508 } 509 } 510 511 protected: 512 union { 513 Tag tag; /* 4-byte identifier. */ 514 OpenTypeFontFace fontFace; 515 TTCHeader ttcHeader; 516 ResourceForkHeader rfHeader; 517 } u; 518 public: 519 DEFINE_SIZE_UNION (4, tag); 520 }; 521 522 523 } /* namespace OT */ 524 525 526 #endif /* HB_OPEN_FILE_HH */ 527