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::GlyphId;
10use crate::parser::{Stream, FromData, LazyArray16, Offset, Offset16, Offset32, NumFrom};
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 as u8);
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 as u8;
189        }
190
191        let entry_idx = self.state_array.get(
192            usize::from(state) * usize::from(self.number_of_classes) + usize::from(class)
193        )?;
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
222/// An [Extended State Table](
223/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
224///
225/// Also called `STXHeader`.
226///
227/// Currently used by `kerx` and `morx` tables.
228#[derive(Clone)]
229pub struct ExtendedStateTable<'a, T> {
230    number_of_classes: u32,
231    lookup: Lookup<'a>,
232    state_array: &'a [u8],
233    entry_table: &'a [u8],
234    entry_type: core::marker::PhantomData<T>,
235}
236
237impl<'a, T: FromData> ExtendedStateTable<'a, T> {
238    // TODO: make private
239    /// Parses an Extended State Table from a stream.
240    ///
241    /// `number_of_glyphs` is from the `maxp` table.
242    pub fn parse(number_of_glyphs: NonZeroU16, s: &mut Stream<'a>) -> Option<Self> {
243        let data = s.tail()?;
244
245        let number_of_classes = s.read::<u32>()?;
246        // Note that offsets are not from the subtable start,
247        // but from subtable start + `header_size`.
248        // So there is not need to subtract the `header_size`.
249        let lookup_table_offset = s.read::<Offset32>()?.to_usize();
250        let state_array_offset = s.read::<Offset32>()?.to_usize();
251        let entry_table_offset = s.read::<Offset32>()?.to_usize();
252
253        Some(ExtendedStateTable {
254            number_of_classes,
255            lookup: Lookup::parse(number_of_glyphs, data.get(lookup_table_offset..)?)?,
256            // We don't know the actual data size and it's kinda expensive to calculate.
257            // So we are simply storing all the data past the offset.
258            // Despite the fact that they may overlap.
259            state_array: data.get(state_array_offset..)?,
260            entry_table: data.get(entry_table_offset..)?,
261            entry_type: core::marker::PhantomData,
262        })
263    }
264
265    /// Returns a glyph class.
266    #[inline]
267    pub fn class(&self, glyph_id: GlyphId) -> Option<u16> {
268        if glyph_id.0 == 0xFFFF {
269            return Some(u16::from(class::DELETED_GLYPH));
270        }
271
272        self.lookup.value(glyph_id)
273    }
274
275    /// Returns a class entry.
276    #[inline]
277    pub fn entry(&self, state: u16, mut class: u16) -> Option<GenericStateEntry<T>> {
278        if u32::from(class) >= self.number_of_classes {
279            class = u16::from(class::OUT_OF_BOUNDS);
280        }
281
282        let state_idx =
283            usize::from(state) * usize::num_from(self.number_of_classes) + usize::from(class);
284
285        let entry_idx: u16 = Stream::read_at(self.state_array, state_idx * u16::SIZE)?;
286        Stream::read_at(self.entry_table, usize::from(entry_idx) * GenericStateEntry::<T>::SIZE)
287    }
288}
289
290impl<T> core::fmt::Debug for ExtendedStateTable<'_, T> {
291    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
292        write!(f, "ExtendedStateTable {{ ... }}")
293    }
294}
295
296
297/// A [lookup table](
298/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
299///
300/// u32 values in Format10 tables will be truncated to u16.
301/// u64 values in Format10 tables are not supported.
302#[derive(Clone)]
303pub struct Lookup<'a> {
304    data: LookupInner<'a>,
305}
306
307impl<'a> Lookup<'a> {
308    /// Parses a lookup table from raw data.
309    ///
310    /// `number_of_glyphs` is from the `maxp` table.
311    #[inline]
312    pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
313        LookupInner::parse(number_of_glyphs, data).map(|data| Self { data })
314    }
315
316    /// Returns a value associated with the specified glyph.
317    #[inline]
318    pub fn value(&self, glyph_id: GlyphId) -> Option<u16> {
319        self.data.value(glyph_id)
320    }
321}
322
323impl core::fmt::Debug for Lookup<'_> {
324    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
325        write!(f, "Lookup {{ ... }}")
326    }
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 { first_glyph, values })
374            }
375            10 => {
376                let value_size = s.read::<u16>()?;
377                let first_glyph = s.read::<u16>()?;
378                let glyph_count = s.read::<u16>()?;
379                Some(Self::Format10 { value_size, first_glyph, glyph_count, data: s.tail()? })
380            }
381            _ => {
382                None
383            }
384        }
385    }
386
387    fn value(&self, glyph_id: GlyphId) -> Option<u16> {
388        match self {
389            Self::Format1(values) => {
390                values.get(glyph_id.0)
391            }
392            Self::Format2(ref bsearch) => {
393                bsearch.get(glyph_id).map(|v| v.value)
394            }
395            Self::Format4(ref bsearch, data) => {
396                // In format 4, LookupSegment contains an offset to a list of u16 values.
397                // One value for each glyph in the LookupSegment range.
398                let segment = bsearch.get(glyph_id)?;
399                let index = glyph_id.0.checked_sub(segment.first_glyph)?;
400                let offset = usize::from(segment.value) + u16::SIZE * usize::from(index);
401                Stream::read_at::<u16>(data, offset)
402            }
403            Self::Format6(ref bsearch) => {
404                bsearch.get(glyph_id).map(|v| v.value)
405            }
406            Self::Format8 { first_glyph, values } => {
407                let idx = glyph_id.0.checked_sub(*first_glyph)?;
408                values.get(idx)
409            }
410            Self::Format10 { value_size, first_glyph, glyph_count, data } => {
411                let idx = glyph_id.0.checked_sub(*first_glyph)?;
412                let mut s = Stream::new(data);
413                match value_size {
414                    1 => s.read_array16::<u8>(*glyph_count)?.get(idx).map(u16::from),
415                    2 => s.read_array16::<u16>(*glyph_count)?.get(idx),
416                    // TODO: we should return u32 here, but this is not supported yet
417                    4 => s.read_array16::<u32>(*glyph_count)?.get(idx).map(|n| n as u16),
418                    _ => None, // 8 is also supported
419                }
420            }
421        }
422    }
423}
424
425/// A binary searching table as defined at
426/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html
427#[derive(Clone)]
428struct BinarySearchTable<'a, T: BinarySearchValue> {
429    values: LazyArray16<'a, T>,
430    len: NonZeroU16, // values length excluding termination segment
431}
432
433impl<'a, T: BinarySearchValue + core::fmt::Debug> BinarySearchTable<'a, T> {
434    #[inline(never)]
435    fn parse(data: &'a [u8]) -> Option<Self> {
436        let mut s = Stream::new(data);
437        let segment_size = s.read::<u16>()?;
438        let number_of_segments = s.read::<u16>()?;
439        s.advance(6); // search_range + entry_selector + range_shift
440
441        if usize::from(segment_size) != T::SIZE {
442            return None;
443        }
444
445        if number_of_segments == 0 {
446            return None;
447        }
448
449        let values = s.read_array16::<T>(number_of_segments)?;
450
451        // 'The number of termination values that need to be included is table-specific.
452        // The value that indicates binary search termination is 0xFFFF.'
453        let mut len = number_of_segments;
454        if values.last()?.is_termination() {
455            len = len.checked_sub(1)?;
456        }
457
458        Some(BinarySearchTable {
459            len: NonZeroU16::new(len)?,
460            values,
461        })
462    }
463
464    fn get(&self, key: GlyphId) -> Option<T> {
465        let mut min = 0;
466        let mut max = (self.len.get() as isize) - 1;
467        while min <= max {
468            let mid = (min + max) / 2;
469            let v = self.values.get(mid as u16)?;
470            match v.contains(key) {
471                core::cmp::Ordering::Less    => max = mid - 1,
472                core::cmp::Ordering::Greater => min = mid + 1,
473                core::cmp::Ordering::Equal   => return Some(v),
474            }
475        }
476
477        None
478    }
479}
480
481
482trait BinarySearchValue: FromData {
483    fn is_termination(&self) -> bool;
484    fn contains(&self, glyph_id: GlyphId) -> core::cmp::Ordering;
485}
486
487
488#[derive(Clone, Copy, Debug)]
489struct LookupSegment {
490    last_glyph: u16,
491    first_glyph: u16,
492    value: u16,
493}
494
495impl FromData for LookupSegment {
496    const SIZE: usize = 6;
497
498    #[inline]
499    fn parse(data: &[u8]) -> Option<Self> {
500        let mut s = Stream::new(data);
501        Some(LookupSegment {
502            last_glyph: s.read::<u16>()?,
503            first_glyph: s.read::<u16>()?,
504            value: s.read::<u16>()?,
505        })
506    }
507}
508
509impl BinarySearchValue for LookupSegment {
510    #[inline]
511    fn is_termination(&self) -> bool {
512        self.last_glyph == 0xFFFF && self.first_glyph == 0xFFFF
513    }
514
515    #[inline]
516    fn contains(&self, id: GlyphId) -> core::cmp::Ordering {
517        if id.0 < self.first_glyph {
518            core::cmp::Ordering::Less
519        } else if id.0 <= self.last_glyph {
520            core::cmp::Ordering::Equal
521        } else {
522            core::cmp::Ordering::Greater
523        }
524    }
525}
526
527
528#[derive(Clone, Copy, Debug)]
529struct LookupSingle {
530    glyph: u16,
531    value: u16,
532}
533
534impl FromData for LookupSingle {
535    const SIZE: usize = 4;
536
537    #[inline]
538    fn parse(data: &[u8]) -> Option<Self> {
539        let mut s = Stream::new(data);
540        Some(LookupSingle {
541            glyph: s.read::<u16>()?,
542            value: s.read::<u16>()?,
543        })
544    }
545}
546
547impl BinarySearchValue for LookupSingle {
548    #[inline]
549    fn is_termination(&self) -> bool {
550        self.glyph == 0xFFFF
551    }
552
553    #[inline]
554    fn contains(&self, id: GlyphId) -> core::cmp::Ordering {
555        id.0.cmp(&self.glyph)
556    }
557}