diff mupdf-source/thirdparty/harfbuzz/src/hb-ot-math-table.hh @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/harfbuzz/src/hb-ot-math-table.hh	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,1139 @@
+/*
+ * Copyright © 2016  Igalia S.L.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Igalia Author(s): Frédéric Wang
+ */
+
+#ifndef HB_OT_MATH_TABLE_HH
+#define HB_OT_MATH_TABLE_HH
+
+#include "hb-open-type.hh"
+#include "hb-ot-layout-common.hh"
+#include "hb-ot-math.h"
+
+namespace OT {
+
+
+struct MathValueRecord
+{
+  hb_position_t get_x_value (hb_font_t *font, const void *base) const
+  { return font->em_scale_x (value) + (base+deviceTable).get_x_delta (font); }
+  hb_position_t get_y_value (hb_font_t *font, const void *base) const
+  { return font->em_scale_y (value) + (base+deviceTable).get_y_delta (font); }
+
+  MathValueRecord* copy (hb_serialize_context_t *c, const void *base) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+    out->deviceTable.serialize_copy (c, deviceTable, base, 0, hb_serialize_context_t::Head);
+
+    return_trace (out);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && deviceTable.sanitize (c, base));
+  }
+
+  protected:
+  HBINT16		value;		/* The X or Y value in design units */
+  Offset16To<Device>	deviceTable;	/* Offset to the device table - from the
+					 * beginning of parent table.  May be NULL.
+					 * Suggested format for device table is 1. */
+
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct MathConstants
+{
+  MathConstants* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->start_embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    HBINT16 *p = c->allocate_size<HBINT16> (HBINT16::static_size * 2);
+    if (unlikely (!p)) return_trace (nullptr);
+    hb_memcpy (p, percentScaleDown, HBINT16::static_size * 2);
+
+    HBUINT16 *m = c->allocate_size<HBUINT16> (HBUINT16::static_size * 2);
+    if (unlikely (!m)) return_trace (nullptr);
+    hb_memcpy (m, minHeight, HBUINT16::static_size * 2);
+
+    unsigned count = ARRAY_LENGTH (mathValueRecords);
+    for (unsigned i = 0; i < count; i++)
+      if (!c->copy (mathValueRecords[i], this))
+        return_trace (nullptr);
+
+    if (!c->embed (radicalDegreeBottomRaisePercent)) return_trace (nullptr);
+    return_trace (out);
+  }
+
+  bool sanitize_math_value_records (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+
+    unsigned int count = ARRAY_LENGTH (mathValueRecords);
+    for (unsigned int i = 0; i < count; i++)
+      if (!mathValueRecords[i].sanitize (c, this))
+	return_trace (false);
+
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && sanitize_math_value_records (c));
+  }
+
+  hb_position_t get_value (hb_ot_math_constant_t constant,
+			   hb_font_t *font) const
+  {
+    switch (constant) {
+
+    case HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN:
+    case HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN:
+      return percentScaleDown[constant - HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN];
+
+    case HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT:
+    case HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT:
+      return font->em_scale_y (minHeight[constant - HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT]);
+
+    case HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE:
+    case HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE:
+    case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP:
+    case HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT:
+      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_x_value (font, this);
+
+    case HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT:
+    case HB_OT_MATH_CONSTANT_AXIS_HEIGHT:
+    case HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT:
+    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS:
+    case HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN:
+    case HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_MATH_LEADING:
+    case HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER:
+    case HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS:
+    case HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP:
+    case HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP:
+    case HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER:
+    case HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS:
+    case HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP:
+    case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP:
+    case HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_STACK_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN:
+    case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN:
+    case HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN:
+    case HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX:
+    case HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX:
+    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT:
+    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN:
+    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED:
+    case HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER:
+    case HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS:
+    case HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP:
+    case HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN:
+    case HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN:
+      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_y_value (font, this);
+
+    case HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT:
+      return radicalDegreeBottomRaisePercent;
+
+    default:
+      return 0;
+    }
+  }
+
+  protected:
+  HBINT16 percentScaleDown[2];
+  HBUINT16 minHeight[2];
+  MathValueRecord mathValueRecords[51];
+  HBINT16 radicalDegreeBottomRaisePercent;
+
+  public:
+  DEFINE_SIZE_STATIC (214);
+};
+
+struct MathItalicsCorrectionInfo
+{
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->_glyphset_mathed;
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+    + hb_zip (this+coverage, italicsCorrection)
+    | hb_filter (glyphset, hb_first)
+    | hb_filter (serialize_math_record_array (c->serializer, out->italicsCorrection, this), hb_second)
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  coverage.sanitize (c, this) &&
+		  italicsCorrection.sanitize (c, this));
+  }
+
+  hb_position_t get_value (hb_codepoint_t glyph,
+			   hb_font_t *font) const
+  {
+    unsigned int index = (this+coverage).get_coverage (glyph);
+    return italicsCorrection[index].get_x_value (font, this);
+  }
+
+  protected:
+  Offset16To<Coverage>       coverage;		/* Offset to Coverage table -
+						 * from the beginning of
+						 * MathItalicsCorrectionInfo
+						 * table. */
+  Array16Of<MathValueRecord> italicsCorrection;	/* Array of MathValueRecords
+						 * defining italics correction
+						 * values for each
+						 * covered glyph. */
+
+  public:
+  DEFINE_SIZE_ARRAY (4, italicsCorrection);
+};
+
+struct MathTopAccentAttachment
+{
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->_glyphset_mathed;
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+    + hb_zip (this+topAccentCoverage, topAccentAttachment)
+    | hb_filter (glyphset, hb_first)
+    | hb_filter (serialize_math_record_array (c->serializer, out->topAccentAttachment, this), hb_second)
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    out->topAccentCoverage.serialize_serialize (c->serializer, new_coverage.iter ());
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  topAccentCoverage.sanitize (c, this) &&
+		  topAccentAttachment.sanitize (c, this));
+  }
+
+  hb_position_t get_value (hb_codepoint_t glyph,
+			   hb_font_t *font) const
+  {
+    unsigned int index = (this+topAccentCoverage).get_coverage (glyph);
+    if (index == NOT_COVERED)
+      return font->get_glyph_h_advance (glyph) / 2;
+    return topAccentAttachment[index].get_x_value (font, this);
+  }
+
+  protected:
+  Offset16To<Coverage>       topAccentCoverage;   /* Offset to Coverage table -
+						 * from the beginning of
+						 * MathTopAccentAttachment
+						 * table. */
+  Array16Of<MathValueRecord> topAccentAttachment; /* Array of MathValueRecords
+						 * defining top accent
+						 * attachment points for each
+						 * covered glyph. */
+
+  public:
+  DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment);
+};
+
+struct MathKern
+{
+  MathKern* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->start_embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    if (unlikely (!c->embed (heightCount))) return_trace (nullptr);
+
+    unsigned count = 2 * heightCount + 1;
+    for (unsigned i = 0; i < count; i++)
+      if (!c->copy (mathValueRecordsZ.arrayZ[i], this))
+        return_trace (nullptr);
+
+    return_trace (out);
+  }
+
+  bool sanitize_math_value_records (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    unsigned int count = 2 * heightCount + 1;
+    for (unsigned int i = 0; i < count; i++)
+      if (!mathValueRecordsZ.arrayZ[i].sanitize (c, this)) return_trace (false);
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  c->check_array (mathValueRecordsZ.arrayZ, 2 * heightCount + 1) &&
+		  sanitize_math_value_records (c));
+  }
+
+  hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
+  {
+    const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
+    const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
+    int sign = font->y_scale < 0 ? -1 : +1;
+
+    /* The description of the MathKern table is a ambiguous, but interpreting
+     * "between the two heights found at those indexes" for 0 < i < len as
+     *
+     *   correctionHeight[i-1] < correction_height <= correctionHeight[i]
+     *
+     * makes the result consistent with the limit cases and we can just use the
+     * binary search algorithm of std::upper_bound:
+     */
+    unsigned int i = 0;
+    unsigned int count = heightCount;
+    while (count > 0)
+    {
+      unsigned int half = count / 2;
+      hb_position_t height = correctionHeight[i + half].get_y_value (font, this);
+      if (sign * height < sign * correction_height)
+      {
+	i += half + 1;
+	count -= half + 1;
+      } else
+	count = half;
+    }
+    return kernValue[i].get_x_value (font, this);
+  }
+
+  unsigned int get_entries (unsigned int start_offset,
+			    unsigned int *entries_count, /* IN/OUT */
+			    hb_ot_math_kern_entry_t *kern_entries, /* OUT */
+			    hb_font_t *font) const
+  {
+    const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
+    const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
+    const unsigned int entriesCount = heightCount + 1;
+
+    if (entries_count)
+    {
+      unsigned int start = hb_min (start_offset, entriesCount);
+      unsigned int end = hb_min (start + *entries_count, entriesCount);
+      *entries_count = end - start;
+
+      for (unsigned int i = 0; i < *entries_count; i++) {
+	unsigned int j = start + i;
+
+	hb_position_t max_height;
+	if (j == heightCount) {
+	  max_height = INT32_MAX;
+	} else {
+	  max_height = correctionHeight[j].get_y_value (font, this);
+	}
+
+	kern_entries[i] = {max_height, kernValue[j].get_x_value (font, this)};
+      }
+    }
+    return entriesCount;
+  }
+
+  protected:
+  HBUINT16	heightCount;
+  UnsizedArrayOf<MathValueRecord>
+		mathValueRecordsZ;
+				/* Array of correction heights at
+				 * which the kern value changes.
+				 * Sorted by the height value in
+				 * design units (heightCount entries),
+				 * Followed by:
+				 * Array of kern values corresponding
+				 * to heights. (heightCount+1 entries).
+				 */
+
+  public:
+  DEFINE_SIZE_ARRAY (2, mathValueRecordsZ);
+};
+
+struct MathKernInfoRecord
+{
+  MathKernInfoRecord* copy (hb_serialize_context_t *c, const void *base) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    unsigned count = ARRAY_LENGTH (mathKern);
+    for (unsigned i = 0; i < count; i++)
+      out->mathKern[i].serialize_copy (c, mathKern[i], base, 0, hb_serialize_context_t::Head);
+
+    return_trace (out);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+
+    unsigned int count = ARRAY_LENGTH (mathKern);
+    for (unsigned int i = 0; i < count; i++)
+      if (unlikely (!mathKern[i].sanitize (c, base)))
+	return_trace (false);
+
+    return_trace (true);
+  }
+
+  hb_position_t get_kerning (hb_ot_math_kern_t kern,
+			     hb_position_t correction_height,
+			     hb_font_t *font,
+			     const void *base) const
+  {
+    unsigned int idx = kern;
+    if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0;
+    return (base+mathKern[idx]).get_value (correction_height, font);
+  }
+
+  unsigned int get_kernings (hb_ot_math_kern_t kern,
+			     unsigned int start_offset,
+			     unsigned int *entries_count, /* IN/OUT */
+			     hb_ot_math_kern_entry_t *kern_entries, /* OUT */
+			     hb_font_t *font,
+			     const void *base) const
+  {
+    unsigned int idx = kern;
+    if (unlikely (idx >= ARRAY_LENGTH (mathKern)) || !mathKern[idx]) {
+      if (entries_count) *entries_count = 0;
+      return 0;
+    }
+    return (base+mathKern[idx]).get_entries (start_offset,
+					     entries_count,
+					     kern_entries,
+					     font);
+  }
+
+  protected:
+  /* Offset to MathKern table for each corner -
+   * from the beginning of MathKernInfo table.  May be NULL. */
+  Offset16To<MathKern> mathKern[4];
+
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct MathKernInfo
+{
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->_glyphset_mathed;
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+    + hb_zip (this+mathKernCoverage, mathKernInfoRecords)
+    | hb_filter (glyphset, hb_first)
+    | hb_filter (serialize_math_record_array (c->serializer, out->mathKernInfoRecords, this), hb_second)
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    out->mathKernCoverage.serialize_serialize (c->serializer, new_coverage.iter ());
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  mathKernCoverage.sanitize (c, this) &&
+		  mathKernInfoRecords.sanitize (c, this));
+  }
+
+  hb_position_t get_kerning (hb_codepoint_t glyph,
+			     hb_ot_math_kern_t kern,
+			     hb_position_t correction_height,
+			     hb_font_t *font) const
+  {
+    unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
+    return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this);
+  }
+
+  unsigned int get_kernings (hb_codepoint_t glyph,
+			     hb_ot_math_kern_t kern,
+			     unsigned int start_offset,
+			     unsigned int *entries_count, /* IN/OUT */
+			     hb_ot_math_kern_entry_t *kern_entries, /* OUT */
+			     hb_font_t *font) const
+  {
+    unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
+    return mathKernInfoRecords[index].get_kernings (kern,
+						    start_offset,
+						    entries_count,
+						    kern_entries,
+						    font,
+						    this);
+  }
+
+  protected:
+  Offset16To<Coverage>
+		mathKernCoverage;
+				/* Offset to Coverage table -
+				 * from the beginning of the
+				 * MathKernInfo table. */
+  Array16Of<MathKernInfoRecord>
+		mathKernInfoRecords;
+				/* Array of MathKernInfoRecords,
+				 * per-glyph information for
+				 * mathematical positioning
+				 * of subscripts and
+				 * superscripts. */
+
+  public:
+  DEFINE_SIZE_ARRAY (4, mathKernInfoRecords);
+};
+
+struct MathGlyphInfo
+{
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    out->mathItalicsCorrectionInfo.serialize_subset (c, mathItalicsCorrectionInfo, this);
+    out->mathTopAccentAttachment.serialize_subset (c, mathTopAccentAttachment, this);
+
+    const hb_set_t &glyphset = *c->plan->_glyphset_mathed;
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto it =
+    + hb_iter (this+extendedShapeCoverage)
+    | hb_filter (glyphset)
+    | hb_map_retains_sorting (glyph_map)
+    ;
+
+    if (it) out->extendedShapeCoverage.serialize_serialize (c->serializer, it);
+    else out->extendedShapeCoverage = 0;
+
+    out->mathKernInfo.serialize_subset (c, mathKernInfo, this);
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  mathItalicsCorrectionInfo.sanitize (c, this) &&
+		  mathTopAccentAttachment.sanitize (c, this) &&
+		  extendedShapeCoverage.sanitize (c, this) &&
+		  mathKernInfo.sanitize (c, this));
+  }
+
+  hb_position_t
+  get_italics_correction (hb_codepoint_t  glyph, hb_font_t *font) const
+  { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); }
+
+  hb_position_t
+  get_top_accent_attachment (hb_codepoint_t  glyph, hb_font_t *font) const
+  { return (this+mathTopAccentAttachment).get_value (glyph, font); }
+
+  bool is_extended_shape (hb_codepoint_t glyph) const
+  { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; }
+
+  hb_position_t get_kerning (hb_codepoint_t glyph,
+			     hb_ot_math_kern_t kern,
+			     hb_position_t correction_height,
+			     hb_font_t *font) const
+  { return (this+mathKernInfo).get_kerning (glyph, kern, correction_height, font); }
+
+  hb_position_t get_kernings (hb_codepoint_t glyph,
+			      hb_ot_math_kern_t kern,
+			      unsigned int start_offset,
+			      unsigned int *entries_count, /* IN/OUT */
+			      hb_ot_math_kern_entry_t *kern_entries, /* OUT */
+			      hb_font_t *font) const
+  { return (this+mathKernInfo).get_kernings (glyph,
+					     kern,
+					     start_offset,
+					     entries_count,
+					     kern_entries,
+					     font); }
+
+  protected:
+  /* Offset to MathItalicsCorrectionInfo table -
+   * from the beginning of MathGlyphInfo table. */
+  Offset16To<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo;
+
+  /* Offset to MathTopAccentAttachment table -
+   * from the beginning of MathGlyphInfo table. */
+  Offset16To<MathTopAccentAttachment> mathTopAccentAttachment;
+
+  /* Offset to coverage table for Extended Shape glyphs -
+   * from the beginning of MathGlyphInfo table. When the left or right glyph of
+   * a box is an extended shape variant, the (ink) box (and not the default
+   * position defined by values in MathConstants table) should be used for
+   * vertical positioning purposes.  May be NULL.. */
+  Offset16To<Coverage> extendedShapeCoverage;
+
+   /* Offset to MathKernInfo table -
+    * from the beginning of MathGlyphInfo table. */
+  Offset16To<MathKernInfo> mathKernInfo;
+
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct MathGlyphVariantRecord
+{
+  friend struct MathGlyphConstruction;
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (this);
+    if (unlikely (!out)) return_trace (false);
+
+    const hb_map_t& glyph_map = *c->plan->glyph_map;
+    return_trace (c->serializer->check_assign (out->variantGlyph, glyph_map.get (variantGlyph), HB_SERIALIZE_ERROR_INT_OVERFLOW));
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  void closure_glyphs (hb_set_t *variant_glyphs) const
+  { variant_glyphs->add (variantGlyph); }
+
+  protected:
+  HBGlyphID16 variantGlyph;       /* Glyph ID for the variant. */
+  HBUINT16  advanceMeasurement; /* Advance width/height, in design units, of the
+				 * variant, in the direction of requested
+				 * glyph extension. */
+
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct PartFlags : HBUINT16
+{
+  enum Flags {
+    Extender	= 0x0001u, /* If set, the part can be skipped or repeated. */
+
+    Defined	= 0x0001u, /* All defined flags. */
+  };
+
+  public:
+  DEFINE_SIZE_STATIC (2);
+};
+
+struct MathGlyphPartRecord
+{
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (this);
+    if (unlikely (!out)) return_trace (false);
+
+    const hb_map_t& glyph_map = *c->plan->glyph_map;
+    return_trace (c->serializer->check_assign (out->glyph, glyph_map.get (glyph), HB_SERIALIZE_ERROR_INT_OVERFLOW));
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  void extract (hb_ot_math_glyph_part_t &out,
+		int64_t mult,
+		hb_font_t *font) const
+  {
+    out.glyph			= glyph;
+
+    out.start_connector_length	= font->em_mult (startConnectorLength, mult);
+    out.end_connector_length	= font->em_mult (endConnectorLength, mult);
+    out.full_advance		= font->em_mult (fullAdvance, mult);
+
+    static_assert ((unsigned int) HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER ==
+		   (unsigned int) PartFlags::Extender, "");
+
+    out.flags = (hb_ot_math_glyph_part_flags_t)
+		(unsigned int)
+		(partFlags & PartFlags::Defined);
+  }
+
+  void closure_glyphs (hb_set_t *variant_glyphs) const
+  { variant_glyphs->add (glyph); }
+
+  protected:
+  HBGlyphID16	glyph;		/* Glyph ID for the part. */
+  HBUINT16	startConnectorLength;
+				/* Advance width/ height of the straight bar
+				 * connector material, in design units, is at
+				 * the beginning of the glyph, in the
+				 * direction of the extension. */
+  HBUINT16	endConnectorLength;
+				/* Advance width/ height of the straight bar
+				 * connector material, in design units, is at
+				 * the end of the glyph, in the direction of
+				 * the extension. */
+  HBUINT16	fullAdvance;	/* Full advance width/height for this part,
+				 * in the direction of the extension.
+				 * In design units. */
+  PartFlags	partFlags;	/* Part qualifiers. */
+
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+struct MathGlyphAssembly
+{
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    if (!c->serializer->copy (italicsCorrection, this)) return_trace (false);
+    if (!c->serializer->copy<HBUINT16> (partRecords.len)) return_trace (false);
+
+    for (const auto& record : partRecords.iter ())
+      if (!record.subset (c)) return_trace (false);
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  italicsCorrection.sanitize (c, this) &&
+		  partRecords.sanitize (c));
+  }
+
+  unsigned int get_parts (hb_direction_t direction,
+			  hb_font_t *font,
+			  unsigned int start_offset,
+			  unsigned int *parts_count, /* IN/OUT */
+			  hb_ot_math_glyph_part_t *parts /* OUT */,
+			  hb_position_t *italics_correction /* OUT */) const
+  {
+    if (parts_count)
+    {
+      int64_t mult = font->dir_mult (direction);
+      for (auto _ : hb_zip (partRecords.as_array ().sub_array (start_offset, parts_count),
+			    hb_array (parts, *parts_count)))
+	_.first.extract (_.second, mult, font);
+    }
+
+    if (italics_correction)
+      *italics_correction = italicsCorrection.get_x_value (font, this);
+
+    return partRecords.len;
+  }
+
+  void closure_glyphs (hb_set_t *variant_glyphs) const
+  {
+    for (const auto& _ : partRecords.iter ())
+      _.closure_glyphs (variant_glyphs);
+  }
+
+  protected:
+  MathValueRecord
+		italicsCorrection;
+				/* Italics correction of this
+				 * MathGlyphAssembly. Should not
+				 * depend on the assembly size. */
+  Array16Of<MathGlyphPartRecord>
+		partRecords;	/* Array of part records, from
+				 * left to right and bottom to
+				 * top. */
+
+  public:
+  DEFINE_SIZE_ARRAY (6, partRecords);
+};
+
+struct MathGlyphConstruction
+{
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    out->glyphAssembly.serialize_subset (c, glyphAssembly, this);
+
+    if (!c->serializer->check_assign (out->mathGlyphVariantRecord.len, mathGlyphVariantRecord.len, HB_SERIALIZE_ERROR_INT_OVERFLOW))
+      return_trace (false);
+    for (const auto& record : mathGlyphVariantRecord.iter ())
+      if (!record.subset (c)) return_trace (false);
+
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  glyphAssembly.sanitize (c, this) &&
+		  mathGlyphVariantRecord.sanitize (c));
+  }
+
+  const MathGlyphAssembly &get_assembly () const { return this+glyphAssembly; }
+
+  unsigned int get_variants (hb_direction_t direction,
+			     hb_font_t *font,
+			     unsigned int start_offset,
+			     unsigned int *variants_count, /* IN/OUT */
+			     hb_ot_math_glyph_variant_t *variants /* OUT */) const
+  {
+    if (variants_count)
+    {
+      int64_t mult = font->dir_mult (direction);
+      for (auto _ : hb_zip (mathGlyphVariantRecord.as_array ().sub_array (start_offset, variants_count),
+			    hb_array (variants, *variants_count)))
+	_.second = {_.first.variantGlyph, font->em_mult (_.first.advanceMeasurement, mult)};
+    }
+    return mathGlyphVariantRecord.len;
+  }
+
+  void closure_glyphs (hb_set_t *variant_glyphs) const
+  {
+    (this+glyphAssembly).closure_glyphs (variant_glyphs);
+
+    for (const auto& _ : mathGlyphVariantRecord.iter ())
+      _.closure_glyphs (variant_glyphs);
+  }
+
+  protected:
+  /* Offset to MathGlyphAssembly table for this shape - from the beginning of
+     MathGlyphConstruction table.  May be NULL. */
+  Offset16To<MathGlyphAssembly>	  glyphAssembly;
+
+  /* MathGlyphVariantRecords for alternative variants of the glyphs. */
+  Array16Of<MathGlyphVariantRecord> mathGlyphVariantRecord;
+
+  public:
+  DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord);
+};
+
+struct MathVariants
+{
+  void closure_glyphs (const hb_set_t *glyph_set,
+                       hb_set_t *variant_glyphs) const
+  {
+    const hb_array_t<const Offset16To<MathGlyphConstruction>> glyph_construction_offsets = glyphConstruction.as_array (vertGlyphCount + horizGlyphCount);
+
+    if (vertGlyphCoverage)
+    {
+      const auto vert_offsets = glyph_construction_offsets.sub_array (0, vertGlyphCount);
+      + hb_zip (this+vertGlyphCoverage, vert_offsets)
+      | hb_filter (glyph_set, hb_first)
+      | hb_map (hb_second)
+      | hb_map (hb_add (this))
+      | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); })
+      ;
+    }
+
+    if (horizGlyphCoverage)
+    {
+      const auto hori_offsets = glyph_construction_offsets.sub_array (vertGlyphCount, horizGlyphCount);
+      + hb_zip (this+horizGlyphCoverage, hori_offsets)
+      | hb_filter (glyph_set, hb_first)
+      | hb_map (hb_second)
+      | hb_map (hb_add (this))
+      | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); })
+      ;
+    }
+  }
+
+  void collect_coverage_and_indices (hb_sorted_vector_t<hb_codepoint_t>& new_coverage,
+                                     const Offset16To<Coverage>& coverage,
+                                     unsigned i,
+                                     unsigned end_index,
+                                     hb_set_t& indices,
+                                     const hb_set_t& glyphset,
+                                     const hb_map_t& glyph_map) const
+  {
+    if (!coverage) return;
+
+    for (const auto _ : (this+coverage).iter ())
+    {
+      if (i >= end_index) return;
+      if (glyphset.has (_))
+      {
+        unsigned new_gid = glyph_map.get (_);
+        new_coverage.push (new_gid);
+        indices.add (i);
+      }
+      i++;
+    }
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->_glyphset_mathed;
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    if (!c->serializer->check_assign (out->minConnectorOverlap, minConnectorOverlap, HB_SERIALIZE_ERROR_INT_OVERFLOW))
+      return_trace (false);
+    
+    hb_sorted_vector_t<hb_codepoint_t> new_vert_coverage;
+    hb_sorted_vector_t<hb_codepoint_t> new_hori_coverage;
+    hb_set_t indices;
+    collect_coverage_and_indices (new_vert_coverage, vertGlyphCoverage, 0, vertGlyphCount, indices, glyphset, glyph_map);
+    collect_coverage_and_indices (new_hori_coverage, horizGlyphCoverage, vertGlyphCount, vertGlyphCount + horizGlyphCount, indices, glyphset, glyph_map);
+    
+    if (!c->serializer->check_assign (out->vertGlyphCount, new_vert_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
+      return_trace (false);
+    if (!c->serializer->check_assign (out->horizGlyphCount, new_hori_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
+      return_trace (false);
+
+    for (unsigned i : indices.iter ())
+    {
+      auto *o = c->serializer->embed (glyphConstruction[i]);
+      if (!o) return_trace (false);
+      o->serialize_subset (c, glyphConstruction[i], this);
+    }
+    
+    if (new_vert_coverage)
+      out->vertGlyphCoverage.serialize_serialize (c->serializer, new_vert_coverage.iter ());
+    
+    if (new_hori_coverage)
+    out->horizGlyphCoverage.serialize_serialize (c->serializer, new_hori_coverage.iter ());
+    return_trace (true);
+  }
+
+  bool sanitize_offsets (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    unsigned int count = vertGlyphCount + horizGlyphCount;
+    for (unsigned int i = 0; i < count; i++)
+      if (!glyphConstruction.arrayZ[i].sanitize (c, this)) return_trace (false);
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  vertGlyphCoverage.sanitize (c, this) &&
+		  horizGlyphCoverage.sanitize (c, this) &&
+		  c->check_array (glyphConstruction.arrayZ, vertGlyphCount + horizGlyphCount) &&
+		  sanitize_offsets (c));
+  }
+
+  hb_position_t get_min_connector_overlap (hb_direction_t direction,
+						  hb_font_t *font) const
+  { return font->em_scale_dir (minConnectorOverlap, direction); }
+
+  unsigned int get_glyph_variants (hb_codepoint_t glyph,
+				   hb_direction_t direction,
+				   hb_font_t *font,
+				   unsigned int start_offset,
+				   unsigned int *variants_count, /* IN/OUT */
+				   hb_ot_math_glyph_variant_t *variants /* OUT */) const
+  { return get_glyph_construction (glyph, direction, font)
+	   .get_variants (direction, font, start_offset, variants_count, variants); }
+
+  unsigned int get_glyph_parts (hb_codepoint_t glyph,
+				hb_direction_t direction,
+				hb_font_t *font,
+				unsigned int start_offset,
+				unsigned int *parts_count, /* IN/OUT */
+				hb_ot_math_glyph_part_t *parts /* OUT */,
+				hb_position_t *italics_correction /* OUT */) const
+  { return get_glyph_construction (glyph, direction, font)
+	   .get_assembly ()
+	   .get_parts (direction, font,
+		       start_offset, parts_count, parts,
+		       italics_correction); }
+
+  private:
+  const MathGlyphConstruction &
+  get_glyph_construction (hb_codepoint_t glyph,
+			  hb_direction_t direction,
+			  hb_font_t *font HB_UNUSED) const
+  {
+    bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
+    unsigned int count = vertical ? vertGlyphCount : horizGlyphCount;
+    const Offset16To<Coverage> &coverage = vertical ? vertGlyphCoverage
+						  : horizGlyphCoverage;
+
+    unsigned int index = (this+coverage).get_coverage (glyph);
+    if (unlikely (index >= count)) return Null (MathGlyphConstruction);
+
+    if (!vertical)
+      index += vertGlyphCount;
+
+    return this+glyphConstruction[index];
+  }
+
+  protected:
+  HBUINT16	minConnectorOverlap;
+				/* Minimum overlap of connecting
+				 * glyphs during glyph construction,
+				 * in design units. */
+  Offset16To<Coverage> vertGlyphCoverage;
+				/* Offset to Coverage table -
+				 * from the beginning of MathVariants
+				 * table. */
+  Offset16To<Coverage> horizGlyphCoverage;
+				/* Offset to Coverage table -
+				 * from the beginning of MathVariants
+				 * table. */
+  HBUINT16	vertGlyphCount;	/* Number of glyphs for which
+				 * information is provided for
+				 * vertically growing variants. */
+  HBUINT16	horizGlyphCount;/* Number of glyphs for which
+				 * information is provided for
+				 * horizontally growing variants. */
+
+  /* Array of offsets to MathGlyphConstruction tables - from the beginning of
+     the MathVariants table, for shapes growing in vertical/horizontal
+     direction. */
+  UnsizedArrayOf<Offset16To<MathGlyphConstruction>>
+			glyphConstruction;
+
+  public:
+  DEFINE_SIZE_ARRAY (10, glyphConstruction);
+};
+
+
+/*
+ * MATH -- Mathematical typesetting
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/math
+ */
+
+struct MATH
+{
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_MATH;
+
+  bool has_data () const { return version.to_int (); }
+
+  void closure_glyphs (hb_set_t *glyph_set) const
+  {
+    if (mathVariants)
+    {
+      hb_set_t variant_glyphs;
+      (this+mathVariants).closure_glyphs (glyph_set, &variant_glyphs);
+      hb_set_union (glyph_set, &variant_glyphs);
+    }
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    out->mathConstants.serialize_copy (c->serializer, mathConstants, this, 0, hb_serialize_context_t::Head);
+    out->mathGlyphInfo.serialize_subset (c, mathGlyphInfo, this);
+    out->mathVariants.serialize_subset (c, mathVariants, this);
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+		  likely (version.major == 1) &&
+		  mathConstants.sanitize (c, this) &&
+		  mathGlyphInfo.sanitize (c, this) &&
+		  mathVariants.sanitize (c, this));
+  }
+
+  hb_position_t get_constant (hb_ot_math_constant_t  constant,
+			      hb_font_t		   *font) const
+  { return (this+mathConstants).get_value (constant, font); }
+
+  const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; }
+
+  const MathVariants &get_variants () const    { return this+mathVariants; }
+
+  protected:
+  FixedVersion<>version;	/* Version of the MATH table
+				 * initially set to 0x00010000u */
+  Offset16To<MathConstants>
+		mathConstants;	/* MathConstants table */
+  Offset16To<MathGlyphInfo>
+		mathGlyphInfo;	/* MathGlyphInfo table */
+  Offset16To<MathVariants>
+		mathVariants;	/* MathVariants table */
+
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_MATH_TABLE_HH */