ttf_parser/ggg/
lookup.rs

1use crate::parser::{FromData, FromSlice, LazyArray16, LazyOffsetArray16, Offset, Offset16, Offset32, Stream};
2
3/// A list of [`Lookup`] values.
4pub type LookupList<'a> = LazyOffsetArray16<'a, Lookup<'a>>;
5
6/// A [Lookup Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-table).
7#[derive(Clone, Copy, Debug)]
8pub struct Lookup<'a> {
9    /// Lookup qualifiers.
10    pub flags: LookupFlags,
11    /// Available subtables.
12    pub subtables: LookupSubtables<'a>,
13    /// Index into GDEF mark glyph sets structure.
14    pub mark_filtering_set: Option<u16>,
15}
16
17impl<'a> FromSlice<'a> for Lookup<'a> {
18    fn parse(data: &'a [u8]) -> Option<Self> {
19        let mut s = Stream::new(data);
20        let kind = s.read::<u16>()?;
21        let flags = s.read::<LookupFlags>()?;
22        let count = s.read::<u16>()?;
23        let offsets = s.read_array16(count)?;
24
25        let mut mark_filtering_set: Option<u16> = None;
26        if flags.use_mark_filtering_set() {
27            mark_filtering_set = Some(s.read::<u16>()?);
28        }
29
30        Some(Self {
31            flags,
32            subtables: LookupSubtables { kind, data, offsets },
33            mark_filtering_set,
34        })
35    }
36}
37
38/// A trait for parsing Lookup subtables.
39///
40/// Internal use only.
41pub trait LookupSubtable<'a>: Sized {
42    /// Parses raw data.
43    fn parse(data: &'a [u8], kind: u16) -> Option<Self>;
44}
45
46/// A list of lookup subtables.
47#[derive(Clone, Copy)]
48pub struct LookupSubtables<'a> {
49    kind: u16,
50    data: &'a [u8],
51    offsets: LazyArray16<'a, Offset16>,
52}
53
54impl core::fmt::Debug for LookupSubtables<'_> {
55    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
56        write!(f, "LookupSubtables {{ ... }}")
57    }
58}
59
60impl<'a> LookupSubtables<'a> {
61    /// Returns a number of items in the LookupSubtables.
62    #[inline]
63    pub fn len(&self) -> u16 {
64        self.offsets.len()
65    }
66
67    /// Parses a subtable at index.
68    ///
69    /// Accepts either
70    /// [`PositioningSubtable`](crate::gpos::PositioningSubtable)
71    /// or [`SubstitutionSubtable`](crate::gsub::SubstitutionSubtable).
72    ///
73    /// Technically, we can enforce it at compile time, but it makes code too convoluted.
74    pub fn get<T: LookupSubtable<'a>>(&self, index: u16) -> Option<T> {
75        let offset = self.offsets.get(index)?.to_usize();
76        let data = self.data.get(offset..)?;
77        T::parse(data, self.kind)
78    }
79
80    /// Creates an iterator over subtables.
81    ///
82    /// We cannot use `IntoIterator` here, because we have to use user-provided base type.
83    pub fn into_iter<T: LookupSubtable<'a>>(self) -> LookupSubtablesIter<'a, T> {
84        LookupSubtablesIter {
85            data: self,
86            index: 0,
87            data_type: core::marker::PhantomData,
88        }
89    }
90}
91
92/// An iterator over lookup subtables.
93#[allow(missing_debug_implementations)]
94pub struct LookupSubtablesIter<'a, T: LookupSubtable<'a>> {
95    data: LookupSubtables<'a>,
96    index: u16,
97    data_type: core::marker::PhantomData<T>,
98}
99
100impl<'a, T: LookupSubtable<'a>> Iterator for LookupSubtablesIter<'a, T> {
101    type Item = T;
102
103    fn next(&mut self) -> Option<Self::Item> {
104        if self.index < self.data.len() {
105            self.index += 1;
106            self.data.get(self.index - 1)
107        } else {
108            None
109        }
110    }
111}
112
113/// Lookup table flags.
114#[allow(missing_docs)]
115#[derive(Clone, Copy, Debug)]
116pub struct LookupFlags(pub u16);
117
118#[allow(missing_docs)]
119impl LookupFlags {
120    #[inline] pub fn right_to_left(self) -> bool { self.0 & 0x0001 != 0 }
121    #[inline] pub fn ignore_base_glyphs(self) -> bool { self.0 & 0x0002 != 0 }
122    #[inline] pub fn ignore_ligatures(self) -> bool { self.0 & 0x0004 != 0 }
123    #[inline] pub fn ignore_marks(self) -> bool { self.0 & 0x0008 != 0 }
124    #[inline] pub fn ignore_flags(self) -> bool { self.0 & 0x000E != 0 }
125    #[inline] pub fn use_mark_filtering_set(self) -> bool { self.0 & 0x0010 != 0 }
126    #[inline] pub fn mark_attachment_type(self) -> u8 { (self.0 & 0xFF00) as u8 }
127}
128
129impl FromData for LookupFlags {
130    const SIZE: usize = 2;
131
132    #[inline]
133    fn parse(data: &[u8]) -> Option<Self> {
134        u16::parse(data).map(Self)
135    }
136}
137
138pub(crate) fn parse_extension_lookup<'a, T: 'a>(
139    data: &'a [u8],
140    parse: impl FnOnce(&'a [u8], u16) -> Option<T>,
141) -> Option<T> {
142    let mut s = Stream::new(data);
143    let format = s.read::<u16>()?;
144    match format {
145        1 => {
146            let kind = s.read::<u16>()?;
147            let offset = s.read::<Offset32>()?.to_usize();
148            parse(data.get(offset..)?, kind)
149        }
150        _ => None,
151    }
152}