read_fonts/tables/
layout.rs

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