ttf_parser/ggg/
layout_table.rs

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