ttf_parser/tables/
kern.rs
1use crate::GlyphId;
17use crate::parser::{Stream, FromData, NumFrom, Offset16, Offset};
18
19
20#[derive(Clone, Copy, Debug)]
21struct OTCoverage(u8);
22
23impl OTCoverage {
24 #[inline]
25 fn is_horizontal(self) -> bool {
26 self.0 & (1 << 0) != 0
27 }
28
29 #[inline]
30 fn has_cross_stream(self) -> bool {
31 self.0 & (1 << 2) != 0
32 }
33}
34
35impl FromData for OTCoverage {
36 const SIZE: usize = 1;
37
38 #[inline]
39 fn parse(data: &[u8]) -> Option<Self> {
40 data.get(0).copied().map(OTCoverage)
41 }
42}
43
44
45#[derive(Clone, Copy, Debug)]
46struct AATCoverage(u8);
47
48impl AATCoverage {
49 #[inline]
50 fn is_horizontal(self) -> bool {
51 self.0 & (1 << 7) == 0
52 }
53
54 #[inline]
55 fn has_cross_stream(self) -> bool {
56 self.0 & (1 << 6) != 0
57 }
58
59 #[inline]
60 fn is_variable(self) -> bool {
61 self.0 & (1 << 5) != 0
62 }
63}
64
65impl FromData for AATCoverage {
66 const SIZE: usize = 1;
67
68 #[inline]
69 fn parse(data: &[u8]) -> Option<Self> {
70 data.get(0).copied().map(AATCoverage)
71 }
72}
73
74
75#[derive(Clone, Copy)]
76struct KerningRecord {
77 pair: u32,
80 value: i16,
81}
82
83impl FromData for KerningRecord {
84 const SIZE: usize = 6;
85
86 #[inline]
87 fn parse(data: &[u8]) -> Option<Self> {
88 let mut s = Stream::new(data);
89 Some(KerningRecord {
90 pair: s.read::<u32>()?,
91 value: s.read::<i16>()?,
92 })
93 }
94}
95
96
97#[derive(Clone, Copy, Default)]
99pub struct Subtable<'a> {
100 is_horizontal: bool,
101 is_variable: bool,
102 has_cross_stream: bool,
103 format: u8,
104 header_size: u8,
105 data: &'a [u8],
106}
107
108impl<'a> Subtable<'a> {
109 #[inline]
113 pub fn is_horizontal(&self) -> bool {
114 self.is_horizontal
115 }
116
117 #[inline]
119 pub fn is_variable(&self) -> bool {
120 self.is_variable
121 }
122
123 #[inline]
125 pub fn has_cross_stream(&self) -> bool {
126 self.has_cross_stream
127 }
128
129 #[inline]
133 pub fn has_state_machine(&self) -> bool {
134 self.format == 1
135 }
136
137 #[inline]
141 pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
142 match self.format {
143 0 => parse_format0(self.data, left, right),
144 2 => parse_format2(left, right, self.header_size, self.data),
145 3 => parse_format3(self.data, left, right),
146 _ => None,
147 }
148 }
149}
150
151impl core::fmt::Debug for Subtable<'_> {
152 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
153 f.debug_struct("Subtable")
155 .field("is_horizontal", &self.is_horizontal())
156 .field("has_state_machine", &self.has_state_machine())
157 .field("has_cross_stream", &self.has_cross_stream())
158 .field("format", &self.format)
159 .finish()
160 }
161}
162
163
164#[allow(missing_debug_implementations)]
166#[derive(Clone, Copy, Default)]
167pub struct Subtables<'a> {
168 is_aat: bool,
170 table_index: u32,
172 number_of_tables: u32,
174 stream: Stream<'a>,
176}
177
178impl<'a> Iterator for Subtables<'a> {
179 type Item = Subtable<'a>;
180
181 fn next(&mut self) -> Option<Self::Item> {
182 if self.table_index == self.number_of_tables {
183 return None;
184 }
185
186 if self.stream.at_end() {
187 return None;
188 }
189
190 if self.is_aat {
191 const HEADER_SIZE: u8 = 8;
192
193 let table_len: u32 = self.stream.read()?;
194 let coverage: AATCoverage = self.stream.read()?;
195 let format: u8 = self.stream.read()?;
196 self.stream.skip::<u16>(); if format > 3 {
199 return None;
201 }
202
203 let data_len = usize::num_from(table_len).checked_sub(usize::from(HEADER_SIZE))?;
205
206 Some(Subtable {
207 is_horizontal: coverage.is_horizontal(),
208 is_variable: coverage.is_variable(),
209 has_cross_stream: coverage.has_cross_stream(),
210 format,
211 header_size: HEADER_SIZE,
212 data: self.stream.read_bytes(data_len)?,
213 })
214 } else {
215 const HEADER_SIZE: u8 = 6;
216
217 self.stream.skip::<u16>(); let table_len: u16 = self.stream.read()?;
219 let format: u8 = self.stream.read()?;
221 let coverage: OTCoverage = self.stream.read()?;
222
223 if format != 0 && format != 2 {
224 return None;
226 }
227
228 let data_len = if self.number_of_tables == 1 {
229 self.stream.tail()?.len()
234 } else {
235 usize::from(table_len).checked_sub(usize::from(HEADER_SIZE))?
237 };
238
239 Some(Subtable {
240 is_horizontal: coverage.is_horizontal(),
241 is_variable: false, has_cross_stream: coverage.has_cross_stream(),
243 format,
244 header_size: HEADER_SIZE,
245 data: self.stream.read_bytes(data_len)?,
246 })
247 }
248 }
249}
250
251pub(crate) fn parse(data: &[u8]) -> Option<Subtables> {
252 let mut s = Stream::new(data);
261 let version: u16 = s.read()?;
262 if version == 0 {
263 let number_of_tables: u16 = s.read()?;
264 Some(Subtables {
265 is_aat: false,
266 table_index: 0,
267 number_of_tables: u32::from(number_of_tables),
268 stream: s,
269 })
270 } else {
271 s.skip::<u16>(); let number_of_tables: u32 = s.read()?;
274 Some(Subtables {
275 is_aat: true,
276 table_index: 0,
277 number_of_tables: u32::from(number_of_tables),
278 stream: s,
279 })
280 }
281}
282
283fn parse_format0(data: &[u8], left: GlyphId, right: GlyphId) -> Option<i16> {
286 let mut s = Stream::new(data);
287 let number_of_pairs: u16 = s.read()?;
288 s.advance(6); let pairs = s.read_array16::<KerningRecord>(number_of_pairs)?;
290
291 let needle = u32::from(left.0) << 16 | u32::from(right.0);
292 pairs.binary_search_by(|v| v.pair.cmp(&needle)).map(|(_, v)| v.value)
293}
294
295fn parse_format2(left: GlyphId, right: GlyphId, header_len: u8, data: &[u8]) -> Option<i16> {
298 let mut s = Stream::new(data);
299 s.skip::<u16>(); let header_len = usize::from(header_len);
304 let left_hand_table_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
305 let right_hand_table_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
306 let array_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
307
308 let left_class = get_format2_class(left.0, left_hand_table_offset, data).unwrap_or(0);
313 let right_class = get_format2_class(right.0, right_hand_table_offset, data).unwrap_or(0);
314
315 if usize::from(left_class) < array_offset {
317 return None;
318 }
319
320 let index = usize::from(left_class) + usize::from(right_class);
322 let value_offset = index.checked_sub(header_len)?;
323 Stream::read_at::<i16>(data, value_offset)
324}
325
326fn get_format2_class(glyph_id: u16, offset: usize, data: &[u8]) -> Option<u16> {
327 let mut s = Stream::new_at(data, offset)?;
328 let first_glyph: u16 = s.read()?;
329 let index = glyph_id.checked_sub(first_glyph)?;
330
331 let number_of_classes: u16 = s.read()?;
332 let classes = s.read_array16::<u16>(number_of_classes)?;
333 classes.get(index)
334}
335
336fn parse_format3(data: &[u8], left: GlyphId, right: GlyphId) -> Option<i16> {
339 let mut s = Stream::new(data);
340 let glyph_count: u16 = s.read()?;
341 let kerning_values_count: u8 = s.read()?;
342 let left_hand_classes_count: u8 = s.read()?;
343 let right_hand_classes_count: u8 = s.read()?;
344 s.skip::<u8>(); let indices_count = u16::from(left_hand_classes_count) * u16::from(right_hand_classes_count);
346
347 let kerning_values = s.read_array16::<i16>(u16::from(kerning_values_count))?;
348 let left_hand_classes = s.read_array16::<u8>(glyph_count)?;
349 let right_hand_classes = s.read_array16::<u8>(glyph_count)?;
350 let indices = s.read_array16::<u8>(indices_count)?;
351
352 let left_class = left_hand_classes.get(left.0)?;
353 let right_class = right_hand_classes.get(right.0)?;
354
355 if left_class > left_hand_classes_count || right_class > right_hand_classes_count {
356 return None;
357 }
358
359 let index = u16::from(left_class) * u16::from(right_hand_classes_count) + u16::from(right_class);
360 let index = indices.get(index)?;
361 kerning_values.get(u16::from(index))
362}