1use crate::parser::{
2 FromData, FromSlice, LazyArray16, LazyOffsetArray16, Offset, Offset16, Offset32, Stream,
3};
4
5pub type LookupList<'a> = LazyOffsetArray16<'a, Lookup<'a>>;
7
8#[derive(Clone, Copy, Debug)]
10pub struct Lookup<'a> {
11 pub flags: LookupFlags,
13 pub subtables: LookupSubtables<'a>,
15 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
44pub trait LookupSubtable<'a>: Sized {
48 fn parse(data: &'a [u8], kind: u16) -> Option<Self>;
50}
51
52#[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 #[inline]
69 pub fn len(&self) -> u16 {
70 self.offsets.len()
71 }
72
73 pub fn is_empty(&self) -> bool {
75 self.offsets.is_empty()
76 }
77
78 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 #[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#[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#[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}