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<'_> = 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: TrackTableRecord = self.records.get(index)?;
50 let mut s: Stream<'_> = Stream::new(self.data.get(index: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<'_> = Stream::new_at(data, offset)?;
114 let tracks_count: u16 = s.read::<u16>()?;
115 let sizes_count: u16 = s.read::<u16>()?;
116 let size_table_offset: Offset32 = s.read::<Offset32>()?; // Offset from start of the table.
117
118 let tracks: 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: LazyArray16<'_, Fixed> = {
127 let mut s: Stream<'_> = Stream::new_at(data, offset: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}
182