1include!("../../generated/generated_mvar.rs");
4
5use super::variations::ItemVariationStore;
6use std::mem::size_of;
7
8impl Mvar {
9 pub fn new(
11 version: MajorMinor,
12 item_variation_store: Option<ItemVariationStore>,
13 value_records: Vec<ValueRecord>,
14 ) -> Self {
15 Self {
16 version,
17 value_record_size: size_of::<ValueRecord>() as u16,
18 value_record_count: value_records.len() as u16,
19 item_variation_store: item_variation_store.into(),
20 value_records,
21 }
22 }
23}
24
25#[cfg(test)]
26mod tests {
27 use font_types::{F2Dot14, Tag};
28 use read_fonts::tables::mvar as read_mvar;
29
30 use crate::dump_table;
31 use crate::tables::variations::{
32 ivs_builder::VariationStoreBuilder, RegionAxisCoordinates, VariationRegion,
33 };
34
35 use super::*;
36
37 #[test]
38 fn empty_smoke_test() {
39 let table = Mvar::new(MajorMinor::new(1, 0), None, vec![]);
40
41 let bytes = dump_table(&table).unwrap();
42 let read = read_mvar::Mvar::read(FontData::new(&bytes)).unwrap();
43
44 assert_eq!(read.version(), table.version);
45 assert_eq!(read.value_record_count(), 0);
46 assert_eq!(read.value_record_size(), 8);
47 assert!(read.item_variation_store().is_none());
48 assert_eq!(read.value_records().len(), 0);
49 }
50
51 fn reg_coords(min: f32, default: f32, max: f32) -> RegionAxisCoordinates {
52 RegionAxisCoordinates {
53 start_coord: F2Dot14::from_f32(min),
54 peak_coord: F2Dot14::from_f32(default),
55 end_coord: F2Dot14::from_f32(max),
56 }
57 }
58
59 fn test_regions() -> [VariationRegion; 3] {
60 [
61 VariationRegion::new(vec![reg_coords(0.0, 1.0, 1.0)]),
62 VariationRegion::new(vec![reg_coords(0.0, 0.5, 1.0)]),
63 VariationRegion::new(vec![reg_coords(0.5, 1.0, 1.0)]),
64 ]
65 }
66
67 fn read_metric_delta(mvar: &read_mvar::Mvar, tag: &[u8; 4], coords: &[f32]) -> f64 {
68 let coords = coords
69 .iter()
70 .map(|c| F2Dot14::from_f32(*c))
71 .collect::<Vec<_>>();
72 mvar.metric_delta(Tag::new(tag), &coords).unwrap().to_f64()
73 }
74
75 fn assert_value_record(actual: &read_mvar::ValueRecord, expected: ValueRecord) {
76 assert_eq!(actual.value_tag(), expected.value_tag);
77 assert_eq!(
78 actual.delta_set_outer_index(),
79 expected.delta_set_outer_index
80 );
81 assert_eq!(
82 actual.delta_set_inner_index(),
83 expected.delta_set_inner_index
84 );
85 }
86
87 #[test]
88 fn simple_smoke_test() {
89 let [r1, r2, r3] = test_regions();
90 let mut builder = VariationStoreBuilder::new(1);
91 let delta_ids = vec![
92 builder.add_deltas(vec![(r1, 10)]),
94 builder.add_deltas(vec![(r2, -20), (r3, -30)]),
96 ];
97 let (varstore, index_map) = builder.build();
98
99 let mut value_records = Vec::new();
100 for (tag, temp_id) in [b"hasc", b"hdsc"].into_iter().zip(delta_ids.into_iter()) {
101 let varidx = index_map.get(temp_id).unwrap();
102 let value_record = ValueRecord::new(
103 Tag::new(tag),
104 varidx.delta_set_outer_index,
105 varidx.delta_set_inner_index,
106 );
107 value_records.push(value_record);
108 }
109
110 let table = Mvar::new(MajorMinor::new(1, 0), Some(varstore), value_records);
111
112 let bytes = dump_table(&table).unwrap();
113 let read = read_mvar::Mvar::read(FontData::new(&bytes)).unwrap();
114
115 assert_eq!(read.version(), table.version);
116 assert_eq!(read.value_record_count(), 2);
117 assert_eq!(read.value_record_size(), 8);
118 assert!(read.item_variation_store().is_some());
119
120 assert_value_record(
121 &read.value_records()[0],
122 ValueRecord::new(Tag::new(b"hasc"), 0, 1),
123 );
124 assert_eq!(read_metric_delta(&read, b"hasc", &[0.0]), 0.0);
125 assert_eq!(read_metric_delta(&read, b"hasc", &[0.5]), 5.0);
127 assert_eq!(read_metric_delta(&read, b"hasc", &[1.0]), 10.0);
128
129 assert_value_record(
130 &read.value_records()[1],
131 ValueRecord::new(Tag::new(b"hdsc"), 0, 0),
132 );
133 assert_eq!(read_metric_delta(&read, b"hdsc", &[0.0]), 0.0);
134 assert_eq!(read_metric_delta(&read, b"hdsc", &[0.5]), -20.0);
136 assert_eq!(read_metric_delta(&read, b"hdsc", &[1.0]), -30.0);
137 }
138}