Coverage Report

Created: 2025-11-24 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/hb-ot-math-table.hh
Line
Count
Source
1
/*
2
 * Copyright © 2016  Igalia S.L.
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
 * Igalia Author(s): Frédéric Wang
25
 */
26
27
#ifndef HB_OT_MATH_TABLE_HH
28
#define HB_OT_MATH_TABLE_HH
29
30
#include "hb-open-type.hh"
31
#include "hb-ot-layout-common.hh"
32
#include "hb-ot-math.h"
33
34
namespace OT {
35
36
37
struct MathValueRecord
38
{
39
  hb_position_t get_x_value (hb_font_t *font, const void *base) const
40
234k
  { return font->em_scale_x (value) + (base+deviceTable).get_x_delta (font); }
41
  hb_position_t get_y_value (hb_font_t *font, const void *base) const
42
1.57M
  { return font->em_scale_y (value) + (base+deviceTable).get_y_delta (font); }
43
44
  MathValueRecord* copy (hb_serialize_context_t *c, const void *base) const
45
0
  {
46
0
    TRACE_SERIALIZE (this);
47
0
    auto *out = c->embed (this);
48
0
    if (unlikely (!out)) return_trace (nullptr);
49
0
    out->deviceTable.serialize_copy (c, deviceTable, base, 0, hb_serialize_context_t::Head);
50
0
51
0
    return_trace (out);
52
0
  }
53
54
  bool sanitize (hb_sanitize_context_t *c, const void *base) const
55
3.58M
  {
56
3.58M
    TRACE_SANITIZE (this);
57
3.58M
    return_trace (c->check_struct (this) && deviceTable.sanitize (c, base));
58
3.58M
  }
59
60
  protected:
61
  HBINT16   value;    /* The X or Y value in design units */
62
  Offset16To<Device>  deviceTable;  /* Offset to the device table - from the
63
           * beginning of parent table.  May be NULL.
64
           * Suggested format for device table is 1. */
65
66
  public:
67
  DEFINE_SIZE_STATIC (4);
68
};
69
70
struct MathConstants
71
{
72
  friend struct MATH;
73
74
  MathConstants* copy (hb_serialize_context_t *c) const
75
0
  {
76
0
    TRACE_SERIALIZE (this);
77
0
    auto *out = c->start_embed (this);
78
0
79
0
    HBINT16 *p = c->allocate_size<HBINT16> (HBINT16::static_size * 2);
80
0
    if (unlikely (!p)) return_trace (nullptr);
81
0
    hb_memcpy (p, percentScaleDown, HBINT16::static_size * 2);
82
0
83
0
    HBUINT16 *m = c->allocate_size<HBUINT16> (HBUINT16::static_size * 2);
84
0
    if (unlikely (!m)) return_trace (nullptr);
85
0
    hb_memcpy (m, minHeight, HBUINT16::static_size * 2);
86
0
87
0
    unsigned count = ARRAY_LENGTH (mathValueRecords);
88
0
    for (unsigned i = 0; i < count; i++)
89
0
      if (!c->copy (mathValueRecords[i], this))
90
0
        return_trace (nullptr);
91
0
92
0
    if (!c->embed (radicalDegreeBottomRaisePercent)) return_trace (nullptr);
93
0
    return_trace (out);
94
0
  }
95
96
  bool sanitize_math_value_records (hb_sanitize_context_t *c) const
97
219
  {
98
219
    TRACE_SANITIZE (this);
99
100
219
    unsigned int count = ARRAY_LENGTH (mathValueRecords);
101
9.79k
    for (unsigned int i = 0; i < count; i++)
102
9.62k
      if (!mathValueRecords[i].sanitize (c, this))
103
45
  return_trace (false);
104
105
219
    return_trace (true);
106
219
  }
107
108
  bool sanitize (hb_sanitize_context_t *c) const
109
228
  {
110
228
    TRACE_SANITIZE (this);
111
228
    return_trace (c->check_struct (this) && sanitize_math_value_records (c));
112
228
  }
113
114
  hb_position_t get_value (hb_ot_math_constant_t constant,
115
         hb_font_t *font) const
116
1.87M
  {
117
1.87M
    switch (constant) {
118
119
33.5k
    case HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN:
120
67.0k
    case HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN:
121
67.0k
      return percentScaleDown[constant - HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN];
122
123
33.5k
    case HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT:
124
67.0k
    case HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT:
125
67.0k
      return font->em_scale_y (minHeight[constant - HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT]);
126
127
33.5k
    case HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE:
128
67.0k
    case HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE:
129
100k
    case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP:
130
134k
    case HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT:
131
134k
      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_x_value (font, this);
132
133
33.5k
    case HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT:
134
67.0k
    case HB_OT_MATH_CONSTANT_AXIS_HEIGHT:
135
100k
    case HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT:
136
134k
    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN:
137
167k
    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN:
138
201k
    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN:
139
234k
    case HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN:
140
268k
    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP:
141
301k
    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN:
142
335k
    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP:
143
368k
    case HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN:
144
402k
    case HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS:
145
435k
    case HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN:
146
469k
    case HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN:
147
502k
    case HB_OT_MATH_CONSTANT_MATH_LEADING:
148
536k
    case HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER:
149
570k
    case HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS:
150
603k
    case HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP:
151
637k
    case HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP:
152
670k
    case HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER:
153
704k
    case HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS:
154
737k
    case HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP:
155
771k
    case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP:
156
804k
    case HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN:
157
838k
    case HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN:
158
871k
    case HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN:
159
905k
    case HB_OT_MATH_CONSTANT_STACK_GAP_MIN:
160
938k
    case HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP:
161
972k
    case HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP:
162
1.00M
    case HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN:
163
1.03M
    case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN:
164
1.07M
    case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN:
165
1.10M
    case HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP:
166
1.14M
    case HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN:
167
1.17M
    case HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN:
168
1.20M
    case HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX:
169
1.24M
    case HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN:
170
1.27M
    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX:
171
1.30M
    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT:
172
1.34M
    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN:
173
1.37M
    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP:
174
1.40M
    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED:
175
1.44M
    case HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER:
176
1.47M
    case HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS:
177
1.50M
    case HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP:
178
1.54M
    case HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN:
179
1.57M
    case HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN:
180
1.57M
      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_y_value (font, this);
181
182
33.5k
    case HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT:
183
33.5k
      return radicalDegreeBottomRaisePercent;
184
185
0
    default:
186
0
      return 0;
187
1.87M
    }
188
1.87M
  }
189
190
  protected:
191
  HBINT16 percentScaleDown[2];
192
  HBUINT16 minHeight[2];
193
  MathValueRecord mathValueRecords[51];
194
  HBINT16 radicalDegreeBottomRaisePercent;
195
196
  public:
197
  DEFINE_SIZE_STATIC (214);
198
};
199
200
struct MathItalicsCorrectionInfo
201
{
202
  bool subset (hb_subset_context_t *c) const
203
0
  {
204
0
    TRACE_SUBSET (this);
205
0
    const hb_set_t &glyphset = c->plan->_glyphset_mathed;
206
0
    const hb_map_t &glyph_map = *c->plan->glyph_map;
207
0
208
0
    auto *out = c->serializer->start_embed (*this);
209
0
    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
210
0
211
0
    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
212
0
    + hb_zip (this+coverage, italicsCorrection)
213
0
    | hb_filter (glyphset, hb_first)
214
0
    | hb_filter (serialize_math_record_array (c->serializer, out->italicsCorrection, this), hb_second)
215
0
    | hb_map (hb_first)
216
0
    | hb_map (glyph_map)
217
0
    | hb_sink (new_coverage)
218
0
    ;
219
0
220
0
    out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
221
0
    return_trace (true);
222
0
  }
223
224
  bool sanitize (hb_sanitize_context_t *c) const
225
140
  {
226
140
    TRACE_SANITIZE (this);
227
140
    return_trace (c->check_struct (this) &&
228
140
      coverage.sanitize (c, this) &&
229
140
      italicsCorrection.sanitize (c, this));
230
140
  }
231
232
  hb_position_t get_value (hb_codepoint_t glyph,
233
         hb_font_t *font) const
234
33.5k
  {
235
33.5k
    unsigned int index = (this+coverage).get_coverage (glyph);
236
33.5k
    return italicsCorrection[index].get_x_value (font, this);
237
33.5k
  }
238
239
  protected:
240
  Offset16To<Coverage>       coverage;    /* Offset to Coverage table -
241
             * from the beginning of
242
             * MathItalicsCorrectionInfo
243
             * table. */
244
  Array16Of<MathValueRecord> italicsCorrection; /* Array of MathValueRecords
245
             * defining italics correction
246
             * values for each
247
             * covered glyph. */
248
249
  public:
250
  DEFINE_SIZE_ARRAY (4, italicsCorrection);
251
};
252
253
struct MathTopAccentAttachment
254
{
255
  bool subset (hb_subset_context_t *c) const
256
0
  {
257
0
    TRACE_SUBSET (this);
258
0
    const hb_set_t &glyphset = c->plan->_glyphset_mathed;
259
0
    const hb_map_t &glyph_map = *c->plan->glyph_map;
260
0
261
0
    auto *out = c->serializer->start_embed (*this);
262
0
    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
263
0
264
0
    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
265
0
    + hb_zip (this+topAccentCoverage, topAccentAttachment)
266
0
    | hb_filter (glyphset, hb_first)
267
0
    | hb_filter (serialize_math_record_array (c->serializer, out->topAccentAttachment, this), hb_second)
268
0
    | hb_map (hb_first)
269
0
    | hb_map (glyph_map)
270
0
    | hb_sink (new_coverage)
271
0
    ;
272
0
273
0
    out->topAccentCoverage.serialize_serialize (c->serializer, new_coverage.iter ());
274
0
    return_trace (true);
275
0
  }
276
277
  bool sanitize (hb_sanitize_context_t *c) const
278
135
  {
279
135
    TRACE_SANITIZE (this);
280
135
    return_trace (c->check_struct (this) &&
281
135
      topAccentCoverage.sanitize (c, this) &&
282
135
      topAccentAttachment.sanitize (c, this));
283
135
  }
284
285
  hb_position_t get_value (hb_codepoint_t glyph,
286
         hb_font_t *font) const
287
33.5k
  {
288
33.5k
    unsigned int index = (this+topAccentCoverage).get_coverage (glyph);
289
33.5k
    if (index == NOT_COVERED)
290
33.5k
      return font->get_glyph_h_advance (glyph) / 2;
291
4
    return topAccentAttachment[index].get_x_value (font, this);
292
33.5k
  }
293
294
  protected:
295
  Offset16To<Coverage>       topAccentCoverage;   /* Offset to Coverage table -
296
             * from the beginning of
297
             * MathTopAccentAttachment
298
             * table. */
299
  Array16Of<MathValueRecord> topAccentAttachment; /* Array of MathValueRecords
300
             * defining top accent
301
             * attachment points for each
302
             * covered glyph. */
303
304
  public:
305
  DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment);
306
};
307
308
struct MathKern
309
{
310
  MathKern* copy (hb_serialize_context_t *c) const
311
0
  {
312
0
    TRACE_SERIALIZE (this);
313
0
    auto *out = c->start_embed (this);
314
0
315
0
    if (unlikely (!c->embed (heightCount))) return_trace (nullptr);
316
0
317
0
    unsigned count = 2 * heightCount + 1;
318
0
    for (unsigned i = 0; i < count; i++)
319
0
      if (!c->copy (mathValueRecordsZ.arrayZ[i], this))
320
0
        return_trace (nullptr);
321
0
322
0
    return_trace (out);
323
0
  }
324
325
  bool sanitize_math_value_records (hb_sanitize_context_t *c) const
326
4.08k
  {
327
4.08k
    TRACE_SANITIZE (this);
328
4.08k
    unsigned int count = 2 * heightCount + 1;
329
3.11M
    for (unsigned int i = 0; i < count; i++)
330
3.11M
      if (!mathValueRecordsZ.arrayZ[i].sanitize (c, this)) return_trace (false);
331
4.08k
    return_trace (true);
332
4.08k
  }
333
334
  bool sanitize (hb_sanitize_context_t *c) const
335
4.09k
  {
336
4.09k
    TRACE_SANITIZE (this);
337
4.09k
    return_trace (c->check_struct (this) &&
338
4.09k
      hb_barrier () &&
339
4.09k
      c->check_array (mathValueRecordsZ.arrayZ, 2 * heightCount + 1) &&
340
4.09k
      sanitize_math_value_records (c));
341
4.09k
  }
342
343
  hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
344
33.5k
  {
345
33.5k
    const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
346
33.5k
    const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
347
33.5k
    int sign = font->y_scale < 0 ? -1 : +1;
348
349
    /* According to OpenType spec (v1.9), except for the boundary cases, the index
350
     * chosen for kern value should be i such that
351
     *    correctionHeight[i-1] <= correction_height < correctionHeight[i]
352
     * We can use the binary search algorithm of std::upper_bound(). Or, we can
353
     * use the internal hb_bsearch_impl.
354
     */
355
33.5k
    unsigned int pos;
356
33.5k
    auto cmp = +[](const void* key, const void* p,
357
33.5k
                   int sign, hb_font_t* font, const MathKern* mathKern) -> int {
358
65
      return sign * *(hb_position_t*)key - sign * ((MathValueRecord*)p)->get_y_value(font, mathKern);
359
65
    };
360
33.5k
    unsigned int i = hb_bsearch_impl(&pos, correction_height, correctionHeight,
361
33.5k
                                     heightCount, MathValueRecord::static_size,
362
33.5k
                                     cmp, sign, font, this) ? pos + 1 : pos;
363
33.5k
    return kernValue[i].get_x_value (font, this);
364
33.5k
  }
365
366
  unsigned int get_entries (unsigned int start_offset,
367
          unsigned int *entries_count, /* IN/OUT */
368
          hb_ot_math_kern_entry_t *kern_entries, /* OUT */
369
          hb_font_t *font) const
370
26
  {
371
26
    const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
372
26
    const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
373
26
    const unsigned int entriesCount = heightCount + 1;
374
375
26
    if (entries_count)
376
13
    {
377
13
      unsigned int start = hb_min (start_offset, entriesCount);
378
13
      unsigned int end = hb_min (start + *entries_count, entriesCount);
379
13
      *entries_count = end - start;
380
381
78
      for (unsigned int i = 0; i < *entries_count; i++) {
382
65
  unsigned int j = start + i;
383
384
65
  hb_position_t max_height;
385
65
  if (j == heightCount) {
386
0
    max_height = INT32_MAX;
387
65
  } else {
388
65
    max_height = correctionHeight[j].get_y_value (font, this);
389
65
  }
390
391
65
  kern_entries[i] = {max_height, kernValue[j].get_x_value (font, this)};
392
65
      }
393
13
    }
394
26
    return entriesCount;
395
26
  }
396
397
  protected:
398
  HBUINT16  heightCount;
399
  UnsizedArrayOf<MathValueRecord>
400
    mathValueRecordsZ;
401
        /* Array of correction heights at
402
         * which the kern value changes.
403
         * Sorted by the height value in
404
         * design units (heightCount entries),
405
         * Followed by:
406
         * Array of kern values corresponding
407
         * to heights. (heightCount+1 entries).
408
         */
409
410
  public:
411
  DEFINE_SIZE_ARRAY (2, mathValueRecordsZ);
412
};
413
414
struct MathKernInfoRecord
415
{
416
  MathKernInfoRecord* copy (hb_serialize_context_t *c, const void *base) const
417
0
  {
418
0
    TRACE_SERIALIZE (this);
419
0
    auto *out = c->embed (this);
420
0
    if (unlikely (!out)) return_trace (nullptr);
421
0
422
0
    unsigned count = ARRAY_LENGTH (mathKern);
423
0
    for (unsigned i = 0; i < count; i++)
424
0
      out->mathKern[i].serialize_copy (c, mathKern[i], base, 0, hb_serialize_context_t::Head);
425
0
426
0
    return_trace (out);
427
0
  }
428
429
  bool sanitize (hb_sanitize_context_t *c, const void *base) const
430
3.04k
  {
431
3.04k
    TRACE_SANITIZE (this);
432
433
3.04k
    unsigned int count = ARRAY_LENGTH (mathKern);
434
15.1k
    for (unsigned int i = 0; i < count; i++)
435
12.1k
      if (unlikely (!mathKern[i].sanitize (c, base)))
436
32
  return_trace (false);
437
438
3.04k
    return_trace (true);
439
3.04k
  }
440
441
  hb_position_t get_kerning (hb_ot_math_kern_t kern,
442
           hb_position_t correction_height,
443
           hb_font_t *font,
444
           const void *base) const
445
33.5k
  {
446
33.5k
    unsigned int idx = kern;
447
33.5k
    if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0;
448
33.5k
    return (base+mathKern[idx]).get_value (correction_height, font);
449
33.5k
  }
450
451
  unsigned int get_kernings (hb_ot_math_kern_t kern,
452
           unsigned int start_offset,
453
           unsigned int *entries_count, /* IN/OUT */
454
           hb_ot_math_kern_entry_t *kern_entries, /* OUT */
455
           hb_font_t *font,
456
           const void *base) const
457
67.0k
  {
458
67.0k
    unsigned int idx = kern;
459
67.0k
    if (unlikely (idx >= ARRAY_LENGTH (mathKern)) || !mathKern[idx]) {
460
67.0k
      if (entries_count) *entries_count = 0;
461
67.0k
      return 0;
462
67.0k
    }
463
26
    return (base+mathKern[idx]).get_entries (start_offset,
464
26
               entries_count,
465
26
               kern_entries,
466
26
               font);
467
67.0k
  }
468
469
  protected:
470
  /* Offset to MathKern table for each corner -
471
   * from the beginning of MathKernInfo table.  May be NULL. */
472
  Offset16To<MathKern> mathKern[4];
473
474
  public:
475
  DEFINE_SIZE_STATIC (8);
476
};
477
478
struct MathKernInfo
479
{
480
  bool subset (hb_subset_context_t *c) const
481
0
  {
482
0
    TRACE_SUBSET (this);
483
0
    const hb_set_t &glyphset = c->plan->_glyphset_mathed;
484
0
    const hb_map_t &glyph_map = *c->plan->glyph_map;
485
0
486
0
    auto *out = c->serializer->start_embed (*this);
487
0
    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
488
0
489
0
    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
490
0
    + hb_zip (this+mathKernCoverage, mathKernInfoRecords)
491
0
    | hb_filter (glyphset, hb_first)
492
0
    | hb_filter (serialize_math_record_array (c->serializer, out->mathKernInfoRecords, this), hb_second)
493
0
    | hb_map (hb_first)
494
0
    | hb_map (glyph_map)
495
0
    | hb_sink (new_coverage)
496
0
    ;
497
0
498
0
    out->mathKernCoverage.serialize_serialize (c->serializer, new_coverage.iter ());
499
0
    return_trace (true);
500
0
  }
501
502
  bool sanitize (hb_sanitize_context_t *c) const
503
108
  {
504
108
    TRACE_SANITIZE (this);
505
108
    return_trace (c->check_struct (this) &&
506
108
      mathKernCoverage.sanitize (c, this) &&
507
108
      mathKernInfoRecords.sanitize (c, this));
508
108
  }
509
510
  hb_position_t get_kerning (hb_codepoint_t glyph,
511
           hb_ot_math_kern_t kern,
512
           hb_position_t correction_height,
513
           hb_font_t *font) const
514
33.5k
  {
515
33.5k
    unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
516
33.5k
    return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this);
517
33.5k
  }
518
519
  unsigned int get_kernings (hb_codepoint_t glyph,
520
           hb_ot_math_kern_t kern,
521
           unsigned int start_offset,
522
           unsigned int *entries_count, /* IN/OUT */
523
           hb_ot_math_kern_entry_t *kern_entries, /* OUT */
524
           hb_font_t *font) const
525
67.0k
  {
526
67.0k
    unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
527
67.0k
    return mathKernInfoRecords[index].get_kernings (kern,
528
67.0k
                start_offset,
529
67.0k
                entries_count,
530
67.0k
                kern_entries,
531
67.0k
                font,
532
67.0k
                this);
533
67.0k
  }
534
535
  protected:
536
  Offset16To<Coverage>
537
    mathKernCoverage;
538
        /* Offset to Coverage table -
539
         * from the beginning of the
540
         * MathKernInfo table. */
541
  Array16Of<MathKernInfoRecord>
542
    mathKernInfoRecords;
543
        /* Array of MathKernInfoRecords,
544
         * per-glyph information for
545
         * mathematical positioning
546
         * of subscripts and
547
         * superscripts. */
548
549
  public:
550
  DEFINE_SIZE_ARRAY (4, mathKernInfoRecords);
551
};
552
553
struct MathGlyphInfo
554
{
555
  bool subset (hb_subset_context_t *c) const
556
0
  {
557
0
    TRACE_SUBSET (this);
558
0
    auto *out = c->serializer->embed (*this);
559
0
    if (unlikely (!out)) return_trace (false);
560
0
561
0
    out->mathItalicsCorrectionInfo.serialize_subset (c, mathItalicsCorrectionInfo, this);
562
0
    out->mathTopAccentAttachment.serialize_subset (c, mathTopAccentAttachment, this);
563
0
564
0
    const hb_set_t &glyphset = c->plan->_glyphset_mathed;
565
0
    const hb_map_t &glyph_map = *c->plan->glyph_map;
566
0
567
0
    auto it =
568
0
    + hb_iter (this+extendedShapeCoverage)
569
0
    | hb_take (c->plan->source->get_num_glyphs ())
570
0
    | hb_filter (glyphset)
571
0
    | hb_map_retains_sorting (glyph_map)
572
0
    ;
573
0
574
0
    if (it) out->extendedShapeCoverage.serialize_serialize (c->serializer, it);
575
0
    else out->extendedShapeCoverage = 0;
576
0
577
0
    out->mathKernInfo.serialize_subset (c, mathKernInfo, this);
578
0
    return_trace (true);
579
0
  }
580
581
  bool sanitize (hb_sanitize_context_t *c) const
582
244
  {
583
244
    TRACE_SANITIZE (this);
584
244
    return_trace (c->check_struct (this) &&
585
244
      mathItalicsCorrectionInfo.sanitize (c, this) &&
586
244
      mathTopAccentAttachment.sanitize (c, this) &&
587
244
      extendedShapeCoverage.sanitize (c, this) &&
588
244
      mathKernInfo.sanitize (c, this));
589
244
  }
590
591
  hb_position_t
592
  get_italics_correction (hb_codepoint_t  glyph, hb_font_t *font) const
593
33.5k
  { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); }
594
595
  hb_position_t
596
  get_top_accent_attachment (hb_codepoint_t  glyph, hb_font_t *font) const
597
33.5k
  { return (this+mathTopAccentAttachment).get_value (glyph, font); }
598
599
  bool is_extended_shape (hb_codepoint_t glyph) const
600
33.5k
  { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; }
601
602
  hb_position_t get_kerning (hb_codepoint_t glyph,
603
           hb_ot_math_kern_t kern,
604
           hb_position_t correction_height,
605
           hb_font_t *font) const
606
33.5k
  { return (this+mathKernInfo).get_kerning (glyph, kern, correction_height, font); }
607
608
  hb_position_t get_kernings (hb_codepoint_t glyph,
609
            hb_ot_math_kern_t kern,
610
            unsigned int start_offset,
611
            unsigned int *entries_count, /* IN/OUT */
612
            hb_ot_math_kern_entry_t *kern_entries, /* OUT */
613
            hb_font_t *font) const
614
67.0k
  { return (this+mathKernInfo).get_kernings (glyph,
615
67.0k
               kern,
616
67.0k
               start_offset,
617
67.0k
               entries_count,
618
67.0k
               kern_entries,
619
67.0k
               font); }
620
621
  protected:
622
  /* Offset to MathItalicsCorrectionInfo table -
623
   * from the beginning of MathGlyphInfo table. */
624
  Offset16To<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo;
625
626
  /* Offset to MathTopAccentAttachment table -
627
   * from the beginning of MathGlyphInfo table. */
628
  Offset16To<MathTopAccentAttachment> mathTopAccentAttachment;
629
630
  /* Offset to coverage table for Extended Shape glyphs -
631
   * from the beginning of MathGlyphInfo table. When the left or right glyph of
632
   * a box is an extended shape variant, the (ink) box (and not the default
633
   * position defined by values in MathConstants table) should be used for
634
   * vertical positioning purposes.  May be NULL.. */
635
  Offset16To<Coverage> extendedShapeCoverage;
636
637
   /* Offset to MathKernInfo table -
638
    * from the beginning of MathGlyphInfo table. */
639
  Offset16To<MathKernInfo> mathKernInfo;
640
641
  public:
642
  DEFINE_SIZE_STATIC (8);
643
};
644
645
struct MathGlyphVariantRecord
646
{
647
  friend struct MathGlyphConstruction;
648
649
  bool subset (hb_subset_context_t *c) const
650
0
  {
651
0
    TRACE_SUBSET (this);
652
0
    auto *out = c->serializer->embed (this);
653
0
    if (unlikely (!out)) return_trace (false);
654
0
655
0
    const hb_map_t& glyph_map = *c->plan->glyph_map;
656
0
    return_trace (c->serializer->check_assign (out->variantGlyph, glyph_map.get (variantGlyph), HB_SERIALIZE_ERROR_INT_OVERFLOW));
657
0
  }
658
659
  bool sanitize (hb_sanitize_context_t *c) const
660
0
  {
661
0
    TRACE_SANITIZE (this);
662
0
    return_trace (c->check_struct (this));
663
0
  }
664
665
  void closure_glyphs (hb_set_t *variant_glyphs) const
666
0
  { variant_glyphs->add (variantGlyph); }
667
668
  protected:
669
  HBGlyphID16 variantGlyph;       /* Glyph ID for the variant. */
670
  HBUINT16  advanceMeasurement; /* Advance width/height, in design units, of the
671
         * variant, in the direction of requested
672
         * glyph extension. */
673
674
  public:
675
  DEFINE_SIZE_STATIC (4);
676
};
677
678
struct PartFlags : HBUINT16
679
{
680
  enum Flags {
681
    Extender  = 0x0001u, /* If set, the part can be skipped or repeated. */
682
683
    Defined = 0x0001u, /* All defined flags. */
684
  };
685
686
  public:
687
  DEFINE_SIZE_STATIC (2);
688
};
689
690
struct MathGlyphPartRecord
691
{
692
  bool subset (hb_subset_context_t *c) const
693
0
  {
694
0
    TRACE_SUBSET (this);
695
0
    auto *out = c->serializer->embed (this);
696
0
    if (unlikely (!out)) return_trace (false);
697
0
698
0
    const hb_map_t& glyph_map = *c->plan->glyph_map;
699
0
    return_trace (c->serializer->check_assign (out->glyph, glyph_map.get (glyph), HB_SERIALIZE_ERROR_INT_OVERFLOW));
700
0
  }
701
702
  bool sanitize (hb_sanitize_context_t *c) const
703
0
  {
704
0
    TRACE_SANITIZE (this);
705
0
    return_trace (c->check_struct (this));
706
0
  }
707
708
  void extract (hb_ot_math_glyph_part_t &out,
709
    int64_t mult,
710
    hb_font_t *font) const
711
6
  {
712
6
    out.glyph     = glyph;
713
714
6
    out.start_connector_length  = font->em_mult (startConnectorLength, mult);
715
6
    out.end_connector_length  = font->em_mult (endConnectorLength, mult);
716
6
    out.full_advance    = font->em_mult (fullAdvance, mult);
717
718
6
    static_assert ((unsigned int) HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER ==
719
6
       (unsigned int) PartFlags::Extender, "");
720
721
6
    out.flags = (hb_ot_math_glyph_part_flags_t)
722
6
    (unsigned int)
723
6
    (partFlags & PartFlags::Defined);
724
6
  }
725
726
  void closure_glyphs (hb_set_t *variant_glyphs) const
727
0
  { variant_glyphs->add (glyph); }
728
729
  protected:
730
  HBGlyphID16 glyph;    /* Glyph ID for the part. */
731
  HBUINT16  startConnectorLength;
732
        /* Advance width/ height of the straight bar
733
         * connector material, in design units, is at
734
         * the beginning of the glyph, in the
735
         * direction of the extension. */
736
  HBUINT16  endConnectorLength;
737
        /* Advance width/ height of the straight bar
738
         * connector material, in design units, is at
739
         * the end of the glyph, in the direction of
740
         * the extension. */
741
  HBUINT16  fullAdvance;  /* Full advance width/height for this part,
742
         * in the direction of the extension.
743
         * In design units. */
744
  PartFlags partFlags;  /* Part qualifiers. */
745
746
  public:
747
  DEFINE_SIZE_STATIC (10);
748
};
749
750
struct MathGlyphAssembly
751
{
752
  bool subset (hb_subset_context_t *c) const
753
0
  {
754
0
    TRACE_SUBSET (this);
755
0
756
0
    if (!c->serializer->copy (italicsCorrection, this)) return_trace (false);
757
0
    if (!c->serializer->copy<HBUINT16> (partRecords.len)) return_trace (false);
758
0
759
0
    for (const auto& record : partRecords.iter ())
760
0
      if (!record.subset (c)) return_trace (false);
761
0
    return_trace (true);
762
0
  }
763
764
  bool sanitize (hb_sanitize_context_t *c) const
765
1.82k
  {
766
1.82k
    TRACE_SANITIZE (this);
767
1.82k
    return_trace (c->check_struct (this) &&
768
1.82k
      italicsCorrection.sanitize (c, this) &&
769
1.82k
      partRecords.sanitize (c));
770
1.82k
  }
771
772
  unsigned int get_parts (hb_direction_t direction,
773
        hb_font_t *font,
774
        unsigned int start_offset,
775
        unsigned int *parts_count, /* IN/OUT */
776
        hb_ot_math_glyph_part_t *parts /* OUT */,
777
        hb_position_t *italics_correction /* OUT */) const
778
100k
  {
779
100k
    if (parts_count)
780
33.5k
    {
781
33.5k
      int64_t mult = font->dir_mult (direction);
782
33.5k
      for (auto _ : hb_zip (partRecords.as_array ().sub_array (start_offset, parts_count),
783
33.5k
          hb_array (parts, *parts_count)))
784
6
  _.first.extract (_.second, mult, font);
785
33.5k
    }
786
787
100k
    if (italics_correction)
788
33.5k
      *italics_correction = italicsCorrection.get_x_value (font, this);
789
790
100k
    return partRecords.len;
791
100k
  }
792
793
  void closure_glyphs (hb_set_t *variant_glyphs) const
794
0
  {
795
0
    for (const auto& _ : partRecords.iter ())
796
0
      _.closure_glyphs (variant_glyphs);
797
0
  }
798
799
  protected:
800
  MathValueRecord
801
    italicsCorrection;
802
        /* Italics correction of this
803
         * MathGlyphAssembly. Should not
804
         * depend on the assembly size. */
805
  Array16Of<MathGlyphPartRecord>
806
    partRecords;  /* Array of part records, from
807
         * left to right and bottom to
808
         * top. */
809
810
  public:
811
  DEFINE_SIZE_ARRAY (6, partRecords);
812
};
813
814
struct MathGlyphConstruction
815
{
816
  bool subset (hb_subset_context_t *c) const
817
0
  {
818
0
    TRACE_SUBSET (this);
819
0
    auto *out = c->serializer->start_embed (*this);
820
0
    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
821
0
822
0
    out->glyphAssembly.serialize_subset (c, glyphAssembly, this);
823
0
824
0
    if (!c->serializer->check_assign (out->mathGlyphVariantRecord.len, mathGlyphVariantRecord.len, HB_SERIALIZE_ERROR_INT_OVERFLOW))
825
0
      return_trace (false);
826
0
    for (const auto& record : mathGlyphVariantRecord.iter ())
827
0
      if (!record.subset (c)) return_trace (false);
828
0
829
0
    return_trace (true);
830
0
  }
831
832
  bool sanitize (hb_sanitize_context_t *c) const
833
3.87k
  {
834
3.87k
    TRACE_SANITIZE (this);
835
3.87k
    return_trace (c->check_struct (this) &&
836
3.87k
      glyphAssembly.sanitize (c, this) &&
837
3.87k
      mathGlyphVariantRecord.sanitize (c));
838
3.87k
  }
839
840
100k
  const MathGlyphAssembly &get_assembly () const { return this+glyphAssembly; }
841
842
  unsigned int get_variants (hb_direction_t direction,
843
           hb_font_t *font,
844
           unsigned int start_offset,
845
           unsigned int *variants_count, /* IN/OUT */
846
           hb_ot_math_glyph_variant_t *variants /* OUT */) const
847
100k
  {
848
100k
    if (variants_count)
849
33.5k
    {
850
33.5k
      int64_t mult = font->dir_mult (direction);
851
33.5k
      for (auto _ : hb_zip (mathGlyphVariantRecord.as_array ().sub_array (start_offset, variants_count),
852
33.5k
          hb_array (variants, *variants_count)))
853
7
  _.second = {_.first.variantGlyph, font->em_mult (_.first.advanceMeasurement, mult)};
854
33.5k
    }
855
100k
    return mathGlyphVariantRecord.len;
856
100k
  }
857
858
  void closure_glyphs (hb_set_t *variant_glyphs) const
859
0
  {
860
0
    (this+glyphAssembly).closure_glyphs (variant_glyphs);
861
0
862
0
    for (const auto& _ : mathGlyphVariantRecord.iter ())
863
0
      _.closure_glyphs (variant_glyphs);
864
0
  }
865
866
  protected:
867
  /* Offset to MathGlyphAssembly table for this shape - from the beginning of
868
     MathGlyphConstruction table.  May be NULL. */
869
  Offset16To<MathGlyphAssembly>   glyphAssembly;
870
871
  /* MathGlyphVariantRecords for alternative variants of the glyphs. */
872
  Array16Of<MathGlyphVariantRecord> mathGlyphVariantRecord;
873
874
  public:
875
  DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord);
876
};
877
878
struct MathVariants
879
{
880
  void closure_glyphs (const hb_set_t *glyph_set,
881
                       hb_set_t *variant_glyphs) const
882
0
  {
883
0
    const hb_array_t<const Offset16To<MathGlyphConstruction>> glyph_construction_offsets = glyphConstruction.as_array (vertGlyphCount + horizGlyphCount);
884
0
885
0
    if (vertGlyphCoverage)
886
0
    {
887
0
      const auto vert_offsets = glyph_construction_offsets.sub_array (0, vertGlyphCount);
888
0
      + hb_zip (this+vertGlyphCoverage, vert_offsets)
889
0
      | hb_filter (glyph_set, hb_first)
890
0
      | hb_map (hb_second)
891
0
      | hb_map (hb_add (this))
892
0
      | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); })
893
0
      ;
894
0
    }
895
0
896
0
    if (horizGlyphCoverage)
897
0
    {
898
0
      const auto hori_offsets = glyph_construction_offsets.sub_array (vertGlyphCount, horizGlyphCount);
899
0
      + hb_zip (this+horizGlyphCoverage, hori_offsets)
900
0
      | hb_filter (glyph_set, hb_first)
901
0
      | hb_map (hb_second)
902
0
      | hb_map (hb_add (this))
903
0
      | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); })
904
0
      ;
905
0
    }
906
0
  }
907
908
  void collect_coverage_and_indices (hb_sorted_vector_t<hb_codepoint_t>& new_coverage,
909
                                     const Offset16To<Coverage>& coverage,
910
                                     unsigned i,
911
                                     unsigned end_index,
912
                                     hb_set_t& indices,
913
                                     const hb_set_t& glyphset,
914
                                     const hb_map_t& glyph_map) const
915
0
  {
916
0
    if (!coverage) return;
917
0
918
0
    for (const auto _ : (this+coverage).iter ())
919
0
    {
920
0
      if (i >= end_index) return;
921
0
      if (glyphset.has (_))
922
0
      {
923
0
        unsigned new_gid = glyph_map.get (_);
924
0
        new_coverage.push (new_gid);
925
0
        indices.add (i);
926
0
      }
927
0
      i++;
928
0
    }
929
0
  }
930
931
  bool subset (hb_subset_context_t *c) const
932
0
  {
933
0
    TRACE_SUBSET (this);
934
0
    const hb_set_t &glyphset = c->plan->_glyphset_mathed;
935
0
    const hb_map_t &glyph_map = *c->plan->glyph_map;
936
0
937
0
    auto *out = c->serializer->start_embed (*this);
938
0
    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
939
0
    if (!c->serializer->check_assign (out->minConnectorOverlap, minConnectorOverlap, HB_SERIALIZE_ERROR_INT_OVERFLOW))
940
0
      return_trace (false);
941
0
942
0
    hb_sorted_vector_t<hb_codepoint_t> new_vert_coverage;
943
0
    hb_sorted_vector_t<hb_codepoint_t> new_hori_coverage;
944
0
    hb_set_t indices;
945
0
    collect_coverage_and_indices (new_vert_coverage, vertGlyphCoverage, 0, vertGlyphCount, indices, glyphset, glyph_map);
946
0
    collect_coverage_and_indices (new_hori_coverage, horizGlyphCoverage, vertGlyphCount, vertGlyphCount + horizGlyphCount, indices, glyphset, glyph_map);
947
0
948
0
    if (!c->serializer->check_assign (out->vertGlyphCount, new_vert_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
949
0
      return_trace (false);
950
0
    if (!c->serializer->check_assign (out->horizGlyphCount, new_hori_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
951
0
      return_trace (false);
952
0
953
0
    for (unsigned i : indices.iter ())
954
0
    {
955
0
      auto *o = c->serializer->embed (glyphConstruction[i]);
956
0
      if (!o) return_trace (false);
957
0
      o->serialize_subset (c, glyphConstruction[i], this);
958
0
    }
959
0
960
0
    if (new_vert_coverage)
961
0
      out->vertGlyphCoverage.serialize_serialize (c->serializer, new_vert_coverage.iter ());
962
0
963
0
    if (new_hori_coverage)
964
0
    out->horizGlyphCoverage.serialize_serialize (c->serializer, new_hori_coverage.iter ());
965
0
    return_trace (true);
966
0
  }
967
968
  bool sanitize_offsets (hb_sanitize_context_t *c) const
969
143
  {
970
143
    TRACE_SANITIZE (this);
971
143
    unsigned int count = vertGlyphCount + horizGlyphCount;
972
9.84k
    for (unsigned int i = 0; i < count; i++)
973
9.74k
      if (!glyphConstruction.arrayZ[i].sanitize (c, this)) return_trace (false);
974
143
    return_trace (true);
975
143
  }
976
977
  bool sanitize (hb_sanitize_context_t *c) const
978
162
  {
979
162
    TRACE_SANITIZE (this);
980
162
    return_trace (c->check_struct (this) &&
981
162
      vertGlyphCoverage.sanitize (c, this) &&
982
162
      horizGlyphCoverage.sanitize (c, this) &&
983
162
      hb_barrier () &&
984
162
      c->check_array (glyphConstruction.arrayZ, vertGlyphCount + horizGlyphCount) &&
985
162
      sanitize_offsets (c));
986
162
  }
987
988
  hb_position_t get_min_connector_overlap (hb_direction_t direction,
989
              hb_font_t *font) const
990
33.5k
  { return font->em_scale_dir (minConnectorOverlap, direction); }
991
992
  unsigned int get_glyph_variants (hb_codepoint_t glyph,
993
           hb_direction_t direction,
994
           hb_font_t *font,
995
           unsigned int start_offset,
996
           unsigned int *variants_count, /* IN/OUT */
997
           hb_ot_math_glyph_variant_t *variants /* OUT */) const
998
100k
  { return get_glyph_construction (glyph, direction, font)
999
100k
     .get_variants (direction, font, start_offset, variants_count, variants); }
1000
1001
  unsigned int get_glyph_parts (hb_codepoint_t glyph,
1002
        hb_direction_t direction,
1003
        hb_font_t *font,
1004
        unsigned int start_offset,
1005
        unsigned int *parts_count, /* IN/OUT */
1006
        hb_ot_math_glyph_part_t *parts /* OUT */,
1007
        hb_position_t *italics_correction /* OUT */) const
1008
100k
  { return get_glyph_construction (glyph, direction, font)
1009
100k
     .get_assembly ()
1010
100k
     .get_parts (direction, font,
1011
100k
           start_offset, parts_count, parts,
1012
100k
           italics_correction); }
1013
1014
  private:
1015
  const MathGlyphConstruction &
1016
  get_glyph_construction (hb_codepoint_t glyph,
1017
        hb_direction_t direction,
1018
        hb_font_t *font HB_UNUSED) const
1019
201k
  {
1020
201k
    bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
1021
201k
    unsigned int count = vertical ? vertGlyphCount : horizGlyphCount;
1022
201k
    const Offset16To<Coverage> &coverage = vertical ? vertGlyphCoverage
1023
201k
              : horizGlyphCoverage;
1024
1025
201k
    unsigned int index = (this+coverage).get_coverage (glyph);
1026
201k
    if (unlikely (index >= count)) return Null (MathGlyphConstruction);
1027
1028
26
    if (!vertical)
1029
20
      index += vertGlyphCount;
1030
1031
26
    return this+glyphConstruction[index];
1032
201k
  }
1033
1034
  protected:
1035
  HBUINT16  minConnectorOverlap;
1036
        /* Minimum overlap of connecting
1037
         * glyphs during glyph construction,
1038
         * in design units. */
1039
  Offset16To<Coverage> vertGlyphCoverage;
1040
        /* Offset to Coverage table -
1041
         * from the beginning of MathVariants
1042
         * table. */
1043
  Offset16To<Coverage> horizGlyphCoverage;
1044
        /* Offset to Coverage table -
1045
         * from the beginning of MathVariants
1046
         * table. */
1047
  HBUINT16  vertGlyphCount; /* Number of glyphs for which
1048
         * information is provided for
1049
         * vertically growing variants. */
1050
  HBUINT16  horizGlyphCount;/* Number of glyphs for which
1051
         * information is provided for
1052
         * horizontally growing variants. */
1053
1054
  /* Array of offsets to MathGlyphConstruction tables - from the beginning of
1055
     the MathVariants table, for shapes growing in vertical/horizontal
1056
     direction. */
1057
  UnsizedArrayOf<Offset16To<MathGlyphConstruction>>
1058
      glyphConstruction;
1059
1060
  public:
1061
  DEFINE_SIZE_ARRAY (10, glyphConstruction);
1062
};
1063
1064
1065
/*
1066
 * MATH -- Mathematical typesetting
1067
 * https://docs.microsoft.com/en-us/typography/opentype/spec/math
1068
 */
1069
1070
struct MATH
1071
{
1072
  static constexpr hb_tag_t tableTag = HB_OT_TAG_MATH;
1073
1074
33.5k
  bool has_data () const { return version.to_int (); }
1075
1076
  void closure_glyphs (hb_set_t *glyph_set) const
1077
0
  {
1078
0
    if (mathVariants)
1079
0
    {
1080
0
      hb_set_t variant_glyphs;
1081
0
      (this+mathVariants).closure_glyphs (glyph_set, &variant_glyphs);
1082
0
      hb_set_union (glyph_set, &variant_glyphs);
1083
0
    }
1084
0
  }
1085
1086
  bool subset (hb_subset_context_t *c) const
1087
0
  {
1088
0
    TRACE_SUBSET (this);
1089
0
    auto *out = c->serializer->embed (*this);
1090
0
    if (unlikely (!out)) return_trace (false);
1091
0
1092
0
    out->mathConstants.serialize_copy (c->serializer, mathConstants, this, 0, hb_serialize_context_t::Head);
1093
0
    out->mathGlyphInfo.serialize_subset (c, mathGlyphInfo, this);
1094
0
    out->mathVariants.serialize_subset (c, mathVariants, this);
1095
0
    return_trace (true);
1096
0
  }
1097
1098
  bool sanitize (hb_sanitize_context_t *c) const
1099
367
  {
1100
367
    TRACE_SANITIZE (this);
1101
367
    return_trace (version.sanitize (c) &&
1102
367
      likely (version.major == 1) &&
1103
367
      hb_barrier () &&
1104
367
      mathConstants.sanitize (c, this) &&
1105
367
      mathGlyphInfo.sanitize (c, this) &&
1106
367
      mathVariants.sanitize (c, this));
1107
367
  }
1108
1109
  // https://github.com/harfbuzz/harfbuzz/issues/4653
1110
  HB_INTERNAL bool is_bad_cambria (hb_font_t *font) const
1111
67.0k
  {
1112
67.0k
#ifndef HB_NO_MATH
1113
67.0k
    switch HB_CODEPOINT_ENCODE3 (font->face->table.MATH.get_blob ()->length,
1114
67.0k
                                 (this+mathConstants).minHeight[1], // displayOperatorMinHeight
1115
                                 (this+mathConstants).minHeight[0]) // delimitedSubFormulaMinHeight
1116
67.0k
    {
1117
      /* sha1sum:ab4a4fe054d23061f3c039493d6f665cfda2ecf5  cambria.ttc
1118
       * sha1sum:086855301bff644f9d8827b88491fcf73a6d4cb9  cambria.ttc
1119
       * sha1sum:b1e5a3feaca2ea3dfcf79ccb377de749ecf60343  cambria.ttc */
1120
2
      case HB_CODEPOINT_ENCODE3 (25722, 2500, 3000):
1121
2
        return true;
1122
67.0k
    }
1123
67.0k
#endif
1124
67.0k
    return false;
1125
67.0k
  }
1126
1127
  hb_position_t get_constant (hb_ot_math_constant_t  constant,
1128
            hb_font_t      *font) const
1129
1.87M
  { return (this+mathConstants).get_value (constant, font); }
1130
1131
201k
  const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; }
1132
1133
234k
  const MathVariants &get_variants () const    { return this+mathVariants; }
1134
1135
  protected:
1136
  FixedVersion<>version;  /* Version of the MATH table
1137
         * initially set to 0x00010000u */
1138
  Offset16To<MathConstants>
1139
    mathConstants;  /* MathConstants table */
1140
  Offset16To<MathGlyphInfo>
1141
    mathGlyphInfo;  /* MathGlyphInfo table */
1142
  Offset16To<MathVariants>
1143
    mathVariants; /* MathVariants table */
1144
1145
  public:
1146
  DEFINE_SIZE_STATIC (10);
1147
};
1148
1149
} /* namespace OT */
1150
1151
1152
#endif /* HB_OT_MATH_TABLE_HH */