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