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