1// Suppresses `minor_version` variable warning.
2#![allow(unused_variables)]
3
4#[cfg(feature = "variable-fonts")]
5use super::FeatureVariations;
6use super::LookupList;
7#[cfg(feature = "variable-fonts")]
8use crate::parser::Offset32;
9use crate::parser::{FromData, LazyArray16, Offset, Offset16, Stream};
10use crate::Tag;
11
12/// A [Layout Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#table-organization).
13#[derive(Clone, Copy, Debug)]
14pub struct LayoutTable<'a> {
15 /// A list of all supported scripts.
16 pub scripts: ScriptList<'a>,
17 /// A list of all supported features.
18 pub features: FeatureList<'a>,
19 /// A list of all lookups.
20 pub lookups: LookupList<'a>,
21 /// Used to substitute an alternate set of lookup tables
22 /// to use for any given feature under specified conditions.
23 #[cfg(feature = "variable-fonts")]
24 pub variations: Option<FeatureVariations<'a>>,
25}
26
27impl<'a> LayoutTable<'a> {
28 pub(crate) fn parse(data: &'a [u8]) -> Option<Self> {
29 let mut s = Stream::new(data);
30
31 let major_version = s.read::<u16>()?;
32 let minor_version = s.read::<u16>()?;
33 if major_version != 1 {
34 return None;
35 }
36
37 let scripts = ScriptList::parse(s.read_at_offset16(data)?)?;
38 let features = FeatureList::parse(s.read_at_offset16(data)?)?;
39 let lookups = LookupList::parse(s.read_at_offset16(data)?)?;
40
41 #[cfg(feature = "variable-fonts")]
42 {
43 let mut variations_offset = None;
44 if minor_version >= 1 {
45 variations_offset = s.read::<Option<Offset32>>()?;
46 }
47
48 let variations = match variations_offset {
49 Some(offset) => data
50 .get(offset.to_usize()..)
51 .and_then(FeatureVariations::parse),
52 None => None,
53 };
54
55 Some(Self {
56 scripts,
57 features,
58 lookups,
59 variations,
60 })
61 }
62
63 #[cfg(not(feature = "variable-fonts"))]
64 {
65 Some(Self {
66 scripts,
67 features,
68 lookups,
69 })
70 }
71 }
72}
73
74/// An index in [`ScriptList`].
75pub type ScriptIndex = u16;
76/// An index in [`LanguageSystemList`].
77pub type LanguageIndex = u16;
78/// An index in [`FeatureList`].
79pub type FeatureIndex = u16;
80/// An index in [`LookupList`].
81pub type LookupIndex = u16;
82/// An index in [`FeatureVariations`].
83pub type VariationIndex = u32;
84
85/// A trait to parse item in [`RecordList`].
86///
87/// Internal use only.
88pub trait RecordListItem<'a>: Sized {
89 /// Parses raw data.
90 fn parse(tag: Tag, data: &'a [u8]) -> Option<Self>;
91}
92
93/// A data storage used by [`ScriptList`], [`LanguageSystemList`] and [`FeatureList`] data types.
94#[derive(Clone, Copy, Debug)]
95pub struct RecordList<'a, T: RecordListItem<'a>> {
96 data: &'a [u8],
97 records: LazyArray16<'a, TagRecord>,
98 data_type: core::marker::PhantomData<T>,
99}
100
101impl<'a, T: RecordListItem<'a>> RecordList<'a, T> {
102 fn parse(data: &'a [u8]) -> Option<Self> {
103 let mut s = Stream::new(data);
104 let count = s.read::<u16>()?;
105 let records = s.read_array16(count)?;
106 Some(Self {
107 data,
108 records,
109 data_type: core::marker::PhantomData,
110 })
111 }
112
113 /// Returns a number of items in the RecordList.
114 pub fn len(&self) -> u16 {
115 self.records.len()
116 }
117
118 /// Checks that RecordList is empty.
119 pub fn is_empty(&self) -> bool {
120 self.records.is_empty()
121 }
122
123 /// Returns RecordList value by index.
124 pub fn get(&self, index: u16) -> Option<T> {
125 let record = self.records.get(index)?;
126 self.data
127 .get(record.offset.to_usize()..)
128 .and_then(|data| T::parse(record.tag, data))
129 }
130
131 /// Returns RecordList value by [`Tag`].
132 pub fn find(&self, tag: Tag) -> Option<T> {
133 let record = self
134 .records
135 .binary_search_by(|record| record.tag.cmp(&tag))
136 .map(|p| p.1)?;
137 self.data
138 .get(record.offset.to_usize()..)
139 .and_then(|data| T::parse(record.tag, data))
140 }
141
142 /// Returns RecordList value index by [`Tag`].
143 pub fn index(&self, tag: Tag) -> Option<u16> {
144 self.records
145 .binary_search_by(|record| record.tag.cmp(&tag))
146 .map(|p| p.0)
147 }
148}
149
150impl<'a, T: RecordListItem<'a>> IntoIterator for RecordList<'a, T> {
151 type Item = T;
152 type IntoIter = RecordListIter<'a, T>;
153
154 #[inline]
155 fn into_iter(self) -> Self::IntoIter {
156 RecordListIter {
157 list: self,
158 index: 0,
159 }
160 }
161}
162
163/// An iterator over [`RecordList`] values.
164#[allow(missing_debug_implementations)]
165pub struct RecordListIter<'a, T: RecordListItem<'a>> {
166 list: RecordList<'a, T>,
167 index: u16,
168}
169
170impl<'a, T: RecordListItem<'a>> Iterator for RecordListIter<'a, T> {
171 type Item = T;
172
173 fn next(&mut self) -> Option<Self::Item> {
174 if self.index < self.list.len() {
175 self.index += 1;
176 self.list.get(self.index - 1)
177 } else {
178 None
179 }
180 }
181}
182
183/// A list of [`Script`] records.
184pub type ScriptList<'a> = RecordList<'a, Script<'a>>;
185/// A list of [`LanguageSystem`] records.
186pub type LanguageSystemList<'a> = RecordList<'a, LanguageSystem<'a>>;
187/// A list of [`Feature`] records.
188pub type FeatureList<'a> = RecordList<'a, Feature<'a>>;
189
190#[derive(Clone, Copy, Debug)]
191struct TagRecord {
192 tag: Tag,
193 offset: Offset16,
194}
195
196impl FromData for TagRecord {
197 const SIZE: usize = 6;
198
199 #[inline]
200 fn parse(data: &[u8]) -> Option<Self> {
201 let mut s: Stream<'_> = Stream::new(data);
202 Some(Self {
203 tag: s.read::<Tag>()?,
204 offset: s.read::<Offset16>()?,
205 })
206 }
207}
208
209/// A [Script Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-table-and-language-system-record).
210#[derive(Clone, Copy, Debug)]
211pub struct Script<'a> {
212 /// Script tag.
213 pub tag: Tag,
214 /// Default language.
215 pub default_language: Option<LanguageSystem<'a>>,
216 /// List of supported languages, excluding the default one. Listed alphabetically.
217 pub languages: LanguageSystemList<'a>,
218}
219
220impl<'a> RecordListItem<'a> for Script<'a> {
221 fn parse(tag: Tag, data: &'a [u8]) -> Option<Self> {
222 let mut s: Stream<'_> = Stream::new(data);
223 let mut default_language: Option> = None;
224 if let Some(offset: Offset16) = s.read::<Option<Offset16>>()? {
225 default_language =
226 LanguageSystem::parse(Tag::from_bytes(b"dflt"), data:data.get(index:offset.to_usize()..)?);
227 }
228 let mut languages: RecordList<'_, LanguageSystem<'_>> = RecordList::parse(data:s.tail()?)?;
229 // Offsets are relative to this table.
230 languages.data = data;
231 Some(Self {
232 tag,
233 default_language,
234 languages,
235 })
236 }
237}
238
239/// A [Language System Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#language-system-table).
240#[derive(Clone, Copy, Debug)]
241pub struct LanguageSystem<'a> {
242 /// Language tag.
243 pub tag: Tag,
244 /// Index of a feature required for this language system.
245 pub required_feature: Option<FeatureIndex>,
246 /// Array of indices into the FeatureList, in arbitrary order.
247 pub feature_indices: LazyArray16<'a, FeatureIndex>,
248}
249
250impl<'a> RecordListItem<'a> for LanguageSystem<'a> {
251 fn parse(tag: Tag, data: &'a [u8]) -> Option<Self> {
252 let mut s: Stream<'_> = Stream::new(data);
253 let _lookup_order: Offset16 = s.read::<Offset16>()?; // Unsupported.
254 let required_feature: Option = match s.read::<FeatureIndex>()? {
255 0xFFFF => None,
256 v: u16 => Some(v),
257 };
258 let count: u16 = s.read::<u16>()?;
259 let feature_indices: LazyArray16<'_, u16> = s.read_array16(count)?;
260 Some(Self {
261 tag,
262 required_feature,
263 feature_indices,
264 })
265 }
266}
267
268/// A [Feature](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-table).
269#[allow(missing_docs)]
270#[derive(Clone, Copy, Debug)]
271pub struct Feature<'a> {
272 pub tag: Tag,
273 pub lookup_indices: LazyArray16<'a, LookupIndex>,
274}
275
276impl<'a> RecordListItem<'a> for Feature<'a> {
277 fn parse(tag: Tag, data: &'a [u8]) -> Option<Self> {
278 let mut s: Stream<'_> = Stream::new(data);
279 let _params_offset: Offset16 = s.read::<Offset16>()?; // Unsupported.
280 let count: u16 = s.read::<u16>()?;
281 let lookup_indices: LazyArray16<'_, u16> = s.read_array16(count)?;
282 Some(Self {
283 tag,
284 lookup_indices,
285 })
286 }
287}
288