Skip to main content

read_fonts/tables/
layout.rs

1//! OpenType Layout common table formats
2
3#[cfg(feature = "std")]
4mod closure;
5
6mod condition;
7mod feature;
8mod feature_variations;
9pub(crate) mod header;
10mod lookup_flag;
11mod script;
12
13use core::cmp::Ordering;
14
15pub use condition::store_deltas;
16pub use lookup_flag::LookupFlag;
17pub use script::{ScriptTags, SelectedScript, UNICODE_TO_NEW_OPENTYPE_SCRIPT_TAGS};
18
19use super::variations::DeltaSetIndex;
20
21#[cfg(feature = "std")]
22use crate::collections::IntSet;
23use crate::TableProvider;
24
25#[cfg(feature = "std")]
26pub(crate) use closure::{
27    ContextFormat1, ContextFormat2, ContextFormat3, LayoutLookupList, LookupClosure,
28    LookupClosureCtx, SeqCache,
29};
30
31#[cfg(feature = "std")]
32pub use closure::Intersect;
33
34#[cfg(test)]
35mod spec_tests;
36
37include!("../../generated/generated_layout.rs");
38
39pub use feature_variations::LookupVariations;
40pub use header::{LayoutOffsetArray, SelectedLookupList};
41
42impl<'a, T: FontRead<'a, Args = ()>> Lookup<'a, T> {
43    pub fn get_subtable(&self, offset: Offset16) -> Result<T, ReadError> {
44        self.resolve_offset(offset)
45    }
46}
47
48/// A trait that abstracts the behaviour of an extension subtable
49///
50/// This is necessary because GPOS and GSUB have different concrete types
51/// for their extension lookups.
52pub trait ExtensionLookup<'a, T: FontRead<'a, Args = ()>>: FontRead<'a, Args = ()> {
53    fn extension(&self) -> Result<T, ReadError>;
54}
55
56/// an array of subtables, maybe behind extension lookups
57///
58/// This is used to implement more ergonomic access to lookup subtables for
59/// GPOS & GSUB lookup tables.
60pub enum Subtables<'a, T: FontRead<'a, Args = ()>, Ext: ExtensionLookup<'a, T>> {
61    Subtable(ArrayOfOffsets<'a, T>),
62    Extension(ArrayOfOffsets<'a, Ext>),
63}
64
65impl<'a, T: FontRead<'a, Args = ()> + 'a, Ext: ExtensionLookup<'a, T> + 'a> Subtables<'a, T, Ext> {
66    /// create a new subtables array given offsets to non-extension subtables
67    pub(crate) fn new(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self {
68        Subtables::Subtable(ArrayOfOffsets::new(offsets, data, ()))
69    }
70
71    /// create a new subtables array given offsets to extension subtables
72    pub(crate) fn new_ext(offsets: &'a [BigEndian<Offset16>], data: FontData<'a>) -> Self {
73        Subtables::Extension(ArrayOfOffsets::new(offsets, data, ()))
74    }
75
76    /// The number of subtables in this collection
77    pub fn len(&self) -> usize {
78        match self {
79            Subtables::Subtable(inner) => inner.len(),
80            Subtables::Extension(inner) => inner.len(),
81        }
82    }
83
84    pub fn is_empty(&self) -> bool {
85        self.len() == 0
86    }
87
88    /// Return the subtable at the given index
89    pub fn get(&self, idx: usize) -> Result<T, ReadError> {
90        match self {
91            Subtables::Subtable(inner) => inner.get(idx),
92            Subtables::Extension(inner) => inner.get(idx).and_then(|ext| ext.extension()),
93        }
94    }
95
96    /// Return an iterator over all the subtables in the collection
97    pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
98        let (left, right) = match self {
99            Subtables::Subtable(inner) => (Some(inner.iter()), None),
100            Subtables::Extension(inner) => (
101                None,
102                Some(inner.iter().map(|ext| ext.and_then(|ext| ext.extension()))),
103            ),
104        };
105        left.into_iter()
106            .flatten()
107            .chain(right.into_iter().flatten())
108    }
109}
110
111/// An enum for different possible tables referenced by [Feature::feature_params_offset]
112pub enum FeatureParams<'a> {
113    StylisticSet(StylisticSetParams<'a>),
114    Size(SizeParams<'a>),
115    CharacterVariant(CharacterVariantParams<'a>),
116}
117
118impl ReadArgs for FeatureParams<'_> {
119    type Args = Tag;
120}
121
122impl<'a> FontRead<'a> for FeatureParams<'a> {
123    fn read_with_args(bytes: FontData<'a>, args: Tag) -> Result<FeatureParams<'a>, ReadError> {
124        match args {
125            t if t == Tag::new(b"size") => SizeParams::read(bytes).map(Self::Size),
126            // to whoever is debugging this dumb bug I wrote: I'm sorry.
127            t if &t.to_raw()[..2] == b"ss" => {
128                StylisticSetParams::read(bytes).map(Self::StylisticSet)
129            }
130            t if &t.to_raw()[..2] == b"cv" => {
131                CharacterVariantParams::read(bytes).map(Self::CharacterVariant)
132            }
133            // NOTE: what even is our error condition here? an offset exists but
134            // we don't know the tag?
135            _ => Err(ReadError::InvalidFormat(0xdead)),
136        }
137    }
138}
139
140impl FeatureVariations<'_> {
141    /// Returns the index of the first feature variation record that matches
142    /// the given coordinates. Format-2 conditions use their default values;
143    /// use [`Self::index_for_coords_with_delta`] to include variation deltas.
144    pub fn index_for_coords(&self, coords: &[F2Dot14]) -> Option<u32> {
145        self.index_for_coords_with_delta(coords, |_| Ok::<_, core::convert::Infallible>(0.0))
146    }
147
148    /// Returns the first matching record, resolving format-2 variation deltas
149    /// with the caller's variation store. Records with invalid conditions are
150    /// skipped. See [`Condition::evaluate`] for evaluation behavior.
151    pub fn index_for_coords_with_delta<E>(
152        &self,
153        coords: &[F2Dot14],
154        mut delta: impl FnMut(u32) -> Result<f64, E>,
155    ) -> Option<u32> {
156        if self.version().major != 1 {
157            return None;
158        }
159        for (index, rec) in self.feature_variation_records().iter().enumerate() {
160            // If the ConditionSet offset is 0, this is treated as the
161            // universal condition: all contexts are matched.
162            if rec.condition_set_offset().is_null() {
163                return Some(index as u32);
164            }
165            let Some(Ok(condition_set)) = rec.condition_set(self.offset_data()) else {
166                continue;
167            };
168            let delta = |index| delta(index).map_err(|_| crate::model::Error::Malformed);
169            if condition_set.evaluate(coords, delta).ok() == Some(true) {
170                return Some(index as u32);
171            }
172        }
173        None
174    }
175}
176
177/// Which features variation indices were selected for a given location.
178///
179/// This selects feature variation records only. Lookup variations are applied
180/// per feature with [`FeatureVariations::lookup_variations`].
181#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
182pub struct SelectedFeatureVariations {
183    /// Feature variation index for the GSUB table.
184    pub gsub: Option<u32>,
185    /// Feature variation index for the GPOS table.
186    pub gpos: Option<u32>,
187    /// True if either table has lookup variations, which then have to be
188    /// resolved per feature.
189    pub has_lookup_variations: bool,
190}
191
192impl SelectedFeatureVariations {
193    /// Selects the feature variation indices for the given font and
194    /// coordinates.
195    pub fn new<'a>(tables: &impl TableProvider<'a>, coords: &[F2Dot14]) -> Self {
196        let feature_var_tables = [
197            tables
198                .gsub()
199                .ok()
200                .and_then(|gsub| gsub.feature_variations()),
201            tables
202                .gpos()
203                .ok()
204                .and_then(|gpos| gpos.feature_variations()),
205        ];
206        let store = feature_var_tables
207            .iter()
208            .any(|table| matches!(table, Some(Ok(_))))
209            .then(|| {
210                tables
211                    .gdef()
212                    .ok()?
213                    .item_var_store()
214                    .transpose()
215                    .ok()
216                    .flatten()
217            })
218            .flatten();
219        let has_lookup_variations = feature_var_tables.iter().any(|table| {
220            table.as_ref().is_some_and(|table| {
221                table
222                    .as_ref()
223                    .is_ok_and(|table| table.lookup_variations().is_some())
224            })
225        });
226        let [gsub, gpos] = feature_var_tables.map(|feature_vars| {
227            feature_vars
228                .transpose()
229                .ok()
230                .flatten()
231                .and_then(|feature_vars| {
232                    feature_vars
233                        .index_for_coords_with_delta(coords, store_deltas(store.as_ref(), coords))
234                })
235        });
236        Self {
237            gsub,
238            gpos,
239            has_lookup_variations,
240        }
241    }
242}
243
244impl FeatureTableSubstitutionRecord {
245    pub fn alternate_feature<'a>(&self, data: FontData<'a>) -> Result<Feature<'a>, ReadError> {
246        self.alternate_feature_offset()
247            .resolve_with_args(data, Tag::new(b"NULL"))
248    }
249}
250
251fn bit_storage(v: u32) -> u32 {
252    u32::BITS - v.leading_zeros()
253}
254
255impl<'a> CoverageTable<'a> {
256    /// Iterates over the covered glyphs, in coverage order.
257    pub fn iter(&self) -> impl Iterator<Item = GlyphId> + 'a {
258        let (one, two, three, four) = match self {
259            Self::Format1(t) => (
260                Some(t.glyph_array().iter().map(|g| GlyphId::from(g.get()))),
261                None,
262                None,
263                None,
264            ),
265            Self::Format2(t) => (
266                None,
267                Some(
268                    t.range_records()
269                        .iter()
270                        .flat_map(|r| r.iter().map(GlyphId::from)),
271                ),
272                None,
273                None,
274            ),
275            Self::Format3(t) => (
276                None,
277                None,
278                Some(t.glyph_array().iter().map(|g| GlyphId::from(g.get()))),
279                None,
280            ),
281            Self::Format4(t) => (
282                None,
283                None,
284                None,
285                Some(t.range_records().iter().flat_map(RangeRecord2::iter)),
286            ),
287        };
288        one.into_iter()
289            .flatten()
290            .chain(two.into_iter().flatten())
291            .chain(three.into_iter().flatten())
292            .chain(four.into_iter().flatten())
293    }
294
295    /// If this glyph is covered, returns its index without narrowing it.
296    // Always inlined: left to the heuristics, the common formats' searches
297    // are folded in first and the result is then too large to inline.
298    #[inline(always)]
299    pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u32> {
300        let gid = gid.into();
301        match self {
302            Self::Format1(t) => t.get(gid).map(u32::from),
303            Self::Format2(t) => t.get(gid).map(u32::from),
304            _ => self.get_wide(gid),
305        }
306    }
307
308    // Out of line so that the common formats stay small enough to inline.
309    #[inline(never)]
310    fn get_wide(&self, gid: GlyphId) -> Option<u32> {
311        match self {
312            Self::Format3(t) => t.get(gid),
313            Self::Format4(t) => t.get(gid),
314            _ => None,
315        }
316    }
317
318    /// Returns if this table contains at least one glyph in the 'glyphs' set.
319    #[cfg(feature = "std")]
320    pub fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
321        match self {
322            Self::Format1(t) => t.intersects(glyphs),
323            Self::Format2(t) => t.intersects(glyphs),
324            Self::Format3(t) => t
325                .glyph_array()
326                .iter()
327                .any(|g| glyphs.contains(GlyphId::from(g.get()))),
328            Self::Format4(t) => t.range_records().iter().any(|r| {
329                glyphs.intersects_range(
330                    GlyphId::from(r.start_glyph_id())..=GlyphId::from(r.end_glyph_id()),
331                )
332            }),
333        }
334    }
335
336    /// Returns the intersection of this table and input 'glyphs' set.
337    #[cfg(feature = "std")]
338    pub fn intersect_set(&self, glyphs: &IntSet<GlyphId>) -> IntSet<GlyphId> {
339        match self {
340            Self::Format1(t) => t.intersect_set(glyphs),
341            Self::Format2(t) => t.intersect_set(glyphs),
342            Self::Format3(t) => t
343                .glyph_array()
344                .iter()
345                .map(|g| GlyphId::from(g.get()))
346                .filter(|g| glyphs.contains(*g))
347                .collect(),
348            Self::Format4(t) => {
349                let mut covered = IntSet::empty();
350                for range in t.range_records() {
351                    if range.start_glyph_id() <= range.end_glyph_id() {
352                        covered.insert_range(
353                            GlyphId::from(range.start_glyph_id())
354                                ..=GlyphId::from(range.end_glyph_id()),
355                        );
356                    }
357                }
358                covered.intersect(glyphs);
359                covered
360            }
361        }
362    }
363
364    /// Return the number of glyphs in this table
365    pub fn population(&self) -> usize {
366        match self {
367            Self::Format1(t) => t.population(),
368            Self::Format2(t) => t.population(),
369            Self::Format3(t) => t.glyph_count().to_u32() as usize,
370            Self::Format4(t) => t.range_records().iter().fold(0, |acc: usize, record| {
371                acc.saturating_add(record.population())
372            }),
373        }
374    }
375
376    /// Return the cost of looking up a glyph in this table
377    pub fn cost(&self) -> u32 {
378        match self {
379            Self::Format1(t) => t.cost(),
380            Self::Format2(t) => t.cost(),
381            Self::Format3(t) => bit_storage(t.glyph_count().to_u32()),
382            Self::Format4(t) => bit_storage(t.range_count().to_u32()),
383        }
384    }
385}
386
387impl CoverageFormat1<'_> {
388    /// If this glyph is in the coverage table, returns its index
389    #[inline]
390    pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u16> {
391        let gid16: GlyphId16 = gid.into().try_into().ok()?;
392        let be_glyph: BigEndian<GlyphId16> = gid16.into();
393        self.glyph_array()
394            .binary_search(&be_glyph)
395            .ok()
396            .map(|idx| idx as _)
397    }
398
399    /// Returns if this table contains at least one glyph in the 'glyphs' set.
400    #[cfg(feature = "std")]
401    fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
402        let glyph_count = self.glyph_count() as u32;
403        if glyph_count > (glyphs.len() as u32) * self.cost() {
404            glyphs.iter().any(|g| self.get(g).is_some())
405        } else {
406            self.glyph_array()
407                .iter()
408                .any(|g| glyphs.contains(GlyphId::from(g.get())))
409        }
410    }
411
412    /// Returns the intersection of this table and input 'glyphs' set.
413    #[cfg(feature = "std")]
414    fn intersect_set(&self, glyphs: &IntSet<GlyphId>) -> IntSet<GlyphId> {
415        let glyph_count = self.glyph_count() as u32;
416        if glyph_count > (glyphs.len() as u32) * self.cost() {
417            glyphs
418                .iter()
419                .filter_map(|g| self.get(g).map(|_| g))
420                .collect()
421        } else {
422            self.glyph_array()
423                .iter()
424                .filter(|g| glyphs.contains(GlyphId::from(g.get())))
425                .map(|g| GlyphId::from(g.get()))
426                .collect()
427        }
428    }
429
430    /// Return the number of glyphs in this table
431    pub fn population(&self) -> usize {
432        self.glyph_count() as usize
433    }
434
435    /// Return the cost of looking up a glyph in this table
436    pub fn cost(&self) -> u32 {
437        bit_storage(self.glyph_count() as u32)
438    }
439}
440
441impl CoverageFormat2<'_> {
442    /// If this glyph is in the coverage table, returns its index
443    #[inline]
444    pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u16> {
445        let gid: GlyphId16 = gid.into().try_into().ok()?;
446        self.range_records()
447            .binary_search_by(|rec| {
448                if rec.end_glyph_id() < gid {
449                    Ordering::Less
450                } else if rec.start_glyph_id() > gid {
451                    Ordering::Greater
452                } else {
453                    Ordering::Equal
454                }
455            })
456            .ok()
457            .and_then(|idx| {
458                let rec = &self.range_records()[idx];
459                // subtract first to avoid u16 overflow (https://github.com/googlefonts/fontations/issues/1887)
460                rec.start_coverage_index()
461                    .checked_add(gid.to_u16() - rec.start_glyph_id().to_u16())
462            })
463    }
464
465    /// Returns if this table contains at least one glyph in the 'glyphs' set.
466    #[cfg(feature = "std")]
467    fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
468        let range_count = self.range_count() as u32;
469        if range_count > (glyphs.len() as u32) * self.cost() {
470            glyphs.iter().any(|g| self.get(g).is_some())
471        } else {
472            self.range_records()
473                .iter()
474                .any(|record| record.intersects(glyphs))
475        }
476    }
477
478    /// Returns the intersection of this table and input 'glyphs' set.
479    #[cfg(feature = "std")]
480    fn intersect_set(&self, glyphs: &IntSet<GlyphId>) -> IntSet<GlyphId> {
481        let range_count = self.range_count() as u32;
482        if range_count > (glyphs.len() as u32) * self.cost() {
483            glyphs
484                .iter()
485                .filter_map(|g| self.get(g).map(|_| g))
486                .collect()
487        } else {
488            let mut out = IntSet::empty();
489            let mut last = GlyphId16::from(0);
490            for record in self.range_records() {
491                // break out of loop for overlapping/broken tables
492                let start_glyph = record.start_glyph_id();
493                if start_glyph < last {
494                    break;
495                }
496                let end = record.end_glyph_id();
497                last = end;
498
499                let start = GlyphId::from(start_glyph);
500                if glyphs.contains(start) {
501                    out.insert(start);
502                }
503
504                for g in glyphs.iter_after(start) {
505                    if g.to_u32() > end.to_u32() {
506                        break;
507                    }
508                    out.insert(g);
509                }
510            }
511            out
512        }
513    }
514
515    /// Return the number of glyphs in this table
516    pub fn population(&self) -> usize {
517        self.range_records().iter().fold(0, |acc: usize, record| {
518            acc.saturating_add(record.population())
519        })
520    }
521
522    /// Return the cost of looking up a glyph in this table
523    pub fn cost(&self) -> u32 {
524        bit_storage(self.range_count() as u32)
525    }
526}
527
528impl RangeRecord {
529    pub fn iter(&self) -> impl Iterator<Item = GlyphId16> + '_ {
530        (self.start_glyph_id().to_u16()..=self.end_glyph_id().to_u16()).map(GlyphId16::new)
531    }
532
533    /// Returns if this table contains at least one glyph in the 'glyphs' set.
534    #[cfg(feature = "std")]
535    pub fn intersects(&self, glyphs: &IntSet<GlyphId>) -> bool {
536        glyphs.intersects_range(
537            GlyphId::from(self.start_glyph_id())..=GlyphId::from(self.end_glyph_id()),
538        )
539    }
540
541    /// Return the number of glyphs in this record
542    pub fn population(&self) -> usize {
543        let start = self.start_glyph_id().to_u32() as usize;
544        let end = self.end_glyph_id().to_u32() as usize;
545        if start > end {
546            0
547        } else {
548            end - start + 1
549        }
550    }
551}
552
553impl CoverageFormat3<'_> {
554    /// If this glyph is in the coverage table, returns its index
555    pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u32> {
556        let gid = GlyphId24::checked_new(gid.into().to_u32())?;
557        self.glyph_array()
558            .binary_search(&BigEndian::from(gid))
559            .ok()
560            .map(|index| index as u32)
561    }
562}
563
564impl CoverageFormat4<'_> {
565    /// If this glyph is in the coverage table, returns its index
566    pub fn get(&self, gid: impl Into<GlyphId>) -> Option<u32> {
567        let gid = gid.into().to_u32();
568        let records = self.range_records();
569        let index = records
570            .binary_search_by(|r| {
571                if r.end_glyph_id().to_u32() < gid {
572                    Ordering::Less
573                } else if r.start_glyph_id().to_u32() > gid {
574                    Ordering::Greater
575                } else {
576                    Ordering::Equal
577                }
578            })
579            .ok()?;
580        let record = &records[index];
581        record
582            .start_coverage_index()
583            .to_u32()
584            .checked_add(gid.checked_sub(record.start_glyph_id().to_u32())?)
585            .filter(|index| *index <= Uint24::MAX.to_u32())
586    }
587}
588
589impl RangeRecord2 {
590    /// Iterates over the glyphs in this range.
591    pub fn iter(&self) -> impl Iterator<Item = GlyphId> + '_ {
592        (self.start_glyph_id().to_u32()..=self.end_glyph_id().to_u32()).map(GlyphId::new)
593    }
594
595    /// Return the number of glyphs in this record
596    pub fn population(&self) -> usize {
597        self.end_glyph_id()
598            .to_u32()
599            .checked_sub(self.start_glyph_id().to_u32())
600            .map_or(0, |n| n as usize + 1)
601    }
602}
603
604impl DeltaFormat {
605    pub(crate) fn value_count(self, start_size: u16, end_size: u16) -> usize {
606        let range_len = end_size.saturating_add(1).saturating_sub(start_size) as usize;
607        let val_per_word = match self {
608            DeltaFormat::Local2BitDeltas => 8,
609            DeltaFormat::Local4BitDeltas => 4,
610            DeltaFormat::Local8BitDeltas => 2,
611            _ => return 0,
612        };
613
614        let count = range_len / val_per_word;
615        let extra = (range_len % val_per_word).min(1);
616        count + extra
617    }
618}
619
620// we as a 'format' in codegen, and the generic error type for an invalid format
621// stores the value as an i64, so we need this conversion.
622impl From<DeltaFormat> for i64 {
623    fn from(value: DeltaFormat) -> Self {
624        value as u16 as _
625    }
626}
627
628impl<'a> ClassDefFormat1<'a> {
629    /// Get the class for this glyph id
630    #[inline]
631    pub fn get(&self, gid: impl Into<GlyphId>) -> u16 {
632        let Some(idx) = gid
633            .into()
634            .to_u32()
635            .checked_sub(self.start_glyph_id().to_u32())
636        else {
637            return 0;
638        };
639        self.class_value_array()
640            .get(idx as usize)
641            .map(|x| x.get())
642            .unwrap_or(0)
643    }
644
645    /// Iterate over each glyph and its class.
646    pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + 'a {
647        let start = self.start_glyph_id();
648        self.class_value_array()
649            .iter()
650            .enumerate()
651            .map(move |(i, val)| {
652                let gid = start.to_u16().saturating_add(i as u16);
653                (GlyphId16::new(gid), val.get())
654            })
655    }
656
657    /// Return the number of glyphs explicitly assigned to a class in this table
658    pub fn population(&self) -> usize {
659        self.glyph_count() as usize
660    }
661
662    /// Return the cost of looking up a glyph in this table
663    pub fn cost(&self) -> u32 {
664        1
665    }
666
667    /// Returns class values for the intersected glyphs of this table and input 'glyphs' set.
668    #[cfg(feature = "std")]
669    fn intersect_classes(&self, glyphs: &IntSet<GlyphId>) -> IntSet<u16> {
670        let mut out = IntSet::empty();
671        if glyphs.is_empty() {
672            return out;
673        }
674
675        let start_glyph = self.start_glyph_id().to_u32();
676        let class_values = self.class_value_array();
677        if class_values.is_empty() {
678            out.insert(0);
679            return out;
680        }
681        let end_glyph = start_glyph + class_values.len() as u32 - 1;
682        if glyphs.first().unwrap().to_u32() < start_glyph
683            || glyphs.last().unwrap().to_u32() > end_glyph
684        {
685            out.insert(0);
686        }
687
688        if glyphs.contains(GlyphId::from(start_glyph)) {
689            let Some(start_glyph_class) = class_values.first() else {
690                return out;
691            };
692            out.insert(start_glyph_class.get());
693        }
694
695        for g in glyphs.iter_after(GlyphId::from(start_glyph)) {
696            let g = g.to_u32();
697            if g > end_glyph {
698                break;
699            }
700
701            let idx = g - start_glyph;
702            let Some(class) = class_values.get(idx as usize) else {
703                break;
704            };
705            out.insert(class.get());
706        }
707        out
708    }
709
710    /// Returns intersected glyphs of this table and input 'glyphs' set that are assigned to input class value.
711    #[cfg(feature = "std")]
712    fn intersected_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> IntSet<GlyphId> {
713        let mut out = IntSet::empty();
714        if glyphs.is_empty() {
715            return out;
716        }
717
718        let start_glyph = self.start_glyph_id().to_u32();
719        let glyph_count = self.glyph_count();
720        // An empty ClassDef assigns class 0 to every glyph. This needs to be
721        // handled up front: the `end_glyph` computation below underflows when
722        // `start_glyph` is 0 and glyph_count is 0.
723        if glyph_count == 0 {
724            if class == 0 {
725                out.extend(glyphs.iter());
726            }
727            return out;
728        }
729        let end_glyph = start_glyph + glyph_count as u32 - 1;
730        if class == 0 {
731            let first = glyphs.first().unwrap();
732            if first.to_u32() < start_glyph {
733                out.extend(glyphs.range(first..GlyphId::from(start_glyph)));
734            }
735
736            let last = glyphs.last().unwrap();
737            if last.to_u32() > end_glyph {
738                out.extend(glyphs.range(GlyphId::from(end_glyph + 1)..=last));
739            }
740            return out;
741        }
742
743        let class_values = self.class_value_array();
744        for g in glyphs.range(GlyphId::from(start_glyph)..=GlyphId::from(end_glyph)) {
745            let idx = g.to_u32() - start_glyph;
746            let Some(c) = class_values.get(idx as usize) else {
747                break;
748            };
749            if c.get() == class {
750                out.insert(g);
751            }
752        }
753        out
754    }
755
756    /// Checks whether any glyph in the given glyphs set intersects with this table and is assigned to the specified class value.
757    #[cfg(feature = "std")]
758    fn intersects_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> bool {
759        if glyphs.is_empty() {
760            return false;
761        }
762
763        let start_glyph = self.start_glyph_id().to_u32();
764        let glyph_count = self.glyph_count();
765        // An empty ClassDef assigns class 0 to every glyph. This needs to be
766        // handled up front: the `end_glyph` computation below underflows when
767        // `start_glyph` is 0.
768        if glyph_count == 0 {
769            return class == 0;
770        }
771        let end_glyph = start_glyph + glyph_count as u32 - 1;
772        if class == 0 {
773            let first = glyphs.first().unwrap();
774            if first.to_u32() < start_glyph {
775                return true;
776            }
777
778            let last = glyphs.last().unwrap();
779            if last.to_u32() > end_glyph {
780                return true;
781            }
782        }
783
784        let class_values = self.class_value_array();
785        for g in glyphs.range(GlyphId::from(start_glyph)..=GlyphId::from(end_glyph)) {
786            let idx = g.to_u32() - start_glyph;
787            let Some(c) = class_values.get(idx as usize) else {
788                return false;
789            };
790            if c.get() == class {
791                return true;
792            }
793        }
794        false
795    }
796}
797
798impl<'a> ClassDefFormat2<'a> {
799    /// Get the class for this glyph id
800    #[inline]
801    pub fn get(&self, gid: impl Into<GlyphId>) -> u16 {
802        let gid = gid.into().to_u32();
803        let records = self.class_range_records();
804        let ix = match records.binary_search_by(|rec| rec.start_glyph_id().to_u32().cmp(&gid)) {
805            Ok(ix) => ix,
806            Err(ix) => ix.saturating_sub(1),
807        };
808        if let Some(record) = records.get(ix) {
809            if (record.start_glyph_id().to_u32()..=record.end_glyph_id().to_u32()).contains(&gid) {
810                return record.class();
811            }
812        }
813        0
814    }
815
816    /// Iterate over each glyph and its class.
817    pub fn iter(&self) -> impl Iterator<Item = (GlyphId16, u16)> + 'a {
818        self.class_range_records().iter().flat_map(|range| {
819            let start = range.start_glyph_id().to_u16();
820            let end = range.end_glyph_id().to_u16();
821            (start..=end).map(|gid| (GlyphId16::new(gid), range.class()))
822        })
823    }
824
825    /// Return the number of glyphs explicitly assigned to a class in this table
826    pub fn population(&self) -> usize {
827        self.class_range_records()
828            .iter()
829            .fold(0, |acc: usize, record| {
830                acc.saturating_add(record.population())
831            })
832    }
833
834    /// Return the cost of looking up a glyph in this table
835    pub fn cost(&self) -> u32 {
836        bit_storage(self.class_range_count() as u32)
837    }
838
839    /// Returns class values for the intersected glyphs of this table and input 'glyphs' set.
840    #[cfg(feature = "std")]
841    fn intersect_classes(&self, glyphs: &IntSet<GlyphId>) -> IntSet<u16> {
842        let mut out = IntSet::empty();
843        if glyphs.is_empty() {
844            return out;
845        }
846
847        let range_records = self.class_range_records();
848        let Some(first_record) = range_records.first() else {
849            out.insert(0);
850            return out;
851        };
852
853        if glyphs.first().unwrap() < first_record.start_glyph_id() {
854            out.insert(0);
855        } else {
856            let mut glyph = GlyphId::from(first_record.end_glyph_id());
857            for record in range_records.iter().skip(1) {
858                let Some(g) = glyphs.iter_after(glyph).next() else {
859                    break;
860                };
861
862                if g < record.start_glyph_id() {
863                    out.insert(0);
864                    break;
865                }
866                glyph = GlyphId::from(record.end_glyph_id());
867            }
868            if glyphs.iter_after(glyph).next().is_some() {
869                out.insert(0);
870            }
871        }
872
873        let num_ranges = self.class_range_count();
874        if num_ranges as u64 > glyphs.len() * self.cost() as u64 {
875            for g in glyphs.iter() {
876                let class = self.get(g);
877                if class != 0 {
878                    out.insert(class);
879                }
880            }
881        } else {
882            for record in range_records {
883                if glyphs.intersects_range(
884                    GlyphId::from(record.start_glyph_id())..=GlyphId::from(record.end_glyph_id()),
885                ) {
886                    out.insert(record.class());
887                }
888            }
889        }
890        out
891    }
892
893    /// Returns intersected glyphs of this table and input 'glyphs' set that are assgiend to input class value.
894    #[cfg(feature = "std")]
895    fn intersected_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> IntSet<GlyphId> {
896        let mut out = IntSet::empty();
897        if glyphs.is_empty() {
898            return out;
899        }
900
901        let first = glyphs.first().unwrap().to_u32();
902        let last = glyphs.last().unwrap().to_u32();
903        if class == 0 {
904            let mut start = first;
905            for range in self.class_range_records() {
906                let range_start = range.start_glyph_id().to_u32();
907                if start < range_start {
908                    out.extend(glyphs.range(GlyphId::from(start)..GlyphId::from(range_start)));
909                }
910
911                let range_end = range.end_glyph_id().to_u32();
912                if range_end >= last {
913                    break;
914                }
915                start = range_end + 1;
916            }
917
918            if start <= last {
919                out.extend(glyphs.range(GlyphId::from(start)..=GlyphId::from(last)));
920            }
921            return out;
922        }
923
924        let num_ranges = self.class_range_count();
925        if num_ranges as u64 > glyphs.len() * self.cost() as u64 {
926            for g in glyphs.iter() {
927                let c = self.get(g);
928                if c == class {
929                    out.insert(g);
930                }
931            }
932        } else {
933            for range in self.class_range_records() {
934                let range_start = range.start_glyph_id().to_u32();
935                let range_end = range.end_glyph_id().to_u32();
936                if range_start > last {
937                    break;
938                }
939                if range.class() != class || range.end_glyph_id().to_u32() < first {
940                    continue;
941                }
942                out.extend(glyphs.range(GlyphId::from(range_start)..=GlyphId::from(range_end)));
943            }
944        }
945        out
946    }
947
948    /// Checks whether any glyph in the given glyphs set intersects with this table and is assigned to the specified class value.
949    #[cfg(feature = "std")]
950    fn intersects_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u16) -> bool {
951        if glyphs.is_empty() {
952            return false;
953        }
954
955        // An empty ClassDef assigns class 0 to every glyph. This needs to be
956        // handled up front: with no ranges the class 0 walk below starts from
957        // `first + 1` and so misses `first` itself.
958        if self.class_range_count() == 0 {
959            return class == 0;
960        }
961
962        let first = glyphs.first().unwrap().to_u32();
963        if class == 0 {
964            let mut last_end = first;
965            for (i, range) in self.class_range_records().iter().enumerate() {
966                let range_start = range.start_glyph_id().to_u32();
967                let range_end = range.end_glyph_id().to_u32();
968                if i == 0 {
969                    if first < range_start {
970                        return true;
971                    }
972                    last_end = range_end;
973                    continue;
974                }
975
976                if range_start == last_end + 1 {
977                    last_end = range_end;
978                    continue;
979                }
980
981                if glyphs
982                    .intersects_range(GlyphId::from(last_end + 1)..=GlyphId::from(range_start - 1))
983                {
984                    return true;
985                };
986                last_end = range_end + 1;
987            }
988            if glyphs
989                .iter_after(GlyphId::from(last_end + 1))
990                .next()
991                .is_some()
992            {
993                return true;
994            }
995        }
996
997        let num_ranges = self.class_range_count();
998        if num_ranges as u64 > glyphs.len() * self.cost() as u64 {
999            for g in glyphs.iter() {
1000                let c = self.get(g);
1001                if c == class {
1002                    return true;
1003                }
1004            }
1005        } else {
1006            let last = glyphs.last().unwrap().to_u32();
1007            for range in self.class_range_records() {
1008                let range_start = range.start_glyph_id().to_u32();
1009                let range_end = range.end_glyph_id().to_u32();
1010                if range_start > last {
1011                    break;
1012                }
1013                if range_end < first {
1014                    continue;
1015                }
1016                if range.class() == class
1017                    && glyphs
1018                        .intersects_range(GlyphId::from(range_start)..=GlyphId::from(range_end))
1019                {
1020                    return true;
1021                }
1022            }
1023        }
1024        false
1025    }
1026}
1027
1028impl ClassRangeRecord {
1029    /// Return the number of glyphs explicitly assigned to a class in this table
1030    pub fn population(&self) -> usize {
1031        let start = self.start_glyph_id().to_u32() as usize;
1032        let end = self.end_glyph_id().to_u32() as usize;
1033        if start > end {
1034            0
1035        } else {
1036            end - start + 1
1037        }
1038    }
1039}
1040
1041impl ClassDefFormat3<'_> {
1042    /// Get the class for this glyph id
1043    pub fn get(&self, gid: impl Into<GlyphId>) -> u32 {
1044        let gid = gid.into().to_u32();
1045        if gid > Uint24::MAX.to_u32() {
1046            return 0;
1047        }
1048        gid.checked_sub(self.start_glyph_id().to_u32())
1049            .and_then(|index| self.class_value_array().get(index as usize))
1050            .map_or(0, |class| class.get().to_u32())
1051    }
1052}
1053
1054impl ClassDefFormat4<'_> {
1055    /// Get the class for this glyph id
1056    pub fn get(&self, gid: impl Into<GlyphId>) -> u32 {
1057        let gid = gid.into().to_u32();
1058        let records = self.class_range_records();
1059        let index = records
1060            .binary_search_by(|r| r.start_glyph_id().to_u32().cmp(&gid))
1061            .unwrap_or_else(|i| i.saturating_sub(1));
1062        records
1063            .get(index)
1064            .filter(|r| (r.start_glyph_id().to_u32()..=r.end_glyph_id().to_u32()).contains(&gid))
1065            .map_or(0, |r| u32::from(r.class()))
1066    }
1067}
1068
1069impl ClassRangeRecord2 {
1070    /// Return the number of glyphs explicitly assigned to a class in this record
1071    pub fn population(&self) -> usize {
1072        self.end_glyph_id()
1073            .to_u32()
1074            .checked_sub(self.start_glyph_id().to_u32())
1075            .map_or(0, |n| n as usize + 1)
1076    }
1077}
1078
1079impl ClassDef<'_> {
1080    /// Gets the class without narrowing 24-bit class values.
1081    // Always inlined, for the reason given on CoverageTable::get.
1082    #[inline(always)]
1083    pub fn get(&self, gid: impl Into<GlyphId>) -> u32 {
1084        let gid = gid.into();
1085        match self {
1086            Self::Format1(t) => u32::from(t.get(gid)),
1087            Self::Format2(t) => u32::from(t.get(gid)),
1088            _ => self.get_wide(gid),
1089        }
1090    }
1091
1092    // Out of line so that the common formats stay small enough to inline.
1093    #[inline(never)]
1094    fn get_wide(&self, gid: GlyphId) -> u32 {
1095        match self {
1096            Self::Format3(t) => t.get(gid),
1097            Self::Format4(t) => t.get(gid),
1098            _ => 0,
1099        }
1100    }
1101
1102    /// Iterates over explicitly assigned glyphs and classes.
1103    pub fn iter(&self) -> impl Iterator<Item = (GlyphId, u32)> + '_ {
1104        let (one, two) = match self {
1105            Self::Format1(t) => (
1106                Some(t.iter().map(|(g, c)| (GlyphId::from(g), u32::from(c)))),
1107                None,
1108            ),
1109            Self::Format2(t) => (
1110                None,
1111                Some(t.iter().map(|(g, c)| (GlyphId::from(g), u32::from(c)))),
1112            ),
1113            _ => (None, None),
1114        };
1115        one.into_iter()
1116            .flatten()
1117            .chain(two.into_iter().flatten())
1118            .chain(self.wide_class_ranges().flat_map(|(r, c)| {
1119                (r.start().to_u32()..=r.end().to_u32()).map(move |g| (GlyphId::new(g), c))
1120            }))
1121    }
1122
1123    /// Return the number of glyphs explicitly assigned to a class in this table
1124    pub fn population(&self) -> usize {
1125        match self {
1126            Self::Format1(t) => t.population(),
1127            Self::Format2(t) => t.population(),
1128            Self::Format3(t) => t.glyph_count().to_u32() as usize,
1129            Self::Format4(t) => t
1130                .class_range_records()
1131                .iter()
1132                .fold(0, |acc: usize, record| {
1133                    acc.saturating_add(record.population())
1134                }),
1135        }
1136    }
1137
1138    /// Return the cost of looking up a glyph in this table
1139    pub fn cost(&self) -> u32 {
1140        match self {
1141            Self::Format1(t) => t.cost(),
1142            Self::Format2(t) => t.cost(),
1143            Self::Format3(_) => 1,
1144            Self::Format4(t) => bit_storage(t.class_range_count().to_u32()),
1145        }
1146    }
1147
1148    /// Returns class values for the intersected glyphs of this table and input 'glyphs' set.
1149    #[cfg(feature = "std")]
1150    pub fn intersect_classes(&self, glyphs: &IntSet<GlyphId>) -> IntSet<u32> {
1151        match self {
1152            Self::Format1(t) => t.intersect_classes(glyphs).iter().map(u32::from).collect(),
1153            Self::Format2(t) => t.intersect_classes(glyphs).iter().map(u32::from).collect(),
1154            _ => {
1155                let mut classes = IntSet::empty();
1156                let mut assigned = IntSet::empty();
1157                for (range, class) in self.wide_class_ranges() {
1158                    if glyphs.intersects_range(range.clone()) {
1159                        classes.insert(class);
1160                    }
1161                    assigned.insert_range(range);
1162                }
1163                if !glyphs.is_subset(&assigned) {
1164                    classes.insert(0);
1165                }
1166                classes
1167            }
1168        }
1169    }
1170
1171    /// Returns intersected glyphs of this table and input 'glyphs' set that are assigned to input class value.
1172    #[cfg(feature = "std")]
1173    pub fn intersected_class_glyphs(
1174        &self,
1175        glyphs: &IntSet<GlyphId>,
1176        class: u32,
1177    ) -> IntSet<GlyphId> {
1178        match self {
1179            Self::Format1(t) => u16::try_from(class)
1180                .map(|c| t.intersected_class_glyphs(glyphs, c))
1181                .unwrap_or_default(),
1182            Self::Format2(t) => u16::try_from(class)
1183                .map(|c| t.intersected_class_glyphs(glyphs, c))
1184                .unwrap_or_default(),
1185            _ => {
1186                let mut out = IntSet::empty();
1187                let mut assigned = IntSet::empty();
1188                for (range, c) in self.wide_class_ranges() {
1189                    if c == class {
1190                        out.insert_range(range.clone());
1191                    }
1192                    if class == 0 {
1193                        assigned.insert_range(range);
1194                    }
1195                }
1196                if class == 0 {
1197                    assigned.invert();
1198                    out.union(&assigned);
1199                }
1200                out.intersect(glyphs);
1201                out
1202            }
1203        }
1204    }
1205
1206    /// Checks whether any glyph in the given glyphs set intersects with this table and is assigned to the specified class value.
1207    #[cfg(feature = "std")]
1208    pub fn intersects_class_glyphs(&self, glyphs: &IntSet<GlyphId>, class: u32) -> bool {
1209        match self {
1210            Self::Format1(t) => {
1211                u16::try_from(class).is_ok_and(|c| t.intersects_class_glyphs(glyphs, c))
1212            }
1213            Self::Format2(t) => {
1214                u16::try_from(class).is_ok_and(|c| t.intersects_class_glyphs(glyphs, c))
1215            }
1216            _ => !self.intersected_class_glyphs(glyphs, class).is_empty(),
1217        }
1218    }
1219
1220    fn wide_class_ranges(
1221        &self,
1222    ) -> impl Iterator<Item = (core::ops::RangeInclusive<GlyphId>, u32)> + '_ {
1223        let (one, two) = match self {
1224            Self::Format3(t) => (
1225                Some(
1226                    t.class_value_array()
1227                        .iter()
1228                        .enumerate()
1229                        .filter_map(move |(i, c)| {
1230                            let g = t.start_glyph_id().to_u32().checked_add(i as u32)?;
1231                            (g <= Uint24::MAX.to_u32())
1232                                .then_some((GlyphId::new(g)..=GlyphId::new(g), c.get().to_u32()))
1233                        }),
1234                ),
1235                None,
1236            ),
1237            Self::Format4(t) => (
1238                None,
1239                Some(t.class_range_records().iter().filter_map(|r| {
1240                    (r.start_glyph_id() <= r.end_glyph_id()).then_some((
1241                        GlyphId::from(r.start_glyph_id())..=GlyphId::from(r.end_glyph_id()),
1242                        u32::from(r.class()),
1243                    ))
1244                })),
1245            ),
1246            _ => (None, None),
1247        };
1248        one.into_iter().flatten().chain(two.into_iter().flatten())
1249    }
1250}
1251
1252impl<'a> Device<'a> {
1253    /// The adjustment this table makes at a size, in pixels.
1254    ///
1255    /// A device table gives a whole pixel correction for each size in the
1256    /// range it covers. Sizes outside that range, and a `ppem` of zero, are
1257    /// adjusted by nothing.
1258    pub fn delta_for_ppem(&self, ppem: u16) -> i32 {
1259        let start = self.start_size();
1260        if ppem == 0 || ppem < start || ppem > self.end_size() {
1261            return 0;
1262        }
1263        self.iter()
1264            .nth((ppem - start) as usize)
1265            .map_or(0, |delta| delta as i32)
1266    }
1267
1268    /// Iterate over the decoded values for this device
1269    pub fn iter(&self) -> impl Iterator<Item = i8> + 'a {
1270        let format = self.delta_format();
1271        let mut n = self
1272            .end_size()
1273            .checked_sub(self.start_size())
1274            .map(|x| x as usize + 1)
1275            .unwrap_or(0);
1276        let deltas_per_word = match format {
1277            DeltaFormat::Local2BitDeltas => 8,
1278            DeltaFormat::Local4BitDeltas => 4,
1279            DeltaFormat::Local8BitDeltas => 2,
1280            _ => 0,
1281        };
1282
1283        self.delta_value().iter().flat_map(move |val| {
1284            let iter = iter_packed_values(val.get(), format, n);
1285            n = n.saturating_sub(deltas_per_word);
1286            iter
1287        })
1288    }
1289}
1290
1291fn iter_packed_values(raw: u16, format: DeltaFormat, n: usize) -> impl Iterator<Item = i8> {
1292    let mut decoded = [None; 8];
1293    let (mask, sign_mask, bits) = match format {
1294        DeltaFormat::Local2BitDeltas => (0b11, 0b10, 2usize),
1295        DeltaFormat::Local4BitDeltas => (0b1111, 0b1000, 4),
1296        DeltaFormat::Local8BitDeltas => (0b1111_1111, 0b1000_0000, 8),
1297        _ => (0, 0, 0),
1298    };
1299
1300    let max_per_word = 16 / bits;
1301    #[allow(clippy::needless_range_loop)] // enumerate() feels weird here
1302    for i in 0..n.min(max_per_word) {
1303        let shift = (16 - bits) - i * bits;
1304        // pull the n-bit field down to the low bits before decoding, so the
1305        // sign handling below always works on a low-aligned value
1306        let val = (raw >> shift) & mask;
1307        let val = if val & sign_mask != 0 {
1308            // sign extend the n-bit value to a full i8
1309            (val | !mask) as i8
1310        } else {
1311            val as i8
1312        };
1313        decoded[i] = Some(val)
1314    }
1315    decoded.into_iter().flatten()
1316}
1317
1318impl From<VariationIndex<'_>> for DeltaSetIndex {
1319    fn from(src: VariationIndex) -> DeltaSetIndex {
1320        DeltaSetIndex {
1321            outer: src.delta_set_outer_index(),
1322            inner: src.delta_set_inner_index(),
1323        }
1324    }
1325}
1326
1327/// Combination of a tag and a child table.
1328///
1329/// Used in script and feature lists where a data structure has an array
1330/// of records with each containing a tag and an offset to a table. This
1331/// allows us to provide convenience methods that return both values.
1332#[derive(Clone)]
1333pub struct TaggedElement<T> {
1334    pub tag: Tag,
1335    pub element: T,
1336}
1337
1338impl<T> TaggedElement<T> {
1339    pub fn new(tag: Tag, element: T) -> Self {
1340        Self { tag, element }
1341    }
1342}
1343
1344impl<T> std::ops::Deref for TaggedElement<T> {
1345    type Target = T;
1346
1347    fn deref(&self) -> &Self::Target {
1348        &self.element
1349    }
1350}
1351
1352#[cfg(test)]
1353mod tests {
1354    use super::*;
1355    use crate::FontRef;
1356    use types::Fixed;
1357
1358    #[test]
1359    fn coverage_24bit_glyphs_and_indices() {
1360        // Format 3: three 24-bit glyph IDs, including both width boundaries.
1361        let bytes = [0, 3, 0, 0, 3, 0, 0xFF, 0xFF, 1, 0, 0, 0xFF, 0xFF, 0xFF];
1362        let coverage = CoverageTable::read(FontData::new(&bytes)).unwrap();
1363        assert_eq!(
1364            coverage.iter().map(GlyphId::to_u32).collect::<Vec<_>>(),
1365            [65535, 65536, 0xFFFFFF]
1366        );
1367        assert_eq!(coverage.get(GlyphId::new(65536)), Some(1));
1368        assert_eq!(coverage.get(GlyphId::new(0x1000000)), None);
1369
1370        // Format 4: a range with a coverage index above 65535.
1371        let bytes = [0, 4, 0, 0, 1, 1, 0, 0, 1, 0, 2, 1, 0, 0];
1372        let coverage = CoverageTable::read(FontData::new(&bytes)).unwrap();
1373        assert_eq!(coverage.get(GlyphId::new(65538)), Some(65538));
1374        assert_eq!(coverage.get(GlyphId::new(65539)), None);
1375        assert_eq!(coverage.population(), 3);
1376    }
1377
1378    #[test]
1379    fn class_widths_follow_the_format() {
1380        // Format 3 uses 24-bit class values, not just 24-bit glyph IDs.
1381        let bytes = [0, 3, 1, 0, 0, 0, 0, 2, 1, 0, 0, 0xFF, 0xFF, 0xFF];
1382        let class = ClassDef::read(FontData::new(&bytes)).unwrap();
1383        assert_eq!(class.get(GlyphId::new(65536)), 65536);
1384        assert_eq!(class.get(GlyphId::new(65537)), 0xFFFFFF);
1385        assert_eq!(class.get(GlyphId::new(65538)), 0);
1386
1387        // Format 4's range records still store a 16-bit class value.
1388        let bytes = [0, 4, 0, 0, 1, 1, 0, 0, 1, 0, 2, 0xFF, 0xFF];
1389        let class = ClassDef::read(FontData::new(&bytes)).unwrap();
1390        assert_eq!(class.get(GlyphId::new(65538)), 65535);
1391        assert_eq!(class.get(GlyphId::new(65539)), 0);
1392        assert_eq!(class.iter().count(), 3);
1393    }
1394
1395    #[test]
1396    fn truncated_wide_arrays_do_not_match() {
1397        for bytes in [&[0, 3, 0, 0, 1][..], &[0, 4, 0, 0, 1][..]] {
1398            let coverage = CoverageTable::read(FontData::new(bytes)).unwrap();
1399            assert_eq!(coverage.get(GlyphId::new(0)), None);
1400        }
1401        for bytes in [&[0, 3, 1, 0, 0, 0, 0, 1][..], &[0, 4, 0, 0, 1][..]] {
1402            let class = ClassDef::read(FontData::new(bytes)).unwrap();
1403            assert_eq!(class.get(GlyphId::new(65536)), 0);
1404        }
1405    }
1406
1407    #[cfg(feature = "std")]
1408    #[test]
1409    fn wide_class_zero_includes_unassigned_glyphs() {
1410        let bytes = [0, 4, 0, 0, 1, 1, 0, 0, 1, 0, 2, 0, 1];
1411        let class = ClassDef::read(FontData::new(&bytes)).unwrap();
1412        let glyphs: IntSet<GlyphId> = [65535, 65536, 65538, 65539]
1413            .map(GlyphId::new)
1414            .into_iter()
1415            .collect();
1416        assert_eq!(
1417            class.intersect_classes(&glyphs).iter().collect::<Vec<_>>(),
1418            [0, 1]
1419        );
1420        assert_eq!(
1421            class
1422                .intersected_class_glyphs(&glyphs, 0)
1423                .iter()
1424                .map(GlyphId::to_u32)
1425                .collect::<Vec<_>>(),
1426            [65535, 65539]
1427        );
1428    }
1429
1430    #[test]
1431    fn coverage_get_format1() {
1432        // manually generated, corresponding to the glyphs (1, 7, 13, 27, 44);
1433        const COV1_DATA: FontData = FontData::new(&[0, 1, 0, 5, 0, 1, 0, 7, 0, 13, 0, 27, 0, 44]);
1434
1435        let coverage = CoverageFormat1::read(COV1_DATA).unwrap();
1436        assert_eq!(coverage.get(GlyphId::new(1)), Some(0));
1437        assert_eq!(coverage.get(GlyphId::new(2)), None);
1438        assert_eq!(coverage.get(GlyphId::new(7)), Some(1));
1439        assert_eq!(coverage.get(GlyphId::new(27)), Some(3));
1440        assert_eq!(coverage.get(GlyphId::new(45)), None);
1441    }
1442
1443    #[test]
1444    fn coverage_get_format2() {
1445        // manually generated, corresponding to glyphs (5..10) and (30..40).
1446        const COV2_DATA: FontData =
1447            FontData::new(&[0, 2, 0, 2, 0, 5, 0, 9, 0, 0, 0, 30, 0, 39, 0, 5]);
1448        let coverage = CoverageFormat2::read(COV2_DATA).unwrap();
1449        assert_eq!(coverage.get(GlyphId::new(2)), None);
1450        assert_eq!(coverage.get(GlyphId::new(7)), Some(2));
1451        assert_eq!(coverage.get(GlyphId::new(9)), Some(4));
1452        assert_eq!(coverage.get(GlyphId::new(10)), None);
1453        assert_eq!(coverage.get(GlyphId::new(32)), Some(7));
1454        assert_eq!(coverage.get(GlyphId::new(39)), Some(14));
1455        assert_eq!(coverage.get(GlyphId::new(40)), None);
1456    }
1457
1458    // <https://github.com/googlefonts/fontations/issues/1887>
1459    #[test]
1460    fn coverage_get_format2_no_u16_overflow() {
1461        // A single range covering glyphs 40000..=40010 with a high
1462        // start_coverage_index, as occurs in large CJK fonts, and which
1463        // was causing an overflow.
1464        const COV2_DATA: FontData =
1465            FontData::new(&[0, 2, 0, 1, 0x9c, 0x40, 0x9c, 0x4a, 0x9c, 0x40]);
1466        let coverage = CoverageFormat2::read(COV2_DATA).unwrap();
1467        assert_eq!(coverage.get(GlyphId::new(40000)), Some(40000));
1468        assert_eq!(coverage.get(GlyphId::new(40005)), Some(40005));
1469        assert_eq!(coverage.get(GlyphId::new(40010)), Some(40010));
1470        assert_eq!(coverage.get(GlyphId::new(40011)), None);
1471    }
1472
1473    #[test]
1474    fn coverage_get_format2_rejects_overflowing_coverage_index() {
1475        // The start_coverage_index plus offset to glyph 2 would overflow u16.
1476        const COV2_DATA: FontData = FontData::new(&[0, 2, 0, 1, 0, 1, 0, 2, 0xff, 0xff]);
1477        let coverage = CoverageFormat2::read(COV2_DATA).unwrap();
1478        assert_eq!(coverage.get(GlyphId::new(1)), Some(u16::MAX));
1479        assert_eq!(coverage.get(GlyphId::new(2)), None);
1480    }
1481
1482    #[test]
1483    fn classdef_get_format2() {
1484        let classdef = ClassDef::read(FontData::new(
1485            font_test_data::gdef::MARKATTACHCLASSDEF_TABLE,
1486        ))
1487        .unwrap();
1488        assert!(matches!(classdef, ClassDef::Format2(..)));
1489        let gid_class_pairs = [
1490            (616, 1),
1491            (617, 1),
1492            (618, 1),
1493            (624, 1),
1494            (625, 1),
1495            (626, 1),
1496            (652, 2),
1497            (653, 2),
1498            (654, 2),
1499            (655, 2),
1500            (661, 2),
1501        ];
1502        for (gid, class) in gid_class_pairs {
1503            assert_eq!(classdef.get(GlyphId16::new(gid)), class);
1504        }
1505        for (gid, class) in classdef.iter() {
1506            assert_eq!(classdef.get(gid), class);
1507        }
1508    }
1509
1510    #[test]
1511    fn classdef_format1_short_read_no_panic() {
1512        // glyph_count is 5, but only one class value is present.
1513        let classdef = ClassDefFormat1::read(FontData::new(&[0, 1, 0, 10, 0, 5, 0, 1])).unwrap();
1514        let glyphs: IntSet<GlyphId> = [GlyphId::new(10), GlyphId::new(11), GlyphId::new(14)]
1515            .into_iter()
1516            .collect();
1517
1518        assert_eq!(classdef.get(GlyphId::new(10)), 0);
1519        assert_eq!(classdef.get(GlyphId::new(11)), 0);
1520        assert!(!classdef.intersects_class_glyphs(&glyphs, 2));
1521
1522        let class_ones = classdef.intersected_class_glyphs(&glyphs, 1);
1523        assert!(class_ones.is_empty());
1524    }
1525
1526    // An empty ClassDef assigns class 0 to every glyph.
1527    #[test]
1528    fn classdef_format1_empty() {
1529        // start_glyph_id = 0, glyph_count = 0. The zero start glyph is what
1530        // made `start_glyph + glyph_count - 1` underflow.
1531        let classdef = ClassDefFormat1::read(FontData::new(&[0, 1, 0, 0, 0, 0])).unwrap();
1532        let glyphs: IntSet<GlyphId> = [GlyphId::new(48), GlyphId::new(49), GlyphId::new(50)]
1533            .into_iter()
1534            .collect();
1535
1536        assert!(classdef.intersects_class_glyphs(&glyphs, 0));
1537        assert!(!classdef.intersects_class_glyphs(&glyphs, 1));
1538
1539        assert!(classdef
1540            .intersected_class_glyphs(&glyphs, 0)
1541            .iter()
1542            .eq(glyphs.iter()));
1543        assert!(classdef.intersected_class_glyphs(&glyphs, 1).is_empty());
1544
1545        assert_eq!(
1546            classdef
1547                .intersect_classes(&glyphs)
1548                .iter()
1549                .collect::<Vec<_>>(),
1550            vec![0]
1551        );
1552
1553        // a single glyph must still be reported as class 0
1554        let one: IntSet<GlyphId> = [GlyphId::new(48)].into_iter().collect();
1555        assert!(classdef.intersects_class_glyphs(&one, 0));
1556
1557        // and nothing intersects an empty glyph set
1558        let none = IntSet::<GlyphId>::empty();
1559        assert!(!classdef.intersects_class_glyphs(&none, 0));
1560        assert!(classdef.intersected_class_glyphs(&none, 0).is_empty());
1561    }
1562
1563    // Same as above but with a non-zero start_glyph_id, which did not overflow
1564    // but must give the same answers.
1565    #[test]
1566    fn classdef_format1_empty_nonzero_start_glyph() {
1567        let classdef = ClassDefFormat1::read(FontData::new(&[0, 1, 0, 60, 0, 0])).unwrap();
1568
1569        // glyphs before, after, and straddling the start glyph
1570        for gids in [vec![48u16, 49], vec![70, 71], vec![48, 70]] {
1571            let glyphs: IntSet<GlyphId> = gids.iter().map(|g| GlyphId::new(*g as u32)).collect();
1572            assert!(classdef.intersects_class_glyphs(&glyphs, 0), "{gids:?}");
1573            assert!(!classdef.intersects_class_glyphs(&glyphs, 1), "{gids:?}");
1574            assert_eq!(
1575                classdef
1576                    .intersected_class_glyphs(&glyphs, 0)
1577                    .iter()
1578                    .collect::<Vec<_>>(),
1579                glyphs.iter().collect::<Vec<_>>(),
1580                "{gids:?}"
1581            );
1582        }
1583    }
1584
1585    #[test]
1586    fn classdef_format2_empty() {
1587        // class_range_count = 0
1588        let classdef = ClassDefFormat2::read(FontData::new(&[0, 2, 0, 0])).unwrap();
1589        let glyphs: IntSet<GlyphId> = [GlyphId::new(48), GlyphId::new(49), GlyphId::new(50)]
1590            .into_iter()
1591            .collect();
1592
1593        assert!(classdef.intersects_class_glyphs(&glyphs, 0));
1594        assert!(!classdef.intersects_class_glyphs(&glyphs, 1));
1595
1596        // regression: the class 0 walk used to start at `first + 1`, so a
1597        // single glyph was missed and this returned false.
1598        let one: IntSet<GlyphId> = [GlyphId::new(48)].into_iter().collect();
1599        assert!(classdef.intersects_class_glyphs(&one, 0));
1600
1601        assert_eq!(
1602            classdef
1603                .intersected_class_glyphs(&glyphs, 0)
1604                .iter()
1605                .collect::<Vec<_>>(),
1606            glyphs.iter().collect::<Vec<_>>()
1607        );
1608        assert!(classdef.intersected_class_glyphs(&glyphs, 1).is_empty());
1609
1610        assert_eq!(
1611            classdef
1612                .intersect_classes(&glyphs)
1613                .iter()
1614                .collect::<Vec<_>>(),
1615            vec![0]
1616        );
1617    }
1618
1619    // Both spellings of an empty ClassDef must behave identically.
1620    #[test]
1621    fn classdef_empty_formats_agree() {
1622        let f1 = ClassDef::read(FontData::new(&[0, 1, 0, 0, 0, 0])).unwrap();
1623        let f2 = ClassDef::read(FontData::new(&[0, 2, 0, 0])).unwrap();
1624        assert!(matches!(f1, ClassDef::Format1(..)));
1625        assert!(matches!(f2, ClassDef::Format2(..)));
1626
1627        for gids in [vec![0u32], vec![48], vec![48, 49, 50]] {
1628            let glyphs: IntSet<GlyphId> = gids.iter().copied().map(GlyphId::new).collect();
1629            for class in [0u32, 1] {
1630                assert_eq!(
1631                    f1.intersects_class_glyphs(&glyphs, class),
1632                    f2.intersects_class_glyphs(&glyphs, class),
1633                    "{gids:?} class {class}"
1634                );
1635                assert_eq!(
1636                    f1.intersected_class_glyphs(&glyphs, class)
1637                        .iter()
1638                        .collect::<Vec<_>>(),
1639                    f2.intersected_class_glyphs(&glyphs, class)
1640                        .iter()
1641                        .collect::<Vec<_>>(),
1642                    "{gids:?} class {class}"
1643                );
1644            }
1645            assert_eq!(
1646                f1.intersect_classes(&glyphs).iter().collect::<Vec<_>>(),
1647                f2.intersect_classes(&glyphs).iter().collect::<Vec<_>>(),
1648                "{gids:?}"
1649            );
1650        }
1651    }
1652
1653    #[test]
1654    fn delta_decode() {
1655        // these examples come from the spec
1656        assert_eq!(
1657            iter_packed_values(0x123f, DeltaFormat::Local4BitDeltas, 4).collect::<Vec<_>>(),
1658            &[1, 2, 3, -1]
1659        );
1660
1661        assert_eq!(
1662            iter_packed_values(0x5540, DeltaFormat::Local2BitDeltas, 5).collect::<Vec<_>>(),
1663            &[1, 1, 1, 1, 1]
1664        );
1665    }
1666
1667    #[test]
1668    fn delta_decode_negative_not_in_last_slot() {
1669        // A negative delta must decode correctly regardless of its position
1670        // within the word, not only in the least significant slot.
1671        // 8-bit: bytes 0xf4, 0x01 -> -12, 1
1672        assert_eq!(
1673            iter_packed_values(0xf401, DeltaFormat::Local8BitDeltas, 2).collect::<Vec<_>>(),
1674            &[-12, 1]
1675        );
1676        // 4-bit: nibbles 0x8, 0x1, 0x2, 0x3 -> -8, 1, 2, 3
1677        assert_eq!(
1678            iter_packed_values(0x8123, DeltaFormat::Local4BitDeltas, 4).collect::<Vec<_>>(),
1679            &[-8, 1, 2, 3]
1680        );
1681        // 2-bit: 10 01 01 01 -> -2, 1, 1, 1
1682        assert_eq!(
1683            iter_packed_values(0x9540, DeltaFormat::Local2BitDeltas, 4).collect::<Vec<_>>(),
1684            &[-2, 1, 1, 1]
1685        );
1686        // the smallest 8-bit value in a leading slot must not overflow
1687        assert_eq!(
1688            iter_packed_values(0x8000, DeltaFormat::Local8BitDeltas, 2).collect::<Vec<_>>(),
1689            &[-128, 0]
1690        );
1691    }
1692
1693    #[test]
1694    fn delta_decode_all() {
1695        // manually generated with write-fonts
1696        let bytes: &[u8] = &[0, 7, 0, 13, 0, 3, 1, 244, 30, 245, 101, 8, 42, 0];
1697        let device = Device::read(bytes.into()).unwrap();
1698        assert_eq!(
1699            device.iter().collect::<Vec<_>>(),
1700            &[1i8, -12, 30, -11, 101, 8, 42]
1701        );
1702    }
1703
1704    #[test]
1705    fn device_decode_does_not_overflow() {
1706        // manually generated with write-fonts
1707        let bytes: &[u8] = &[0, 0xA, 0, 1, 0, 1];
1708        // Don't panic with overflow
1709        Device::read(bytes.into()).unwrap().iter().count();
1710    }
1711
1712    #[test]
1713    fn bit_storage_tests() {
1714        assert_eq!(bit_storage(0), 0);
1715        assert_eq!(bit_storage(1), 1);
1716        assert_eq!(bit_storage(2), 2);
1717        assert_eq!(bit_storage(4), 3);
1718        assert_eq!(bit_storage(9), 4);
1719        assert_eq!(bit_storage(0x123), 9);
1720        assert_eq!(bit_storage(0x1234), 13);
1721        assert_eq!(bit_storage(0xffff), 16);
1722        assert_eq!(bit_storage(0xffff_ffff), 32);
1723    }
1724
1725    #[test]
1726    fn default_coverage() {
1727        let coverage = CoverageTable::default();
1728        assert_eq!(coverage.iter().count(), 0)
1729    }
1730
1731    /// Normalized coordinates for the font at one value on one axis.
1732    fn coords_at(font: &FontRef, tag: Tag, value: f32) -> Vec<F2Dot14> {
1733        let fvar = font.fvar().unwrap();
1734        let mut coords = vec![F2Dot14::ZERO; fvar.axis_count() as usize];
1735        fvar.user_to_normalized(
1736            font.avar().ok().as_ref(),
1737            [(tag, Fixed::from_f64(value as f64))],
1738            &mut coords,
1739        );
1740        coords
1741    }
1742
1743    #[test]
1744    fn index_for_coords_honors_the_condition_set() {
1745        let font = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).unwrap();
1746        let feature_vars = font.gsub().unwrap().feature_variations().unwrap().unwrap();
1747        let fill = Tag::new(b"FILL");
1748        assert_eq!(
1749            feature_vars.index_for_coords(&coords_at(&font, fill, 1.0)),
1750            Some(0)
1751        );
1752        assert_eq!(
1753            feature_vars.index_for_coords(&coords_at(&font, fill, 0.5)),
1754            None
1755        );
1756        // A short slice reads as the default location: an axis it omits
1757        // takes its default value.
1758        assert_eq!(feature_vars.index_for_coords(&[]), None);
1759    }
1760
1761    #[test]
1762    fn selected_feature_variations() {
1763        let font = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).unwrap();
1764        let cases = [
1765            // (FILL value, [GSUB feature variation index, GPOS feature variation index])
1766            (0.0, [None, None]),
1767            (0.5, [None, None]),
1768            (0.98, [None, None]),
1769            (0.99, [Some(0), None]),
1770            (1.0, [Some(0), None]),
1771        ];
1772        for (fill, expected) in cases {
1773            let coords = coords_at(&font, Tag::new(b"FILL"), fill);
1774            let selected = SelectedFeatureVariations::new(&font, &coords);
1775            assert_eq!([selected.gsub, selected.gpos], expected, "fill={fill}");
1776        }
1777    }
1778
1779    #[test]
1780    fn a_font_without_the_layout_tables_selects_nothing() {
1781        let font = FontRef::new(font_test_data::NAMES_ONLY).unwrap();
1782        assert_eq!(
1783            SelectedFeatureVariations::new(&font, &[]),
1784            SelectedFeatureVariations::default()
1785        );
1786    }
1787
1788    #[test]
1789    fn default_classdef() {
1790        let classdef = ClassDef::default();
1791        assert_eq!(classdef.population(), 0);
1792        assert_eq!(classdef.iter().count(), 0);
1793    }
1794}