ttf_parser/tables/
trak.rs

1//! A [Tracking Table](
2//! https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html) implementation.
3
4use crate::parser::{Fixed, FromData, LazyArray16, Offset, Offset16, Offset32, Stream};
5
6#[derive(Clone, Copy, Debug)]
7struct TrackTableRecord {
8    value: Fixed,
9    name_id: u16,
10    offset: Offset16, // Offset from start of the table.
11}
12
13impl FromData for TrackTableRecord {
14    const SIZE: usize = 8;
15
16    #[inline]
17    fn parse(data: &[u8]) -> Option<Self> {
18        let mut s = Stream::new(data);
19        Some(TrackTableRecord {
20            value: s.read::<Fixed>()?,
21            name_id: s.read::<u16>()?,
22            offset: s.read::<Offset16>()?,
23        })
24    }
25}
26
27/// A single track.
28#[derive(Clone, Copy, Debug)]
29pub struct Track<'a> {
30    /// A track value.
31    pub value: f32,
32    /// The `name` table index for the track's name.
33    pub name_index: u16,
34    /// A list of tracking values for each size.
35    pub values: LazyArray16<'a, i16>,
36}
37
38/// A list of tracks.
39#[derive(Clone, Copy, Default, Debug)]
40pub struct Tracks<'a> {
41    data: &'a [u8], // the whole table
42    records: LazyArray16<'a, TrackTableRecord>,
43    sizes_count: u16,
44}
45
46impl<'a> Tracks<'a> {
47    /// Returns a track at index.
48    pub fn get(&self, index: u16) -> Option<Track<'a>> {
49        let record = self.records.get(index)?;
50        let mut s = Stream::new(self.data.get(record.offset.to_usize()..)?);
51        Some(Track {
52            value: record.value.0,
53            values: s.read_array16::<i16>(self.sizes_count)?,
54            name_index: record.name_id,
55        })
56    }
57
58    /// Returns the number of tracks.
59    pub fn len(&self) -> u16 {
60        self.records.len()
61    }
62
63    /// Checks if there are any tracks.
64    pub fn is_empty(&self) -> bool {
65        self.records.is_empty()
66    }
67}
68
69impl<'a> IntoIterator for Tracks<'a> {
70    type Item = Track<'a>;
71    type IntoIter = TracksIter<'a>;
72
73    #[inline]
74    fn into_iter(self) -> Self::IntoIter {
75        TracksIter {
76            tracks: self,
77            index: 0,
78        }
79    }
80}
81
82/// An iterator over [`Tracks`].
83#[allow(missing_debug_implementations)]
84pub struct TracksIter<'a> {
85    tracks: Tracks<'a>,
86    index: u16,
87}
88
89impl<'a> Iterator for TracksIter<'a> {
90    type Item = Track<'a>;
91
92    fn next(&mut self) -> Option<Self::Item> {
93        if self.index < self.tracks.len() {
94            self.index += 1;
95            self.tracks.get(self.index - 1)
96        } else {
97            None
98        }
99    }
100}
101
102/// A track data.
103#[derive(Clone, Copy, Default, Debug)]
104pub struct TrackData<'a> {
105    /// A list of tracks.
106    pub tracks: Tracks<'a>,
107    /// A list of sizes.
108    pub sizes: LazyArray16<'a, Fixed>,
109}
110
111impl<'a> TrackData<'a> {
112    fn parse(offset: usize, data: &'a [u8]) -> Option<Self> {
113        let mut s = Stream::new_at(data, offset)?;
114        let tracks_count = s.read::<u16>()?;
115        let sizes_count = s.read::<u16>()?;
116        let size_table_offset = s.read::<Offset32>()?; // Offset from start of the table.
117
118        let tracks = Tracks {
119            data,
120            records: s.read_array16::<TrackTableRecord>(tracks_count)?,
121            sizes_count,
122        };
123
124        // TODO: Isn't the size table is directly after the tracks table?!
125        //       Why we need an offset then?
126        let sizes = {
127            let mut s = Stream::new_at(data, size_table_offset.to_usize())?;
128            s.read_array16::<Fixed>(sizes_count)?
129        };
130
131        Some(TrackData { tracks, sizes })
132    }
133}
134
135/// A [Tracking Table](
136/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html).
137#[derive(Clone, Copy, Debug)]
138pub struct Table<'a> {
139    /// Horizontal track data.
140    pub horizontal: TrackData<'a>,
141    /// Vertical track data.
142    pub vertical: TrackData<'a>,
143}
144
145impl<'a> Table<'a> {
146    /// Parses a table from raw data.
147    pub fn parse(data: &'a [u8]) -> Option<Self> {
148        let mut s = Stream::new(data);
149
150        let version = s.read::<u32>()?;
151        if version != 0x00010000 {
152            return None;
153        }
154
155        let format = s.read::<u16>()?;
156        if format != 0 {
157            return None;
158        }
159
160        let hor_offset = s.read::<Option<Offset16>>()?;
161        let ver_offset = s.read::<Option<Offset16>>()?;
162        s.skip::<u16>(); // reserved
163
164        let horizontal = if let Some(offset) = hor_offset {
165            TrackData::parse(offset.to_usize(), data)?
166        } else {
167            TrackData::default()
168        };
169
170        let vertical = if let Some(offset) = ver_offset {
171            TrackData::parse(offset.to_usize(), data)?
172        } else {
173            TrackData::default()
174        };
175
176        Some(Table {
177            horizontal,
178            vertical,
179        })
180    }
181}