1
//! Binary reading of the `morx` table.
2
use std::convert::TryInto;
3

            
4
use crate::binary::read::{
5
    ReadArray, ReadBinary, ReadBinaryDep, ReadCtxt, ReadFrom, ReadUnchecked,
6
};
7
use crate::binary::{U16Be, U32Be, U64Be, U8};
8
use crate::error::ParseError;
9
use crate::size;
10
use crate::SafeFrom;
11

            
12
use super::aat::VecTable;
13

            
14
/// The extended glyph metamorphosis table.
15
///
16
/// <https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html>
17
#[derive(Debug)]
18
pub struct MorxTable<'a> {
19
    pub version: u16,
20
    pub chains: Vec<Chain<'a>>,
21
}
22

            
23
impl ReadBinaryDep for MorxTable<'_> {
24
    type HostType<'a> = MorxTable<'a>;
25
    type Args<'a> = u16;
26

            
27
    fn read_dep<'a>(
28
        ctxt: &mut ReadCtxt<'a>,
29
        n_glyphs: u16,
30
    ) -> Result<Self::HostType<'a>, ParseError> {
31
        let version = ctxt.read_u16be()?;
32
        // TODO: handle this:
33
        // If the 'morx' table version is 3 or greater, then the last subtable in the chain is
34
        // followed by a subtableGlyphCoverageArray.
35
        //
36
        // Presumably a version 1 morx table used a State Table instead of Extended State Table
37
        // (STXHeader)
38
        ctxt.check_version(version == 2 || version == 3)?;
39
        let _unused = ctxt.read_u16be()?;
40
        let n_chains = ctxt.read_u32be()?;
41
        let mut chains = Vec::with_capacity(usize::safe_from(n_chains));
42

            
43
        for _i in 0..n_chains {
44
            // Read the chain header to get the chain length
45
            let scope_hdr = ctxt.scope();
46
            let chain_header = scope_hdr.read::<ChainHeader>()?;
47
            let chain_length = usize::safe_from(chain_header.chain_length);
48

            
49
            // Get a scope of length "chain_length" to read the chain and advance to the correct
50
            // position in the buffer for reading the next chain, regardless whether the "Subtable
51
            // Glyph Coverage table" is present at the end of the chain.
52
            let chain_scope = ctxt.read_scope(chain_length)?;
53
            let chain = chain_scope.read_dep::<Chain<'a>>(n_glyphs)?;
54
            chains.push(chain);
55
        }
56

            
57
        Ok(MorxTable { version, chains })
58
    }
59
}
60

            
61
#[derive(Debug)]
62
pub struct ChainHeader {
63
    pub default_flags: u32,
64
    chain_length: u32,
65
    n_feature_entries: u32,
66
    n_subtables: u32,
67
}
68

            
69
impl ReadFrom for ChainHeader {
70
    type ReadType = (U32Be, U32Be, U32Be, U32Be);
71

            
72
    fn read_from(
73
        (default_flags, chain_length, n_feature_entries, n_subtables): (u32, u32, u32, u32),
74
    ) -> Self {
75
        ChainHeader {
76
            default_flags,
77
            chain_length,
78
            n_feature_entries,
79
            n_subtables,
80
        }
81
    }
82
}
83

            
84
#[derive(Debug, Clone, Copy)]
85
pub struct Feature {
86
    pub feature_type: u16,
87
    pub feature_setting: u16,
88
    pub enable_flags: u32,
89
    pub disable_flags: u32,
90
}
91

            
92
impl ReadFrom for Feature {
93
    type ReadType = (U16Be, U16Be, U32Be, U32Be);
94

            
95
    fn read_from(
96
        (feature_type, feature_setting, enable_flags, disable_flags): (u16, u16, u32, u32),
97
    ) -> Self {
98
        Feature {
99
            feature_type,
100
            feature_setting,
101
            enable_flags,
102
            disable_flags,
103
        }
104
    }
105
}
106

            
107
#[derive(Debug)]
108
pub struct Chain<'a> {
109
    pub chain_header: ChainHeader,
110
    pub feature_array: ReadArray<'a, Feature>,
111
    pub subtables: Vec<Subtable<'a>>,
112
}
113

            
114
impl ReadBinaryDep for Chain<'_> {
115
    type HostType<'a> = Chain<'a>;
116
    type Args<'a> = u16;
117

            
118
    fn read_dep<'a>(
119
        ctxt: &mut ReadCtxt<'a>,
120
        n_glyphs: u16,
121
    ) -> Result<Self::HostType<'a>, ParseError> {
122
        let chain_header = ctxt.read::<ChainHeader>()?;
123
        let feature_array =
124
            ctxt.read_array::<Feature>(usize::safe_from(chain_header.n_feature_entries))?;
125
        let subtables = (0..chain_header.n_subtables)
126
            .map(|_i| ctxt.read_dep::<Subtable<'a>>(n_glyphs))
127
            .collect::<Result<Vec<_>, _>>()?;
128

            
129
        Ok(Chain {
130
            chain_header,
131
            feature_array,
132
            subtables,
133
        })
134
    }
135
}
136

            
137
#[derive(Debug)]
138
pub struct Coverage(u32);
139

            
140
impl Coverage {
141
    /// If set, this subtable will only be applied to vertical text. If clear, this subtable will
142
    /// only be applied to horizontal text.
143
    pub fn vertical_text(&self) -> bool {
144
        self.0 & 0x80000000 != 0
145
    }
146

            
147
    /// If set, this subtable will process glyphs in descending order. If clear, it will process
148
    /// the glyphs in ascending order.
149
    pub fn descending_order(&self) -> bool {
150
        self.0 & 0x40000000 != 0
151
    }
152

            
153
    /// If set, this subtable will be applied to both horizontal and vertical text (i.e. the state
154
    /// of bit 0x80000000 is ignored).
155
    pub fn all_text(&self) -> bool {
156
        self.0 & 0x20000000 != 0
157
    }
158

            
159
    /// If set, this subtable will process glyphs in logical order (or reverse logical order,
160
    /// depending on the value of bit 0x80000000).
161
    pub fn logical_order(&self) -> bool {
162
        self.0 & 0x10000000 != 0
163
    }
164

            
165
    /// Subtable type.
166
    fn subtable_type(&self) -> u32 {
167
        self.0 & 0x000000FF
168
    }
169
}
170

            
171
#[derive(Debug)]
172
pub struct SubtableHeader {
173
    length: u32,
174
    pub coverage: Coverage,
175
    pub sub_feature_flags: u32,
176
}
177

            
178
impl ReadFrom for SubtableHeader {
179
    type ReadType = (U32Be, U32Be, U32Be);
180

            
181
    fn read_from((length, coverage, sub_feature_flags): (u32, u32, u32)) -> Self {
182
        SubtableHeader {
183
            length,
184
            coverage: Coverage(coverage),
185
            sub_feature_flags,
186
        }
187
    }
188
}
189

            
190
#[derive(Debug)]
191
pub struct Subtable<'a> {
192
    pub subtable_header: SubtableHeader,
193
    pub subtable_body: SubtableType<'a>,
194
}
195

            
196
impl ReadBinaryDep for Subtable<'_> {
197
    type HostType<'a> = Subtable<'a>;
198
    type Args<'a> = u16;
199

            
200
    fn read_dep<'a>(
201
        ctxt: &mut ReadCtxt<'a>,
202
        n_glyphs: u16,
203
    ) -> Result<Self::HostType<'a>, ParseError> {
204
        let subtable_header = ctxt.read::<SubtableHeader>()?;
205

            
206
        // 12 is the length of the subtable header that needs to be skipped.
207
        let subtable_body_length = subtable_header
208
            .length
209
            .checked_sub(12)
210
            .map(usize::safe_from)
211
            .ok_or(ParseError::BadEof)?;
212

            
213
        // Get a shorter scope from the ReadCtxt to read the subtable
214
        let subtable_scope = ctxt.read_scope(subtable_body_length)?;
215

            
216
        let subtable_body = match subtable_header.coverage.subtable_type() {
217
            0 => SubtableType::Rearrangement(
218
                subtable_scope.read_dep::<RearrangementSubtable<'a>>(n_glyphs)?,
219
            ),
220
            1 => SubtableType::Contextual(
221
                subtable_scope.read_dep::<ContextualSubtable<'a>>(n_glyphs)?,
222
            ),
223
            2 => SubtableType::Ligature(subtable_scope.read_dep::<LigatureSubtable<'a>>(n_glyphs)?),
224
            4 => SubtableType::NonContextual(
225
                subtable_scope.read_dep::<NonContextualSubtable<'a>>(n_glyphs)?,
226
            ),
227
            // The insertion subtable is not yet supported.
228
            5 => {
229
                SubtableType::Insertion(subtable_scope.read_dep::<InsertionSubtable<'a>>(n_glyphs)?)
230
            }
231
            _ => return Err(ParseError::BadValue),
232
        };
233

            
234
        Ok(Subtable {
235
            subtable_header,
236
            subtable_body,
237
        })
238
    }
239
}
240

            
241
#[derive(Debug)]
242
pub enum SubtableType<'a> {
243
    Rearrangement(RearrangementSubtable<'a>),
244
    Contextual(ContextualSubtable<'a>),
245
    Ligature(LigatureSubtable<'a>),
246
    NonContextual(NonContextualSubtable<'a>),
247
    Insertion(InsertionSubtable<'a>),
248
}
249

            
250
/// Extended State Table
251
///
252
/// > Historically the class table had been a tight array of 8-bit values. However, in certain cases
253
/// > (such as Asian fonts) the potential wide separation between glyph indices covered by the same
254
/// > class table has led to much wasted space in the table. Therefore, the class tables in extended
255
/// > state tables are now simply LookupTables, where the looked-up value is a 16-bit class value.
256
/// > Note that a format 8 LookupTable (trimmed array) yields the same results as class array defined
257
/// > in the original state table format.
258
#[derive(Debug)]
259
struct StxHeader {
260
    n_classes: u32,
261
    class_table_offset: u32,
262
    state_array_offset: u32,
263
    entry_table_offset: u32,
264
}
265

            
266
impl ReadFrom for StxHeader {
267
    type ReadType = (U32Be, U32Be, U32Be, U32Be);
268

            
269
    fn read_from(
270
        (n_classes, class_table_offset, state_array_offset, entry_table_offset): (
271
            u32,
272
            u32,
273
            u32,
274
            u32,
275
        ),
276
    ) -> Self {
277
        StxHeader {
278
            n_classes,
279
            class_table_offset,
280
            state_array_offset,
281
            entry_table_offset,
282
        }
283
    }
284
}
285

            
286
pub trait StxTable<'a, T> {
287
    fn class_table(&self) -> &ClassLookupTable<'a>;
288

            
289
    fn state_array(&self) -> &StateArray<'a>;
290

            
291
    fn entry_table(&self) -> &VecTable<T>;
292
}
293

            
294
macro_rules! stx_table {
295
    ($entry: ident, $struct: ident) => {
296
        impl<'a> StxTable<'a, $entry> for $struct<'a> {
297
            fn class_table(&self) -> &ClassLookupTable<'a> {
298
                &self.class_table
299
            }
300

            
301
            fn state_array(&self) -> &StateArray<'a> {
302
                &self.state_array
303
            }
304

            
305
            fn entry_table(&self) -> &VecTable<$entry> {
306
                &self.entry_table
307
            }
308
        }
309
    };
310
}
311

            
312
stx_table!(RearrangementEntry, RearrangementSubtable);
313
stx_table!(ContextualEntry, ContextualSubtable);
314
stx_table!(LigatureEntry, LigatureSubtable);
315
stx_table!(InsertionEntry, InsertionSubtable);
316

            
317
#[derive(Debug)]
318
pub struct RearrangementSubtable<'a> {
319
    class_table: ClassLookupTable<'a>,
320
    state_array: StateArray<'a>,
321
    entry_table: VecTable<RearrangementEntry>,
322
}
323

            
324
impl ReadBinaryDep for RearrangementSubtable<'_> {
325
    type HostType<'a> = RearrangementSubtable<'a>;
326
    type Args<'a> = u16;
327

            
328
    fn read_dep<'a>(
329
        ctxt: &mut ReadCtxt<'a>,
330
        n_glyphs: u16,
331
    ) -> Result<Self::HostType<'a>, ParseError> {
332
        let subtable = ctxt.scope();
333

            
334
        let stx_header = ctxt.read::<StxHeader>()?;
335

            
336
        let class_table = subtable
337
            .offset(usize::safe_from(stx_header.class_table_offset))
338
            .read_dep::<ClassLookupTable<'a>>(n_glyphs)?;
339

            
340
        let state_array = subtable
341
            .offset(usize::safe_from(stx_header.state_array_offset))
342
            .read_dep::<StateArray<'a>>(NClasses(stx_header.n_classes))?;
343

            
344
        let entry_table = subtable
345
            .offset(usize::safe_from(stx_header.entry_table_offset))
346
            .read::<VecTable<RearrangementEntry>>()?;
347

            
348
        Ok(RearrangementSubtable {
349
            class_table,
350
            state_array,
351
            entry_table,
352
        })
353
    }
354
}
355

            
356
#[derive(Debug)]
357
pub struct RearrangementEntry {
358
    pub next_state: u16,
359
    flags: u16,
360
}
361

            
362
impl RearrangementEntry {
363
    /// If set, make the current glyph the first glyph to be rearranged.
364
    pub fn mark_first(&self) -> bool {
365
        self.flags & 0x8000 != 0
366
    }
367

            
368
    /// If set, don't advance to the next glyph before going to the new state. This means that the
369
    /// glyph index doesn't change, even if the glyph at that index has changed.
370
    pub fn dont_advance(&self) -> bool {
371
        self.flags & 0x4000 != 0
372
    }
373

            
374
    /// If set, make the current glyph the last glyph to be rearranged.
375
    pub fn mark_last(&self) -> bool {
376
        self.flags & 0x2000 != 0
377
    }
378

            
379
    /// The type of rearrangement specified.
380
    pub fn verb(&self) -> RearrangementVerb {
381
        use RearrangementVerb::*;
382
        match self.flags & 0x000F {
383
            0 => Verb0,
384
            1 => Verb1,
385
            2 => Verb2,
386
            3 => Verb3,
387
            4 => Verb4,
388
            5 => Verb5,
389
            6 => Verb6,
390
            7 => Verb7,
391
            8 => Verb8,
392
            9 => Verb9,
393
            10 => Verb10,
394
            11 => Verb11,
395
            12 => Verb12,
396
            13 => Verb13,
397
            14 => Verb14,
398
            15 => Verb15,
399
            _ => unreachable!(),
400
        }
401
    }
402
}
403

            
404
#[derive(Debug)]
405
pub enum RearrangementVerb {
406
    Verb0,  // No change
407
    Verb1,  // Ax => xA
408
    Verb2,  // xD => Dx
409
    Verb3,  // AxD => DxA
410
    Verb4,  // ABx => xAB
411
    Verb5,  // ABx => xBA
412
    Verb6,  // xCD => CDx
413
    Verb7,  // xCD => DCx
414
    Verb8,  // AxCD => CDxA
415
    Verb9,  // AxCD => DCxA
416
    Verb10, // ABxD => DxAB
417
    Verb11, // ABxD => DxBA
418
    Verb12, // ABxCD => CDxAB
419
    Verb13, // ABxCD => CDxBA
420
    Verb14, // ABxCD => DCxAB
421
    Verb15, // ABxCD => DCxBA
422
}
423

            
424
impl ReadFrom for RearrangementEntry {
425
    type ReadType = (U16Be, U16Be);
426

            
427
    fn read_from((next_state, flags): (u16, u16)) -> Self {
428
        RearrangementEntry { next_state, flags }
429
    }
430
}
431

            
432
/// Contextual Glyph Substitution Subtable
433
#[derive(Debug)]
434
pub struct ContextualSubtable<'a> {
435
    class_table: ClassLookupTable<'a>,
436
    state_array: StateArray<'a>,
437
    entry_table: VecTable<ContextualEntry>,
438
    pub substitution_subtables: Vec<ClassLookupTable<'a>>,
439
}
440

            
441
impl ReadBinaryDep for ContextualSubtable<'_> {
442
    type HostType<'a> = ContextualSubtable<'a>;
443
    type Args<'a> = u16;
444

            
445
    fn read_dep<'a>(
446
        ctxt: &mut ReadCtxt<'a>,
447
        n_glyphs: u16,
448
    ) -> Result<Self::HostType<'a>, ParseError> {
449
        let subtable = ctxt.scope();
450

            
451
        let stx_header = ctxt.read::<StxHeader>()?;
452
        let substitution_subtables_offset = ctxt.read_u32be()?;
453

            
454
        let class_table = subtable
455
            .offset(usize::safe_from(stx_header.class_table_offset))
456
            .read_dep::<ClassLookupTable<'a>>(n_glyphs)?;
457

            
458
        let state_array = subtable
459
            .offset(usize::safe_from(stx_header.state_array_offset))
460
            .read_dep::<StateArray<'a>>(NClasses(stx_header.n_classes))?;
461

            
462
        let entry_table = subtable
463
            .offset(usize::safe_from(stx_header.entry_table_offset))
464
            .read::<VecTable<ContextualEntry>>()?;
465

            
466
        let first_offset_to_subst_tables = subtable
467
            .offset(usize::safe_from(substitution_subtables_offset))
468
            .ctxt()
469
            .read_u32be()?;
470

            
471
        // This assumes the offsets are in order, which they may not be
472
        let offset_array_len = first_offset_to_subst_tables / 4;
473
        let mut subst_tables_ctxt = subtable
474
            .offset(usize::safe_from(substitution_subtables_offset))
475
            .ctxt();
476

            
477
        // The spec notes:
478
        //
479
        // > Note that nowhere is there specified the number of LookupTables. Since this number is an
480
        // > artifact of the font production process, and is not needed by the runtime metamorphosis
481
        // > software, there was no need to include it explicitly.
482
        //
483
        // We attempt to read them all up-front, which is fragile but works for the set of fonts
484
        // tested.
485
        let mut substitution_subtables: Vec<ClassLookupTable<'a>> = Vec::new();
486
        for _i in 0..offset_array_len {
487
            let offset = match subst_tables_ctxt.read_u32be() {
488
                Ok(offset) => usize::safe_from(offset),
489
                Err(_err) => break,
490
            };
491

            
492
            let subst_subtable = match subtable
493
                .offset(usize::safe_from(substitution_subtables_offset))
494
                .offset(offset)
495
                .read_dep::<ClassLookupTable<'a>>(n_glyphs)
496
            {
497
                Ok(val) => val,
498
                Err(_err) => break,
499
            };
500
            substitution_subtables.push(subst_subtable);
501
        }
502

            
503
        Ok(ContextualSubtable {
504
            class_table,
505
            state_array,
506
            entry_table,
507
            substitution_subtables,
508
        })
509
    }
510
}
511

            
512
#[derive(Debug)]
513
pub struct ContextualEntry {
514
    pub next_state: u16,
515
    pub flags: ContextualEntryFlags,
516
    pub mark_index: u16,
517
    pub current_index: u16,
518
}
519

            
520
#[enumflags2::bitflags]
521
#[repr(u16)]
522
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
523
#[allow(non_camel_case_types)]
524
pub enum ContextualEntryFlag {
525
    /// If set, make the current glyph the marked glyph.
526
    SET_MARK = 0x8000,
527
    /// If set, don't advance to the next glyph before going to the new state.
528
    DONT_ADVANCE = 0x4000,
529
    // 0x3FFF 	reserved 	These bits are reserved and should be set to 0.
530
}
531

            
532
pub type ContextualEntryFlags = enumflags2::BitFlags<ContextualEntryFlag>;
533

            
534
impl ReadFrom for ContextualEntry {
535
    type ReadType = (U16Be, U16Be, U16Be, U16Be);
536

            
537
    fn read_from((next_state, flags, mark_index, current_index): (u16, u16, u16, u16)) -> Self {
538
        ContextualEntry {
539
            next_state,
540
            flags: ContextualEntryFlags::from_bits_truncate(flags),
541
            mark_index,
542
            current_index,
543
        }
544
    }
545
}
546

            
547
/// Noncontextual Glyph Substitution Subtable
548
#[derive(Debug)]
549
pub struct NonContextualSubtable<'a> {
550
    pub lookup_table: ClassLookupTable<'a>,
551
}
552

            
553
impl ReadBinaryDep for NonContextualSubtable<'_> {
554
    type HostType<'a> = NonContextualSubtable<'a>;
555
    type Args<'a> = u16;
556

            
557
    fn read_dep<'a>(
558
        ctxt: &mut ReadCtxt<'a>,
559
        n_glyphs: u16,
560
    ) -> Result<Self::HostType<'a>, ParseError> {
561
        let lookup_table = ctxt.read_dep::<ClassLookupTable<'a>>(n_glyphs)?;
562

            
563
        Ok(NonContextualSubtable { lookup_table })
564
    }
565
}
566

            
567
/// Ligature subtable
568
#[derive(Debug)]
569
pub struct LigatureSubtable<'a> {
570
    class_table: ClassLookupTable<'a>,
571
    state_array: StateArray<'a>,
572
    entry_table: VecTable<LigatureEntry>,
573
    pub action_table: VecTable<LigatureAction>,
574
    pub component_table: ComponentTable<'a>,
575
    pub ligature_list: LigatureList<'a>,
576
}
577

            
578
impl ReadBinaryDep for LigatureSubtable<'_> {
579
    type HostType<'a> = LigatureSubtable<'a>;
580
    type Args<'a> = u16;
581

            
582
    fn read_dep<'a>(
583
        ctxt: &mut ReadCtxt<'a>,
584
        n_glyphs: u16,
585
    ) -> Result<Self::HostType<'a>, ParseError> {
586
        let subtable = ctxt.scope();
587

            
588
        let stx_header = ctxt.read::<StxHeader>()?;
589

            
590
        let lig_action_offset = ctxt.read_u32be()?;
591

            
592
        let component_offset = ctxt.read_u32be()?;
593

            
594
        let ligature_list_offset = ctxt.read_u32be()?;
595

            
596
        let class_table = subtable
597
            .offset(usize::safe_from(stx_header.class_table_offset))
598
            .read_dep::<ClassLookupTable<'a>>(n_glyphs)?;
599

            
600
        let state_array = subtable
601
            .offset(usize::safe_from(stx_header.state_array_offset))
602
            .read_dep::<StateArray<'a>>(NClasses(stx_header.n_classes))?;
603

            
604
        let entry_table = subtable
605
            .offset(usize::safe_from(stx_header.entry_table_offset))
606
            .read::<VecTable<LigatureEntry>>()?;
607

            
608
        let action_table = subtable
609
            .offset(usize::safe_from(lig_action_offset))
610
            .read::<VecTable<LigatureAction>>()?;
611

            
612
        let component_table = subtable
613
            .offset(usize::safe_from(component_offset))
614
            .read::<ComponentTable<'a>>()?;
615

            
616
        let ligature_list = subtable
617
            .offset(usize::safe_from(ligature_list_offset))
618
            .read::<LigatureList<'a>>()?;
619

            
620
        Ok(LigatureSubtable {
621
            class_table,
622
            state_array,
623
            entry_table,
624
            action_table,
625
            component_table,
626
            ligature_list,
627
        })
628
    }
629
}
630

            
631
#[derive(Debug)]
632
pub struct LigatureEntry {
633
    pub next_state_index: u16,
634
    pub flags: LigatureEntryFlags,
635
    pub lig_action_index: u16,
636
}
637

            
638
#[enumflags2::bitflags]
639
#[repr(u16)]
640
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
641
#[allow(non_camel_case_types)]
642
pub enum LigatureEntryFlag {
643
    /// Push this glyph onto the component stack for eventual processing.
644
    SET_COMPONENT = 0x8000,
645
    /// Leave the glyph pointer at this glyph for the next iteration.
646
    DONT_ADVANCE = 0x4000,
647
    /// Use the ligActionIndex to process a ligature group.
648
    PERFORM_ACTION = 0x2000,
649
    // 0x1FFF   RESERVED    Reserved; set to zero.
650
}
651

            
652
pub type LigatureEntryFlags = enumflags2::BitFlags<LigatureEntryFlag>;
653

            
654
impl ReadFrom for LigatureEntry {
655
    type ReadType = (U16Be, U16Be, U16Be);
656

            
657
    fn read_from((next_state_index, flags, lig_action_index): (u16, u16, u16)) -> Self {
658
        LigatureEntry {
659
            next_state_index,
660
            flags: LigatureEntryFlags::from_bits_truncate(flags),
661
            lig_action_index,
662
        }
663
    }
664
}
665

            
666
#[derive(Debug)]
667
pub struct LigatureAction(u32);
668

            
669
impl LigatureAction {
670
    /// This is the last action in the list. This also implies storage.
671
    pub fn last(&self) -> bool {
672
        self.0 & 0x80000000 != 0
673
    }
674

            
675
    /// Store the ligature at the current cumulated index in the ligature table in place of the
676
    /// marked (i.e. currently-popped) glyph.
677
    pub fn store(&self) -> bool {
678
        self.0 & 0x40000000 != 0
679
    }
680

            
681
    /// A 30-bit value which is sign-extended to 32-bits and added to the glyph ID, resulting in an
682
    /// index into the component table.
683
    pub fn offset(&self) -> i32 {
684
        let mut offset = self.0 & 0x3FFFFFFF; // Take 30 bits.
685
        if offset & 0x20000000 != 0 {
686
            offset |= 0xC0000000; // Sign-extend it to 32 bits.
687
        }
688
        offset as i32 // Cast is safe due to masking.
689
    }
690
}
691

            
692
impl ReadFrom for LigatureAction {
693
    type ReadType = U32Be;
694

            
695
    fn read_from(action: u32) -> Self {
696
        LigatureAction(action)
697
    }
698
}
699

            
700
#[derive(Debug)]
701
pub struct InsertionSubtable<'a> {
702
    class_table: ClassLookupTable<'a>,
703
    state_array: StateArray<'a>,
704
    entry_table: VecTable<InsertionEntry>,
705
    pub action_table: VecTable<InsertionAction>,
706
}
707

            
708
impl ReadBinaryDep for InsertionSubtable<'_> {
709
    type HostType<'a> = InsertionSubtable<'a>;
710
    type Args<'a> = u16;
711

            
712
    fn read_dep<'a>(
713
        ctxt: &mut ReadCtxt<'a>,
714
        n_glyphs: u16,
715
    ) -> Result<Self::HostType<'a>, ParseError> {
716
        let subtable = ctxt.scope();
717

            
718
        let stx_header = ctxt.read::<StxHeader>()?;
719
        let insertion_action_offset = ctxt.read_u32be()?;
720

            
721
        let class_table = subtable
722
            .offset(usize::safe_from(stx_header.class_table_offset))
723
            .read_dep::<ClassLookupTable<'a>>(n_glyphs)?;
724

            
725
        let state_array = subtable
726
            .offset(usize::safe_from(stx_header.state_array_offset))
727
            .read_dep::<StateArray<'a>>(NClasses(stx_header.n_classes))?;
728

            
729
        let entry_table = subtable
730
            .offset(usize::safe_from(stx_header.entry_table_offset))
731
            .read::<VecTable<InsertionEntry>>()?;
732

            
733
        let action_table = subtable
734
            .offset(usize::safe_from(insertion_action_offset))
735
            .read::<VecTable<InsertionAction>>()?;
736

            
737
        Ok(InsertionSubtable {
738
            class_table,
739
            state_array,
740
            entry_table,
741
            action_table,
742
        })
743
    }
744
}
745

            
746
#[derive(Debug)]
747
pub struct InsertionEntry {
748
    pub next_state: u16,
749
    flags: u16,
750
    pub current_insert_index: u16,
751
    pub marked_insert_index: u16,
752
}
753

            
754
impl InsertionEntry {
755
    /// If set, mark the current glyph.
756
    pub fn set_mark(&self) -> bool {
757
        self.flags & 0x8000 != 0
758
    }
759

            
760
    /// If set, don't update the glyph index before going to the new state. This does not mean that
761
    /// the glyph pointed to is the same one as before. If you've made insertions immediately
762
    /// downstream of the current glyph, the next glyph processed would in fact be the first one
763
    /// inserted.
764
    pub fn dont_advance(&self) -> bool {
765
        self.flags & 0x4000 != 0
766
    }
767

            
768
    /// If set, and the currentInsertList is nonzero, then the specified glyph list will be inserted
769
    /// as a kashida-like insertion, either before or after the current glyph (depending on the
770
    /// state of the currentInsertBefore flag). If clear, and the currentInsertList is nonzero, then
771
    /// the specified glyph list will be inserted as a split-vowel-like insertion, either before or
772
    /// after the current glyph (depending on the state of the currentInsertBefore flag).
773
    pub fn current_is_kashida_like(&self) -> bool {
774
        self.flags & 0x2000 != 0
775
    }
776

            
777
    /// If set, and the markedInsertList is nonzero, then the specified glyph list will be inserted
778
    /// as a kashida-like insertion, either before or after the marked glyph (depending on the state
779
    /// of the markedInsertBefore flag). If clear, and the markedInsertList is nonzero, then the
780
    /// specified glyph list will be inserted as a split-vowel-like insertion, either before or
781
    /// after the marked glyph (depending on the state of the markedInsertBefore flag).
782
    pub fn marked_is_kashida_like(&self) -> bool {
783
        self.flags & 0x1000 != 0
784
    }
785

            
786
    /// If set, specifies that insertions are to be made to the left of the current glyph. If clear,
787
    /// they're made to the right of the current glyph.
788
    pub fn current_insert_before(&self) -> bool {
789
        self.flags & 0x0800 != 0
790
    }
791

            
792
    /// If set, specifies that insertions are to be made to the left of the marked glyph. If clear,
793
    /// they're made to the right of the marked glyph.
794
    pub fn marked_insert_before(&self) -> bool {
795
        self.flags & 0x0400 != 0
796
    }
797

            
798
    /// This 5-bit field is treated as a count of the number of glyphs to insert at the current
799
    /// position. Since zero means no insertions, the largest number of insertions at any given
800
    /// current location is 31 glyphs.
801
    pub fn current_insert_count(&self) -> usize {
802
        usize::from((self.flags & 0x03E0) >> 5)
803
    }
804

            
805
    /// This 5-bit field is treated as a count of the number of glyphs to insert at the marked
806
    /// position. Since zero means no insertions, the largest number of insertions at any given
807
    /// marked location is 31 glyphs.
808
    pub fn marked_insert_count(&self) -> usize {
809
        usize::from(self.flags & 0x001F)
810
    }
811
}
812

            
813
impl ReadFrom for InsertionEntry {
814
    type ReadType = (U16Be, U16Be, U16Be, U16Be);
815

            
816
    fn read_from(
817
        (next_state, flags, current_insert_index, marked_insert_index): (u16, u16, u16, u16),
818
    ) -> Self {
819
        InsertionEntry {
820
            next_state,
821
            flags,
822
            current_insert_index,
823
            marked_insert_index,
824
        }
825
    }
826
}
827

            
828
#[derive(Debug)]
829
pub struct InsertionAction(pub u16);
830

            
831
impl ReadFrom for InsertionAction {
832
    type ReadType = U16Be;
833

            
834
    fn read_from(action: u16) -> Self {
835
        InsertionAction(action)
836
    }
837
}
838

            
839
#[derive(Debug, Clone, Copy)]
840
pub struct NClasses(u32);
841

            
842
#[derive(Debug)]
843
pub struct StateArray<'a>(pub Vec<ReadArray<'a, U16Be>>);
844

            
845
impl ReadBinaryDep for StateArray<'_> {
846
    type Args<'a> = NClasses;
847
    type HostType<'a> = StateArray<'a>;
848

            
849
    fn read_dep<'a>(
850
        ctxt: &mut ReadCtxt<'a>,
851
        NClasses(n_classes): NClasses,
852
    ) -> Result<Self::HostType<'a>, ParseError> {
853
        let mut state_array: Vec<ReadArray<'a, U16Be>> = Vec::new();
854
        let state_row_len = usize::safe_from(n_classes);
855

            
856
        loop {
857
            let state_row = match ctxt.read_array::<U16Be>(state_row_len) {
858
                Ok(array) => array,
859
                Err(ParseError::BadEof) => break,
860
                Err(err) => return Err(err),
861
            };
862

            
863
            state_array.push(state_row);
864
        }
865

            
866
        Ok(StateArray(state_array))
867
    }
868
}
869

            
870
#[derive(Debug)]
871
pub struct ComponentTable<'a> {
872
    pub component_array: ReadArray<'a, U16Be>,
873
}
874

            
875
impl ReadBinary for ComponentTable<'_> {
876
    type HostType<'a> = ComponentTable<'a>;
877

            
878
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
879
        let len_remaining = ctxt.scope().data().len();
880
        let component_array = ctxt.read_array::<U16Be>(len_remaining / size::U16)?;
881

            
882
        Ok(ComponentTable { component_array })
883
    }
884
}
885

            
886
#[derive(Debug)]
887
pub struct LigatureList<'a>(pub ReadArray<'a, U16Be>);
888

            
889
impl LigatureList<'_> {
890
    pub fn get(&self, index: u16) -> Option<u16> {
891
        let index = usize::from(index);
892
        self.0.get_item(index)
893
    }
894
}
895

            
896
impl ReadBinary for LigatureList<'_> {
897
    type HostType<'a> = LigatureList<'a>;
898

            
899
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
900
        let len_remaining = ctxt.scope().data().len();
901
        let ligature_list = ctxt.read_array::<U16Be>(len_remaining / size::U16)?;
902

            
903
        Ok(LigatureList(ligature_list))
904
    }
905
}
906

            
907
#[derive(Debug)]
908
pub struct LookupTableHeader {
909
    pub format: u16,
910
    bin_srch_header: Option<BinSrchHeader>,
911
}
912

            
913
impl ReadBinary for LookupTableHeader {
914
    type HostType<'a> = Self;
915

            
916
    fn read(ctxt: &mut ReadCtxt<'_>) -> Result<Self, ParseError> {
917
        let format = ctxt.read_u16be()?;
918

            
919
        let bin_srch_header = match format {
920
            2 | 4 | 6 => Some(ctxt.read::<BinSrchHeader>()?),
921
            0 | 8 | 10 => None,
922
            _ => return Err(ParseError::BadValue),
923
        };
924

            
925
        Ok(LookupTableHeader {
926
            format,
927
            bin_srch_header,
928
        })
929
    }
930
}
931

            
932
#[derive(Debug, Clone, Copy)]
933
pub struct BinSrchHeader {
934
    unit_size: u16,
935
    n_units: u16,
936
}
937

            
938
impl ReadBinary for BinSrchHeader {
939
    type HostType<'a> = Self;
940

            
941
    fn read(ctxt: &mut ReadCtxt<'_>) -> Result<Self, ParseError> {
942
        let unit_size = ctxt.read_u16be()?;
943
        let n_units = ctxt.read_u16be()?;
944

            
945
        // Note that the searchRange, entrySelector, and rangeShift fields are redundant. Binary
946
        // search tables were designed to work efficiently with the processors available in the late
947
        // 1980s; the inclusion of these three fields allowed the use of a very efficient lookup
948
        // algorithm on such processors. This optimization is not needed on modern processors and
949
        // these three fields are no longer used.
950
        let _search_range = ctxt.read_u16be()?;
951
        let _entry_selector = ctxt.read_u16be()?;
952
        let _range_shift = ctxt.read_u16be()?;
953

            
954
        Ok(BinSrchHeader { unit_size, n_units })
955
    }
956
}
957

            
958
#[derive(Debug)]
959
pub enum LookupTable<'a> {
960
    /// Simple Array format 0
961
    Format0(ReadArray<'a, U16Be>),
962
    /// Segment Single format 2
963
    Format2(ReadArray<'a, LookupSegmentFmt2>),
964
    /// Segment Array format 4
965
    Format4(Vec<LookupValuesFmt4<'a>>),
966
    /// Single Table format 6
967
    Format6(ReadArray<'a, LookupSingleFmt6>),
968
    /// Trimmed Array format 8
969
    Format8(LookupTableFormat8<'a>),
970
    /// Trimmed Array format 10
971
    Format10(LookupTableFormat10<'a>),
972
}
973

            
974
#[derive(Debug)]
975
pub struct LookupTableFormat8<'a> {
976
    first_glyph: u16,
977
    lookup_values: ReadArray<'a, U16Be>,
978
}
979

            
980
impl<'a> LookupTableFormat8<'a> {
981
    pub fn new(
982
        first_glyph: u16,
983
        lookup_values: ReadArray<'a, U16Be>,
984
    ) -> Option<LookupTableFormat8<'a>> {
985
        // Validate arguments
986
        let len = lookup_values.len().try_into().ok()?;
987
        let _last = first_glyph.checked_add(len)?;
988
        Some(LookupTableFormat8 {
989
            first_glyph,
990
            lookup_values,
991
        })
992
    }
993
    pub fn contains(&self, glyph: u16) -> bool {
994
        // NOTE(cast): Safe due to validation in new
995
        let end = self.first_glyph + self.lookup_values.len() as u16;
996
        (self.first_glyph..end).contains(&glyph)
997
    }
998

            
999
    pub fn lookup(&self, glyph: u16) -> Option<u16> {
        if self.contains(glyph) {
            // NOTE(sub): Won't underflow due to contains check
            self.lookup_values
                .get_item(usize::from(glyph - self.first_glyph))
        } else {
            None
        }
    }
}
// TODO: Format8 is basically this with a unit size of 2
#[derive(Debug)]
pub struct LookupTableFormat10<'a> {
    first_glyph: u16,
    lookup_values: UnitSize<'a>,
}
impl<'a> LookupTableFormat10<'a> {
    pub fn new(first_glyph: u16, lookup_values: UnitSize<'a>) -> Option<Self> {
        // Validate arguments
        let len = lookup_values.len().try_into().ok()?;
        let _last = first_glyph.checked_add(len)?;
        Some(LookupTableFormat10 {
            first_glyph,
            lookup_values,
        })
    }
    pub fn contains(&self, glyph: u16) -> bool {
        // NOTE(cast): Safe due to validation in new
        let end = self.first_glyph + self.lookup_values.len() as u16;
        (self.first_glyph..end).contains(&glyph)
    }
    pub fn lookup(&self, glyph: u16) -> Option<u16> {
        if self.contains(glyph) {
            // NOTE(sub): Won't underflow due to contains check
            let index = glyph - self.first_glyph;
            match &self.lookup_values {
                UnitSize::OneByte(one_byte_values) => {
                    one_byte_values.get_item(usize::from(index)).map(u16::from)
                }
                UnitSize::TwoByte(two_byte_values) => two_byte_values.get_item(usize::from(index)),
                // Note: ignore 4-byte and 8-byte lookup values for now
                UnitSize::FourByte { .. } | UnitSize::EightByte { .. } => {
                    todo!("handle 4 and 8-bit lookup values")
                }
            }
        } else {
            None
        }
    }
}
#[derive(Debug)]
pub enum UnitSize<'a> {
    OneByte(ReadArray<'a, U8>),
    TwoByte(ReadArray<'a, U16Be>),
    FourByte(ReadArray<'a, U32Be>),
    EightByte(ReadArray<'a, U64Be>),
}
impl UnitSize<'_> {
    pub fn len(&self) -> usize {
        match self {
            UnitSize::OneByte(array) => array.len(),
            UnitSize::TwoByte(array) => array.len(),
            UnitSize::FourByte(array) => array.len(),
            UnitSize::EightByte(array) => array.len(),
        }
    }
}
#[derive(Debug, Copy, Clone)]
pub struct LookupSegmentFmt2 {
    pub last_glyph: u16,
    pub first_glyph: u16,
    // Assumption: lookup values are commonly u16. If this is not the case
    // an error will be returned when reading the segment.
    pub lookup_value: u16,
}
impl LookupSegmentFmt2 {
    pub fn contains(&self, glyph: u16) -> bool {
        (self.first_glyph..=self.last_glyph).contains(&glyph)
    }
}
impl ReadFrom for LookupSegmentFmt2 {
    type ReadType = (U16Be, U16Be, U16Be);
    fn read_from((last_glyph, first_glyph, lookup_value): (u16, u16, u16)) -> Self {
        LookupSegmentFmt2 {
            last_glyph,
            first_glyph,
            lookup_value,
        }
    }
}
#[derive(Debug)]
pub struct LookupSegmentFmt4 {
    last_glyph: u16,
    first_glyph: u16,
    offset: u16,
}
impl ReadFrom for LookupSegmentFmt4 {
    type ReadType = (U16Be, U16Be, U16Be);
    fn read_from((last_glyph, first_glyph, offset): (u16, u16, u16)) -> Self {
        LookupSegmentFmt4 {
            last_glyph,
            first_glyph,
            offset,
        }
    }
}
#[derive(Debug)]
pub struct LookupValuesFmt4<'a> {
    pub last_glyph: u16,
    pub first_glyph: u16,
    pub lookup_values: ReadArray<'a, U16Be>,
}
impl LookupValuesFmt4<'_> {
    pub fn contains(&self, glyph: u16) -> bool {
        (self.first_glyph..=self.last_glyph).contains(&glyph)
    }
}
#[derive(Debug, Copy, Clone)]
pub struct LookupSingleFmt6 {
    pub glyph: u16,
    // Assumption: lookup values are commonly u16. If this is not the case
    // an error will be returned when reading the segment.
    pub lookup_value: u16,
}
impl ReadFrom for LookupSingleFmt6 {
    type ReadType = (U16Be, U16Be);
    fn read_from((glyph, lookup_value): (u16, u16)) -> Self {
        LookupSingleFmt6 {
            glyph,
            lookup_value,
        }
    }
}
#[derive(Debug)]
pub struct ClassLookupTable<'a> {
    pub lookup_table: LookupTable<'a>,
}
impl ReadBinaryDep for ClassLookupTable<'_> {
    type HostType<'a> = ClassLookupTable<'a>;
    type Args<'a> = u16;
    fn read_dep<'a>(
        ctxt: &mut ReadCtxt<'a>,
        n_glyphs: u16,
    ) -> Result<Self::HostType<'a>, ParseError> {
        let class_table = ctxt.scope();
        let lookup_header = ctxt.read::<LookupTableHeader>()?;
        match (lookup_header.format, lookup_header.bin_srch_header) {
            // Format 0 lookup table presents an array of lookup values, indexed by glyph index.
            (0, None) => {
                let lookup_values = ctxt.read_array(usize::from(n_glyphs))?;
                let lookup_table = LookupTable::Format0(lookup_values);
                Ok(ClassLookupTable { lookup_table })
            }
            (2, Some(b_sch_header)) => {
                // FIXME: 6 is a minimum
                // The units for this binary search are of type LookupSegment, and always have a minimum length of 6.
                if usize::from(b_sch_header.unit_size) != LookupSegmentFmt2::SIZE {
                    return Err(ParseError::BadValue);
                }
                let lookup_segments =
                    ctxt.read_array::<LookupSegmentFmt2>(usize::from(b_sch_header.n_units))?;
                let lookup_table = LookupTable::Format2(lookup_segments);
                Ok(ClassLookupTable { lookup_table })
            }
            (4, Some(b_sch_header)) => {
                // FIXME: 6 is a minimum
                // The units for this binary search are of type LookupSegment and always have a minimum length of 6.
                if usize::from(b_sch_header.unit_size) != LookupSegmentFmt4::SIZE {
                    return Err(ParseError::BadValue);
                }
                let mut lookup_segments: Vec<LookupValuesFmt4<'_>> =
                    Vec::with_capacity(usize::from(b_sch_header.n_units));
                for _i in 0..b_sch_header.n_units {
                    let segment = ctxt.read::<LookupSegmentFmt4>()?;
                    // To guarantee that a binary search terminates, you must include one or more
                    // special "end of search table" values at the end of the data to be searched.
                    // The number of termination values that need to be included is table-specific.
                    // The value that indicates binary search termination is 0xFFFF.
                    if (segment.first_glyph == 0xFFFF) && (segment.last_glyph == 0xFFFF) {
                        break;
                    }
                    let mut read_ctxt = class_table.offset(usize::from(segment.offset)).ctxt();
                    let num_lookup_values = segment
                        .last_glyph
                        .checked_sub(segment.first_glyph)
                        .ok_or(ParseError::BadValue)?
                        .checked_add(1)
                        .ok_or(ParseError::BadValue)?;
                    let lookup_values =
                        read_ctxt.read_array::<U16Be>(usize::from(num_lookup_values))?;
                    let lookup_segment = LookupValuesFmt4 {
                        last_glyph: segment.last_glyph,
                        first_glyph: segment.first_glyph,
                        lookup_values,
                    };
                    lookup_segments.push(lookup_segment);
                }
                let lookup_table = LookupTable::Format4(lookup_segments);
                Ok(ClassLookupTable { lookup_table })
            }
            (6, Some(b_sch_header)) => {
                // FIXME: 4 is a minimum
                // The units for this binary search are of type LookupSingle and always have a minimum length of 4.
                if usize::from(b_sch_header.unit_size) != LookupSingleFmt6::SIZE {
                    return Err(ParseError::BadValue);
                }
                let lookup_entries =
                    ctxt.read_array::<LookupSingleFmt6>(usize::from(b_sch_header.n_units))?;
                let lookup_table = LookupTable::Format6(lookup_entries);
                Ok(ClassLookupTable { lookup_table })
            }
            (8, None) => {
                let first_glyph = ctxt.read_u16be()?;
                let glyph_count = ctxt.read_u16be()?;
                let lookup_values = ctxt.read_array::<U16Be>(usize::from(glyph_count))?;
                let lookup_table = LookupTableFormat8::new(first_glyph, lookup_values)
                    .ok_or(ParseError::BadValue)?;
                Ok(ClassLookupTable {
                    lookup_table: LookupTable::Format8(lookup_table),
                })
            }
            (10, None) => {
                // Size of a lookup unit for this lookup table in bytes. Allowed values are 1, 2, 4, and 8.
                let unit_size = ctxt.read_u16be()?;
                let first_glyph = ctxt.read_u16be()?;
                let glyph_count = ctxt.read_u16be().map(usize::from)?;
                let lookup_values = match unit_size {
                    1 => {
                        let lookup_values = ctxt.read_array::<U8>(glyph_count)?;
                        UnitSize::OneByte(lookup_values)
                    }
                    2 => {
                        let lookup_values = ctxt.read_array::<U16Be>(glyph_count)?;
                        UnitSize::TwoByte(lookup_values)
                    }
                    4 => {
                        let lookup_values = ctxt.read_array::<U32Be>(glyph_count)?;
                        UnitSize::FourByte(lookup_values)
                    }
                    8 => {
                        let lookup_values = ctxt.read_array::<U64Be>(glyph_count)?;
                        UnitSize::EightByte(lookup_values)
                    }
                    _ => return Err(ParseError::BadValue),
                };
                let lookup_table = LookupTableFormat10::new(first_glyph, lookup_values)
                    .ok_or(ParseError::BadValue)?;
                Ok(ClassLookupTable {
                    lookup_table: LookupTable::Format10(lookup_table),
                })
            }
            _ => Err(ParseError::BadVersion),
        }
    }
}