Coverage Report

Created: 2025-07-11 06:34

/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
0
  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
0
  { 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
0
  {
102
0
    Tag t;
103
0
    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
0
    if (tables.len < 16)
108
0
      return tables.lfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
109
0
    else
110
0
      return tables.bfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
111
0
  }
112
  const TableRecord& get_table_by_tag (hb_tag_t tag) const
113
0
  {
114
0
    unsigned int table_index;
115
0
    find_table_index (tag, &table_index);
116
0
    return get_table (table_index);
117
0
  }
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
0
  {
127
0
    TRACE_SERIALIZE (this);
128
    /* Alloc 12 for the OTHeader. */
129
0
    if (unlikely (!c->extend_min (this))) return_trace (false);
130
    /* Write sfntVersion (bytes 0..3). */
131
0
    sfnt_version = sfnt_tag;
132
    /* Take space for numTables, searchRange, entrySelector, RangeShift
133
     * and the TableRecords themselves.  */
134
0
    unsigned num_items = hb_len (it);
135
0
    if (unlikely (!tables.serialize (c, num_items))) return_trace (false);
136
137
0
    const char *dir_end = (const char *) c->head;
138
0
    HBUINT32 *checksum_adjustment = nullptr;
139
140
    /* Write OffsetTables, alloc for and write actual table blobs. */
141
0
    unsigned i = 0;
142
0
    for (hb_pair_t<hb_tag_t, hb_blob_t*> entry : it)
143
0
    {
144
0
      hb_blob_t *blob = entry.second;
145
0
      unsigned len = blob->length;
146
147
      /* Allocate room for the table and copy it. */
148
0
      char *start = (char *) c->allocate_size<void> (len, false);
149
0
      if (unlikely (!start)) return false;
150
151
0
      TableRecord &rec = tables.arrayZ[i];
152
0
      rec.tag = entry.first;
153
0
      rec.length = len;
154
0
      rec.offset = 0;
155
0
      if (unlikely (!c->check_assign (rec.offset,
156
0
              (unsigned) ((char *) start - (char *) this),
157
0
              HB_SERIALIZE_ERROR_OFFSET_OVERFLOW)))
158
0
        return_trace (false);
159
160
0
      if (likely (len))
161
0
  hb_memcpy (start, blob->data, len);
162
163
      /* 4-byte alignment. */
164
0
      c->align (4);
165
0
      const char *end = (const char *) c->head;
166
167
0
      if (entry.first == HB_OT_TAG_head &&
168
0
    (unsigned) (end - start) >= head::static_size)
169
0
      {
170
0
  head *h = (head *) start;
171
0
  checksum_adjustment = &h->checkSumAdjustment;
172
0
  *checksum_adjustment = 0;
173
0
      }
174
175
0
      rec.checkSum.set_for_data (start, end - start);
176
0
      i++;
177
0
    }
178
179
0
    tables.qsort ();
180
181
0
    if (checksum_adjustment)
182
0
    {
183
0
      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
0
      checksum.set_for_data (this, dir_end - (const char *) this);
188
0
      for (unsigned int i = 0; i < num_items; i++)
189
0
      {
190
0
  TableRecord &rec = tables.arrayZ[i];
191
0
  checksum = checksum + rec.checkSum;
192
0
      }
193
194
0
      *checksum_adjustment = 0xB1B0AFBAu - checksum;
195
0
    }
196
197
0
    return_trace (true);
198
0
  }
199
200
  bool sanitize (hb_sanitize_context_t *c) const
201
0
  {
202
0
    TRACE_SANITIZE (this);
203
0
    return_trace (c->check_struct (this) && tables.sanitize (c));
204
0
  }
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
0
  const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
225
226
  bool sanitize (hb_sanitize_context_t *c) const
227
0
  {
228
0
    TRACE_SANITIZE (this);
229
0
    return_trace (table.sanitize (c, this));
230
0
  }
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: hb_barrier (); 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
0
  {
259
0
    switch (u.header.version.major) {
260
0
    case 2: /* version 2 is compatible with version 1 */
261
0
    case 1: hb_barrier (); return u.version1.get_face (i);
262
0
    default:return Null (OpenTypeFontFace);
263
0
    }
264
0
  }
265
266
  bool sanitize (hb_sanitize_context_t *c) const
267
0
  {
268
0
    TRACE_SANITIZE (this);
269
0
    if (unlikely (!u.header.version.sanitize (c))) return_trace (false);
270
0
    hb_barrier ();
271
0
    switch (u.header.version.major) {
272
0
    case 2: /* version 2 is compatible with version 1 */
273
0
    case 1: hb_barrier (); return_trace (u.version1.sanitize (c));
274
0
    default:return_trace (true);
275
0
    }
276
0
  }
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
{
297
  const OpenTypeFontFace & get_face (const void *data_base) const
298
0
  { return * reinterpret_cast<const OpenTypeFontFace *> ((data_base+offset).arrayZ); }
299
300
  bool sanitize (hb_sanitize_context_t *c,
301
     const void *data_base) const
302
0
  {
303
0
    TRACE_SANITIZE (this);
304
0
    return_trace (c->check_struct (this) &&
305
0
      offset.sanitize (c, data_base) &&
306
0
      hb_barrier () &&
307
0
      get_face (data_base).sanitize (c));
308
0
  }
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
0
#define HB_TAG_sfnt HB_TAG ('s','f','n','t')
324
325
struct ResourceTypeRecord
326
{
327
  unsigned int get_resource_count () const
328
0
  { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; }
329
330
0
  bool is_sfnt () const { return tag == HB_TAG_sfnt; }
331
332
  const ResourceRecord& get_resource_record (unsigned int i,
333
               const void *type_base) const
334
0
  { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; }
335
336
  bool sanitize (hb_sanitize_context_t *c,
337
     const void *type_base,
338
     const void *data_base) const
339
0
  {
340
0
    TRACE_SANITIZE (this);
341
0
    return_trace (c->check_struct (this) &&
342
0
      hb_barrier () &&
343
0
      resourcesZ.sanitize (c, type_base,
344
0
               get_resource_count (),
345
0
               data_base));
346
0
  }
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
{
360
  unsigned int get_face_count () const
361
0
  {
362
0
    unsigned int count = get_type_count ();
363
0
    for (unsigned int i = 0; i < count; i++)
364
0
    {
365
0
      const ResourceTypeRecord& type = get_type_record (i);
366
0
      if (type.is_sfnt ())
367
0
  return type.get_resource_count ();
368
0
    }
369
0
    return 0;
370
0
  }
371
372
  const OpenTypeFontFace& get_face (unsigned int idx,
373
            const void *data_base) const
374
0
  {
375
0
    unsigned int count = get_type_count ();
376
0
    for (unsigned int i = 0; i < count; i++)
377
0
    {
378
0
      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
0
      if (type.is_sfnt () && idx < type.get_resource_count ())
382
0
  return type.get_resource_record (idx, &(this+typeList)).get_face (data_base);
383
0
    }
384
0
    return Null (OpenTypeFontFace);
385
0
  }
386
387
  bool sanitize (hb_sanitize_context_t *c, const void *data_base) const
388
0
  {
389
0
    TRACE_SANITIZE (this);
390
0
    return_trace (c->check_struct (this) &&
391
0
      hb_barrier () &&
392
0
      typeList.sanitize (c, this,
393
0
             &(this+typeList),
394
0
             data_base));
395
0
  }
396
397
  private:
398
0
  unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; }
399
400
  const ResourceTypeRecord& get_type_record (unsigned int i) const
401
0
  { 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
{
419
  unsigned int get_face_count () const
420
0
  { return (this+map).get_face_count (); }
421
422
  const OpenTypeFontFace& get_face (unsigned int idx,
423
            unsigned int *base_offset = nullptr) const
424
0
  {
425
0
    const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data));
426
0
    if (base_offset)
427
0
      *base_offset = (const char *) &face - (const char *) this;
428
0
    return face;
429
0
  }
430
431
  bool sanitize (hb_sanitize_context_t *c) const
432
0
  {
433
0
    TRACE_SANITIZE (this);
434
0
    return_trace (c->check_struct (this) &&
435
0
      hb_barrier () &&
436
0
      data.sanitize (c, this, dataLen) &&
437
0
      map.sanitize (c, this, &(this+data)));
438
0
  }
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
468
0
  hb_tag_t get_tag () const { return u.tag; }
469
470
  unsigned int get_face_count () const
471
0
  {
472
0
    switch (u.tag) {
473
0
    case CFFTag:  /* All the non-collection tags */
474
0
    case TrueTag:
475
0
    case Typ1Tag:
476
0
    case TrueTypeTag: return 1;
477
0
    case TTCTag:  return u.ttcHeader.get_face_count ();
478
0
    case DFontTag:  return u.rfHeader.get_face_count ();
479
0
    default:    return 0;
480
0
    }
481
0
  }
482
  const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const
483
0
  {
484
0
    if (base_offset)
485
0
      *base_offset = 0;
486
0
    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
0
    case CFFTag:  /* All the non-collection tags */
491
0
    case TrueTag:
492
0
    case Typ1Tag:
493
0
    case TrueTypeTag: return u.fontFace;
494
0
    case TTCTag:  return u.ttcHeader.get_face (i);
495
0
    case DFontTag:  return u.rfHeader.get_face (i, base_offset);
496
0
    default:    return Null (OpenTypeFontFace);
497
0
    }
498
0
  }
499
500
  template <typename Iterator,
501
      hb_requires ((hb_is_source_of<Iterator, hb_pair_t<hb_tag_t, hb_blob_t *>>::value))>
502
  bool serialize_single (hb_serialize_context_t *c,
503
       hb_tag_t sfnt_tag,
504
       Iterator items)
505
0
  {
506
0
    TRACE_SERIALIZE (this);
507
0
    assert (sfnt_tag != TTCTag);
508
0
    if (unlikely (!c->extend_min (this))) return_trace (false);
509
0
    return_trace (u.fontFace.serialize (c, sfnt_tag, items));
510
0
  }
511
512
  bool sanitize (hb_sanitize_context_t *c) const
513
0
  {
514
0
    TRACE_SANITIZE (this);
515
0
    if (unlikely (!u.tag.sanitize (c))) return_trace (false);
516
0
    hb_barrier ();
517
0
    switch (u.tag) {
518
0
    case CFFTag:  /* All the non-collection tags */
519
0
    case TrueTag:
520
0
    case Typ1Tag:
521
0
    case TrueTypeTag: return_trace (u.fontFace.sanitize (c));
522
0
    case TTCTag:  return_trace (u.ttcHeader.sanitize (c));
523
0
    case DFontTag:  return_trace (u.rfHeader.sanitize (c));
524
0
    default:    return_trace (true);
525
0
    }
526
0
  }
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 */