ttf_parser/tables/
mvar.rs

1//! A [Metrics Variations Table](
2//! https://docs.microsoft.com/en-us/typography/opentype/spec/mvar) implementation.
3
4use crate::parser::{FromData, LazyArray16, Offset, Offset16, Stream};
5use crate::var_store::ItemVariationStore;
6use crate::{NormalizedCoordinate, Tag};
7
8#[derive(Clone, Copy)]
9struct ValueRecord {
10    value_tag: Tag,
11    delta_set_outer_index: u16,
12    delta_set_inner_index: u16,
13}
14
15impl FromData for ValueRecord {
16    const SIZE: usize = 8;
17
18    #[inline]
19    fn parse(data: &[u8]) -> Option<Self> {
20        let mut s = Stream::new(data);
21        Some(ValueRecord {
22            value_tag: s.read::<Tag>()?,
23            delta_set_outer_index: s.read::<u16>()?,
24            delta_set_inner_index: s.read::<u16>()?,
25        })
26    }
27}
28
29/// A [Metrics Variations Table](
30/// https://docs.microsoft.com/en-us/typography/opentype/spec/mvar).
31#[derive(Clone, Copy)]
32pub struct Table<'a> {
33    variation_store: ItemVariationStore<'a>,
34    records: LazyArray16<'a, ValueRecord>,
35}
36
37impl<'a> Table<'a> {
38    /// Parses a table from raw data.
39    pub fn parse(data: &'a [u8]) -> Option<Self> {
40        let mut s = Stream::new(data);
41
42        let version = s.read::<u32>()?;
43        if version != 0x00010000 {
44            return None;
45        }
46
47        s.skip::<u16>(); // reserved
48        let value_record_size = s.read::<u16>()?;
49
50        if usize::from(value_record_size) != ValueRecord::SIZE {
51            return None;
52        }
53
54        let count = s.read::<u16>()?;
55        if count == 0 {
56            return None;
57        }
58
59        let var_store_offset = s.read::<Option<Offset16>>()??.to_usize();
60        let records = s.read_array16::<ValueRecord>(count)?;
61        let variation_store = ItemVariationStore::parse(Stream::new_at(data, var_store_offset)?)?;
62
63        Some(Table {
64            variation_store,
65            records,
66        })
67    }
68
69    /// Returns a metric offset by tag.
70    pub fn metric_offset(&self, tag: Tag, coordinates: &[NormalizedCoordinate]) -> Option<f32> {
71        let (_, record) = self.records.binary_search_by(|r| r.value_tag.cmp(&tag))?;
72        self.variation_store.parse_delta(
73            record.delta_set_outer_index,
74            record.delta_set_inner_index,
75            coordinates,
76        )
77    }
78}
79
80impl core::fmt::Debug for Table<'_> {
81    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
82        write!(f, "Table {{ ... }}")
83    }
84}