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<'_> = Stream::new(data);
22 let kind: u16 = s.read::<u16>()?;
23 let flags: LookupFlags = s.read::<LookupFlags>()?;
24 let count: u16 = s.read::<u16>()?;
25 let offsets: LazyArray16<'_, Offset16> = 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) 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<'_> = Stream::new(data);
156 let format: u16 = s.read::<u16>()?;
157 match format {
158 1 => {
159 let kind: u16 = s.read::<u16>()?;
160 let offset: usize = s.read::<Offset32>()?.to_usize();
161 parse(data.get(index:offset..)?, kind)
162 }
163 _ => None,
164 }
165}
166