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   bool sanitize (hb_sanitize_context_t *c, const void *base) const
  45   {
  46     TRACE_SANITIZE (this);
  47     return_trace (c->check_struct (this) && deviceTable.sanitize (c, base));
  48   }
  49 
  50   protected:
  51   HBINT16                       value;          /* The X or Y value in design units */
  52   OffsetTo<Device>      deviceTable;    /* Offset to the device table - from the
  53                                          * beginning of parent table.  May be NULL.
  54                                          * Suggested format for device table is 1. */
  55 
  56   public:
  57   DEFINE_SIZE_STATIC (4);
  58 };
  59 
  60 struct MathConstants
  61 {
  62   bool sanitize_math_value_records (hb_sanitize_context_t *c) const
  63   {
  64     TRACE_SANITIZE (this);
  65 
  66     unsigned int count = ARRAY_LENGTH (mathValueRecords);
  67     for (unsigned int i = 0; i < count; i++)
  68       if (!mathValueRecords[i].sanitize (c, this))
  69         return_trace (false);
  70 
  71     return_trace (true);
  72   }
  73 
  74   bool sanitize (hb_sanitize_context_t *c) const
  75   {
  76     TRACE_SANITIZE (this);
  77     return_trace (c->check_struct (this) && sanitize_math_value_records (c));
  78   }
  79 
  80   hb_position_t get_value (hb_ot_math_constant_t constant,
  81                                   hb_font_t *font) const
  82   {
  83     switch (constant) {
  84 
  85     case HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN:
  86     case HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN:
  87       return percentScaleDown[constant - HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN];
  88 
  89     case HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT:
  90     case HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT:
  91       return font->em_scale_y (minHeight[constant - HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT]);
  92 
  93     case HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE:
  94     case HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE:
  95     case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP:
  96     case HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT:
  97       return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_x_value (font, this);
  98 
  99     case HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT:
 100     case HB_OT_MATH_CONSTANT_AXIS_HEIGHT:
 101     case HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT:
 102     case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN:
 103     case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN:
 104     case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN:
 105     case HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN:
 106     case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP:
 107     case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN:
 108     case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP:
 109     case HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN:
 110     case HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS:
 111     case HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN:
 112     case HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN:
 113     case HB_OT_MATH_CONSTANT_MATH_LEADING:
 114     case HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER:
 115     case HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS:
 116     case HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP:
 117     case HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP:
 118     case HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER:
 119     case HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS:
 120     case HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP:
 121     case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP:
 122     case HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN:
 123     case HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN:
 124     case HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN:
 125     case HB_OT_MATH_CONSTANT_STACK_GAP_MIN:
 126     case HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP:
 127     case HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP:
 128     case HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN:
 129     case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN:
 130     case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN:
 131     case HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP:
 132     case HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN:
 133     case HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN:
 134     case HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX:
 135     case HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN:
 136     case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX:
 137     case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT:
 138     case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN:
 139     case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP:
 140     case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED:
 141     case HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER:
 142     case HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS:
 143     case HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP:
 144     case HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN:
 145     case HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN:
 146       return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_y_value (font, this);
 147 
 148     case HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT:
 149       return radicalDegreeBottomRaisePercent;
 150 
 151     default:
 152       return 0;
 153     }
 154   }
 155 
 156   protected:
 157   HBINT16 percentScaleDown[2];
 158   HBUINT16 minHeight[2];
 159   MathValueRecord mathValueRecords[51];
 160   HBINT16 radicalDegreeBottomRaisePercent;
 161 
 162   public:
 163   DEFINE_SIZE_STATIC (214);
 164 };
 165 
 166 struct MathItalicsCorrectionInfo
 167 {
 168   bool sanitize (hb_sanitize_context_t *c) const
 169   {
 170     TRACE_SANITIZE (this);
 171     return_trace (c->check_struct (this) &&
 172                   coverage.sanitize (c, this) &&
 173                   italicsCorrection.sanitize (c, this));
 174   }
 175 
 176   hb_position_t get_value (hb_codepoint_t glyph,
 177                            hb_font_t *font) const
 178   {
 179     unsigned int index = (this+coverage).get_coverage (glyph);
 180     return italicsCorrection[index].get_x_value (font, this);
 181   }
 182 
 183   protected:
 184   OffsetTo<Coverage>       coverage;            /* Offset to Coverage table -
 185                                                  * from the beginning of
 186                                                  * MathItalicsCorrectionInfo
 187                                                  * table. */
 188   ArrayOf<MathValueRecord> italicsCorrection;   /* Array of MathValueRecords
 189                                                  * defining italics correction
 190                                                  * values for each
 191                                                  * covered glyph. */
 192 
 193   public:
 194   DEFINE_SIZE_ARRAY (4, italicsCorrection);
 195 };
 196 
 197 struct MathTopAccentAttachment
 198 {
 199   bool sanitize (hb_sanitize_context_t *c) const
 200   {
 201     TRACE_SANITIZE (this);
 202     return_trace (c->check_struct (this) &&
 203                   topAccentCoverage.sanitize (c, this) &&
 204                   topAccentAttachment.sanitize (c, this));
 205   }
 206 
 207   hb_position_t get_value (hb_codepoint_t glyph,
 208                            hb_font_t *font) const
 209   {
 210     unsigned int index = (this+topAccentCoverage).get_coverage (glyph);
 211     if (index == NOT_COVERED)
 212       return font->get_glyph_h_advance (glyph) / 2;
 213     return topAccentAttachment[index].get_x_value (font, this);
 214   }
 215 
 216   protected:
 217   OffsetTo<Coverage>       topAccentCoverage;   /* Offset to Coverage table -
 218                                                  * from the beginning of
 219                                                  * MathTopAccentAttachment
 220                                                  * table. */
 221   ArrayOf<MathValueRecord> topAccentAttachment; /* Array of MathValueRecords
 222                                                  * defining top accent
 223                                                  * attachment points for each
 224                                                  * covered glyph. */
 225 
 226   public:
 227   DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment);
 228 };
 229 
 230 struct MathKern
 231 {
 232   bool sanitize_math_value_records (hb_sanitize_context_t *c) const
 233   {
 234     TRACE_SANITIZE (this);
 235     unsigned int count = 2 * heightCount + 1;
 236     for (unsigned int i = 0; i < count; i++)
 237       if (!mathValueRecordsZ.arrayZ[i].sanitize (c, this)) return_trace (false);
 238     return_trace (true);
 239   }
 240 
 241   bool sanitize (hb_sanitize_context_t *c) const
 242   {
 243     TRACE_SANITIZE (this);
 244     return_trace (c->check_struct (this) &&
 245                   c->check_array (mathValueRecordsZ.arrayZ, 2 * heightCount + 1) &&
 246                   sanitize_math_value_records (c));
 247   }
 248 
 249   hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
 250   {
 251     const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
 252     const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
 253     int sign = font->y_scale < 0 ? -1 : +1;
 254 
 255     /* The description of the MathKern table is a ambiguous, but interpreting
 256      * "between the two heights found at those indexes" for 0 < i < len as
 257      *
 258      *   correctionHeight[i-1] < correction_height <= correctionHeight[i]
 259      *
 260      * makes the result consistent with the limit cases and we can just use the
 261      * binary search algorithm of std::upper_bound:
 262      */
 263     unsigned int i = 0;
 264     unsigned int count = heightCount;
 265     while (count > 0)
 266     {
 267       unsigned int half = count / 2;
 268       hb_position_t height = correctionHeight[i + half].get_y_value (font, this);
 269       if (sign * height < sign * correction_height)
 270       {
 271         i += half + 1;
 272         count -= half + 1;
 273       } else
 274         count = half;
 275     }
 276     return kernValue[i].get_x_value (font, this);
 277   }
 278 
 279   protected:
 280   HBUINT16      heightCount;
 281   UnsizedArrayOf<MathValueRecord>
 282                 mathValueRecordsZ;      /* Array of correction heights at
 283                                          * which the kern value changes.
 284                                          * Sorted by the height value in
 285                                          * design units (heightCount entries),
 286                                          * Followed by:
 287                                          * Array of kern values corresponding
 288                                          * to heights. (heightCount+1 entries).
 289                                          */
 290 
 291   public:
 292   DEFINE_SIZE_ARRAY (2, mathValueRecordsZ);
 293 };
 294 
 295 struct MathKernInfoRecord
 296 {
 297   bool sanitize (hb_sanitize_context_t *c, const void *base) const
 298   {
 299     TRACE_SANITIZE (this);
 300 
 301     unsigned int count = ARRAY_LENGTH (mathKern);
 302     for (unsigned int i = 0; i < count; i++)
 303       if (unlikely (!mathKern[i].sanitize (c, base)))
 304         return_trace (false);
 305 
 306     return_trace (true);
 307   }
 308 
 309   hb_position_t get_kerning (hb_ot_math_kern_t kern,
 310                              hb_position_t correction_height,
 311                              hb_font_t *font,
 312                              const void *base) const
 313   {
 314     unsigned int idx = kern;
 315     if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0;
 316     return (base+mathKern[idx]).get_value (correction_height, font);
 317   }
 318 
 319   protected:
 320   /* Offset to MathKern table for each corner -
 321    * from the beginning of MathKernInfo table.  May be NULL. */
 322   OffsetTo<MathKern> mathKern[4];
 323 
 324   public:
 325   DEFINE_SIZE_STATIC (8);
 326 };
 327 
 328 struct MathKernInfo
 329 {
 330   bool sanitize (hb_sanitize_context_t *c) const
 331   {
 332     TRACE_SANITIZE (this);
 333     return_trace (c->check_struct (this) &&
 334                   mathKernCoverage.sanitize (c, this) &&
 335                   mathKernInfoRecords.sanitize (c, this));
 336   }
 337 
 338   hb_position_t get_kerning (hb_codepoint_t glyph,
 339                              hb_ot_math_kern_t kern,
 340                              hb_position_t correction_height,
 341                              hb_font_t *font) const
 342   {
 343     unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
 344     return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this);
 345   }
 346 
 347   protected:
 348   OffsetTo<Coverage>            mathKernCoverage;    /* Offset to Coverage table -
 349                                                       * from the beginning of the
 350                                                       * MathKernInfo table. */
 351   ArrayOf<MathKernInfoRecord>   mathKernInfoRecords; /* Array of
 352                                                       * MathKernInfoRecords,
 353                                                       * per-glyph information for
 354                                                       * mathematical positioning
 355                                                       * of subscripts and
 356                                                       * superscripts. */
 357 
 358   public:
 359   DEFINE_SIZE_ARRAY (4, mathKernInfoRecords);
 360 };
 361 
 362 struct MathGlyphInfo
 363 {
 364   bool sanitize (hb_sanitize_context_t *c) const
 365   {
 366     TRACE_SANITIZE (this);
 367     return_trace (c->check_struct (this) &&
 368                   mathItalicsCorrectionInfo.sanitize (c, this) &&
 369                   mathTopAccentAttachment.sanitize (c, this) &&
 370                   extendedShapeCoverage.sanitize (c, this) &&
 371                   mathKernInfo.sanitize (c, this));
 372   }
 373 
 374   hb_position_t
 375   get_italics_correction (hb_codepoint_t  glyph, hb_font_t *font) const
 376   { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); }
 377 
 378   hb_position_t
 379   get_top_accent_attachment (hb_codepoint_t  glyph, hb_font_t *font) const
 380   { return (this+mathTopAccentAttachment).get_value (glyph, font); }
 381 
 382   bool is_extended_shape (hb_codepoint_t glyph) const
 383   { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; }
 384 
 385   hb_position_t get_kerning (hb_codepoint_t glyph,
 386                              hb_ot_math_kern_t kern,
 387                              hb_position_t correction_height,
 388                              hb_font_t *font) const
 389   { return (this+mathKernInfo).get_kerning (glyph, kern, correction_height, font); }
 390 
 391   protected:
 392   /* Offset to MathItalicsCorrectionInfo table -
 393    * from the beginning of MathGlyphInfo table. */
 394   OffsetTo<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo;
 395 
 396   /* Offset to MathTopAccentAttachment table -
 397    * from the beginning of MathGlyphInfo table. */
 398   OffsetTo<MathTopAccentAttachment> mathTopAccentAttachment;
 399 
 400   /* Offset to coverage table for Extended Shape glyphs -
 401    * from the beginning of MathGlyphInfo table. When the left or right glyph of
 402    * a box is an extended shape variant, the (ink) box (and not the default
 403    * position defined by values in MathConstants table) should be used for
 404    * vertical positioning purposes.  May be NULL.. */
 405   OffsetTo<Coverage> extendedShapeCoverage;
 406 
 407    /* Offset to MathKernInfo table -
 408     * from the beginning of MathGlyphInfo table. */
 409   OffsetTo<MathKernInfo> mathKernInfo;
 410 
 411   public:
 412   DEFINE_SIZE_STATIC (8);
 413 };
 414 
 415 struct MathGlyphVariantRecord
 416 {
 417   friend struct MathGlyphConstruction;
 418 
 419   bool sanitize (hb_sanitize_context_t *c) const
 420   {
 421     TRACE_SANITIZE (this);
 422     return_trace (c->check_struct (this));
 423   }
 424 
 425   protected:
 426   GlyphID variantGlyph;       /* Glyph ID for the variant. */
 427   HBUINT16  advanceMeasurement; /* Advance width/height, in design units, of the
 428                                  * variant, in the direction of requested
 429                                  * glyph extension. */
 430 
 431   public:
 432   DEFINE_SIZE_STATIC (4);
 433 };
 434 
 435 struct PartFlags : HBUINT16
 436 {
 437   enum Flags {
 438     Extender    = 0x0001u, /* If set, the part can be skipped or repeated. */
 439 
 440     Defined     = 0x0001u, /* All defined flags. */
 441   };
 442 
 443   public:
 444   DEFINE_SIZE_STATIC (2);
 445 };
 446 
 447 struct MathGlyphPartRecord
 448 {
 449   bool sanitize (hb_sanitize_context_t *c) const
 450   {
 451     TRACE_SANITIZE (this);
 452     return_trace (c->check_struct (this));
 453   }
 454 
 455   void extract (hb_ot_math_glyph_part_t &out,
 456                 int scale,
 457                 hb_font_t *font) const
 458   {
 459     out.glyph                   = glyph;
 460 
 461     out.start_connector_length  = font->em_scale (startConnectorLength, scale);
 462     out.end_connector_length    = font->em_scale (endConnectorLength, scale);
 463     out.full_advance            = font->em_scale (fullAdvance, scale);
 464 
 465     static_assert ((unsigned int) HB_MATH_GLYPH_PART_FLAG_EXTENDER ==
 466                    (unsigned int) PartFlags::Extender, "");
 467 
 468     out.flags = (hb_ot_math_glyph_part_flags_t)
 469                 (unsigned int)
 470                 (partFlags & PartFlags::Defined);
 471   }
 472 
 473   protected:
 474   GlyphID   glyph;                /* Glyph ID for the part. */
 475   HBUINT16    startConnectorLength; /* Advance width/ height of the straight bar
 476                                    * connector material, in design units, is at
 477                                    * the beginning of the glyph, in the
 478                                    * direction of the extension. */
 479   HBUINT16    endConnectorLength;   /* Advance width/ height of the straight bar
 480                                    * connector material, in design units, is at
 481                                    * the end of the glyph, in the direction of
 482                                    * the extension. */
 483   HBUINT16    fullAdvance;        /* Full advance width/height for this part,
 484                                    * in the direction of the extension.
 485                                    * In design units. */
 486   PartFlags partFlags;            /* Part qualifiers. */
 487 
 488   public:
 489   DEFINE_SIZE_STATIC (10);
 490 };
 491 
 492 struct MathGlyphAssembly
 493 {
 494   bool sanitize (hb_sanitize_context_t *c) const
 495   {
 496     TRACE_SANITIZE (this);
 497     return_trace (c->check_struct (this) &&
 498                   italicsCorrection.sanitize (c, this) &&
 499                   partRecords.sanitize (c));
 500   }
 501 
 502   unsigned int get_parts (hb_direction_t direction,
 503                           hb_font_t *font,
 504                           unsigned int start_offset,
 505                           unsigned int *parts_count, /* IN/OUT */
 506                           hb_ot_math_glyph_part_t *parts /* OUT */,
 507                           hb_position_t *italics_correction /* OUT */) const
 508   {
 509     if (parts_count)
 510     {
 511       int scale = font->dir_scale (direction);
 512       hb_array_t<const MathGlyphPartRecord> arr = partRecords.sub_array (start_offset, parts_count);
 513       unsigned int count = arr.length;
 514       for (unsigned int i = 0; i < count; i++)
 515         arr[i].extract (parts[i], scale, font);
 516     }
 517 
 518     if (italics_correction)
 519       *italics_correction = italicsCorrection.get_x_value (font, this);
 520 
 521     return partRecords.len;
 522   }
 523 
 524   protected:
 525   MathValueRecord          italicsCorrection; /* Italics correction of this
 526                                                * MathGlyphAssembly. Should not
 527                                                * depend on the assembly size. */
 528   ArrayOf<MathGlyphPartRecord> partRecords;   /* Array of part records, from
 529                                                * left to right and bottom to
 530                                                * top. */
 531 
 532   public:
 533   DEFINE_SIZE_ARRAY (6, partRecords);
 534 };
 535 
 536 struct MathGlyphConstruction
 537 {
 538   bool sanitize (hb_sanitize_context_t *c) const
 539   {
 540     TRACE_SANITIZE (this);
 541     return_trace (c->check_struct (this) &&
 542                   glyphAssembly.sanitize (c, this) &&
 543                   mathGlyphVariantRecord.sanitize (c));
 544   }
 545 
 546   const MathGlyphAssembly &get_assembly () const { return this+glyphAssembly; }
 547 
 548   unsigned int get_variants (hb_direction_t direction,
 549                              hb_font_t *font,
 550                              unsigned int start_offset,
 551                              unsigned int *variants_count, /* IN/OUT */
 552                              hb_ot_math_glyph_variant_t *variants /* OUT */) const
 553   {
 554     if (variants_count)
 555     {
 556       int scale = font->dir_scale (direction);
 557       hb_array_t<const MathGlyphVariantRecord> arr = mathGlyphVariantRecord.sub_array (start_offset, variants_count);
 558       unsigned int count = arr.length;
 559       for (unsigned int i = 0; i < count; i++)
 560       {
 561         variants[i].glyph = arr[i].variantGlyph;
 562         variants[i].advance = font->em_scale (arr[i].advanceMeasurement, scale);
 563       }
 564     }
 565     return mathGlyphVariantRecord.len;
 566   }
 567 
 568   protected:
 569   /* Offset to MathGlyphAssembly table for this shape - from the beginning of
 570      MathGlyphConstruction table.  May be NULL. */
 571   OffsetTo<MathGlyphAssembly>     glyphAssembly;
 572 
 573   /* MathGlyphVariantRecords for alternative variants of the glyphs. */
 574   ArrayOf<MathGlyphVariantRecord> mathGlyphVariantRecord;
 575 
 576   public:
 577   DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord);
 578 };
 579 
 580 struct MathVariants
 581 {
 582   bool sanitize_offsets (hb_sanitize_context_t *c) const
 583   {
 584     TRACE_SANITIZE (this);
 585     unsigned int count = vertGlyphCount + horizGlyphCount;
 586     for (unsigned int i = 0; i < count; i++)
 587       if (!glyphConstruction.arrayZ[i].sanitize (c, this)) return_trace (false);
 588     return_trace (true);
 589   }
 590 
 591   bool sanitize (hb_sanitize_context_t *c) const
 592   {
 593     TRACE_SANITIZE (this);
 594     return_trace (c->check_struct (this) &&
 595                   vertGlyphCoverage.sanitize (c, this) &&
 596                   horizGlyphCoverage.sanitize (c, this) &&
 597                   c->check_array (glyphConstruction.arrayZ, vertGlyphCount + horizGlyphCount) &&
 598                   sanitize_offsets (c));
 599   }
 600 
 601   hb_position_t get_min_connector_overlap (hb_direction_t direction,
 602                                                   hb_font_t *font) const
 603   { return font->em_scale_dir (minConnectorOverlap, direction); }
 604 
 605   unsigned int get_glyph_variants (hb_codepoint_t glyph,
 606                                    hb_direction_t direction,
 607                                    hb_font_t *font,
 608                                    unsigned int start_offset,
 609                                    unsigned int *variants_count, /* IN/OUT */
 610                                    hb_ot_math_glyph_variant_t *variants /* OUT */) const
 611   { return get_glyph_construction (glyph, direction, font)
 612            .get_variants (direction, font, start_offset, variants_count, variants); }
 613 
 614   unsigned int get_glyph_parts (hb_codepoint_t glyph,
 615                                        hb_direction_t direction,
 616                                        hb_font_t *font,
 617                                        unsigned int start_offset,
 618                                        unsigned int *parts_count, /* IN/OUT */
 619                                        hb_ot_math_glyph_part_t *parts /* OUT */,
 620                                        hb_position_t *italics_correction /* OUT */) const
 621   { return get_glyph_construction (glyph, direction, font)
 622            .get_assembly ()
 623            .get_parts (direction, font,
 624                        start_offset, parts_count, parts,
 625                        italics_correction); }
 626 
 627   private:
 628   const MathGlyphConstruction &
 629   get_glyph_construction (hb_codepoint_t glyph,
 630                           hb_direction_t direction,
 631                           hb_font_t *font HB_UNUSED) const
 632   {
 633     bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
 634     unsigned int count = vertical ? vertGlyphCount : horizGlyphCount;
 635     const OffsetTo<Coverage> &coverage = vertical ? vertGlyphCoverage
 636                                                   : horizGlyphCoverage;
 637 
 638     unsigned int index = (this+coverage).get_coverage (glyph);
 639     if (unlikely (index >= count)) return Null (MathGlyphConstruction);
 640 
 641     if (!vertical)
 642       index += vertGlyphCount;
 643 
 644     return this+glyphConstruction[index];
 645   }
 646 
 647   protected:
 648   HBUINT16           minConnectorOverlap; /* Minimum overlap of connecting
 649                                            * glyphs during glyph construction,
 650                                            * in design units. */
 651   OffsetTo<Coverage> vertGlyphCoverage;   /* Offset to Coverage table -
 652                                            * from the beginning of MathVariants
 653                                            * table. */
 654   OffsetTo<Coverage> horizGlyphCoverage;  /* Offset to Coverage table -
 655                                            * from the beginning of MathVariants
 656                                            * table. */
 657   HBUINT16           vertGlyphCount;      /* Number of glyphs for which
 658                                            * information is provided for
 659                                            * vertically growing variants. */
 660   HBUINT16           horizGlyphCount;     /* Number of glyphs for which
 661                                            * information is provided for
 662                                            * horizontally growing variants. */
 663 
 664   /* Array of offsets to MathGlyphConstruction tables - from the beginning of
 665      the MathVariants table, for shapes growing in vertical/horizontal
 666      direction. */
 667   UnsizedArrayOf<OffsetTo<MathGlyphConstruction> >
 668                         glyphConstruction;
 669 
 670   public:
 671   DEFINE_SIZE_ARRAY (10, glyphConstruction);
 672 };
 673 
 674 
 675 /*
 676  * MATH -- Mathematical typesetting
 677  * https://docs.microsoft.com/en-us/typography/opentype/spec/math
 678  */
 679 
 680 struct MATH
 681 {
 682   static constexpr hb_tag_t tableTag = HB_OT_TAG_MATH;
 683 
 684   bool has_data () const { return version.to_int (); }
 685 
 686   bool sanitize (hb_sanitize_context_t *c) const
 687   {
 688     TRACE_SANITIZE (this);
 689     return_trace (version.sanitize (c) &&
 690                   likely (version.major == 1) &&
 691                   mathConstants.sanitize (c, this) &&
 692                   mathGlyphInfo.sanitize (c, this) &&
 693                   mathVariants.sanitize (c, this));
 694   }
 695 
 696   hb_position_t get_constant (hb_ot_math_constant_t  constant,
 697                                      hb_font_t             *font) const
 698   { return (this+mathConstants).get_value (constant, font); }
 699 
 700   const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; }
 701 
 702   const MathVariants &get_variants () const    { return this+mathVariants; }
 703 
 704   protected:
 705   FixedVersion<>version;                /* Version of the MATH table
 706                                          * initially set to 0x00010000u */
 707   OffsetTo<MathConstants> mathConstants;/* MathConstants table */
 708   OffsetTo<MathGlyphInfo> mathGlyphInfo;/* MathGlyphInfo table */
 709   OffsetTo<MathVariants>  mathVariants; /* MathVariants table */
 710 
 711   public:
 712   DEFINE_SIZE_STATIC (10);
 713 };
 714 
 715 } /* namespace OT */
 716 
 717 
 718 #endif /* HB_OT_MATH_TABLE_HH */