Coverage Report

Created: 2026-01-09 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}