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
34namespace OT {
35
36
37struct 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 MathValueRecord* copy (hb_serialize_context_t *c, const void *base) const
45 {
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);
50
51 return_trace (out);
52 }
53
54 bool sanitize (hb_sanitize_context_t *c, const void *base) const
55 {
56 TRACE_SANITIZE (this);
57 return_trace (c->check_struct (this) && deviceTable.sanitize (c, base));
58 }
59
60 protected:
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. */
65
66 public:
67 DEFINE_SIZE_STATIC (4);
68};
69
70struct MathConstants
71{
72 MathConstants* copy (hb_serialize_context_t *c) const
73 {
74 TRACE_SERIALIZE (this);
75 auto *out = c->start_embed (this);
76
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);
80
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);
84
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);
89
90 if (!c->embed (radicalDegreeBottomRaisePercent)) return_trace (nullptr);
91 return_trace (out);
92 }
93
94 bool sanitize_math_value_records (hb_sanitize_context_t *c) const
95 {
96 TRACE_SANITIZE (this);
97
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);
102
103 return_trace (true);
104 }
105
106 bool sanitize (hb_sanitize_context_t *c) const
107 {
108 TRACE_SANITIZE (this);
109 return_trace (c->check_struct (this) && sanitize_math_value_records (c));
110 }
111
112 hb_position_t get_value (hb_ot_math_constant_t constant,
113 hb_font_t *font) const
114 {
115 switch (constant) {
116
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];
120
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]);
124
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);
130
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);
179
180 case HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT:
181 return radicalDegreeBottomRaisePercent;
182
183 default:
184 return 0;
185 }
186 }
187
188 protected:
189 HBINT16 percentScaleDown[2];
190 HBUINT16 minHeight[2];
191 MathValueRecord mathValueRecords[51];
192 HBINT16 radicalDegreeBottomRaisePercent;
193
194 public:
195 DEFINE_SIZE_STATIC (214);
196};
197
198struct MathItalicsCorrectionInfo
199{
200 bool subset (hb_subset_context_t *c) const
201 {
202 TRACE_SUBSET (this);
203 const hb_set_t &glyphset = c->plan->_glyphset_mathed;
204 const hb_map_t &glyph_map = *c->plan->glyph_map;
205
206 auto *out = c->serializer->start_embed (*this);
207 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
208
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)
213 | hb_map (hb_first)
214 | hb_map (glyph_map)
215 | hb_sink (new_coverage)
216 ;
217
218 out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
219 return_trace (true);
220 }
221
222 bool sanitize (hb_sanitize_context_t *c) const
223 {
224 TRACE_SANITIZE (this);
225 return_trace (c->check_struct (this) &&
226 coverage.sanitize (c, this) &&
227 italicsCorrection.sanitize (c, this));
228 }
229
230 hb_position_t get_value (hb_codepoint_t glyph,
231 hb_font_t *font) const
232 {
233 unsigned int index = (this+coverage).get_coverage (glyph);
234 return italicsCorrection[index].get_x_value (font, this);
235 }
236
237 protected:
238 Offset16To<Coverage> coverage; /* Offset to Coverage table -
239 * from the beginning of
240 * MathItalicsCorrectionInfo
241 * table. */
242 Array16Of<MathValueRecord> italicsCorrection; /* Array of MathValueRecords
243 * defining italics correction
244 * values for each
245 * covered glyph. */
246
247 public:
248 DEFINE_SIZE_ARRAY (4, italicsCorrection);
249};
250
251struct MathTopAccentAttachment
252{
253 bool subset (hb_subset_context_t *c) const
254 {
255 TRACE_SUBSET (this);
256 const hb_set_t &glyphset = c->plan->_glyphset_mathed;
257 const hb_map_t &glyph_map = *c->plan->glyph_map;
258
259 auto *out = c->serializer->start_embed (*this);
260 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
261
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)
266 | hb_map (hb_first)
267 | hb_map (glyph_map)
268 | hb_sink (new_coverage)
269 ;
270
271 out->topAccentCoverage.serialize_serialize (c->serializer, new_coverage.iter ());
272 return_trace (true);
273 }
274
275 bool sanitize (hb_sanitize_context_t *c) const
276 {
277 TRACE_SANITIZE (this);
278 return_trace (c->check_struct (this) &&
279 topAccentCoverage.sanitize (c, this) &&
280 topAccentAttachment.sanitize (c, this));
281 }
282
283 hb_position_t get_value (hb_codepoint_t glyph,
284 hb_font_t *font) const
285 {
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);
290 }
291
292 protected:
293 Offset16To<Coverage> topAccentCoverage; /* Offset to Coverage table -
294 * from the beginning of
295 * MathTopAccentAttachment
296 * table. */
297 Array16Of<MathValueRecord> topAccentAttachment; /* Array of MathValueRecords
298 * defining top accent
299 * attachment points for each
300 * covered glyph. */
301
302 public:
303 DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment);
304};
305
306struct MathKern
307{
308 MathKern* copy (hb_serialize_context_t *c) const
309 {
310 TRACE_SERIALIZE (this);
311 auto *out = c->start_embed (this);
312
313 if (unlikely (!c->embed (heightCount))) return_trace (nullptr);
314
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);
319
320 return_trace (out);
321 }
322
323 bool sanitize_math_value_records (hb_sanitize_context_t *c) const
324 {
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);
329 return_trace (true);
330 }
331
332 bool sanitize (hb_sanitize_context_t *c) const
333 {
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));
338 }
339
340 hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
341 {
342 const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
343 const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
344 int sign = font->y_scale < 0 ? -1 : +1;
345
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
348 *
349 * correctionHeight[i-1] < correction_height <= correctionHeight[i]
350 *
351 * makes the result consistent with the limit cases and we can just use the
352 * binary search algorithm of std::upper_bound:
353 */
354 unsigned int i = 0;
355 unsigned int count = heightCount;
356 while (count > 0)
357 {
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)
361 {
362 i += half + 1;
363 count -= half + 1;
364 } else
365 count = half;
366 }
367 return kernValue[i].get_x_value (font, this);
368 }
369
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
374 {
375 const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
376 const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
377 const unsigned int entriesCount = heightCount + 1;
378
379 if (entries_count)
380 {
381 unsigned int start = hb_min (start_offset, entriesCount);
382 unsigned int end = hb_min (start + *entries_count, entriesCount);
383 *entries_count = end - start;
384
385 for (unsigned int i = 0; i < *entries_count; i++) {
386 unsigned int j = start + i;
387
388 hb_position_t max_height;
389 if (j == heightCount) {
390 max_height = INT32_MAX;
391 } else {
392 max_height = correctionHeight[j].get_y_value (font, this);
393 }
394
395 kern_entries[i] = {max_height, kernValue[j].get_x_value (font, this)};
396 }
397 }
398 return entriesCount;
399 }
400
401 protected:
402 HBUINT16 heightCount;
403 UnsizedArrayOf<MathValueRecord>
404 mathValueRecordsZ;
405 /* Array of correction heights at
406 * which the kern value changes.
407 * Sorted by the height value in
408 * design units (heightCount entries),
409 * Followed by:
410 * Array of kern values corresponding
411 * to heights. (heightCount+1 entries).
412 */
413
414 public:
415 DEFINE_SIZE_ARRAY (2, mathValueRecordsZ);
416};
417
418struct MathKernInfoRecord
419{
420 MathKernInfoRecord* copy (hb_serialize_context_t *c, const void *base) const
421 {
422 TRACE_SERIALIZE (this);
423 auto *out = c->embed (this);
424 if (unlikely (!out)) return_trace (nullptr);
425
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);
429
430 return_trace (out);
431 }
432
433 bool sanitize (hb_sanitize_context_t *c, const void *base) const
434 {
435 TRACE_SANITIZE (this);
436
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);
441
442 return_trace (true);
443 }
444
445 hb_position_t get_kerning (hb_ot_math_kern_t kern,
446 hb_position_t correction_height,
447 hb_font_t *font,
448 const void *base) const
449 {
450 unsigned int idx = kern;
451 if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0;
452 return (base+mathKern[idx]).get_value (correction_height, font);
453 }
454
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 */
459 hb_font_t *font,
460 const void *base) const
461 {
462 unsigned int idx = kern;
463 if (unlikely (idx >= ARRAY_LENGTH (mathKern)) || !mathKern[idx]) {
464 if (entries_count) *entries_count = 0;
465 return 0;
466 }
467 return (base+mathKern[idx]).get_entries (start_offset,
468 entries_count,
469 kern_entries,
470 font);
471 }
472
473 protected:
474 /* Offset to MathKern table for each corner -
475 * from the beginning of MathKernInfo table. May be NULL. */
476 Offset16To<MathKern> mathKern[4];
477
478 public:
479 DEFINE_SIZE_STATIC (8);
480};
481
482struct MathKernInfo
483{
484 bool subset (hb_subset_context_t *c) const
485 {
486 TRACE_SUBSET (this);
487 const hb_set_t &glyphset = c->plan->_glyphset_mathed;
488 const hb_map_t &glyph_map = *c->plan->glyph_map;
489
490 auto *out = c->serializer->start_embed (*this);
491 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
492
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)
497 | hb_map (hb_first)
498 | hb_map (glyph_map)
499 | hb_sink (new_coverage)
500 ;
501
502 out->mathKernCoverage.serialize_serialize (c->serializer, new_coverage.iter ());
503 return_trace (true);
504 }
505
506 bool sanitize (hb_sanitize_context_t *c) const
507 {
508 TRACE_SANITIZE (this);
509 return_trace (c->check_struct (this) &&
510 mathKernCoverage.sanitize (c, this) &&
511 mathKernInfoRecords.sanitize (c, this));
512 }
513
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
518 {
519 unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
520 return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this);
521 }
522
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
529 {
530 unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
531 return mathKernInfoRecords[index].get_kernings (kern,
532 start_offset,
533 entries_count,
534 kern_entries,
535 font,
536 this);
537 }
538
539 protected:
540 Offset16To<Coverage>
541 mathKernCoverage;
542 /* Offset to Coverage table -
543 * from the beginning of the
544 * MathKernInfo table. */
545 Array16Of<MathKernInfoRecord>
546 mathKernInfoRecords;
547 /* Array of MathKernInfoRecords,
548 * per-glyph information for
549 * mathematical positioning
550 * of subscripts and
551 * superscripts. */
552
553 public:
554 DEFINE_SIZE_ARRAY (4, mathKernInfoRecords);
555};
556
557struct MathGlyphInfo
558{
559 bool subset (hb_subset_context_t *c) const
560 {
561 TRACE_SUBSET (this);
562 auto *out = c->serializer->embed (*this);
563 if (unlikely (!out)) return_trace (false);
564
565 out->mathItalicsCorrectionInfo.serialize_subset (c, mathItalicsCorrectionInfo, this);
566 out->mathTopAccentAttachment.serialize_subset (c, mathTopAccentAttachment, this);
567
568 const hb_set_t &glyphset = c->plan->_glyphset_mathed;
569 const hb_map_t &glyph_map = *c->plan->glyph_map;
570
571 auto it =
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)
576 ;
577
578 if (it) out->extendedShapeCoverage.serialize_serialize (c->serializer, it);
579 else out->extendedShapeCoverage = 0;
580
581 out->mathKernInfo.serialize_subset (c, mathKernInfo, this);
582 return_trace (true);
583 }
584
585 bool sanitize (hb_sanitize_context_t *c) const
586 {
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));
593 }
594
595 hb_position_t
596 get_italics_correction (hb_codepoint_t glyph, hb_font_t *font) const
597 { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); }
598
599 hb_position_t
600 get_top_accent_attachment (hb_codepoint_t glyph, hb_font_t *font) const
601 { return (this+mathTopAccentAttachment).get_value (glyph, font); }
602
603 bool is_extended_shape (hb_codepoint_t glyph) const
604 { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; }
605
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); }
611
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,
619 kern,
620 start_offset,
621 entries_count,
622 kern_entries,
623 font); }
624
625 protected:
626 /* Offset to MathItalicsCorrectionInfo table -
627 * from the beginning of MathGlyphInfo table. */
628 Offset16To<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo;
629
630 /* Offset to MathTopAccentAttachment table -
631 * from the beginning of MathGlyphInfo table. */
632 Offset16To<MathTopAccentAttachment> mathTopAccentAttachment;
633
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;
640
641 /* Offset to MathKernInfo table -
642 * from the beginning of MathGlyphInfo table. */
643 Offset16To<MathKernInfo> mathKernInfo;
644
645 public:
646 DEFINE_SIZE_STATIC (8);
647};
648
649struct MathGlyphVariantRecord
650{
651 friend struct MathGlyphConstruction;
652
653 bool subset (hb_subset_context_t *c) const
654 {
655 TRACE_SUBSET (this);
656 auto *out = c->serializer->embed (this);
657 if (unlikely (!out)) return_trace (false);
658
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));
661 }
662
663 bool sanitize (hb_sanitize_context_t *c) const
664 {
665 TRACE_SANITIZE (this);
666 return_trace (c->check_struct (this));
667 }
668
669 void closure_glyphs (hb_set_t *variant_glyphs) const
670 { variant_glyphs->add (variantGlyph); }
671
672 protected:
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. */
677
678 public:
679 DEFINE_SIZE_STATIC (4);
680};
681
682struct PartFlags : HBUINT16
683{
684 enum Flags {
685 Extender = 0x0001u, /* If set, the part can be skipped or repeated. */
686
687 Defined = 0x0001u, /* All defined flags. */
688 };
689
690 public:
691 DEFINE_SIZE_STATIC (2);
692};
693
694struct MathGlyphPartRecord
695{
696 bool subset (hb_subset_context_t *c) const
697 {
698 TRACE_SUBSET (this);
699 auto *out = c->serializer->embed (this);
700 if (unlikely (!out)) return_trace (false);
701
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));
704 }
705
706 bool sanitize (hb_sanitize_context_t *c) const
707 {
708 TRACE_SANITIZE (this);
709 return_trace (c->check_struct (this));
710 }
711
712 void extract (hb_ot_math_glyph_part_t &out,
713 int64_t mult,
714 hb_font_t *font) const
715 {
716 out.glyph = glyph;
717
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);
721
722 static_assert ((unsigned int) HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER ==
723 (unsigned int) PartFlags::Extender, "");
724
725 out.flags = (hb_ot_math_glyph_part_flags_t)
726 (unsigned int)
727 (partFlags & PartFlags::Defined);
728 }
729
730 void closure_glyphs (hb_set_t *variant_glyphs) const
731 { variant_glyphs->add (glyph); }
732
733 protected:
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
744 * the extension. */
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. */
749
750 public:
751 DEFINE_SIZE_STATIC (10);
752};
753
754struct MathGlyphAssembly
755{
756 bool subset (hb_subset_context_t *c) const
757 {
758 TRACE_SUBSET (this);
759
760 if (!c->serializer->copy (italicsCorrection, this)) return_trace (false);
761 if (!c->serializer->copy<HBUINT16> (partRecords.len)) return_trace (false);
762
763 for (const auto& record : partRecords.iter ())
764 if (!record.subset (c)) return_trace (false);
765 return_trace (true);
766 }
767
768 bool sanitize (hb_sanitize_context_t *c) const
769 {
770 TRACE_SANITIZE (this);
771 return_trace (c->check_struct (this) &&
772 italicsCorrection.sanitize (c, this) &&
773 partRecords.sanitize (c));
774 }
775
776 unsigned int get_parts (hb_direction_t direction,
777 hb_font_t *font,
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
782 {
783 if (parts_count)
784 {
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);
789 }
790
791 if (italics_correction)
792 *italics_correction = italicsCorrection.get_x_value (font, this);
793
794 return partRecords.len;
795 }
796
797 void closure_glyphs (hb_set_t *variant_glyphs) const
798 {
799 for (const auto& _ : partRecords.iter ())
800 _.closure_glyphs (variant_glyphs);
801 }
802
803 protected:
804 MathValueRecord
805 italicsCorrection;
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
812 * top. */
813
814 public:
815 DEFINE_SIZE_ARRAY (6, partRecords);
816};
817
818struct MathGlyphConstruction
819{
820 bool subset (hb_subset_context_t *c) const
821 {
822 TRACE_SUBSET (this);
823 auto *out = c->serializer->start_embed (*this);
824 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
825
826 out->glyphAssembly.serialize_subset (c, glyphAssembly, this);
827
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);
832
833 return_trace (true);
834 }
835
836 bool sanitize (hb_sanitize_context_t *c) const
837 {
838 TRACE_SANITIZE (this);
839 return_trace (c->check_struct (this) &&
840 glyphAssembly.sanitize (c, this) &&
841 mathGlyphVariantRecord.sanitize (c));
842 }
843
844 const MathGlyphAssembly &get_assembly () const { return this+glyphAssembly; }
845
846 unsigned int get_variants (hb_direction_t direction,
847 hb_font_t *font,
848 unsigned int start_offset,
849 unsigned int *variants_count, /* IN/OUT */
850 hb_ot_math_glyph_variant_t *variants /* OUT */) const
851 {
852 if (variants_count)
853 {
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)};
858 }
859 return mathGlyphVariantRecord.len;
860 }
861
862 void closure_glyphs (hb_set_t *variant_glyphs) const
863 {
864 (this+glyphAssembly).closure_glyphs (variant_glyphs);
865
866 for (const auto& _ : mathGlyphVariantRecord.iter ())
867 _.closure_glyphs (variant_glyphs);
868 }
869
870 protected:
871 /* Offset to MathGlyphAssembly table for this shape - from the beginning of
872 MathGlyphConstruction table. May be NULL. */
873 Offset16To<MathGlyphAssembly> glyphAssembly;
874
875 /* MathGlyphVariantRecords for alternative variants of the glyphs. */
876 Array16Of<MathGlyphVariantRecord> mathGlyphVariantRecord;
877
878 public:
879 DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord);
880};
881
882struct MathVariants
883{
884 void closure_glyphs (const hb_set_t *glyph_set,
885 hb_set_t *variant_glyphs) const
886 {
887 const hb_array_t<const Offset16To<MathGlyphConstruction>> glyph_construction_offsets = glyphConstruction.as_array (vertGlyphCount + horizGlyphCount);
888
889 if (vertGlyphCoverage)
890 {
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)
894 | hb_map (hb_second)
895 | hb_map (hb_add (this))
896 | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); })
897 ;
898 }
899
900 if (horizGlyphCoverage)
901 {
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)
905 | hb_map (hb_second)
906 | hb_map (hb_add (this))
907 | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); })
908 ;
909 }
910 }
911
912 void collect_coverage_and_indices (hb_sorted_vector_t<hb_codepoint_t>& new_coverage,
913 const Offset16To<Coverage>& coverage,
914 unsigned i,
915 unsigned end_index,
916 hb_set_t& indices,
917 const hb_set_t& glyphset,
918 const hb_map_t& glyph_map) const
919 {
920 if (!coverage) return;
921
922 for (const auto _ : (this+coverage).iter ())
923 {
924 if (i >= end_index) return;
925 if (glyphset.has (_))
926 {
927 unsigned new_gid = glyph_map.get (_);
928 new_coverage.push (new_gid);
929 indices.add (i);
930 }
931 i++;
932 }
933 }
934
935 bool subset (hb_subset_context_t *c) const
936 {
937 TRACE_SUBSET (this);
938 const hb_set_t &glyphset = c->plan->_glyphset_mathed;
939 const hb_map_t &glyph_map = *c->plan->glyph_map;
940
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);
945
946 hb_sorted_vector_t<hb_codepoint_t> new_vert_coverage;
947 hb_sorted_vector_t<hb_codepoint_t> new_hori_coverage;
948 hb_set_t indices;
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);
951
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);
956
957 for (unsigned i : indices.iter ())
958 {
959 auto *o = c->serializer->embed (glyphConstruction[i]);
960 if (!o) return_trace (false);
961 o->serialize_subset (c, glyphConstruction[i], this);
962 }
963
964 if (new_vert_coverage)
965 out->vertGlyphCoverage.serialize_serialize (c->serializer, new_vert_coverage.iter ());
966
967 if (new_hori_coverage)
968 out->horizGlyphCoverage.serialize_serialize (c->serializer, new_hori_coverage.iter ());
969 return_trace (true);
970 }
971
972 bool sanitize_offsets (hb_sanitize_context_t *c) const
973 {
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);
978 return_trace (true);
979 }
980
981 bool sanitize (hb_sanitize_context_t *c) const
982 {
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));
989 }
990
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); }
994
995 unsigned int get_glyph_variants (hb_codepoint_t glyph,
996 hb_direction_t direction,
997 hb_font_t *font,
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); }
1003
1004 unsigned int get_glyph_parts (hb_codepoint_t glyph,
1005 hb_direction_t direction,
1006 hb_font_t *font,
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)
1012 .get_assembly ()
1013 .get_parts (direction, font,
1014 start_offset, parts_count, parts,
1015 italics_correction); }
1016
1017 private:
1018 const MathGlyphConstruction &
1019 get_glyph_construction (hb_codepoint_t glyph,
1020 hb_direction_t direction,
1021 hb_font_t *font HB_UNUSED) const
1022 {
1023 bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
1024 unsigned int count = vertical ? vertGlyphCount : horizGlyphCount;
1025 const Offset16To<Coverage> &coverage = vertical ? vertGlyphCoverage
1026 : horizGlyphCoverage;
1027
1028 unsigned int index = (this+coverage).get_coverage (glyph);
1029 if (unlikely (index >= count)) return Null (MathGlyphConstruction);
1030
1031 if (!vertical)
1032 index += vertGlyphCount;
1033
1034 return this+glyphConstruction[index];
1035 }
1036
1037 protected:
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
1045 * table. */
1046 Offset16To<Coverage> horizGlyphCoverage;
1047 /* Offset to Coverage table -
1048 * from the beginning of MathVariants
1049 * table. */
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. */
1056
1057 /* Array of offsets to MathGlyphConstruction tables - from the beginning of
1058 the MathVariants table, for shapes growing in vertical/horizontal
1059 direction. */
1060 UnsizedArrayOf<Offset16To<MathGlyphConstruction>>
1061 glyphConstruction;
1062
1063 public:
1064 DEFINE_SIZE_ARRAY (10, glyphConstruction);
1065};
1066
1067
1068/*
1069 * MATH -- Mathematical typesetting
1070 * https://docs.microsoft.com/en-us/typography/opentype/spec/math
1071 */
1072
1073struct MATH
1074{
1075 static constexpr hb_tag_t tableTag = HB_OT_TAG_MATH;
1076
1077 bool has_data () const { return version.to_int (); }
1078
1079 void closure_glyphs (hb_set_t *glyph_set) const
1080 {
1081 if (mathVariants)
1082 {
1083 hb_set_t variant_glyphs;
1084 (this+mathVariants).closure_glyphs (glyph_set, &variant_glyphs);
1085 hb_set_union (glyph_set, &variant_glyphs);
1086 }
1087 }
1088
1089 bool subset (hb_subset_context_t *c) const
1090 {
1091 TRACE_SUBSET (this);
1092 auto *out = c->serializer->embed (*this);
1093 if (unlikely (!out)) return_trace (false);
1094
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);
1099 }
1100
1101 bool sanitize (hb_sanitize_context_t *c) const
1102 {
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));
1109 }
1110
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); }
1114
1115 const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; }
1116
1117 const MathVariants &get_variants () const { return this+mathVariants; }
1118
1119 protected:
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 */
1128
1129 public:
1130 DEFINE_SIZE_STATIC (10);
1131};
1132
1133} /* namespace OT */
1134
1135
1136#endif /* HB_OT_MATH_TABLE_HH */
1137