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