1//! A [Math Table](https://docs.microsoft.com/en-us/typography/opentype/spec/math) implementation.
2
3use crate::gpos::Device;
4use crate::opentype_layout::Coverage;
5use crate::parser::{
6 FromData, FromSlice, LazyArray16, LazyOffsetArray16, Offset, Offset16, Stream,
7};
8use crate::GlyphId;
9
10/// A [Math Value](https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathvaluerecord)
11/// with optional device corrections.
12#[derive(Clone, Copy, Debug)]
13pub struct MathValue<'a> {
14 /// The X or Y value in font design units.
15 pub value: i16,
16 /// Device corrections for this value.
17 pub device: Option<Device<'a>>,
18}
19
20impl<'a> MathValue<'a> {
21 fn parse(data: &'a [u8], parent: &'a [u8]) -> Option<Self> {
22 Some(MathValueRecord::parse(data)?.get(data:parent))
23 }
24}
25
26/// A math value record with unresolved offset.
27#[derive(Clone, Copy)]
28struct MathValueRecord {
29 value: i16,
30 device_offset: Option<Offset16>,
31}
32
33impl FromData for MathValueRecord {
34 const SIZE: usize = 4;
35
36 fn parse(data: &[u8]) -> Option<Self> {
37 let mut s: Stream<'_> = Stream::new(data);
38 let value: i16 = s.read::<i16>()?;
39 let device_offset: Option = s.read::<Option<Offset16>>()?;
40 Some(MathValueRecord {
41 value,
42 device_offset,
43 })
44 }
45}
46
47impl MathValueRecord {
48 fn get(self, data: &[u8]) -> MathValue {
49 let device: Option> = self
50 .device_offset
51 .and_then(|offset: Offset16| data.get(index:offset.to_usize()..))
52 .and_then(Device::parse);
53 MathValue {
54 value: self.value,
55 device,
56 }
57 }
58}
59
60/// A mapping from glyphs to
61/// [Math Values](https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathvaluerecord).
62#[derive(Clone, Copy)]
63pub struct MathValues<'a> {
64 data: &'a [u8],
65 coverage: Coverage<'a>,
66 records: LazyArray16<'a, MathValueRecord>,
67}
68
69impl<'a> FromSlice<'a> for MathValues<'a> {
70 fn parse(data: &'a [u8]) -> Option<Self> {
71 let mut s: Stream<'_> = Stream::new(data);
72 let coverage: Coverage<'_> = s.parse_at_offset16::<Coverage>(data)?;
73 let count: u16 = s.read::<u16>()?;
74 let records: LazyArray16<'_, MathValueRecord> = s.read_array16::<MathValueRecord>(count)?;
75 Some(MathValues {
76 data,
77 coverage,
78 records,
79 })
80 }
81}
82
83impl<'a> MathValues<'a> {
84 /// Returns the value for the glyph or `None` if it is not covered.
85 #[inline]
86 pub fn get(&self, glyph: GlyphId) -> Option<MathValue<'a>> {
87 let index: u16 = self.coverage.get(glyph)?;
88 Some(self.records.get(index)?.get(self.data))
89 }
90}
91
92impl core::fmt::Debug for MathValues<'_> {
93 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
94 write!(f, "MathValues {{ ... }}")
95 }
96}
97
98/// A [Math Constants Table](https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathconstants-table).
99#[derive(Clone, Copy)]
100pub struct Constants<'a> {
101 data: &'a [u8],
102}
103
104impl<'a> FromSlice<'a> for Constants<'a> {
105 fn parse(data: &'a [u8]) -> Option<Self> {
106 Some(Constants { data })
107 }
108}
109
110impl core::fmt::Debug for Constants<'_> {
111 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
112 write!(f, "Constants {{ ... }}")
113 }
114}
115
116const SCRIPT_PERCENT_SCALE_DOWN_OFFSET: usize = 0;
117const SCRIPT_SCRIPT_PERCENT_SCALE_DOWN_OFFSET: usize = 2;
118const DELIMITED_SUB_FORMULA_MIN_HEIGHT_OFFSET: usize = 4;
119const DISPLAY_OPERATOR_MIN_HEIGHT_OFFSET: usize = 6;
120const MATH_LEADING_OFFSET: usize = 8;
121const AXIS_HEIGHT_OFFSET: usize = 12;
122const ACCENT_BASE_HEIGHT_OFFSET: usize = 16;
123const FLATTENED_ACCENT_BASE_HEIGHT_OFFSET: usize = 20;
124const SUBSCRIPT_SHIFT_DOWN_OFFSET: usize = 24;
125const SUBSCRIPT_TOP_MAX_OFFSET: usize = 28;
126const SUBSCRIPT_BASELINE_DROP_MIN_OFFSET: usize = 32;
127const SUPERSCRIPT_SHIFT_UP_OFFSET: usize = 36;
128const SUPERSCRIPT_SHIFT_UP_CRAMPED_OFFSET: usize = 40;
129const SUPERSCRIPT_BOTTOM_MIN_OFFSET: usize = 44;
130const SUPERSCRIPT_BASELINE_DROP_MAX_OFFSET: usize = 48;
131const SUB_SUPERSCRIPT_GAP_MIN_OFFSET: usize = 52;
132const SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT_OFFSET: usize = 56;
133const SPACE_AFTER_SCRIPT_OFFSET: usize = 60;
134const UPPER_LIMIT_GAP_MIN_OFFSET: usize = 64;
135const UPPER_LIMIT_BASELINE_RISE_MIN_OFFSET: usize = 68;
136const LOWER_LIMIT_GAP_MIN_OFFSET: usize = 72;
137const LOWER_LIMIT_BASELINE_DROP_MIN_OFFSET: usize = 76;
138const STACK_TOP_SHIFT_UP_OFFSET: usize = 80;
139const STACK_TOP_DISPLAY_STYLE_SHIFT_UP_OFFSET: usize = 84;
140const STACK_BOTTOM_SHIFT_DOWN_OFFSET: usize = 88;
141const STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN_OFFSET: usize = 92;
142const STACK_GAP_MIN_OFFSET: usize = 96;
143const STACK_DISPLAY_STYLE_GAP_MIN_OFFSET: usize = 100;
144const STRETCH_STACK_TOP_SHIFT_UP_OFFSET: usize = 104;
145const STRETCH_STACK_BOTTOM_SHIFT_DOWN_OFFSET: usize = 108;
146const STRETCH_STACK_GAP_ABOVE_MIN_OFFSET: usize = 112;
147const STRETCH_STACK_GAP_BELOW_MIN_OFFSET: usize = 116;
148const FRACTION_NUMERATOR_SHIFT_UP_OFFSET: usize = 120;
149const FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP_OFFSET: usize = 124;
150const FRACTION_DENOMINATOR_SHIFT_DOWN_OFFSET: usize = 128;
151const FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN_OFFSET: usize = 132;
152const FRACTION_NUMERATOR_GAP_MIN_OFFSET: usize = 136;
153const FRACTION_NUM_DISPLAY_STYLE_GAP_MIN_OFFSET: usize = 140;
154const FRACTION_RULE_THICKNESS_OFFSET: usize = 144;
155const FRACTION_DENOMINATOR_GAP_MIN_OFFSET: usize = 148;
156const FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN_OFFSET: usize = 152;
157const SKEWED_FRACTION_HORIZONTAL_GAP_OFFSET: usize = 156;
158const SKEWED_FRACTION_VERTICAL_GAP_OFFSET: usize = 160;
159const OVERBAR_VERTICAL_GAP_OFFSET: usize = 164;
160const OVERBAR_RULE_THICKNESS_OFFSET: usize = 168;
161const OVERBAR_EXTRA_ASCENDER_OFFSET: usize = 172;
162const UNDERBAR_VERTICAL_GAP_OFFSET: usize = 176;
163const UNDERBAR_RULE_THICKNESS_OFFSET: usize = 180;
164const UNDERBAR_EXTRA_DESCENDER_OFFSET: usize = 184;
165const RADICAL_VERTICAL_GAP_OFFSET: usize = 188;
166const RADICAL_DISPLAY_STYLE_VERTICAL_GAP_OFFSET: usize = 192;
167const RADICAL_RULE_THICKNESS_OFFSET: usize = 196;
168const RADICAL_EXTRA_ASCENDER_OFFSET: usize = 200;
169const RADICAL_KERN_BEFORE_DEGREE_OFFSET: usize = 204;
170const RADICAL_KERN_AFTER_DEGREE_OFFSET: usize = 208;
171const RADICAL_DEGREE_BOTTOM_RAISE_PERCENT_OFFSET: usize = 212;
172
173impl<'a> Constants<'a> {
174 /// Percentage of scaling down for level 1 superscripts and subscripts.
175 #[inline]
176 pub fn script_percent_scale_down(&self) -> i16 {
177 self.read_i16(SCRIPT_PERCENT_SCALE_DOWN_OFFSET)
178 }
179
180 /// Percentage of scaling down for level 2 (scriptScript) superscripts and subscripts.
181 #[inline]
182 pub fn script_script_percent_scale_down(&self) -> i16 {
183 self.read_i16(SCRIPT_SCRIPT_PERCENT_SCALE_DOWN_OFFSET)
184 }
185
186 /// Minimum height required for a delimited expression (contained within parentheses, etc.) to
187 /// be treated as a sub-formula.
188 #[inline]
189 pub fn delimited_sub_formula_min_height(&self) -> u16 {
190 self.read_u16(DELIMITED_SUB_FORMULA_MIN_HEIGHT_OFFSET)
191 }
192
193 /// Minimum height of n-ary operators (such as integral and summation) for formulas in display
194 /// mode (that is, appearing as standalone page elements, not embedded inline within text).
195 #[inline]
196 pub fn display_operator_min_height(&self) -> u16 {
197 self.read_u16(DISPLAY_OPERATOR_MIN_HEIGHT_OFFSET)
198 }
199
200 /// White space to be left between math formulas to ensure proper line spacing.
201 #[inline]
202 pub fn math_leading(&self) -> MathValue<'a> {
203 self.read_record(MATH_LEADING_OFFSET)
204 }
205
206 /// Axis height of the font.
207 #[inline]
208 pub fn axis_height(&self) -> MathValue<'a> {
209 self.read_record(AXIS_HEIGHT_OFFSET)
210 }
211
212 /// Maximum (ink) height of accent base that does not require raising the accents.
213 #[inline]
214 pub fn accent_base_height(&self) -> MathValue<'a> {
215 self.read_record(ACCENT_BASE_HEIGHT_OFFSET)
216 }
217
218 /// Maximum (ink) height of accent base that does not require flattening the accents.
219 #[inline]
220 pub fn flattened_accent_base_height(&self) -> MathValue<'a> {
221 self.read_record(FLATTENED_ACCENT_BASE_HEIGHT_OFFSET)
222 }
223
224 /// The standard shift down applied to subscript elements.
225 #[inline]
226 pub fn subscript_shift_down(&self) -> MathValue<'a> {
227 self.read_record(SUBSCRIPT_SHIFT_DOWN_OFFSET)
228 }
229
230 /// Maximum allowed height of the (ink) top of subscripts that does not require moving
231 /// subscripts further down.
232 #[inline]
233 pub fn subscript_top_max(&self) -> MathValue<'a> {
234 self.read_record(SUBSCRIPT_TOP_MAX_OFFSET)
235 }
236
237 /// Minimum allowed drop of the baseline of subscripts relative to the (ink) bottom of the
238 /// base.
239 #[inline]
240 pub fn subscript_baseline_drop_min(&self) -> MathValue<'a> {
241 self.read_record(SUBSCRIPT_BASELINE_DROP_MIN_OFFSET)
242 }
243
244 /// Standard shift up applied to superscript elements.
245 #[inline]
246 pub fn superscript_shift_up(&self) -> MathValue<'a> {
247 self.read_record(SUPERSCRIPT_SHIFT_UP_OFFSET)
248 }
249
250 /// Standard shift of superscripts relative to the base, in cramped style.
251 #[inline]
252 pub fn superscript_shift_up_cramped(&self) -> MathValue<'a> {
253 self.read_record(SUPERSCRIPT_SHIFT_UP_CRAMPED_OFFSET)
254 }
255
256 /// Minimum allowed height of the (ink) bottom of superscripts that does not require moving
257 /// subscripts further up.
258 #[inline]
259 pub fn superscript_bottom_min(&self) -> MathValue<'a> {
260 self.read_record(SUPERSCRIPT_BOTTOM_MIN_OFFSET)
261 }
262
263 /// Maximum allowed drop of the baseline of superscripts relative to the (ink) top of the
264 /// base.
265 #[inline]
266 pub fn superscript_baseline_drop_max(&self) -> MathValue<'a> {
267 self.read_record(SUPERSCRIPT_BASELINE_DROP_MAX_OFFSET)
268 }
269
270 /// Minimum gap between the superscript and subscript ink.
271 #[inline]
272 pub fn sub_superscript_gap_min(&self) -> MathValue<'a> {
273 self.read_record(SUB_SUPERSCRIPT_GAP_MIN_OFFSET)
274 }
275
276 /// The maximum level to which the (ink) bottom of superscript can be pushed to increase the
277 /// gap between superscript and subscript, before subscript starts being moved down.
278 #[inline]
279 pub fn superscript_bottom_max_with_subscript(&self) -> MathValue<'a> {
280 self.read_record(SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT_OFFSET)
281 }
282
283 /// Extra white space to be added after each subscript and superscript.
284 #[inline]
285 pub fn space_after_script(&self) -> MathValue<'a> {
286 self.read_record(SPACE_AFTER_SCRIPT_OFFSET)
287 }
288
289 /// Minimum gap between the (ink) bottom of the upper limit, and the (ink) top of the base
290 /// operator.
291 #[inline]
292 pub fn upper_limit_gap_min(&self) -> MathValue<'a> {
293 self.read_record(UPPER_LIMIT_GAP_MIN_OFFSET)
294 }
295
296 /// Minimum distance between baseline of upper limit and (ink) top of the base operator.
297 #[inline]
298 pub fn upper_limit_baseline_rise_min(&self) -> MathValue<'a> {
299 self.read_record(UPPER_LIMIT_BASELINE_RISE_MIN_OFFSET)
300 }
301
302 /// Minimum gap between (ink) top of the lower limit, and (ink) bottom of the base operator.
303 #[inline]
304 pub fn lower_limit_gap_min(&self) -> MathValue<'a> {
305 self.read_record(LOWER_LIMIT_GAP_MIN_OFFSET)
306 }
307
308 /// Minimum distance between baseline of the lower limit and (ink) bottom of the base operator.
309 #[inline]
310 pub fn lower_limit_baseline_drop_min(&self) -> MathValue<'a> {
311 self.read_record(LOWER_LIMIT_BASELINE_DROP_MIN_OFFSET)
312 }
313
314 /// Standard shift up applied to the top element of a stack.
315 #[inline]
316 pub fn stack_top_shift_up(&self) -> MathValue<'a> {
317 self.read_record(STACK_TOP_SHIFT_UP_OFFSET)
318 }
319
320 /// Standard shift up applied to the top element of a stack in display style.
321 #[inline]
322 pub fn stack_top_display_style_shift_up(&self) -> MathValue<'a> {
323 self.read_record(STACK_TOP_DISPLAY_STYLE_SHIFT_UP_OFFSET)
324 }
325
326 /// Standard shift down applied to the bottom element of a stack.
327 #[inline]
328 pub fn stack_bottom_shift_down(&self) -> MathValue<'a> {
329 self.read_record(STACK_BOTTOM_SHIFT_DOWN_OFFSET)
330 }
331
332 /// Standard shift down applied to the bottom element of a stack in display style.
333 #[inline]
334 pub fn stack_bottom_display_style_shift_down(&self) -> MathValue<'a> {
335 self.read_record(STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN_OFFSET)
336 }
337
338 /// Minimum gap between (ink) bottom of the top element of a stack, and the (ink) top of the
339 /// bottom element.
340 #[inline]
341 pub fn stack_gap_min(&self) -> MathValue<'a> {
342 self.read_record(STACK_GAP_MIN_OFFSET)
343 }
344
345 /// Minimum gap between (ink) bottom of the top element of a stack, and the (ink) top of the
346 /// bottom element in display style.
347 #[inline]
348 pub fn stack_display_style_gap_min(&self) -> MathValue<'a> {
349 self.read_record(STACK_DISPLAY_STYLE_GAP_MIN_OFFSET)
350 }
351
352 /// Standard shift up applied to the top element of the stretch stack.
353 #[inline]
354 pub fn stretch_stack_top_shift_up(&self) -> MathValue<'a> {
355 self.read_record(STRETCH_STACK_TOP_SHIFT_UP_OFFSET)
356 }
357
358 /// Standard shift down applied to the bottom element of the stretch stack.
359 #[inline]
360 pub fn stretch_stack_bottom_shift_down(&self) -> MathValue<'a> {
361 self.read_record(STRETCH_STACK_BOTTOM_SHIFT_DOWN_OFFSET)
362 }
363
364 /// Minimum gap between the ink of the stretched element, and the (ink) bottom of the element above.
365 #[inline]
366 pub fn stretch_stack_gap_above_min(&self) -> MathValue<'a> {
367 self.read_record(STRETCH_STACK_GAP_ABOVE_MIN_OFFSET)
368 }
369
370 /// Minimum gap between the ink of the stretched element, and the (ink) top of the element below.
371 #[inline]
372 pub fn stretch_stack_gap_below_min(&self) -> MathValue<'a> {
373 self.read_record(STRETCH_STACK_GAP_BELOW_MIN_OFFSET)
374 }
375
376 /// Standard shift up applied to the numerator.
377 #[inline]
378 pub fn fraction_numerator_shift_up(&self) -> MathValue<'a> {
379 self.read_record(FRACTION_NUMERATOR_SHIFT_UP_OFFSET)
380 }
381
382 /// Standard shift up applied to the numerator in display style.
383 #[inline]
384 pub fn fraction_numerator_display_style_shift_up(&self) -> MathValue<'a> {
385 self.read_record(FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP_OFFSET)
386 }
387
388 /// Standard shift down applied to the denominator.
389 #[inline]
390 pub fn fraction_denominator_shift_down(&self) -> MathValue<'a> {
391 self.read_record(FRACTION_DENOMINATOR_SHIFT_DOWN_OFFSET)
392 }
393
394 /// Standard shift down applied to the denominator in display style.
395 #[inline]
396 pub fn fraction_denominator_display_style_shift_down(&self) -> MathValue<'a> {
397 self.read_record(FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN_OFFSET)
398 }
399
400 /// Minimum tolerated gap between the (ink) bottom of the numerator and the ink of the
401 /// fraction bar.
402 #[inline]
403 pub fn fraction_numerator_gap_min(&self) -> MathValue<'a> {
404 self.read_record(FRACTION_NUMERATOR_GAP_MIN_OFFSET)
405 }
406
407 /// Minimum tolerated gap between the (ink) bottom of the numerator and the ink of the
408 /// fraction bar in display style.
409 #[inline]
410 pub fn fraction_num_display_style_gap_min(&self) -> MathValue<'a> {
411 self.read_record(FRACTION_NUM_DISPLAY_STYLE_GAP_MIN_OFFSET)
412 }
413
414 /// Thickness of the fraction bar.
415 #[inline]
416 pub fn fraction_rule_thickness(&self) -> MathValue<'a> {
417 self.read_record(FRACTION_RULE_THICKNESS_OFFSET)
418 }
419
420 /// Minimum tolerated gap between the (ink) top of the denominator and the ink of the fraction bar.
421 #[inline]
422 pub fn fraction_denominator_gap_min(&self) -> MathValue<'a> {
423 self.read_record(FRACTION_DENOMINATOR_GAP_MIN_OFFSET)
424 }
425
426 /// Minimum tolerated gap between the (ink) top of the denominator and the ink of the fraction
427 /// bar in display style.
428 #[inline]
429 pub fn fraction_denom_display_style_gap_min(&self) -> MathValue<'a> {
430 self.read_record(FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN_OFFSET)
431 }
432
433 /// Horizontal distance between the top and bottom elements of a skewed fraction.
434 #[inline]
435 pub fn skewed_fraction_horizontal_gap(&self) -> MathValue<'a> {
436 self.read_record(SKEWED_FRACTION_HORIZONTAL_GAP_OFFSET)
437 }
438
439 /// Vertical distance between the ink of the top and bottom elements of a skewed fraction.
440 #[inline]
441 pub fn skewed_fraction_vertical_gap(&self) -> MathValue<'a> {
442 self.read_record(SKEWED_FRACTION_VERTICAL_GAP_OFFSET)
443 }
444
445 /// Distance between the overbar and the (ink) top of he base.
446 #[inline]
447 pub fn overbar_vertical_gap(&self) -> MathValue<'a> {
448 self.read_record(OVERBAR_VERTICAL_GAP_OFFSET)
449 }
450
451 /// Thickness of overbar.
452 #[inline]
453 pub fn overbar_rule_thickness(&self) -> MathValue<'a> {
454 self.read_record(OVERBAR_RULE_THICKNESS_OFFSET)
455 }
456
457 /// Extra white space reserved above the overbar.
458 #[inline]
459 pub fn overbar_extra_ascender(&self) -> MathValue<'a> {
460 self.read_record(OVERBAR_EXTRA_ASCENDER_OFFSET)
461 }
462
463 /// Distance between underbar and (ink) bottom of the base.
464 #[inline]
465 pub fn underbar_vertical_gap(&self) -> MathValue<'a> {
466 self.read_record(UNDERBAR_VERTICAL_GAP_OFFSET)
467 }
468
469 /// Thickness of underbar.
470 #[inline]
471 pub fn underbar_rule_thickness(&self) -> MathValue<'a> {
472 self.read_record(UNDERBAR_RULE_THICKNESS_OFFSET)
473 }
474
475 /// Extra white space reserved below the underbar.
476 #[inline]
477 pub fn underbar_extra_descender(&self) -> MathValue<'a> {
478 self.read_record(UNDERBAR_EXTRA_DESCENDER_OFFSET)
479 }
480
481 /// Space between the (ink) top of the expression and the bar over it.
482 #[inline]
483 pub fn radical_vertical_gap(&self) -> MathValue<'a> {
484 self.read_record(RADICAL_VERTICAL_GAP_OFFSET)
485 }
486
487 /// Space between the (ink) top of the expression and the bar over it.
488 #[inline]
489 pub fn radical_display_style_vertical_gap(&self) -> MathValue<'a> {
490 self.read_record(RADICAL_DISPLAY_STYLE_VERTICAL_GAP_OFFSET)
491 }
492
493 /// Thickness of the radical rule.
494 #[inline]
495 pub fn radical_rule_thickness(&self) -> MathValue<'a> {
496 self.read_record(RADICAL_RULE_THICKNESS_OFFSET)
497 }
498
499 /// Extra white space reserved above the radical.
500 #[inline]
501 pub fn radical_extra_ascender(&self) -> MathValue<'a> {
502 self.read_record(RADICAL_EXTRA_ASCENDER_OFFSET)
503 }
504
505 /// Extra horizontal kern before the degree of a radical, if such is present.
506 #[inline]
507 pub fn radical_kern_before_degree(&self) -> MathValue<'a> {
508 self.read_record(RADICAL_KERN_BEFORE_DEGREE_OFFSET)
509 }
510
511 /// Negative kern after the degree of a radical, if such is present.
512 #[inline]
513 pub fn radical_kern_after_degree(&self) -> MathValue<'a> {
514 self.read_record(RADICAL_KERN_AFTER_DEGREE_OFFSET)
515 }
516
517 /// Height of the bottom of the radical degree, if such is present, in proportion to the
518 /// ascender of the radical sign.
519 #[inline]
520 pub fn radical_degree_bottom_raise_percent(&self) -> i16 {
521 self.read_i16(RADICAL_DEGREE_BOTTOM_RAISE_PERCENT_OFFSET)
522 }
523
524 /// Read an `i16` at an offset into the table.
525 #[inline]
526 fn read_i16(&self, offset: usize) -> i16 {
527 Stream::read_at(self.data, offset).unwrap_or(0)
528 }
529
530 /// Read an `u16` at an offset into the table.
531 #[inline]
532 fn read_u16(&self, offset: usize) -> u16 {
533 Stream::read_at(self.data, offset).unwrap_or(0)
534 }
535
536 /// Read a `MathValueRecord` at an offset into the table.
537 #[inline]
538 fn read_record(&self, offset: usize) -> MathValue<'a> {
539 self.data
540 .get(offset..)
541 .and_then(|data| MathValue::parse(data, self.data))
542 .unwrap_or(MathValue {
543 value: 0,
544 device: None,
545 })
546 }
547}
548
549/// A [Math Kern Table](https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathkern-table).
550#[derive(Clone)]
551pub struct Kern<'a> {
552 data: &'a [u8],
553 heights: LazyArray16<'a, MathValueRecord>,
554 kerns: LazyArray16<'a, MathValueRecord>,
555}
556
557impl<'a> Kern<'a> {
558 /// Number of heights at which the kern value changes.
559 pub fn count(&self) -> u16 {
560 self.heights.len()
561 }
562
563 /// The correction height at the given index.
564 ///
565 /// The index must be smaller than `count()`.
566 pub fn height(&self, index: u16) -> Option<MathValue<'a>> {
567 Some(self.heights.get(index)?.get(self.data))
568 }
569
570 /// The kern value at the given index.
571 ///
572 /// The index must be smaller than or equal to `count()`.
573 pub fn kern(&self, index: u16) -> Option<MathValue<'a>> {
574 Some(self.kerns.get(index)?.get(self.data))
575 }
576}
577
578impl<'a> FromSlice<'a> for Kern<'a> {
579 fn parse(data: &'a [u8]) -> Option<Self> {
580 let mut s: Stream<'_> = Stream::new(data);
581 let count: u16 = s.read::<u16>()?;
582 let heights: LazyArray16<'_, MathValueRecord> = s.read_array16::<MathValueRecord>(count)?;
583 let kerns: LazyArray16<'_, MathValueRecord> = s.read_array16::<MathValueRecord>(count:count + 1)?;
584 Some(Kern {
585 data,
586 heights,
587 kerns,
588 })
589 }
590}
591
592impl core::fmt::Debug for Kern<'_> {
593 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
594 write!(f, "Kern {{ ... }}")
595 }
596}
597
598#[derive(Clone, Copy)]
599struct KernInfoRecord {
600 top_right: Option<Offset16>,
601 top_left: Option<Offset16>,
602 bottom_right: Option<Offset16>,
603 bottom_left: Option<Offset16>,
604}
605
606impl FromData for KernInfoRecord {
607 const SIZE: usize = 8;
608
609 fn parse(data: &[u8]) -> Option<Self> {
610 let mut s: Stream<'_> = Stream::new(data);
611 Some(KernInfoRecord {
612 top_right: s.read::<Option<Offset16>>()?,
613 top_left: s.read::<Option<Offset16>>()?,
614 bottom_right: s.read::<Option<Offset16>>()?,
615 bottom_left: s.read::<Option<Offset16>>()?,
616 })
617 }
618}
619
620impl KernInfoRecord {
621 fn get<'a>(&self, data: &'a [u8]) -> KernInfo<'a> {
622 let parse_field: impl Fn(Option) -> … = |offset: Option<Offset16>| {
623 offsetOption<&[u8]>
624 .and_then(|offset: Offset16| data.get(index:offset.to_usize()..))
625 .and_then(Kern::parse)
626 };
627 KernInfo {
628 top_right: parse_field(self.top_right),
629 top_left: parse_field(self.top_left),
630 bottom_right: parse_field(self.bottom_right),
631 bottom_left: parse_field(self.bottom_left),
632 }
633 }
634}
635
636/// An [entry in a Math Kern Info Table](
637/// https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathkerninforecord).
638#[derive(Clone, Debug)]
639pub struct KernInfo<'a> {
640 /// The kerning data for the top-right corner.
641 pub top_right: Option<Kern<'a>>,
642 /// The kerning data for the top-left corner.
643 pub top_left: Option<Kern<'a>>,
644 /// The kerning data for the bottom-right corner.
645 pub bottom_right: Option<Kern<'a>>,
646 /// The kerning data for the bottom-left corner.
647 pub bottom_left: Option<Kern<'a>>,
648}
649
650/// A [Math Kern Info Table](https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathkerninfo-table).
651#[derive(Clone, Copy)]
652pub struct KernInfos<'a> {
653 data: &'a [u8],
654 coverage: Coverage<'a>,
655 records: LazyArray16<'a, KernInfoRecord>,
656}
657
658impl<'a> FromSlice<'a> for KernInfos<'a> {
659 fn parse(data: &'a [u8]) -> Option<Self> {
660 let mut s: Stream<'_> = Stream::new(data);
661 let coverage: Coverage<'_> = s.parse_at_offset16::<Coverage>(data)?;
662 let count: u16 = s.read::<u16>()?;
663 let records: LazyArray16<'_, KernInfoRecord> = s.read_array16::<KernInfoRecord>(count)?;
664 Some(KernInfos {
665 data,
666 coverage,
667 records,
668 })
669 }
670}
671
672impl<'a> KernInfos<'a> {
673 /// Returns the kerning info for the glyph or `None` if it is not covered.
674 #[inline]
675 pub fn get(&self, glyph: GlyphId) -> Option<KernInfo<'a>> {
676 let index: u16 = self.coverage.get(glyph)?;
677 Some(self.records.get(index)?.get(self.data))
678 }
679}
680
681impl core::fmt::Debug for KernInfos<'_> {
682 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
683 write!(f, "KernInfos {{ ... }}")
684 }
685}
686
687/// A [Math Glyph Info Table](https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathglyphinfo-table).
688#[derive(Clone, Copy, Debug)]
689pub struct GlyphInfo<'a> {
690 /// Per-glyph italics correction values.
691 pub italic_corrections: Option<MathValues<'a>>,
692 /// Per-glyph horizontal positions for attaching mathematical accents.
693 pub top_accent_attachments: Option<MathValues<'a>>,
694 /// Glyphs which are _extended shapes_.
695 pub extended_shapes: Option<Coverage<'a>>,
696 /// Per-glyph information for mathematical kerning.
697 pub kern_infos: Option<KernInfos<'a>>,
698}
699
700impl<'a> FromSlice<'a> for GlyphInfo<'a> {
701 fn parse(data: &'a [u8]) -> Option<Self> {
702 let mut s: Stream<'_> = Stream::new(data);
703 Some(GlyphInfo {
704 italic_corrections: s.parse_at_offset16::<MathValues>(data),
705 top_accent_attachments: s.parse_at_offset16::<MathValues>(data),
706 extended_shapes: s.parse_at_offset16::<Coverage>(data),
707 kern_infos: s.parse_at_offset16::<KernInfos>(data),
708 })
709 }
710}
711
712/// Glyph part flags.
713#[derive(Clone, Copy, Debug)]
714pub struct PartFlags(pub u16);
715
716#[allow(missing_docs)]
717impl PartFlags {
718 #[inline]
719 pub fn extender(self) -> bool {
720 self.0 & 0x0001 != 0
721 }
722}
723
724impl FromData for PartFlags {
725 const SIZE: usize = 2;
726
727 fn parse(data: &[u8]) -> Option<Self> {
728 u16::parse(data).map(PartFlags)
729 }
730}
731
732/// Details for a glyph part in an assembly.
733#[derive(Clone, Copy, Debug)]
734pub struct GlyphPart {
735 /// Glyph ID for the part.
736 pub glyph_id: GlyphId,
737 /// Lengths of the connectors on the start of the glyph, in font design units.
738 pub start_connector_length: u16,
739 /// Lengths of the connectors on the end of the glyph, in font design units.
740 pub end_connector_length: u16,
741 /// The full advance of the part, in font design units.
742 pub full_advance: u16,
743 /// Part flags.
744 pub part_flags: PartFlags,
745}
746
747impl FromData for GlyphPart {
748 const SIZE: usize = 10;
749
750 fn parse(data: &[u8]) -> Option<Self> {
751 let mut s: Stream<'_> = Stream::new(data);
752 Some(GlyphPart {
753 glyph_id: s.read::<GlyphId>()?,
754 start_connector_length: s.read::<u16>()?,
755 end_connector_length: s.read::<u16>()?,
756 full_advance: s.read::<u16>()?,
757 part_flags: s.read::<PartFlags>()?,
758 })
759 }
760}
761
762/// A [Glyph Assembly Table](https://learn.microsoft.com/en-us/typography/opentype/spec/math#glyphassembly-table).
763#[derive(Clone, Copy, Debug)]
764pub struct GlyphAssembly<'a> {
765 /// The italics correction of the assembled glyph.
766 pub italics_correction: MathValue<'a>,
767 /// Parts the assembly is composed of.
768 pub parts: LazyArray16<'a, GlyphPart>,
769}
770
771impl<'a> FromSlice<'a> for GlyphAssembly<'a> {
772 fn parse(data: &'a [u8]) -> Option<Self> {
773 let mut s: Stream<'_> = Stream::new(data);
774 let italics_correction: MathValue<'_> = s.read::<MathValueRecord>()?.get(data);
775 let count: u16 = s.read::<u16>()?;
776 let parts: LazyArray16<'_, GlyphPart> = s.read_array16::<GlyphPart>(count)?;
777 Some(GlyphAssembly {
778 italics_correction,
779 parts,
780 })
781 }
782}
783
784/// Description of math glyph variants.
785#[derive(Clone, Copy, Debug)]
786pub struct GlyphVariant {
787 /// The ID of the variant glyph.
788 pub variant_glyph: GlyphId,
789 /// Advance width/height, in design units, of the variant glyph.
790 pub advance_measurement: u16,
791}
792
793impl FromData for GlyphVariant {
794 const SIZE: usize = 4;
795
796 fn parse(data: &[u8]) -> Option<Self> {
797 let mut s: Stream<'_> = Stream::new(data);
798 Some(GlyphVariant {
799 variant_glyph: s.read::<GlyphId>()?,
800 advance_measurement: s.read::<u16>()?,
801 })
802 }
803}
804
805/// A [Math Glyph Construction Table](
806/// https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathglyphconstruction-table).
807#[derive(Clone, Copy, Debug)]
808pub struct GlyphConstruction<'a> {
809 /// A general recipe on how to construct a variant with large advance width/height.
810 pub assembly: Option<GlyphAssembly<'a>>,
811 /// Prepared variants of the glyph with varying advances.
812 pub variants: LazyArray16<'a, GlyphVariant>,
813}
814
815impl<'a> FromSlice<'a> for GlyphConstruction<'a> {
816 fn parse(data: &'a [u8]) -> Option<Self> {
817 let mut s: Stream<'_> = Stream::new(data);
818 let assembly: Option> = s.parse_at_offset16::<GlyphAssembly>(data);
819 let variant_count: u16 = s.read::<u16>()?;
820 let variants: LazyArray16<'_, GlyphVariant> = s.read_array16::<GlyphVariant>(variant_count)?;
821 Some(GlyphConstruction { assembly, variants })
822 }
823}
824
825/// A mapping from glyphs to
826/// [Math Glyph Construction Tables](
827/// https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathglyphconstruction-table).
828#[derive(Clone, Copy)]
829pub struct GlyphConstructions<'a> {
830 coverage: Coverage<'a>,
831 constructions: LazyOffsetArray16<'a, GlyphConstruction<'a>>,
832}
833
834impl<'a> GlyphConstructions<'a> {
835 fn new(
836 data: &'a [u8],
837 coverage: Option<Coverage<'a>>,
838 offsets: LazyArray16<'a, Option<Offset16>>,
839 ) -> Self {
840 GlyphConstructions {
841 coverage: coverage.unwrap_or(default:Coverage::Format1 {
842 glyphs: LazyArray16::new(&[]),
843 }),
844 constructions: LazyOffsetArray16::new(data, offsets),
845 }
846 }
847
848 /// Returns the construction for the glyph or `None` if it is not covered.
849 #[inline]
850 pub fn get(&self, glyph: GlyphId) -> Option<GlyphConstruction<'a>> {
851 let index: u16 = self.coverage.get(glyph)?;
852 self.constructions.get(index)
853 }
854}
855
856impl core::fmt::Debug for GlyphConstructions<'_> {
857 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
858 write!(f, "GlyphConstructions {{ ... }}")
859 }
860}
861
862/// A [Math Variants Table](
863/// https://learn.microsoft.com/en-us/typography/opentype/spec/math#mathvariants-table).
864#[derive(Clone, Copy, Debug)]
865pub struct Variants<'a> {
866 /// Minimum overlap of connecting glyphs during glyph construction, in design units.
867 pub min_connector_overlap: u16,
868 /// Constructions for shapes growing in the vertical direction.
869 pub vertical_constructions: GlyphConstructions<'a>,
870 /// Constructions for shapes growing in the horizontal direction.
871 pub horizontal_constructions: GlyphConstructions<'a>,
872}
873
874impl<'a> FromSlice<'a> for Variants<'a> {
875 fn parse(data: &'a [u8]) -> Option<Self> {
876 let mut s = Stream::new(data);
877 let min_connector_overlap = s.read::<u16>()?;
878 let vertical_coverage = s.parse_at_offset16::<Coverage>(data);
879 let horizontal_coverage = s.parse_at_offset16::<Coverage>(data);
880 let vertical_count = s.read::<u16>()?;
881 let horizontal_count = s.read::<u16>()?;
882 let vertical_offsets = s.read_array16::<Option<Offset16>>(vertical_count)?;
883 let horizontal_offsets = s.read_array16::<Option<Offset16>>(horizontal_count)?;
884 Some(Variants {
885 min_connector_overlap,
886 vertical_constructions: GlyphConstructions::new(
887 data,
888 vertical_coverage,
889 vertical_offsets,
890 ),
891 horizontal_constructions: GlyphConstructions::new(
892 data,
893 horizontal_coverage,
894 horizontal_offsets,
895 ),
896 })
897 }
898}
899
900/// A [Math Table](https://docs.microsoft.com/en-us/typography/opentype/spec/math).
901#[derive(Clone, Copy, Debug)]
902pub struct Table<'a> {
903 /// Math positioning constants.
904 pub constants: Option<Constants<'a>>,
905 /// Per-glyph positioning information.
906 pub glyph_info: Option<GlyphInfo<'a>>,
907 /// Variants and assembly recipes for growable glyphs.
908 pub variants: Option<Variants<'a>>,
909}
910
911impl<'a> Table<'a> {
912 /// Parses a table from raw data.
913 pub fn parse(data: &'a [u8]) -> Option<Self> {
914 let mut s: Stream<'_> = Stream::new(data);
915 let major_version: u8 = s.read::<u16>()? as u8;
916 s.skip::<u16>(); // minor version
917 if major_version != 1 {
918 return None;
919 }
920
921 Some(Table {
922 constants: s.parse_at_offset16::<Constants>(data),
923 glyph_info: s.parse_at_offset16::<GlyphInfo>(data),
924 variants: s.parse_at_offset16::<Variants>(data),
925 })
926 }
927}
928
929trait StreamExt<'a> {
930 fn parse_at_offset16<T: FromSlice<'a>>(&mut self, data: &'a [u8]) -> Option<T>;
931}
932
933impl<'a> StreamExt<'a> for Stream<'a> {
934 fn parse_at_offset16<T: FromSlice<'a>>(&mut self, data: &'a [u8]) -> Option<T> {
935 let offset: usize = self.read::<Option<Offset16>>()??.to_usize();
936 data.get(index:offset..).and_then(T::parse)
937 }
938}
939