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