Home | History | Annotate | Download | only in src
      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): Frdric Wang
     25  */
     26 
     27 #ifndef HB_OT_MATH_TABLE_HH
     28 #define HB_OT_MATH_TABLE_HH
     29 
     30 #include "hb-open-type-private.hh"
     31 #include "hb-ot-layout-common-private.hh"
     32 #include "hb-ot-math.h"
     33 
     34 namespace OT {
     35 
     36 
     37 struct MathValueRecord
     38 {
     39   inline 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   inline 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   inline 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   INT16			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 nullptr.
     54 					 * Suggested format for device table is 1. */
     55 
     56   public:
     57   DEFINE_SIZE_STATIC (4);
     58 };
     59 
     60 struct MathConstants
     61 {
     62   inline 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   inline 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   inline 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   INT16 percentScaleDown[2];
    158   UINT16 minHeight[2];
    159   MathValueRecord mathValueRecords[51];
    160   INT16 radicalDegreeBottomRaisePercent;
    161 
    162   public:
    163   DEFINE_SIZE_STATIC (214);
    164 };
    165 
    166 struct MathItalicsCorrectionInfo
    167 {
    168   inline 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   inline 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   inline 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   inline 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   inline 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 (!mathValueRecords[i].sanitize (c, this)) return_trace (false);
    238     return_trace (true);
    239   }
    240 
    241   inline bool sanitize (hb_sanitize_context_t *c) const
    242   {
    243     TRACE_SANITIZE (this);
    244     return_trace (c->check_struct (this) &&
    245 		  c->check_array (mathValueRecords,
    246 				  mathValueRecords[0].static_size,
    247 				  2 * heightCount + 1) &&
    248 		  sanitize_math_value_records (c));
    249   }
    250 
    251   inline hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
    252   {
    253     const MathValueRecord* correctionHeight = mathValueRecords;
    254     const MathValueRecord* kernValue = mathValueRecords + heightCount;
    255     int sign = font->y_scale < 0 ? -1 : +1;
    256 
    257     /* The description of the MathKern table is a ambiguous, but interpreting
    258      * "between the two heights found at those indexes" for 0 < i < len as
    259      *
    260      *   correctionHeight[i-1] < correction_height <= correctionHeight[i]
    261      *
    262      * makes the result consistent with the limit cases and we can just use the
    263      * binary search algorithm of std::upper_bound:
    264      */
    265     unsigned int i = 0;
    266     unsigned int count = heightCount;
    267     while (count > 0)
    268     {
    269       unsigned int half = count / 2;
    270       hb_position_t height = correctionHeight[i + half].get_y_value(font, this);
    271       if (sign * height < sign * correction_height)
    272       {
    273 	i += half + 1;
    274 	count -= half + 1;
    275       } else
    276 	count = half;
    277     }
    278     return kernValue[i].get_x_value(font, this);
    279   }
    280 
    281   protected:
    282   UINT16	  heightCount;
    283   MathValueRecord mathValueRecords[VAR]; /* Array of correction heights at
    284 					  * which the kern value changes.
    285 					  * Sorted by the height value in
    286 					  * design units (heightCount entries),
    287 					  * Followed by:
    288 					  * Array of kern values corresponding
    289 					  * to heights. (heightCount+1 entries).
    290 					  */
    291 
    292   public:
    293   DEFINE_SIZE_ARRAY (2, mathValueRecords);
    294 };
    295 
    296 struct MathKernInfoRecord
    297 {
    298   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
    299   {
    300     TRACE_SANITIZE (this);
    301 
    302     unsigned int count = ARRAY_LENGTH (mathKern);
    303     for (unsigned int i = 0; i < count; i++)
    304       if (unlikely (!mathKern[i].sanitize (c, base)))
    305 	return_trace (false);
    306 
    307     return_trace (true);
    308   }
    309 
    310   inline hb_position_t get_kerning (hb_ot_math_kern_t kern,
    311 				    hb_position_t correction_height,
    312 				    hb_font_t *font,
    313 				    const void *base) const
    314   {
    315     unsigned int idx = kern;
    316     if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0;
    317     return (base+mathKern[idx]).get_value (correction_height, font);
    318   }
    319 
    320   protected:
    321   /* Offset to MathKern table for each corner -
    322    * from the beginning of MathKernInfo table. May be nullptr. */
    323   OffsetTo<MathKern> mathKern[4];
    324 
    325   public:
    326   DEFINE_SIZE_STATIC (8);
    327 };
    328 
    329 struct MathKernInfo
    330 {
    331   inline bool sanitize (hb_sanitize_context_t *c) const
    332   {
    333     TRACE_SANITIZE (this);
    334     return_trace (c->check_struct (this) &&
    335 		  mathKernCoverage.sanitize (c, this) &&
    336 		  mathKernInfoRecords.sanitize (c, this));
    337   }
    338 
    339   inline hb_position_t get_kerning (hb_codepoint_t glyph,
    340 				    hb_ot_math_kern_t kern,
    341 				    hb_position_t correction_height,
    342 				    hb_font_t *font) const
    343   {
    344     unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
    345     return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this);
    346   }
    347 
    348   protected:
    349   OffsetTo<Coverage>		mathKernCoverage;    /* Offset to Coverage table -
    350 						      * from the beginning of the
    351 						      * MathKernInfo table. */
    352   ArrayOf<MathKernInfoRecord>	mathKernInfoRecords; /* Array of
    353 						      * MathKernInfoRecords,
    354 						      * per-glyph information for
    355 						      * mathematical positioning
    356 						      * of subscripts and
    357 						      * superscripts. */
    358 
    359   public:
    360   DEFINE_SIZE_ARRAY (4, mathKernInfoRecords);
    361 };
    362 
    363 struct MathGlyphInfo
    364 {
    365   inline bool sanitize (hb_sanitize_context_t *c) const
    366   {
    367     TRACE_SANITIZE (this);
    368     return_trace (c->check_struct (this) &&
    369 		  mathItalicsCorrectionInfo.sanitize (c, this) &&
    370 		  mathTopAccentAttachment.sanitize (c, this) &&
    371 		  extendedShapeCoverage.sanitize (c, this) &&
    372 		  mathKernInfo.sanitize(c, this));
    373   }
    374 
    375   inline hb_position_t
    376   get_italics_correction (hb_codepoint_t  glyph, hb_font_t *font) const
    377   { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); }
    378 
    379   inline hb_position_t
    380   get_top_accent_attachment (hb_codepoint_t  glyph, hb_font_t *font) const
    381   { return (this+mathTopAccentAttachment).get_value (glyph, font); }
    382 
    383   inline bool is_extended_shape (hb_codepoint_t glyph) const
    384   { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; }
    385 
    386   inline hb_position_t get_kerning (hb_codepoint_t glyph,
    387 				    hb_ot_math_kern_t kern,
    388 				    hb_position_t correction_height,
    389 				    hb_font_t *font) const
    390   { return (this+mathKernInfo).get_kerning (glyph, kern, correction_height, font); }
    391 
    392   protected:
    393   /* Offset to MathItalicsCorrectionInfo table -
    394    * from the beginning of MathGlyphInfo table. */
    395   OffsetTo<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo;
    396 
    397   /* Offset to MathTopAccentAttachment table -
    398    * from the beginning of MathGlyphInfo table. */
    399   OffsetTo<MathTopAccentAttachment> mathTopAccentAttachment;
    400 
    401   /* Offset to coverage table for Extended Shape glyphs -
    402    * from the beginning of MathGlyphInfo table. When the left or right glyph of
    403    * a box is an extended shape variant, the (ink) box (and not the default
    404    * position defined by values in MathConstants table) should be used for
    405    * vertical positioning purposes. May be nullptr.. */
    406   OffsetTo<Coverage> extendedShapeCoverage;
    407 
    408    /* Offset to MathKernInfo table -
    409     * from the beginning of MathGlyphInfo table. */
    410   OffsetTo<MathKernInfo> mathKernInfo;
    411 
    412   public:
    413   DEFINE_SIZE_STATIC (8);
    414 };
    415 
    416 struct MathGlyphVariantRecord
    417 {
    418   friend struct MathGlyphConstruction;
    419 
    420   inline bool sanitize (hb_sanitize_context_t *c) const
    421   {
    422     TRACE_SANITIZE (this);
    423     return_trace (c->check_struct (this));
    424   }
    425 
    426   protected:
    427   GlyphID variantGlyph;       /* Glyph ID for the variant. */
    428   UINT16  advanceMeasurement; /* Advance width/height, in design units, of the
    429 			       * variant, in the direction of requested
    430 			       * glyph extension. */
    431 
    432   public:
    433   DEFINE_SIZE_STATIC (4);
    434 };
    435 
    436 struct PartFlags : UINT16
    437 {
    438   enum Flags {
    439     Extender	= 0x0001u, /* If set, the part can be skipped or repeated. */
    440 
    441     Defined	= 0x0001u, /* All defined flags. */
    442   };
    443 
    444   public:
    445   DEFINE_SIZE_STATIC (2);
    446 };
    447 
    448 struct MathGlyphPartRecord
    449 {
    450   inline bool sanitize (hb_sanitize_context_t *c) const
    451   {
    452     TRACE_SANITIZE (this);
    453     return_trace (c->check_struct (this));
    454   }
    455 
    456   inline void extract (hb_ot_math_glyph_part_t &out,
    457 		       int scale,
    458 		       hb_font_t *font) const
    459   {
    460     out.glyph			= glyph;
    461 
    462     out.start_connector_length	= font->em_scale (startConnectorLength, scale);
    463     out.end_connector_length	= font->em_scale (endConnectorLength, scale);
    464     out.full_advance		= font->em_scale (fullAdvance, scale);
    465 
    466     static_assert ((unsigned int) HB_MATH_GLYPH_PART_FLAG_EXTENDER ==
    467 		   (unsigned int) PartFlags::Extender, "");
    468 
    469     out.flags = (hb_ot_math_glyph_part_flags_t)
    470 		(unsigned int)
    471 		(partFlags & PartFlags::Defined);
    472   }
    473 
    474   protected:
    475   GlyphID   glyph;		  /* Glyph ID for the part. */
    476   UINT16    startConnectorLength; /* Advance width/ height of the straight bar
    477 				   * connector material, in design units, is at
    478 				   * the beginning of the glyph, in the
    479 				   * direction of the extension. */
    480   UINT16    endConnectorLength;   /* Advance width/ height of the straight bar
    481 				   * connector material, in design units, is at
    482 				   * the end of the glyph, in the direction of
    483 				   * the extension. */
    484   UINT16    fullAdvance;	  /* Full advance width/height for this part,
    485 				   * in the direction of the extension.
    486 				   * In design units. */
    487   PartFlags partFlags;		  /* Part qualifiers. */
    488 
    489   public:
    490   DEFINE_SIZE_STATIC (10);
    491 };
    492 
    493 struct MathGlyphAssembly
    494 {
    495   inline bool sanitize (hb_sanitize_context_t *c) const
    496   {
    497     TRACE_SANITIZE (this);
    498     return_trace (c->check_struct (this) &&
    499 		  italicsCorrection.sanitize(c, this) &&
    500 		  partRecords.sanitize(c));
    501   }
    502 
    503   inline unsigned int get_parts (hb_direction_t direction,
    504 				 hb_font_t *font,
    505 				 unsigned int start_offset,
    506 				 unsigned int *parts_count, /* IN/OUT */
    507 				 hb_ot_math_glyph_part_t *parts /* OUT */,
    508 				 hb_position_t *italics_correction /* OUT */) const
    509   {
    510     if (parts_count)
    511     {
    512       int scale = font->dir_scale (direction);
    513       const MathGlyphPartRecord *arr =
    514 	    partRecords.sub_array (start_offset, parts_count);
    515       unsigned int count = *parts_count;
    516       for (unsigned int i = 0; i < count; i++)
    517 	arr[i].extract (parts[i], scale, font);
    518     }
    519 
    520     if (italics_correction)
    521       *italics_correction = italicsCorrection.get_x_value (font, this);
    522 
    523     return partRecords.len;
    524   }
    525 
    526   protected:
    527   MathValueRecord	   italicsCorrection; /* Italics correction of this
    528 					       * MathGlyphAssembly. Should not
    529 					       * depend on the assembly size. */
    530   ArrayOf<MathGlyphPartRecord> partRecords;   /* Array of part records, from
    531 					       * left to right and bottom to
    532 					       * top. */
    533 
    534   public:
    535   DEFINE_SIZE_ARRAY (6, partRecords);
    536 };
    537 
    538 struct MathGlyphConstruction
    539 {
    540   inline bool sanitize (hb_sanitize_context_t *c) const
    541   {
    542     TRACE_SANITIZE (this);
    543     return_trace (c->check_struct (this) &&
    544 		  glyphAssembly.sanitize(c, this) &&
    545 		  mathGlyphVariantRecord.sanitize(c));
    546   }
    547 
    548   inline const MathGlyphAssembly &get_assembly (void) const
    549   { return this+glyphAssembly; }
    550 
    551   inline unsigned int get_variants (hb_direction_t direction,
    552 				    hb_font_t *font,
    553 				    unsigned int start_offset,
    554 				    unsigned int *variants_count, /* IN/OUT */
    555 				    hb_ot_math_glyph_variant_t *variants /* OUT */) const
    556   {
    557     if (variants_count)
    558     {
    559       int scale = font->dir_scale (direction);
    560       const MathGlyphVariantRecord *arr =
    561 	    mathGlyphVariantRecord.sub_array (start_offset, variants_count);
    562       unsigned int count = *variants_count;
    563       for (unsigned int i = 0; i < count; i++)
    564       {
    565 	variants[i].glyph = arr[i].variantGlyph;
    566 	variants[i].advance = font->em_scale (arr[i].advanceMeasurement, scale);
    567       }
    568     }
    569     return mathGlyphVariantRecord.len;
    570   }
    571 
    572   protected:
    573   /* Offset to MathGlyphAssembly table for this shape - from the beginning of
    574      MathGlyphConstruction table. May be nullptr. */
    575   OffsetTo<MathGlyphAssembly>	  glyphAssembly;
    576 
    577   /* MathGlyphVariantRecords for alternative variants of the glyphs. */
    578   ArrayOf<MathGlyphVariantRecord> mathGlyphVariantRecord;
    579 
    580   public:
    581   DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord);
    582 };
    583 
    584 struct MathVariants
    585 {
    586   inline bool sanitize_offsets (hb_sanitize_context_t *c) const
    587   {
    588     TRACE_SANITIZE (this);
    589     unsigned int count = vertGlyphCount + horizGlyphCount;
    590     for (unsigned int i = 0; i < count; i++)
    591       if (!glyphConstruction[i].sanitize (c, this)) return_trace (false);
    592     return_trace (true);
    593   }
    594 
    595   inline bool sanitize (hb_sanitize_context_t *c) const
    596   {
    597     TRACE_SANITIZE (this);
    598     return_trace (c->check_struct (this) &&
    599 		  vertGlyphCoverage.sanitize (c, this) &&
    600 		  horizGlyphCoverage.sanitize (c, this) &&
    601 		  c->check_array (glyphConstruction,
    602 				  glyphConstruction[0].static_size,
    603 				  vertGlyphCount + horizGlyphCount) &&
    604 		  sanitize_offsets (c));
    605   }
    606 
    607   inline hb_position_t get_min_connector_overlap (hb_direction_t direction,
    608 						  hb_font_t *font) const
    609   { return font->em_scale_dir (minConnectorOverlap, direction); }
    610 
    611   inline unsigned int get_glyph_variants (hb_codepoint_t glyph,
    612 					  hb_direction_t direction,
    613 					  hb_font_t *font,
    614 					  unsigned int start_offset,
    615 					  unsigned int *variants_count, /* IN/OUT */
    616 					  hb_ot_math_glyph_variant_t *variants /* OUT */) const
    617   { return get_glyph_construction (glyph, direction, font)
    618 	   .get_variants (direction, font, start_offset, variants_count, variants); }
    619 
    620   inline unsigned int get_glyph_parts (hb_codepoint_t glyph,
    621 				       hb_direction_t direction,
    622 				       hb_font_t *font,
    623 				       unsigned int start_offset,
    624 				       unsigned int *parts_count, /* IN/OUT */
    625 				       hb_ot_math_glyph_part_t *parts /* OUT */,
    626 				       hb_position_t *italics_correction /* OUT */) const
    627   { return get_glyph_construction (glyph, direction, font)
    628 	   .get_assembly ()
    629 	   .get_parts (direction, font,
    630 		       start_offset, parts_count, parts,
    631 		       italics_correction); }
    632 
    633   private:
    634   inline const MathGlyphConstruction &
    635 		get_glyph_construction (hb_codepoint_t glyph,
    636 					hb_direction_t direction,
    637 					hb_font_t *font) const
    638   {
    639     bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
    640     unsigned int count = vertical ? vertGlyphCount : horizGlyphCount;
    641     const OffsetTo<Coverage> &coverage = vertical ? vertGlyphCoverage
    642 						  : horizGlyphCoverage;
    643 
    644     unsigned int index = (this+coverage).get_coverage (glyph);
    645     if (unlikely (index >= count)) return Null(MathGlyphConstruction);
    646 
    647     if (!vertical)
    648       index += vertGlyphCount;
    649 
    650     return this+glyphConstruction[index];
    651   }
    652 
    653   protected:
    654   UINT16	     minConnectorOverlap; /* Minimum overlap of connecting
    655 					   * glyphs during glyph construction,
    656 					   * in design units. */
    657   OffsetTo<Coverage> vertGlyphCoverage;   /* Offset to Coverage table -
    658 					   * from the beginning of MathVariants
    659 					   * table. */
    660   OffsetTo<Coverage> horizGlyphCoverage;  /* Offset to Coverage table -
    661 					   * from the beginning of MathVariants
    662 					   * table. */
    663   UINT16	     vertGlyphCount;      /* Number of glyphs for which
    664 					   * information is provided for
    665 					   * vertically growing variants. */
    666   UINT16	     horizGlyphCount;     /* Number of glyphs for which
    667 					   * information is provided for
    668 					   * horizontally growing variants. */
    669 
    670   /* Array of offsets to MathGlyphConstruction tables - from the beginning of
    671      the MathVariants table, for shapes growing in vertical/horizontal
    672      direction. */
    673   OffsetTo<MathGlyphConstruction> glyphConstruction[VAR];
    674 
    675   public:
    676   DEFINE_SIZE_ARRAY (10, glyphConstruction);
    677 };
    678 
    679 
    680 /*
    681  * MATH -- The MATH Table
    682  */
    683 
    684 struct MATH
    685 {
    686   static const hb_tag_t tableTag	= HB_OT_TAG_MATH;
    687 
    688   inline bool sanitize (hb_sanitize_context_t *c) const
    689   {
    690     TRACE_SANITIZE (this);
    691     return_trace (version.sanitize (c) &&
    692 		  likely (version.major == 1) &&
    693 		  mathConstants.sanitize (c, this) &&
    694 		  mathGlyphInfo.sanitize (c, this) &&
    695 		  mathVariants.sanitize (c, this));
    696   }
    697 
    698   inline hb_position_t get_constant (hb_ot_math_constant_t  constant,
    699 				     hb_font_t		   *font) const
    700   { return (this+mathConstants).get_value (constant, font); }
    701 
    702   inline const MathGlyphInfo &get_math_glyph_info (void) const
    703   { return this+mathGlyphInfo; }
    704 
    705   inline const MathVariants &get_math_variants (void) const
    706   { return this+mathVariants; }
    707 
    708   protected:
    709   FixedVersion<>version;		/* Version of the MATH table
    710 					 * initially set to 0x00010000u */
    711   OffsetTo<MathConstants> mathConstants;/* MathConstants table */
    712   OffsetTo<MathGlyphInfo> mathGlyphInfo;/* MathGlyphInfo table */
    713   OffsetTo<MathVariants>  mathVariants;	/* MathVariants table */
    714 
    715   public:
    716   DEFINE_SIZE_STATIC (10);
    717 };
    718 
    719 } /* namespace OT */
    720 
    721 
    722 #endif /* HB_OT_MATH_TABLE_HH */
    723