read_fonts/tables/
layout.rs

1//! OpenType Layout common table formats
2
3mod feature;
4mod lookup_flag;
5mod script;
6
7use core::cmp::Ordering;
8
9pub use lookup_flag::LookupFlag;
10pub use script::{ScriptTags, SelectedScript, UNICODE_TO_NEW_OPENTYPE_SCRIPT_TAGS};
11
12use super::variations::DeltaSetIndex;
13
14#[cfg(test)]
15#[path = "../tests/layout.rs"]
16mod spec_tests;
17
18include!("../../generated/generated_layout.rs");
19
20impl<'a, T: FontRead<'a>> Lookup<'a, T> {
21    pub fn get_subtable(&self, offset: Offset16) -> Result<T, ReadError> {
22        self.resolve_offset(offset)
23    }
24
25    #[cfg(feature = "experimental_traverse")]
26    fn traverse_lookup_flag(&self) -> traversal::FieldType<'a> {
27        self.lookup_flag().to_bits().into()
28    }
29}
30
31/// A trait that abstracts the behaviour of an extension subtable
32///
33/// This is necessary because GPOS and GSUB have different concrete types
34/// for their extension lookups.
35pub trait ExtensionLookup<'a, T: FontRead<'a>>: FontRead<'a> {
36    fn extension(&self) -> Result<T, ReadError>;
37}
38
39/// an array of subtables, maybe behind extension lookups
40///
41/// This is used to implement more ergonomic access to lookup subtables for
42/// GPOS & GSUB lookup tables.
43pub enum Subtables<'a, T: FontRead<'a>, Ext: ExtensionLookup<'a, T>> {
44    Subtable(ArrayOfOffsets<'a, T>),
45    Extension(ArrayOfOffsets<'a, Ext>),
46}
47
48impl<'a, T: FontRead<'a> + 'a, Ext: ExtensionLookup<'a, T> + 'a> Subtables<'a, T, Ext> {
49    /// create a new subtables array given offsets to non-extension subtables
50    pub(crate) fn new(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self {
51        Subtables::Subtable(ArrayOfOffsets::new(offsets, data, ()))
52    }
53
54    /// create a new subtables array given offsets to extension subtables
55    pub(crate) fn new_ext(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self {
56        Subtables::Extension(ArrayOfOffsets::new(offsets, data, ()))
57    }
58
59    /// The number of subtables in this collection
60    pub fn len(&self) -> usize {
61        match self {
62            Subtables::Subtable(inner) => inner.len(),
63            Subtables::Extension(inner) => inner.len(),
64        }
65    }
66
67    pub fn is_empty(&self) -> bool {
68        self.len() == 0
69    }
70
71    /// Return the subtable at the given index
72    pub fn get(&self, idx: usize) -> Result<T, ReadError> {
73        match self {
74            Subtables::Subtable(inner) => inner.get(idx),
75            Subtables::Extension(inner) => inner.get(idx).and_then(|ext| ext.extension()),
76        }
77    }
78
79    /// Return an iterator over all the subtables in the collection
80    pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
81        let (left, right) = match self {
82            Subtables::Subtable(inner) => (Some(inner.iter()), None),
83            Subtables::Extension(inner) => (
84                None,
85                Some(inner.iter().map(|ext| ext.and_then(|ext| ext.extension()))),
86            ),
87        };
88        left.into_iter()
89            .flatten()
90            .chain(right.into_iter().flatten())
91    }
92}
93
94/// An enum for different possible tables referenced by [Feature::feature_params_offset]
95pub enum FeatureParams<'a> {
96    StylisticSet(StylisticSetParams<'a>),
97    Size(SizeParams<'a>),
98    CharacterVariant(CharacterVariantParams<'a>),
99}
100
101impl ReadArgs for FeatureParams<'_> {
102    type Args = Tag;
103}
104
105impl<'a> FontReadWithArgs<'a> for FeatureParams<'a> {
106    fn read_with_args(bytes: FontData<'a>, args: &Tag) -> Result<FeatureParams<'a>, ReadError> {
107        match *args {
108            t if t == Tag::new(b"size") => SizeParams::read(bytes).map(Self::Size),
109            // to whoever is debugging this dumb bug I wrote: I'm sorry.
110            t if &t.to_raw()[..2] == b"ss" => {
111                StylisticSetParams::read(bytes).map(Self::StylisticSet)
112            }
113            t if &t.to_raw()[..2] == b"cv" => {
114                CharacterVariantParams::read(bytes).map(Self::CharacterVariant)
115            }
116            // NOTE: what even is our error condition here? an offset exists but
117            // we don't know the tag?
118            _ => Err(ReadError::InvalidFormat(0xdead)),
119        }
120    }
121}
122
123#[cfg(feature = "experimental_traverse")]
124impl<'a> SomeTable<'a> for FeatureParams<'a> {
125    fn type_name(&self) -> &str {
126        match self {
127            FeatureParams::StylisticSet(table) => table.type_name(),
128            FeatureParams::Size(table) => table.type_name(),
129            FeatureParams::CharacterVariant(table) => table.type_name(),
130        }
131    }
132
133    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
134        match self {
135            FeatureParams::StylisticSet(table) => table.get_field(idx),
136            FeatureParams::Size(table) => table.get_field(idx),
137            FeatureParams::CharacterVariant(table) => table.get_field(idx),
138        }
139    }
140}
141
142impl FeatureTableSubstitutionRecord {
143    pub fn alternate_feature<'a>(&self, data: FontData<'a>) -> Result<Feature<'a>, ReadError> {
144        self.alternate_feature_offset()
145            .resolve_with_args(data, &Tag::new(b"NULL"))
146    }
147}
148
149impl<'a> CoverageTable<'a> {
150    pub fn iter(&self) -> impl Iterator<Item = GlyphId16> + 'a {
151        // all one expression so that we have a single return type
152        let (iter1, iter2) = match self {
153            CoverageTable::Format1(t) => (Some(t.glyph_array().iter().map(|g| g.get())), None),
154            CoverageTable::Format2(t) => {
155                let iter = t.range_records().iter().flat_map(RangeRecord::iter);
156                (None, Some(iter))
157            }
158        };
159
160        iter1
161            .into_iter()
162            .flatten()
163            .chain(iter2.into_iter().flatten())
164    }
165
166    /// If this glyph is in the coverage table, returns its index
167    pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u16> {
168        match self {
169            CoverageTable::Format1(sub) => sub.get(gid),
170            CoverageTable::Format2(sub) => sub.get(gid),
171        }
172    }
173}
174
175impl CoverageFormat1<'_> {
176    /// If this glyph is in the coverage table, returns its index
177    pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u16> {
178        let gid16: GlyphId16 = gid.into().try_into().ok()?;
179        let be_glyph: BigEndian<GlyphId16> = gid16.into();
180        self.glyph_array()
181            .binary_search(&be_glyph)
182            .ok()
183            .map(|idx| idx as _)
184    }
185}
186
187impl CoverageFormat2<'_> {
188    /// If this glyph is in the coverage table, returns its index
189    pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u16> {
190        let gid: GlyphId16 = gid.into().try_into().ok()?;
191        self.range_records()
192            .binary_search_by(|rec| {
193                if rec.end_glyph_id() < gid {
194                    Ordering::Less
195                } else if rec.start_glyph_id() > gid {
196                    Ordering::Greater
197                } else {
198                    Ordering::Equal
199                }
200            })
201            .ok()
202            .map(|idx| {
203                let rec = &self.range_records()[idx];
204                rec.start_coverage_index() + gid.to_u16() - rec.start_glyph_id().to_u16()
205            })
206    }
207}
208
209impl RangeRecord {
210    fn iter(&self) -> impl Iterator<Item = GlyphId16> + '_ {
211        (self.start_glyph_id().to_u16()..=self.end_glyph_id().to_u16()).map(GlyphId16::new)
212    }
213}
214
215impl DeltaFormat {
216    pub(crate) fn value_count(self, start_size: u16, end_size: u16) -> usize {
217        let range_len = end_size.saturating_add(1).saturating_sub(start_size) as usize;
218        let val_per_word = match self {
219            DeltaFormat::Local2BitDeltas => 8,
220            DeltaFormat::Local4BitDeltas => 4,
221            DeltaFormat::Local8BitDeltas => 2,
222            _ => return 0,
223        };
224
225        let count = range_len / val_per_word;
226        let extra = (range_len % val_per_word).min(1);
227        count + extra
228    }
229}
230
231// we as a 'format' in codegen, and the generic error type for an invalid format
232// stores the value as an i64, so we need this conversion.
233impl From<DeltaFormat> for i64 {
234    fn from(value: DeltaFormat) -> Self {
235        value as u16 as _
236    }
237}
238
239impl<'a> ClassDefFormat1<'a> {
240    /// Get the class for this glyph id
241    pub fn get(&self, gid: GlyphId16) -> u16 {
242        if gid < self.start_glyph_id() {
243            return 0;
244        }
245        let idx = gid.to_u16() - self.start_glyph_id().to_u16();
246        self.class_value_array()
247            .get(idx as usize)
248            .map(|x| x.get())
249            .unwrap_or(0)
250    }
251
252    /// Iterate over each glyph and its class.
253    pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + 'a {
254        let start = self.start_glyph_id();
255        self.class_value_array()
256            .iter()
257            .enumerate()
258            .map(move |(i, val)| {
259                let gid = start.to_u16().saturating_add(i as u16);
260                (GlyphId16::new(gid), val.get())
261            })
262    }
263}
264
265impl<'a> ClassDefFormat2<'a> {
266    /// Get the class for this glyph id
267    pub fn get(&self, gid: GlyphId16) -> u16 {
268        let records = self.class_range_records();
269        let ix = match records.binary_search_by(|rec| rec.start_glyph_id().cmp(&gid)) {
270            Ok(ix) => ix,
271            Err(ix) => ix.saturating_sub(1),
272        };
273        if let Some(record) = records.get(ix) {
274            if (record.start_glyph_id()..=record.end_glyph_id()).contains(&gid) {
275                return record.class();
276            }
277        }
278        0
279    }
280
281    /// Iterate over each glyph and its class.
282    pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + 'a {
283        self.class_range_records().iter().flat_map(|range| {
284            let start = range.start_glyph_id().to_u16();
285            let end = range.end_glyph_id().to_u16();
286            (start..=end).map(|gid| (GlyphId16::new(gid), range.class()))
287        })
288    }
289}
290
291impl ClassDef<'_> {
292    /// Get the class for this glyph id
293    pub fn get(&self, gid: GlyphId16) -> u16 {
294        match self {
295            ClassDef::Format1(table) => table.get(gid),
296            ClassDef::Format2(table) => table.get(gid),
297        }
298    }
299
300    /// Iterate over each glyph and its class.
301    ///
302    /// This will not include class 0 unless it has been explicitly assigned.
303    pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + '_ {
304        let (one, two) = match self {
305            ClassDef::Format1(inner) => (Some(inner.iter()), None),
306            ClassDef::Format2(inner) => (None, Some(inner.iter())),
307        };
308        one.into_iter().flatten().chain(two.into_iter().flatten())
309    }
310}
311
312impl<'a> Device<'a> {
313    /// Iterate over the decoded values for this device
314    pub fn iter(&self) -> impl Iterator<Item = i8> + 'a {
315        let format = self.delta_format();
316        let mut n = (self.end_size() - self.start_size()) as usize + 1;
317        let deltas_per_word = match format {
318            DeltaFormat::Local2BitDeltas => 8,
319            DeltaFormat::Local4BitDeltas => 4,
320            DeltaFormat::Local8BitDeltas => 2,
321            _ => 0,
322        };
323
324        self.delta_value().iter().flat_map(move |val| {
325            let iter = iter_packed_values(val.get(), format, n);
326            n = n.saturating_sub(deltas_per_word);
327            iter
328        })
329    }
330}
331
332fn iter_packed_values(raw: u16, format: DeltaFormat, n: usize) -> impl Iterator<Item = i8> {
333    let mut decoded = [None; 8];
334    let (mask, sign_mask, bits) = match format {
335        DeltaFormat::Local2BitDeltas => (0b11, 0b10, 2usize),
336        DeltaFormat::Local4BitDeltas => (0b1111, 0b1000, 4),
337        DeltaFormat::Local8BitDeltas => (0b1111_1111, 0b1000_0000, 8),
338        _ => (0, 0, 0),
339    };
340
341    let max_per_word = 16 / bits;
342    #[allow(clippy::needless_range_loop)] // enumerate() feels weird here
343    for i in 0..n.min(max_per_word) {
344        let mask = mask << ((16 - bits) - i * bits);
345        let val = (raw & mask) >> ((16 - bits) - i * bits);
346        let sign = val & sign_mask != 0;
347
348        let val = if sign {
349            // it is 2023 and I am googling to remember how twos compliment works
350            -((((!val) & mask) + 1) as i8)
351        } else {
352            val as i8
353        };
354        decoded[i] = Some(val)
355    }
356    decoded.into_iter().flatten()
357}
358
359impl From<VariationIndex<'_>> for DeltaSetIndex {
360    fn from(src: VariationIndex) -> DeltaSetIndex {
361        DeltaSetIndex {
362            outer: src.delta_set_outer_index(),
363            inner: src.delta_set_inner_index(),
364        }
365    }
366}
367
368/// Combination of a tag and a child table.
369///
370/// Used in script and feature lists where a data structure has an array
371/// of records with each containing a tag and an offset to a table. This
372/// allows us to provide convenience methods that return both values.
373#[derive(Clone)]
374pub struct TaggedElement<T> {
375    pub tag: Tag,
376    pub element: T,
377}
378
379impl<T> TaggedElement<T> {
380    pub fn new(tag: Tag, element: T) -> Self {
381        Self { tag, element }
382    }
383}
384
385impl<T> std::ops::Deref for TaggedElement<T> {
386    type Target = T;
387
388    fn deref(&self) -> &Self::Target {
389        &self.element
390    }
391}
392
393#[cfg(test)]
394mod tests {
395    use super::*;
396
397    #[test]
398    fn coverage_get_format1() {
399        // manually generated, corresponding to the glyphs (1, 7, 13, 27, 44);
400        const COV1_DATA: FontData = FontData::new(&[0, 1, 0, 5, 0, 1, 0, 7, 0, 13, 0, 27, 0, 44]);
401
402        let coverage = CoverageFormat1::read(COV1_DATA).unwrap();
403        assert_eq!(coverage.get(GlyphId::new(1)), Some(0));
404        assert_eq!(coverage.get(GlyphId::new(2)), None);
405        assert_eq!(coverage.get(GlyphId::new(7)), Some(1));
406        assert_eq!(coverage.get(GlyphId::new(27)), Some(3));
407        assert_eq!(coverage.get(GlyphId::new(45)), None);
408    }
409
410    #[test]
411    fn coverage_get_format2() {
412        // manually generated, corresponding to glyphs (5..10) and (30..40).
413        const COV2_DATA: FontData =
414            FontData::new(&[0, 2, 0, 2, 0, 5, 0, 9, 0, 0, 0, 30, 0, 39, 0, 5]);
415        let coverage = CoverageFormat2::read(COV2_DATA).unwrap();
416        assert_eq!(coverage.get(GlyphId::new(2)), None);
417        assert_eq!(coverage.get(GlyphId::new(7)), Some(2));
418        assert_eq!(coverage.get(GlyphId::new(9)), Some(4));
419        assert_eq!(coverage.get(GlyphId::new(10)), None);
420        assert_eq!(coverage.get(GlyphId::new(32)), Some(7));
421        assert_eq!(coverage.get(GlyphId::new(39)), Some(14));
422        assert_eq!(coverage.get(GlyphId::new(40)), None);
423    }
424
425    #[test]
426    fn classdef_get_format2() {
427        let classdef = ClassDef::read(FontData::new(
428            font_test_data::gdef::MARKATTACHCLASSDEF_TABLE,
429        ))
430        .unwrap();
431        assert!(matches!(classdef, ClassDef::Format2(..)));
432        let gid_class_pairs = [
433            (616, 1),
434            (617, 1),
435            (618, 1),
436            (624, 1),
437            (625, 1),
438            (626, 1),
439            (652, 2),
440            (653, 2),
441            (654, 2),
442            (655, 2),
443            (661, 2),
444        ];
445        for (gid, class) in gid_class_pairs {
446            assert_eq!(classdef.get(GlyphId16::new(gid)), class);
447        }
448        for (gid, class) in classdef.iter() {
449            assert_eq!(classdef.get(gid), class);
450        }
451    }
452
453    #[test]
454    fn delta_decode() {
455        // these examples come from the spec
456        assert_eq!(
457            iter_packed_values(0x123f, DeltaFormat::Local4BitDeltas, 4).collect::<Vec<_>>(),
458            &[1, 2, 3, -1]
459        );
460
461        assert_eq!(
462            iter_packed_values(0x5540, DeltaFormat::Local2BitDeltas, 5).collect::<Vec<_>>(),
463            &[1, 1, 1, 1, 1]
464        );
465    }
466
467    #[test]
468    fn delta_decode_all() {
469        // manually generated with write-fonts
470        let bytes: &[u8] = &[0, 7, 0, 13, 0, 3, 1, 244, 30, 245, 101, 8, 42, 0];
471        let device = Device::read(bytes.into()).unwrap();
472        assert_eq!(
473            device.iter().collect::<Vec<_>>(),
474            &[1i8, -12, 30, -11, 101, 8, 42]
475        );
476    }
477}