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