1use crate::GlyphId;
8use crate::opentype_layout::{ChainedContextLookup, ContextLookup, Coverage, LookupSubtable};
9use crate::parser::{FromSlice, LazyArray16, LazyOffsetArray16, Stream};
10
11#[allow(missing_docs)]
13#[derive(Clone, Copy, Debug)]
14pub enum SingleSubstitution<'a> {
15 Format1 {
16 coverage: Coverage<'a>,
17 delta: i16,
18 },
19 Format2 {
20 coverage: Coverage<'a>,
21 substitutes: LazyArray16<'a, GlyphId>,
22 },
23}
24
25impl<'a> SingleSubstitution<'a> {
26 fn parse(data: &'a [u8]) -> Option<Self> {
27 let mut s = Stream::new(data);
28 match s.read::<u16>()? {
29 1 => {
30 let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
31 let delta = s.read::<i16>()?;
32 Some(Self::Format1 { coverage, delta })
33 }
34 2 => {
35 let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
36 let count = s.read::<u16>()?;
37 let substitutes = s.read_array16(count)?;
38 Some(Self::Format2 { coverage, substitutes })
39 }
40 _ => None,
41 }
42 }
43
44 #[inline]
46 pub fn coverage(&self) -> Coverage<'a> {
47 match self {
48 Self::Format1 { coverage, .. } => *coverage,
49 Self::Format2 { coverage, .. } => *coverage,
50 }
51 }
52}
53
54
55#[derive(Clone, Copy, Debug)]
58pub struct Sequence<'a> {
59 pub substitutes: LazyArray16<'a, GlyphId>,
61}
62
63impl<'a> FromSlice<'a> for Sequence<'a> {
64 fn parse(data: &'a [u8]) -> Option<Self> {
65 let mut s = Stream::new(data);
66 let count = s.read::<u16>()?;
67 let substitutes = s.read_array16(count)?;
68 Some(Self { substitutes })
69 }
70}
71
72pub type SequenceList<'a> = LazyOffsetArray16<'a, Sequence<'a>>;
74
75#[allow(missing_docs)]
77#[derive(Clone, Copy, Debug)]
78pub struct MultipleSubstitution<'a> {
79 pub coverage: Coverage<'a>,
80 pub sequences: SequenceList<'a>,
81}
82
83impl<'a> MultipleSubstitution<'a> {
84 fn parse(data: &'a [u8]) -> Option<Self> {
85 let mut s = Stream::new(data);
86 match s.read::<u16>()? {
87 1 => {
88 let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
89 let count = s.read::<u16>()?;
90 let offsets = s.read_array16(count)?;
91 Some(Self {
92 coverage,
93 sequences: SequenceList::new(data, offsets),
94 })
95 }
96 _ => None,
97 }
98 }
99}
100
101
102#[derive(Clone, Copy, Debug)]
105pub struct AlternateSet<'a> {
106 pub alternates: LazyArray16<'a, GlyphId>,
108}
109
110impl<'a> FromSlice<'a> for AlternateSet<'a> {
111 fn parse(data: &'a [u8]) -> Option<Self> {
112 let mut s = Stream::new(data);
113 let count = s.read::<u16>()?;
114 let alternates = s.read_array16(count)?;
115 Some(Self { alternates })
116 }
117}
118
119pub type AlternateSets<'a> = LazyOffsetArray16<'a, AlternateSet<'a>>;
121
122#[allow(missing_docs)]
124#[derive(Clone, Copy, Debug)]
125pub struct AlternateSubstitution<'a> {
126 pub coverage: Coverage<'a>,
127 pub alternate_sets: AlternateSets<'a>,
128}
129
130impl<'a> AlternateSubstitution<'a> {
131 fn parse(data: &'a [u8]) -> Option<Self> {
132 let mut s = Stream::new(data);
133 match s.read::<u16>()? {
134 1 => {
135 let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
136 let count = s.read::<u16>()?;
137 let offsets = s.read_array16(count)?;
138 Some(Self {
139 coverage,
140 alternate_sets: AlternateSets::new(data, offsets),
141 })
142 }
143 _ => None,
144 }
145 }
146}
147
148
149#[derive(Clone, Copy, Debug)]
151pub struct Ligature<'a> {
152 pub glyph: GlyphId,
154 pub components: LazyArray16<'a, GlyphId>,
156}
157
158impl<'a> FromSlice<'a> for Ligature<'a> {
159 fn parse(data: &'a [u8]) -> Option<Self> {
160 let mut s = Stream::new(data);
161 let glyph = s.read::<GlyphId>()?;
162 let count = s.read::<u16>()?;
163 let components = s.read_array16(count.checked_sub(1)?)?;
164 Some(Self { glyph, components })
165 }
166}
167
168pub type LigatureSet<'a> = LazyOffsetArray16<'a, Ligature<'a>>;
170
171impl<'a> FromSlice<'a> for LigatureSet<'a> {
172 fn parse(data: &'a [u8]) -> Option<Self> {
173 Self::parse(data)
174 }
175}
176
177pub type LigatureSets<'a> = LazyOffsetArray16<'a, LigatureSet<'a>>;
179
180#[allow(missing_docs)]
182#[derive(Clone, Copy, Debug)]
183pub struct LigatureSubstitution<'a> {
184 pub coverage: Coverage<'a>,
185 pub ligature_sets: LigatureSets<'a>,
186}
187
188impl<'a> LigatureSubstitution<'a> {
189 fn parse(data: &'a [u8]) -> Option<Self> {
190 let mut s = Stream::new(data);
191 match s.read::<u16>()? {
192 1 => {
193 let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
194 let count = s.read::<u16>()?;
195 let offsets = s.read_array16(count)?;
196 Some(Self {
197 coverage,
198 ligature_sets: LigatureSets::new(data, offsets),
199 })
200 }
201 _ => None,
202 }
203 }
204}
205
206
207#[allow(missing_docs)]
210#[derive(Clone, Copy, Debug)]
211pub struct ReverseChainSingleSubstitution<'a> {
212 pub coverage: Coverage<'a>,
213 pub backtrack_coverages: LazyOffsetArray16<'a, Coverage<'a>>,
214 pub lookahead_coverages: LazyOffsetArray16<'a, Coverage<'a>>,
215 pub substitutes: LazyArray16<'a, GlyphId>,
216}
217
218impl<'a> ReverseChainSingleSubstitution<'a> {
219 fn parse(data: &'a [u8]) -> Option<Self> {
220 let mut s = Stream::new(data);
221 match s.read::<u16>()? {
222 1 => {
223 let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
224 let backtrack_count = s.read::<u16>()?;
225 let backtrack_coverages = s.read_array16(backtrack_count)?;
226 let lookahead_count = s.read::<u16>()?;
227 let lookahead_coverages = s.read_array16(lookahead_count)?;
228 let substitute_count = s.read::<u16>()?;
229 let substitutes = s.read_array16(substitute_count)?;
230 Some(Self {
231 coverage,
232 backtrack_coverages: LazyOffsetArray16::new(data, backtrack_coverages),
233 lookahead_coverages: LazyOffsetArray16::new(data, lookahead_coverages),
234 substitutes,
235 })
236 }
237 _ => None,
238 }
239 }
240}
241
242
243#[allow(missing_docs)]
247#[derive(Clone, Copy, Debug)]
248pub enum SubstitutionSubtable<'a> {
249 Single(SingleSubstitution<'a>),
250 Multiple(MultipleSubstitution<'a>),
251 Alternate(AlternateSubstitution<'a>),
252 Ligature(LigatureSubstitution<'a>),
253 Context(ContextLookup<'a>),
254 ChainContext(ChainedContextLookup<'a>),
255 ReverseChainSingle(ReverseChainSingleSubstitution<'a>),
256}
257
258impl<'a> LookupSubtable<'a> for SubstitutionSubtable<'a> {
259 fn parse(data: &'a [u8], kind: u16) -> Option<Self> {
260 match kind {
261 1 => SingleSubstitution::parse(data).map(Self::Single),
262 2 => MultipleSubstitution::parse(data).map(Self::Multiple),
263 3 => AlternateSubstitution::parse(data).map(Self::Alternate),
264 4 => LigatureSubstitution::parse(data).map(Self::Ligature),
265 5 => ContextLookup::parse(data).map(Self::Context),
266 6 => ChainedContextLookup::parse(data).map(Self::ChainContext),
267 7 => crate::ggg::parse_extension_lookup(data, Self::parse),
268 8 => ReverseChainSingleSubstitution::parse(data).map(Self::ReverseChainSingle),
269 _ => None,
270 }
271 }
272}
273
274impl<'a> SubstitutionSubtable<'a> {
275 #[inline]
277 pub fn coverage(&self) -> Coverage<'a> {
278 match self {
279 Self::Single(t) => t.coverage(),
280 Self::Multiple(t) => t.coverage,
281 Self::Alternate(t) => t.coverage,
282 Self::Ligature(t) => t.coverage,
283 Self::Context(t) => t.coverage(),
284 Self::ChainContext(t) => t.coverage(),
285 Self::ReverseChainSingle(t) => t.coverage,
286 }
287 }
288
289 #[inline]
291 pub fn is_reverse(&self) -> bool {
292 matches!(self, Self::ReverseChainSingle(_))
293 }
294}