1use crate::GlyphId;
9use crate::parser::{Stream, FromData, FromSlice, LazyArray16};
10
11mod context;
12mod chained_context;
13mod lookup;
14mod layout_table;
15#[cfg(feature = "variable-fonts")] mod feature_variations;
16
17pub use context::*;
18pub use chained_context::*;
19pub use lookup::*;
20pub use layout_table::*;
21#[cfg(feature = "variable-fonts")] pub use feature_variations::*;
22
23#[derive(Clone, Copy, Debug)]
25pub struct RangeRecord {
26    pub start: GlyphId,
28    pub end: GlyphId,
30    pub value: u16,
32}
33
34impl LazyArray16<'_, RangeRecord> {
35    pub fn range(&self, glyph: GlyphId) -> Option<RangeRecord> {
37        self.binary_search_by(|record| {
38            if glyph < record.start {
39                core::cmp::Ordering::Greater
40            } else if glyph <= record.end {
41                core::cmp::Ordering::Equal
42            } else {
43                core::cmp::Ordering::Less
44            }
45        }).map(|p| p.1)
46    }
47}
48
49impl FromData for RangeRecord {
50    const SIZE: usize = 6;
51
52    #[inline]
53    fn parse(data: &[u8]) -> Option<Self> {
54        let mut s = Stream::new(data);
55        Some(RangeRecord {
56            start: s.read::<GlyphId>()?,
57            end: s.read::<GlyphId>()?,
58            value: s.read::<u16>()?,
59        })
60    }
61}
62
63
64#[allow(missing_docs)]
67#[derive(Clone, Copy, Debug)]
68pub enum Coverage<'a> {
69    Format1 {
70        glyphs: LazyArray16<'a, GlyphId>,
72    },
73    Format2 {
74        records: LazyArray16<'a, RangeRecord>,
76    },
77}
78
79impl<'a> FromSlice<'a> for Coverage<'a> {
80    fn parse(data: &'a [u8]) -> Option<Self> {
81        let mut s = Stream::new(data);
82        match s.read::<u16>()? {
83            1 => {
84                let count = s.read::<u16>()?;
85                let glyphs = s.read_array16(count)?;
86                Some(Self::Format1 { glyphs })
87            }
88            2 => {
89                let count = s.read::<u16>()?;
90                let records = s.read_array16(count)?;
91                Some(Self::Format2 { records })
92            }
93            _ => None,
94        }
95    }
96}
97
98impl<'a> Coverage<'a> {
99    pub fn contains(&self, glyph: GlyphId) -> bool {
101        self.get(glyph).is_some()
102    }
103
104    pub fn get(&self, glyph: GlyphId) -> Option<u16> {
106        match self {
107            Self::Format1 { glyphs } => {
108                glyphs.binary_search(&glyph).map(|p| p.0)
109            }
110            Self::Format2 { records } => {
111                let record = records.range(glyph)?;
112                let offset = glyph.0 - record.start.0;
113                record.value.checked_add(offset)
114            }
115        }
116    }
117}
118
119pub type Class = u16;
122
123#[allow(missing_docs)]
126#[derive(Clone, Copy, Debug)]
127pub enum ClassDefinition<'a> {
128    Format1 {
129        start: GlyphId,
130        classes: LazyArray16<'a, Class>,
131    },
132    Format2 {
133        records: LazyArray16<'a, RangeRecord>,
134    },
135}
136
137impl<'a> ClassDefinition<'a> {
138    #[inline]
139    pub(crate) fn parse(data: &'a [u8]) -> Option<Self> {
140        let mut s = Stream::new(data);
141        match s.read::<u16>()? {
142            1 => {
143                let start = s.read::<GlyphId>()?;
144                let count = s.read::<u16>()?;
145                let classes = s.read_array16(count)?;
146                Some(Self::Format1 { start, classes })
147            },
148            2 => {
149                let count = s.read::<u16>()?;
150                let records = s.read_array16(count)?;
151                Some(Self::Format2 { records })
152            },
153            _ => None,
154        }
155    }
156
157    pub fn get(&self, glyph: GlyphId) -> Class {
159        match self {
160            Self::Format1 { start, classes } => {
161                glyph.0.checked_sub(start.0).and_then(|index| classes.get(index))
162            }
163            Self::Format2 { records } => {
164                records.range(glyph).map(|record| record.value)
165            }
166        }.unwrap_or(0)
167    }
168}