ttf_parser/ggg/
layout_table.rs

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::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::new(data);
223        let mut default_language = None;
224        if let Some(offset) = s.read::<Option<Offset16>>()? {
225            default_language =
226                LanguageSystem::parse(Tag::from_bytes(b"dflt"), data.get(offset.to_usize()..)?);
227        }
228        let mut languages = RecordList::parse(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::new(data);
253        let _lookup_order = s.read::<Offset16>()?; // Unsupported.
254        let required_feature = match s.read::<FeatureIndex>()? {
255            0xFFFF => None,
256            v => Some(v),
257        };
258        let count = s.read::<u16>()?;
259        let feature_indices = 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::new(data);
279        let _params_offset = s.read::<Offset16>()?; // Unsupported.
280        let count = s.read::<u16>()?;
281        let lookup_indices = s.read_array16(count)?;
282        Some(Self {
283            tag,
284            lookup_indices,
285        })
286    }
287}