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