Imported Upstream version 2.6.7
[platform/upstream/harfbuzz.git] / src / hb-ot-math-table.hh
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;
283                                 /* 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, mathValueRecordsZ);
294 };
295
296 struct MathKernInfoRecord
297 {
298   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   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 NULL. */
323   OffsetTo<MathKern> mathKern[4];
324
325   public:
326   DEFINE_SIZE_STATIC (8);
327 };
328
329 struct MathKernInfo
330 {
331   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   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>
350                 mathKernCoverage;
351                                 /* Offset to Coverage table -
352                                  * from the beginning of the
353                                  * MathKernInfo table. */
354   ArrayOf<MathKernInfoRecord>
355                 mathKernInfoRecords;
356                                 /* Array of MathKernInfoRecords,
357                                  * per-glyph information for
358                                  * mathematical positioning
359                                  * of subscripts and
360                                  * superscripts. */
361
362   public:
363   DEFINE_SIZE_ARRAY (4, mathKernInfoRecords);
364 };
365
366 struct MathGlyphInfo
367 {
368   bool sanitize (hb_sanitize_context_t *c) const
369   {
370     TRACE_SANITIZE (this);
371     return_trace (c->check_struct (this) &&
372                   mathItalicsCorrectionInfo.sanitize (c, this) &&
373                   mathTopAccentAttachment.sanitize (c, this) &&
374                   extendedShapeCoverage.sanitize (c, this) &&
375                   mathKernInfo.sanitize (c, this));
376   }
377
378   hb_position_t
379   get_italics_correction (hb_codepoint_t  glyph, hb_font_t *font) const
380   { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); }
381
382   hb_position_t
383   get_top_accent_attachment (hb_codepoint_t  glyph, hb_font_t *font) const
384   { return (this+mathTopAccentAttachment).get_value (glyph, font); }
385
386   bool is_extended_shape (hb_codepoint_t glyph) const
387   { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; }
388
389   hb_position_t get_kerning (hb_codepoint_t glyph,
390                              hb_ot_math_kern_t kern,
391                              hb_position_t correction_height,
392                              hb_font_t *font) const
393   { return (this+mathKernInfo).get_kerning (glyph, kern, correction_height, font); }
394
395   protected:
396   /* Offset to MathItalicsCorrectionInfo table -
397    * from the beginning of MathGlyphInfo table. */
398   OffsetTo<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo;
399
400   /* Offset to MathTopAccentAttachment table -
401    * from the beginning of MathGlyphInfo table. */
402   OffsetTo<MathTopAccentAttachment> mathTopAccentAttachment;
403
404   /* Offset to coverage table for Extended Shape glyphs -
405    * from the beginning of MathGlyphInfo table. When the left or right glyph of
406    * a box is an extended shape variant, the (ink) box (and not the default
407    * position defined by values in MathConstants table) should be used for
408    * vertical positioning purposes.  May be NULL.. */
409   OffsetTo<Coverage> extendedShapeCoverage;
410
411    /* Offset to MathKernInfo table -
412     * from the beginning of MathGlyphInfo table. */
413   OffsetTo<MathKernInfo> mathKernInfo;
414
415   public:
416   DEFINE_SIZE_STATIC (8);
417 };
418
419 struct MathGlyphVariantRecord
420 {
421   friend struct MathGlyphConstruction;
422
423   bool sanitize (hb_sanitize_context_t *c) const
424   {
425     TRACE_SANITIZE (this);
426     return_trace (c->check_struct (this));
427   }
428
429   protected:
430   HBGlyphID variantGlyph;       /* Glyph ID for the variant. */
431   HBUINT16  advanceMeasurement; /* Advance width/height, in design units, of the
432                                  * variant, in the direction of requested
433                                  * glyph extension. */
434
435   public:
436   DEFINE_SIZE_STATIC (4);
437 };
438
439 struct PartFlags : HBUINT16
440 {
441   enum Flags {
442     Extender    = 0x0001u, /* If set, the part can be skipped or repeated. */
443
444     Defined     = 0x0001u, /* All defined flags. */
445   };
446
447   public:
448   DEFINE_SIZE_STATIC (2);
449 };
450
451 struct MathGlyphPartRecord
452 {
453   bool sanitize (hb_sanitize_context_t *c) const
454   {
455     TRACE_SANITIZE (this);
456     return_trace (c->check_struct (this));
457   }
458
459   void extract (hb_ot_math_glyph_part_t &out,
460                 int64_t mult,
461                 hb_font_t *font) const
462   {
463     out.glyph                   = glyph;
464
465     out.start_connector_length  = font->em_mult (startConnectorLength, mult);
466     out.end_connector_length    = font->em_mult (endConnectorLength, mult);
467     out.full_advance            = font->em_mult (fullAdvance, mult);
468
469     static_assert ((unsigned int) HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER ==
470                    (unsigned int) PartFlags::Extender, "");
471
472     out.flags = (hb_ot_math_glyph_part_flags_t)
473                 (unsigned int)
474                 (partFlags & PartFlags::Defined);
475   }
476
477   protected:
478   HBGlyphID     glyph;          /* Glyph ID for the part. */
479   HBUINT16      startConnectorLength;
480                                 /* Advance width/ height of the straight bar
481                                  * connector material, in design units, is at
482                                  * the beginning of the glyph, in the
483                                  * direction of the extension. */
484   HBUINT16      endConnectorLength;
485                                 /* Advance width/ height of the straight bar
486                                  * connector material, in design units, is at
487                                  * the end of the glyph, in the direction of
488                                  * the extension. */
489   HBUINT16      fullAdvance;    /* Full advance width/height for this part,
490                                  * in the direction of the extension.
491                                  * In design units. */
492   PartFlags     partFlags;      /* Part qualifiers. */
493
494   public:
495   DEFINE_SIZE_STATIC (10);
496 };
497
498 struct MathGlyphAssembly
499 {
500   bool sanitize (hb_sanitize_context_t *c) const
501   {
502     TRACE_SANITIZE (this);
503     return_trace (c->check_struct (this) &&
504                   italicsCorrection.sanitize (c, this) &&
505                   partRecords.sanitize (c));
506   }
507
508   unsigned int get_parts (hb_direction_t direction,
509                           hb_font_t *font,
510                           unsigned int start_offset,
511                           unsigned int *parts_count, /* IN/OUT */
512                           hb_ot_math_glyph_part_t *parts /* OUT */,
513                           hb_position_t *italics_correction /* OUT */) const
514   {
515     if (parts_count)
516     {
517       int64_t mult = font->dir_mult (direction);
518       hb_array_t<const MathGlyphPartRecord> arr = partRecords.sub_array (start_offset, parts_count);
519       unsigned int count = arr.length;
520       for (unsigned int i = 0; i < count; i++)
521         arr[i].extract (parts[i], mult, font);
522     }
523
524     if (italics_correction)
525       *italics_correction = italicsCorrection.get_x_value (font, this);
526
527     return partRecords.len;
528   }
529
530   protected:
531   MathValueRecord
532                 italicsCorrection;
533                                 /* Italics correction of this
534                                  * MathGlyphAssembly. Should not
535                                  * depend on the assembly size. */
536   ArrayOf<MathGlyphPartRecord>
537                 partRecords;    /* Array of part records, from
538                                  * left to right and bottom to
539                                  * top. */
540
541   public:
542   DEFINE_SIZE_ARRAY (6, partRecords);
543 };
544
545 struct MathGlyphConstruction
546 {
547   bool sanitize (hb_sanitize_context_t *c) const
548   {
549     TRACE_SANITIZE (this);
550     return_trace (c->check_struct (this) &&
551                   glyphAssembly.sanitize (c, this) &&
552                   mathGlyphVariantRecord.sanitize (c));
553   }
554
555   const MathGlyphAssembly &get_assembly () const { return this+glyphAssembly; }
556
557   unsigned int get_variants (hb_direction_t direction,
558                              hb_font_t *font,
559                              unsigned int start_offset,
560                              unsigned int *variants_count, /* IN/OUT */
561                              hb_ot_math_glyph_variant_t *variants /* OUT */) const
562   {
563     if (variants_count)
564     {
565       int64_t mult = font->dir_mult (direction);
566       hb_array_t<const MathGlyphVariantRecord> arr = mathGlyphVariantRecord.sub_array (start_offset, variants_count);
567       unsigned int count = arr.length;
568       for (unsigned int i = 0; i < count; i++)
569       {
570         variants[i].glyph = arr[i].variantGlyph;
571         variants[i].advance = font->em_mult (arr[i].advanceMeasurement, mult);
572       }
573     }
574     return mathGlyphVariantRecord.len;
575   }
576
577   protected:
578   /* Offset to MathGlyphAssembly table for this shape - from the beginning of
579      MathGlyphConstruction table.  May be NULL. */
580   OffsetTo<MathGlyphAssembly>     glyphAssembly;
581
582   /* MathGlyphVariantRecords for alternative variants of the glyphs. */
583   ArrayOf<MathGlyphVariantRecord> mathGlyphVariantRecord;
584
585   public:
586   DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord);
587 };
588
589 struct MathVariants
590 {
591   bool sanitize_offsets (hb_sanitize_context_t *c) const
592   {
593     TRACE_SANITIZE (this);
594     unsigned int count = vertGlyphCount + horizGlyphCount;
595     for (unsigned int i = 0; i < count; i++)
596       if (!glyphConstruction.arrayZ[i].sanitize (c, this)) return_trace (false);
597     return_trace (true);
598   }
599
600   bool sanitize (hb_sanitize_context_t *c) const
601   {
602     TRACE_SANITIZE (this);
603     return_trace (c->check_struct (this) &&
604                   vertGlyphCoverage.sanitize (c, this) &&
605                   horizGlyphCoverage.sanitize (c, this) &&
606                   c->check_array (glyphConstruction.arrayZ, vertGlyphCount + horizGlyphCount) &&
607                   sanitize_offsets (c));
608   }
609
610   hb_position_t get_min_connector_overlap (hb_direction_t direction,
611                                                   hb_font_t *font) const
612   { return font->em_scale_dir (minConnectorOverlap, direction); }
613
614   unsigned int get_glyph_variants (hb_codepoint_t glyph,
615                                    hb_direction_t direction,
616                                    hb_font_t *font,
617                                    unsigned int start_offset,
618                                    unsigned int *variants_count, /* IN/OUT */
619                                    hb_ot_math_glyph_variant_t *variants /* OUT */) const
620   { return get_glyph_construction (glyph, direction, font)
621            .get_variants (direction, font, start_offset, variants_count, variants); }
622
623   unsigned int get_glyph_parts (hb_codepoint_t glyph,
624                                        hb_direction_t direction,
625                                        hb_font_t *font,
626                                        unsigned int start_offset,
627                                        unsigned int *parts_count, /* IN/OUT */
628                                        hb_ot_math_glyph_part_t *parts /* OUT */,
629                                        hb_position_t *italics_correction /* OUT */) const
630   { return get_glyph_construction (glyph, direction, font)
631            .get_assembly ()
632            .get_parts (direction, font,
633                        start_offset, parts_count, parts,
634                        italics_correction); }
635
636   private:
637   const MathGlyphConstruction &
638   get_glyph_construction (hb_codepoint_t glyph,
639                           hb_direction_t direction,
640                           hb_font_t *font HB_UNUSED) const
641   {
642     bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
643     unsigned int count = vertical ? vertGlyphCount : horizGlyphCount;
644     const OffsetTo<Coverage> &coverage = vertical ? vertGlyphCoverage
645                                                   : horizGlyphCoverage;
646
647     unsigned int index = (this+coverage).get_coverage (glyph);
648     if (unlikely (index >= count)) return Null (MathGlyphConstruction);
649
650     if (!vertical)
651       index += vertGlyphCount;
652
653     return this+glyphConstruction[index];
654   }
655
656   protected:
657   HBUINT16      minConnectorOverlap;
658                                 /* Minimum overlap of connecting
659                                  * glyphs during glyph construction,
660                                  * in design units. */
661   OffsetTo<Coverage> vertGlyphCoverage;
662                                 /* Offset to Coverage table -
663                                  * from the beginning of MathVariants
664                                  * table. */
665   OffsetTo<Coverage> horizGlyphCoverage;
666                                 /* Offset to Coverage table -
667                                  * from the beginning of MathVariants
668                                  * table. */
669   HBUINT16      vertGlyphCount; /* Number of glyphs for which
670                                  * information is provided for
671                                  * vertically growing variants. */
672   HBUINT16      horizGlyphCount;/* Number of glyphs for which
673                                  * information is provided for
674                                  * horizontally growing variants. */
675
676   /* Array of offsets to MathGlyphConstruction tables - from the beginning of
677      the MathVariants table, for shapes growing in vertical/horizontal
678      direction. */
679   UnsizedArrayOf<OffsetTo<MathGlyphConstruction>>
680                         glyphConstruction;
681
682   public:
683   DEFINE_SIZE_ARRAY (10, glyphConstruction);
684 };
685
686
687 /*
688  * MATH -- Mathematical typesetting
689  * https://docs.microsoft.com/en-us/typography/opentype/spec/math
690  */
691
692 struct MATH
693 {
694   static constexpr hb_tag_t tableTag = HB_OT_TAG_MATH;
695
696   bool has_data () const { return version.to_int (); }
697
698   bool sanitize (hb_sanitize_context_t *c) const
699   {
700     TRACE_SANITIZE (this);
701     return_trace (version.sanitize (c) &&
702                   likely (version.major == 1) &&
703                   mathConstants.sanitize (c, this) &&
704                   mathGlyphInfo.sanitize (c, this) &&
705                   mathVariants.sanitize (c, this));
706   }
707
708   hb_position_t get_constant (hb_ot_math_constant_t  constant,
709                                      hb_font_t             *font) const
710   { return (this+mathConstants).get_value (constant, font); }
711
712   const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; }
713
714   const MathVariants &get_variants () const    { return this+mathVariants; }
715
716   protected:
717   FixedVersion<>version;        /* Version of the MATH table
718                                  * initially set to 0x00010000u */
719   OffsetTo<MathConstants>
720                 mathConstants;  /* MathConstants table */
721   OffsetTo<MathGlyphInfo>
722                 mathGlyphInfo;  /* MathGlyphInfo table */
723   OffsetTo<MathVariants>
724                 mathVariants;   /* MathVariants table */
725
726   public:
727   DEFINE_SIZE_STATIC (10);
728 };
729
730 } /* namespace OT */
731
732
733 #endif /* HB_OT_MATH_TABLE_HH */