1use crate::parser::{read_i16, read_u16};
36use crate::tables::device::resolve_device_delta;
37use crate::tables::gdef::coverage_lookup;
38use crate::tables::mvar::ItemVariationStore;
39use crate::Error;
40
41pub const MATH_TABLE_TAG: [u8; 4] = *b"MATH";
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61#[doc(hidden)]
63pub struct MathValueRecord {
64 pub value: i16,
66 pub device_offset: u16,
69}
70
71impl MathValueRecord {
72 const LEN: usize = 4;
73
74 fn read(bytes: &[u8], at: usize) -> Result<Self, Error> {
75 Ok(Self {
76 value: read_i16(bytes, at)?,
77 device_offset: read_u16(bytes, at + 2)?,
78 })
79 }
80
81 pub fn resolved_value(
94 &self,
95 parent_bytes: &[u8],
96 ivs: Option<&ItemVariationStore>,
97 coords: &[f32],
98 ) -> f32 {
99 let delta = resolve_device_delta(parent_bytes, self.device_offset, ivs, coords);
100 self.value as f32 + delta
101 }
102}
103
104#[derive(Debug, Clone)]
107#[doc(hidden)]
109pub struct MathTable<'a> {
110 data: &'a [u8],
111 constants_off: usize,
112 glyph_info_off: usize,
113 variants_off: usize,
114}
115
116impl<'a> MathTable<'a> {
117 pub fn parse(data: &'a [u8]) -> Result<Self, Error> {
119 let major = read_u16(data, 0)?;
121 if major != 1 {
122 return Err(Error::BadStructure("MATH major version not 1"));
123 }
124 let constants_off = read_u16(data, 4)? as usize;
125 let glyph_info_off = read_u16(data, 6)? as usize;
126 let variants_off = read_u16(data, 8)? as usize;
127 for &o in &[constants_off, glyph_info_off, variants_off] {
131 if o != 0 && o >= data.len() {
132 return Err(Error::BadStructure("MATH sub-table offset OOB"));
133 }
134 }
135 Ok(Self {
136 data,
137 constants_off,
138 glyph_info_off,
139 variants_off,
140 })
141 }
142
143 pub fn constants(&self) -> Option<MathConstants<'a>> {
145 if self.constants_off == 0 {
146 return None;
147 }
148 Some(MathConstants {
149 data: self.data,
150 base: self.constants_off,
151 })
152 }
153
154 pub fn glyph_info(&self) -> Option<MathGlyphInfo<'a>> {
156 if self.glyph_info_off == 0 {
157 return None;
158 }
159 let base = self.glyph_info_off;
160 Some(MathGlyphInfo {
161 data: self.data,
162 base,
163 })
164 }
165
166 pub fn variants(&self) -> Option<MathVariants<'a>> {
168 if self.variants_off == 0 {
169 return None;
170 }
171 Some(MathVariants {
172 data: self.data,
173 base: self.variants_off,
174 })
175 }
176}
177
178#[derive(Debug, Clone, Copy)]
196#[doc(hidden)]
198pub struct MathConstants<'a> {
199 data: &'a [u8],
200 base: usize,
201}
202
203pub mod constant {
208 pub const MATH_LEADING: usize = 0;
211 pub const AXIS_HEIGHT: usize = 1;
212 pub const ACCENT_BASE_HEIGHT: usize = 2;
213 pub const FLATTENED_ACCENT_BASE_HEIGHT: usize = 3;
214 pub const SUBSCRIPT_SHIFT_DOWN: usize = 4;
215 pub const SUBSCRIPT_TOP_MAX: usize = 5;
216 pub const SUBSCRIPT_BASELINE_DROP_MIN: usize = 6;
217 pub const SUPERSCRIPT_SHIFT_UP: usize = 7;
218 pub const SUPERSCRIPT_SHIFT_UP_CRAMPED: usize = 8;
219 pub const SUPERSCRIPT_BOTTOM_MIN: usize = 9;
220 pub const SUPERSCRIPT_BASELINE_DROP_MAX: usize = 10;
221 pub const SUB_SUPERSCRIPT_GAP_MIN: usize = 11;
222 pub const SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT: usize = 12;
223 pub const SPACE_AFTER_SCRIPT: usize = 13;
224 pub const UPPER_LIMIT_GAP_MIN: usize = 14;
225 pub const UPPER_LIMIT_BASELINE_RISE_MIN: usize = 15;
226 pub const LOWER_LIMIT_GAP_MIN: usize = 16;
227 pub const LOWER_LIMIT_BASELINE_DROP_MIN: usize = 17;
228 pub const STACK_TOP_SHIFT_UP: usize = 18;
229 pub const STACK_TOP_DISPLAY_STYLE_SHIFT_UP: usize = 19;
230 pub const STACK_BOTTOM_SHIFT_DOWN: usize = 20;
231 pub const STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN: usize = 21;
232 pub const STACK_GAP_MIN: usize = 22;
233 pub const STACK_DISPLAY_STYLE_GAP_MIN: usize = 23;
234 pub const STRETCH_STACK_TOP_SHIFT_UP: usize = 24;
235 pub const STRETCH_STACK_BOTTOM_SHIFT_DOWN: usize = 25;
236 pub const STRETCH_STACK_GAP_ABOVE_MIN: usize = 26;
237 pub const STRETCH_STACK_GAP_BELOW_MIN: usize = 27;
238 pub const FRACTION_NUMERATOR_SHIFT_UP: usize = 28;
239 pub const FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP: usize = 29;
240 pub const FRACTION_DENOMINATOR_SHIFT_DOWN: usize = 30;
241 pub const FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN: usize = 31;
242 pub const FRACTION_NUMERATOR_GAP_MIN: usize = 32;
243 pub const FRACTION_NUM_DISPLAY_STYLE_GAP_MIN: usize = 33;
244 pub const FRACTION_RULE_THICKNESS: usize = 34;
245 pub const FRACTION_DENOMINATOR_GAP_MIN: usize = 35;
246 pub const FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN: usize = 36;
247 pub const SKEWED_FRACTION_HORIZONTAL_GAP: usize = 37;
248 pub const SKEWED_FRACTION_VERTICAL_GAP: usize = 38;
249 pub const OVERBAR_VERTICAL_GAP: usize = 39;
250 pub const OVERBAR_RULE_THICKNESS: usize = 40;
251 pub const OVERBAR_EXTRA_ASCENDER: usize = 41;
252 pub const UNDERBAR_VERTICAL_GAP: usize = 42;
253 pub const UNDERBAR_RULE_THICKNESS: usize = 43;
254 pub const UNDERBAR_EXTRA_DESCENDER: usize = 44;
255 pub const RADICAL_VERTICAL_GAP: usize = 45;
256 pub const RADICAL_DISPLAY_STYLE_VERTICAL_GAP: usize = 46;
257 pub const RADICAL_RULE_THICKNESS: usize = 47;
258 pub const RADICAL_EXTRA_ASCENDER: usize = 48;
259 pub const RADICAL_KERN_BEFORE_DEGREE: usize = 49;
260 pub const RADICAL_KERN_AFTER_DEGREE: usize = 50;
261}
262
263impl<'a> MathConstants<'a> {
264 pub fn script_percent_scale_down(&self) -> i16 {
266 read_i16(self.data, self.base).unwrap_or(0)
267 }
268
269 pub fn script_script_percent_scale_down(&self) -> i16 {
272 read_i16(self.data, self.base + 2).unwrap_or(0)
273 }
274
275 pub fn delimited_sub_formula_min_height(&self) -> u16 {
277 read_u16(self.data, self.base + 4).unwrap_or(0)
278 }
279
280 pub fn display_operator_min_height(&self) -> u16 {
282 read_u16(self.data, self.base + 6).unwrap_or(0)
283 }
284
285 pub fn radical_degree_bottom_raise_percent(&self) -> i16 {
289 let at = self.base + 8 + 51 * MathValueRecord::LEN;
290 read_i16(self.data, at).unwrap_or(0)
291 }
292
293 pub fn value(&self, index: usize) -> Option<MathValueRecord> {
296 if index > constant::RADICAL_KERN_AFTER_DEGREE {
297 return None;
298 }
299 let at = self.base + 8 + index * MathValueRecord::LEN;
300 MathValueRecord::read(self.data, at).ok()
301 }
302
303 pub fn value_i16(&self, index: usize) -> i16 {
306 self.value(index).map(|r| r.value).unwrap_or(0)
307 }
308
309 pub fn value_resolved(
318 &self,
319 index: usize,
320 ivs: Option<&ItemVariationStore>,
321 coords: &[f32],
322 ) -> f32 {
323 match self.value(index) {
324 Some(r) => r.resolved_value(&self.data[self.base..], ivs, coords),
325 None => 0.0,
326 }
327 }
328}
329
330#[derive(Debug, Clone, Copy)]
334#[doc(hidden)]
336pub struct MathGlyphInfo<'a> {
337 data: &'a [u8],
338 base: usize,
339}
340
341impl<'a> MathGlyphInfo<'a> {
342 fn sub_off(&self, idx: usize) -> Option<usize> {
343 let o = read_u16(self.data, self.base + idx * 2).ok()? as usize;
344 if o == 0 {
345 None
346 } else {
347 Some(self.base + o)
348 }
349 }
350
351 pub fn italics_correction(&self, gid: u16) -> Option<i16> {
354 let base = self.sub_off(0)?; let cov_off = read_u16(self.data, base).ok()? as usize;
356 if cov_off == 0 {
357 return None;
358 }
359 let idx = coverage_lookup(self.data.get(base + cov_off..)?, gid)? as usize;
360 let count = read_u16(self.data, base + 2).ok()? as usize;
361 if idx >= count {
362 return None;
363 }
364 let at = base + 4 + idx * MathValueRecord::LEN;
365 Some(MathValueRecord::read(self.data, at).ok()?.value)
366 }
367
368 fn value_record_for(&self, value_sub: usize, gid: u16) -> Option<(MathValueRecord, usize)> {
374 let base = self.sub_off(value_sub)?;
375 let cov_off = read_u16(self.data, base).ok()? as usize;
376 if cov_off == 0 {
377 return None;
378 }
379 let idx = coverage_lookup(self.data.get(base + cov_off..)?, gid)? as usize;
380 let count = read_u16(self.data, base + 2).ok()? as usize;
381 if idx >= count {
382 return None;
383 }
384 let at = base + 4 + idx * MathValueRecord::LEN;
385 Some((MathValueRecord::read(self.data, at).ok()?, base))
386 }
387
388 pub fn italics_correction_resolved(
393 &self,
394 gid: u16,
395 ivs: Option<&ItemVariationStore>,
396 coords: &[f32],
397 ) -> Option<f32> {
398 let (rec, base) = self.value_record_for(0, gid)?;
399 Some(rec.resolved_value(&self.data[base..], ivs, coords))
400 }
401
402 pub fn top_accent_attachment(&self, gid: u16) -> Option<i16> {
405 let base = self.sub_off(1)?; let cov_off = read_u16(self.data, base).ok()? as usize;
407 if cov_off == 0 {
408 return None;
409 }
410 let idx = coverage_lookup(self.data.get(base + cov_off..)?, gid)? as usize;
411 let count = read_u16(self.data, base + 2).ok()? as usize;
412 if idx >= count {
413 return None;
414 }
415 let at = base + 4 + idx * MathValueRecord::LEN;
416 Some(MathValueRecord::read(self.data, at).ok()?.value)
417 }
418
419 pub fn top_accent_attachment_resolved(
422 &self,
423 gid: u16,
424 ivs: Option<&ItemVariationStore>,
425 coords: &[f32],
426 ) -> Option<f32> {
427 let (rec, base) = self.value_record_for(1, gid)?;
428 Some(rec.resolved_value(&self.data[base..], ivs, coords))
429 }
430
431 pub fn is_extended_shape(&self, gid: u16) -> bool {
433 match self.sub_off(2) {
435 Some(cov) => self
436 .data
437 .get(cov..)
438 .and_then(|b| coverage_lookup(b, gid))
439 .is_some(),
440 None => false,
441 }
442 }
443
444 pub fn math_kern(&self, gid: u16, corner: MathKernCorner, height: i16) -> Option<i16> {
448 let base = self.sub_off(3)?; let cov_off = read_u16(self.data, base).ok()? as usize;
450 if cov_off == 0 {
451 return None;
452 }
453 let idx = coverage_lookup(self.data.get(base + cov_off..)?, gid)? as usize;
454 let count = read_u16(self.data, base + 2).ok()? as usize;
455 if idx >= count {
456 return None;
457 }
458 let rec_at = base + 4 + idx * 8;
460 let kern_off = read_u16(self.data, rec_at + corner as usize * 2).ok()? as usize;
461 if kern_off == 0 {
462 return None;
463 }
464 math_kern_value(self.data, base + kern_off, height).map(|r| r.value)
465 }
466
467 pub fn math_kern_resolved(
473 &self,
474 gid: u16,
475 corner: MathKernCorner,
476 height: i16,
477 ivs: Option<&ItemVariationStore>,
478 coords: &[f32],
479 ) -> Option<f32> {
480 let base = self.sub_off(3)?; let cov_off = read_u16(self.data, base).ok()? as usize;
482 if cov_off == 0 {
483 return None;
484 }
485 let idx = coverage_lookup(self.data.get(base + cov_off..)?, gid)? as usize;
486 let count = read_u16(self.data, base + 2).ok()? as usize;
487 if idx >= count {
488 return None;
489 }
490 let rec_at = base + 4 + idx * 8;
491 let kern_off = read_u16(self.data, rec_at + corner as usize * 2).ok()? as usize;
492 if kern_off == 0 {
493 return None;
494 }
495 let kern_base = base + kern_off;
496 let rec = math_kern_value(self.data, kern_base, height)?;
497 Some(rec.resolved_value(&self.data[kern_base..], ivs, coords))
498 }
499}
500
501#[derive(Debug, Clone, Copy, PartialEq, Eq)]
503pub enum MathKernCorner {
504 TopRight = 0,
505 TopLeft = 1,
506 BottomRight = 2,
507 BottomLeft = 3,
508}
509
510fn math_kern_value(data: &[u8], base: usize, height: i16) -> Option<MathValueRecord> {
514 let n = read_u16(data, base).ok()? as usize;
515 let heights_at = base + 2;
517 let kerns_at = heights_at + n * MathValueRecord::LEN;
518 let mut sel = n; for i in 0..n {
522 let h = MathValueRecord::read(data, heights_at + i * MathValueRecord::LEN)
523 .ok()?
524 .value;
525 if height < h {
526 sel = i;
527 break;
528 }
529 }
530 let at = kerns_at + sel * MathValueRecord::LEN;
531 MathValueRecord::read(data, at).ok()
532}
533
534#[derive(Debug, Clone, Copy, PartialEq, Eq)]
538pub enum GrowDirection {
539 Vertical,
540 Horizontal,
541}
542
543#[derive(Debug, Clone, Copy, PartialEq, Eq)]
545#[doc(hidden)]
547pub struct GlyphVariant {
548 pub glyph: u16,
550 pub advance: u16,
552}
553
554#[derive(Debug, Clone, Copy, PartialEq, Eq)]
556#[doc(hidden)]
558pub struct GlyphPart {
559 pub glyph: u16,
560 pub start_connector_length: u16,
561 pub end_connector_length: u16,
562 pub full_advance: u16,
563 pub part_flags: u16,
565}
566
567impl GlyphPart {
568 pub fn is_extender(&self) -> bool {
570 self.part_flags & 0x0001 != 0
571 }
572}
573
574#[derive(Debug, Clone, Copy)]
576#[doc(hidden)]
578pub struct MathVariants<'a> {
579 data: &'a [u8],
580 base: usize,
581}
582
583impl<'a> MathVariants<'a> {
584 pub fn min_connector_overlap(&self) -> u16 {
586 read_u16(self.data, self.base).unwrap_or(0)
587 }
588
589 fn coverage_off(&self, dir: GrowDirection) -> Option<usize> {
590 let field = match dir {
592 GrowDirection::Vertical => 2,
593 GrowDirection::Horizontal => 4,
594 };
595 let o = read_u16(self.data, self.base + field).ok()? as usize;
596 if o == 0 {
597 None
598 } else {
599 Some(self.base + o)
600 }
601 }
602
603 fn glyph_count(&self, dir: GrowDirection) -> u16 {
604 let field = match dir {
606 GrowDirection::Vertical => 6,
607 GrowDirection::Horizontal => 8,
608 };
609 read_u16(self.data, self.base + field).unwrap_or(0)
610 }
611
612 fn construction_off(&self, gid: u16, dir: GrowDirection) -> Option<usize> {
616 let cov = self.coverage_off(dir)?;
617 let idx = coverage_lookup(self.data.get(cov..)?, gid)? as usize;
618 let count = self.glyph_count(dir) as usize;
619 if idx >= count {
620 return None;
621 }
622 let vert_count = self.glyph_count(GrowDirection::Vertical) as usize;
624 let array_base = match dir {
625 GrowDirection::Vertical => self.base + 10,
626 GrowDirection::Horizontal => self.base + 10 + vert_count * 2,
627 };
628 let o = read_u16(self.data, array_base + idx * 2).ok()? as usize;
629 if o == 0 {
630 None
631 } else {
632 Some(self.base + o)
633 }
634 }
635
636 pub fn variants(&self, gid: u16, dir: GrowDirection) -> Vec<GlyphVariant> {
639 let mut out = Vec::new();
640 let Some(ctor) = self.construction_off(gid, dir) else {
641 return out;
642 };
643 let Ok(count) = read_u16(self.data, ctor + 2) else {
646 return out;
647 };
648 for i in 0..count as usize {
649 let at = ctor + 4 + i * 4;
650 let (Ok(glyph), Ok(advance)) = (read_u16(self.data, at), read_u16(self.data, at + 2))
651 else {
652 break;
653 };
654 out.push(GlyphVariant { glyph, advance });
655 }
656 out
657 }
658
659 pub fn assembly(&self, gid: u16, dir: GrowDirection) -> Option<(i16, Vec<GlyphPart>)> {
664 let ctor = self.construction_off(gid, dir)?;
665 let asm_off = read_u16(self.data, ctor).ok()? as usize;
666 if asm_off == 0 {
667 return None;
668 }
669 let asm = ctor + asm_off;
670 let italics = MathValueRecord::read(self.data, asm).ok()?.value;
673 let part_count = read_u16(self.data, asm + MathValueRecord::LEN).ok()? as usize;
674 let parts_at = asm + MathValueRecord::LEN + 2;
675 let mut parts = Vec::with_capacity(part_count);
676 for i in 0..part_count {
677 let at = parts_at + i * 10; parts.push(GlyphPart {
679 glyph: read_u16(self.data, at).ok()?,
680 start_connector_length: read_u16(self.data, at + 2).ok()?,
681 end_connector_length: read_u16(self.data, at + 4).ok()?,
682 full_advance: read_u16(self.data, at + 6).ok()?,
683 part_flags: read_u16(self.data, at + 8).ok()?,
684 });
685 }
686 Some((italics, parts))
687 }
688
689 pub fn assembly_italics_correction_resolved(
696 &self,
697 gid: u16,
698 dir: GrowDirection,
699 ivs: Option<&ItemVariationStore>,
700 coords: &[f32],
701 ) -> Option<f32> {
702 let ctor = self.construction_off(gid, dir)?;
703 let asm_off = read_u16(self.data, ctor).ok()? as usize;
704 if asm_off == 0 {
705 return None;
706 }
707 let asm = ctor + asm_off;
708 let rec = MathValueRecord::read(self.data, asm).ok()?;
709 Some(rec.resolved_value(&self.data[asm..], ivs, coords))
710 }
711}
712
713#[cfg(test)]
714mod tests {
715 use super::*;
716
717 fn build_math_constants_only() -> Vec<u8> {
720 let mut data = Vec::new();
721 data.extend_from_slice(&1u16.to_be_bytes());
723 data.extend_from_slice(&0u16.to_be_bytes());
724 let const_off_pos = data.len();
725 data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); let const_off = data.len();
730 data.extend_from_slice(&80i16.to_be_bytes()); data.extend_from_slice(&60i16.to_be_bytes()); data.extend_from_slice(&300u16.to_be_bytes()); data.extend_from_slice(&1500u16.to_be_bytes()); for i in 0..51 {
737 let v: i16 = if i == constant::AXIS_HEIGHT as i32 as usize {
738 250
739 } else if i == constant::FRACTION_RULE_THICKNESS {
740 40
741 } else {
742 0
743 };
744 data.extend_from_slice(&v.to_be_bytes());
745 data.extend_from_slice(&0u16.to_be_bytes()); }
747 data.extend_from_slice(&60i16.to_be_bytes());
749
750 let off = const_off as u16;
752 data[const_off_pos..const_off_pos + 2].copy_from_slice(&off.to_be_bytes());
753 data
754 }
755
756 #[test]
757 fn math_constants_scalars_and_records() {
758 let data = build_math_constants_only();
759 let m = MathTable::parse(&data).expect("parse");
760 let c = m.constants().expect("constants");
761 assert_eq!(c.script_percent_scale_down(), 80);
762 assert_eq!(c.script_script_percent_scale_down(), 60);
763 assert_eq!(c.delimited_sub_formula_min_height(), 300);
764 assert_eq!(c.display_operator_min_height(), 1500);
765 assert_eq!(c.value_i16(constant::AXIS_HEIGHT), 250);
766 assert_eq!(c.value_i16(constant::FRACTION_RULE_THICKNESS), 40);
767 assert_eq!(c.value_i16(constant::MATH_LEADING), 0);
768 assert_eq!(c.radical_degree_bottom_raise_percent(), 60);
769 assert!(c.value(100).is_none());
771 assert!(m.glyph_info().is_none());
772 assert!(m.variants().is_none());
773 }
774
775 #[test]
776 fn rejects_wrong_version() {
777 let mut data = vec![0u8; 10];
778 data[1] = 2; assert!(MathTable::parse(&data).is_err());
780 }
781
782 fn build_single_region_ivs(delta: i16) -> Vec<u8> {
787 let mut b = vec![0u8; 32];
788 b[0..2].copy_from_slice(&1u16.to_be_bytes()); b[2..6].copy_from_slice(&12u32.to_be_bytes()); b[6..8].copy_from_slice(&1u16.to_be_bytes()); b[8..12].copy_from_slice(&22u32.to_be_bytes()); b[12..14].copy_from_slice(&1u16.to_be_bytes()); b[14..16].copy_from_slice(&1u16.to_be_bytes()); b[16..18].copy_from_slice(&0i16.to_be_bytes()); b[18..20].copy_from_slice(&16384i16.to_be_bytes()); b[20..22].copy_from_slice(&16384i16.to_be_bytes()); b[22..24].copy_from_slice(&1u16.to_be_bytes()); b[24..26].copy_from_slice(&1u16.to_be_bytes()); b[26..28].copy_from_slice(&1u16.to_be_bytes()); b[28..30].copy_from_slice(&0u16.to_be_bytes()); b[30..32].copy_from_slice(&delta.to_be_bytes()); b
803 }
804
805 fn build_math_constants_with_var_axis_height(base: i16) -> Vec<u8> {
811 let mut data = Vec::new();
812 data.extend_from_slice(&1u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); let const_off_pos = data.len();
815 data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); let const_off = data.len();
820 data.extend_from_slice(&80i16.to_be_bytes()); data.extend_from_slice(&60i16.to_be_bytes()); data.extend_from_slice(&300u16.to_be_bytes()); data.extend_from_slice(&1500u16.to_be_bytes()); let records_at = data.len();
825 for i in 0..51usize {
826 if i == constant::AXIS_HEIGHT {
827 data.extend_from_slice(&base.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); } else {
830 data.extend_from_slice(&0i16.to_be_bytes());
831 data.extend_from_slice(&0u16.to_be_bytes());
832 }
833 }
834 data.extend_from_slice(&60i16.to_be_bytes()); let var_idx_at = data.len();
840 data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0x8000u16.to_be_bytes()); let dev_off_pos = records_at + constant::AXIS_HEIGHT * MathValueRecord::LEN + 2;
846 let dev_off = (var_idx_at - const_off) as u16;
847 data[dev_off_pos..dev_off_pos + 2].copy_from_slice(&dev_off.to_be_bytes());
848 data[const_off_pos..const_off_pos + 2].copy_from_slice(&(const_off as u16).to_be_bytes());
850 data
851 }
852
853 #[test]
854 fn math_constants_value_resolves_variation_index_delta() {
855 let data = build_math_constants_with_var_axis_height(250);
856 let m = MathTable::parse(&data).expect("parse");
857 let c = m.constants().expect("constants");
858 let ivs_bytes = build_single_region_ivs(-40);
859 let ivs = ItemVariationStore::parse(&ivs_bytes).expect("ivs");
860
861 assert_eq!(c.value_i16(constant::AXIS_HEIGHT), 250);
863
864 assert_eq!(c.value_resolved(constant::AXIS_HEIGHT, None, &[0.0]), 250.0);
866 assert_eq!(
868 c.value_resolved(constant::AXIS_HEIGHT, Some(&ivs), &[0.0]),
869 250.0
870 );
871 assert_eq!(
873 c.value_resolved(constant::AXIS_HEIGHT, Some(&ivs), &[1.0]),
874 210.0
875 );
876 assert_eq!(
878 c.value_resolved(constant::AXIS_HEIGHT, Some(&ivs), &[0.5]),
879 230.0
880 );
881
882 assert_eq!(
884 c.value_resolved(constant::MATH_LEADING, Some(&ivs), &[1.0]),
885 0.0
886 );
887 }
888
889 fn build_math_variants() -> Vec<u8> {
892 let mut data = Vec::new();
893 data.extend_from_slice(&1u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); let var_off_pos = data.len();
898 data.extend_from_slice(&0u16.to_be_bytes()); let var_base = data.len();
901 data.extend_from_slice(&20u16.to_be_bytes()); let vcov_pos = data.len();
904 data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&1u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); let vctor_pos = data.len();
909 data.extend_from_slice(&0u16.to_be_bytes()); let cov_at = data.len();
913 data.extend_from_slice(&1u16.to_be_bytes()); data.extend_from_slice(&1u16.to_be_bytes()); data.extend_from_slice(&5u16.to_be_bytes()); data[vcov_pos..vcov_pos + 2].copy_from_slice(&((cov_at - var_base) as u16).to_be_bytes());
917
918 let ctor_at = data.len();
920 let asm_off_pos = data.len();
921 data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&2u16.to_be_bytes()); data.extend_from_slice(&10u16.to_be_bytes());
925 data.extend_from_slice(&1000u16.to_be_bytes());
926 data.extend_from_slice(&11u16.to_be_bytes());
927 data.extend_from_slice(&2000u16.to_be_bytes());
928 data[vctor_pos..vctor_pos + 2]
929 .copy_from_slice(&((ctor_at - var_base) as u16).to_be_bytes());
930
931 let asm_at = data.len();
933 data.extend_from_slice(&0i16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&2u16.to_be_bytes()); data.extend_from_slice(&20u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&50u16.to_be_bytes()); data.extend_from_slice(&300u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&21u16.to_be_bytes());
944 data.extend_from_slice(&50u16.to_be_bytes());
945 data.extend_from_slice(&50u16.to_be_bytes());
946 data.extend_from_slice(&200u16.to_be_bytes());
947 data.extend_from_slice(&1u16.to_be_bytes()); data[asm_off_pos..asm_off_pos + 2]
949 .copy_from_slice(&((asm_at - ctor_at) as u16).to_be_bytes());
950
951 data[var_off_pos..var_off_pos + 2].copy_from_slice(&(var_base as u16).to_be_bytes());
952 data
953 }
954
955 #[test]
956 fn math_variants_and_assembly() {
957 let data = build_math_variants();
958 let m = MathTable::parse(&data).expect("parse");
959 let v = m.variants().expect("variants");
960 assert_eq!(v.min_connector_overlap(), 20);
961
962 let vars = v.variants(5, GrowDirection::Vertical);
963 assert_eq!(vars.len(), 2);
964 assert_eq!(
965 vars[0],
966 GlyphVariant {
967 glyph: 10,
968 advance: 1000
969 }
970 );
971 assert_eq!(
972 vars[1],
973 GlyphVariant {
974 glyph: 11,
975 advance: 2000
976 }
977 );
978 assert!(v.variants(99, GrowDirection::Vertical).is_empty());
980 assert!(v.variants(5, GrowDirection::Horizontal).is_empty());
982
983 let (italics, parts) = v.assembly(5, GrowDirection::Vertical).expect("assembly");
984 assert_eq!(italics, 0);
985 assert_eq!(parts.len(), 2);
986 assert_eq!(parts[0].glyph, 20);
987 assert!(!parts[0].is_extender());
988 assert_eq!(parts[1].glyph, 21);
989 assert!(parts[1].is_extender());
990 assert_eq!(parts[1].full_advance, 200);
991 }
992
993 fn build_math_glyph_info(ic: i16, tac: i16, kern_hi: i16) -> Vec<u8> {
1001 let mut gi = Vec::new();
1008 let header_len = 8usize; let mut ic_sub = Vec::new();
1015 ic_sub.extend_from_slice(&8u16.to_be_bytes()); ic_sub.extend_from_slice(&1u16.to_be_bytes()); ic_sub.extend_from_slice(&ic.to_be_bytes()); let ic_dev_pos = ic_sub.len();
1019 ic_sub.extend_from_slice(&0u16.to_be_bytes()); ic_sub.extend_from_slice(&1u16.to_be_bytes());
1022 ic_sub.extend_from_slice(&1u16.to_be_bytes());
1023 ic_sub.extend_from_slice(&7u16.to_be_bytes());
1024 let ic_var_at = ic_sub.len();
1026 ic_sub.extend_from_slice(&0u16.to_be_bytes());
1027 ic_sub.extend_from_slice(&0u16.to_be_bytes());
1028 ic_sub.extend_from_slice(&0x8000u16.to_be_bytes());
1029 ic_sub[ic_dev_pos..ic_dev_pos + 2].copy_from_slice(&(ic_var_at as u16).to_be_bytes());
1030
1031 let mut tac_sub = Vec::new();
1033 tac_sub.extend_from_slice(&8u16.to_be_bytes()); tac_sub.extend_from_slice(&1u16.to_be_bytes()); tac_sub.extend_from_slice(&tac.to_be_bytes());
1036 tac_sub.extend_from_slice(&0u16.to_be_bytes()); tac_sub.extend_from_slice(&1u16.to_be_bytes()); tac_sub.extend_from_slice(&1u16.to_be_bytes());
1039 tac_sub.extend_from_slice(&7u16.to_be_bytes());
1040
1041 let mut kern_sub = Vec::new();
1046 let kcov_pos = kern_sub.len();
1047 kern_sub.extend_from_slice(&0u16.to_be_bytes()); kern_sub.extend_from_slice(&1u16.to_be_bytes()); let krec_pos = kern_sub.len();
1050 kern_sub.extend_from_slice(&0u16.to_be_bytes()); kern_sub.extend_from_slice(&0u16.to_be_bytes()); kern_sub.extend_from_slice(&0u16.to_be_bytes()); kern_sub.extend_from_slice(&0u16.to_be_bytes()); let kcov_at = kern_sub.len();
1056 kern_sub.extend_from_slice(&1u16.to_be_bytes());
1057 kern_sub.extend_from_slice(&1u16.to_be_bytes());
1058 kern_sub.extend_from_slice(&7u16.to_be_bytes());
1059 kern_sub[kcov_pos..kcov_pos + 2].copy_from_slice(&(kcov_at as u16).to_be_bytes());
1060 let mkern_at = kern_sub.len();
1063 kern_sub.extend_from_slice(&1u16.to_be_bytes()); kern_sub.extend_from_slice(&100i16.to_be_bytes()); kern_sub.extend_from_slice(&0u16.to_be_bytes()); kern_sub.extend_from_slice(&10i16.to_be_bytes()); kern_sub.extend_from_slice(&0u16.to_be_bytes()); kern_sub.extend_from_slice(&kern_hi.to_be_bytes()); let khi_dev_pos = kern_sub.len();
1070 kern_sub.extend_from_slice(&0u16.to_be_bytes()); let kvar_at = kern_sub.len();
1072 kern_sub.extend_from_slice(&0u16.to_be_bytes()); kern_sub.extend_from_slice(&0u16.to_be_bytes()); kern_sub.extend_from_slice(&0x8000u16.to_be_bytes()); kern_sub[khi_dev_pos..khi_dev_pos + 2]
1077 .copy_from_slice(&((kvar_at - mkern_at) as u16).to_be_bytes());
1078 kern_sub[krec_pos..krec_pos + 2].copy_from_slice(&(mkern_at as u16).to_be_bytes());
1079
1080 let ic_at = header_len;
1082 let tac_at = ic_at + ic_sub.len();
1083 let kern_at = tac_at + tac_sub.len();
1084 gi.extend_from_slice(&(ic_at as u16).to_be_bytes()); gi.extend_from_slice(&(tac_at as u16).to_be_bytes()); gi.extend_from_slice(&0u16.to_be_bytes()); gi.extend_from_slice(&(kern_at as u16).to_be_bytes()); gi.extend_from_slice(&ic_sub);
1089 gi.extend_from_slice(&tac_sub);
1090 gi.extend_from_slice(&kern_sub);
1091
1092 let mut data = Vec::new();
1094 data.extend_from_slice(&1u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); let gi_off_pos = data.len();
1098 data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&0u16.to_be_bytes()); let gi_off = data.len();
1101 data.extend_from_slice(&gi);
1102 data[gi_off_pos..gi_off_pos + 2].copy_from_slice(&(gi_off as u16).to_be_bytes());
1103 data
1104 }
1105
1106 #[test]
1107 fn glyph_info_values_resolve_variation_deltas() {
1108 let data = build_math_glyph_info(120, 300, 25);
1109 let m = MathTable::parse(&data).expect("parse");
1110 let gi = m.glyph_info().expect("glyph info");
1111 let ivs_bytes = build_single_region_ivs(-15);
1112 let ivs = ItemVariationStore::parse(&ivs_bytes).expect("ivs");
1113
1114 assert_eq!(gi.italics_correction(7), Some(120));
1116 assert_eq!(gi.top_accent_attachment(7), Some(300));
1117 assert_eq!(gi.math_kern(7, MathKernCorner::TopRight, 50), Some(10));
1119 assert_eq!(gi.math_kern(7, MathKernCorner::TopRight, 150), Some(25));
1121
1122 assert_eq!(
1124 gi.italics_correction_resolved(7, Some(&ivs), &[0.0]),
1125 Some(120.0)
1126 );
1127 assert_eq!(
1128 gi.italics_correction_resolved(7, Some(&ivs), &[1.0]),
1129 Some(105.0)
1130 ); assert_eq!(
1133 gi.top_accent_attachment_resolved(7, Some(&ivs), &[1.0]),
1134 Some(300.0)
1135 );
1136 assert_eq!(
1138 gi.math_kern_resolved(7, MathKernCorner::TopRight, 50, Some(&ivs), &[1.0]),
1139 Some(10.0)
1140 );
1141 assert_eq!(
1143 gi.math_kern_resolved(7, MathKernCorner::TopRight, 150, Some(&ivs), &[0.0]),
1144 Some(25.0)
1145 );
1146 assert_eq!(
1147 gi.math_kern_resolved(7, MathKernCorner::TopRight, 150, Some(&ivs), &[1.0]),
1148 Some(10.0)
1149 ); assert!(gi
1153 .italics_correction_resolved(99, Some(&ivs), &[1.0])
1154 .is_none());
1155 assert!(gi
1156 .math_kern_resolved(99, MathKernCorner::TopRight, 0, Some(&ivs), &[1.0])
1157 .is_none());
1158 }
1159}