Coverage Report

Created: 2024-06-06 05:24

/src/harfbuzz/src/hb-open-file.hh
Line
Count
Source (jump to first uncovered line)
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
{
56
142M
  int cmp (Tag t) const { return -t.cmp (tag); }
57
58
  HB_INTERNAL static int cmp (const void *pa, const void *pb)
59
0
  {
60
0
    const TableRecord *a = (const TableRecord *) pa;
61
0
    const TableRecord *b = (const TableRecord *) pb;
62
0
    return b->cmp (a->tag);
63
0
  }
64
65
  bool sanitize (hb_sanitize_context_t *c) const
66
0
  {
67
0
    TRACE_SANITIZE (this);
68
0
    return_trace (c->check_struct (this));
69
0
  }
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
84
0
  unsigned int get_table_count () const { return tables.len; }
85
  const TableRecord& get_table (unsigned int i) const
86
18.3M
  { return tables[i]; }
87
  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
0
  {
91
0
    if (table_count)
92
0
    {
93
0
      + tables.as_array ().sub_array (start_offset, table_count)
94
0
      | hb_map (&TableRecord::tag)
95
0
      | hb_sink (hb_array (table_tags, *table_count))
96
0
      ;
97
0
    }
98
0
    return tables.len;
99
0
  }
100
  bool find_table_index (hb_tag_t tag, unsigned int *table_index) const
101
18.3M
  {
102
18.3M
    Tag t;
103
18.3M
    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
18.3M
    if (tables.len < 16)
108
15.4M
      return tables.lfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
109
2.85M
    else
110
2.85M
      return tables.bfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
111
18.3M
  }
112
  const TableRecord& get_table_by_tag (hb_tag_t tag) const
113
18.3M
  {
114
18.3M
    unsigned int table_index;
115
18.3M
    find_table_index (tag, &table_index);
116
18.3M
    return get_table (table_index);
117
18.3M
  }
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))>
123
  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
200
  bool sanitize (hb_sanitize_context_t *c) const
201
488k
  {
202
488k
    TRACE_SANITIZE (this);
203
488k
    return_trace (c->check_struct (this) && tables.sanitize (c));
204
488k
  }
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
223
0
  unsigned int get_face_count () const { return table.len; }
224
19.8k
  const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
225
226
  bool sanitize (hb_sanitize_context_t *c) const
227
2.00k
  {
228
2.00k
    TRACE_SANITIZE (this);
229
2.00k
    return_trace (table.sanitize (c, this));
230
2.00k
  }
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
249
  unsigned int get_face_count () const
250
0
  {
251
0
    switch (u.header.version.major) {
252
0
    case 2: /* version 2 is compatible with version 1 */
253
0
    case 1: return u.version1.get_face_count ();
254
0
    default:return 0;
255
0
    }
256
0
  }
257
  const OpenTypeFontFace& get_face (unsigned int i) const
258
21.6k
  {
259
21.6k
    switch (u.header.version.major) {
260
1
    case 2: /* version 2 is compatible with version 1 */
261
19.8k
    case 1: return u.version1.get_face (i);
262
1.75k
    default:return Null (OpenTypeFontFace);
263
21.6k
    }
264
21.6k
  }
265
266
  bool sanitize (hb_sanitize_context_t *c) const
267
2.07k
  {
268
2.07k
    TRACE_SANITIZE (this);
269
2.07k
    if (unlikely (!u.header.version.sanitize (c))) return_trace (false);
270
2.07k
    switch (u.header.version.major) {
271
4
    case 2: /* version 2 is compatible with version 1 */
272
2.00k
    case 1: return_trace (u.version1.sanitize (c));
273
75
    default:return_trace (true);
274
2.07k
    }
275
2.07k
  }
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
{
296
  const OpenTypeFontFace & get_face (const void *data_base) const
297
28.7k
  { return * reinterpret_cast<const OpenTypeFontFace *> ((data_base+offset).arrayZ); }
298
299
  bool sanitize (hb_sanitize_context_t *c,
300
     const void *data_base) const
301
22.0k
  {
302
22.0k
    TRACE_SANITIZE (this);
303
22.0k
    return_trace (c->check_struct (this) &&
304
22.0k
      offset.sanitize (c, data_base) &&
305
22.0k
      get_face (data_base).sanitize (c));
306
22.0k
  }
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
11.3M
#define HB_TAG_sfnt HB_TAG ('s','f','n','t')
322
323
struct ResourceTypeRecord
324
{
325
  unsigned int get_resource_count () const
326
348k
  { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; }
327
328
11.0M
  bool is_sfnt () const { return tag == HB_TAG_sfnt; }
329
330
  const ResourceRecord& get_resource_record (unsigned int i,
331
               const void *type_base) const
332
7.08k
  { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; }
333
334
  bool sanitize (hb_sanitize_context_t *c,
335
     const void *type_base,
336
     const void *data_base) const
337
334k
  {
338
334k
    TRACE_SANITIZE (this);
339
334k
    return_trace (c->check_struct (this) &&
340
334k
      resourcesZ.sanitize (c, type_base,
341
334k
               get_resource_count (),
342
334k
               data_base));
343
334k
  }
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
{
357
  unsigned int get_face_count () const
358
0
  {
359
0
    unsigned int count = get_type_count ();
360
0
    for (unsigned int i = 0; i < count; i++)
361
0
    {
362
0
      const ResourceTypeRecord& type = get_type_record (i);
363
0
      if (type.is_sfnt ())
364
0
  return type.get_resource_count ();
365
0
    }
366
0
    return 0;
367
0
  }
368
369
  const OpenTypeFontFace& get_face (unsigned int idx,
370
            const void *data_base) const
371
23.2k
  {
372
23.2k
    unsigned int count = get_type_count ();
373
11.0M
    for (unsigned int i = 0; i < count; i++)
374
11.0M
    {
375
11.0M
      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
11.0M
      if (type.is_sfnt () && idx < type.get_resource_count ())
379
7.08k
  return type.get_resource_record (idx, &(this+typeList)).get_face (data_base);
380
11.0M
    }
381
16.1k
    return Null (OpenTypeFontFace);
382
23.2k
  }
383
384
  bool sanitize (hb_sanitize_context_t *c, const void *data_base) const
385
1.34k
  {
386
1.34k
    TRACE_SANITIZE (this);
387
1.34k
    return_trace (c->check_struct (this) &&
388
1.34k
      typeList.sanitize (c, this,
389
1.34k
             &(this+typeList),
390
1.34k
             data_base));
391
1.34k
  }
392
393
  private:
394
23.2k
  unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; }
395
396
  const ResourceTypeRecord& get_type_record (unsigned int i) const
397
11.0M
  { 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
{
415
  unsigned int get_face_count () const
416
0
  { return (this+map).get_face_count (); }
417
418
  const OpenTypeFontFace& get_face (unsigned int idx,
419
            unsigned int *base_offset = nullptr) const
420
23.2k
  {
421
23.2k
    const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data));
422
23.2k
    if (base_offset)
423
23.2k
      *base_offset = (const char *) &face - (const char *) this;
424
23.2k
    return face;
425
23.2k
  }
426
427
  bool sanitize (hb_sanitize_context_t *c) const
428
1.53k
  {
429
1.53k
    TRACE_SANITIZE (this);
430
1.53k
    return_trace (c->check_struct (this) &&
431
1.53k
      data.sanitize (c, this, dataLen) &&
432
1.53k
      map.sanitize (c, this, &(this+data)));
433
1.53k
  }
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
463
0
  hb_tag_t get_tag () const { return u.tag; }
464
465
  unsigned int get_face_count () const
466
0
  {
467
0
    switch (u.tag) {
468
0
    case CFFTag:  /* All the non-collection tags */
469
0
    case TrueTag:
470
0
    case Typ1Tag:
471
0
    case TrueTypeTag: return 1;
472
0
    case TTCTag:  return u.ttcHeader.get_face_count ();
473
0
    case DFontTag:  return u.rfHeader.get_face_count ();
474
0
    default:    return 0;
475
0
    }
476
0
  }
477
  const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const
478
18.3M
  {
479
18.3M
    if (base_offset)
480
18.3M
      *base_offset = 0;
481
18.3M
    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
4.39M
    case CFFTag:  /* All the non-collection tags */
486
4.45M
    case TrueTag:
487
4.45M
    case Typ1Tag:
488
16.9M
    case TrueTypeTag: return u.fontFace;
489
21.6k
    case TTCTag:  return u.ttcHeader.get_face (i);
490
23.2k
    case DFontTag:  return u.rfHeader.get_face (i, base_offset);
491
1.30M
    default:    return Null (OpenTypeFontFace);
492
18.3M
    }
493
18.3M
  }
494
495
  template <typename Iterator,
496
      hb_requires ((hb_is_source_of<Iterator, hb_pair_t<hb_tag_t, hb_blob_t *>>::value))>
497
  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
507
  bool sanitize (hb_sanitize_context_t *c) const
508
482k
  {
509
482k
    TRACE_SANITIZE (this);
510
482k
    if (unlikely (!u.tag.sanitize (c))) return_trace (false);
511
482k
    switch (u.tag) {
512
115k
    case CFFTag:  /* All the non-collection tags */
513
117k
    case TrueTag:
514
117k
    case Typ1Tag:
515
447k
    case TrueTypeTag: return_trace (u.fontFace.sanitize (c));
516
2.07k
    case TTCTag:  return_trace (u.ttcHeader.sanitize (c));
517
1.53k
    case DFontTag:  return_trace (u.rfHeader.sanitize (c));
518
30.5k
    default:    return_trace (true);
519
482k
    }
520
482k
  }
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 */