/src/ttf-parser/src/ggg/layout_table.rs
Line | Count | Source |
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 | 1.85k | pub(crate) fn parse(data: &'a [u8]) -> Option<Self> { |
29 | 1.85k | let mut s = Stream::new(data); |
30 | | |
31 | 1.85k | let major_version = s.read::<u16>()?; |
32 | 1.82k | let minor_version = s.read::<u16>()?; |
33 | 1.81k | if major_version != 1 { |
34 | 711 | return None; |
35 | 1.10k | } |
36 | | |
37 | 1.10k | let scripts = ScriptList::parse(s.read_at_offset16(data)?)?; |
38 | 921 | let features = FeatureList::parse(s.read_at_offset16(data)?)?; |
39 | 763 | let lookups = LookupList::parse(s.read_at_offset16(data)?)?; |
40 | | |
41 | | #[cfg(feature = "variable-fonts")] |
42 | | { |
43 | 650 | let mut variations_offset = None; |
44 | 650 | if minor_version >= 1 { |
45 | 479 | variations_offset = s.read::<Option<Offset32>>()?; |
46 | 171 | } |
47 | | |
48 | 638 | let variations = match variations_offset { |
49 | 451 | Some(offset) => data |
50 | 451 | .get(offset.to_usize()..) |
51 | 451 | .and_then(FeatureVariations::parse), |
52 | 187 | None => None, |
53 | | }; |
54 | | |
55 | 638 | Some(Self { |
56 | 638 | scripts, |
57 | 638 | features, |
58 | 638 | lookups, |
59 | 638 | variations, |
60 | 638 | }) |
61 | | } |
62 | | |
63 | | #[cfg(not(feature = "variable-fonts"))] |
64 | | { |
65 | | Some(Self { |
66 | | scripts, |
67 | | features, |
68 | | lookups, |
69 | | }) |
70 | | } |
71 | 1.85k | } |
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 | 1.93k | fn parse(data: &'a [u8]) -> Option<Self> { |
103 | 1.93k | let mut s = Stream::new(data); |
104 | 1.93k | let count = s.read::<u16>()?; |
105 | 1.91k | let records = s.read_array16(count)?; |
106 | 1.68k | Some(Self { |
107 | 1.68k | data, |
108 | 1.68k | records, |
109 | 1.68k | data_type: core::marker::PhantomData, |
110 | 1.68k | }) |
111 | 1.93k | } Unexecuted instantiation: <ttf_parser::ggg::layout_table::RecordList<ttf_parser::ggg::layout_table::LanguageSystem>>::parse <ttf_parser::ggg::layout_table::RecordList<ttf_parser::ggg::layout_table::Script>>::parse Line | Count | Source | 102 | 1.05k | fn parse(data: &'a [u8]) -> Option<Self> { | 103 | 1.05k | let mut s = Stream::new(data); | 104 | 1.05k | let count = s.read::<u16>()?; | 105 | 1.04k | let records = s.read_array16(count)?; | 106 | 921 | Some(Self { | 107 | 921 | data, | 108 | 921 | records, | 109 | 921 | data_type: core::marker::PhantomData, | 110 | 921 | }) | 111 | 1.05k | } |
<ttf_parser::ggg::layout_table::RecordList<ttf_parser::ggg::layout_table::Feature>>::parse Line | Count | Source | 102 | 876 | fn parse(data: &'a [u8]) -> Option<Self> { | 103 | 876 | let mut s = Stream::new(data); | 104 | 876 | let count = s.read::<u16>()?; | 105 | 868 | let records = s.read_array16(count)?; | 106 | 763 | Some(Self { | 107 | 763 | data, | 108 | 763 | records, | 109 | 763 | data_type: core::marker::PhantomData, | 110 | 763 | }) | 111 | 876 | } |
|
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::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 | 0 | fn parse(tag: Tag, data: &'a [u8]) -> Option<Self> { |
222 | 0 | let mut s = Stream::new(data); |
223 | 0 | let mut default_language = None; |
224 | 0 | if let Some(offset) = s.read::<Option<Offset16>>()? { |
225 | | default_language = |
226 | 0 | LanguageSystem::parse(Tag::from_bytes(b"dflt"), data.get(offset.to_usize()..)?); |
227 | 0 | } |
228 | 0 | let mut languages = RecordList::parse(s.tail()?)?; |
229 | | // Offsets are relative to this table. |
230 | 0 | languages.data = data; |
231 | 0 | Some(Self { |
232 | 0 | tag, |
233 | 0 | default_language, |
234 | 0 | languages, |
235 | 0 | }) |
236 | 0 | } |
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 | 0 | fn parse(tag: Tag, data: &'a [u8]) -> Option<Self> { |
252 | 0 | let mut s = Stream::new(data); |
253 | 0 | let _lookup_order = s.read::<Offset16>()?; // Unsupported. |
254 | 0 | let required_feature = match s.read::<FeatureIndex>()? { |
255 | 0 | 0xFFFF => None, |
256 | 0 | v => Some(v), |
257 | | }; |
258 | 0 | let count = s.read::<u16>()?; |
259 | 0 | let feature_indices = s.read_array16(count)?; |
260 | 0 | Some(Self { |
261 | 0 | tag, |
262 | 0 | required_feature, |
263 | 0 | feature_indices, |
264 | 0 | }) |
265 | 0 | } |
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 | 0 | fn parse(tag: Tag, data: &'a [u8]) -> Option<Self> { |
278 | 0 | let mut s = Stream::new(data); |
279 | 0 | let _params_offset = s.read::<Offset16>()?; // Unsupported. |
280 | 0 | let count = s.read::<u16>()?; |
281 | 0 | let lookup_indices = s.read_array16(count)?; |
282 | 0 | Some(Self { |
283 | 0 | tag, |
284 | 0 | lookup_indices, |
285 | 0 | }) |
286 | 0 | } |
287 | | } |