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