Coverage Report

Created: 2023-05-11 17:16

/src/harfbuzz/src/hb-ot-name-table.hh
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright © 2011,2012  Google, Inc.
3
 *
4
 *  This is part of HarfBuzz, a text shaping library.
5
 *
6
 * Permission is hereby granted, without written agreement and without
7
 * license or royalty fees, to use, copy, modify, and distribute this
8
 * software and its documentation for any purpose, provided that the
9
 * above copyright notice and the following two paragraphs appear in
10
 * all copies of this software.
11
 *
12
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16
 * DAMAGE.
17
 *
18
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23
 *
24
 * Google Author(s): Behdad Esfahbod
25
 */
26
27
#ifndef HB_OT_NAME_TABLE_HH
28
#define HB_OT_NAME_TABLE_HH
29
30
#include "hb-open-type.hh"
31
#include "hb-ot-name-language.hh"
32
#include "hb-aat-layout.hh"
33
34
35
namespace OT {
36
37
38
201M
#define entry_score var.u16[0]
39
389M
#define entry_index var.u16[1]
40
41
42
/*
43
 * name -- Naming
44
 * https://docs.microsoft.com/en-us/typography/opentype/spec/name
45
 */
46
#define HB_OT_TAG_name HB_TAG('n','a','m','e')
47
48
6.34M
#define UNSUPPORTED 42
49
50
struct NameRecord
51
{
52
  hb_language_t language (hb_face_t *face) const
53
3.12M
  {
54
3.12M
#ifndef HB_NO_OT_NAME_LANGUAGE
55
3.12M
    unsigned int p = platformID;
56
3.12M
    unsigned int l = languageID;
57
58
3.12M
    if (p == 3)
59
1.46M
      return _hb_ot_name_language_for_ms_code (l);
60
61
1.65M
    if (p == 1)
62
403k
      return _hb_ot_name_language_for_mac_code (l);
63
64
1.25M
#ifndef HB_NO_OT_NAME_LANGUAGE_AAT
65
1.25M
    if (p == 0)
66
1.15M
      return face->table.ltag->get_language (l);
67
101k
#endif
68
69
101k
#endif
70
101k
    return HB_LANGUAGE_INVALID;
71
1.25M
  }
72
73
  uint16_t score () const
74
3.12M
  {
75
    /* Same order as in cmap::find_best_subtable(). */
76
3.12M
    unsigned int p = platformID;
77
3.12M
    unsigned int e = encodingID;
78
79
    /* 32-bit. */
80
3.12M
    if (p == 3 && e == 10) return 0;
81
3.12M
    if (p == 0 && e ==  6) return 1;
82
3.12M
    if (p == 0 && e ==  4) return 2;
83
84
    /* 16-bit. */
85
3.12M
    if (p == 3 && e ==  1) return 3;
86
1.66M
    if (p == 0 && e ==  3) return 4;
87
1.66M
    if (p == 0 && e ==  2) return 5;
88
1.66M
    if (p == 0 && e ==  1) return 6;
89
1.65M
    if (p == 0 && e ==  0) return 7;
90
91
    /* Symbol. */
92
515k
    if (p == 3 && e ==  0) return 8;
93
94
    /* We treat all Mac Latin names as ASCII only. */
95
514k
    if (p == 1 && e ==  0) return 10; /* 10 is magic number :| */
96
97
120k
    return UNSUPPORTED;
98
514k
  }
99
100
  NameRecord* copy (hb_serialize_context_t *c, const void *base) const
101
0
  {
102
0
    TRACE_SERIALIZE (this);
103
0
    auto *out = c->embed (this);
104
0
    if (unlikely (!out)) return_trace (nullptr);
105
0
    out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length);
106
0
    return_trace (out);
107
0
  }
108
109
  bool isUnicode () const
110
0
  {
111
0
    unsigned int p = platformID;
112
0
    unsigned int e = encodingID;
113
0
114
0
    return (p == 0 ||
115
0
      (p == 3 && (e == 0 || e == 1 || e == 10)));
116
0
  }
117
118
  static int cmp (const void *pa, const void *pb)
119
0
  {
120
0
    const NameRecord *a = (const NameRecord *)pa;
121
0
    const NameRecord *b = (const NameRecord *)pb;
122
0
123
0
    if (a->platformID != b->platformID)
124
0
      return a->platformID - b->platformID;
125
0
126
0
    if (a->encodingID != b->encodingID)
127
0
      return a->encodingID - b->encodingID;
128
0
129
0
    if (a->languageID != b->languageID)
130
0
      return a->languageID - b->languageID;
131
0
132
0
    if (a->nameID != b->nameID)
133
0
      return a->nameID - b->nameID;
134
0
135
0
    if (a->length != b->length)
136
0
      return a->length - b->length;
137
0
138
0
    return 0;
139
0
  }
140
141
  bool sanitize (hb_sanitize_context_t *c, const void *base) const
142
3.38M
  {
143
3.38M
    TRACE_SANITIZE (this);
144
3.38M
    return_trace (c->check_struct (this) && offset.sanitize (c, base, length));
145
3.38M
  }
146
147
  HBUINT16  platformID; /* Platform ID. */
148
  HBUINT16  encodingID; /* Platform-specific encoding ID. */
149
  HBUINT16  languageID; /* Language ID. */
150
  HBUINT16  nameID;   /* Name ID. */
151
  HBUINT16  length;   /* String length (in bytes). */
152
  NNOffset16To<UnsizedArrayOf<HBUINT8>>
153
    offset;   /* String offset from start of storage area (in bytes). */
154
  public:
155
  DEFINE_SIZE_STATIC (12);
156
};
157
158
static int
159
_hb_ot_name_entry_cmp_key (const void *pa, const void *pb, bool exact)
160
105M
{
161
105M
  const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
162
105M
  const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
163
164
  /* Compare by name_id, then language. */
165
166
105M
  if (a->name_id != b->name_id)
167
8.36M
    return a->name_id - b->name_id;
168
169
97.2M
  if (a->language == b->language) return 0;
170
58.9k
  if (!a->language) return -1;
171
51.5k
  if (!b->language) return +1;
172
173
42.2k
  const char *astr = hb_language_to_string (a->language);
174
42.2k
  const char *bstr = hb_language_to_string (b->language);
175
176
42.2k
  signed c = strcmp (astr, bstr);
177
178
  // 'a' is the user request, and 'b' is string in the font.
179
  // If eg. user asks for "en-us" and font has "en", approve.
180
42.2k
  if (!exact && c &&
181
42.2k
      hb_language_matches (b->language, a->language))
182
0
    return 0;
183
184
42.2k
  return c;
185
42.2k
}
Unexecuted instantiation: hb-ot-face.cc:OT::_hb_ot_name_entry_cmp_key(void const*, void const*, bool)
Unexecuted instantiation: hb-ot-layout.cc:OT::_hb_ot_name_entry_cmp_key(void const*, void const*, bool)
hb-ot-name.cc:OT::_hb_ot_name_entry_cmp_key(void const*, void const*, bool)
Line
Count
Source
160
105M
{
161
105M
  const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
162
105M
  const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
163
164
  /* Compare by name_id, then language. */
165
166
105M
  if (a->name_id != b->name_id)
167
8.36M
    return a->name_id - b->name_id;
168
169
97.2M
  if (a->language == b->language) return 0;
170
58.9k
  if (!a->language) return -1;
171
51.5k
  if (!b->language) return +1;
172
173
42.2k
  const char *astr = hb_language_to_string (a->language);
174
42.2k
  const char *bstr = hb_language_to_string (b->language);
175
176
42.2k
  signed c = strcmp (astr, bstr);
177
178
  // 'a' is the user request, and 'b' is string in the font.
179
  // If eg. user asks for "en-us" and font has "en", approve.
180
42.2k
  if (!exact && c &&
181
42.2k
      hb_language_matches (b->language, a->language))
182
0
    return 0;
183
184
42.2k
  return c;
185
42.2k
}
186
187
static int
188
_hb_ot_name_entry_cmp (const void *pa, const void *pb)
189
103M
{
190
  /* Compare by name_id, then language, then score, then index. */
191
192
103M
  int v = _hb_ot_name_entry_cmp_key (pa, pb, true);
193
103M
  if (v)
194
6.29M
    return v;
195
196
97.0M
  const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
197
97.0M
  const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
198
199
97.0M
  if (a->entry_score != b->entry_score)
200
403k
    return a->entry_score - b->entry_score;
201
202
96.6M
  if (a->entry_index != b->entry_index)
203
96.6M
    return a->entry_index - b->entry_index;
204
205
0
  return 0;
206
96.6M
}
Unexecuted instantiation: hb-ot-face.cc:OT::_hb_ot_name_entry_cmp(void const*, void const*)
Unexecuted instantiation: hb-ot-layout.cc:OT::_hb_ot_name_entry_cmp(void const*, void const*)
hb-ot-name.cc:OT::_hb_ot_name_entry_cmp(void const*, void const*)
Line
Count
Source
189
103M
{
190
  /* Compare by name_id, then language, then score, then index. */
191
192
103M
  int v = _hb_ot_name_entry_cmp_key (pa, pb, true);
193
103M
  if (v)
194
6.29M
    return v;
195
196
97.0M
  const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
197
97.0M
  const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
198
199
97.0M
  if (a->entry_score != b->entry_score)
200
403k
    return a->entry_score - b->entry_score;
201
202
96.6M
  if (a->entry_index != b->entry_index)
203
96.6M
    return a->entry_index - b->entry_index;
204
205
0
  return 0;
206
96.6M
}
207
208
struct name
209
{
210
  static constexpr hb_tag_t tableTag = HB_OT_TAG_name;
211
212
  unsigned int get_size () const
213
0
  { return min_size + count * nameRecordZ.item_size; }
214
215
  template <typename Iterator,
216
      hb_requires (hb_is_source_of (Iterator, const NameRecord &))>
217
  bool serialize (hb_serialize_context_t *c,
218
      Iterator it,
219
      const void *src_string_pool)
220
0
  {
221
0
    TRACE_SERIALIZE (this);
222
0
223
0
    if (unlikely (!c->extend_min ((*this))))  return_trace (false);
224
0
225
0
    this->format = 0;
226
0
    this->count = it.len ();
227
0
228
0
    NameRecord *name_records = (NameRecord *) hb_calloc (it.len (), NameRecord::static_size);
229
0
    if (unlikely (!name_records)) return_trace (false);
230
0
231
0
    hb_array_t<NameRecord> records (name_records, it.len ());
232
0
233
0
    for (const NameRecord& record : it)
234
0
    {
235
0
      memcpy (name_records, &record, NameRecord::static_size);
236
0
      name_records++;
237
0
    }
238
0
239
0
    records.qsort ();
240
0
241
0
    c->copy_all (records, src_string_pool);
242
0
    hb_free (records.arrayZ);
243
0
244
0
245
0
    if (unlikely (c->ran_out_of_room ())) return_trace (false);
246
0
247
0
    this->stringOffset = c->length ();
248
0
249
0
    return_trace (true);
250
0
  }
Unexecuted instantiation: hb-ot-face.cc:bool OT::name::serialize<hb_filter_iter_t<hb_filter_iter_t<hb_filter_iter_t<hb_array_t<OT::NameRecord const>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, OT::name::subset(hb_subset_context_t*) const::{lambda(OT::NameRecord const&)#1}, $_7 const&, (void*)0>, (void*)0>(hb_serialize_context_t*, hb_filter_iter_t<hb_filter_iter_t<hb_filter_iter_t<hb_array_t<OT::NameRecord const>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, OT::name::subset(hb_subset_context_t*) const::{lambda(OT::NameRecord const&)#1}, $_7 const&, (void*)0>, void const*)
Unexecuted instantiation: hb-ot-layout.cc:bool OT::name::serialize<hb_filter_iter_t<hb_filter_iter_t<hb_filter_iter_t<hb_array_t<OT::NameRecord const>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, OT::name::subset(hb_subset_context_t*) const::{lambda(OT::NameRecord const&)#1}, $_7 const&, (void*)0>, (void*)0>(hb_serialize_context_t*, hb_filter_iter_t<hb_filter_iter_t<hb_filter_iter_t<hb_array_t<OT::NameRecord const>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, OT::name::subset(hb_subset_context_t*) const::{lambda(OT::NameRecord const&)#1}, $_7 const&, (void*)0>, void const*)
Unexecuted instantiation: hb-ot-name.cc:bool OT::name::serialize<hb_filter_iter_t<hb_filter_iter_t<hb_filter_iter_t<hb_array_t<OT::NameRecord const>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, OT::name::subset(hb_subset_context_t*) const::{lambda(OT::NameRecord const&)#1}, $_7 const&, (void*)0>, (void*)0>(hb_serialize_context_t*, hb_filter_iter_t<hb_filter_iter_t<hb_filter_iter_t<hb_array_t<OT::NameRecord const>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, hb_set_t*&, OT::IntType<unsigned short, 2u> OT::NameRecord::*, (void*)0>, OT::name::subset(hb_subset_context_t*) const::{lambda(OT::NameRecord const&)#1}, $_7 const&, (void*)0>, void const*)
251
252
  bool subset (hb_subset_context_t *c) const
253
0
  {
254
0
    TRACE_SUBSET (this);
255
0
256
0
    name *name_prime = c->serializer->start_embed<name> ();
257
0
    if (unlikely (!name_prime)) return_trace (false);
258
0
259
0
    auto it =
260
0
    + nameRecordZ.as_array (count)
261
0
    | hb_filter (c->plan->name_ids, &NameRecord::nameID)
262
0
    | hb_filter (c->plan->name_languages, &NameRecord::languageID)
263
0
    | hb_filter ([&] (const NameRecord& namerecord) {
264
0
      return
265
0
          (c->plan->flags & HB_SUBSET_FLAGS_NAME_LEGACY)
266
0
          || namerecord.isUnicode ();
267
0
    })
268
0
    ;
269
0
270
0
    name_prime->serialize (c->serializer, it, std::addressof (this + stringOffset));
271
0
    return_trace (name_prime->count);
272
0
  }
273
274
  bool sanitize_records (hb_sanitize_context_t *c) const
275
157k
  {
276
157k
    TRACE_SANITIZE (this);
277
157k
    const void *string_pool = (this+stringOffset).arrayZ;
278
157k
    return_trace (nameRecordZ.sanitize (c, count, string_pool));
279
157k
  }
280
281
  bool sanitize (hb_sanitize_context_t *c) const
282
207k
  {
283
207k
    TRACE_SANITIZE (this);
284
207k
    return_trace (c->check_struct (this) &&
285
207k
      likely (format == 0 || format == 1) &&
286
207k
      c->check_array (nameRecordZ.arrayZ, count) &&
287
207k
      c->check_range (this, stringOffset) &&
288
207k
      sanitize_records (c));
289
207k
  }
290
291
  struct accelerator_t
292
  {
293
    accelerator_t (hb_face_t *face)
294
569k
    {
295
569k
      this->table = hb_sanitize_context_t ().reference_table<name> (face);
296
569k
      assert (this->table.get_length () >= this->table->stringOffset);
297
0
      this->pool = (const char *) (const void *) (this->table+this->table->stringOffset);
298
569k
      this->pool_len = this->table.get_length () - this->table->stringOffset;
299
569k
      const hb_array_t<const NameRecord> all_names (this->table->nameRecordZ.arrayZ,
300
569k
                this->table->count);
301
302
569k
      this->names.alloc (all_names.length);
303
304
3.69M
      for (unsigned int i = 0; i < all_names.length; i++)
305
3.12M
      {
306
3.12M
  hb_ot_name_entry_t *entry = this->names.push ();
307
308
3.12M
  entry->name_id = all_names[i].nameID;
309
3.12M
  entry->language = all_names[i].language (face);
310
3.12M
  entry->entry_score =  all_names[i].score ();
311
3.12M
  entry->entry_index = i;
312
3.12M
      }
313
314
569k
      this->names.qsort (_hb_ot_name_entry_cmp);
315
      /* Walk and pick best only for each name_id,language pair,
316
       * while dropping unsupported encodings. */
317
569k
      unsigned int j = 0;
318
3.67M
      for (unsigned int i = 0; i < this->names.length; i++)
319
3.11M
      {
320
3.11M
  if (this->names[i].entry_score == UNSUPPORTED ||
321
3.11M
      this->names[i].language == HB_LANGUAGE_INVALID)
322
1.27M
    continue;
323
1.83M
  if (i &&
324
1.83M
      this->names[i - 1].name_id  == this->names[i].name_id &&
325
1.83M
      this->names[i - 1].language == this->names[i].language)
326
368k
    continue;
327
1.47M
  this->names[j++] = this->names[i];
328
1.47M
      }
329
569k
      this->names.resize (j);
330
569k
    }
331
    ~accelerator_t ()
332
567k
    {
333
567k
      this->table.destroy ();
334
567k
    }
335
336
    int get_index (hb_ot_name_id_t  name_id,
337
       hb_language_t    language,
338
       unsigned int    *width=nullptr) const
339
1.72M
    {
340
1.72M
      const hb_ot_name_entry_t key = {name_id, {0}, language};
341
1.72M
      const hb_ot_name_entry_t *entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names,
342
1.72M
                this->names.length,
343
1.72M
                sizeof (hb_ot_name_entry_t),
344
1.72M
                _hb_ot_name_entry_cmp_key,
345
1.72M
                true);
346
347
1.72M
      if (!entry)
348
1.63M
      {
349
1.63M
  entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names,
350
1.63M
          this->names.length,
351
1.63M
          sizeof (hb_ot_name_entry_t),
352
1.63M
          _hb_ot_name_entry_cmp_key,
353
1.63M
          false);
354
1.63M
      }
355
356
1.72M
      if (!entry)
357
1.63M
  return -1;
358
359
88.9k
      if (width)
360
88.9k
  *width = entry->entry_score < 10 ? 2 : 1;
361
362
88.9k
      return entry->entry_index;
363
1.72M
    }
364
365
    hb_bytes_t get_name (unsigned int idx) const
366
88.9k
    {
367
88.9k
      const hb_array_t<const NameRecord> all_names (table->nameRecordZ.arrayZ, table->count);
368
88.9k
      const NameRecord &record = all_names[idx];
369
88.9k
      const hb_bytes_t string_pool (pool, pool_len);
370
88.9k
      return string_pool.sub_array (record.offset, record.length);
371
88.9k
    }
372
373
    private:
374
    const char *pool;
375
    unsigned int pool_len;
376
    public:
377
    hb_blob_ptr_t<name> table;
378
    hb_vector_t<hb_ot_name_entry_t> names;
379
  };
380
381
  /* We only implement format 0 for now. */
382
  HBUINT16  format;   /* Format selector (=0/1). */
383
  HBUINT16  count;    /* Number of name records. */
384
  NNOffset16To<UnsizedArrayOf<HBUINT8>>
385
    stringOffset; /* Offset to start of string storage (from start of table). */
386
  UnsizedArrayOf<NameRecord>
387
    nameRecordZ;  /* The name records where count is the number of records. */
388
  public:
389
  DEFINE_SIZE_ARRAY (6, nameRecordZ);
390
};
391
392
#undef entry_index
393
#undef entry_score
394
395
struct name_accelerator_t : name::accelerator_t {
396
569k
  name_accelerator_t (hb_face_t *face) : name::accelerator_t (face) {}
397
};
398
399
} /* namespace OT */
400
401
402
#endif /* HB_OT_NAME_TABLE_HH */