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_PRIVATE_HH
  30 #define HB_OPEN_FILE_PRIVATE_HH
  31 
  32 #include "hb-open-type-private.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
  58   { return -t.cmp (tag); }
  59 
  60   static int cmp (const void *pa, const void *pb)
  61   {
  62     const TableRecord *a = (const TableRecord *) pa;
  63     const TableRecord *b = (const TableRecord *) pb;
  64     return b->cmp (a->tag);
  65   }
  66 
  67   inline bool sanitize (hb_sanitize_context_t *c) const
  68   {
  69     TRACE_SANITIZE (this);
  70     return_trace (c->check_struct (this));
  71   }
  72 
  73   Tag           tag;            /* 4-byte identifier. */
  74   CheckSum      checkSum;       /* CheckSum for this table. */
  75   Offset32      offset;         /* Offset from beginning of TrueType font
  76                                  * file. */
  77   HBUINT32      length;         /* Length of this table. */
  78   public:
  79   DEFINE_SIZE_STATIC (16);
  80 } OpenTypeTable;
  81 
  82 typedef struct OffsetTable
  83 {
  84   friend struct OpenTypeFontFile;
  85 
  86   inline unsigned int get_table_count (void) const
  87   { return tables.len; }
  88   inline const TableRecord& get_table (unsigned int i) const
  89   {
  90     return tables[i];
  91   }
  92   inline unsigned int get_table_tags (unsigned int  start_offset,
  93                                       unsigned int *table_count, /* IN/OUT */
  94                                       hb_tag_t     *table_tags /* OUT */) const
  95   {
  96     if (table_count)
  97     {
  98       if (start_offset >= tables.len)
  99         *table_count = 0;
 100       else
 101         *table_count = MIN<unsigned int> (*table_count, tables.len - start_offset);
 102 
 103       const TableRecord *sub_tables = tables.arrayZ + start_offset;
 104       unsigned int count = *table_count;
 105       for (unsigned int i = 0; i < count; i++)
 106         table_tags[i] = sub_tables[i].tag;
 107     }
 108     return tables.len;
 109   }
 110   inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const
 111   {
 112     Tag t;
 113     t.set (tag);
 114     /* Linear-search for small tables to work around fonts with unsorted
 115      * table list. */
 116     int i = tables.len < 64 ? tables.lsearch (t) : tables.bsearch (t);
 117     if (table_index)
 118       *table_index = i == -1 ? Index::NOT_FOUND_INDEX : (unsigned int) i;
 119     return i != -1;
 120   }
 121   inline const TableRecord& get_table_by_tag (hb_tag_t tag) const
 122   {
 123     unsigned int table_index;
 124     find_table_index (tag, &table_index);
 125     return get_table (table_index);
 126   }
 127 
 128   public:
 129 
 130   inline bool serialize (hb_serialize_context_t *c,
 131                          hb_tag_t sfnt_tag,
 132                          Supplier<hb_tag_t> &tags,
 133                          Supplier<hb_blob_t *> &blobs,
 134                          unsigned int table_count)
 135   {
 136     TRACE_SERIALIZE (this);
 137     /* Alloc 12 for the OTHeader. */
 138     if (unlikely (!c->extend_min (*this))) return_trace (false);
 139     /* Write sfntVersion (bytes 0..3). */
 140     sfnt_version.set (sfnt_tag);
 141     /* Take space for numTables, searchRange, entrySelector, RangeShift
 142      * and the TableRecords themselves.  */
 143     if (unlikely (!tables.serialize (c, table_count))) return_trace (false);
 144 
 145     const char *dir_end = (const char *) c->head;
 146     HBUINT32 *checksum_adjustment = nullptr;
 147 
 148     /* Write OffsetTables, alloc for and write actual table blobs. */
 149     for (unsigned int i = 0; i < table_count; i++)
 150     {
 151       TableRecord &rec = tables.arrayZ[i];
 152       hb_blob_t *blob = blobs[i];
 153       rec.tag.set (tags[i]);
 154       rec.length.set (hb_blob_get_length (blob));
 155       rec.offset.serialize (c, this);
 156 
 157       /* Allocate room for the table and copy it. */
 158       char *start = (char *) c->allocate_size<void> (rec.length);
 159       if (unlikely (!start)) {return false;}
 160 
 161       memcpy (start, hb_blob_get_data (blob, nullptr), rec.length);
 162 
 163       /* 4-byte allignment. */
 164       if (rec.length % 4)
 165         c->allocate_size<void> (4 - rec.length % 4);
 166       const char *end = (const char *) c->head;
 167 
 168       if (tags[i] == HB_OT_TAG_head && end - start >= head::static_size)
 169       {
 170         head *h = (head *) start;
 171         checksum_adjustment = &h->checkSumAdjustment;
 172         checksum_adjustment->set (0);
 173       }
 174 
 175       rec.checkSum.set_for_data (start, end - start);
 176     }
 177     tags += table_count;
 178     blobs += table_count;
 179 
 180     tables.qsort ();
 181 
 182     if (checksum_adjustment)
 183     {
 184       CheckSum checksum;
 185 
 186       /* The following line is a slower version of the following block. */
 187       //checksum.set_for_data (this, (const char *) c->head - (const char *) this);
 188       checksum.set_for_data (this, dir_end - (const char *) this);
 189       for (unsigned int i = 0; i < table_count; i++)
 190       {
 191         TableRecord &rec = tables.arrayZ[i];
 192         checksum.set (checksum + rec.checkSum);
 193       }
 194 
 195       checksum_adjustment->set (0xB1B0AFBAu - checksum);
 196     }
 197 
 198     return_trace (true);
 199   }
 200 
 201   inline bool sanitize (hb_sanitize_context_t *c) const
 202   {
 203     TRACE_SANITIZE (this);
 204     return_trace (c->check_struct (this) && tables.sanitize (c));
 205   }
 206 
 207   protected:
 208   Tag           sfnt_version;   /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */
 209   BinSearchArrayOf<TableRecord>
 210                 tables;
 211   public:
 212   DEFINE_SIZE_ARRAY (12, tables);
 213 } OpenTypeFontFace;
 214 
 215 
 216 /*
 217  * TrueType Collections
 218  */
 219 
 220 struct TTCHeaderVersion1
 221 {
 222   friend struct TTCHeader;
 223 
 224   inline unsigned int get_face_count (void) const { return table.len; }
 225   inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
 226 
 227   inline bool sanitize (hb_sanitize_context_t *c) const
 228   {
 229     TRACE_SANITIZE (this);
 230     return_trace (table.sanitize (c, this));
 231   }
 232 
 233   protected:
 234   Tag           ttcTag;         /* TrueType Collection ID string: 'ttcf' */
 235   FixedVersion<>version;        /* Version of the TTC Header (1.0),
 236                                  * 0x00010000u */
 237   LArrayOf<LOffsetTo<OffsetTable> >
 238                 table;          /* Array of offsets to the OffsetTable for each font
 239                                  * from the beginning of the file */
 240   public:
 241   DEFINE_SIZE_ARRAY (12, table);
 242 };
 243 
 244 struct TTCHeader
 245 {
 246   friend struct OpenTypeFontFile;
 247 
 248   private:
 249 
 250   inline unsigned int get_face_count (void) const
 251   {
 252     switch (u.header.version.major) {
 253     case 2: /* version 2 is compatible with version 1 */
 254     case 1: return u.version1.get_face_count ();
 255     default:return 0;
 256     }
 257   }
 258   inline const OpenTypeFontFace& get_face (unsigned int i) const
 259   {
 260     switch (u.header.version.major) {
 261     case 2: /* version 2 is compatible with version 1 */
 262     case 1: return u.version1.get_face (i);
 263     default:return Null(OpenTypeFontFace);
 264     }
 265   }
 266 
 267   inline bool sanitize (hb_sanitize_context_t *c) const
 268   {
 269     TRACE_SANITIZE (this);
 270     if (unlikely (!u.header.version.sanitize (c))) return_trace (false);
 271     switch (u.header.version.major) {
 272     case 2: /* version 2 is compatible with version 1 */
 273     case 1: 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 
 293 struct ResourceRefItem
 294 {
 295   inline bool sanitize (hb_sanitize_context_t *c) const
 296   {
 297     TRACE_SANITIZE (this);
 298     // actual data sanitization is done on ResourceForkHeader sanitizer
 299     return_trace (likely (c->check_struct (this)));
 300   }
 301 
 302   HBINT16       id;             /* Resource ID, is really should be signed? */
 303   HBINT16       nameOffset;     /* Offset from beginning of resource name list
 304                                  * to resource name, minus means there is no */
 305   HBUINT8       attr;           /* Resource attributes */
 306   HBUINT24      dataOffset;     /* Offset from beginning of resource data to
 307                                  * data for this resource */
 308   HBUINT32      reserved;       /* Reserved for handle to resource */
 309   public:
 310   DEFINE_SIZE_STATIC (12);
 311 };
 312 
 313 struct ResourceTypeItem
 314 {
 315   inline bool sanitize (hb_sanitize_context_t *c) const
 316   {
 317     TRACE_SANITIZE (this);
 318     // RefList sanitization is done on ResourceMap sanitizer
 319     return_trace (likely (c->check_struct (this)));
 320   }
 321 
 322   inline unsigned int get_resource_count () const
 323   {
 324     return numRes + 1;
 325   }
 326 
 327   inline bool is_sfnt () const
 328   {
 329     return type == HB_TAG ('s','f','n','t');
 330   }
 331 
 332   inline const ResourceRefItem& get_ref_item (const void *base,
 333                                               unsigned int i) const
 334   {
 335     return (base+refList)[i];
 336   }
 337 
 338   protected:
 339   Tag           type;           /* Resource type */
 340   HBUINT16      numRes;         /* Number of resource this type in map minus 1 */
 341   OffsetTo<UnsizedArrayOf<ResourceRefItem> >
 342                 refList;        /* Offset from beginning of resource type list
 343                                  * to reference list for this type */
 344   public:
 345   DEFINE_SIZE_STATIC (8);
 346 };
 347 
 348 struct ResourceMap
 349 {
 350   inline bool sanitize (hb_sanitize_context_t *c) const
 351   {
 352     TRACE_SANITIZE (this);
 353     if (unlikely (!c->check_struct (this)))
 354       return_trace (false);
 355     for (unsigned int i = 0; i < get_types_count (); ++i)
 356     {
 357       const ResourceTypeItem& type = get_type (i);
 358       if (unlikely (!type.sanitize (c)))
 359         return_trace (false);
 360       for (unsigned int j = 0; j < type.get_resource_count (); ++j)
 361         if (unlikely (!get_ref_item (type, j).sanitize (c)))
 362           return_trace (false);
 363     }
 364     return_trace (true);
 365   }
 366 
 367   inline const ResourceTypeItem& get_type (unsigned int i) const
 368   {
 369     // Why offset from the second byte of the object? I'm not sure
 370     return ((&reserved[2])+typeList)[i];
 371   }
 372 
 373   inline unsigned int get_types_count () const
 374   {
 375     return nTypes + 1;
 376   }
 377 
 378   inline const ResourceRefItem &get_ref_item (const ResourceTypeItem &type,
 379                                               unsigned int i) const
 380   {
 381     return type.get_ref_item (&(this+typeList), i);
 382   }
 383 
 384   inline const PString& get_name (const ResourceRefItem &item,
 385                                   unsigned int i) const
 386   {
 387     if (item.nameOffset == -1)
 388       return Null (PString);
 389 
 390     return StructAtOffset<PString> (this, nameList + item.nameOffset);
 391   }
 392 
 393   protected:
 394   HBUINT8       reserved[16];   /* Reserved for copy of resource header */
 395   LOffsetTo<ResourceMap>
 396                 reserved1;      /* Reserved for handle to next resource map */
 397   HBUINT16      reserved2;      /* Reserved for file reference number */
 398   HBUINT16      attr;           /* Resource fork attribute */
 399   OffsetTo<UnsizedArrayOf<ResourceTypeItem> >
 400                 typeList;       /* Offset from beginning of map to
 401                                  * resource type list */
 402   HBUINT16      nameList;       /* Offset from beginning of map to
 403                                  * resource name list */
 404   HBUINT16      nTypes;         /* Number of types in the map minus 1 */
 405   public:
 406   DEFINE_SIZE_STATIC (30);
 407 };
 408 
 409 struct ResourceForkHeader
 410 {
 411   inline unsigned int get_face_count () const
 412   {
 413     const ResourceMap &resource_map = this+map;
 414     for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
 415     {
 416       const ResourceTypeItem& type = resource_map.get_type (i);
 417       if (type.is_sfnt ())
 418         return type.get_resource_count ();
 419     }
 420     return 0;
 421   }
 422 
 423   inline const LArrayOf<HBUINT8>& get_data (const ResourceTypeItem& type,
 424                                             unsigned int idx) const
 425   {
 426     const ResourceMap &resource_map = this+map;
 427     unsigned int offset = dataOffset;
 428     offset += resource_map.get_ref_item (type, idx).dataOffset;
 429     return StructAtOffset<LArrayOf<HBUINT8> > (this, offset);
 430   }
 431 
 432   inline const OpenTypeFontFace& get_face (unsigned int idx) const
 433   {
 434     const ResourceMap &resource_map = this+map;
 435     for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
 436     {
 437       const ResourceTypeItem& type = resource_map.get_type (i);
 438       if (type.is_sfnt () && idx < type.get_resource_count ())
 439         return (OpenTypeFontFace&) get_data (type, idx).arrayZ;
 440     }
 441     return Null (OpenTypeFontFace);
 442   }
 443 
 444   inline bool sanitize (hb_sanitize_context_t *c) const
 445   {
 446     TRACE_SANITIZE (this);
 447     if (unlikely (!c->check_struct (this)))
 448       return_trace (false);
 449 
 450     const ResourceMap &resource_map = this+map;
 451     if (unlikely (!resource_map.sanitize (c)))
 452       return_trace (false);
 453 
 454     for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
 455     {
 456       const ResourceTypeItem& type = resource_map.get_type (i);
 457       for (unsigned int j = 0; j < type.get_resource_count (); ++j)
 458       {
 459         const LArrayOf<HBUINT8>& data = get_data (type, j);
 460         if (unlikely (!(data.sanitize (c) &&
 461                         ((OpenTypeFontFace&) data.arrayZ).sanitize (c))))
 462           return_trace (false);
 463       }
 464     }
 465 
 466     return_trace (true);
 467   }
 468 
 469   protected:
 470   HBUINT32      dataOffset;     /* Offset from beginning of resource fork
 471                                  * to resource data */
 472   LOffsetTo<ResourceMap>
 473                 map;            /* Offset from beginning of resource fork
 474                                  * to resource map */
 475   HBUINT32      dataLen;        /* Length of resource data */
 476   HBUINT32      mapLen;         /* Length of resource map */
 477   public:
 478   DEFINE_SIZE_STATIC (16);
 479 };
 480 
 481 /*
 482  * OpenType Font File
 483  */
 484 
 485 struct OpenTypeFontFile
 486 {
 487   static const hb_tag_t tableTag        = HB_TAG ('_','_','_','_'); /* Sanitizer needs this. */
 488 
 489   enum {
 490     CFFTag              = HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */
 491     TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */
 492     TTCTag              = HB_TAG ('t','t','c','f'), /* TrueType Collection */
 493     DFontTag            = HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */
 494     TrueTag             = HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */
 495     Typ1Tag             = HB_TAG ('t','y','p','1')  /* Obsolete Apple Type1 font in SFNT container */
 496   };
 497 
 498   inline hb_tag_t get_tag (void) const { return u.tag; }
 499 
 500   inline unsigned int get_face_count (void) const
 501   {
 502     switch (u.tag) {
 503     case CFFTag:        /* All the non-collection tags */
 504     case TrueTag:
 505     case Typ1Tag:
 506     case TrueTypeTag:   return 1;
 507     case TTCTag:        return u.ttcHeader.get_face_count ();
 508 //    case DFontTag:    return u.rfHeader.get_face_count ();
 509     default:            return 0;
 510     }
 511   }
 512   inline const OpenTypeFontFace& get_face (unsigned int i) const
 513   {
 514     switch (u.tag) {
 515     /* Note: for non-collection SFNT data we ignore index.  This is because
 516      * Apple dfont container is a container of SFNT's.  So each SFNT is a
 517      * non-TTC, but the index is more than zero. */
 518     case CFFTag:        /* All the non-collection tags */
 519     case TrueTag:
 520     case Typ1Tag:
 521     case TrueTypeTag:   return u.fontFace;
 522     case TTCTag:        return u.ttcHeader.get_face (i);
 523 //    case DFontTag:    return u.rfHeader.get_face (i);
 524     default:            return Null(OpenTypeFontFace);
 525     }
 526   }
 527 
 528   inline bool serialize_single (hb_serialize_context_t *c,
 529                                 hb_tag_t sfnt_tag,
 530                                 Supplier<hb_tag_t> &tags,
 531                                 Supplier<hb_blob_t *> &blobs,
 532                                 unsigned int table_count)
 533   {
 534     TRACE_SERIALIZE (this);
 535     assert (sfnt_tag != TTCTag);
 536     if (unlikely (!c->extend_min (*this))) return_trace (false);
 537     return_trace (u.fontFace.serialize (c, sfnt_tag, tags, blobs, table_count));
 538   }
 539 
 540   inline bool sanitize (hb_sanitize_context_t *c) const
 541   {
 542     TRACE_SANITIZE (this);
 543     if (unlikely (!u.tag.sanitize (c))) return_trace (false);
 544     switch (u.tag) {
 545     case CFFTag:        /* All the non-collection tags */
 546     case TrueTag:
 547     case Typ1Tag:
 548     case TrueTypeTag:   return_trace (u.fontFace.sanitize (c));
 549     case TTCTag:        return_trace (u.ttcHeader.sanitize (c));
 550 //    case DFontTag:    return_trace (u.rfHeader.sanitize (c));
 551     default:            return_trace (true);
 552     }
 553   }
 554 
 555   protected:
 556   union {
 557   Tag                   tag;            /* 4-byte identifier. */
 558   OpenTypeFontFace      fontFace;
 559   TTCHeader             ttcHeader;
 560   ResourceForkHeader    rfHeader;
 561   } u;
 562   public:
 563   DEFINE_SIZE_UNION (4, tag);
 564 };
 565 
 566 
 567 } /* namespace OT */
 568 
 569 
 570 #endif /* HB_OPEN_FILE_PRIVATE_HH */