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.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.len;
    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.len;
    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   enum { 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 */
    719