ttf_parser/tables/
kern.rs

1/*!
2A [Kerning Table](
3https://docs.microsoft.com/en-us/typography/opentype/spec/kern) implementation.
4
5Supports both
6[OpenType](https://docs.microsoft.com/en-us/typography/opentype/spec/kern)
7and
8[Apple Advanced Typography](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html)
9variants.
10
11Since there is no single correct way to process a kerning data,
12we have to provide an access to kerning subtables, so a caller can implement
13a kerning algorithm manually.
14But we still try to keep the API as high-level as possible.
15*/
16
17use crate::GlyphId;
18use crate::parser::{FromData, LazyArray16, NumFrom, Offset, Offset16, Stream};
19#[cfg(feature = "apple-layout")] use crate::aat;
20
21#[derive(Clone, Copy, Debug)]
22struct OTCoverage(u8);
23
24impl OTCoverage {
25    #[inline] fn is_horizontal(self) -> bool { self.0 & (1 << 0) != 0 }
26    #[inline] fn has_cross_stream(self) -> bool { self.0 & (1 << 2) != 0 }
27}
28
29impl FromData for OTCoverage {
30    const SIZE: usize = 1;
31
32    #[inline]
33    fn parse(data: &[u8]) -> Option<Self> {
34        data.get(0).copied().map(OTCoverage)
35    }
36}
37
38
39#[derive(Clone, Copy, Debug)]
40struct AATCoverage(u8);
41
42impl AATCoverage {
43    #[inline] fn is_horizontal(self) -> bool { self.0 & (1 << 7) == 0 }
44    #[inline] fn has_cross_stream(self) -> bool { self.0 & (1 << 6) != 0 }
45    #[inline] fn is_variable(self) -> bool { self.0 & (1 << 5) != 0 }
46}
47
48impl FromData for AATCoverage {
49    const SIZE: usize = 1;
50
51    #[inline]
52    fn parse(data: &[u8]) -> Option<Self> {
53        data.get(0).copied().map(AATCoverage)
54    }
55}
56
57
58/// A kerning pair.
59#[derive(Clone, Copy, Debug)]
60pub struct KerningPair {
61    /// Glyphs pair.
62    ///
63    /// In the kern table spec, a kerning pair is stored as two u16,
64    /// but we are using one u32, so we can binary search it directly.
65    pub pair: u32,
66    /// Kerning value.
67    pub value: i16,
68}
69
70impl KerningPair {
71    /// Returns left glyph ID.
72    #[inline]
73    pub fn left(&self) -> GlyphId {
74        GlyphId((self.pair >> 16) as u16)
75    }
76
77    /// Returns right glyph ID.
78    #[inline]
79    pub fn right(&self) -> GlyphId {
80        GlyphId(self.pair as u16)
81    }
82}
83
84impl FromData for KerningPair {
85    const SIZE: usize = 6;
86
87    #[inline]
88    fn parse(data: &[u8]) -> Option<Self> {
89        let mut s = Stream::new(data);
90        Some(KerningPair {
91            pair: s.read::<u32>()?,
92            value: s.read::<i16>()?,
93        })
94    }
95}
96
97
98/// A kerning subtable format.
99#[allow(missing_docs)]
100#[derive(Clone, Debug)]
101pub enum Format<'a> {
102    Format0(Subtable0<'a>),
103    #[cfg(feature = "apple-layout")] Format1(aat::StateTable<'a>),
104    #[cfg(not(feature = "apple-layout"))] Format1,
105    Format2(Subtable2<'a>),
106    Format3(Subtable3<'a>),
107}
108
109
110/// A kerning subtable.
111#[derive(Clone, Debug)]
112pub struct Subtable<'a> {
113    /// Indicates that subtable is for horizontal text.
114    pub horizontal: bool,
115    /// Indicates that subtable is variable.
116    pub variable: bool,
117    /// Indicates that subtable has a cross-stream values.
118    pub has_cross_stream: bool,
119    /// Indicates that subtable uses a state machine.
120    ///
121    /// In this case `glyphs_kerning()` will return `None`.
122    pub has_state_machine: bool,
123    /// Subtable format.
124    pub format: Format<'a>,
125}
126
127impl<'a> Subtable<'a> {
128    /// Returns kerning for a pair of glyphs.
129    ///
130    /// Returns `None` in case of state machine based subtable.
131    #[inline]
132    pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
133        match self.format {
134            Format::Format0(ref subtable) => subtable.glyphs_kerning(left, right),
135            Format::Format2(ref subtable) => subtable.glyphs_kerning(left, right),
136            Format::Format3(ref subtable) => subtable.glyphs_kerning(left, right),
137            _ => None,
138        }
139    }
140}
141
142
143/// A list of subtables.
144///
145/// The internal data layout is not designed for random access,
146/// therefore we're not providing the `get()` method and only an iterator.
147#[derive(Clone, Copy)]
148pub struct Subtables<'a> {
149    /// Indicates an Apple Advanced Typography format.
150    is_aat: bool,
151    /// The total number of tables.
152    count: u32,
153    /// Actual data. Starts right after the `kern` header.
154    data: &'a [u8],
155}
156
157impl<'a> Subtables<'a> {
158    /// Returns the number of subtables.
159    pub fn len(&self) -> u32 {
160        self.count
161    }
162}
163
164impl core::fmt::Debug for Subtables<'_> {
165    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
166        write!(f, "Subtables {{ ... }}")
167    }
168}
169
170impl<'a> IntoIterator for Subtables<'a> {
171    type Item = Subtable<'a>;
172    type IntoIter = SubtablesIter<'a>;
173
174    #[inline]
175    fn into_iter(self) -> Self::IntoIter {
176        SubtablesIter {
177            is_aat: self.is_aat,
178            table_index: 0,
179            number_of_tables: self.count,
180            stream: Stream::new(self.data),
181        }
182    }
183}
184
185
186/// An iterator over kerning subtables.
187#[allow(missing_debug_implementations)]
188#[derive(Clone, Copy, Default)]
189pub struct SubtablesIter<'a> {
190    /// Indicates an Apple Advanced Typography format.
191    is_aat: bool,
192    /// The current table index,
193    table_index: u32,
194    /// The total number of tables.
195    number_of_tables: u32,
196    /// Actual data. Starts right after `kern` header.
197    stream: Stream<'a>,
198}
199
200impl<'a> Iterator for SubtablesIter<'a> {
201    type Item = Subtable<'a>;
202
203    fn next(&mut self) -> Option<Self::Item> {
204        if self.table_index == self.number_of_tables {
205            return None;
206        }
207
208        if self.stream.at_end() {
209            return None;
210        }
211
212        if self.is_aat {
213            const HEADER_SIZE: u8 = 8;
214
215            let table_len = self.stream.read::<u32>()?;
216            let coverage = self.stream.read::<AATCoverage>()?;
217            let format_id = self.stream.read::<u8>()?;
218            self.stream.skip::<u16>(); // variation tuple index
219
220            if format_id > 3 {
221                // Unknown format.
222                return None;
223            }
224
225            // Subtract the header size.
226            let data_len = usize::num_from(table_len).checked_sub(usize::from(HEADER_SIZE))?;
227            let data = self.stream.read_bytes(data_len)?;
228
229            let format = match format_id {
230                0 => Format::Format0(Subtable0::parse(data)?),
231                #[cfg(feature = "apple-layout")]
232                1 => Format::Format1(aat::StateTable::parse(data)?),
233                #[cfg(not(feature = "apple-layout"))]
234                1 => Format::Format1,
235                2 => Format::Format2(Subtable2::parse(HEADER_SIZE, data)?),
236                3 => Format::Format3(Subtable3::parse(data)?),
237                _ => return None,
238            };
239
240            Some(Subtable {
241                horizontal: coverage.is_horizontal(),
242                variable: coverage.is_variable(),
243                has_cross_stream: coverage.has_cross_stream(),
244                has_state_machine: format_id == 1,
245                format,
246            })
247        } else {
248            const HEADER_SIZE: u8 = 6;
249
250            self.stream.skip::<u16>(); // version
251            let table_len = self.stream.read::<u16>()?;
252            // In the OpenType variant, `format` comes first.
253            let format_id = self.stream.read::<u8>()?;
254            let coverage = self.stream.read::<OTCoverage>()?;
255
256            if format_id != 0 && format_id != 2 {
257                // Unknown format.
258                return None;
259            }
260
261            let data_len = if self.number_of_tables == 1 {
262                // An OpenType `kern` table with just one subtable is a special case.
263                // The `table_len` property is mainly required to jump to the next subtable,
264                // but if there is only one subtable, this property can be ignored.
265                // This is abused by some fonts, to get around the `u16` size limit.
266                self.stream.tail()?.len()
267            } else {
268                // Subtract the header size.
269                usize::from(table_len).checked_sub(usize::from(HEADER_SIZE))?
270            };
271
272            let data = self.stream.read_bytes(data_len)?;
273
274            let format = match format_id {
275                0 => Format::Format0(Subtable0::parse(data)?),
276                2 => Format::Format2(Subtable2::parse(HEADER_SIZE, data)?),
277                _ => return None,
278            };
279
280            Some(Subtable {
281                horizontal: coverage.is_horizontal(),
282                variable: false, // Only AAT supports it.
283                has_cross_stream: coverage.has_cross_stream(),
284                has_state_machine: format_id == 1,
285                format,
286            })
287        }
288    }
289}
290
291
292/// A format 0 subtable.
293///
294/// Ordered List of Kerning Pairs.
295#[derive(Clone, Copy, Debug)]
296pub struct Subtable0<'a> {
297    /// A list of kerning pairs.
298    pub pairs: LazyArray16<'a, KerningPair>,
299}
300
301impl<'a> Subtable0<'a> {
302    /// Parses a subtable from raw data.
303    pub fn parse(data: &'a [u8]) -> Option<Self> {
304        let mut s = Stream::new(data);
305        let number_of_pairs = s.read::<u16>()?;
306        s.advance(6); // search_range (u16) + entry_selector (u16) + range_shift (u16)
307        let pairs = s.read_array16::<KerningPair>(number_of_pairs)?;
308        Some(Self { pairs })
309    }
310
311    /// Returns kerning for a pair of glyphs.
312    #[inline]
313    pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
314        let needle = u32::from(left.0) << 16 | u32::from(right.0);
315        self.pairs.binary_search_by(|v| v.pair.cmp(&needle)).map(|(_, v)| v.value)
316    }
317}
318
319
320/// A format 2 subtable.
321///
322/// Simple n x m Array of Kerning Values.
323#[derive(Clone, Copy, Debug)]
324pub struct Subtable2<'a> {
325    // TODO: parse actual structure
326    data: &'a [u8],
327    header_len: u8,
328}
329
330impl<'a> Subtable2<'a> {
331    /// Parses a subtable from raw data.
332    pub fn parse(header_len: u8, data: &'a [u8]) -> Option<Self> {
333        Some(Self { header_len, data })
334    }
335
336    /// Returns kerning for a pair of glyphs.
337    pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
338        let mut s = Stream::new(self.data);
339        s.skip::<u16>(); // row_width
340
341        // Offsets are from beginning of the subtable and not from the `data` start,
342        // so we have to subtract the header.
343        let header_len = usize::from(self.header_len);
344        let left_hand_table_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
345        let right_hand_table_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
346        let array_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
347
348        // 'The array can be indexed by completing the left-hand and right-hand class mappings,
349        // adding the class values to the address of the subtable,
350        // and fetching the kerning value to which the new address points.'
351
352        let left_class = get_format2_class(left.0, left_hand_table_offset, self.data).unwrap_or(0);
353        let right_class = get_format2_class(right.0, right_hand_table_offset, self.data).unwrap_or(0);
354
355        // 'Values within the left-hand offset table should not be less than the kerning array offset.'
356        if usize::from(left_class) < array_offset {
357            return None;
358        }
359
360        // Classes are already premultiplied, so we only need to sum them.
361        let index = usize::from(left_class) + usize::from(right_class);
362        let value_offset = index.checked_sub(header_len)?;
363        Stream::read_at::<i16>(self.data, value_offset)
364    }
365}
366
367pub(crate) fn get_format2_class(glyph_id: u16, offset: usize, data: &[u8]) -> Option<u16> {
368    let mut s = Stream::new_at(data, offset)?;
369    let first_glyph = s.read::<u16>()?;
370    let index = glyph_id.checked_sub(first_glyph)?;
371
372    let number_of_classes = s.read::<u16>()?;
373    let classes = s.read_array16::<u16>(number_of_classes)?;
374    classes.get(index)
375}
376
377
378/// A format 3 subtable.
379///
380/// Simple n x m Array of Kerning Indices.
381#[derive(Clone, Copy, Debug)]
382pub struct Subtable3<'a> {
383    // TODO: parse actual structure
384    data: &'a [u8],
385}
386
387impl<'a> Subtable3<'a> {
388    /// Parses a subtable from raw data.
389    pub fn parse(data: &'a [u8]) -> Option<Self> {
390        Some(Self { data })
391    }
392
393    /// Returns kerning for a pair of glyphs.
394    #[inline]
395    pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
396        let mut s = Stream::new(self.data);
397        let glyph_count = s.read::<u16>()?;
398        let kerning_values_count = s.read::<u8>()?;
399        let left_hand_classes_count = s.read::<u8>()?;
400        let right_hand_classes_count = s.read::<u8>()?;
401        s.skip::<u8>(); // reserved
402        let indices_count = u16::from(left_hand_classes_count) * u16::from(right_hand_classes_count);
403
404        let kerning_values = s.read_array16::<i16>(u16::from(kerning_values_count))?;
405        let left_hand_classes = s.read_array16::<u8>(glyph_count)?;
406        let right_hand_classes = s.read_array16::<u8>(glyph_count)?;
407        let indices = s.read_array16::<u8>(indices_count)?;
408
409        let left_class = left_hand_classes.get(left.0)?;
410        let right_class = right_hand_classes.get(right.0)?;
411
412        if left_class > left_hand_classes_count || right_class > right_hand_classes_count {
413            return None;
414        }
415
416        let index = u16::from(left_class) * u16::from(right_hand_classes_count) + u16::from(right_class);
417        let index = indices.get(index)?;
418        kerning_values.get(u16::from(index))
419    }
420}
421
422
423/// A [Kerning Table](https://docs.microsoft.com/en-us/typography/opentype/spec/kern).
424#[derive(Clone, Copy, Debug)]
425pub struct Table<'a> {
426    /// A list of subtables.
427    pub subtables: Subtables<'a>,
428}
429
430impl<'a> Table<'a> {
431    /// Parses a table from raw data.
432    pub fn parse(data: &'a [u8]) -> Option<Self> {
433        // The `kern` table has two variants: OpenType and Apple.
434        // And they both have different headers.
435        // There are no robust way to distinguish them, so we have to guess.
436        //
437        // The OpenType one has the first two bytes (UInt16) as a version set to 0.
438        // While Apple one has the first four bytes (Fixed) set to 1.0
439        // So the first two bytes in case of an OpenType format will be 0x0000
440        // and 0x0001 in case of an Apple format.
441        let mut s = Stream::new(data);
442        let version = s.read::<u16>()?;
443        let subtables = if version == 0 {
444            let count = s.read::<u16>()?;
445            Subtables {
446                is_aat: false,
447                count: u32::from(count),
448                data: s.tail()?,
449            }
450        } else {
451            s.skip::<u16>(); // Skip the second part of u32 version.
452            // Note that AAT stores the number of tables as u32 and not as u16.
453            let count = s.read::<u32>()?;
454            Subtables {
455                is_aat: true,
456                count,
457                data: s.tail()?,
458            }
459        };
460
461        Some(Self { subtables })
462    }
463}