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