ttf_parser/tables/
morx.rs

1//! An [Extended Glyph Metamorphosis Table](
2//! https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html) implementation.
3
4// Note: We do not have tests for this table because it has a very complicated structure.
5// Specifically, the State Machine Tables. I have no idea how to generate them.
6// And all fonts that use this table are mainly Apple one, so we cannot use them for legal reasons.
7//
8// On the other hand, this table is tested indirectly by https://github.com/RazrFalcon/rustybuzz
9// And it has like 170 tests. Which is pretty good.
10// Therefore after applying any changes to this table,
11// you have to check that all rustybuzz tests are still passing.
12
13use core::num::NonZeroU16;
14
15use crate::{aat, GlyphId};
16use crate::parser::{Stream, FromData, LazyArray32, NumFrom, Offset32, Offset};
17
18/// The feature table is used to compute the sub-feature flags
19/// for a list of requested features and settings.
20#[derive(Clone, Copy, Debug)]
21pub struct Feature {
22    /// The type of feature.
23    pub kind: u16,
24    /// The feature's setting (aka selector).
25    pub setting: u16,
26    /// Flags for the settings that this feature and setting enables.
27    pub enable_flags: u32,
28    /// Complement of flags for the settings that this feature and setting disable.
29    pub disable_flags: u32,
30}
31
32impl FromData for Feature {
33    const SIZE: usize = 12;
34
35    #[inline]
36    fn parse(data: &[u8]) -> Option<Self> {
37        let mut s = Stream::new(data);
38        Some(Feature {
39            kind: s.read::<u16>()?,
40            setting: s.read::<u16>()?,
41            enable_flags: s.read::<u32>()?,
42            disable_flags: s.read::<u32>()?,
43        })
44    }
45}
46
47
48/// A contextual subtable state table trailing data.
49#[derive(Clone, Copy, Debug)]
50pub struct ContextualEntryData {
51    /// A mark index.
52    pub mark_index: u16,
53    /// A current index.
54    pub current_index: u16,
55}
56
57impl FromData for ContextualEntryData {
58    const SIZE: usize = 4;
59
60    #[inline]
61    fn parse(data: &[u8]) -> Option<Self> {
62        let mut s = Stream::new(data);
63        Some(ContextualEntryData {
64            mark_index: s.read::<u16>()?,
65            current_index: s.read::<u16>()?,
66        })
67    }
68}
69
70/// A contextual subtable.
71#[derive(Clone)]
72pub struct ContextualSubtable<'a> {
73    /// The contextual glyph substitution state table.
74    pub state: aat::ExtendedStateTable<'a, ContextualEntryData>,
75    offsets_data: &'a [u8],
76    offsets: LazyArray32<'a, Offset32>,
77    number_of_glyphs: NonZeroU16,
78}
79
80impl<'a> ContextualSubtable<'a> {
81    fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
82        let mut s = Stream::new(data);
83
84        let state = aat::ExtendedStateTable::parse(number_of_glyphs, &mut s)?;
85
86        // While the spec clearly states that this is an
87        // 'offset from the beginning of the state subtable',
88        // it's actually not. Subtable header should not be included.
89        let offset = s.read::<Offset32>()?.to_usize();
90
91        // The offsets list is unsized.
92        let offsets_data = data.get(offset..)?;
93        let offsets = LazyArray32::<Offset32>::new(offsets_data);
94
95        Some(ContextualSubtable {
96            state,
97            offsets_data,
98            offsets,
99            number_of_glyphs,
100        })
101    }
102
103    /// Returns a [Lookup](aat::Lookup) at index.
104    pub fn lookup(&self, index: u32) -> Option<aat::Lookup<'a>> {
105        let offset = self.offsets.get(index)?.to_usize();
106        let lookup_data = self.offsets_data.get(offset..)?;
107        aat::Lookup::parse(self.number_of_glyphs, lookup_data)
108    }
109}
110
111impl core::fmt::Debug for ContextualSubtable<'_> {
112    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
113        write!(f, "ContextualSubtable {{ ... }}")
114    }
115}
116
117
118/// A ligature subtable.
119#[derive(Clone, Debug)]
120pub struct LigatureSubtable<'a> {
121    /// A state table.
122    pub state: aat::ExtendedStateTable<'a, u16>,
123    /// Ligature actions.
124    pub ligature_actions: LazyArray32<'a, u32>,
125    /// Ligature components.
126    pub components: LazyArray32<'a, u16>,
127    /// Ligatures.
128    pub ligatures: LazyArray32<'a, GlyphId>,
129}
130
131impl<'a> LigatureSubtable<'a> {
132    fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
133        let mut s = Stream::new(data);
134
135        let state = aat::ExtendedStateTable::parse(number_of_glyphs, &mut s)?;
136
137        // Offset are from `ExtendedStateTable`/`data`, not from subtable start.
138        let ligature_action_offset = s.read::<Offset32>()?.to_usize();
139        let component_offset = s.read::<Offset32>()?.to_usize();
140        let ligature_offset = s.read::<Offset32>()?.to_usize();
141
142        // All three arrays are unsized, so we're simply reading/mapping all the data past offset.
143        let ligature_actions = LazyArray32::<u32>::new(data.get(ligature_action_offset..)?);
144        let components = LazyArray32::<u16>::new(data.get(component_offset..)?);
145        let ligatures = LazyArray32::<GlyphId>::new(data.get(ligature_offset..)?);
146
147        Some(LigatureSubtable {
148            state,
149            ligature_actions,
150            components,
151            ligatures,
152        })
153    }
154}
155
156
157/// A contextual subtable state table trailing data.
158#[derive(Clone, Copy, Debug)]
159pub struct InsertionEntryData {
160    /// A current insert index.
161    pub current_insert_index: u16,
162    /// A marked insert index.
163    pub marked_insert_index: u16,
164}
165
166impl FromData for InsertionEntryData {
167    const SIZE: usize = 4;
168
169    #[inline]
170    fn parse(data: &[u8]) -> Option<Self> {
171        let mut s = Stream::new(data);
172        Some(InsertionEntryData {
173            current_insert_index: s.read::<u16>()?,
174            marked_insert_index: s.read::<u16>()?,
175        })
176    }
177}
178
179
180/// An insertion subtable.
181#[derive(Clone, Debug)]
182pub struct InsertionSubtable<'a> {
183    /// A state table.
184    pub state: aat::ExtendedStateTable<'a, InsertionEntryData>,
185    /// Insertion glyphs.
186    pub glyphs: LazyArray32<'a, GlyphId>,
187}
188
189impl<'a> InsertionSubtable<'a> {
190    fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
191        let mut s = Stream::new(data);
192        let state = aat::ExtendedStateTable::parse(number_of_glyphs, &mut s)?;
193        let offset = s.read::<Offset32>()?.to_usize();
194
195        // TODO: unsized array?
196        // The list is unsized.
197        let glyphs = LazyArray32::<GlyphId>::new(data.get(offset..)?);
198
199        Some(InsertionSubtable {
200            state,
201            glyphs,
202        })
203    }
204}
205
206
207/// A subtable kind.
208#[allow(missing_docs)]
209#[derive(Clone, Debug)]
210pub enum SubtableKind<'a> {
211    Rearrangement(aat::ExtendedStateTable<'a, ()>),
212    Contextual(ContextualSubtable<'a>),
213    Ligature(LigatureSubtable<'a>),
214    NonContextual(aat::Lookup<'a>),
215    Insertion(InsertionSubtable<'a>),
216}
217
218
219/// A subtable coverage.
220#[derive(Clone, Copy, Debug)]
221pub struct Coverage(u8);
222
223impl Coverage {
224    /// If true, this subtable will process glyphs in logical order
225    /// (or reverse logical order if [`is_vertical`](Self::is_vertical) is also true).
226    #[inline] pub fn is_logical(self) -> bool { self.0 & 0x10 != 0 }
227    /// If true, this subtable will be applied to both horizontal and vertical text
228    /// ([`is_vertical`](Self::is_vertical) should be ignored).
229    #[inline] pub fn is_all_directions(self) -> bool { self.0 & 0x20 != 0 }
230    /// If true, this subtable will process glyphs in descending order.
231    #[inline] pub fn is_backwards(self) -> bool { self.0 & 0x40 != 0 }
232    /// If true, this subtable will only be applied to vertical text.
233    #[inline] pub fn is_vertical(self) -> bool { self.0 & 0x80 != 0 }
234}
235
236
237/// A subtable in a metamorphosis chain.
238#[derive(Clone, Debug)]
239pub struct Subtable<'a> {
240    /// A subtable kind.
241    pub kind: SubtableKind<'a>,
242    /// A subtable coverage.
243    pub coverage: Coverage,
244    /// Subtable feature flags.
245    pub feature_flags: u32,
246}
247
248
249/// A list of subtables in a metamorphosis chain.
250///
251/// The internal data layout is not designed for random access,
252/// therefore we're not providing the `get()` method and only an iterator.
253#[derive(Clone, Copy)]
254pub struct Subtables<'a> {
255    count: u32,
256    data: &'a [u8],
257    number_of_glyphs: NonZeroU16,
258}
259
260impl<'a> IntoIterator for Subtables<'a> {
261    type Item = Subtable<'a>;
262    type IntoIter = SubtablesIter<'a>;
263
264    #[inline]
265    fn into_iter(self) -> Self::IntoIter {
266        SubtablesIter {
267            index: 0,
268            count: self.count,
269            stream: Stream::new(self.data),
270            number_of_glyphs: self.number_of_glyphs,
271        }
272    }
273}
274
275impl core::fmt::Debug for Subtables<'_> {
276    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
277        write!(f, "Subtables {{ ... }}")
278    }
279}
280
281
282/// An iterator over a metamorphosis chain subtables.
283#[allow(missing_debug_implementations)]
284#[derive(Clone, Copy)]
285pub struct SubtablesIter<'a> {
286    index: u32,
287    count: u32,
288    stream: Stream<'a>,
289    number_of_glyphs: NonZeroU16,
290}
291
292impl<'a> Iterator for SubtablesIter<'a> {
293    type Item = Subtable<'a>;
294
295    fn next(&mut self) -> Option<Self::Item> {
296        if self.index == self.count {
297            return None;
298        }
299
300        let s = &mut self.stream;
301        if s.at_end() {
302            return None;
303        }
304
305        let len = s.read::<u32>()?;
306        let coverage = Coverage(s.read::<u8>()?);
307        s.skip::<u16>(); // reserved
308        let kind = s.read::<u8>()?;
309        let feature_flags = s.read::<u32>()?;
310
311        const HEADER_LEN: usize = 12;
312        let len = usize::num_from(len).checked_sub(HEADER_LEN)?;
313        let subtables_data = s.read_bytes(len)?;
314
315        let kind = match kind {
316            0 => {
317                let mut s = Stream::new(subtables_data);
318                let table = aat::ExtendedStateTable::parse(self.number_of_glyphs, &mut s)?;
319                SubtableKind::Rearrangement(table)
320            }
321            1 => {
322                let table = ContextualSubtable::parse(self.number_of_glyphs, subtables_data)?;
323                SubtableKind::Contextual(table)
324            }
325            2 => {
326                let table = LigatureSubtable::parse(self.number_of_glyphs, subtables_data)?;
327                SubtableKind::Ligature(table)
328            }
329            // 3 - reserved
330            4 => {
331                SubtableKind::NonContextual(aat::Lookup::parse(self.number_of_glyphs, subtables_data)?)
332            }
333            5 => {
334                let table = InsertionSubtable::parse(self.number_of_glyphs, subtables_data)?;
335                SubtableKind::Insertion(table)
336            }
337            _ => return None,
338        };
339
340        Some(Subtable {
341            kind,
342            coverage,
343            feature_flags,
344        })
345    }
346}
347
348
349/// A metamorphosis chain.
350#[derive(Clone, Copy, Debug)]
351pub struct Chain<'a> {
352    /// Default chain features.
353    pub default_flags: u32,
354    /// A list of chain features.
355    pub features: LazyArray32<'a, Feature>,
356    /// A list of chain subtables.
357    pub subtables: Subtables<'a>,
358}
359
360
361/// A list of metamorphosis chains.
362///
363/// The internal data layout is not designed for random access,
364/// therefore we're not providing the `get()` method and only an iterator.
365#[derive(Clone, Copy)]
366pub struct Chains<'a> {
367    data: &'a [u8],
368    count: u32,
369    number_of_glyphs: NonZeroU16,
370}
371
372impl<'a> Chains<'a> {
373    fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
374        let mut s = Stream::new(data);
375
376        s.skip::<u16>(); // version
377        s.skip::<u16>(); // reserved
378        let count = s.read::<u32>()?;
379
380        Some(Chains {
381            count,
382            data: s.tail()?,
383            number_of_glyphs,
384        })
385    }
386}
387
388impl<'a> IntoIterator for Chains<'a> {
389    type Item = Chain<'a>;
390    type IntoIter = ChainsIter<'a>;
391
392    #[inline]
393    fn into_iter(self) -> Self::IntoIter {
394        ChainsIter {
395            index: 0,
396            count: self.count,
397            stream: Stream::new(self.data),
398            number_of_glyphs: self.number_of_glyphs,
399        }
400    }
401}
402
403impl core::fmt::Debug for Chains<'_> {
404    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
405        write!(f, "Chains {{ ... }}")
406    }
407}
408
409/// An iterator over metamorphosis chains.
410#[allow(missing_debug_implementations)]
411#[derive(Clone, Copy)]
412pub struct ChainsIter<'a> {
413    index: u32,
414    count: u32,
415    stream: Stream<'a>,
416    number_of_glyphs: NonZeroU16,
417}
418
419impl<'a> Iterator for ChainsIter<'a> {
420    type Item = Chain<'a>;
421
422    fn next(&mut self) -> Option<Self::Item> {
423        if self.index == self.count {
424            return None;
425        }
426
427        if self.stream.at_end() {
428            return None;
429        }
430
431        let default_flags = self.stream.read::<u32>()?;
432        let len = self.stream.read::<u32>()?;
433        let features_count = self.stream.read::<u32>()?;
434        let subtables_count = self.stream.read::<u32>()?;
435
436        let features = self.stream.read_array32::<Feature>(features_count)?;
437
438        const HEADER_LEN: usize = 16;
439        let len = usize::num_from(len)
440            .checked_sub(HEADER_LEN)?
441            .checked_sub(Feature::SIZE * usize::num_from(features_count))?;
442
443        let subtables_data = self.stream.read_bytes(len)?;
444
445        let subtables = Subtables {
446            data: subtables_data,
447            count: subtables_count,
448            number_of_glyphs: self.number_of_glyphs,
449        };
450
451        Some(Chain {
452            default_flags,
453            features,
454            subtables,
455        })
456    }
457}
458
459
460/// An [Extended Glyph Metamorphosis Table](
461/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html).
462///
463/// Subtable Glyph Coverage used by morx v3 is not supported.
464#[derive(Clone)]
465pub struct Table<'a> {
466    /// A list of metamorphosis chains.
467    pub chains: Chains<'a>,
468}
469
470impl core::fmt::Debug for Table<'_> {
471    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
472        write!(f, "Table {{ ... }}")
473    }
474}
475
476impl<'a> Table<'a> {
477    /// Parses a table from raw data.
478    ///
479    /// `number_of_glyphs` is from the `maxp` table.
480    pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
481        Chains::parse(number_of_glyphs, data).map(|chains| Self { chains })
482    }
483}