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