swash/internal/
at.rs

1//! OpenType advanced typography tables.
2
3use super::{raw_tag, Bytes, RawTag};
4
5pub const GDEF: RawTag = raw_tag(b"GDEF");
6pub const GSUB: RawTag = raw_tag(b"GSUB");
7pub const GPOS: RawTag = raw_tag(b"GPOS");
8
9pub const DFLT: RawTag = raw_tag(b"DFLT");
10
11/// Glyph definition table.
12#[derive(Copy, Clone)]
13pub struct Gdef<'a> {
14    data: Bytes<'a>,
15    classes: u16,
16    mark_classes: u16,
17    mark_sets: u16,
18    var_store: u32,
19}
20
21impl<'a> Gdef<'a> {
22    pub fn new(data: &'a [u8]) -> Option<Self> {
23        let b = Bytes::new(data);
24        let major = b.read::<u16>(0)?;
25        let minor = b.read::<u16>(2)?;
26        let classes = b.read::<u16>(4)?;
27        let mark_classes = b.read::<u16>(10)?;
28        let mark_sets = if major > 1 || minor >= 2 {
29            b.read_or_default::<u16>(12)
30        } else {
31            0
32        };
33        let var_store = if major > 1 || minor >= 3 {
34            b.read_or_default::<u32>(14)
35        } else {
36            0
37        };
38        Some(Self {
39            data: b,
40            classes,
41            mark_classes,
42            mark_sets,
43            var_store,
44        })
45    }
46
47    pub fn from_offset(data: &'a [u8], offset: u32) -> Option<Self> {
48        if offset == 0 {
49            return None;
50        }
51        Self::new(data.get(offset as usize..)?)
52    }
53
54    pub fn empty() -> Self {
55        Self {
56            data: Bytes::new(&[]),
57            classes: 0,
58            mark_classes: 0,
59            mark_sets: 0,
60            var_store: 0,
61        }
62    }
63
64    pub fn ok(&self) -> bool {
65        self.data.len() != 0
66    }
67
68    /// Returns true if glyph classes are available.
69    pub fn has_classes(&self) -> bool {
70        self.classes != 0
71    }
72
73    /// Returns the class for the specified glyph id.
74    pub fn class(&self, glyph_id: u16) -> u16 {
75        classdef(&self.data, self.classes as u32, glyph_id)
76    }
77
78    /// Returns true if mark glyph classes are available.
79    pub fn has_mark_classes(&self) -> bool {
80        self.mark_classes != 0
81    }
82
83    /// Returns the mark class for the specified glyph id.
84    pub fn mark_class(&self, glyph_id: u16) -> u16 {
85        classdef(&self.data, self.mark_classes as u32, glyph_id)
86    }
87
88    pub fn mark_set_coverage(&self, set_offset: u32, glyph_id: u16) -> Option<u16> {
89        if set_offset == 0 {
90            return None;
91        }
92        // Coverage is validated by mark_set_offset() below.
93        unsafe { fast_coverage(&self.data, set_offset, glyph_id) }
94    }
95
96    pub fn mark_set_offset(&self, set_index: u16) -> Option<u32> {
97        if self.mark_sets == 0 {
98            return None;
99        }
100        let set = set_index as usize;
101        let b = &self.data;
102        let sets_base = self.mark_sets as usize;
103        let len = b.read::<u16>(sets_base + 2)? as usize;
104        if set >= len {
105            return None;
106        }
107        let offset = b.read::<u32>(sets_base + 4 + set * 4)?;
108        let set_offset = sets_base as u32 + offset;
109        (offset != 0 && validate_coverage(b, set_offset)).then_some(set_offset)
110    }
111
112    pub fn has_var_store(&self) -> bool {
113        self.var_store != 0
114    }
115
116    pub fn delta(&self, outer: u16, inner: u16, coords: &[i16]) -> f32 {
117        if self.var_store != 0 {
118            super::var::item_delta(self.data.data(), self.var_store, outer, inner, coords)
119                .map(|d| d.to_f32())
120                .unwrap_or(0.)
121        } else {
122            0.
123        }
124    }
125}
126
127/// Feature lookup kind.
128#[derive(Copy, Clone, PartialEq, Debug)]
129#[repr(u8)]
130pub enum LookupKind {
131    SingleSub,
132    MultiSub,
133    AltSub,
134    LigSub,
135    SingleAdj,
136    PairAdj,
137    Cursive,
138    MarkToBase,
139    MarkToLig,
140    MarkToMark,
141    Context,
142    ChainContext,
143    RevChainContext,
144}
145
146/// Data associated with a feature lookup.
147#[derive(Copy, Clone)]
148pub struct LookupData {
149    pub index: u16,
150    pub stage: u8,
151    pub kind: LookupKind,
152    pub feature: u16,
153    pub mask: u8,
154    pub ignored: u8,
155    pub is_ext: bool,
156    pub offset: u32,
157    pub coverage: u32,
158    pub count: u16,
159    pub subtables: (u16, u16),
160    pub mark_set: u32,
161    pub mark_check: u8,
162    pub mark_class: u8,
163}
164
165impl LookupData {
166    pub fn subtable_data(&self, b: &Bytes, index: u16) -> Option<SubtableData> {
167        let base = self.offset as usize;
168        let subtable_base = base + 6;
169        let mut offset = base + b.read::<u16>(subtable_base + index as usize * 2)? as usize;
170        if self.is_ext {
171            offset = offset + b.read::<u32>(offset + 4)? as usize;
172        }
173        let fmt = b.read::<u16>(offset)?;
174        subtable_data(b, offset as u32, self.kind, fmt)
175    }
176}
177
178/// Lookup subtable kind, flattened to include the associated format.
179#[derive(Copy, Clone, PartialEq, Debug)]
180#[repr(u8)]
181pub enum SubtableKind {
182    SingleSub1,
183    SingleSub2,
184    MultiSub1,
185    AltSub1,
186    LigSub1,
187    SingleAdj1,
188    SingleAdj2,
189    PairAdj1,
190    PairAdj2,
191    Cursive1,
192    MarkToBase1,
193    MarkToLig1,
194    MarkToMark1,
195    Context1,
196    Context2,
197    Context3,
198    ChainContext1,
199    ChainContext2,
200    ChainContext3,
201    RevChainContext1,
202}
203
204/// Data associated with a lookup subtable.
205#[derive(Copy, Clone)]
206pub struct SubtableData {
207    pub offset: u32,
208    pub kind: SubtableKind,
209    pub coverage: u16,
210}
211
212impl SubtableData {
213    pub fn coverage(&self, b: &Bytes, glyph_id: u16) -> Option<u16> {
214        unsafe { fast_coverage(b, self.offset + self.coverage as u32, glyph_id) }
215    }
216}
217
218/// Feature substitutions for variable fonts.
219#[derive(Copy, Clone)]
220pub struct FeatureSubsts(u32);
221
222impl FeatureSubsts {
223    pub fn new(b: &Bytes, offset: u32, coords: &[i16]) -> Option<Self> {
224        if offset == 0 || coords.is_empty() {
225            return None;
226        }
227        let base = offset as usize;
228        let count = b.read::<u32>(base + 4)? as usize;
229        for i in 0..count {
230            let rec = base + 8 + i * 8;
231            let condset_table = base + b.read::<u32>(rec)? as usize;
232            let condset_count = b.read::<u16>(condset_table)? as usize;
233            let mut matched = 0;
234            for j in 0..condset_count {
235                let cond_table = condset_table + b.read::<u32>(condset_table + 2 + j * 4)? as usize;
236                let format = b.read::<u16>(cond_table)?;
237                if format != 1 {
238                    break;
239                }
240                let axis = b.read::<u16>(cond_table + 2)? as usize;
241                if axis >= coords.len() {
242                    break;
243                }
244                let coord = coords[axis];
245                let min = b.read::<i16>(cond_table + 4)?;
246                if coord < min {
247                    break;
248                }
249                let max = b.read::<i16>(cond_table + 6)?;
250                if coord > max {
251                    break;
252                }
253                matched += 1;
254            }
255            if matched == condset_count {
256                return Some(Self(offset + b.read::<u32>(rec + 4)?));
257            }
258        }
259        None
260    }
261
262    pub fn apply(self, b: &Bytes, index: u16) -> Option<usize> {
263        let mut base = self.0 as usize;
264        let count = b.read::<u16>(base + 4)? as usize;
265        base += 6;
266        let mut l = 0;
267        let mut h = count;
268        while l < h {
269            use core::cmp::Ordering::*;
270            let i = (l + h) / 2;
271            let rec = base + i * 6;
272            let idx = b.read::<u16>(rec)?;
273            match index.cmp(&idx) {
274                Less => h = i,
275                Greater => l = i + 1,
276                Equal => return Some((self.0 + b.read::<u32>(rec + 2)?) as usize),
277            }
278        }
279        None
280    }
281}
282
283pub fn script_count(b: &Bytes, gsubgpos_offset: u32) -> u16 {
284    if gsubgpos_offset == 0 {
285        return 0;
286    }
287    let base = gsubgpos_offset as usize;
288    let offset = b.read_or_default::<u16>(base + 4) as usize;
289    if offset == 0 {
290        return 0;
291    }
292    b.read_or_default::<u16>(base + offset)
293}
294
295pub fn script_at(b: &Bytes, gsubgpos_offset: u32, index: u16) -> Option<(RawTag, u32)> {
296    if gsubgpos_offset == 0 {
297        return None;
298    }
299    let base = gsubgpos_offset as usize;
300    let sbase = base + b.read::<u16>(base + 4)? as usize;
301    let rec = sbase + 2 + index as usize * 6;
302    let tag = b.read::<u32>(rec)?;
303    let offset = sbase as u32 + b.read::<u16>(rec + 4)? as u32;
304    Some((tag, offset))
305}
306
307pub fn script_by_tag(b: &Bytes, gsubgpos_offset: u32, script: RawTag) -> Option<u32> {
308    if gsubgpos_offset == 0 {
309        return None;
310    }
311    let base = gsubgpos_offset as usize;
312    let sbase = base + b.read::<u16>(base + 4)? as usize;
313    let mut l = 0;
314    let mut h = b.read::<u16>(sbase)? as usize;
315    while l < h {
316        use core::cmp::Ordering::*;
317        let i = l + (h - l) / 2;
318        let rec = sbase + 2 + i * 6;
319        let t = b.read::<u32>(rec)?;
320        match script.cmp(&t) {
321            Less => h = i,
322            Greater => l = i + 1,
323            Equal => return Some(sbase as u32 + b.read::<u16>(rec + 4)? as u32),
324        }
325    }
326    None
327}
328
329pub fn script_language_count(b: &Bytes, script_offset: u32) -> u16 {
330    if script_offset == 0 {
331        return 0;
332    }
333    b.read::<u16>(script_offset as usize + 2)
334        .map(|n| n + 1)
335        .unwrap_or(0)
336}
337
338pub fn script_default_language(b: &Bytes, script_offset: u32) -> Option<u32> {
339    if script_offset == 0 {
340        return None;
341    }
342    let offset = b.read::<u16>(script_offset as usize)? as u32;
343    if offset == 0 {
344        None
345    } else {
346        Some(script_offset + offset)
347    }
348}
349
350pub fn script_language_at(b: &Bytes, script_offset: u32, index: u16) -> Option<(RawTag, u32)> {
351    if script_offset == 0 {
352        return None;
353    }
354    let index = if index == 0 {
355        return Some((DFLT, script_default_language(b, script_offset)?));
356    } else {
357        index - 1
358    };
359    let rec = script_offset as usize + 4 + index as usize * 6;
360    let tag = b.read::<u32>(rec)?;
361    let offset = b.read::<u16>(rec + 4)? as u32;
362    if offset == 0 {
363        return None;
364    }
365    Some((tag, script_offset + offset))
366}
367
368pub fn script_language_by_tag(
369    b: &Bytes,
370    script_offset: u32,
371    language: Option<RawTag>,
372) -> Option<(u32, bool)> {
373    if script_offset == 0 {
374        return None;
375    }
376    let base = script_offset as usize;
377    if let Some(lang) = language {
378        let mut l = 0;
379        let mut h = b.read::<u16>(base + 2)? as usize;
380        while l < h {
381            use core::cmp::Ordering::*;
382            let i = (l + h) / 2;
383            let rec = base + 4 + i * 6;
384            let t = b.read::<u32>(rec)?;
385            match lang.cmp(&t) {
386                Less => h = i,
387                Greater => l = i + 1,
388                Equal => {
389                    let lang_offset = b.read::<u16>(rec + 4)? as usize;
390                    if lang_offset == 0 {
391                        return None;
392                    }
393                    return Some((script_offset + lang_offset as u32, false));
394                }
395            }
396        }
397    }
398    let default = b.read::<u16>(base)? as usize;
399    if default == 0 {
400        return None;
401    }
402    Some(((base + default) as u32, true))
403}
404
405pub fn language_or_default_by_tags(
406    b: &Bytes,
407    gsubgpos_offset: u32,
408    script: RawTag,
409    lang: Option<RawTag>,
410) -> Option<(u32, [RawTag; 2])> {
411    if let Some(script_offset) = script_by_tag(b, gsubgpos_offset, script) {
412        let (lang_offset, is_default) = script_language_by_tag(b, script_offset, lang)?;
413        Some((
414            lang_offset,
415            [
416                script,
417                if is_default {
418                    DFLT
419                } else {
420                    lang.unwrap_or(DFLT)
421                },
422            ],
423        ))
424    } else {
425        let (lang_offset, is_default) = language_by_tags(b, gsubgpos_offset, DFLT, lang)?;
426        Some((
427            lang_offset,
428            [
429                DFLT,
430                if is_default {
431                    DFLT
432                } else {
433                    lang.unwrap_or(DFLT)
434                },
435            ],
436        ))
437    }
438}
439
440pub fn language_by_tags(
441    b: &Bytes,
442    gsubgpos_offset: u32,
443    script: RawTag,
444    language: Option<RawTag>,
445) -> Option<(u32, bool)> {
446    script_language_by_tag(b, script_by_tag(b, gsubgpos_offset, script)?, language)
447}
448
449pub fn language_feature_count(b: &Bytes, language_offset: u32) -> u16 {
450    if language_offset == 0 {
451        return 0;
452    }
453    b.read_or_default(language_offset as usize + 4)
454}
455
456pub fn language_feature_at(b: &Bytes, language_offset: u32, index: u16) -> Option<u16> {
457    b.read(language_offset as usize + 6 + index as usize * 2)
458}
459
460pub fn language_features<'a>(
461    b: Bytes<'a>,
462    gsubgpos_offset: u32,
463    language_offset: u32,
464) -> impl Iterator<Item = (RawTag, u32)> + 'a + Clone {
465    let mut count = language_feature_count(&b, language_offset);
466    if gsubgpos_offset == 0 {
467        count = 0;
468    }
469    let base = gsubgpos_offset as usize;
470    let fbase = b.read_or_default::<u16>(base + 6) as usize;
471    if fbase == 0 {
472        count = 0;
473    }
474    let fbase = base + fbase;
475    (0..count).filter_map(move |i| {
476        let index = language_feature_at(&b, language_offset, i)?;
477        let rec = fbase + 2 + index as usize * 6;
478        let tag = b.read::<u32>(rec)?;
479        let offset = b.read::<u16>(rec + 4)?;
480        if offset == 0 {
481            return None;
482        }
483        Some((tag, fbase as u32 + offset as u32))
484    })
485}
486
487pub fn feature_count(b: &Bytes, gsubgpos_offset: u32) -> u16 {
488    if gsubgpos_offset == 0 {
489        return 0;
490    }
491    let base = gsubgpos_offset as usize;
492    let fbase = b.read_or_default::<u16>(base + 6) as usize;
493    if fbase == 0 {
494        return 0;
495    }
496    b.read_or_default::<u16>(base + fbase)
497}
498
499pub fn feature_at(b: &Bytes, gsubgpos_offset: u32, index: u16) -> Option<(RawTag, u32)> {
500    if gsubgpos_offset == 0 {
501        return None;
502    }
503    let base = gsubgpos_offset as usize;
504    let fbase = b.read::<u16>(base + 6)? as usize;
505    if fbase == 0 {
506        return None;
507    }
508    let fbase = base + fbase;
509    let rec = fbase + 2 + index as usize * 6;
510    let tag = b.read::<u32>(rec)?;
511    let offset = b.read::<u16>(rec + 4)?;
512    if offset == 0 {
513        return None;
514    }
515    Some((tag, fbase as u32 + offset as u32))
516}
517
518pub fn feature_var_offset(b: &Bytes, gsubgpos_offset: u32) -> u32 {
519    if gsubgpos_offset == 0 {
520        return 0;
521    }
522    let base = gsubgpos_offset as usize;
523    let major = b.read_or_default::<u16>(base);
524    if major > 1 || (major == 1 && b.read_or_default::<u16>(base + 2) >= 1) {
525        let offset = b.read_or_default::<u32>(base + 10);
526        if offset != 0 {
527            gsubgpos_offset + offset
528        } else {
529            0
530        }
531    } else {
532        0
533    }
534}
535
536pub fn lookup_data(
537    b: &Bytes,
538    stage: u8,
539    list_base: u32,
540    index: u16,
541    mask: u8,
542    gdef: Option<&Gdef>,
543) -> Option<LookupData> {
544    if list_base == 0 {
545        return None;
546    }
547    let base = list_base as usize;
548    let rec = base + 2 + index as usize * 2;
549    let offset = b.read::<u16>(rec)?;
550    let base = base + offset as usize;
551    let mut kind = b.read::<u16>(base)? as u8;
552    let flag = b.read::<u16>(base + 2)?;
553    let f = flag as u8;
554    let count = b.read::<u16>(base + 4)?;
555    let mark_class = (flag >> 8) as u8;
556    let ignore_marks = f & (1 << 3) != 0;
557    let mut mark_check = 0;
558    let mut mark_set = 0;
559    if !ignore_marks {
560        if let Some(gdef) = gdef {
561            mark_check = (mark_class != 0 && gdef.has_mark_classes()) as u8;
562            mark_set = if gdef.ok() && flag & 0x10 != 0 {
563                let idx = b.read::<u16>(base + 6 + count as usize * 2)?;
564                mark_check = 1;
565                gdef.mark_set_offset(idx).unwrap_or(0)
566            } else {
567                0
568            };
569        }
570    }
571    let is_sub = stage == 0;
572    let subtables = base + 6;
573    let is_ext = (is_sub && kind == 7) || (!is_sub && kind == 9);
574    if is_ext && count > 0 {
575        let s = base + b.read::<u16>(subtables)? as usize;
576        kind = b.read::<u16>(s + 2)? as u8;
577    }
578    use LookupKind::*;
579    let kind = if stage == 0 {
580        match kind {
581            1 => SingleSub,
582            2 => MultiSub,
583            3 => AltSub,
584            4 => LigSub,
585            5 => Context,
586            6 => ChainContext,
587            8 => RevChainContext,
588            _ => return None,
589        }
590    } else {
591        match kind {
592            1 => SingleAdj,
593            2 => PairAdj,
594            3 => Cursive,
595            4 => MarkToBase,
596            5 => MarkToLig,
597            6 => MarkToMark,
598            7 => Context,
599            8 => ChainContext,
600            _ => return None,
601        }
602    };
603    let ignored = (f & 0b1110) | 1 << 5;
604    Some(LookupData {
605        index,
606        stage,
607        kind,
608        feature: 0,
609        mask,
610        ignored,
611        is_ext,
612        offset: base as u32,
613        count,
614        coverage: !0,
615        subtables: (0, 0),
616        mark_class,
617        mark_set,
618        mark_check,
619    })
620}
621
622pub fn subtable_data(b: &Bytes, offset: u32, kind: LookupKind, fmt: u16) -> Option<SubtableData> {
623    let base = offset as usize;
624    fn cov(b: &Bytes, base: usize, offset: usize) -> Option<u16> {
625        let c = b.read::<u16>(base + offset)?;
626        validate_coverage(b, base as u32 + c as u32).then_some(c)
627    }
628    use LookupKind::*;
629    match kind {
630        SingleSub => {
631            let kind = match fmt {
632                1 => SubtableKind::SingleSub1,
633                2 => SubtableKind::SingleSub2,
634                _ => return None,
635            };
636            let coverage = cov(b, base, 2)?;
637            Some(SubtableData {
638                offset,
639                kind,
640                coverage,
641            })
642        }
643        MultiSub => {
644            let kind = match fmt {
645                1 => SubtableKind::MultiSub1,
646                _ => return None,
647            };
648            let coverage = cov(b, base, 2)?;
649            Some(SubtableData {
650                offset,
651                kind,
652                coverage,
653            })
654        }
655        AltSub => {
656            let kind = match fmt {
657                1 => SubtableKind::AltSub1,
658                _ => return None,
659            };
660            let coverage = cov(b, base, 2)?;
661            Some(SubtableData {
662                offset,
663                kind,
664                coverage,
665            })
666        }
667        LigSub => {
668            let kind = match fmt {
669                1 => SubtableKind::LigSub1,
670                _ => return None,
671            };
672            let coverage = cov(b, base, 2)?;
673            Some(SubtableData {
674                offset,
675                kind,
676                coverage,
677            })
678        }
679        SingleAdj => {
680            let kind = match fmt {
681                1 => SubtableKind::SingleAdj1,
682                2 => SubtableKind::SingleAdj2,
683                _ => return None,
684            };
685            let coverage = cov(b, base, 2)?;
686            Some(SubtableData {
687                offset,
688                kind,
689                coverage,
690            })
691        }
692        PairAdj => {
693            let kind = match fmt {
694                1 => SubtableKind::PairAdj1,
695                2 => SubtableKind::PairAdj2,
696                _ => return None,
697            };
698            let coverage = cov(b, base, 2)?;
699            Some(SubtableData {
700                offset,
701                kind,
702                coverage,
703            })
704        }
705        Cursive => {
706            let kind = match fmt {
707                1 => SubtableKind::Cursive1,
708                _ => return None,
709            };
710            let coverage = cov(b, base, 2)?;
711            Some(SubtableData {
712                offset,
713                kind,
714                coverage,
715            })
716        }
717        MarkToBase => {
718            let kind = match fmt {
719                1 => SubtableKind::MarkToBase1,
720                _ => return None,
721            };
722            let coverage = cov(b, base, 2)?;
723            Some(SubtableData {
724                offset,
725                kind,
726                coverage,
727            })
728        }
729        MarkToLig => {
730            let kind = match fmt {
731                1 => SubtableKind::MarkToLig1,
732                _ => return None,
733            };
734            let coverage = cov(b, base, 2)?;
735            Some(SubtableData {
736                offset,
737                kind,
738                coverage,
739            })
740        }
741        MarkToMark => {
742            let kind = match fmt {
743                1 => SubtableKind::MarkToMark1,
744                _ => return None,
745            };
746            let coverage = cov(b, base, 2)?;
747            Some(SubtableData {
748                offset,
749                kind,
750                coverage,
751            })
752        }
753        Context => match fmt {
754            1 | 2 => {
755                let kind = if fmt == 1 {
756                    SubtableKind::Context1
757                } else {
758                    SubtableKind::Context2
759                };
760                let coverage = cov(b, base, 2)?;
761                Some(SubtableData {
762                    offset,
763                    kind,
764                    coverage,
765                })
766            }
767            3 => {
768                let coverage = cov(b, base, 6)?;
769                Some(SubtableData {
770                    kind: SubtableKind::Context3,
771                    offset,
772                    coverage,
773                })
774            }
775            _ => None,
776        },
777        ChainContext => match fmt {
778            1 | 2 => {
779                let kind = if fmt == 1 {
780                    SubtableKind::ChainContext1
781                } else {
782                    SubtableKind::ChainContext2
783                };
784                let coverage = cov(b, base, 2)?;
785                Some(SubtableData {
786                    offset,
787                    kind,
788                    coverage,
789                })
790            }
791            3 => {
792                let backtrack_len = b.read::<u16>(base + 2)? as usize * 2;
793                let input_len = b.read::<u16>(base + backtrack_len + 4)?;
794                if input_len == 0 {
795                    return None;
796                }
797                let coverage = cov(b, base, backtrack_len + 6)?;
798                Some(SubtableData {
799                    kind: SubtableKind::ChainContext3,
800                    offset,
801                    coverage,
802                })
803            }
804            _ => None,
805        },
806        RevChainContext => {
807            let kind = match fmt {
808                1 => SubtableKind::RevChainContext1,
809                _ => return None,
810            };
811            let coverage = cov(b, base, 2)?;
812            Some(SubtableData {
813                offset,
814                kind,
815                coverage,
816            })
817        }
818    }
819}
820
821fn validate_coverage(b: &Bytes, coverage_offset: u32) -> bool {
822    if coverage_offset == 0 {
823        return false;
824    }
825    let base = coverage_offset as usize;
826    let arr = base + 4;
827    match (b.read::<u16>(base), b.read::<u16>(base + 2)) {
828        // Empty subtable coverage is useless, so mark empty coverage subtables as invalid.
829        (Some(_), Some(0)) => false,
830        (Some(1), Some(len)) => b.check_range(arr, len as usize * 2),
831        (Some(2), Some(len)) => b.check_range(arr, len as usize * 6),
832        _ => false,
833    }
834}
835
836pub unsafe fn fast_coverage(b: &Bytes, coverage_offset: u32, glyph_id: u16) -> Option<u16> {
837    let base = coverage_offset as usize;
838    let fmt = b.read_unchecked::<u16>(base);
839    let len = b.read_unchecked::<u16>(base + 2) as usize;
840    let arr = base + 4;
841    if fmt == 1 {
842        let mut l = 0;
843        let mut h = len;
844        while l < h {
845            use core::cmp::Ordering::*;
846            let i = (l + h) / 2;
847            let g = b.read_unchecked::<u16>(arr + i * 2);
848            match glyph_id.cmp(&g) {
849                Less => h = i,
850                Greater => l = i + 1,
851                Equal => return Some(i as u16),
852            }
853        }
854    } else if fmt == 2 {
855        let mut l = 0;
856        let mut h = len;
857        while l < h {
858            let i = (l + h) / 2;
859            let rec = arr + i * 6;
860            let start = b.read_unchecked::<u16>(rec);
861            if glyph_id < start {
862                h = i;
863            } else if glyph_id > b.read_unchecked::<u16>(rec + 2) {
864                l = i + 1;
865            } else {
866                let base = b.read_unchecked::<u16>(rec + 4);
867                return Some(base + glyph_id - start);
868            }
869        }
870    }
871    None
872}
873
874pub fn coverage(b: &Bytes, coverage_offset: u32, glyph_id: u16) -> Option<u16> {
875    if coverage_offset == 0 {
876        return None;
877    }
878    let base = coverage_offset as usize;
879    let fmt = b.read::<u16>(base)?;
880    let len = b.read::<u16>(base + 2)? as usize;
881    let arr = base + 4;
882    if fmt == 1 {
883        if !b.check_range(arr, len * 2) {
884            return None;
885        }
886        let mut l = 0;
887        let mut h = len;
888        while l < h {
889            use core::cmp::Ordering::*;
890            let i = (l + h) / 2;
891            let g = unsafe { b.read_unchecked::<u16>(arr + i * 2) };
892            match glyph_id.cmp(&g) {
893                Less => h = i,
894                Greater => l = i + 1,
895                Equal => return Some(i as u16),
896            }
897        }
898    } else if fmt == 2 {
899        if !b.check_range(arr, len * 6) {
900            return None;
901        }
902        let mut l = 0;
903        let mut h = len;
904        while l < h {
905            let i = (l + h) / 2;
906            let rec = arr + i * 6;
907            let start = unsafe { b.read_unchecked::<u16>(rec) };
908            if glyph_id < start {
909                h = i;
910            } else if glyph_id > unsafe { b.read_unchecked::<u16>(rec + 2) } {
911                l = i + 1;
912            } else {
913                let base = unsafe { b.read_unchecked::<u16>(rec + 4) };
914                return Some(base + (glyph_id - start));
915            }
916        }
917    }
918    None
919}
920
921pub fn classdef(b: &Bytes, classdef_offset: u32, glyph_id: u16) -> u16 {
922    if classdef_offset == 0 {
923        return 0;
924    }
925    let base = classdef_offset as usize;
926    let fmt = b.read_or_default::<u16>(base);
927    if fmt == 1 {
928        let start = b.read_or_default::<u16>(base + 2);
929        let len = b.read_or_default::<u16>(base + 4);
930        let end = start + len - 1;
931        let arr = base + 6;
932        if glyph_id >= start && glyph_id <= end {
933            return b.read_or_default::<u16>(arr + (glyph_id - start) as usize * 2);
934        }
935        return 0;
936    } else if fmt == 2 {
937        let len = b.read_or_default::<u16>(base + 2) as usize;
938        let arr = base + 4;
939        if !b.check_range(arr, len * 6) {
940            return 0;
941        }
942        let mut l = 0;
943        let mut h = len;
944        while l < h {
945            let i = (l + h) / 2;
946            let rec = arr + i * 6;
947            let start = unsafe { b.read_unchecked::<u16>(rec) };
948            if glyph_id < start {
949                h = i;
950            } else if glyph_id > unsafe { b.read_unchecked::<u16>(rec + 2) } {
951                l = i + 1;
952            } else {
953                return unsafe { b.read_unchecked::<u16>(rec + 4) };
954            }
955        }
956    }
957    0
958}