ttf_parser/
aat.rs

1/*!
2A collection of [Apple Advanced Typography](
3https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html)
4related types.
5*/
6
7use core::num::NonZeroU16;
8
9use crate::parser::{FromData, LazyArray16, NumFrom, Offset, Offset16, Offset32, Stream};
10use crate::GlyphId;
11
12/// Predefined states.
13pub mod state {
14    #![allow(missing_docs)]
15    pub const START_OF_TEXT: u16 = 0;
16}
17
18/// Predefined classes.
19///
20/// Search for _Class Code_ in [Apple Advanced Typography Font Tables](
21/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
22pub mod class {
23    #![allow(missing_docs)]
24    pub const END_OF_TEXT: u8 = 0;
25    pub const OUT_OF_BOUNDS: u8 = 1;
26    pub const DELETED_GLYPH: u8 = 2;
27}
28
29/// A State Table entry.
30///
31/// Used by legacy and extended tables.
32#[derive(Clone, Copy, Debug)]
33pub struct GenericStateEntry<T: FromData> {
34    /// A new state.
35    pub new_state: u16,
36    /// Entry flags.
37    pub flags: u16,
38    /// Additional data.
39    ///
40    /// Use `()` if no data expected.
41    pub extra: T,
42}
43
44impl<T: FromData> FromData for GenericStateEntry<T> {
45    const SIZE: usize = 4 + T::SIZE;
46
47    #[inline]
48    fn parse(data: &[u8]) -> Option<Self> {
49        let mut s = Stream::new(data);
50        Some(GenericStateEntry {
51            new_state: s.read::<u16>()?,
52            flags: s.read::<u16>()?,
53            extra: s.read::<T>()?,
54        })
55    }
56}
57
58impl<T: FromData> GenericStateEntry<T> {
59    /// Checks that entry has an offset.
60    #[inline]
61    pub fn has_offset(&self) -> bool {
62        self.flags & 0x3FFF != 0
63    }
64
65    /// Returns a value offset.
66    ///
67    /// Used by kern::format1 subtable.
68    #[inline]
69    pub fn value_offset(&self) -> ValueOffset {
70        ValueOffset(self.flags & 0x3FFF)
71    }
72
73    /// If set, reset the kerning data (clear the stack).
74    #[inline]
75    pub fn has_reset(&self) -> bool {
76        self.flags & 0x2000 != 0
77    }
78
79    /// If set, advance to the next glyph before going to the new state.
80    #[inline]
81    pub fn has_advance(&self) -> bool {
82        self.flags & 0x4000 == 0
83    }
84
85    /// If set, push this glyph on the kerning stack.
86    #[inline]
87    pub fn has_push(&self) -> bool {
88        self.flags & 0x8000 != 0
89    }
90
91    /// If set, remember this glyph as the marked glyph.
92    ///
93    /// Used by kerx::format4 subtable.
94    ///
95    /// Yes, the same as [`has_push`](Self::has_push).
96    #[inline]
97    pub fn has_mark(&self) -> bool {
98        self.flags & 0x8000 != 0
99    }
100}
101
102/// A legacy state entry used by [StateTable].
103pub type StateEntry = GenericStateEntry<()>;
104
105/// A type-safe wrapper for a kerning value offset.
106#[derive(Clone, Copy, Debug)]
107pub struct ValueOffset(u16);
108
109impl ValueOffset {
110    /// Returns the next offset.
111    ///
112    /// After reaching u16::MAX will start from 0.
113    #[inline]
114    pub fn next(self) -> Self {
115        ValueOffset(self.0.wrapping_add(u16::SIZE as u16))
116    }
117}
118
119/// A [State Table](
120/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
121///
122/// Also called `STHeader`.
123///
124/// Currently used by `kern` table.
125#[derive(Clone)]
126pub struct StateTable<'a> {
127    number_of_classes: u16,
128    first_glyph: GlyphId,
129    class_table: &'a [u8],
130    state_array_offset: u16,
131    state_array: &'a [u8],
132    entry_table: &'a [u8],
133    actions: &'a [u8],
134}
135
136impl<'a> StateTable<'a> {
137    pub(crate) fn parse(data: &'a [u8]) -> Option<Self> {
138        let mut s = Stream::new(data);
139
140        let number_of_classes: u16 = s.read()?;
141        // Note that in format1 subtable, offsets are not from the subtable start,
142        // but from subtable start + `header_size`.
143        // So there is not need to subtract the `header_size`.
144        let class_table_offset = s.read::<Offset16>()?.to_usize();
145        let state_array_offset = s.read::<Offset16>()?.to_usize();
146        let entry_table_offset = s.read::<Offset16>()?.to_usize();
147        // Ignore `values_offset` since we don't use it.
148
149        // Parse class subtable.
150        let mut s = Stream::new_at(data, class_table_offset)?;
151        let first_glyph: GlyphId = s.read()?;
152        let number_of_glyphs: u16 = s.read()?;
153        // The class table contains u8, so it's easier to use just a slice
154        // instead of a LazyArray.
155        let class_table = s.read_bytes(usize::from(number_of_glyphs))?;
156
157        Some(StateTable {
158            number_of_classes,
159            first_glyph,
160            class_table,
161            state_array_offset: state_array_offset as u16,
162            // We don't know the actual data size and it's kinda expensive to calculate.
163            // So we are simply storing all the data past the offset.
164            // Despite the fact that they may overlap.
165            state_array: data.get(state_array_offset..)?,
166            entry_table: data.get(entry_table_offset..)?,
167            // `ValueOffset` defines an offset from the start of the subtable data.
168            // We do not check that the provided offset is actually after `values_offset`.
169            actions: data,
170        })
171    }
172
173    /// Returns a glyph class.
174    #[inline]
175    pub fn class(&self, glyph_id: GlyphId) -> Option<u8> {
176        if glyph_id.0 == 0xFFFF {
177            return Some(class::DELETED_GLYPH);
178        }
179
180        let idx = glyph_id.0.checked_sub(self.first_glyph.0)?;
181        self.class_table.get(usize::from(idx)).copied()
182    }
183
184    /// Returns a class entry.
185    #[inline]
186    pub fn entry(&self, state: u16, mut class: u8) -> Option<StateEntry> {
187        if u16::from(class) >= self.number_of_classes {
188            class = class::OUT_OF_BOUNDS;
189        }
190
191        let entry_idx = self
192            .state_array
193            .get(usize::from(state) * usize::from(self.number_of_classes) + usize::from(class))?;
194
195        Stream::read_at(self.entry_table, usize::from(*entry_idx) * StateEntry::SIZE)
196    }
197
198    /// Returns kerning at offset.
199    #[inline]
200    pub fn kerning(&self, offset: ValueOffset) -> Option<i16> {
201        Stream::read_at(self.actions, usize::from(offset.0))
202    }
203
204    /// Produces a new state.
205    #[inline]
206    pub fn new_state(&self, state: u16) -> u16 {
207        let n = (i32::from(state) - i32::from(self.state_array_offset))
208            / i32::from(self.number_of_classes);
209
210        use core::convert::TryFrom;
211        u16::try_from(n).unwrap_or(0)
212    }
213}
214
215impl core::fmt::Debug for StateTable<'_> {
216    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
217        write!(f, "StateTable {{ ... }}")
218    }
219}
220
221/// An [Extended State Table](
222/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
223///
224/// Also called `STXHeader`.
225///
226/// Currently used by `kerx` and `morx` tables.
227#[derive(Clone)]
228pub struct ExtendedStateTable<'a, T> {
229    number_of_classes: u32,
230    lookup: Lookup<'a>,
231    state_array: &'a [u8],
232    entry_table: &'a [u8],
233    entry_type: core::marker::PhantomData<T>,
234}
235
236impl<'a, T: FromData> ExtendedStateTable<'a, T> {
237    // TODO: make private
238    /// Parses an Extended State Table from a stream.
239    ///
240    /// `number_of_glyphs` is from the `maxp` table.
241    pub fn parse(number_of_glyphs: NonZeroU16, s: &mut Stream<'a>) -> Option<Self> {
242        let data = s.tail()?;
243
244        let number_of_classes = s.read::<u32>()?;
245        // Note that offsets are not from the subtable start,
246        // but from subtable start + `header_size`.
247        // So there is not need to subtract the `header_size`.
248        let lookup_table_offset = s.read::<Offset32>()?.to_usize();
249        let state_array_offset = s.read::<Offset32>()?.to_usize();
250        let entry_table_offset = s.read::<Offset32>()?.to_usize();
251
252        Some(ExtendedStateTable {
253            number_of_classes,
254            lookup: Lookup::parse(number_of_glyphs, data.get(lookup_table_offset..)?)?,
255            // We don't know the actual data size and it's kinda expensive to calculate.
256            // So we are simply storing all the data past the offset.
257            // Despite the fact that they may overlap.
258            state_array: data.get(state_array_offset..)?,
259            entry_table: data.get(entry_table_offset..)?,
260            entry_type: core::marker::PhantomData,
261        })
262    }
263
264    /// Returns a glyph class.
265    #[inline]
266    pub fn class(&self, glyph_id: GlyphId) -> Option<u16> {
267        if glyph_id.0 == 0xFFFF {
268            return Some(u16::from(class::DELETED_GLYPH));
269        }
270
271        self.lookup.value(glyph_id)
272    }
273
274    /// Returns a class entry.
275    #[inline]
276    pub fn entry(&self, state: u16, mut class: u16) -> Option<GenericStateEntry<T>> {
277        if u32::from(class) >= self.number_of_classes {
278            class = u16::from(class::OUT_OF_BOUNDS);
279        }
280
281        let state_idx =
282            usize::from(state) * usize::num_from(self.number_of_classes) + usize::from(class);
283
284        let entry_idx: u16 = Stream::read_at(self.state_array, state_idx * u16::SIZE)?;
285        Stream::read_at(
286            self.entry_table,
287            usize::from(entry_idx) * GenericStateEntry::<T>::SIZE,
288        )
289    }
290}
291
292impl<T> core::fmt::Debug for ExtendedStateTable<'_, T> {
293    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
294        write!(f, "ExtendedStateTable {{ ... }}")
295    }
296}
297
298/// A [lookup table](
299/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
300///
301/// u32 values in Format10 tables will be truncated to u16.
302/// u64 values in Format10 tables are not supported.
303#[derive(Clone)]
304pub struct Lookup<'a> {
305    data: LookupInner<'a>,
306}
307
308impl<'a> Lookup<'a> {
309    /// Parses a lookup table from raw data.
310    ///
311    /// `number_of_glyphs` is from the `maxp` table.
312    #[inline]
313    pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
314        LookupInner::parse(number_of_glyphs, data).map(|data| Self { data })
315    }
316
317    /// Returns a value associated with the specified glyph.
318    #[inline]
319    pub fn value(&self, glyph_id: GlyphId) -> Option<u16> {
320        self.data.value(glyph_id)
321    }
322}
323
324impl core::fmt::Debug for Lookup<'_> {
325    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
326        write!(f, "Lookup {{ ... }}")
327    }
328}
329
330#[derive(Clone)]
331enum LookupInner<'a> {
332    Format1(LazyArray16<'a, u16>),
333    Format2(BinarySearchTable<'a, LookupSegment>),
334    Format4(BinarySearchTable<'a, LookupSegment>, &'a [u8]),
335    Format6(BinarySearchTable<'a, LookupSingle>),
336    Format8 {
337        first_glyph: u16,
338        values: LazyArray16<'a, u16>,
339    },
340    Format10 {
341        value_size: u16,
342        first_glyph: u16,
343        glyph_count: u16,
344        data: &'a [u8],
345    },
346}
347
348impl<'a> LookupInner<'a> {
349    fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
350        let mut s = Stream::new(data);
351        let format = s.read::<u16>()?;
352        match format {
353            0 => {
354                let values = s.read_array16::<u16>(number_of_glyphs.get())?;
355                Some(Self::Format1(values))
356            }
357            2 => {
358                let bsearch = BinarySearchTable::<LookupSegment>::parse(s.tail()?)?;
359                Some(Self::Format2(bsearch))
360            }
361            4 => {
362                let bsearch = BinarySearchTable::<LookupSegment>::parse(s.tail()?)?;
363                Some(Self::Format4(bsearch, data))
364            }
365            6 => {
366                let bsearch = BinarySearchTable::<LookupSingle>::parse(s.tail()?)?;
367                Some(Self::Format6(bsearch))
368            }
369            8 => {
370                let first_glyph = s.read::<u16>()?;
371                let glyph_count = s.read::<u16>()?;
372                let values = s.read_array16::<u16>(glyph_count)?;
373                Some(Self::Format8 {
374                    first_glyph,
375                    values,
376                })
377            }
378            10 => {
379                let value_size = s.read::<u16>()?;
380                let first_glyph = s.read::<u16>()?;
381                let glyph_count = s.read::<u16>()?;
382                Some(Self::Format10 {
383                    value_size,
384                    first_glyph,
385                    glyph_count,
386                    data: s.tail()?,
387                })
388            }
389            _ => None,
390        }
391    }
392
393    fn value(&self, glyph_id: GlyphId) -> Option<u16> {
394        match self {
395            Self::Format1(values) => values.get(glyph_id.0),
396            Self::Format2(ref bsearch) => bsearch.get(glyph_id).map(|v| v.value),
397            Self::Format4(ref bsearch, data) => {
398                // In format 4, LookupSegment contains an offset to a list of u16 values.
399                // One value for each glyph in the LookupSegment range.
400                let segment = bsearch.get(glyph_id)?;
401                let index = glyph_id.0.checked_sub(segment.first_glyph)?;
402                let offset = usize::from(segment.value) + u16::SIZE * usize::from(index);
403                Stream::read_at::<u16>(data, offset)
404            }
405            Self::Format6(ref bsearch) => bsearch.get(glyph_id).map(|v| v.value),
406            Self::Format8 {
407                first_glyph,
408                values,
409            } => {
410                let idx = glyph_id.0.checked_sub(*first_glyph)?;
411                values.get(idx)
412            }
413            Self::Format10 {
414                value_size,
415                first_glyph,
416                glyph_count,
417                data,
418            } => {
419                let idx = glyph_id.0.checked_sub(*first_glyph)?;
420                let mut s = Stream::new(data);
421                match value_size {
422                    1 => s.read_array16::<u8>(*glyph_count)?.get(idx).map(u16::from),
423                    2 => s.read_array16::<u16>(*glyph_count)?.get(idx),
424                    // TODO: we should return u32 here, but this is not supported yet
425                    4 => s
426                        .read_array16::<u32>(*glyph_count)?
427                        .get(idx)
428                        .map(|n| n as u16),
429                    _ => None, // 8 is also supported
430                }
431            }
432        }
433    }
434}
435
436/// A binary searching table as defined at
437/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html
438#[derive(Clone)]
439struct BinarySearchTable<'a, T: BinarySearchValue> {
440    values: LazyArray16<'a, T>,
441    len: NonZeroU16, // values length excluding termination segment
442}
443
444impl<'a, T: BinarySearchValue + core::fmt::Debug> BinarySearchTable<'a, T> {
445    #[inline(never)]
446    fn parse(data: &'a [u8]) -> Option<Self> {
447        let mut s = Stream::new(data);
448        let segment_size = s.read::<u16>()?;
449        let number_of_segments = s.read::<u16>()?;
450        s.advance(6); // search_range + entry_selector + range_shift
451
452        if usize::from(segment_size) != T::SIZE {
453            return None;
454        }
455
456        if number_of_segments == 0 {
457            return None;
458        }
459
460        let values = s.read_array16::<T>(number_of_segments)?;
461
462        // 'The number of termination values that need to be included is table-specific.
463        // The value that indicates binary search termination is 0xFFFF.'
464        let mut len = number_of_segments;
465        if values.last()?.is_termination() {
466            len = len.checked_sub(1)?;
467        }
468
469        Some(BinarySearchTable {
470            len: NonZeroU16::new(len)?,
471            values,
472        })
473    }
474
475    fn get(&self, key: GlyphId) -> Option<T> {
476        let mut min = 0;
477        let mut max = (self.len.get() as isize) - 1;
478        while min <= max {
479            let mid = (min + max) / 2;
480            let v = self.values.get(mid as u16)?;
481            match v.contains(key) {
482                core::cmp::Ordering::Less => max = mid - 1,
483                core::cmp::Ordering::Greater => min = mid + 1,
484                core::cmp::Ordering::Equal => return Some(v),
485            }
486        }
487
488        None
489    }
490}
491
492trait BinarySearchValue: FromData {
493    fn is_termination(&self) -> bool;
494    fn contains(&self, glyph_id: GlyphId) -> core::cmp::Ordering;
495}
496
497#[derive(Clone, Copy, Debug)]
498struct LookupSegment {
499    last_glyph: u16,
500    first_glyph: u16,
501    value: u16,
502}
503
504impl FromData for LookupSegment {
505    const SIZE: usize = 6;
506
507    #[inline]
508    fn parse(data: &[u8]) -> Option<Self> {
509        let mut s = Stream::new(data);
510        Some(LookupSegment {
511            last_glyph: s.read::<u16>()?,
512            first_glyph: s.read::<u16>()?,
513            value: s.read::<u16>()?,
514        })
515    }
516}
517
518impl BinarySearchValue for LookupSegment {
519    #[inline]
520    fn is_termination(&self) -> bool {
521        self.last_glyph == 0xFFFF && self.first_glyph == 0xFFFF
522    }
523
524    #[inline]
525    fn contains(&self, id: GlyphId) -> core::cmp::Ordering {
526        if id.0 < self.first_glyph {
527            core::cmp::Ordering::Less
528        } else if id.0 <= self.last_glyph {
529            core::cmp::Ordering::Equal
530        } else {
531            core::cmp::Ordering::Greater
532        }
533    }
534}
535
536#[derive(Clone, Copy, Debug)]
537struct LookupSingle {
538    glyph: u16,
539    value: u16,
540}
541
542impl FromData for LookupSingle {
543    const SIZE: usize = 4;
544
545    #[inline]
546    fn parse(data: &[u8]) -> Option<Self> {
547        let mut s = Stream::new(data);
548        Some(LookupSingle {
549            glyph: s.read::<u16>()?,
550            value: s.read::<u16>()?,
551        })
552    }
553}
554
555impl BinarySearchValue for LookupSingle {
556    #[inline]
557    fn is_termination(&self) -> bool {
558        self.glyph == 0xFFFF
559    }
560
561    #[inline]
562    fn contains(&self, id: GlyphId) -> core::cmp::Ordering {
563        id.0.cmp(&self.glyph)
564    }
565}