/src/ttf-parser/src/tables/trak.rs
Line | Count | Source |
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 | 50.6k | fn parse(data: &[u8]) -> Option<Self> { |
18 | 50.6k | let mut s = Stream::new(data); |
19 | | Some(TrackTableRecord { |
20 | 50.6k | value: s.read::<Fixed>()?, |
21 | 50.6k | name_id: s.read::<u16>()?, |
22 | 50.6k | offset: s.read::<Offset16>()?, |
23 | | }) |
24 | 50.6k | } |
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 | 50.6k | pub fn get(&self, index: u16) -> Option<Track<'a>> { |
49 | 50.6k | let record = self.records.get(index)?; |
50 | 50.6k | let mut s = Stream::new(self.data.get(record.offset.to_usize()..)?); |
51 | | Some(Track { |
52 | 50.6k | value: record.value.0, |
53 | 50.6k | values: s.read_array16::<i16>(self.sizes_count)?, |
54 | 50.6k | name_index: record.name_id, |
55 | | }) |
56 | 50.6k | } |
57 | | |
58 | | /// Returns the number of tracks. |
59 | 50.6k | pub fn len(&self) -> u16 { |
60 | 50.6k | self.records.len() |
61 | 50.6k | } |
62 | | |
63 | | /// Checks if there are any tracks. |
64 | 0 | pub fn is_empty(&self) -> bool { |
65 | 0 | self.records.is_empty() |
66 | 0 | } |
67 | | } |
68 | | |
69 | | impl<'a> IntoIterator for Tracks<'a> { |
70 | | type Item = Track<'a>; |
71 | | type IntoIter = TracksIter<'a>; |
72 | | |
73 | | #[inline] |
74 | 5 | fn into_iter(self) -> Self::IntoIter { |
75 | 5 | TracksIter { |
76 | 5 | tracks: self, |
77 | 5 | index: 0, |
78 | 5 | } |
79 | 5 | } |
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 | 50.6k | fn next(&mut self) -> Option<Self::Item> { |
93 | 50.6k | if self.index < self.tracks.len() { |
94 | 50.6k | self.index += 1; |
95 | 50.6k | self.tracks.get(self.index - 1) |
96 | | } else { |
97 | 5 | None |
98 | | } |
99 | 50.6k | } |
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 | 327 | fn parse(offset: usize, data: &'a [u8]) -> Option<Self> { |
113 | 327 | let mut s = Stream::new_at(data, offset)?; |
114 | 274 | let tracks_count = s.read::<u16>()?; |
115 | 270 | let sizes_count = s.read::<u16>()?; |
116 | 264 | let size_table_offset = s.read::<Offset32>()?; // Offset from start of the table. |
117 | | |
118 | 207 | let tracks = Tracks { |
119 | 252 | data, |
120 | 252 | records: s.read_array16::<TrackTableRecord>(tracks_count)?, |
121 | 207 | 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 | 70 | let sizes = { |
127 | 207 | let mut s = Stream::new_at(data, size_table_offset.to_usize())?; |
128 | 139 | s.read_array16::<Fixed>(sizes_count)? |
129 | | }; |
130 | | |
131 | 70 | Some(TrackData { tracks, sizes }) |
132 | 327 | } |
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 | 417 | pub fn parse(data: &'a [u8]) -> Option<Self> { |
148 | 417 | let mut s = Stream::new(data); |
149 | | |
150 | 417 | let version = s.read::<u32>()?; |
151 | 412 | if version != 0x00010000 { |
152 | 94 | return None; |
153 | 318 | } |
154 | | |
155 | 318 | let format = s.read::<u16>()?; |
156 | 313 | if format != 0 { |
157 | 20 | return None; |
158 | 293 | } |
159 | | |
160 | 293 | let hor_offset = s.read::<Option<Offset16>>()?; |
161 | 289 | let ver_offset = s.read::<Option<Offset16>>()?; |
162 | 285 | s.skip::<u16>(); // reserved |
163 | | |
164 | 285 | let horizontal = if let Some(offset) = hor_offset { |
165 | 226 | TrackData::parse(offset.to_usize(), data)? |
166 | | } else { |
167 | 59 | TrackData::default() |
168 | | }; |
169 | | |
170 | 111 | let vertical = if let Some(offset) = ver_offset { |
171 | 101 | TrackData::parse(offset.to_usize(), data)? |
172 | | } else { |
173 | 10 | TrackData::default() |
174 | | }; |
175 | | |
176 | 28 | Some(Table { |
177 | 28 | horizontal, |
178 | 28 | vertical, |
179 | 28 | }) |
180 | 417 | } |
181 | | } |