allsorts_subset_browser/tables/variable_fonts/
mvar.rs1use crate::binary::read::{ReadArray, ReadBinary, ReadCtxt, ReadFrom, ReadUnchecked};
6use crate::binary::{U16Be, U32Be};
7use crate::error::ParseError;
8use crate::tables::variable_fonts::{DeltaSetIndexMapEntry, ItemVariationStore, OwnedTuple};
9
10pub struct MvarTable<'a> {
12 pub major_version: u16,
14 pub minor_version: u16,
16 item_variation_store: Option<ItemVariationStore<'a>>,
18 value_records: ReadArray<'a, ValueRecord>,
23}
24
25#[derive(Copy, Clone)]
27pub struct ValueRecord {
28 pub value_tag: u32,
30 delta_set_outer_index: u16,
35 delta_set_inner_index: u16,
39}
40
41impl<'a> MvarTable<'a> {
42 pub fn lookup(&self, tag: u32, instance: &OwnedTuple) -> Option<f32> {
45 let item_variation_store = self.item_variation_store.as_ref()?;
46 let value_record = self
47 .value_records
48 .binary_search_by(|record| record.value_tag.cmp(&tag))
49 .ok()
50 .map(|index| self.value_records.get_item(index))?;
51 item_variation_store
66 .adjustment(value_record.into(), instance)
67 .ok()
68 }
69
70 pub fn value_records(&self) -> impl Iterator<Item = ValueRecord> + 'a {
72 self.value_records.iter()
73 }
74
75 pub fn value_records_len(&self) -> u16 {
77 self.value_records.len() as u16
79 }
80}
81
82impl ReadBinary for MvarTable<'_> {
83 type HostType<'a> = MvarTable<'a>;
84
85 fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
86 let scope = ctxt.scope();
87 let major_version = ctxt.read_u16be()?;
88 ctxt.check_version(major_version == 1)?;
89 let minor_version = ctxt.read_u16be()?;
90 let _reserved = ctxt.read_u16be()?;
91 let value_record_size = ctxt.read_u16be()?;
92 let value_record_count = ctxt.read_u16be()?;
93 let item_variation_store_offset = ctxt.read_u16be()?;
94 let value_records = if value_record_count > 0 {
95 ctxt.check(usize::from(value_record_size) >= ValueRecord::SIZE)?;
99 ctxt.read_array_stride::<ValueRecord>(
100 usize::from(value_record_count),
101 usize::from(value_record_size),
102 )?
103 } else {
104 ReadArray::empty()
105 };
106 let item_variation_store = (item_variation_store_offset > 0)
107 .then(|| {
108 scope
109 .offset(usize::from(item_variation_store_offset))
110 .read::<ItemVariationStore<'_>>()
111 })
112 .transpose()?;
113
114 Ok(MvarTable {
115 major_version,
116 minor_version,
117 item_variation_store,
118 value_records,
119 })
120 }
121}
122
123impl ReadFrom for ValueRecord {
124 type ReadType = (U32Be, U16Be, U16Be);
125
126 fn read_from(
127 (value_tag, delta_set_outer_index, delta_set_inner_index): (u32, u16, u16),
128 ) -> Self {
129 ValueRecord {
130 value_tag,
131 delta_set_outer_index,
132 delta_set_inner_index,
133 }
134 }
135}
136
137impl From<ValueRecord> for DeltaSetIndexMapEntry {
138 fn from(record: ValueRecord) -> DeltaSetIndexMapEntry {
139 DeltaSetIndexMapEntry {
140 outer_index: record.delta_set_outer_index,
141 inner_index: record.delta_set_inner_index,
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use crate::binary::read::ReadScope;
150 use crate::font_data::FontData;
151 use crate::tables::variable_fonts::fvar::FvarTable;
152 use crate::tables::{Fixed, FontTableProvider};
153 use crate::tag;
154 use crate::tests::{assert_close, read_fixture};
155
156 #[test]
157 fn lookup_value() {
158 let buffer = read_fixture("tests/fonts/opentype/NotoSans-VF.abc.ttf");
159 let scope = ReadScope::new(&buffer);
160 let font_file = scope
161 .read::<FontData<'_>>()
162 .expect("unable to parse font file");
163 let table_provider = font_file
164 .table_provider(0)
165 .expect("unable to create font provider");
166 let fvar_data = table_provider
167 .read_table_data(tag::FVAR)
168 .expect("unable to read fvar table data");
169 let fvar = ReadScope::new(&fvar_data).read::<FvarTable<'_>>().unwrap();
170 let mvar_data = table_provider
171 .read_table_data(tag::MVAR)
172 .expect("unable to read mvar table data");
173 let mvar = ReadScope::new(&mvar_data).read::<MvarTable<'_>>().unwrap();
174 let user_tuple = [Fixed::from(900), Fixed::from(62.5), Fixed::from(100)];
177 let instance = fvar.normalize(user_tuple.iter().copied(), None).unwrap();
178 let val = mvar.lookup(tag!(b"xhgt"), &instance).unwrap();
179 assert_close(val, 17.0);
186 }
187}