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 {
get_x_valueOT::MathValueRecord39   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); }
get_y_valueOT::MathValueRecord41   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 
sanitizeOT::MathValueRecord44   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 {
sanitize_math_value_recordsOT::MathConstants62   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 
sanitizeOT::MathConstants74   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 
get_valueOT::MathConstants80   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 {
sanitizeOT::MathItalicsCorrectionInfo168   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 
get_valueOT::MathItalicsCorrectionInfo176   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 {
sanitizeOT::MathTopAccentAttachment199   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 
get_valueOT::MathTopAccentAttachment207   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 {
sanitize_math_value_recordsOT::MathKern232   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 
sanitizeOT::MathKern241   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 
get_valueOT::MathKern249   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 {
sanitizeOT::MathKernInfoRecord297   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 
get_kerningOT::MathKernInfoRecord309   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 {
sanitizeOT::MathKernInfo330   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 
get_kerningOT::MathKernInfo338   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 {
sanitizeOT::MathGlyphInfo364   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
get_italics_correctionOT::MathGlyphInfo375   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
get_top_accent_attachmentOT::MathGlyphInfo379   get_top_accent_attachment (hb_codepoint_t  glyph, hb_font_t *font) const
380   { return (this+mathTopAccentAttachment).get_value (glyph, font); }
381 
is_extended_shapeOT::MathGlyphInfo382   bool is_extended_shape (hb_codepoint_t glyph) const
383   { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; }
384 
get_kerningOT::MathGlyphInfo385   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 
sanitizeOT::MathGlyphVariantRecord419   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   HBGlyphID 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 {
sanitizeOT::MathGlyphPartRecord449   bool sanitize (hb_sanitize_context_t *c) const
450   {
451     TRACE_SANITIZE (this);
452     return_trace (c->check_struct (this));
453   }
454 
extractOT::MathGlyphPartRecord455   void extract (hb_ot_math_glyph_part_t &out,
456 		int64_t mult,
457 		hb_font_t *font) const
458   {
459     out.glyph			= glyph;
460 
461     out.start_connector_length	= font->em_mult (startConnectorLength, mult);
462     out.end_connector_length	= font->em_mult (endConnectorLength, mult);
463     out.full_advance		= font->em_mult (fullAdvance, mult);
464 
465     static_assert ((unsigned int) HB_OT_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   HBGlyphID   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 {
sanitizeOT::MathGlyphAssembly494   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 
get_partsOT::MathGlyphAssembly502   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       int64_t mult = font->dir_mult (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], mult, 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 {
sanitizeOT::MathGlyphConstruction538   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 
get_assemblyOT::MathGlyphConstruction546   const MathGlyphAssembly &get_assembly () const { return this+glyphAssembly; }
547 
get_variantsOT::MathGlyphConstruction548   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       int64_t mult = font->dir_mult (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_mult (arr[i].advanceMeasurement, mult);
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 {
sanitize_offsetsOT::MathVariants582   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 
sanitizeOT::MathVariants591   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 
get_min_connector_overlapOT::MathVariants601   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 
get_glyph_variantsOT::MathVariants605   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 
get_glyph_partsOT::MathVariants614   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 &
get_glyph_constructionOT::MathVariants629   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 
has_dataOT::MATH684   bool has_data () const { return version.to_int (); }
685 
sanitizeOT::MATH686   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 
get_constantOT::MATH696   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 
get_glyph_infoOT::MATH700   const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; }
701 
get_variantsOT::MATH702   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