Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/hb-ot-layout-base-table.hh
Line
Count
Source
1
/*
2
 * Copyright © 2016  Elie Roux <elie.roux@telecom-bretagne.eu>
3
 * Copyright © 2018  Google, Inc.
4
 * Copyright © 2018-2019  Ebrahim Byagowi
5
 *
6
 *  This is part of HarfBuzz, a text shaping library.
7
 *
8
 * Permission is hereby granted, without written agreement and without
9
 * license or royalty fees, to use, copy, modify, and distribute this
10
 * software and its documentation for any purpose, provided that the
11
 * above copyright notice and the following two paragraphs appear in
12
 * all copies of this software.
13
 *
14
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
15
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
17
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18
 * DAMAGE.
19
 *
20
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
21
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
23
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
24
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25
 *
26
 * Google Author(s): Behdad Esfahbod
27
 */
28
29
#ifndef HB_OT_LAYOUT_BASE_TABLE_HH
30
#define HB_OT_LAYOUT_BASE_TABLE_HH
31
32
#include "hb-open-type.hh"
33
#include "hb-ot-layout-common.hh"
34
35
namespace OT {
36
37
/*
38
 * BASE -- Baseline
39
 * https://docs.microsoft.com/en-us/typography/opentype/spec/base
40
 */
41
42
struct BaseCoordFormat1
43
{
44
  hb_position_t get_coord (hb_font_t *font, hb_direction_t direction) const
45
0
  {
46
0
    return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate);
47
0
  }
48
49
  bool subset (hb_subset_context_t *c) const
50
0
  {
51
0
    TRACE_SUBSET (this);
52
0
    return_trace ((bool) c->serializer->embed (*this));
53
0
  }
54
55
  bool sanitize (hb_sanitize_context_t *c) const
56
2.39k
  {
57
2.39k
    TRACE_SANITIZE (this);
58
2.39k
    return_trace (c->check_struct (this));
59
2.39k
  }
60
61
  protected:
62
  HBUINT16  format;   /* Format identifier--format = 1 */
63
  FWORD   coordinate; /* X or Y value, in design units */
64
  public:
65
  DEFINE_SIZE_STATIC (4);
66
};
67
68
template <typename Types>
69
struct BaseCoordFormat2_4
70
{
71
  hb_position_t get_coord (hb_font_t *font, hb_direction_t direction) const
72
0
  {
73
    /* TODO */
74
0
    return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate);
75
0
  }
Unexecuted instantiation: OT::BaseCoordFormat2_4<OT::Layout::SmallTypes>::get_coord(hb_font_t*, hb_direction_t) const
Unexecuted instantiation: OT::BaseCoordFormat2_4<OT::Layout::MediumTypes>::get_coord(hb_font_t*, hb_direction_t) const
76
77
  bool subset (hb_subset_context_t *c) const
78
0
  {
79
0
    TRACE_SUBSET (this);
80
0
    auto *out = c->serializer->embed (*this);
81
0
    if (unlikely (!out)) return_trace (false);
82
0
83
0
    hb_codepoint_t *new_gid;
84
0
    if (!c->plan->glyph_map->has (referenceGlyph, &new_gid))
85
0
      return_trace (false);
86
0
87
0
    return_trace (c->serializer->check_assign (out->referenceGlyph,
88
0
                                               *new_gid,
89
0
                                               HB_SERIALIZE_ERROR_INT_OVERFLOW));
90
0
  }
Unexecuted instantiation: OT::BaseCoordFormat2_4<OT::Layout::SmallTypes>::subset(hb_subset_context_t*) const
Unexecuted instantiation: OT::BaseCoordFormat2_4<OT::Layout::MediumTypes>::subset(hb_subset_context_t*) const
91
92
  bool sanitize (hb_sanitize_context_t *c) const
93
932
  {
94
932
    TRACE_SANITIZE (this);
95
932
    return_trace (c->check_struct (this));
96
932
  }
OT::BaseCoordFormat2_4<OT::Layout::SmallTypes>::sanitize(hb_sanitize_context_t*) const
Line
Count
Source
93
648
  {
94
648
    TRACE_SANITIZE (this);
95
648
    return_trace (c->check_struct (this));
96
648
  }
OT::BaseCoordFormat2_4<OT::Layout::MediumTypes>::sanitize(hb_sanitize_context_t*) const
Line
Count
Source
93
284
  {
94
284
    TRACE_SANITIZE (this);
95
284
    return_trace (c->check_struct (this));
96
284
  }
97
98
  protected:
99
  HBUINT16  format;   /* Format identifier--format = 2 or 4 */
100
  FWORD   coordinate; /* X or Y value, in design units */
101
  typename Types::HBGlyphID
102
    referenceGlyph; /* Glyph ID of control glyph */
103
  HBUINT16  coordPoint; /* Index of contour point on the
104
         * reference glyph */
105
  public:
106
  DEFINE_SIZE_STATIC (6 + Types::HBGlyphID::static_size);
107
};
108
109
struct BaseCoordFormat3
110
{
111
  hb_position_t get_coord (hb_font_t *font,
112
         const ItemVariationStore &var_store,
113
         hb_direction_t direction) const
114
0
  {
115
0
    const Device &device = this+deviceTable;
116
117
0
    return HB_DIRECTION_IS_HORIZONTAL (direction)
118
0
   ? hb_saturate_add (font->em_scale_y (coordinate), device.get_y_delta (font, var_store))
119
0
   : hb_saturate_add (font->em_scale_x (coordinate), device.get_x_delta (font, var_store));
120
0
  }
121
122
  void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const
123
0
  {
124
0
    unsigned varidx = (this+deviceTable).get_variation_index ();
125
0
    varidx_set.add (varidx);
126
0
  }
127
128
  bool subset (hb_subset_context_t *c) const
129
0
  {
130
0
    TRACE_SUBSET (this);
131
0
    auto *out = c->serializer->embed (*this);
132
0
    if (unlikely (!out)) return_trace (false);
133
0
134
0
    if (!c->plan->pinned_at_default)
135
0
    {
136
0
      unsigned var_idx = (this+deviceTable).get_variation_index ();
137
0
      if (var_idx != VarIdx::NO_VARIATION)
138
0
      {
139
0
        hb_pair_t<unsigned, int> *v;
140
0
        if (!c->plan->base_variation_idx_map.has (var_idx, &v))
141
0
          return_trace (false);
142
0
        
143
0
        if (unlikely (!c->serializer->check_assign (out->coordinate, coordinate + hb_second (*v),
144
0
                                                    HB_SERIALIZE_ERROR_INT_OVERFLOW)))
145
0
          return_trace (false);
146
0
      }
147
0
    }
148
0
    
149
0
    out->deviceTable.serialize_copy (c->serializer, deviceTable,
150
0
                                     this, 0,
151
0
                                     hb_serialize_context_t::Head,
152
0
                                     &c->plan->base_variation_idx_map);
153
0
    return_trace (true);
154
0
  }
155
156
  bool sanitize (hb_sanitize_context_t *c) const
157
784
  {
158
784
    TRACE_SANITIZE (this);
159
784
    return_trace (likely (c->check_struct (this) &&
160
784
        deviceTable.sanitize (c, this)));
161
784
  }
162
163
  protected:
164
  HBUINT16  format;   /* Format identifier--format = 3 */
165
  FWORD   coordinate; /* X or Y value, in design units */
166
  Offset16To<Device>
167
    deviceTable;  /* Offset to Device table for X or
168
         * Y value, from beginning of
169
         * BaseCoord table (may be NULL). */
170
  public:
171
  DEFINE_SIZE_STATIC (6);
172
};
173
174
struct BaseCoord
175
{
176
2
  bool has_data () const { return u.format.v; }
177
178
  hb_position_t get_coord (hb_font_t            *font,
179
         const ItemVariationStore &var_store,
180
         hb_direction_t        direction) const
181
0
  {
182
0
    switch (u.format.v) {
183
0
    case 1: hb_barrier (); return u.format1.get_coord (font, direction);
184
0
    case 2: hb_barrier (); return u.format2.get_coord (font, direction);
185
0
    case 3: hb_barrier (); return u.format3.get_coord (font, var_store, direction);
186
0
#ifndef HB_NO_BEYOND_64K
187
0
    case 4: hb_barrier (); return u.format4.get_coord (font, direction);
188
0
#endif
189
0
    default:return 0;
190
0
    }
191
0
  }
192
193
  void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const
194
0
  {
195
0
    switch (u.format.v) {
196
0
    case 3: hb_barrier (); u.format3.collect_variation_indices (varidx_set); return;
197
0
    default:return;
198
0
    }
199
0
  }
200
201
  template <typename context_t, typename ...Ts>
202
  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
203
0
  {
204
0
    if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
205
0
    TRACE_DISPATCH (this, u.format.v);
206
0
    switch (u.format.v) {
207
0
    case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
208
0
    case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
209
0
    case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
210
0
#ifndef HB_NO_BEYOND_64K
211
0
    case 4: hb_barrier (); return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...));
212
0
#endif
213
0
    default:return_trace (c->default_return_value ());
214
0
    }
215
0
  }
216
217
  bool sanitize (hb_sanitize_context_t *c) const
218
4.15k
  {
219
4.15k
    TRACE_SANITIZE (this);
220
4.15k
    if (unlikely (!u.format.v.sanitize (c))) return_trace (false);
221
4.12k
    hb_barrier ();
222
4.12k
    switch (u.format.v) {
223
2.39k
    case 1: hb_barrier (); return_trace (u.format1.sanitize (c));
224
648
    case 2: hb_barrier (); return_trace (u.format2.sanitize (c));
225
784
    case 3: hb_barrier (); return_trace (u.format3.sanitize (c));
226
0
#ifndef HB_NO_BEYOND_64K
227
284
    case 4: hb_barrier (); return_trace (u.format4.sanitize (c));
228
0
#endif
229
16
    default:return_trace (false);
230
4.12k
    }
231
4.12k
  }
232
233
  protected:
234
  union {
235
  struct { HBUINT16 v; }  format;
236
  BaseCoordFormat1  format1;
237
  BaseCoordFormat2_4<Layout::SmallTypes>
238
      format2;
239
  BaseCoordFormat3  format3;
240
#ifndef HB_NO_BEYOND_64K
241
  BaseCoordFormat2_4<Layout::MediumTypes>
242
      format4;
243
#endif
244
  } u;
245
  public:
246
  DEFINE_SIZE_UNION (2, format.v);
247
};
248
249
struct FeatMinMaxRecord
250
{
251
0
  int cmp (hb_tag_t key) const { return tag.cmp (key); }
252
253
0
  bool has_data () const { return tag; }
254
255
0
  hb_tag_t get_feature_tag () const { return tag; }
256
257
  void get_min_max (const BaseCoord **min, const BaseCoord **max) const
258
0
  {
259
0
    if (likely (min)) *min = &(this+minCoord);
260
0
    if (likely (max)) *max = &(this+maxCoord);
261
0
  }
262
263
  void collect_variation_indices (const hb_subset_plan_t* plan,
264
                                  const void *base,
265
                                  hb_set_t& varidx_set /* OUT */) const
266
0
  {
267
0
    if (!plan->layout_features.has (tag))
268
0
      return;
269
0
270
0
    (base+minCoord).collect_variation_indices (varidx_set);
271
0
    (base+maxCoord).collect_variation_indices (varidx_set);
272
0
  }
273
274
  bool subset (hb_subset_context_t *c,
275
               const void *base) const
276
0
  {
277
0
    TRACE_SUBSET (this);
278
0
    auto *out = c->serializer->embed (*this);
279
0
    if (unlikely (!out)) return_trace (false);
280
0
281
0
    bool ret = out->minCoord.serialize_subset (c, minCoord, base);
282
0
    ret |= out->maxCoord.serialize_subset (c, maxCoord, base);
283
0
    return_trace (ret);
284
0
  }
285
286
  bool sanitize (hb_sanitize_context_t *c, const void *base) const
287
2.55k
  {
288
2.55k
    TRACE_SANITIZE (this);
289
2.55k
    return_trace (likely (c->check_struct (this) &&
290
2.55k
        minCoord.sanitize (c, base) &&
291
2.55k
        maxCoord.sanitize (c, base)));
292
2.55k
  }
293
294
  protected:
295
  Tag   tag;    /* 4-byte feature identification tag--must
296
         * match feature tag in FeatureList */
297
  Offset16To<BaseCoord>
298
    minCoord; /* Offset to BaseCoord table that defines
299
         * the minimum extent value, from beginning
300
         * of MinMax table (may be NULL) */
301
  Offset16To<BaseCoord>
302
    maxCoord; /* Offset to BaseCoord table that defines
303
         * the maximum extent value, from beginning
304
         * of MinMax table (may be NULL) */
305
  public:
306
  DEFINE_SIZE_STATIC (8);
307
};
308
309
struct MinMax
310
{
311
  void get_min_max (hb_tag_t          feature_tag,
312
        const BaseCoord **min,
313
        const BaseCoord **max) const
314
0
  {
315
0
    const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag);
316
0
    if (minMaxCoord.has_data ())
317
0
      minMaxCoord.get_min_max (min, max);
318
0
    else
319
0
    {
320
0
      if (likely (min)) *min = &(this+minCoord);
321
0
      if (likely (max)) *max = &(this+maxCoord);
322
0
    }
323
0
  }
324
325
  void collect_variation_indices (const hb_subset_plan_t* plan,
326
                                  hb_set_t& varidx_set /* OUT */) const
327
0
  {
328
0
    (this+minCoord).collect_variation_indices (varidx_set);
329
0
    (this+maxCoord).collect_variation_indices (varidx_set);
330
0
    for (const FeatMinMaxRecord& record : featMinMaxRecords)
331
0
      record.collect_variation_indices (plan, this, varidx_set);
332
0
  }
333
334
  bool subset (hb_subset_context_t *c) const
335
0
  {
336
0
    TRACE_SUBSET (this);
337
0
    auto *out = c->serializer->start_embed (*this);
338
0
    if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
339
0
340
0
    bool ret = out->minCoord.serialize_subset (c, minCoord, this);
341
0
    ret |= out->maxCoord.serialize_subset (c, maxCoord, this);
342
0
343
0
    unsigned len = 0;
344
0
    for (const FeatMinMaxRecord& _ : featMinMaxRecords)
345
0
    {
346
0
      hb_tag_t feature_tag = _.get_feature_tag ();
347
0
      if (!c->plan->layout_features.has (feature_tag))
348
0
        continue;
349
0
350
0
      auto snap = c->serializer->snapshot ();
351
0
      if (!_.subset (c, this)) c->serializer->revert (snap);
352
0
      else len++;
353
0
    }
354
0
355
0
    if (len > 0) ret = true;
356
0
    if (!ret) return_trace (false);
357
0
    return_trace (c->serializer->check_assign (out->featMinMaxRecords.len, len,
358
0
                                               HB_SERIALIZE_ERROR_INT_OVERFLOW));
359
0
  }
360
361
  bool sanitize (hb_sanitize_context_t *c) const
362
1.74k
  {
363
1.74k
    TRACE_SANITIZE (this);
364
1.74k
    return_trace (likely (c->check_struct (this) &&
365
1.74k
        minCoord.sanitize (c, this) &&
366
1.74k
        maxCoord.sanitize (c, this) &&
367
1.74k
        featMinMaxRecords.sanitize (c, this)));
368
1.74k
  }
369
370
  protected:
371
  Offset16To<BaseCoord>
372
    minCoord; /* Offset to BaseCoord table that defines
373
         * minimum extent value, from the beginning
374
         * of MinMax table (may be NULL) */
375
  Offset16To<BaseCoord>
376
    maxCoord; /* Offset to BaseCoord table that defines
377
         * maximum extent value, from the beginning
378
         * of MinMax table (may be NULL) */
379
  SortedArray16Of<FeatMinMaxRecord>
380
    featMinMaxRecords;
381
        /* Array of FeatMinMaxRecords, in alphabetical
382
         * order by featureTableTag */
383
  public:
384
  DEFINE_SIZE_ARRAY (6, featMinMaxRecords);
385
};
386
387
struct BaseValues
388
{
389
  const BaseCoord &get_base_coord (int baseline_tag_index) const
390
2
  {
391
2
    if (baseline_tag_index == -1) baseline_tag_index = defaultIndex;
392
2
    return this+baseCoords[baseline_tag_index];
393
2
  }
394
395
  void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const
396
0
  {
397
0
    for (const auto& _ : baseCoords)
398
0
      (this+_).collect_variation_indices (varidx_set);
399
0
  }
400
401
  bool subset (hb_subset_context_t *c) const
402
0
  {
403
0
    TRACE_SUBSET (this);
404
0
    auto *out = c->serializer->start_embed (*this);
405
0
    if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
406
0
    out->defaultIndex = defaultIndex;
407
0
408
0
    bool ret = false;
409
0
    for (const auto& _ : baseCoords)
410
0
    {
411
0
      auto *o = out->baseCoords.serialize_append (c->serializer);
412
0
      if (unlikely (!o)) return_trace (false);
413
0
       ret |= o->serialize_subset (c, _, this);
414
0
    }
415
0
416
0
    return_trace (ret);
417
0
  }
418
419
  bool sanitize (hb_sanitize_context_t *c) const
420
1.01k
  {
421
1.01k
    TRACE_SANITIZE (this);
422
1.01k
    return_trace (likely (c->check_struct (this) &&
423
1.01k
        baseCoords.sanitize (c, this)));
424
1.01k
  }
425
426
  protected:
427
  Index   defaultIndex; /* Index number of default baseline for this
428
         * script — equals index position of baseline tag
429
         * in baselineTags array of the BaseTagList */
430
  Array16OfOffset16To<BaseCoord>
431
    baseCoords; /* Number of BaseCoord tables defined — should equal
432
         * baseTagCount in the BaseTagList
433
         *
434
         * Array of offsets to BaseCoord tables, from beginning of
435
         * BaseValues table — order matches baselineTags array in
436
         * the BaseTagList */
437
  public:
438
  DEFINE_SIZE_ARRAY (4, baseCoords);
439
};
440
441
struct BaseLangSysRecord
442
{
443
0
  int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); }
444
445
0
  bool has_data () const { return baseLangSysTag; }
446
447
0
  const MinMax &get_min_max (const void* base) const { return base+minMax; }
448
449
  void collect_variation_indices (const void* base,
450
                                  const hb_subset_plan_t* plan,
451
                                  hb_set_t& varidx_set /* OUT */) const
452
0
  { (base+minMax).collect_variation_indices (plan, varidx_set); }
453
454
  bool subset (hb_subset_context_t *c,
455
               const void *base) const
456
0
  {
457
0
    TRACE_SUBSET (this);
458
0
    auto *out = c->serializer->embed (*this);
459
0
    if (unlikely (!out)) return_trace (false);
460
0
461
0
    return_trace (out->minMax.serialize_subset (c, minMax, base));
462
0
  }
463
464
  bool sanitize (hb_sanitize_context_t *c, const void *base) const
465
2.45k
  {
466
2.45k
    TRACE_SANITIZE (this);
467
2.45k
    return_trace (likely (c->check_struct (this) &&
468
2.45k
        minMax.sanitize (c, base)));
469
2.45k
  }
470
471
  protected:
472
  Tag   baseLangSysTag; /* 4-byte language system identification tag */
473
  Offset16To<MinMax>
474
    minMax;   /* Offset to MinMax table, from beginning
475
         * of BaseScript table */
476
  public:
477
  DEFINE_SIZE_STATIC (6);
478
};
479
480
struct BaseScript
481
{
482
  const MinMax &get_min_max (hb_tag_t language_tag) const
483
0
  {
484
0
    const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag);
485
0
    return record.has_data () ? record.get_min_max (this) : this+defaultMinMax;
486
0
  }
487
488
  const BaseCoord &get_base_coord (int baseline_tag_index) const
489
2
  { return (this+baseValues).get_base_coord (baseline_tag_index); }
490
491
43.5k
  bool has_values () const { return baseValues; }
492
0
  bool has_min_max () const { return defaultMinMax || baseLangSysRecords; }
493
494
  void collect_variation_indices (const hb_subset_plan_t* plan,
495
                                  hb_set_t& varidx_set /* OUT */) const
496
0
  {
497
0
    (this+baseValues).collect_variation_indices (varidx_set);
498
0
    (this+defaultMinMax).collect_variation_indices (plan, varidx_set);
499
0
    
500
0
    for (const BaseLangSysRecord& _ : baseLangSysRecords)
501
0
      _.collect_variation_indices (this, plan, varidx_set);
502
0
  }
503
504
  bool subset (hb_subset_context_t *c) const
505
0
  {
506
0
    TRACE_SUBSET (this);
507
0
    auto *out = c->serializer->start_embed (*this);
508
0
    if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
509
0
510
0
    bool ret = out->baseValues.serialize_subset (c, baseValues, this);
511
0
    ret |= out->defaultMinMax.serialize_subset (c, defaultMinMax, this);
512
0
513
0
    unsigned len = 0;
514
0
    for (const auto& _ : baseLangSysRecords)
515
0
    {
516
0
      auto snap = c->serializer->snapshot ();
517
0
      if (_.subset (c, this)) {
518
0
        ret = true;
519
0
        len++;
520
0
      }
521
0
      else c->serializer->revert (snap);
522
0
    }
523
0
524
0
    if (!ret) return_trace (false);
525
0
    return_trace (c->serializer->check_assign (out->baseLangSysRecords.len, len,
526
0
                                               HB_SERIALIZE_ERROR_INT_OVERFLOW));
527
0
  }
528
529
  bool sanitize (hb_sanitize_context_t *c) const
530
2.11k
  {
531
2.11k
    TRACE_SANITIZE (this);
532
2.11k
    return_trace (likely (c->check_struct (this) &&
533
2.11k
        baseValues.sanitize (c, this) &&
534
2.11k
        defaultMinMax.sanitize (c, this) &&
535
2.11k
        baseLangSysRecords.sanitize (c, this)));
536
2.11k
  }
537
538
  protected:
539
  Offset16To<BaseValues>
540
    baseValues; /* Offset to BaseValues table, from beginning
541
         * of BaseScript table (may be NULL) */
542
  Offset16To<MinMax>
543
    defaultMinMax;  /* Offset to MinMax table, from beginning of
544
         * BaseScript table (may be NULL) */
545
  SortedArray16Of<BaseLangSysRecord>
546
    baseLangSysRecords;
547
        /* Number of BaseLangSysRecords
548
         * defined — may be zero (0) */
549
550
  public:
551
  DEFINE_SIZE_ARRAY (6, baseLangSysRecords);
552
};
553
554
struct BaseScriptList;
555
struct BaseScriptRecord
556
{
557
291
  int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); }
558
559
87.1k
  bool has_data () const { return baseScriptTag; }
560
561
0
  hb_tag_t get_script_tag () const { return baseScriptTag; }
562
563
  const BaseScript &get_base_script (const BaseScriptList *list) const
564
14
  { return list+baseScript; }
565
566
  void collect_variation_indices (const hb_subset_plan_t* plan,
567
                                  const void* list,
568
                                  hb_set_t& varidx_set /* OUT */) const
569
0
  {
570
0
    if (!plan->layout_scripts.has (baseScriptTag))
571
0
      return;
572
0
573
0
    (list+baseScript).collect_variation_indices (plan, varidx_set);
574
0
  }
575
576
  bool subset (hb_subset_context_t *c,
577
               const void *base) const
578
0
  {
579
0
    TRACE_SUBSET (this);
580
0
    auto *out = c->serializer->embed (*this);
581
0
    if (unlikely (!out)) return_trace (false);
582
0
583
0
    return_trace (out->baseScript.serialize_subset (c, baseScript, base));
584
0
  }
585
586
  bool sanitize (hb_sanitize_context_t *c, const void *base) const
587
3.04k
  {
588
3.04k
    TRACE_SANITIZE (this);
589
3.04k
    return_trace (likely (c->check_struct (this) &&
590
3.04k
        baseScript.sanitize (c, base)));
591
3.04k
  }
592
593
  protected:
594
  Tag   baseScriptTag;  /* 4-byte script identification tag */
595
  Offset16To<BaseScript>
596
    baseScript; /* Offset to BaseScript table, from beginning
597
         * of BaseScriptList */
598
599
  public:
600
  DEFINE_SIZE_STATIC (6);
601
};
602
603
struct BaseScriptList
604
{
605
  const BaseScript &get_base_script (hb_tag_t script) const
606
43.5k
  {
607
43.5k
    const BaseScriptRecord *record = &baseScriptRecords.bsearch (script);
608
43.5k
    if (!record->has_data ()) record = &baseScriptRecords.bsearch (HB_TAG ('D','F','L','T'));
609
43.5k
    return record->has_data () ? record->get_base_script (this) : Null (BaseScript);
610
43.5k
  }
611
612
  void collect_variation_indices (const hb_subset_plan_t* plan,
613
                                  hb_set_t& varidx_set /* OUT */) const
614
0
  {
615
0
    for (const BaseScriptRecord& _ : baseScriptRecords)
616
0
      _.collect_variation_indices (plan, this, varidx_set);
617
0
  }
618
619
  bool subset (hb_subset_context_t *c) const
620
0
  {
621
0
    TRACE_SUBSET (this);
622
0
    auto *out = c->serializer->start_embed (*this);
623
0
    if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
624
0
625
0
    unsigned len = 0;
626
0
    for (const BaseScriptRecord& _ : baseScriptRecords)
627
0
    {
628
0
      hb_tag_t script_tag = _.get_script_tag ();
629
0
      if (!c->plan->layout_scripts.has (script_tag))
630
0
        continue;
631
0
632
0
      auto snap = c->serializer->snapshot ();
633
0
      if (!_.subset (c, this)) c->serializer->revert (snap);
634
0
      else len++;
635
0
    }
636
0
637
0
    if (!len) return_trace (false);
638
0
    return_trace (c->serializer->check_assign (out->baseScriptRecords.len, len,
639
0
                                               HB_SERIALIZE_ERROR_INT_OVERFLOW));
640
0
  }
641
642
  bool sanitize (hb_sanitize_context_t *c) const
643
263
  {
644
263
    TRACE_SANITIZE (this);
645
263
    return_trace (c->check_struct (this) &&
646
263
      baseScriptRecords.sanitize (c, this));
647
263
  }
648
649
  protected:
650
  SortedArray16Of<BaseScriptRecord>
651
      baseScriptRecords;
652
653
  public:
654
  DEFINE_SIZE_ARRAY (2, baseScriptRecords);
655
};
656
657
struct Axis
658
{
659
  bool get_baseline (hb_tag_t          baseline_tag,
660
         hb_tag_t          script_tag,
661
         hb_tag_t          language_tag,
662
         const BaseCoord **coord) const
663
43.5k
  {
664
43.5k
    const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
665
43.5k
    if (!base_script.has_values ())
666
43.5k
    {
667
43.5k
      *coord = nullptr;
668
43.5k
      return false;
669
43.5k
    }
670
671
13
    if (likely (coord))
672
13
    {
673
13
      unsigned int tag_index = 0;
674
13
      if (!(this+baseTagList).bfind (baseline_tag, &tag_index))
675
11
      {
676
11
        *coord = nullptr;
677
11
        return false;
678
11
      }
679
2
      *coord = &base_script.get_base_coord (tag_index);
680
2
    }
681
682
2
    return true;
683
13
  }
684
685
  bool get_min_max (hb_tag_t          script_tag,
686
        hb_tag_t          language_tag,
687
        hb_tag_t          feature_tag,
688
        const BaseCoord **min_coord,
689
        const BaseCoord **max_coord) const
690
0
  {
691
0
    const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
692
0
    if (!base_script.has_min_max ())
693
0
    {
694
0
      *min_coord = *max_coord = nullptr;
695
0
      return false;
696
0
    }
697
698
0
    base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord);
699
700
0
    return true;
701
0
  }
702
703
  void collect_variation_indices (const hb_subset_plan_t* plan,
704
                                  hb_set_t& varidx_set /* OUT */) const
705
0
  { (this+baseScriptList).collect_variation_indices (plan, varidx_set); }
706
707
  bool subset (hb_subset_context_t *c) const
708
0
  {
709
0
    TRACE_SUBSET (this);
710
0
    auto *out = c->serializer->embed (*this);
711
0
    if (unlikely (!out)) return_trace (false);
712
0
713
0
    bool ret = out->baseTagList.serialize_copy (c->serializer, baseTagList, this);
714
0
    ret |= out->baseScriptList.serialize_subset (c, baseScriptList, this);
715
0
    return_trace (ret);
716
0
  }
717
718
  bool sanitize (hb_sanitize_context_t *c) const
719
305
  {
720
305
    TRACE_SANITIZE (this);
721
305
    return_trace (likely (c->check_struct (this) &&
722
305
        baseTagList.sanitize (c, this) &&
723
305
        baseScriptList.sanitize (c, this)));
724
305
  }
725
726
  protected:
727
  Offset16To<SortedArray16Of<Tag>>
728
    baseTagList;  /* Offset to BaseTagList table, from beginning
729
         * of Axis table (may be NULL)
730
         * Array of 4-byte baseline identification tags — must
731
         * be in alphabetical order */
732
  Offset16To<BaseScriptList>
733
    baseScriptList; /* Offset to BaseScriptList table, from beginning
734
         * of Axis table
735
         * Array of BaseScriptRecords, in alphabetical order
736
         * by baseScriptTag */
737
738
  public:
739
  DEFINE_SIZE_STATIC (4);
740
};
741
742
struct BASE
743
{
744
  static constexpr hb_tag_t tableTag = HB_OT_TAG_BASE;
745
746
  const Axis &get_axis (hb_direction_t direction) const
747
43.5k
  { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; }
748
749
  bool has_var_store () const
750
0
  { return version.to_int () >= 0x00010001u && varStore != 0; }
751
752
  const ItemVariationStore &get_var_store () const
753
0
  { return version.to_int () < 0x00010001u ? Null (ItemVariationStore) : this+varStore; }
754
755
  void collect_variation_indices (const hb_subset_plan_t* plan,
756
                                  hb_set_t& varidx_set /* OUT */) const
757
0
  {
758
0
    (this+hAxis).collect_variation_indices (plan, varidx_set);
759
0
    (this+vAxis).collect_variation_indices (plan, varidx_set);
760
0
  }
761
762
  bool subset_varstore (hb_subset_context_t *c,
763
                        BASE *out /* OUT */) const
764
0
  {
765
0
    TRACE_SUBSET (this);
766
0
    if (!c->serializer->allocate_size<Offset32To<ItemVariationStore>> (Offset32To<ItemVariationStore>::static_size))
767
0
        return_trace (false);
768
0
    if (!c->plan->normalized_coords)
769
0
      return_trace (out->varStore.serialize_subset (c, varStore, this, c->plan->base_varstore_inner_maps.as_array ()));
770
0
771
0
    if (c->plan->all_axes_pinned)
772
0
      return_trace (true);
773
0
774
0
    item_variations_t item_vars;
775
0
    if (!item_vars.instantiate (this+varStore, c->plan, true, true,
776
0
                                c->plan->base_varstore_inner_maps.as_array ()))
777
0
      return_trace (false);
778
0
779
0
    if (!out->varStore.serialize_serialize (c->serializer,
780
0
                                            item_vars.has_long_word (),
781
0
                                            c->plan->axis_tags,
782
0
                                            item_vars.get_region_list (),
783
0
                                            item_vars.get_vardata_encodings ()))
784
0
      return_trace (false);
785
0
786
0
    const hb_map_t &varidx_map = item_vars.get_varidx_map ();
787
0
    /* base_variation_idx_map in the plan is old_varidx->(varidx, delta)
788
0
     * mapping, new varidx is generated for subsetting, we need to remap this
789
0
     * after instancing */
790
0
    for (auto _ : c->plan->base_variation_idx_map.iter_ref ())
791
0
    {
792
0
      uint32_t varidx = _.second.first;
793
0
      uint32_t *new_varidx;
794
0
      if (varidx_map.has (varidx, &new_varidx))
795
0
        _.second.first = *new_varidx;
796
0
      else
797
0
        _.second.first = HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
798
0
    }
799
0
    return_trace (true);
800
0
  }
801
802
  bool subset (hb_subset_context_t *c) const
803
0
  {
804
0
    TRACE_SUBSET (this);
805
0
    auto *out = c->serializer->start_embed (*this);
806
0
    if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
807
0
808
0
    out->version = version;
809
0
    bool ret = false;
810
0
    if (version.to_int () >= 0x00010001u) ret |= subset_varstore (c, out);
811
0
812
0
    ret |= out->hAxis.serialize_subset (c, hAxis, this);
813
0
    ret |= out->vAxis.serialize_subset (c, vAxis, this);
814
0
815
0
    return_trace (ret);
816
0
  }
817
818
  bool get_baseline (hb_font_t      *font,
819
         hb_tag_t        baseline_tag,
820
         hb_direction_t  direction,
821
         hb_tag_t        script_tag,
822
         hb_tag_t        language_tag,
823
         hb_position_t  *base) const
824
43.5k
  {
825
43.5k
    const BaseCoord *base_coord = nullptr;
826
43.5k
    if (unlikely (!get_axis (direction).get_baseline (baseline_tag, script_tag, language_tag, &base_coord) ||
827
43.5k
      !base_coord || !base_coord->has_data ()))
828
43.5k
      return false;
829
830
1
    if (likely (base))
831
0
      *base = base_coord->get_coord (font, get_var_store (), direction);
832
833
1
    return true;
834
43.5k
  }
835
836
  bool get_min_max (hb_font_t      *font,
837
        hb_direction_t  direction,
838
        hb_tag_t        script_tag,
839
        hb_tag_t        language_tag,
840
        hb_tag_t        feature_tag,
841
        hb_position_t  *min,
842
        hb_position_t  *max) const
843
0
  {
844
0
    const BaseCoord *min_coord, *max_coord;
845
0
    if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,
846
0
             &min_coord, &max_coord))
847
0
      return false;
848
849
0
    const ItemVariationStore &var_store = get_var_store ();
850
0
    if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction);
851
0
    if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction);
852
0
    return true;
853
0
  }
854
855
  bool sanitize (hb_sanitize_context_t *c) const
856
624
  {
857
624
    TRACE_SANITIZE (this);
858
624
    return_trace (likely (c->check_struct (this) &&
859
624
        hb_barrier () &&
860
624
        likely (version.major == 1) &&
861
624
        hAxis.sanitize (c, this) &&
862
624
        vAxis.sanitize (c, this) &&
863
624
        (version.to_int () < 0x00010001u || varStore.sanitize (c, this))));
864
624
  }
865
866
  protected:
867
  FixedVersion<>version;  /* Version of the BASE table */
868
  Offset16To<Axis>hAxis;    /* Offset to horizontal Axis table, from beginning
869
         * of BASE table (may be NULL) */
870
  Offset16To<Axis>vAxis;    /* Offset to vertical Axis table, from beginning
871
         * of BASE table (may be NULL) */
872
  Offset32To<ItemVariationStore>
873
    varStore; /* Offset to the table of Item Variation
874
         * Store--from beginning of BASE
875
         * header (may be NULL).  Introduced
876
         * in version 0x00010001. */
877
  public:
878
  DEFINE_SIZE_MIN (8);
879
};
880
881
882
} /* namespace OT */
883
884
885
#endif /* HB_OT_LAYOUT_BASE_TABLE_HH */