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