tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

hb-ot-math-table.hh (38094B)


      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  { 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  { 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  {
     46    TRACE_SERIALIZE (this);
     47    auto *out = c->embed (this);
     48    if (unlikely (!out)) return_trace (nullptr);
     49    out->deviceTable.serialize_copy (c, deviceTable, base, 0, hb_serialize_context_t::Head);
     50 
     51    return_trace (out);
     52  }
     53 
     54  bool sanitize (hb_sanitize_context_t *c, const void *base) const
     55  {
     56    TRACE_SANITIZE (this);
     57    return_trace (c->check_struct (this) && deviceTable.sanitize (c, base));
     58  }
     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  {
     76    TRACE_SERIALIZE (this);
     77    auto *out = c->start_embed (this);
     78 
     79    HBINT16 *p = c->allocate_size<HBINT16> (HBINT16::static_size * 2);
     80    if (unlikely (!p)) return_trace (nullptr);
     81    hb_memcpy (p, percentScaleDown, HBINT16::static_size * 2);
     82 
     83    HBUINT16 *m = c->allocate_size<HBUINT16> (HBUINT16::static_size * 2);
     84    if (unlikely (!m)) return_trace (nullptr);
     85    hb_memcpy (m, minHeight, HBUINT16::static_size * 2);
     86 
     87    unsigned count = ARRAY_LENGTH (mathValueRecords);
     88    for (unsigned i = 0; i < count; i++)
     89      if (!c->copy (mathValueRecords[i], this))
     90        return_trace (nullptr);
     91 
     92    if (!c->embed (radicalDegreeBottomRaisePercent)) return_trace (nullptr);
     93    return_trace (out);
     94  }
     95 
     96  bool sanitize_math_value_records (hb_sanitize_context_t *c) const
     97  {
     98    TRACE_SANITIZE (this);
     99 
    100    unsigned int count = ARRAY_LENGTH (mathValueRecords);
    101    for (unsigned int i = 0; i < count; i++)
    102      if (!mathValueRecords[i].sanitize (c, this))
    103 return_trace (false);
    104 
    105    return_trace (true);
    106  }
    107 
    108  bool sanitize (hb_sanitize_context_t *c) const
    109  {
    110    TRACE_SANITIZE (this);
    111    return_trace (c->check_struct (this) && sanitize_math_value_records (c));
    112  }
    113 
    114  hb_position_t get_value (hb_ot_math_constant_t constant,
    115 		   hb_font_t *font) const
    116  {
    117    switch (constant) {
    118 
    119    case HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN:
    120    case HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN:
    121      return percentScaleDown[constant - HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN];
    122 
    123    case HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT:
    124    case HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT:
    125      return font->em_scale_y (minHeight[constant - HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT]);
    126 
    127    case HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE:
    128    case HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE:
    129    case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP:
    130    case HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT:
    131      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_x_value (font, this);
    132 
    133    case HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT:
    134    case HB_OT_MATH_CONSTANT_AXIS_HEIGHT:
    135    case HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT:
    136    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN:
    137    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN:
    138    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN:
    139    case HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN:
    140    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP:
    141    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN:
    142    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP:
    143    case HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN:
    144    case HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS:
    145    case HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN:
    146    case HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN:
    147    case HB_OT_MATH_CONSTANT_MATH_LEADING:
    148    case HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER:
    149    case HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS:
    150    case HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP:
    151    case HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP:
    152    case HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER:
    153    case HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS:
    154    case HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP:
    155    case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP:
    156    case HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN:
    157    case HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN:
    158    case HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN:
    159    case HB_OT_MATH_CONSTANT_STACK_GAP_MIN:
    160    case HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP:
    161    case HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP:
    162    case HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN:
    163    case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN:
    164    case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN:
    165    case HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP:
    166    case HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN:
    167    case HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN:
    168    case HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX:
    169    case HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN:
    170    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX:
    171    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT:
    172    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN:
    173    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP:
    174    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED:
    175    case HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER:
    176    case HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS:
    177    case HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP:
    178    case HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN:
    179    case HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN:
    180      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_y_value (font, this);
    181 
    182    case HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT:
    183      return radicalDegreeBottomRaisePercent;
    184 
    185    default:
    186      return 0;
    187    }
    188  }
    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  {
    204    TRACE_SUBSET (this);
    205    const hb_set_t &glyphset = c->plan->_glyphset_mathed;
    206    const hb_map_t &glyph_map = *c->plan->glyph_map;
    207 
    208    auto *out = c->serializer->start_embed (*this);
    209    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
    210 
    211    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
    212    + hb_zip (this+coverage, italicsCorrection)
    213    | hb_filter (glyphset, hb_first)
    214    | hb_filter (serialize_math_record_array (c->serializer, out->italicsCorrection, this), hb_second)
    215    | hb_map (hb_first)
    216    | hb_map (glyph_map)
    217    | hb_sink (new_coverage)
    218    ;
    219 
    220    out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
    221    return_trace (true);
    222  }
    223 
    224  bool sanitize (hb_sanitize_context_t *c) const
    225  {
    226    TRACE_SANITIZE (this);
    227    return_trace (c->check_struct (this) &&
    228 	  coverage.sanitize (c, this) &&
    229 	  italicsCorrection.sanitize (c, this));
    230  }
    231 
    232  hb_position_t get_value (hb_codepoint_t glyph,
    233 		   hb_font_t *font) const
    234  {
    235    unsigned int index = (this+coverage).get_coverage (glyph);
    236    return italicsCorrection[index].get_x_value (font, this);
    237  }
    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  {
    257    TRACE_SUBSET (this);
    258    const hb_set_t &glyphset = c->plan->_glyphset_mathed;
    259    const hb_map_t &glyph_map = *c->plan->glyph_map;
    260 
    261    auto *out = c->serializer->start_embed (*this);
    262    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
    263 
    264    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
    265    + hb_zip (this+topAccentCoverage, topAccentAttachment)
    266    | hb_filter (glyphset, hb_first)
    267    | hb_filter (serialize_math_record_array (c->serializer, out->topAccentAttachment, this), hb_second)
    268    | hb_map (hb_first)
    269    | hb_map (glyph_map)
    270    | hb_sink (new_coverage)
    271    ;
    272 
    273    out->topAccentCoverage.serialize_serialize (c->serializer, new_coverage.iter ());
    274    return_trace (true);
    275  }
    276 
    277  bool sanitize (hb_sanitize_context_t *c) const
    278  {
    279    TRACE_SANITIZE (this);
    280    return_trace (c->check_struct (this) &&
    281 	  topAccentCoverage.sanitize (c, this) &&
    282 	  topAccentAttachment.sanitize (c, this));
    283  }
    284 
    285  hb_position_t get_value (hb_codepoint_t glyph,
    286 		   hb_font_t *font) const
    287  {
    288    unsigned int index = (this+topAccentCoverage).get_coverage (glyph);
    289    if (index == NOT_COVERED)
    290      return font->get_glyph_h_advance (glyph) / 2;
    291    return topAccentAttachment[index].get_x_value (font, this);
    292  }
    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  {
    312    TRACE_SERIALIZE (this);
    313    auto *out = c->start_embed (this);
    314 
    315    if (unlikely (!c->embed (heightCount))) return_trace (nullptr);
    316 
    317    unsigned count = 2 * heightCount + 1;
    318    for (unsigned i = 0; i < count; i++)
    319      if (!c->copy (mathValueRecordsZ.arrayZ[i], this))
    320        return_trace (nullptr);
    321 
    322    return_trace (out);
    323  }
    324 
    325  bool sanitize_math_value_records (hb_sanitize_context_t *c) const
    326  {
    327    TRACE_SANITIZE (this);
    328    unsigned int count = 2 * heightCount + 1;
    329    for (unsigned int i = 0; i < count; i++)
    330      if (!mathValueRecordsZ.arrayZ[i].sanitize (c, this)) return_trace (false);
    331    return_trace (true);
    332  }
    333 
    334  bool sanitize (hb_sanitize_context_t *c) const
    335  {
    336    TRACE_SANITIZE (this);
    337    return_trace (c->check_struct (this) &&
    338 	  hb_barrier () &&
    339 	  c->check_array (mathValueRecordsZ.arrayZ, 2 * heightCount + 1) &&
    340 	  sanitize_math_value_records (c));
    341  }
    342 
    343  hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
    344  {
    345    const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
    346    const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
    347    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    unsigned int pos;
    356    auto cmp = +[](const void* key, const void* p,
    357                   int sign, hb_font_t* font, const MathKern* mathKern) -> int {
    358      return sign * *(hb_position_t*)key - sign * ((MathValueRecord*)p)->get_y_value(font, mathKern);
    359    };
    360    unsigned int i = hb_bsearch_impl(&pos, correction_height, correctionHeight,
    361                                     heightCount, MathValueRecord::static_size,
    362                                     cmp, sign, font, this) ? pos + 1 : pos;
    363    return kernValue[i].get_x_value (font, this);
    364  }
    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  {
    371    const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
    372    const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
    373    const unsigned int entriesCount = heightCount + 1;
    374 
    375    if (entries_count)
    376    {
    377      unsigned int start = hb_min (start_offset, entriesCount);
    378      unsigned int end = hb_min (start + *entries_count, entriesCount);
    379      *entries_count = end - start;
    380 
    381      for (unsigned int i = 0; i < *entries_count; i++) {
    382 unsigned int j = start + i;
    383 
    384 hb_position_t max_height;
    385 if (j == heightCount) {
    386   max_height = INT32_MAX;
    387 } else {
    388   max_height = correctionHeight[j].get_y_value (font, this);
    389 }
    390 
    391 kern_entries[i] = {max_height, kernValue[j].get_x_value (font, this)};
    392      }
    393    }
    394    return entriesCount;
    395  }
    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  {
    418    TRACE_SERIALIZE (this);
    419    auto *out = c->embed (this);
    420    if (unlikely (!out)) return_trace (nullptr);
    421 
    422    unsigned count = ARRAY_LENGTH (mathKern);
    423    for (unsigned i = 0; i < count; i++)
    424      out->mathKern[i].serialize_copy (c, mathKern[i], base, 0, hb_serialize_context_t::Head);
    425 
    426    return_trace (out);
    427  }
    428 
    429  bool sanitize (hb_sanitize_context_t *c, const void *base) const
    430  {
    431    TRACE_SANITIZE (this);
    432 
    433    unsigned int count = ARRAY_LENGTH (mathKern);
    434    for (unsigned int i = 0; i < count; i++)
    435      if (unlikely (!mathKern[i].sanitize (c, base)))
    436 return_trace (false);
    437 
    438    return_trace (true);
    439  }
    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  {
    446    unsigned int idx = kern;
    447    if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0;
    448    return (base+mathKern[idx]).get_value (correction_height, font);
    449  }
    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  {
    458    unsigned int idx = kern;
    459    if (unlikely (idx >= ARRAY_LENGTH (mathKern)) || !mathKern[idx]) {
    460      if (entries_count) *entries_count = 0;
    461      return 0;
    462    }
    463    return (base+mathKern[idx]).get_entries (start_offset,
    464 				     entries_count,
    465 				     kern_entries,
    466 				     font);
    467  }
    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  {
    482    TRACE_SUBSET (this);
    483    const hb_set_t &glyphset = c->plan->_glyphset_mathed;
    484    const hb_map_t &glyph_map = *c->plan->glyph_map;
    485 
    486    auto *out = c->serializer->start_embed (*this);
    487    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
    488 
    489    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
    490    + hb_zip (this+mathKernCoverage, mathKernInfoRecords)
    491    | hb_filter (glyphset, hb_first)
    492    | hb_filter (serialize_math_record_array (c->serializer, out->mathKernInfoRecords, this), hb_second)
    493    | hb_map (hb_first)
    494    | hb_map (glyph_map)
    495    | hb_sink (new_coverage)
    496    ;
    497 
    498    out->mathKernCoverage.serialize_serialize (c->serializer, new_coverage.iter ());
    499    return_trace (true);
    500  }
    501 
    502  bool sanitize (hb_sanitize_context_t *c) const
    503  {
    504    TRACE_SANITIZE (this);
    505    return_trace (c->check_struct (this) &&
    506 	  mathKernCoverage.sanitize (c, this) &&
    507 	  mathKernInfoRecords.sanitize (c, this));
    508  }
    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  {
    515    unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
    516    return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this);
    517  }
    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  {
    526    unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
    527    return mathKernInfoRecords[index].get_kernings (kern,
    528 					    start_offset,
    529 					    entries_count,
    530 					    kern_entries,
    531 					    font,
    532 					    this);
    533  }
    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  {
    557    TRACE_SUBSET (this);
    558    auto *out = c->serializer->embed (*this);
    559    if (unlikely (!out)) return_trace (false);
    560 
    561    out->mathItalicsCorrectionInfo.serialize_subset (c, mathItalicsCorrectionInfo, this);
    562    out->mathTopAccentAttachment.serialize_subset (c, mathTopAccentAttachment, this);
    563 
    564    const hb_set_t &glyphset = c->plan->_glyphset_mathed;
    565    const hb_map_t &glyph_map = *c->plan->glyph_map;
    566 
    567    auto it =
    568    + hb_iter (this+extendedShapeCoverage)
    569    | hb_take (c->plan->source->get_num_glyphs ())
    570    | hb_filter (glyphset)
    571    | hb_map_retains_sorting (glyph_map)
    572    ;
    573 
    574    if (it) out->extendedShapeCoverage.serialize_serialize (c->serializer, it);
    575    else out->extendedShapeCoverage = 0;
    576 
    577    out->mathKernInfo.serialize_subset (c, mathKernInfo, this);
    578    return_trace (true);
    579  }
    580 
    581  bool sanitize (hb_sanitize_context_t *c) const
    582  {
    583    TRACE_SANITIZE (this);
    584    return_trace (c->check_struct (this) &&
    585 	  mathItalicsCorrectionInfo.sanitize (c, this) &&
    586 	  mathTopAccentAttachment.sanitize (c, this) &&
    587 	  extendedShapeCoverage.sanitize (c, this) &&
    588 	  mathKernInfo.sanitize (c, this));
    589  }
    590 
    591  hb_position_t
    592  get_italics_correction (hb_codepoint_t  glyph, hb_font_t *font) const
    593  { 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  { return (this+mathTopAccentAttachment).get_value (glyph, font); }
    598 
    599  bool is_extended_shape (hb_codepoint_t glyph) const
    600  { 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  { 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  { return (this+mathKernInfo).get_kernings (glyph,
    615 				     kern,
    616 				     start_offset,
    617 				     entries_count,
    618 				     kern_entries,
    619 				     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  {
    651    TRACE_SUBSET (this);
    652    auto *out = c->serializer->embed (this);
    653    if (unlikely (!out)) return_trace (false);
    654 
    655    const hb_map_t& glyph_map = *c->plan->glyph_map;
    656    return_trace (c->serializer->check_assign (out->variantGlyph, glyph_map.get (variantGlyph), HB_SERIALIZE_ERROR_INT_OVERFLOW));
    657  }
    658 
    659  bool sanitize (hb_sanitize_context_t *c) const
    660  {
    661    TRACE_SANITIZE (this);
    662    return_trace (c->check_struct (this));
    663  }
    664 
    665  void closure_glyphs (hb_set_t *variant_glyphs) const
    666  { 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  {
    694    TRACE_SUBSET (this);
    695    auto *out = c->serializer->embed (this);
    696    if (unlikely (!out)) return_trace (false);
    697 
    698    const hb_map_t& glyph_map = *c->plan->glyph_map;
    699    return_trace (c->serializer->check_assign (out->glyph, glyph_map.get (glyph), HB_SERIALIZE_ERROR_INT_OVERFLOW));
    700  }
    701 
    702  bool sanitize (hb_sanitize_context_t *c) const
    703  {
    704    TRACE_SANITIZE (this);
    705    return_trace (c->check_struct (this));
    706  }
    707 
    708  void extract (hb_ot_math_glyph_part_t &out,
    709 	int64_t mult,
    710 	hb_font_t *font) const
    711  {
    712    out.glyph			= glyph;
    713 
    714    out.start_connector_length	= font->em_mult (startConnectorLength, mult);
    715    out.end_connector_length	= font->em_mult (endConnectorLength, mult);
    716    out.full_advance		= font->em_mult (fullAdvance, mult);
    717 
    718    static_assert ((unsigned int) HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER ==
    719 	   (unsigned int) PartFlags::Extender, "");
    720 
    721    out.flags = (hb_ot_math_glyph_part_flags_t)
    722 	(unsigned int)
    723 	(partFlags & PartFlags::Defined);
    724  }
    725 
    726  void closure_glyphs (hb_set_t *variant_glyphs) const
    727  { 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  {
    754    TRACE_SUBSET (this);
    755 
    756    if (!c->serializer->copy (italicsCorrection, this)) return_trace (false);
    757    if (!c->serializer->copy<HBUINT16> (partRecords.len)) return_trace (false);
    758 
    759    for (const auto& record : partRecords.iter ())
    760      if (!record.subset (c)) return_trace (false);
    761    return_trace (true);
    762  }
    763 
    764  bool sanitize (hb_sanitize_context_t *c) const
    765  {
    766    TRACE_SANITIZE (this);
    767    return_trace (c->check_struct (this) &&
    768 	  italicsCorrection.sanitize (c, this) &&
    769 	  partRecords.sanitize (c));
    770  }
    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  {
    779    if (parts_count)
    780    {
    781      int64_t mult = font->dir_mult (direction);
    782      for (auto _ : hb_zip (partRecords.as_array ().sub_array (start_offset, parts_count),
    783 		    hb_array (parts, *parts_count)))
    784 _.first.extract (_.second, mult, font);
    785    }
    786 
    787    if (italics_correction)
    788      *italics_correction = italicsCorrection.get_x_value (font, this);
    789 
    790    return partRecords.len;
    791  }
    792 
    793  void closure_glyphs (hb_set_t *variant_glyphs) const
    794  {
    795    for (const auto& _ : partRecords.iter ())
    796      _.closure_glyphs (variant_glyphs);
    797  }
    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  {
    818    TRACE_SUBSET (this);
    819    auto *out = c->serializer->start_embed (*this);
    820    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
    821 
    822    out->glyphAssembly.serialize_subset (c, glyphAssembly, this);
    823 
    824    if (!c->serializer->check_assign (out->mathGlyphVariantRecord.len, mathGlyphVariantRecord.len, HB_SERIALIZE_ERROR_INT_OVERFLOW))
    825      return_trace (false);
    826    for (const auto& record : mathGlyphVariantRecord.iter ())
    827      if (!record.subset (c)) return_trace (false);
    828 
    829    return_trace (true);
    830  }
    831 
    832  bool sanitize (hb_sanitize_context_t *c) const
    833  {
    834    TRACE_SANITIZE (this);
    835    return_trace (c->check_struct (this) &&
    836 	  glyphAssembly.sanitize (c, this) &&
    837 	  mathGlyphVariantRecord.sanitize (c));
    838  }
    839 
    840  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  {
    848    if (variants_count)
    849    {
    850      int64_t mult = font->dir_mult (direction);
    851      for (auto _ : hb_zip (mathGlyphVariantRecord.as_array ().sub_array (start_offset, variants_count),
    852 		    hb_array (variants, *variants_count)))
    853 _.second = {_.first.variantGlyph, font->em_mult (_.first.advanceMeasurement, mult)};
    854    }
    855    return mathGlyphVariantRecord.len;
    856  }
    857 
    858  void closure_glyphs (hb_set_t *variant_glyphs) const
    859  {
    860    (this+glyphAssembly).closure_glyphs (variant_glyphs);
    861 
    862    for (const auto& _ : mathGlyphVariantRecord.iter ())
    863      _.closure_glyphs (variant_glyphs);
    864  }
    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  {
    883    const hb_array_t<const Offset16To<MathGlyphConstruction>> glyph_construction_offsets = glyphConstruction.as_array (vertGlyphCount + horizGlyphCount);
    884 
    885    if (vertGlyphCoverage)
    886    {
    887      const auto vert_offsets = glyph_construction_offsets.sub_array (0, vertGlyphCount);
    888      + hb_zip (this+vertGlyphCoverage, vert_offsets)
    889      | hb_filter (glyph_set, hb_first)
    890      | hb_map (hb_second)
    891      | hb_map (hb_add (this))
    892      | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); })
    893      ;
    894    }
    895 
    896    if (horizGlyphCoverage)
    897    {
    898      const auto hori_offsets = glyph_construction_offsets.sub_array (vertGlyphCount, horizGlyphCount);
    899      + hb_zip (this+horizGlyphCoverage, hori_offsets)
    900      | hb_filter (glyph_set, hb_first)
    901      | hb_map (hb_second)
    902      | hb_map (hb_add (this))
    903      | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); })
    904      ;
    905    }
    906  }
    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  {
    916    if (!coverage) return;
    917 
    918    for (const auto _ : (this+coverage).iter ())
    919    {
    920      if (i >= end_index) return;
    921      if (glyphset.has (_))
    922      {
    923        unsigned new_gid = glyph_map.get (_);
    924        new_coverage.push (new_gid);
    925        indices.add (i);
    926      }
    927      i++;
    928    }
    929  }
    930 
    931  bool subset (hb_subset_context_t *c) const
    932  {
    933    TRACE_SUBSET (this);
    934    const hb_set_t &glyphset = c->plan->_glyphset_mathed;
    935    const hb_map_t &glyph_map = *c->plan->glyph_map;
    936 
    937    auto *out = c->serializer->start_embed (*this);
    938    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
    939    if (!c->serializer->check_assign (out->minConnectorOverlap, minConnectorOverlap, HB_SERIALIZE_ERROR_INT_OVERFLOW))
    940      return_trace (false);
    941 
    942    hb_sorted_vector_t<hb_codepoint_t> new_vert_coverage;
    943    hb_sorted_vector_t<hb_codepoint_t> new_hori_coverage;
    944    hb_set_t indices;
    945    collect_coverage_and_indices (new_vert_coverage, vertGlyphCoverage, 0, vertGlyphCount, indices, glyphset, glyph_map);
    946    collect_coverage_and_indices (new_hori_coverage, horizGlyphCoverage, vertGlyphCount, vertGlyphCount + horizGlyphCount, indices, glyphset, glyph_map);
    947 
    948    if (!c->serializer->check_assign (out->vertGlyphCount, new_vert_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
    949      return_trace (false);
    950    if (!c->serializer->check_assign (out->horizGlyphCount, new_hori_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
    951      return_trace (false);
    952 
    953    for (unsigned i : indices.iter ())
    954    {
    955      auto *o = c->serializer->embed (glyphConstruction[i]);
    956      if (!o) return_trace (false);
    957      o->serialize_subset (c, glyphConstruction[i], this);
    958    }
    959 
    960    if (new_vert_coverage)
    961      out->vertGlyphCoverage.serialize_serialize (c->serializer, new_vert_coverage.iter ());
    962 
    963    if (new_hori_coverage)
    964    out->horizGlyphCoverage.serialize_serialize (c->serializer, new_hori_coverage.iter ());
    965    return_trace (true);
    966  }
    967 
    968  bool sanitize_offsets (hb_sanitize_context_t *c) const
    969  {
    970    TRACE_SANITIZE (this);
    971    unsigned int count = vertGlyphCount + horizGlyphCount;
    972    for (unsigned int i = 0; i < count; i++)
    973      if (!glyphConstruction.arrayZ[i].sanitize (c, this)) return_trace (false);
    974    return_trace (true);
    975  }
    976 
    977  bool sanitize (hb_sanitize_context_t *c) const
    978  {
    979    TRACE_SANITIZE (this);
    980    return_trace (c->check_struct (this) &&
    981 	  vertGlyphCoverage.sanitize (c, this) &&
    982 	  horizGlyphCoverage.sanitize (c, this) &&
    983 	  hb_barrier () &&
    984 	  c->check_array (glyphConstruction.arrayZ, vertGlyphCount + horizGlyphCount) &&
    985 	  sanitize_offsets (c));
    986  }
    987 
    988  hb_position_t get_min_connector_overlap (hb_direction_t direction,
    989 					  hb_font_t *font) const
    990  { 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  { return get_glyph_construction (glyph, direction, font)
    999    .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  { return get_glyph_construction (glyph, direction, font)
   1009    .get_assembly ()
   1010    .get_parts (direction, font,
   1011 	       start_offset, parts_count, parts,
   1012 	       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  {
   1020    bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
   1021    unsigned int count = vertical ? vertGlyphCount : horizGlyphCount;
   1022    const Offset16To<Coverage> &coverage = vertical ? vertGlyphCoverage
   1023 					  : horizGlyphCoverage;
   1024 
   1025    unsigned int index = (this+coverage).get_coverage (glyph);
   1026    if (unlikely (index >= count)) return Null (MathGlyphConstruction);
   1027 
   1028    if (!vertical)
   1029      index += vertGlyphCount;
   1030 
   1031    return this+glyphConstruction[index];
   1032  }
   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  bool has_data () const { return version.to_int (); }
   1075 
   1076  void closure_glyphs (hb_set_t *glyph_set) const
   1077  {
   1078    if (mathVariants)
   1079    {
   1080      hb_set_t variant_glyphs;
   1081      (this+mathVariants).closure_glyphs (glyph_set, &variant_glyphs);
   1082      hb_set_union (glyph_set, &variant_glyphs);
   1083    }
   1084  }
   1085 
   1086  bool subset (hb_subset_context_t *c) const
   1087  {
   1088    TRACE_SUBSET (this);
   1089    auto *out = c->serializer->embed (*this);
   1090    if (unlikely (!out)) return_trace (false);
   1091 
   1092    out->mathConstants.serialize_copy (c->serializer, mathConstants, this, 0, hb_serialize_context_t::Head);
   1093    out->mathGlyphInfo.serialize_subset (c, mathGlyphInfo, this);
   1094    out->mathVariants.serialize_subset (c, mathVariants, this);
   1095    return_trace (true);
   1096  }
   1097 
   1098  bool sanitize (hb_sanitize_context_t *c) const
   1099  {
   1100    TRACE_SANITIZE (this);
   1101    return_trace (version.sanitize (c) &&
   1102 	  likely (version.major == 1) &&
   1103 	  hb_barrier () &&
   1104 	  mathConstants.sanitize (c, this) &&
   1105 	  mathGlyphInfo.sanitize (c, this) &&
   1106 	  mathVariants.sanitize (c, this));
   1107  }
   1108 
   1109  // https://github.com/harfbuzz/harfbuzz/issues/4653
   1110  HB_INTERNAL bool is_bad_cambria (hb_font_t *font) const
   1111  {
   1112 #ifndef HB_NO_MATH
   1113    switch HB_CODEPOINT_ENCODE3 (font->face->table.MATH.get_blob ()->length,
   1114                                 (this+mathConstants).minHeight[1], // displayOperatorMinHeight
   1115                                 (this+mathConstants).minHeight[0]) // delimitedSubFormulaMinHeight
   1116    {
   1117      /* sha1sum:ab4a4fe054d23061f3c039493d6f665cfda2ecf5  cambria.ttc
   1118       * sha1sum:086855301bff644f9d8827b88491fcf73a6d4cb9  cambria.ttc
   1119       * sha1sum:b1e5a3feaca2ea3dfcf79ccb377de749ecf60343  cambria.ttc */
   1120      case HB_CODEPOINT_ENCODE3 (25722, 2500, 3000):
   1121        return true;
   1122    }
   1123 #endif
   1124    return false;
   1125  }
   1126 
   1127  hb_position_t get_constant (hb_ot_math_constant_t  constant,
   1128 		      hb_font_t		   *font) const
   1129  { return (this+mathConstants).get_value (constant, font); }
   1130 
   1131  const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; }
   1132 
   1133  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 */