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