1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use crate::parser::{FromSlice, LazyArray16, LazyOffsetArray16, Stream};
use super::{ClassDefinition, Coverage, SequenceLookupRecord};

/// A [Chained Contextual Lookup Subtable](
/// https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chseqctxt1).
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
pub enum ChainedContextLookup<'a> {
    /// Simple glyph contexts.
    Format1 {
        coverage: Coverage<'a>,
        sets: ChainedSequenceRuleSets<'a>,
    },
    /// Class-based glyph contexts.
    Format2 {
        coverage: Coverage<'a>,
        backtrack_classes: ClassDefinition<'a>,
        input_classes: ClassDefinition<'a>,
        lookahead_classes: ClassDefinition<'a>,
        sets: ChainedSequenceRuleSets<'a>,
    },
    /// Coverage-based glyph contexts.
    Format3 {
        coverage: Coverage<'a>,
        backtrack_coverages: LazyOffsetArray16<'a, Coverage<'a>>,
        input_coverages: LazyOffsetArray16<'a, Coverage<'a>>,
        lookahead_coverages: LazyOffsetArray16<'a, Coverage<'a>>,
        lookups: LazyArray16<'a, SequenceLookupRecord>,
    },
}

impl<'a> ChainedContextLookup<'a> {
    pub(crate) fn parse(data: &'a [u8]) -> Option<Self> {
        let mut s = Stream::new(data);
        match s.read::<u16>()? {
            1 => {
                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
                let count = s.read::<u16>()?;
                let offsets = s.read_array16(count)?;
                Some(Self::Format1 {
                    coverage,
                    sets: ChainedSequenceRuleSets::new(data, offsets),
                })
            }
            2 => {
                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
                let backtrack_classes = ClassDefinition::parse(s.read_at_offset16(data)?)?;
                let input_classes = ClassDefinition::parse(s.read_at_offset16(data)?)?;
                let lookahead_classes = ClassDefinition::parse(s.read_at_offset16(data)?)?;
                let count = s.read::<u16>()?;
                let offsets = s.read_array16(count)?;
                Some(Self::Format2 {
                    coverage,
                    backtrack_classes,
                    input_classes,
                    lookahead_classes,
                    sets: LazyOffsetArray16::new(data, offsets),
                })
            }
            3 => {
                let backtrack_count = s.read::<u16>()?;
                let backtrack_coverages = s.read_array16(backtrack_count)?;
                let input_count = s.read::<u16>()?;
                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
                let input_coverages = s.read_array16(input_count.checked_sub(1)?)?;
                let lookahead_count = s.read::<u16>()?;
                let lookahead_coverages = s.read_array16(lookahead_count)?;
                let lookup_count = s.read::<u16>()?;
                let lookups = s.read_array16(lookup_count)?;
                Some(Self::Format3 {
                    coverage,
                    backtrack_coverages: LazyOffsetArray16::new(data, backtrack_coverages),
                    input_coverages: LazyOffsetArray16::new(data, input_coverages),
                    lookahead_coverages: LazyOffsetArray16::new(data, lookahead_coverages),
                    lookups,
                })
            }
            _ => None,
        }
    }

    /// Returns the subtable coverage.
    #[inline]
    pub fn coverage(&self) -> Coverage<'a> {
        match self {
            Self::Format1 { coverage, .. } => *coverage,
            Self::Format2 { coverage, .. } => *coverage,
            Self::Format3 { coverage, .. } => *coverage,
        }
    }
}

/// A list of [`ChainedSequenceRule`] sets.
pub type ChainedSequenceRuleSets<'a> = LazyOffsetArray16<'a, ChainedSequenceRuleSet<'a>>;

/// A set of [`ChainedSequenceRule`].
pub type ChainedSequenceRuleSet<'a> = LazyOffsetArray16<'a, ChainedSequenceRule<'a>>;

impl<'a> FromSlice<'a> for ChainedSequenceRuleSet<'a> {
    fn parse(data: &'a [u8]) -> Option<Self> {
        Self::parse(data)
    }
}

/// A [Chained Sequence Rule](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-1-simple-glyph-contexts).
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
pub struct ChainedSequenceRule<'a> {
    /// Contains either glyph IDs or glyph Classes.
    pub backtrack: LazyArray16<'a, u16>,
    pub input: LazyArray16<'a, u16>,
    /// Contains either glyph IDs or glyph Classes.
    pub lookahead: LazyArray16<'a, u16>,
    pub lookups: LazyArray16<'a, SequenceLookupRecord>,
}

impl<'a> FromSlice<'a> for ChainedSequenceRule<'a> {
    fn parse(data: &'a [u8]) -> Option<Self> {
        let mut s = Stream::new(data);
        let backtrack_count = s.read::<u16>()?;
        let backtrack = s.read_array16(backtrack_count)?;
        let input_count = s.read::<u16>()?;
        let input = s.read_array16(input_count.checked_sub(1)?)?;
        let lookahead_count = s.read::<u16>()?;
        let lookahead = s.read_array16(lookahead_count)?;
        let lookup_count = s.read::<u16>()?;
        let lookups = s.read_array16(lookup_count)?;
        Some(Self { backtrack, input, lookahead, lookups })
    }
}