ttf_parser/tables/
kerx.rs

1//! An [Extended Kerning Table](
2//! https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html) implementation.
3
4// TODO: find a way to test this table
5// This table is basically untested because it uses Apple's State Tables
6// and I have no idea how to generate them.
7
8use core::num::NonZeroU16;
9
10use crate::{aat, GlyphId};
11use crate::kern::KerningPair;
12use crate::parser::{Stream, FromData, NumFrom, Offset32, Offset, LazyArray32};
13
14const HEADER_SIZE: usize = 12;
15
16/// A format 0 subtable.
17///
18/// Ordered List of Kerning Pairs.
19///
20/// The same as in `kern`, but uses `LazyArray32` instead of `LazyArray16`.
21#[derive(Clone, Copy, Debug)]
22pub struct Subtable0<'a> {
23    /// A list of kerning pairs.
24    pub pairs: LazyArray32<'a, KerningPair>,
25}
26
27impl<'a> Subtable0<'a> {
28    /// Parses a subtable from raw data.
29    fn parse(data: &'a [u8]) -> Option<Self> {
30        let mut s = Stream::new(data);
31        let number_of_pairs = s.read::<u32>()?;
32        s.advance(12); // search_range (u32) + entry_selector (u32) + range_shift (u32)
33        let pairs = s.read_array32::<KerningPair>(number_of_pairs)?;
34        Some(Self { pairs })
35    }
36
37    /// Returns kerning for a pair of glyphs.
38    #[inline]
39    pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
40        let needle = u32::from(left.0) << 16 | u32::from(right.0);
41        self.pairs.binary_search_by(|v| v.pair.cmp(&needle)).map(|(_, v)| v.value)
42    }
43}
44
45
46/// A state machine entry.
47#[derive(Clone, Copy, Debug)]
48pub struct EntryData {
49    /// An action index.
50    pub action_index: u16,
51}
52
53impl FromData for EntryData {
54    const SIZE: usize = 2;
55
56    #[inline]
57    fn parse(data: &[u8]) -> Option<Self> {
58        let mut s = Stream::new(data);
59        Some(EntryData {
60            action_index: s.read::<u16>()?,
61        })
62    }
63}
64
65/// A format 1 subtable.
66///
67/// State Table for Contextual Kerning.
68#[derive(Clone)]
69pub struct Subtable1<'a> {
70    /// A state table.
71    pub state_table: aat::ExtendedStateTable<'a, EntryData>,
72    actions_data: &'a [u8],
73}
74
75impl<'a> Subtable1<'a> {
76    fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
77        let mut s = Stream::new(data);
78        let state_table = aat::ExtendedStateTable::parse(number_of_glyphs, &mut s)?;
79
80        // Actions offset is right after the state table.
81        let actions_offset = s.read::<Offset32>()?;
82        // Actions offset is from the start of the state table and not from the start of subtable.
83        // And since we don't know the length of the actions data,
84        // simply store all the data after the offset.
85        let actions_data = data.get(actions_offset.to_usize()..)?;
86
87        Some(Subtable1 {
88            state_table,
89            actions_data,
90        })
91    }
92
93    /// Returns kerning at action index.
94    #[inline]
95    pub fn glyphs_kerning(&self, action_index: u16) -> Option<i16> {
96        Stream::read_at(self.actions_data, usize::from(action_index) * i16::SIZE)
97    }
98}
99
100impl<'a> core::ops::Deref for Subtable1<'a> {
101    type Target = aat::ExtendedStateTable<'a, EntryData>;
102
103    fn deref(&self) -> &Self::Target {
104        &self.state_table
105    }
106}
107
108impl core::fmt::Debug for Subtable1<'_> {
109    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
110        write!(f, "Subtable1 {{ ... }}")
111    }
112}
113
114
115/// A format 2 subtable.
116///
117/// Simple n x m Array of Kerning Values.
118///
119/// The same as in `kern`, but uses 32bit offsets instead of 16bit one.
120#[derive(Clone, Copy)]
121pub struct Subtable2<'a>(&'a [u8]); // TODO: parse actual structure
122
123impl<'a> Subtable2<'a> {
124    /// Returns kerning for a pair of glyphs.
125    pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
126        let mut s = Stream::new(self.0);
127        s.skip::<u32>(); // row_width
128
129        // Offsets are from beginning of the subtable and not from the `data` start,
130        // so we have to subtract the header.
131        let left_hand_table_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
132        let right_hand_table_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
133        let array_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
134
135        // 'The array can be indexed by completing the left-hand and right-hand class mappings,
136        // adding the class values to the address of the subtable,
137        // and fetching the kerning value to which the new address points.'
138
139        let left_class = crate::kern::get_format2_class(left.0, left_hand_table_offset, self.0).unwrap_or(0);
140        let right_class = crate::kern::get_format2_class(right.0, right_hand_table_offset, self.0).unwrap_or(0);
141
142        // 'Values within the left-hand offset table should not be less than the kerning array offset.'
143        if usize::from(left_class) < array_offset {
144            return None;
145        }
146
147        // Classes are already premultiplied, so we only need to sum them.
148        let index = usize::from(left_class) + usize::from(right_class);
149        let value_offset = index.checked_sub(HEADER_SIZE)?;
150        Stream::read_at::<i16>(self.0, value_offset)
151    }
152}
153
154impl core::fmt::Debug for Subtable2<'_> {
155    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
156        write!(f, "Subtable2 {{ ... }}")
157    }
158}
159
160
161/// A container of Anchor Points used by [`Subtable4`].
162#[derive(Clone, Copy)]
163pub struct AnchorPoints<'a>(&'a [u8]);
164
165impl AnchorPoints<'_> {
166    /// Returns a mark and current anchor points at action index.
167    pub fn get(&self, action_index: u16) -> Option<(u16, u16)> {
168        let offset = usize::from(action_index) * u16::SIZE;
169        let mut s = Stream::new_at(self.0, offset)?;
170        Some((s.read::<u16>()?, s.read::<u16>()?))
171    }
172}
173
174impl core::fmt::Debug for AnchorPoints<'_> {
175    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
176        write!(f, "AnchorPoints {{ ... }}")
177    }
178}
179
180/// A format 4 subtable.
181///
182/// State Table for Control Point/Anchor Point Positioning.
183///
184/// Note: I wasn't able to find any fonts that actually use
185/// `ControlPointActions` and/or `ControlPointCoordinateActions`,
186/// therefore only `AnchorPointActions` is supported.
187#[derive(Clone)]
188pub struct Subtable4<'a> {
189    /// A state table.
190    pub state_table: aat::ExtendedStateTable<'a, EntryData>,
191    /// Anchor points.
192    pub anchor_points: AnchorPoints<'a>,
193}
194
195impl<'a> Subtable4<'a> {
196    fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
197        let mut s = Stream::new(data);
198        let state_table = aat::ExtendedStateTable::parse(number_of_glyphs, &mut s)?;
199        let flags = s.read::<u32>()?;
200        let action_type = ((flags & 0xC0000000) >> 30) as u8;
201        let points_offset = usize::num_from(flags & 0x00FFFFFF);
202
203        // We support only Anchor Point Actions.
204        if action_type != 1 {
205            return None;
206        }
207
208        Some(Self {
209            state_table,
210            anchor_points: AnchorPoints(data.get(points_offset..)?),
211        })
212    }
213}
214
215impl<'a> core::ops::Deref for Subtable4<'a> {
216    type Target = aat::ExtendedStateTable<'a, EntryData>;
217
218    fn deref(&self) -> &Self::Target {
219        &self.state_table
220    }
221}
222
223impl core::fmt::Debug for Subtable4<'_> {
224    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
225        write!(f, "Subtable4 {{ ... }}")
226    }
227}
228
229
230/// A format 6 subtable.
231///
232/// Simple Index-based n x m Array of Kerning Values.
233#[derive(Clone, Copy)]
234pub struct Subtable6<'a>{
235    data: &'a [u8],
236    number_of_glyphs: NonZeroU16,
237}
238
239impl<'a> Subtable6<'a> {
240    // TODO: parse actual structure
241    fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Self {
242        Subtable6 { number_of_glyphs, data }
243    }
244
245    /// Returns kerning for a pair of glyphs.
246    pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
247        use core::convert::TryFrom;
248
249        let mut s = Stream::new(self.data);
250        let flags = s.read::<u32>()?;
251        s.skip::<u16>(); // row_count
252        s.skip::<u16>(); // col_count
253        // All offsets are from the start of the subtable.
254        let row_index_table_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
255        let column_index_table_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
256        let kerning_array_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
257        let kerning_vector_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
258
259        let row_index_table_data = self.data.get(row_index_table_offset..)?;
260        let column_index_table_data = self.data.get(column_index_table_offset..)?;
261        let kerning_array_data = self.data.get(kerning_array_offset..)?;
262        let kerning_vector_data = self.data.get(kerning_vector_offset..)?;
263
264        let has_long_values = flags & 0x00000001 != 0;
265        if has_long_values {
266            let l: u32 = aat::Lookup::parse(self.number_of_glyphs, row_index_table_data)?
267                .value(left).unwrap_or(0) as u32;
268
269            let r: u32 = aat::Lookup::parse(self.number_of_glyphs, column_index_table_data)?
270                .value(right).unwrap_or(0) as u32;
271
272            let array_offset = usize::try_from(l + r).ok()?.checked_mul(i32::SIZE)?;
273            let vector_offset: u32 = Stream::read_at(kerning_array_data, array_offset)?;
274
275            Stream::read_at(kerning_vector_data, usize::num_from(vector_offset))
276        } else {
277            let l: u16 = aat::Lookup::parse(self.number_of_glyphs, row_index_table_data)?
278                .value(left).unwrap_or(0);
279
280            let r: u16 = aat::Lookup::parse(self.number_of_glyphs, column_index_table_data)?
281                .value(right).unwrap_or(0);
282
283            let array_offset = usize::try_from(l + r).ok()?.checked_mul(i16::SIZE)?;
284            let vector_offset: u16 = Stream::read_at(kerning_array_data, array_offset)?;
285
286            Stream::read_at(kerning_vector_data, usize::from(vector_offset))
287        }
288    }
289}
290
291impl core::fmt::Debug for Subtable6<'_> {
292    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
293        write!(f, "Subtable6 {{ ... }}")
294    }
295}
296
297
298/// An extended kerning subtable format.
299#[allow(missing_docs)]
300#[derive(Clone, Debug)]
301pub enum Format<'a> {
302    Format0(Subtable0<'a>),
303    Format1(Subtable1<'a>),
304    Format2(Subtable2<'a>),
305    Format4(Subtable4<'a>),
306    Format6(Subtable6<'a>),
307}
308
309/// A kerning subtable.
310#[derive(Clone, Debug)]
311pub struct Subtable<'a> {
312    /// Indicates that subtable is for horizontal text.
313    pub horizontal: bool,
314    /// Indicates that subtable is variable.
315    pub variable: bool,
316    /// Indicates that subtable has a cross-stream values.
317    pub has_cross_stream: bool,
318    /// Indicates that subtable uses a state machine.
319    ///
320    /// In this case `glyphs_kerning()` will return `None`.
321    pub has_state_machine: bool,
322    /// The tuple count.
323    ///
324    /// This value is only used with variation fonts and should be 0 for all other fonts.
325    pub tuple_count: u32,
326    /// Subtable format.
327    pub format: Format<'a>,
328}
329
330impl<'a> Subtable<'a> {
331    /// Returns kerning for a pair of glyphs.
332    ///
333    /// Returns `None` in case of state machine based subtable.
334    #[inline]
335    pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
336        match self.format {
337            Format::Format0(ref subtable) => subtable.glyphs_kerning(left, right),
338            Format::Format1(_) => None,
339            Format::Format2(ref subtable) => subtable.glyphs_kerning(left, right),
340            Format::Format4(_) => None,
341            Format::Format6(ref subtable) => subtable.glyphs_kerning(left, right),
342        }
343    }
344}
345
346
347#[derive(Clone, Copy, Debug)]
348struct Coverage(u8);
349
350impl Coverage {
351    // TODO: use hex
352    #[inline] pub fn is_horizontal(self) -> bool { self.0 & (1 << 7) == 0 }
353    #[inline] pub fn has_cross_stream(self) -> bool { self.0 & (1 << 6) != 0 }
354    #[inline] pub fn is_variable(self) -> bool { self.0 & (1 << 5) != 0 }
355}
356
357
358/// A list of extended kerning subtables.
359///
360/// The internal data layout is not designed for random access,
361/// therefore we're not providing the `get()` method and only an iterator.
362#[derive(Clone, Copy)]
363pub struct Subtables<'a> {
364    /// The number of glyphs from the `maxp` table.
365    number_of_glyphs: NonZeroU16,
366    /// The total number of tables.
367    number_of_tables: u32,
368    /// Actual data. Starts right after the `kerx` header.
369    data: &'a [u8],
370}
371
372impl core::fmt::Debug for Subtables<'_> {
373    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
374        write!(f, "Subtables {{ ... }}")
375    }
376}
377
378impl<'a> IntoIterator for Subtables<'a> {
379    type Item = Subtable<'a>;
380    type IntoIter = SubtablesIter<'a>;
381
382    #[inline]
383    fn into_iter(self) -> Self::IntoIter {
384        SubtablesIter {
385            number_of_glyphs: self.number_of_glyphs,
386            table_index: 0,
387            number_of_tables: self.number_of_tables,
388            stream: Stream::new(self.data),
389        }
390    }
391}
392
393/// An iterator over extended kerning subtables.
394#[allow(missing_debug_implementations)]
395#[derive(Clone, Copy)]
396pub struct SubtablesIter<'a> {
397    /// The number of glyphs from the `maxp` table.
398    number_of_glyphs: NonZeroU16,
399    /// The current table index.
400    table_index: u32,
401    /// The total number of tables.
402    number_of_tables: u32,
403    /// Actual data. Starts right after the `kerx` header.
404    stream: Stream<'a>,
405}
406
407impl<'a> Iterator for SubtablesIter<'a> {
408    type Item = Subtable<'a>;
409
410    fn next(&mut self) -> Option<Self::Item> {
411        if self.table_index == self.number_of_tables {
412            return None;
413        }
414
415        if self.stream.at_end() {
416            return None;
417        }
418
419        let s = &mut self.stream;
420
421        let table_len = s.read::<u32>()?;
422        let coverage = Coverage(s.read::<u8>()?);
423        s.skip::<u16>(); // unused
424        let raw_format = s.read::<u8>()?;
425        let tuple_count = s.read::<u32>()?;
426
427        // Subtract the header size.
428        let data_len = usize::num_from(table_len).checked_sub(HEADER_SIZE)?;
429        let data = s.read_bytes(data_len)?;
430
431        let format = match raw_format {
432            0 => Subtable0::parse(data).map(Format::Format0)?,
433            1 => Subtable1::parse(self.number_of_glyphs, data).map(Format::Format1)?,
434            2 => Format::Format2(Subtable2(data)),
435            4 => Subtable4::parse(self.number_of_glyphs, data).map(Format::Format4)?,
436            6 => Format::Format6(Subtable6::parse(self.number_of_glyphs, data)),
437            _ => {
438                // Unknown format.
439                return None;
440            }
441        };
442
443        Some(Subtable {
444            horizontal: coverage.is_horizontal(),
445            variable: coverage.is_variable(),
446            has_cross_stream: coverage.has_cross_stream(),
447            has_state_machine: raw_format == 1 || raw_format == 4,
448            tuple_count,
449            format,
450        })
451    }
452}
453
454
455/// An [Extended Kerning Table](
456/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html).
457#[derive(Clone, Copy, Debug)]
458pub struct Table<'a> {
459    /// A list of subtables.
460    pub subtables: Subtables<'a>,
461}
462
463impl<'a> Table<'a> {
464    /// Parses a table from raw data.
465    ///
466    /// `number_of_glyphs` is from the `maxp` table.
467    pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
468        let mut s = Stream::new(data);
469        s.skip::<u16>(); // version
470        s.skip::<u16>(); // padding
471        let number_of_tables = s.read::<u32>()?;
472        let subtables = Subtables {
473            number_of_glyphs,
474            number_of_tables,
475            data: s.tail()?,
476        };
477
478        Some(Table { subtables })
479    }
480}