Skip to main content

read_fonts/tables/
layout.rs

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