Coverage Report

Created: 2026-09-28 08:21

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