ttf_parser/ggg/
lookup.rs

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