1 | /* |
2 | * Copyright © 2016 Igalia S.L. |
3 | * |
4 | * This is part of HarfBuzz, a text shaping library. |
5 | * |
6 | * Permission is hereby granted, without written agreement and without |
7 | * license or royalty fees, to use, copy, modify, and distribute this |
8 | * software and its documentation for any purpose, provided that the |
9 | * above copyright notice and the following two paragraphs appear in |
10 | * all copies of this software. |
11 | * |
12 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
15 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 | * DAMAGE. |
17 | * |
18 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
21 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 | * |
24 | * Igalia Author(s): Frédéric Wang |
25 | */ |
26 | |
27 | #ifndef HB_OT_MATH_TABLE_HH |
28 | #define HB_OT_MATH_TABLE_HH |
29 | |
30 | #include "hb-open-type.hh" |
31 | #include "hb-ot-layout-common.hh" |
32 | #include "hb-ot-math.h" |
33 | |
34 | namespace OT { |
35 | |
36 | |
37 | struct MathValueRecord |
38 | { |
39 | hb_position_t get_x_value (hb_font_t *font, const void *base) const |
40 | { return font->em_scale_x (value) + (base+deviceTable).get_x_delta (font); } |
41 | hb_position_t get_y_value (hb_font_t *font, const void *base) const |
42 | { return font->em_scale_y (value) + (base+deviceTable).get_y_delta (font); } |
43 | |
44 | 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 | |
70 | struct 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 | |
198 | struct 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 | |
251 | struct 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 | |
306 | struct 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 | |
418 | struct 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 | |
482 | struct 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 | |
557 | struct 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 | |
649 | struct 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 | |
682 | struct 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 | |
694 | struct 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 (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 | |
754 | struct 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 | |
818 | struct 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 | |
882 | struct 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 | |
1073 | struct 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 | |