read_fonts/tables/layout/
header.rs1use super::*;
4
5#[derive(Clone)]
7pub enum LayoutOffsetArray<'a, T: FontRead<'a, Args = ()>> {
8 Offset16(ArrayOfOffsets<'a, T, Offset16>),
10 Offset24(ArrayOfOffsets<'a, T, Offset24>),
12 Offset32(ArrayOfOffsets<'a, T, Offset32>),
14}
15
16impl<'a, T: FontRead<'a, Args = ()> + 'a> LayoutOffsetArray<'a, T> {
17 pub fn len(&self) -> usize {
19 match self {
20 Self::Offset16(array) => array.len(),
21 Self::Offset24(array) => array.len(),
22 Self::Offset32(array) => array.len(),
23 }
24 }
25
26 pub fn is_empty(&self) -> bool {
28 self.len() == 0
29 }
30
31 pub fn get(&self, index: usize) -> Result<T, ReadError> {
33 match self {
34 Self::Offset16(array) => array.get(index),
35 Self::Offset24(array) => array.get(index),
36 Self::Offset32(array) => array.get(index),
37 }
38 }
39
40 pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
42 let (one, two, three) = match self {
43 Self::Offset16(array) => (Some(array.iter()), None, None),
44 Self::Offset24(array) => (None, Some(array.iter()), None),
45 Self::Offset32(array) => (None, None, Some(array.iter())),
46 };
47 one.into_iter()
48 .flatten()
49 .chain(two.into_iter().flatten())
50 .chain(three.into_iter().flatten())
51 }
52
53 pub fn iter_as_nullable(&self) -> impl Iterator<Item = Option<Result<T, ReadError>>> + 'a {
55 self.iter().map(|result| match result {
56 Err(ReadError::NullOffset) => None,
57 other => Some(other),
58 })
59 }
60}
61
62#[derive(Clone)]
65pub enum SelectedLookupList<'a, T: FontRead<'a, Args = ()>> {
66 Original(LookupList<'a, T>),
68 Extended(LookupList2<'a, T>),
70}
71
72impl<'a, T: FontRead<'a, Args = ()>> From<LookupList<'a, T>> for SelectedLookupList<'a, T> {
73 fn from(list: LookupList<'a, T>) -> Self {
74 Self::Original(list)
75 }
76}
77
78impl<'a, T: FontRead<'a, Args = ()>> From<LookupList2<'a, T>> for SelectedLookupList<'a, T> {
79 fn from(list: LookupList2<'a, T>) -> Self {
80 Self::Extended(list)
81 }
82}
83
84impl<'a, T: FontRead<'a, Args = ()> + 'a> SelectedLookupList<'a, T> {
85 pub fn lookup_count(&self) -> u16 {
87 match self {
88 Self::Original(table) => table.lookup_count(),
89 Self::Extended(table) => table.lookup_count(),
90 }
91 }
92
93 pub fn lookups(&self) -> LayoutOffsetArray<'a, T> {
95 match self {
96 Self::Original(table) => LayoutOffsetArray::Offset16(table.lookups()),
97 Self::Extended(table) => LayoutOffsetArray::Offset32(table.lookups()),
98 }
99 }
100
101 pub fn offset_data(&self) -> FontData<'a> {
103 match self {
104 Self::Original(table) => table.offset_data(),
105 Self::Extended(table) => table.offset_data(),
106 }
107 }
108}
109
110#[derive(Clone, Copy, Debug, PartialEq, Eq)]
116pub(crate) enum HeaderOffset {
117 Original(Offset32),
118 Extended(Offset32),
119}
120
121impl HeaderOffset {
122 pub(crate) fn select(
125 original: Nullable<Offset16>,
126 extended: Option<Nullable<Offset32>>,
127 has_extended: bool,
128 ) -> Result<Self, ReadError> {
129 if has_extended {
130 let extended = *extended.ok_or(ReadError::OutOfBounds)?.offset();
131 if !extended.is_null() {
132 return Ok(Self::Extended(extended));
133 }
134 }
135 Ok(Self::Original(Offset32::new(original.offset().to_u32())))
136 }
137
138 pub(crate) fn offset(self) -> Offset32 {
139 match self {
140 Self::Original(offset) | Self::Extended(offset) => offset,
141 }
142 }
143
144 pub(crate) fn is_null(self) -> bool {
145 self.offset().is_null()
146 }
147
148 pub(crate) fn resolve<'a, T: FontRead<'a, Args = ()>>(
150 self,
151 data: FontData<'a>,
152 ) -> Option<Result<T, ReadError>> {
153 (!self.is_null()).then(|| self.offset().resolve(data))
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 fn original(offset: u16) -> Nullable<Offset16> {
162 Scalar::from_raw(offset.to_be_bytes())
163 }
164
165 fn extended(offset: u32) -> Option<Nullable<Offset32>> {
166 Some(Scalar::from_raw(offset.to_be_bytes()))
167 }
168
169 #[test]
170 fn header_offset_prefers_nonzero_extended_offset() {
171 assert_eq!(
172 HeaderOffset::select(original(10), extended(70000), true),
173 Ok(HeaderOffset::Extended(Offset32::new(70000)))
174 );
175 }
176
177 #[test]
178 fn header_offset_falls_back_to_original_offset() {
179 assert_eq!(
181 HeaderOffset::select(original(10), extended(0), true),
182 Ok(HeaderOffset::Original(Offset32::new(10)))
183 );
184 assert_eq!(
186 HeaderOffset::select(original(10), extended(70000), false),
187 Ok(HeaderOffset::Original(Offset32::new(10)))
188 );
189 assert!(HeaderOffset::select(original(0), extended(0), true)
190 .unwrap()
191 .is_null());
192 }
193
194 #[test]
195 fn header_offset_requires_extended_field_in_extended_header() {
196 assert_eq!(
197 HeaderOffset::select(original(10), None, true),
198 Err(ReadError::OutOfBounds)
199 );
200 assert_eq!(
201 HeaderOffset::select(original(10), None, false),
202 Ok(HeaderOffset::Original(Offset32::new(10)))
203 );
204 }
205}