1 | // Suppresses `minor_version` variable warning. |
2 | #![allow (unused_variables)] |
3 | |
4 | #[cfg (feature = "variable-fonts" )] |
5 | use super::FeatureVariations; |
6 | use super::LookupList; |
7 | #[cfg (feature = "variable-fonts" )] |
8 | use crate::parser::Offset32; |
9 | use crate::parser::{FromData, LazyArray16, Offset, Offset16, Stream}; |
10 | use crate::Tag; |
11 | |
12 | /// A [Layout Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#table-organization). |
13 | #[derive (Clone, Copy, Debug)] |
14 | pub 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 | |
27 | impl<'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`]. |
75 | pub type ScriptIndex = u16; |
76 | /// An index in [`LanguageSystemList`]. |
77 | pub type LanguageIndex = u16; |
78 | /// An index in [`FeatureList`]. |
79 | pub type FeatureIndex = u16; |
80 | /// An index in [`LookupList`]. |
81 | pub type LookupIndex = u16; |
82 | /// An index in [`FeatureVariations`]. |
83 | pub type VariationIndex = u32; |
84 | |
85 | /// A trait to parse item in [`RecordList`]. |
86 | /// |
87 | /// Internal use only. |
88 | pub 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)] |
95 | pub struct RecordList<'a, T: RecordListItem<'a>> { |
96 | data: &'a [u8], |
97 | records: LazyArray16<'a, TagRecord>, |
98 | data_type: core::marker::PhantomData<T>, |
99 | } |
100 | |
101 | impl<'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 | |
150 | impl<'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)] |
165 | pub struct RecordListIter<'a, T: RecordListItem<'a>> { |
166 | list: RecordList<'a, T>, |
167 | index: u16, |
168 | } |
169 | |
170 | impl<'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. |
184 | pub type ScriptList<'a> = RecordList<'a, Script<'a>>; |
185 | /// A list of [`LanguageSystem`] records. |
186 | pub type LanguageSystemList<'a> = RecordList<'a, LanguageSystem<'a>>; |
187 | /// A list of [`Feature`] records. |
188 | pub type FeatureList<'a> = RecordList<'a, Feature<'a>>; |
189 | |
190 | #[derive (Clone, Copy, Debug)] |
191 | struct TagRecord { |
192 | tag: Tag, |
193 | offset: Offset16, |
194 | } |
195 | |
196 | impl 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)] |
211 | pub 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 | |
220 | impl<'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)] |
241 | pub 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 | |
250 | impl<'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)] |
271 | pub struct Feature<'a> { |
272 | pub tag: Tag, |
273 | pub lookup_indices: LazyArray16<'a, LookupIndex>, |
274 | } |
275 | |
276 | impl<'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 | |