2 * Copyright © 2016 Igalia S.L.
4 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
24 * Igalia Author(s): Frédéric Wang
27 #ifndef HB_OT_MATH_TABLE_HH
28 #define HB_OT_MATH_TABLE_HH
30 #include "hb-open-type.hh"
31 #include "hb-ot-layout-common.hh"
32 #include "hb-ot-math.h"
37 struct MathValueRecord
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); }
44 MathValueRecord* copy (hb_serialize_context_t *c, const void *base) const
46 TRACE_SERIALIZE (this);
47 auto *out = c->embed (this);
48 if (unlikely (!out)) return_trace (nullptr);
49 out->deviceTable.serialize_copy (c, deviceTable, base, 0, hb_serialize_context_t::Head);
54 bool sanitize (hb_sanitize_context_t *c, const void *base) const
56 TRACE_SANITIZE (this);
57 return_trace (c->check_struct (this) && deviceTable.sanitize (c, base));
61 HBINT16 value; /* The X or Y value in design units */
62 Offset16To<Device> deviceTable; /* Offset to the device table - from the
63 * beginning of parent table. May be NULL.
64 * Suggested format for device table is 1. */
67 DEFINE_SIZE_STATIC (4);
72 MathConstants* copy (hb_serialize_context_t *c) const
74 TRACE_SERIALIZE (this);
75 auto *out = c->start_embed (this);
77 HBINT16 *p = c->allocate_size<HBINT16> (HBINT16::static_size * 2);
78 if (unlikely (!p)) return_trace (nullptr);
79 hb_memcpy (p, percentScaleDown, HBINT16::static_size * 2);
81 HBUINT16 *m = c->allocate_size<HBUINT16> (HBUINT16::static_size * 2);
82 if (unlikely (!m)) return_trace (nullptr);
83 hb_memcpy (m, minHeight, HBUINT16::static_size * 2);
85 unsigned count = ARRAY_LENGTH (mathValueRecords);
86 for (unsigned i = 0; i < count; i++)
87 if (!c->copy (mathValueRecords[i], this))
88 return_trace (nullptr);
90 if (!c->embed (radicalDegreeBottomRaisePercent)) return_trace (nullptr);
94 bool sanitize_math_value_records (hb_sanitize_context_t *c) const
96 TRACE_SANITIZE (this);
98 unsigned int count = ARRAY_LENGTH (mathValueRecords);
99 for (unsigned int i = 0; i < count; i++)
100 if (!mathValueRecords[i].sanitize (c, this))
101 return_trace (false);
106 bool sanitize (hb_sanitize_context_t *c) const
108 TRACE_SANITIZE (this);
109 return_trace (c->check_struct (this) && sanitize_math_value_records (c));
112 hb_position_t get_value (hb_ot_math_constant_t constant,
113 hb_font_t *font) const
117 case HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN:
118 case HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN:
119 return percentScaleDown[constant - HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN];
121 case HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT:
122 case HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT:
123 return font->em_scale_y (minHeight[constant - HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT]);
125 case HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE:
126 case HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE:
127 case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP:
128 case HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT:
129 return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_x_value (font, this);
131 case HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT:
132 case HB_OT_MATH_CONSTANT_AXIS_HEIGHT:
133 case HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT:
134 case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN:
135 case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN:
136 case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN:
137 case HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN:
138 case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP:
139 case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN:
140 case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP:
141 case HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN:
142 case HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS:
143 case HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN:
144 case HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN:
145 case HB_OT_MATH_CONSTANT_MATH_LEADING:
146 case HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER:
147 case HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS:
148 case HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP:
149 case HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP:
150 case HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER:
151 case HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS:
152 case HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP:
153 case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP:
154 case HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN:
155 case HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN:
156 case HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN:
157 case HB_OT_MATH_CONSTANT_STACK_GAP_MIN:
158 case HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP:
159 case HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP:
160 case HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN:
161 case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN:
162 case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN:
163 case HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP:
164 case HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN:
165 case HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN:
166 case HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX:
167 case HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN:
168 case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX:
169 case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT:
170 case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN:
171 case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP:
172 case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED:
173 case HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER:
174 case HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS:
175 case HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP:
176 case HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN:
177 case HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN:
178 return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_y_value (font, this);
180 case HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT:
181 return radicalDegreeBottomRaisePercent;
189 HBINT16 percentScaleDown[2];
190 HBUINT16 minHeight[2];
191 MathValueRecord mathValueRecords[51];
192 HBINT16 radicalDegreeBottomRaisePercent;
195 DEFINE_SIZE_STATIC (214);
198 struct MathItalicsCorrectionInfo
200 bool subset (hb_subset_context_t *c) const
203 const hb_set_t &glyphset = c->plan->_glyphset_mathed;
204 const hb_map_t &glyph_map = *c->plan->glyph_map;
206 auto *out = c->serializer->start_embed (*this);
207 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
209 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
210 + hb_zip (this+coverage, italicsCorrection)
211 | hb_filter (glyphset, hb_first)
212 | hb_filter (serialize_math_record_array (c->serializer, out->italicsCorrection, this), hb_second)
215 | hb_sink (new_coverage)
218 out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
222 bool sanitize (hb_sanitize_context_t *c) const
224 TRACE_SANITIZE (this);
225 return_trace (c->check_struct (this) &&
226 coverage.sanitize (c, this) &&
227 italicsCorrection.sanitize (c, this));
230 hb_position_t get_value (hb_codepoint_t glyph,
231 hb_font_t *font) const
233 unsigned int index = (this+coverage).get_coverage (glyph);
234 return italicsCorrection[index].get_x_value (font, this);
238 Offset16To<Coverage> coverage; /* Offset to Coverage table -
239 * from the beginning of
240 * MathItalicsCorrectionInfo
242 Array16Of<MathValueRecord> italicsCorrection; /* Array of MathValueRecords
243 * defining italics correction
248 DEFINE_SIZE_ARRAY (4, italicsCorrection);
251 struct MathTopAccentAttachment
253 bool subset (hb_subset_context_t *c) const
256 const hb_set_t &glyphset = c->plan->_glyphset_mathed;
257 const hb_map_t &glyph_map = *c->plan->glyph_map;
259 auto *out = c->serializer->start_embed (*this);
260 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
262 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
263 + hb_zip (this+topAccentCoverage, topAccentAttachment)
264 | hb_filter (glyphset, hb_first)
265 | hb_filter (serialize_math_record_array (c->serializer, out->topAccentAttachment, this), hb_second)
268 | hb_sink (new_coverage)
271 out->topAccentCoverage.serialize_serialize (c->serializer, new_coverage.iter ());
275 bool sanitize (hb_sanitize_context_t *c) const
277 TRACE_SANITIZE (this);
278 return_trace (c->check_struct (this) &&
279 topAccentCoverage.sanitize (c, this) &&
280 topAccentAttachment.sanitize (c, this));
283 hb_position_t get_value (hb_codepoint_t glyph,
284 hb_font_t *font) const
286 unsigned int index = (this+topAccentCoverage).get_coverage (glyph);
287 if (index == NOT_COVERED)
288 return font->get_glyph_h_advance (glyph) / 2;
289 return topAccentAttachment[index].get_x_value (font, this);
293 Offset16To<Coverage> topAccentCoverage; /* Offset to Coverage table -
294 * from the beginning of
295 * MathTopAccentAttachment
297 Array16Of<MathValueRecord> topAccentAttachment; /* Array of MathValueRecords
298 * defining top accent
299 * attachment points for each
303 DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment);
308 MathKern* copy (hb_serialize_context_t *c) const
310 TRACE_SERIALIZE (this);
311 auto *out = c->start_embed (this);
313 if (unlikely (!c->embed (heightCount))) return_trace (nullptr);
315 unsigned count = 2 * heightCount + 1;
316 for (unsigned i = 0; i < count; i++)
317 if (!c->copy (mathValueRecordsZ.arrayZ[i], this))
318 return_trace (nullptr);
323 bool sanitize_math_value_records (hb_sanitize_context_t *c) const
325 TRACE_SANITIZE (this);
326 unsigned int count = 2 * heightCount + 1;
327 for (unsigned int i = 0; i < count; i++)
328 if (!mathValueRecordsZ.arrayZ[i].sanitize (c, this)) return_trace (false);
332 bool sanitize (hb_sanitize_context_t *c) const
334 TRACE_SANITIZE (this);
335 return_trace (c->check_struct (this) &&
336 c->check_array (mathValueRecordsZ.arrayZ, 2 * heightCount + 1) &&
337 sanitize_math_value_records (c));
340 hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
342 const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
343 const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
344 int sign = font->y_scale < 0 ? -1 : +1;
346 /* The description of the MathKern table is a ambiguous, but interpreting
347 * "between the two heights found at those indexes" for 0 < i < len as
349 * correctionHeight[i-1] < correction_height <= correctionHeight[i]
351 * makes the result consistent with the limit cases and we can just use the
352 * binary search algorithm of std::upper_bound:
355 unsigned int count = heightCount;
358 unsigned int half = count / 2;
359 hb_position_t height = correctionHeight[i + half].get_y_value (font, this);
360 if (sign * height < sign * correction_height)
367 return kernValue[i].get_x_value (font, this);
370 unsigned int get_entries (unsigned int start_offset,
371 unsigned int *entries_count, /* IN/OUT */
372 hb_ot_math_kern_entry_t *kern_entries, /* OUT */
373 hb_font_t *font) const
375 const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
376 const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
377 const unsigned int entriesCount = heightCount + 1;
381 unsigned int start = hb_min (start_offset, entriesCount);
382 unsigned int end = hb_min (start + *entries_count, entriesCount);
383 *entries_count = end - start;
385 for (unsigned int i = 0; i < *entries_count; i++) {
386 unsigned int j = start + i;
388 hb_position_t max_height;
389 if (j == heightCount) {
390 max_height = INT32_MAX;
392 max_height = correctionHeight[j].get_y_value (font, this);
395 kern_entries[i] = {max_height, kernValue[j].get_x_value (font, this)};
402 HBUINT16 heightCount;
403 UnsizedArrayOf<MathValueRecord>
405 /* Array of correction heights at
406 * which the kern value changes.
407 * Sorted by the height value in
408 * design units (heightCount entries),
410 * Array of kern values corresponding
411 * to heights. (heightCount+1 entries).
415 DEFINE_SIZE_ARRAY (2, mathValueRecordsZ);
418 struct MathKernInfoRecord
420 MathKernInfoRecord* copy (hb_serialize_context_t *c, const void *base) const
422 TRACE_SERIALIZE (this);
423 auto *out = c->embed (this);
424 if (unlikely (!out)) return_trace (nullptr);
426 unsigned count = ARRAY_LENGTH (mathKern);
427 for (unsigned i = 0; i < count; i++)
428 out->mathKern[i].serialize_copy (c, mathKern[i], base, 0, hb_serialize_context_t::Head);
433 bool sanitize (hb_sanitize_context_t *c, const void *base) const
435 TRACE_SANITIZE (this);
437 unsigned int count = ARRAY_LENGTH (mathKern);
438 for (unsigned int i = 0; i < count; i++)
439 if (unlikely (!mathKern[i].sanitize (c, base)))
440 return_trace (false);
445 hb_position_t get_kerning (hb_ot_math_kern_t kern,
446 hb_position_t correction_height,
448 const void *base) const
450 unsigned int idx = kern;
451 if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0;
452 return (base+mathKern[idx]).get_value (correction_height, font);
455 unsigned int get_kernings (hb_ot_math_kern_t kern,
456 unsigned int start_offset,
457 unsigned int *entries_count, /* IN/OUT */
458 hb_ot_math_kern_entry_t *kern_entries, /* OUT */
460 const void *base) const
462 unsigned int idx = kern;
463 if (unlikely (idx >= ARRAY_LENGTH (mathKern)) || !mathKern[idx]) {
464 if (entries_count) *entries_count = 0;
467 return (base+mathKern[idx]).get_entries (start_offset,
474 /* Offset to MathKern table for each corner -
475 * from the beginning of MathKernInfo table. May be NULL. */
476 Offset16To<MathKern> mathKern[4];
479 DEFINE_SIZE_STATIC (8);
484 bool subset (hb_subset_context_t *c) const
487 const hb_set_t &glyphset = c->plan->_glyphset_mathed;
488 const hb_map_t &glyph_map = *c->plan->glyph_map;
490 auto *out = c->serializer->start_embed (*this);
491 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
493 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
494 + hb_zip (this+mathKernCoverage, mathKernInfoRecords)
495 | hb_filter (glyphset, hb_first)
496 | hb_filter (serialize_math_record_array (c->serializer, out->mathKernInfoRecords, this), hb_second)
499 | hb_sink (new_coverage)
502 out->mathKernCoverage.serialize_serialize (c->serializer, new_coverage.iter ());
506 bool sanitize (hb_sanitize_context_t *c) const
508 TRACE_SANITIZE (this);
509 return_trace (c->check_struct (this) &&
510 mathKernCoverage.sanitize (c, this) &&
511 mathKernInfoRecords.sanitize (c, this));
514 hb_position_t get_kerning (hb_codepoint_t glyph,
515 hb_ot_math_kern_t kern,
516 hb_position_t correction_height,
517 hb_font_t *font) const
519 unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
520 return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this);
523 unsigned int get_kernings (hb_codepoint_t glyph,
524 hb_ot_math_kern_t kern,
525 unsigned int start_offset,
526 unsigned int *entries_count, /* IN/OUT */
527 hb_ot_math_kern_entry_t *kern_entries, /* OUT */
528 hb_font_t *font) const
530 unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
531 return mathKernInfoRecords[index].get_kernings (kern,
542 /* Offset to Coverage table -
543 * from the beginning of the
544 * MathKernInfo table. */
545 Array16Of<MathKernInfoRecord>
547 /* Array of MathKernInfoRecords,
548 * per-glyph information for
549 * mathematical positioning
554 DEFINE_SIZE_ARRAY (4, mathKernInfoRecords);
559 bool subset (hb_subset_context_t *c) const
562 auto *out = c->serializer->embed (*this);
563 if (unlikely (!out)) return_trace (false);
565 out->mathItalicsCorrectionInfo.serialize_subset (c, mathItalicsCorrectionInfo, this);
566 out->mathTopAccentAttachment.serialize_subset (c, mathTopAccentAttachment, this);
568 const hb_set_t &glyphset = c->plan->_glyphset_mathed;
569 const hb_map_t &glyph_map = *c->plan->glyph_map;
572 + hb_iter (this+extendedShapeCoverage)
573 | hb_take (c->plan->source->get_num_glyphs ())
574 | hb_filter (glyphset)
575 | hb_map_retains_sorting (glyph_map)
578 if (it) out->extendedShapeCoverage.serialize_serialize (c->serializer, it);
579 else out->extendedShapeCoverage = 0;
581 out->mathKernInfo.serialize_subset (c, mathKernInfo, this);
585 bool sanitize (hb_sanitize_context_t *c) const
587 TRACE_SANITIZE (this);
588 return_trace (c->check_struct (this) &&
589 mathItalicsCorrectionInfo.sanitize (c, this) &&
590 mathTopAccentAttachment.sanitize (c, this) &&
591 extendedShapeCoverage.sanitize (c, this) &&
592 mathKernInfo.sanitize (c, this));
596 get_italics_correction (hb_codepoint_t glyph, hb_font_t *font) const
597 { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); }
600 get_top_accent_attachment (hb_codepoint_t glyph, hb_font_t *font) const
601 { return (this+mathTopAccentAttachment).get_value (glyph, font); }
603 bool is_extended_shape (hb_codepoint_t glyph) const
604 { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; }
606 hb_position_t get_kerning (hb_codepoint_t glyph,
607 hb_ot_math_kern_t kern,
608 hb_position_t correction_height,
609 hb_font_t *font) const
610 { return (this+mathKernInfo).get_kerning (glyph, kern, correction_height, font); }
612 hb_position_t get_kernings (hb_codepoint_t glyph,
613 hb_ot_math_kern_t kern,
614 unsigned int start_offset,
615 unsigned int *entries_count, /* IN/OUT */
616 hb_ot_math_kern_entry_t *kern_entries, /* OUT */
617 hb_font_t *font) const
618 { return (this+mathKernInfo).get_kernings (glyph,
626 /* Offset to MathItalicsCorrectionInfo table -
627 * from the beginning of MathGlyphInfo table. */
628 Offset16To<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo;
630 /* Offset to MathTopAccentAttachment table -
631 * from the beginning of MathGlyphInfo table. */
632 Offset16To<MathTopAccentAttachment> mathTopAccentAttachment;
634 /* Offset to coverage table for Extended Shape glyphs -
635 * from the beginning of MathGlyphInfo table. When the left or right glyph of
636 * a box is an extended shape variant, the (ink) box (and not the default
637 * position defined by values in MathConstants table) should be used for
638 * vertical positioning purposes. May be NULL.. */
639 Offset16To<Coverage> extendedShapeCoverage;
641 /* Offset to MathKernInfo table -
642 * from the beginning of MathGlyphInfo table. */
643 Offset16To<MathKernInfo> mathKernInfo;
646 DEFINE_SIZE_STATIC (8);
649 struct MathGlyphVariantRecord
651 friend struct MathGlyphConstruction;
653 bool subset (hb_subset_context_t *c) const
656 auto *out = c->serializer->embed (this);
657 if (unlikely (!out)) return_trace (false);
659 const hb_map_t& glyph_map = *c->plan->glyph_map;
660 return_trace (c->serializer->check_assign (out->variantGlyph, glyph_map.get (variantGlyph), HB_SERIALIZE_ERROR_INT_OVERFLOW));
663 bool sanitize (hb_sanitize_context_t *c) const
665 TRACE_SANITIZE (this);
666 return_trace (c->check_struct (this));
669 void closure_glyphs (hb_set_t *variant_glyphs) const
670 { variant_glyphs->add (variantGlyph); }
673 HBGlyphID16 variantGlyph; /* Glyph ID for the variant. */
674 HBUINT16 advanceMeasurement; /* Advance width/height, in design units, of the
675 * variant, in the direction of requested
676 * glyph extension. */
679 DEFINE_SIZE_STATIC (4);
682 struct PartFlags : HBUINT16
685 Extender = 0x0001u, /* If set, the part can be skipped or repeated. */
687 Defined = 0x0001u, /* All defined flags. */
691 DEFINE_SIZE_STATIC (2);
694 struct MathGlyphPartRecord
696 bool subset (hb_subset_context_t *c) const
699 auto *out = c->serializer->embed (this);
700 if (unlikely (!out)) return_trace (false);
702 const hb_map_t& glyph_map = *c->plan->glyph_map;
703 return_trace (c->serializer->check_assign (out->glyph, glyph_map.get (glyph), HB_SERIALIZE_ERROR_INT_OVERFLOW));
706 bool sanitize (hb_sanitize_context_t *c) const
708 TRACE_SANITIZE (this);
709 return_trace (c->check_struct (this));
712 void extract (hb_ot_math_glyph_part_t &out,
714 hb_font_t *font) const
718 out.start_connector_length = font->em_mult (startConnectorLength, mult);
719 out.end_connector_length = font->em_mult (endConnectorLength, mult);
720 out.full_advance = font->em_mult (fullAdvance, mult);
722 static_assert ((unsigned int) HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER ==
723 (unsigned int) PartFlags::Extender, "");
725 out.flags = (hb_ot_math_glyph_part_flags_t)
727 (partFlags & PartFlags::Defined);
730 void closure_glyphs (hb_set_t *variant_glyphs) const
731 { variant_glyphs->add (glyph); }
734 HBGlyphID16 glyph; /* Glyph ID for the part. */
735 HBUINT16 startConnectorLength;
736 /* Advance width/ height of the straight bar
737 * connector material, in design units, is at
738 * the beginning of the glyph, in the
739 * direction of the extension. */
740 HBUINT16 endConnectorLength;
741 /* Advance width/ height of the straight bar
742 * connector material, in design units, is at
743 * the end of the glyph, in the direction of
745 HBUINT16 fullAdvance; /* Full advance width/height for this part,
746 * in the direction of the extension.
747 * In design units. */
748 PartFlags partFlags; /* Part qualifiers. */
751 DEFINE_SIZE_STATIC (10);
754 struct MathGlyphAssembly
756 bool subset (hb_subset_context_t *c) const
760 if (!c->serializer->copy (italicsCorrection, this)) return_trace (false);
761 if (!c->serializer->copy<HBUINT16> (partRecords.len)) return_trace (false);
763 for (const auto& record : partRecords.iter ())
764 if (!record.subset (c)) return_trace (false);
768 bool sanitize (hb_sanitize_context_t *c) const
770 TRACE_SANITIZE (this);
771 return_trace (c->check_struct (this) &&
772 italicsCorrection.sanitize (c, this) &&
773 partRecords.sanitize (c));
776 unsigned int get_parts (hb_direction_t direction,
778 unsigned int start_offset,
779 unsigned int *parts_count, /* IN/OUT */
780 hb_ot_math_glyph_part_t *parts /* OUT */,
781 hb_position_t *italics_correction /* OUT */) const
785 int64_t mult = font->dir_mult (direction);
786 for (auto _ : hb_zip (partRecords.as_array ().sub_array (start_offset, parts_count),
787 hb_array (parts, *parts_count)))
788 _.first.extract (_.second, mult, font);
791 if (italics_correction)
792 *italics_correction = italicsCorrection.get_x_value (font, this);
794 return partRecords.len;
797 void closure_glyphs (hb_set_t *variant_glyphs) const
799 for (const auto& _ : partRecords.iter ())
800 _.closure_glyphs (variant_glyphs);
806 /* Italics correction of this
807 * MathGlyphAssembly. Should not
808 * depend on the assembly size. */
809 Array16Of<MathGlyphPartRecord>
810 partRecords; /* Array of part records, from
811 * left to right and bottom to
815 DEFINE_SIZE_ARRAY (6, partRecords);
818 struct MathGlyphConstruction
820 bool subset (hb_subset_context_t *c) const
823 auto *out = c->serializer->start_embed (*this);
824 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
826 out->glyphAssembly.serialize_subset (c, glyphAssembly, this);
828 if (!c->serializer->check_assign (out->mathGlyphVariantRecord.len, mathGlyphVariantRecord.len, HB_SERIALIZE_ERROR_INT_OVERFLOW))
829 return_trace (false);
830 for (const auto& record : mathGlyphVariantRecord.iter ())
831 if (!record.subset (c)) return_trace (false);
836 bool sanitize (hb_sanitize_context_t *c) const
838 TRACE_SANITIZE (this);
839 return_trace (c->check_struct (this) &&
840 glyphAssembly.sanitize (c, this) &&
841 mathGlyphVariantRecord.sanitize (c));
844 const MathGlyphAssembly &get_assembly () const { return this+glyphAssembly; }
846 unsigned int get_variants (hb_direction_t direction,
848 unsigned int start_offset,
849 unsigned int *variants_count, /* IN/OUT */
850 hb_ot_math_glyph_variant_t *variants /* OUT */) const
854 int64_t mult = font->dir_mult (direction);
855 for (auto _ : hb_zip (mathGlyphVariantRecord.as_array ().sub_array (start_offset, variants_count),
856 hb_array (variants, *variants_count)))
857 _.second = {_.first.variantGlyph, font->em_mult (_.first.advanceMeasurement, mult)};
859 return mathGlyphVariantRecord.len;
862 void closure_glyphs (hb_set_t *variant_glyphs) const
864 (this+glyphAssembly).closure_glyphs (variant_glyphs);
866 for (const auto& _ : mathGlyphVariantRecord.iter ())
867 _.closure_glyphs (variant_glyphs);
871 /* Offset to MathGlyphAssembly table for this shape - from the beginning of
872 MathGlyphConstruction table. May be NULL. */
873 Offset16To<MathGlyphAssembly> glyphAssembly;
875 /* MathGlyphVariantRecords for alternative variants of the glyphs. */
876 Array16Of<MathGlyphVariantRecord> mathGlyphVariantRecord;
879 DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord);
884 void closure_glyphs (const hb_set_t *glyph_set,
885 hb_set_t *variant_glyphs) const
887 const hb_array_t<const Offset16To<MathGlyphConstruction>> glyph_construction_offsets = glyphConstruction.as_array (vertGlyphCount + horizGlyphCount);
889 if (vertGlyphCoverage)
891 const auto vert_offsets = glyph_construction_offsets.sub_array (0, vertGlyphCount);
892 + hb_zip (this+vertGlyphCoverage, vert_offsets)
893 | hb_filter (glyph_set, hb_first)
895 | hb_map (hb_add (this))
896 | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); })
900 if (horizGlyphCoverage)
902 const auto hori_offsets = glyph_construction_offsets.sub_array (vertGlyphCount, horizGlyphCount);
903 + hb_zip (this+horizGlyphCoverage, hori_offsets)
904 | hb_filter (glyph_set, hb_first)
906 | hb_map (hb_add (this))
907 | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); })
912 void collect_coverage_and_indices (hb_sorted_vector_t<hb_codepoint_t>& new_coverage,
913 const Offset16To<Coverage>& coverage,
917 const hb_set_t& glyphset,
918 const hb_map_t& glyph_map) const
920 if (!coverage) return;
922 for (const auto _ : (this+coverage).iter ())
924 if (i >= end_index) return;
925 if (glyphset.has (_))
927 unsigned new_gid = glyph_map.get (_);
928 new_coverage.push (new_gid);
935 bool subset (hb_subset_context_t *c) const
938 const hb_set_t &glyphset = c->plan->_glyphset_mathed;
939 const hb_map_t &glyph_map = *c->plan->glyph_map;
941 auto *out = c->serializer->start_embed (*this);
942 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
943 if (!c->serializer->check_assign (out->minConnectorOverlap, minConnectorOverlap, HB_SERIALIZE_ERROR_INT_OVERFLOW))
944 return_trace (false);
946 hb_sorted_vector_t<hb_codepoint_t> new_vert_coverage;
947 hb_sorted_vector_t<hb_codepoint_t> new_hori_coverage;
949 collect_coverage_and_indices (new_vert_coverage, vertGlyphCoverage, 0, vertGlyphCount, indices, glyphset, glyph_map);
950 collect_coverage_and_indices (new_hori_coverage, horizGlyphCoverage, vertGlyphCount, vertGlyphCount + horizGlyphCount, indices, glyphset, glyph_map);
952 if (!c->serializer->check_assign (out->vertGlyphCount, new_vert_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
953 return_trace (false);
954 if (!c->serializer->check_assign (out->horizGlyphCount, new_hori_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
955 return_trace (false);
957 for (unsigned i : indices.iter ())
959 auto *o = c->serializer->embed (glyphConstruction[i]);
960 if (!o) return_trace (false);
961 o->serialize_subset (c, glyphConstruction[i], this);
964 if (new_vert_coverage)
965 out->vertGlyphCoverage.serialize_serialize (c->serializer, new_vert_coverage.iter ());
967 if (new_hori_coverage)
968 out->horizGlyphCoverage.serialize_serialize (c->serializer, new_hori_coverage.iter ());
972 bool sanitize_offsets (hb_sanitize_context_t *c) const
974 TRACE_SANITIZE (this);
975 unsigned int count = vertGlyphCount + horizGlyphCount;
976 for (unsigned int i = 0; i < count; i++)
977 if (!glyphConstruction.arrayZ[i].sanitize (c, this)) return_trace (false);
981 bool sanitize (hb_sanitize_context_t *c) const
983 TRACE_SANITIZE (this);
984 return_trace (c->check_struct (this) &&
985 vertGlyphCoverage.sanitize (c, this) &&
986 horizGlyphCoverage.sanitize (c, this) &&
987 c->check_array (glyphConstruction.arrayZ, vertGlyphCount + horizGlyphCount) &&
988 sanitize_offsets (c));
991 hb_position_t get_min_connector_overlap (hb_direction_t direction,
992 hb_font_t *font) const
993 { return font->em_scale_dir (minConnectorOverlap, direction); }
995 unsigned int get_glyph_variants (hb_codepoint_t glyph,
996 hb_direction_t direction,
998 unsigned int start_offset,
999 unsigned int *variants_count, /* IN/OUT */
1000 hb_ot_math_glyph_variant_t *variants /* OUT */) const
1001 { return get_glyph_construction (glyph, direction, font)
1002 .get_variants (direction, font, start_offset, variants_count, variants); }
1004 unsigned int get_glyph_parts (hb_codepoint_t glyph,
1005 hb_direction_t direction,
1007 unsigned int start_offset,
1008 unsigned int *parts_count, /* IN/OUT */
1009 hb_ot_math_glyph_part_t *parts /* OUT */,
1010 hb_position_t *italics_correction /* OUT */) const
1011 { return get_glyph_construction (glyph, direction, font)
1013 .get_parts (direction, font,
1014 start_offset, parts_count, parts,
1015 italics_correction); }
1018 const MathGlyphConstruction &
1019 get_glyph_construction (hb_codepoint_t glyph,
1020 hb_direction_t direction,
1021 hb_font_t *font HB_UNUSED) const
1023 bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
1024 unsigned int count = vertical ? vertGlyphCount : horizGlyphCount;
1025 const Offset16To<Coverage> &coverage = vertical ? vertGlyphCoverage
1026 : horizGlyphCoverage;
1028 unsigned int index = (this+coverage).get_coverage (glyph);
1029 if (unlikely (index >= count)) return Null (MathGlyphConstruction);
1032 index += vertGlyphCount;
1034 return this+glyphConstruction[index];
1038 HBUINT16 minConnectorOverlap;
1039 /* Minimum overlap of connecting
1040 * glyphs during glyph construction,
1041 * in design units. */
1042 Offset16To<Coverage> vertGlyphCoverage;
1043 /* Offset to Coverage table -
1044 * from the beginning of MathVariants
1046 Offset16To<Coverage> horizGlyphCoverage;
1047 /* Offset to Coverage table -
1048 * from the beginning of MathVariants
1050 HBUINT16 vertGlyphCount; /* Number of glyphs for which
1051 * information is provided for
1052 * vertically growing variants. */
1053 HBUINT16 horizGlyphCount;/* Number of glyphs for which
1054 * information is provided for
1055 * horizontally growing variants. */
1057 /* Array of offsets to MathGlyphConstruction tables - from the beginning of
1058 the MathVariants table, for shapes growing in vertical/horizontal
1060 UnsizedArrayOf<Offset16To<MathGlyphConstruction>>
1064 DEFINE_SIZE_ARRAY (10, glyphConstruction);
1069 * MATH -- Mathematical typesetting
1070 * https://docs.microsoft.com/en-us/typography/opentype/spec/math
1075 static constexpr hb_tag_t tableTag = HB_OT_TAG_MATH;
1077 bool has_data () const { return version.to_int (); }
1079 void closure_glyphs (hb_set_t *glyph_set) const
1083 hb_set_t variant_glyphs;
1084 (this+mathVariants).closure_glyphs (glyph_set, &variant_glyphs);
1085 hb_set_union (glyph_set, &variant_glyphs);
1089 bool subset (hb_subset_context_t *c) const
1091 TRACE_SUBSET (this);
1092 auto *out = c->serializer->embed (*this);
1093 if (unlikely (!out)) return_trace (false);
1095 out->mathConstants.serialize_copy (c->serializer, mathConstants, this, 0, hb_serialize_context_t::Head);
1096 out->mathGlyphInfo.serialize_subset (c, mathGlyphInfo, this);
1097 out->mathVariants.serialize_subset (c, mathVariants, this);
1098 return_trace (true);
1101 bool sanitize (hb_sanitize_context_t *c) const
1103 TRACE_SANITIZE (this);
1104 return_trace (version.sanitize (c) &&
1105 likely (version.major == 1) &&
1106 mathConstants.sanitize (c, this) &&
1107 mathGlyphInfo.sanitize (c, this) &&
1108 mathVariants.sanitize (c, this));
1111 hb_position_t get_constant (hb_ot_math_constant_t constant,
1112 hb_font_t *font) const
1113 { return (this+mathConstants).get_value (constant, font); }
1115 const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; }
1117 const MathVariants &get_variants () const { return this+mathVariants; }
1120 FixedVersion<>version; /* Version of the MATH table
1121 * initially set to 0x00010000u */
1122 Offset16To<MathConstants>
1123 mathConstants; /* MathConstants table */
1124 Offset16To<MathGlyphInfo>
1125 mathGlyphInfo; /* MathGlyphInfo table */
1126 Offset16To<MathVariants>
1127 mathVariants; /* MathVariants table */
1130 DEFINE_SIZE_STATIC (10);
1133 } /* namespace OT */
1136 #endif /* HB_OT_MATH_TABLE_HH */