Skip to main content

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        if glyph_count > (glyphs.len() as u32) * self.cost() {
245            glyphs.iter().any(|g| self.get(g).is_some())
246        } else {
247            self.glyph_array()
248                .iter()
249                .any(|g| glyphs.contains(GlyphId::from(g.get())))
250        }
251    }
252
253    /// Returns the intersection of this table and input 'glyphs' set.
254    #[cfg(feature = "std")]
255    fn intersect_set(&self, glyphs: &IntSet<GlyphId>) -> IntSet<GlyphId> {
256        let glyph_count = self.glyph_count() as u32;
257        if glyph_count > (glyphs.len() as u32) * self.cost() {
258            glyphs
259                .iter()
260                .filter_map(|g| self.get(g).map(|_| g))
261                .collect()
262        } else {
263            self.glyph_array()
264                .iter()
265                .filter(|g| glyphs.contains(GlyphId::from(g.get())))
266                .map(|g| GlyphId::from(g.get()))
267                .collect()
268        }
269    }
270
271    /// Return the number of glyphs in this table
272    pub fn population(&self) -> usize {
273        self.glyph_count() as usize
274    }
275
276    /// Return the cost of looking up a glyph in this table
277    pub fn cost(&self) -> u32 {
278        bit_storage(self.glyph_count() as u32)
279    }
280}
281
282impl CoverageFormat2<'_> {
283    /// If this glyph is in the coverage table, returns its index
284    #[inline]
285    pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u16> {
286        let gid: GlyphId16 = gid.into().try_into().ok()?;
287        self.range_records()
288            .binary_search_by(|rec| {
289                if rec.end_glyph_id() < gid {
290                    Ordering::Less
291                } else if rec.start_glyph_id() > gid {
292                    Ordering::Greater
293                } else {
294                    Ordering::Equal
295                }
296            })
297            .ok()
298            .map(|idx| {
299                let rec = &self.range_records()[idx];
300                rec.start_coverage_index() + gid.to_u16() - rec.start_glyph_id().to_u16()
301            })
302    }
303
304    /// Returns if this table contains at least one glyph in the 'glyphs' set.
305    #[cfg(feature = "std")]
306    fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
307        let range_count = self.range_count() as u32;
308        if range_count > (glyphs.len() as u32) * self.cost() {
309            glyphs.iter().any(|g| self.get(g).is_some())
310        } else {
311            self.range_records()
312                .iter()
313                .any(|record| record.intersects(glyphs))
314        }
315    }
316
317    /// Returns the intersection of this table and input 'glyphs' set.
318    #[cfg(feature = "std")]
319    fn intersect_set(&self, glyphs: &IntSet<GlyphId>) -> IntSet<GlyphId> {
320        let range_count = self.range_count() as u32;
321        if range_count > (glyphs.len() as u32) * self.cost() {
322            glyphs
323                .iter()
324                .filter_map(|g| self.get(g).map(|_| g))
325                .collect()
326        } else {
327            let mut out = IntSet::empty();
328            let mut last = GlyphId16::from(0);
329            for record in self.range_records() {
330                // break out of loop for overlapping/broken tables
331                let start_glyph = record.start_glyph_id();
332                if start_glyph < last {
333                    break;
334                }
335                let end = record.end_glyph_id();
336                last = end;
337
338                let start = GlyphId::from(start_glyph);
339                if glyphs.contains(start) {
340                    out.insert(start);
341                }
342
343                for g in glyphs.iter_after(start) {
344                    if g.to_u32() > end.to_u32() {
345                        break;
346                    }
347                    out.insert(g);
348                }
349            }
350            out
351        }
352    }
353
354    /// Return the number of glyphs in this table
355    pub fn population(&self) -> usize {
356        self.range_records()
357            .iter()
358            .fold(0, |acc, record| acc + record.population())
359    }
360
361    /// Return the cost of looking up a glyph in this table
362    pub fn cost(&self) -> u32 {
363        bit_storage(self.range_count() as u32)
364    }
365}
366
367impl RangeRecord {
368    pub fn iter(&self) -> impl Iterator<Item = GlyphId16> + '_ {
369        (self.start_glyph_id().to_u16()..=self.end_glyph_id().to_u16()).map(GlyphId16::new)
370    }
371
372    /// Returns if this table contains at least one glyph in the 'glyphs' set.
373    #[cfg(feature = "std")]
374    pub fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
375        glyphs.intersects_range(
376            GlyphId::from(self.start_glyph_id())..=GlyphId::from(self.end_glyph_id()),
377        )
378    }
379
380    /// Return the number of glyphs in this record
381    pub fn population(&self) -> usize {
382        let start = self.start_glyph_id().to_u32() as usize;
383        let end = self.end_glyph_id().to_u32() as usize;
384        if start > end {
385            0
386        } else {
387            end - start + 1
388        }
389    }
390}
391
392impl DeltaFormat {
393    pub(crate) fn value_count(self, start_size: u16, end_size: u16) -> usize {
394        let range_len = end_size.saturating_add(1).saturating_sub(start_size) as usize;
395        let val_per_word = match self {
396            DeltaFormat::Local2BitDeltas => 8,
397            DeltaFormat::Local4BitDeltas => 4,
398            DeltaFormat::Local8BitDeltas => 2,
399            _ => return 0,
400        };
401
402        let count = range_len / val_per_word;
403        let extra = (range_len % val_per_word).min(1);
404        count + extra
405    }
406}
407
408// we as a 'format' in codegen, and the generic error type for an invalid format
409// stores the value as an i64, so we need this conversion.
410impl From<DeltaFormat> for i64 {
411    fn from(value: DeltaFormat) -> Self {
412        value as u16 as _
413    }
414}
415
416impl<'a> ClassDefFormat1<'a> {
417    /// Get the class for this glyph id
418    #[inline]
419    pub fn get(&self, gid: impl Into<GlyphId>) -> u16 {
420        let Some(idx) = gid
421            .into()
422            .to_u32()
423            .checked_sub(self.start_glyph_id().to_u32())
424        else {
425            return 0;
426        };
427        self.class_value_array()
428            .get(idx as usize)
429            .map(|x| x.get())
430            .unwrap_or(0)
431    }
432
433    /// Iterate over each glyph and its class.
434    pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + 'a {
435        let start = self.start_glyph_id();
436        self.class_value_array()
437            .iter()
438            .enumerate()
439            .map(move |(i, val)| {
440                let gid = start.to_u16().saturating_add(i as u16);
441                (GlyphId16::new(gid), val.get())
442            })
443    }
444
445    /// Return the number of glyphs explicitly assigned to a class in this table
446    pub fn population(&self) -> usize {
447        self.glyph_count() as usize
448    }
449
450    /// Return the cost of looking up a glyph in this table
451    pub fn cost(&self) -> u32 {
452        1
453    }
454
455    /// Returns class values for the intersected glyphs of this table and input 'glyphs' set.
456    #[cfg(feature = "std")]
457    fn intersect_classes(&self, glyphs: &IntSet<GlyphId>) -> IntSet<u16> {
458        let mut out = IntSet::empty();
459        if glyphs.is_empty() {
460            return out;
461        }
462
463        let start_glyph = self.start_glyph_id().to_u32();
464        let glyph_count = self.glyph_count();
465        let end_glyph = start_glyph + glyph_count as u32 - 1;
466        if glyphs.first().unwrap().to_u32() < start_glyph
467            || glyphs.last().unwrap().to_u32() > end_glyph
468        {
469            out.insert(0);
470        }
471
472        let class_values = self.class_value_array();
473        if glyphs.contains(GlyphId::from(start_glyph)) {
474            let Some(start_glyph_class) = class_values.first() else {
475                return out;
476            };
477            out.insert(start_glyph_class.get());
478        }
479
480        for g in glyphs.iter_after(GlyphId::from(start_glyph)) {
481            let g = g.to_u32();
482            if g > end_glyph {
483                break;
484            }
485
486            let idx = g - start_glyph;
487            let Some(class) = class_values.get(idx as usize) else {
488                break;
489            };
490            out.insert(class.get());
491        }
492        out
493    }
494
495    /// Returns intersected glyphs of this table and input 'glyphs' set that are assigned to input class value.
496    #[cfg(feature = "std")]
497    fn intersected_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> IntSet<GlyphId> {
498        let mut out = IntSet::empty();
499        if glyphs.is_empty() {
500            return out;
501        }
502
503        let start_glyph = self.start_glyph_id().to_u32();
504        let glyph_count = self.glyph_count();
505        let end_glyph = start_glyph + glyph_count as u32 - 1;
506        if class == 0 {
507            let first = glyphs.first().unwrap();
508            if first.to_u32() < start_glyph {
509                out.extend(glyphs.range(first..GlyphId::from(start_glyph)));
510            }
511
512            let last = glyphs.last().unwrap();
513            if last.to_u32() > end_glyph {
514                out.extend(glyphs.range(GlyphId::from(end_glyph + 1)..=last));
515            }
516            return out;
517        }
518
519        let class_values = self.class_value_array();
520        for g in glyphs.range(GlyphId::from(start_glyph)..=GlyphId::from(end_glyph)) {
521            let idx = g.to_u32() - start_glyph;
522            let Some(c) = class_values.get(idx as usize) else {
523                break;
524            };
525            if c.get() == class {
526                out.insert(g);
527            }
528        }
529        out
530    }
531}
532
533impl<'a> ClassDefFormat2<'a> {
534    /// Get the class for this glyph id
535    #[inline]
536    pub fn get(&self, gid: impl Into<GlyphId>) -> u16 {
537        let gid = gid.into().to_u32();
538        let records = self.class_range_records();
539        let ix = match records.binary_search_by(|rec| rec.start_glyph_id().to_u32().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().to_u32()..=record.end_glyph_id().to_u32()).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        if num_ranges as u64 > glyphs.len() * self.cost() as u64 {
610            for g in glyphs.iter() {
611                let class = self.get(g);
612                if class != 0 {
613                    out.insert(class);
614                }
615            }
616        } else {
617            for record in range_records {
618                if glyphs.intersects_range(
619                    GlyphId::from(record.start_glyph_id())..=GlyphId::from(record.end_glyph_id()),
620                ) {
621                    out.insert(record.class());
622                }
623            }
624        }
625        out
626    }
627
628    /// Returns intersected glyphs of this table and input 'glyphs' set that are assgiend to input class value.
629    #[cfg(feature = "std")]
630    fn intersected_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> IntSet<GlyphId> {
631        let mut out = IntSet::empty();
632        if glyphs.is_empty() {
633            return out;
634        }
635
636        let first = glyphs.first().unwrap().to_u32();
637        let last = glyphs.last().unwrap().to_u32();
638        if class == 0 {
639            let mut start = first;
640            for range in self.class_range_records() {
641                let range_start = range.start_glyph_id().to_u32();
642                if start < range_start {
643                    out.extend(glyphs.range(GlyphId::from(start)..GlyphId::from(range_start)));
644                }
645
646                let range_end = range.end_glyph_id().to_u32();
647                if range_end >= last {
648                    break;
649                }
650                start = range_end + 1;
651            }
652
653            if start <= last {
654                out.extend(glyphs.range(GlyphId::from(start)..=GlyphId::from(last)));
655            }
656            return out;
657        }
658
659        let num_ranges = self.class_range_count();
660        if num_ranges as u64 > glyphs.len() * self.cost() as u64 {
661            for g in glyphs.iter() {
662                let c = self.get(g);
663                if c == class {
664                    out.insert(g);
665                }
666            }
667        } else {
668            for range in self.class_range_records() {
669                let range_start = range.start_glyph_id().to_u32();
670                let range_end = range.end_glyph_id().to_u32();
671                if range_start > last {
672                    break;
673                }
674                if range.class() != class || range.end_glyph_id().to_u32() < first {
675                    continue;
676                }
677                out.extend(glyphs.range(GlyphId::from(range_start)..=GlyphId::from(range_end)));
678            }
679        }
680        out
681    }
682}
683
684impl ClassRangeRecord {
685    /// Return the number of glyphs explicitly assigned to a class in this table
686    pub fn population(&self) -> usize {
687        let start = self.start_glyph_id().to_u32() as usize;
688        let end = self.end_glyph_id().to_u32() as usize;
689        if start > end {
690            0
691        } else {
692            end - start + 1
693        }
694    }
695}
696
697impl ClassDef<'_> {
698    /// Get the class for this glyph id
699    #[inline]
700    pub fn get(&self, gid: impl Into<GlyphId>) -> u16 {
701        match self {
702            ClassDef::Format1(table) => table.get(gid),
703            ClassDef::Format2(table) => table.get(gid),
704        }
705    }
706
707    /// Iterate over each glyph and its class.
708    ///
709    /// This will not include class 0 unless it has been explicitly assigned.
710    pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + '_ {
711        let (one, two) = match self {
712            ClassDef::Format1(inner) => (Some(inner.iter()), None),
713            ClassDef::Format2(inner) => (None, Some(inner.iter())),
714        };
715        one.into_iter().flatten().chain(two.into_iter().flatten())
716    }
717
718    /// Return the number of glyphs explicitly assigned to a class in this table
719    pub fn population(&self) -> usize {
720        match self {
721            ClassDef::Format1(table) => table.population(),
722            ClassDef::Format2(table) => table.population(),
723        }
724    }
725
726    /// Return the cost of looking up a glyph in this table
727    pub fn cost(&self) -> u32 {
728        match self {
729            ClassDef::Format1(sub) => sub.cost(),
730            ClassDef::Format2(sub) => sub.cost(),
731        }
732    }
733
734    /// Returns class values for the intersected glyphs of this table and input 'glyphs' set.
735    #[cfg(feature = "std")]
736    pub fn intersect_classes(&self, glyphs: &IntSet<GlyphId>) -> IntSet<u16> {
737        match self {
738            ClassDef::Format1(table) => table.intersect_classes(glyphs),
739            ClassDef::Format2(table) => table.intersect_classes(glyphs),
740        }
741    }
742
743    /// Returns intersected glyphs of this table and input 'glyphs' set that are assgiend to input class value.
744    #[cfg(feature = "std")]
745    pub fn intersected_class_glyphs(
746        &self,
747        glyphs: &IntSet<GlyphId>,
748        class: u16,
749    ) -> IntSet<GlyphId> {
750        match self {
751            ClassDef::Format1(table) => table.intersected_class_glyphs(glyphs, class),
752            ClassDef::Format2(table) => table.intersected_class_glyphs(glyphs, class),
753        }
754    }
755}
756
757impl<'a> Device<'a> {
758    /// Iterate over the decoded values for this device
759    pub fn iter(&self) -> impl Iterator<Item = i8> + 'a {
760        let format = self.delta_format();
761        let mut n = (self.end_size() - self.start_size()) as usize + 1;
762        let deltas_per_word = match format {
763            DeltaFormat::Local2BitDeltas => 8,
764            DeltaFormat::Local4BitDeltas => 4,
765            DeltaFormat::Local8BitDeltas => 2,
766            _ => 0,
767        };
768
769        self.delta_value().iter().flat_map(move |val| {
770            let iter = iter_packed_values(val.get(), format, n);
771            n = n.saturating_sub(deltas_per_word);
772            iter
773        })
774    }
775}
776
777fn iter_packed_values(raw: u16, format: DeltaFormat, n: usize) -> impl Iterator<Item = i8> {
778    let mut decoded = [None; 8];
779    let (mask, sign_mask, bits) = match format {
780        DeltaFormat::Local2BitDeltas => (0b11, 0b10, 2usize),
781        DeltaFormat::Local4BitDeltas => (0b1111, 0b1000, 4),
782        DeltaFormat::Local8BitDeltas => (0b1111_1111, 0b1000_0000, 8),
783        _ => (0, 0, 0),
784    };
785
786    let max_per_word = 16 / bits;
787    #[allow(clippy::needless_range_loop)] // enumerate() feels weird here
788    for i in 0..n.min(max_per_word) {
789        let mask = mask << ((16 - bits) - i * bits);
790        let val = (raw & mask) >> ((16 - bits) - i * bits);
791        let sign = val & sign_mask != 0;
792
793        let val = if sign {
794            // it is 2023 and I am googling to remember how twos compliment works
795            -((((!val) & mask) + 1) as i8)
796        } else {
797            val as i8
798        };
799        decoded[i] = Some(val)
800    }
801    decoded.into_iter().flatten()
802}
803
804impl From<VariationIndex<'_>> for DeltaSetIndex {
805    fn from(src: VariationIndex) -> DeltaSetIndex {
806        DeltaSetIndex {
807            outer: src.delta_set_outer_index(),
808            inner: src.delta_set_inner_index(),
809        }
810    }
811}
812
813/// Combination of a tag and a child table.
814///
815/// Used in script and feature lists where a data structure has an array
816/// of records with each containing a tag and an offset to a table. This
817/// allows us to provide convenience methods that return both values.
818#[derive(Clone)]
819pub struct TaggedElement<T> {
820    pub tag: Tag,
821    pub element: T,
822}
823
824impl<T> TaggedElement<T> {
825    pub fn new(tag: Tag, element: T) -> Self {
826        Self { tag, element }
827    }
828}
829
830impl<T> std::ops::Deref for TaggedElement<T> {
831    type Target = T;
832
833    fn deref(&self) -> &Self::Target {
834        &self.element
835    }
836}
837
838#[cfg(test)]
839mod tests {
840    use super::*;
841
842    #[test]
843    fn coverage_get_format1() {
844        // manually generated, corresponding to the glyphs (1, 7, 13, 27, 44);
845        const COV1_DATA: FontData = FontData::new(&[0, 1, 0, 5, 0, 1, 0, 7, 0, 13, 0, 27, 0, 44]);
846
847        let coverage = CoverageFormat1::read(COV1_DATA).unwrap();
848        assert_eq!(coverage.get(GlyphId::new(1)), Some(0));
849        assert_eq!(coverage.get(GlyphId::new(2)), None);
850        assert_eq!(coverage.get(GlyphId::new(7)), Some(1));
851        assert_eq!(coverage.get(GlyphId::new(27)), Some(3));
852        assert_eq!(coverage.get(GlyphId::new(45)), None);
853    }
854
855    #[test]
856    fn coverage_get_format2() {
857        // manually generated, corresponding to glyphs (5..10) and (30..40).
858        const COV2_DATA: FontData =
859            FontData::new(&[0, 2, 0, 2, 0, 5, 0, 9, 0, 0, 0, 30, 0, 39, 0, 5]);
860        let coverage = CoverageFormat2::read(COV2_DATA).unwrap();
861        assert_eq!(coverage.get(GlyphId::new(2)), None);
862        assert_eq!(coverage.get(GlyphId::new(7)), Some(2));
863        assert_eq!(coverage.get(GlyphId::new(9)), Some(4));
864        assert_eq!(coverage.get(GlyphId::new(10)), None);
865        assert_eq!(coverage.get(GlyphId::new(32)), Some(7));
866        assert_eq!(coverage.get(GlyphId::new(39)), Some(14));
867        assert_eq!(coverage.get(GlyphId::new(40)), None);
868    }
869
870    #[test]
871    fn classdef_get_format2() {
872        let classdef = ClassDef::read(FontData::new(
873            font_test_data::gdef::MARKATTACHCLASSDEF_TABLE,
874        ))
875        .unwrap();
876        assert!(matches!(classdef, ClassDef::Format2(..)));
877        let gid_class_pairs = [
878            (616, 1),
879            (617, 1),
880            (618, 1),
881            (624, 1),
882            (625, 1),
883            (626, 1),
884            (652, 2),
885            (653, 2),
886            (654, 2),
887            (655, 2),
888            (661, 2),
889        ];
890        for (gid, class) in gid_class_pairs {
891            assert_eq!(classdef.get(GlyphId16::new(gid)), class);
892        }
893        for (gid, class) in classdef.iter() {
894            assert_eq!(classdef.get(gid), class);
895        }
896    }
897
898    #[test]
899    fn delta_decode() {
900        // these examples come from the spec
901        assert_eq!(
902            iter_packed_values(0x123f, DeltaFormat::Local4BitDeltas, 4).collect::<Vec<_>>(),
903            &[1, 2, 3, -1]
904        );
905
906        assert_eq!(
907            iter_packed_values(0x5540, DeltaFormat::Local2BitDeltas, 5).collect::<Vec<_>>(),
908            &[1, 1, 1, 1, 1]
909        );
910    }
911
912    #[test]
913    fn delta_decode_all() {
914        // manually generated with write-fonts
915        let bytes: &[u8] = &[0, 7, 0, 13, 0, 3, 1, 244, 30, 245, 101, 8, 42, 0];
916        let device = Device::read(bytes.into()).unwrap();
917        assert_eq!(
918            device.iter().collect::<Vec<_>>(),
919            &[1i8, -12, 30, -11, 101, 8, 42]
920        );
921    }
922
923    #[test]
924    fn bit_storage_tests() {
925        assert_eq!(bit_storage(0), 0);
926        assert_eq!(bit_storage(1), 1);
927        assert_eq!(bit_storage(2), 2);
928        assert_eq!(bit_storage(4), 3);
929        assert_eq!(bit_storage(9), 4);
930        assert_eq!(bit_storage(0x123), 9);
931        assert_eq!(bit_storage(0x1234), 13);
932        assert_eq!(bit_storage(0xffff), 16);
933        assert_eq!(bit_storage(0xffff_ffff), 32);
934    }
935
936    #[test]
937    fn default_coverage() {
938        let coverage = CoverageTable::default();
939        assert_eq!(coverage.iter().count(), 0)
940    }
941
942    #[test]
943    fn default_classdef() {
944        let classdef = ClassDef::default();
945        assert_eq!(classdef.population(), 0);
946        assert_eq!(classdef.iter().count(), 0);
947    }
948}