read_fonts/tables/
layout.rs

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