1 | //! A [Tracking Table]( |
2 | //! https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html) implementation. |
3 | |
4 | use crate::parser::{Fixed, FromData, LazyArray16, Offset, Offset16, Offset32, Stream}; |
5 | |
6 | #[derive (Clone, Copy, Debug)] |
7 | struct TrackTableRecord { |
8 | value: Fixed, |
9 | name_id: u16, |
10 | offset: Offset16, // Offset from start of the table. |
11 | } |
12 | |
13 | impl 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)] |
29 | pub 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)] |
40 | pub struct Tracks<'a> { |
41 | data: &'a [u8], // the whole table |
42 | records: LazyArray16<'a, TrackTableRecord>, |
43 | sizes_count: u16, |
44 | } |
45 | |
46 | impl<'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 | |
69 | impl<'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)] |
84 | pub struct TracksIter<'a> { |
85 | tracks: Tracks<'a>, |
86 | index: u16, |
87 | } |
88 | |
89 | impl<'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)] |
104 | pub 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 | |
111 | impl<'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)] |
138 | pub struct Table<'a> { |
139 | /// Horizontal track data. |
140 | pub horizontal: TrackData<'a>, |
141 | /// Vertical track data. |
142 | pub vertical: TrackData<'a>, |
143 | } |
144 | |
145 | impl<'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 | |