ttf_parser/tables/
kern.rs

1/*!
2A kerning table implementation.
3
4Supports both
5[OpenType](https://docs.microsoft.com/en-us/typography/opentype/spec/kern)
6and
7[Apple Advanced Typography](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html)
8variants.
9
10Since there is no single correct way to process a kerning data,
11we have to provide an access to kerning subtables, so a caller can implement
12a kerning algorithm manually.
13But we still try to keep the API as high-level as possible.
14*/
15
16use 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    // In the kern table spec, a kerning pair is stored as two u16,
78    // but we are using one u32, so we can binary search it directly.
79    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/// A kerning subtable.
98#[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    // Use getters so we can change flags storage type later.
110
111    /// Checks that subtable is for horizontal text.
112    #[inline]
113    pub fn is_horizontal(&self) -> bool {
114        self.is_horizontal
115    }
116
117    /// Checks that subtable is variable.
118    #[inline]
119    pub fn is_variable(&self) -> bool {
120        self.is_variable
121    }
122
123    /// Checks that subtable has a cross-stream values.
124    #[inline]
125    pub fn has_cross_stream(&self) -> bool {
126        self.has_cross_stream
127    }
128
129    /// Checks that subtable uses a state machine.
130    ///
131    /// In this case `glyphs_kerning()` will return `None`.
132    #[inline]
133    pub fn has_state_machine(&self) -> bool {
134        self.format == 1
135    }
136
137    /// Returns kerning for a pair of glyphs.
138    ///
139    /// Returns `None` in case of state machine based subtable.
140    #[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        // TODO: finish_non_exhaustive
154        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/// An iterator over kerning subtables.
165#[allow(missing_debug_implementations)]
166#[derive(Clone, Copy, Default)]
167pub struct Subtables<'a> {
168    /// Indicates an Apple Advanced Typography format.
169    is_aat: bool,
170    /// The current table index,
171    table_index: u32,
172    /// The total number of tables.
173    number_of_tables: u32,
174    /// Actual data. Starts right after `kern` header.
175    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>(); // variation tuple index
197
198            if format > 3 {
199                // Unknown format.
200                return None;
201            }
202
203            // Subtract the header size.
204            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>(); // version
218            let table_len: u16 = self.stream.read()?;
219            // In the OpenType variant, `format` comes first.
220            let format: u8 = self.stream.read()?;
221            let coverage: OTCoverage = self.stream.read()?;
222
223            if format != 0 && format != 2 {
224                // Unknown format.
225                return None;
226            }
227
228            let data_len = if self.number_of_tables == 1 {
229                // An OpenType `kern` table with just one subtable is a special case.
230                // The `table_len` property is mainly required to jump to the next subtable,
231                // but if there is only one subtable, this property can be ignored.
232                // This is abused by some fonts, to get around the `u16` size limit.
233                self.stream.tail()?.len()
234            } else {
235                // Subtract the header size.
236                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, // Only AAT supports it.
242                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    // The `kern` table has two variants: OpenType one and Apple one.
253    // And they both have different headers.
254    // There are no robust way to distinguish them, so we have to guess.
255    //
256    // The OpenType one has the first two bytes (UInt16) as a version set to 0.
257    // While Apple one has the first four bytes (Fixed) set to 1.0
258    // So the first two bytes in case of an OpenType format will be 0x0000
259    // and 0x0001 in case of an Apple format.
260    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>(); // Skip the second part of u32 version.
272        // Note that AAT stores the number of tables as u32 and not as u16.
273        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
283/// A *Format 0 Kerning Subtable (Ordered List of Kerning Pairs)* implementation
284/// from https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html
285fn 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); // search_range (u16) + entry_selector (u16) + range_shift (u16)
289    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
295/// A *Format 2 Kerning Table (Simple n x m Array of Kerning Values)* implementation
296/// from https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html
297fn parse_format2(left: GlyphId, right: GlyphId, header_len: u8, data: &[u8]) -> Option<i16> {
298    let mut s = Stream::new(data);
299    s.skip::<u16>(); // row_width
300
301    // Offsets are from beginning of the subtable and not from the `data` start,
302    // so we have to subtract the header.
303    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    // 'The array can be indexed by completing the left-hand and right-hand class mappings,
309    // adding the class values to the address of the subtable,
310    // and fetching the kerning value to which the new address points.'
311
312    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    // 'Values within the left-hand offset table should not be less than the kerning array offset.'
316    if usize::from(left_class) < array_offset {
317        return None;
318    }
319
320    // Classes are already premultiplied, so we only need to sum them.
321    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
336/// A *Format 3 Kerning Table (Simple n x m Array of Kerning Indices)* implementation
337/// from https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html
338fn 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>(); // reserved
345    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}