pinot/otl/
sub.rs

1use super::{CoverageArray, Covered, Subtable};
2use crate::parse_prelude::*;
3
4/// Single substitution format 1.
5///
6/// <https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#lookuptype-1-single-substitution-subtable>
7#[derive(Copy, Clone, Debug)]
8pub struct SingleSubst1<'a>(pub(super) Subtable<'a>);
9
10impl<'a> SingleSubst1<'a> {
11    /// Returns the replacement for the specified covered glyph.
12    pub fn get(&self, glyph_id: Covered) -> Option<u16> {
13        self.get_impl(glyph_id.glyph_id())
14    }
15
16    /// Single delta value applied to all glyphs covered by the subtable.
17    pub fn delta(&self) -> Option<i16> {
18        let (data, offset) = self.0.data_and_offset();
19        data.read_i16(offset + 4)
20    }
21
22    /// Invokes the specified closure with all substitutions in the subtable.
23    pub fn substs_with(&self, mut f: impl FnMut(GlyphId, GlyphId) -> bool) -> Option<bool> {
24        self.0.coverage().indices_with(|glyph_id, _| {
25            if let Some(subst) = self.get_impl(glyph_id) {
26                if !f(glyph_id, subst) {
27                    return false;
28                }
29            }
30            true
31        })
32    }
33
34    fn get_impl(&self, glyph_id: u16) -> Option<u16> {
35        let delta = self.delta()? as i32;
36        Some((glyph_id as i32 + delta) as u16)
37    }
38}
39
40/// Single substitution format 2.
41///
42/// <https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2>
43#[derive(Copy, Clone, Debug)]
44pub struct SingleSubst2<'a>(pub(super) Subtable<'a>);
45
46impl<'a> SingleSubst2<'a> {
47    /// Returns the replacement for the specified covered glyph.
48    pub fn get(&self, glyph_id: Covered) -> Option<u16> {
49        self.get_impl(glyph_id.coverage_index())
50    }
51
52    /// Invokes the specified closure with all substitutions in the subtable.
53    pub fn substs_with(&self, mut f: impl FnMut(GlyphId, GlyphId) -> bool) -> Option<bool> {
54        self.0.coverage().indices_with(|glyph_id, coverage| {
55            if let Some(subst) = self.get_impl(coverage) {
56                if !f(glyph_id, subst) {
57                    return false;
58                }
59            }
60            true
61        })
62    }
63
64    fn get_impl(&self, coverage: u16) -> Option<u16> {
65        let array_base = self.0.record.offset as usize + 6;
66        self.0
67            .data()
68            .read::<u16>(array_base + coverage as usize * 2)
69    }
70}
71
72/// Multiple substitution format 1.
73///
74/// <https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#21-multiple-substitution-format-1>
75#[derive(Copy, Clone, Debug)]
76pub struct MultipleSubst1<'a>(pub(super) Subtable<'a>);
77
78impl<'a> MultipleSubst1<'a> {
79    /// Returns the replacement sequence for the specified covered glyph.
80    pub fn get(&self, glyph_id: Covered) -> Option<Slice<'a, GlyphId>> {
81        self.get_impl(glyph_id.coverage_index())
82    }
83
84    /// Invokes the specified closure with all substitutions in the subtable.
85    pub fn substs_with(
86        &self,
87        mut f: impl FnMut(GlyphId, Slice<'a, GlyphId>) -> bool,
88    ) -> Option<bool> {
89        self.0.coverage().indices_with(|glyph_id, coverage| {
90            if let Some(subst) = self.get_impl(coverage) {
91                if !f(glyph_id, subst) {
92                    return false;
93                }
94            }
95            true
96        })
97    }
98
99    fn get_impl(&self, coverage: u16) -> Option<Slice<'a, GlyphId>> {
100        let data = self.0.data();
101        let base = self.0.record.offset as usize;
102        let array_base = base + data.read::<u16>(base + 6 + coverage as usize * 2)? as usize;
103        let array_len = data.read::<u16>(array_base)? as usize;
104        data.read_slice::<u16>(array_base + 2, array_len)
105    }
106}
107
108/// Alternate substitution format 1.
109///
110/// <https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#lookuptype-3-alternate-substitution-subtable>
111#[derive(Copy, Clone, Debug)]
112pub struct AlternateSubst1<'a>(pub(super) Subtable<'a>);
113
114impl<'a> AlternateSubst1<'a> {
115    /// Returns the replacement candidates for the specified covered glyph.
116    pub fn get(&self, glyph_id: Covered) -> Option<Slice<'a, GlyphId>> {
117        MultipleSubst1(self.0).get(glyph_id)
118    }
119
120    /// Invokes the specified closure with all alternates in the subtable.
121    pub fn alternates_with(
122        &self,
123        f: impl FnMut(GlyphId, Slice<'a, GlyphId>) -> bool,
124    ) -> Option<bool> {
125        MultipleSubst1(self.0).substs_with(f)
126    }
127}
128
129/// Ligature substitution format 1.
130///
131/// <https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#41-ligature-substitution-format-1>
132#[derive(Copy, Clone, Debug)]
133pub struct LigatureSubst1<'a>(pub(super) Subtable<'a>);
134
135impl<'a> LigatureSubst1<'a> {
136    /// Returns an iterator over the ligatures beginning with the specified covered glyph.
137    pub fn get(&self, covered: Covered) -> Option<Ligatures<'a>> {
138        self.get_impl(covered.glyph_id(), covered.coverage_index())
139    }
140
141    /// Invokes the specified closure with all ligatures in the subtable.
142    pub fn ligatures_with(&self, mut f: impl FnMut(Ligature<'a>) -> bool) -> Option<bool> {
143        self.0.coverage().indices_with(|glyph_id, coverage| {
144            if let Some(ligatures) = self.get_impl(glyph_id, coverage) {
145                for ligature in ligatures {
146                    if !f(ligature) {
147                        return false;
148                    }
149                }
150            }
151            true
152        })
153    }
154
155    fn get_impl(&self, glyph_id: u16, coverage: u16) -> Option<Ligatures<'a>> {
156        let data = self.0.data();
157        let base = self.0.record.offset as usize;
158        let set_base = base + data.read::<u16>(base + 6 + coverage as usize * 2)? as usize;
159        let len = data.read::<u16>(set_base)? as usize;
160        Some(Ligatures {
161            data: Buffer::with_offset(data.data(), set_base)?,
162            first_component: glyph_id,
163            len,
164            pos: 0,
165        })
166    }
167}
168
169/// Iterator over the set of ligatures for an initial glyph.
170#[derive(Copy, Clone)]
171pub struct Ligatures<'a> {
172    data: Buffer<'a>,
173    first_component: u16,
174    len: usize,
175    pos: usize,
176}
177
178impl<'a> Iterator for Ligatures<'a> {
179    type Item = Ligature<'a>;
180
181    fn next(&mut self) -> Option<Self::Item> {
182        if self.pos >= self.len {
183            return None;
184        }
185        let pos = self.pos;
186        self.pos += 1;
187        let base = self.data.read::<u16>(2 + pos * 2)? as usize;
188        let ligature = self.data.read::<u16>(base)?;
189        let len = (self.data.read::<u16>(base + 2)? as usize).saturating_sub(1);
190        let trailing_components = self.data.read_slice::<u16>(base + 4, len)?;
191        Some(Ligature {
192            first_component: self.first_component,
193            trailing_components,
194            ligature,
195        })
196    }
197}
198
199/// Ligature glyph and components.
200#[derive(Copy, Clone)]
201pub struct Ligature<'a> {
202    /// Initial component of the ligature.
203    pub first_component: GlyphId,
204    /// Sequence of identifers that represent the trailing component glyphs.
205    pub trailing_components: Slice<'a, GlyphId>,
206    /// Identifier of the ligature glyph.
207    pub ligature: GlyphId,
208}
209
210/// Reverse chained context format 1.
211///
212/// <https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#lookuptype-8-reverse-chaining-contextual-single-substitution-subtable>
213#[derive(Copy, Clone, Debug)]
214pub struct RevChainContext1<'a>(pub(super) Subtable<'a>);
215
216impl<'a> RevChainContext1<'a> {
217    pub fn rule(&self) -> Option<RevChainContextRule<'a>> {
218        let data = *self.0.data();
219        let base = self.0.record.offset;
220        let mut s = data.cursor_at(base as usize + 4)?;
221        let backtrack_count = s.read::<u16>()? as usize;
222        let backtrack = CoverageArray::new(data, base, s.read_slice::<u16>(backtrack_count)?);
223        let lookahead_count = s.read::<u16>()? as usize;
224        let lookahead = CoverageArray::new(data, base, s.read_slice::<u16>(lookahead_count)?);
225        let substitution_count = s.read::<u16>()? as usize;
226        let substitutions = s.read_slice::<u16>(substitution_count)?;
227        Some(RevChainContextRule {
228            backtrack,
229            lookahead,
230            substitutions,
231        })
232    }
233}
234
235/// Rule for a reverse chained context subtable.
236#[derive(Copy, Clone)]
237pub struct RevChainContextRule<'a> {
238    /// Backtrack coverage sequence.
239    pub backtrack: CoverageArray<'a>,
240    /// Lookahead coverage sequence.
241    pub lookahead: CoverageArray<'a>,
242    /// Substitution glyph array, ordered by primary coverage index.
243    pub substitutions: Slice<'a, GlyphId>,
244}