1
//! `morx` layout transformations.
2

            
3
use std::cmp;
4
use tinyvec::{tiny_vec, TinyVec};
5

            
6
use crate::error::ParseError;
7
use crate::glyph_position::TextDirection;
8
use crate::gsub::{Feature, FeatureMask, GlyphOrigin, RawGlyph, RawGlyphFlags};
9
use crate::scripts::horizontal_text_direction;
10
use crate::tables::aat::{
11
    CLASS_CODE_DELETED, CLASS_CODE_EOT, CLASS_CODE_OOB, DELETED_GLYPH, MAX_LEN, MAX_OPS,
12
};
13
use crate::tables::morx::{
14
    self, Chain, ClassLookupTable, ContextualSubtable, InsertionSubtable, LigatureSubtable,
15
    LookupTable, MorxTable, NonContextualSubtable, RearrangementSubtable, RearrangementVerb,
16
    StxTable, Subtable, SubtableHeader, SubtableType,
17
};
18
use crate::tables::morx::{ContextualEntryFlag, LigatureEntryFlag};
19

            
20
/// Perform a lookup in a class lookup table.
21
fn lookup(glyph: u16, lookup_table: &ClassLookupTable<'_>) -> Option<u16> {
22
    match &lookup_table.lookup_table {
23
        LookupTable::Format0(lookup_values) => lookup_values.get_item(usize::from(glyph)),
24
        LookupTable::Format2(lookup_segments) => {
25
            lookup_segments.iter().find_map(|lookup_segment| {
26
                lookup_segment
27
                    .contains(glyph)
28
                    .then_some(lookup_segment.lookup_value)
29
            })
30
        }
31
        LookupTable::Format4(lookup_segments) => {
32
            for lookup_segment in lookup_segments {
33
                // The segments are meant to be non-overlapping so if a segment contains the glyph
34
                // then we always return a result.
35
                if lookup_segment.contains(glyph) {
36
                    let index = usize::from(glyph - lookup_segment.first_glyph);
37
                    return lookup_segment.lookup_values.get_item(index);
38
                }
39
            }
40
            None
41
        }
42
        LookupTable::Format6(lookup_entries) => lookup_entries.iter().find_map(|lookup_entry| {
43
            (lookup_entry.glyph == glyph).then_some(lookup_entry.lookup_value)
44
        }),
45
        LookupTable::Format8(lookup_table) => lookup_table.lookup(glyph),
46
        LookupTable::Format10(lookup_table) => lookup_table.lookup(glyph),
47
    }
48
}
49

            
50
fn get_class<'a, T, U>(glyphs: &[RawGlyph<()>], i: usize, subtable: &T) -> u16
51
where
52
    T: StxTable<'a, U>,
53
{
54
    let class_table = subtable.class_table();
55
    match glyphs.get(i) {
56
        Some(g) => {
57
            if g.glyph_index == DELETED_GLYPH {
58
                CLASS_CODE_DELETED
59
            } else {
60
                lookup(g.glyph_index, class_table).unwrap_or(CLASS_CODE_OOB)
61
            }
62
        }
63
        None => CLASS_CODE_EOT,
64
    }
65
}
66

            
67
fn get_entry<'a, T, U>(class: u16, next_state: u16, subtable: &T) -> Result<&U, ParseError>
68
where
69
    T: StxTable<'a, U>,
70
{
71
    let entry_table_index = subtable
72
        .state_array()
73
        .0
74
        .get(usize::from(next_state))
75
        .and_then(|s| s.get_item(usize::from(class)))
76
        .ok_or(ParseError::BadIndex)?;
77

            
78
    subtable
79
        .entry_table()
80
        .0
81
        .get(usize::from(entry_table_index))
82
        .ok_or(ParseError::BadIndex)
83
}
84

            
85
struct RearrangementTransformation<'a> {
86
    max_ops: isize,
87
    glyphs: &'a mut Vec<RawGlyph<()>>,
88
    next_state: u16,
89
    mark_first_index: usize,
90
    mark_last_index: usize,
91
}
92

            
93
impl<'a> RearrangementTransformation<'a> {
94
    fn new(glyphs: &'a mut Vec<RawGlyph<()>>) -> RearrangementTransformation<'a> {
95
        RearrangementTransformation {
96
            max_ops: MAX_OPS,
97
            glyphs,
98
            next_state: 0,
99
            mark_first_index: 0,
100
            mark_last_index: 0,
101
        }
102
    }
103

            
104
    fn process_glyphs(
105
        &mut self,
106
        rearrangement_subtable: &RearrangementSubtable<'_>,
107
    ) -> Result<(), ParseError> {
108
        let len = self.glyphs.len();
109
        let mut i = 0;
110
        while i <= len {
111
            let class = get_class(self.glyphs, i, rearrangement_subtable);
112
            let entry = get_entry(class, self.next_state, rearrangement_subtable)?;
113
            self.next_state = entry.next_state;
114

            
115
            if entry.mark_first() {
116
                self.mark_first_index = i;
117
            }
118

            
119
            if entry.mark_last() {
120
                self.mark_last_index = cmp::min(i + 1, len);
121
            }
122

            
123
            if self.mark_first_index < self.mark_last_index {
124
                let seq = &mut self.glyphs[self.mark_first_index..self.mark_last_index];
125
                rearrange_glyphs(entry.verb(), seq);
126
            }
127

            
128
            // Guard against infinite loop during end-of-text processing, which is caused by the
129
            // presence of the DONT_ADVANCE flag.
130
            if class == CLASS_CODE_EOT {
131
                break;
132
            }
133

            
134
            self.max_ops -= 1;
135
            if !entry.dont_advance() || self.max_ops <= 0 {
136
                i += 1;
137
            }
138
        }
139

            
140
        Ok(())
141
    }
142
}
143

            
144
fn rearrange_glyphs<T>(verb: RearrangementVerb, seq: &mut [T]) {
145
    use RearrangementVerb::*;
146

            
147
    let len = seq.len();
148
    match verb {
149
        Verb1 if len > 1 => seq.rotate_left(1),
150
        Verb2 if len > 1 => seq.rotate_right(1),
151
        Verb3 if len > 1 => seq.swap(0, len - 1),
152
        Verb4 if len > 2 => seq.rotate_left(2),
153
        Verb5 if len > 1 => {
154
            seq.swap(0, 1);
155
            seq.rotate_left(2);
156
        }
157
        Verb6 if len > 2 => seq.rotate_right(2),
158
        Verb7 if len > 1 => {
159
            seq.rotate_right(2);
160
            seq.swap(0, 1);
161
        }
162
        Verb8 if len > 2 => {
163
            seq.rotate_right(2);
164
            seq[2..].rotate_left(1);
165
        }
166
        Verb9 if len > 2 => {
167
            seq.rotate_right(2);
168
            seq.swap(0, 1);
169
            seq[2..].rotate_left(1);
170
        }
171
        Verb10 if len > 2 => {
172
            seq.rotate_right(1);
173
            seq[1..].rotate_left(2);
174
        }
175
        Verb11 if len > 2 => {
176
            seq.swap(0, 1);
177
            seq.rotate_right(1);
178
            seq[1..].rotate_left(2);
179
        }
180
        Verb12 if len > 3 => {
181
            seq.rotate_right(2);
182
            seq[2..].rotate_left(2);
183
        }
184
        Verb13 if len > 3 => {
185
            seq.swap(0, 1);
186
            seq.rotate_right(2);
187
            seq[2..].rotate_left(2);
188
        }
189
        Verb14 if len > 3 => {
190
            seq.rotate_right(2);
191
            seq.swap(0, 1);
192
            seq[2..].rotate_left(2);
193
        }
194
        Verb15 if len > 3 => {
195
            seq.swap(0, 1);
196
            seq.rotate_right(2);
197
            seq.swap(0, 1);
198
            seq[2..].rotate_left(2);
199
        }
200
        _ => {}
201
    }
202
}
203

            
204
struct ContextualSubstitution<'a> {
205
    max_ops: isize,
206
    glyphs: &'a mut Vec<RawGlyph<()>>,
207
    next_state: u16,
208
    mark_index: Option<usize>,
209
}
210

            
211
impl<'a> ContextualSubstitution<'a> {
212
    fn new(glyphs: &'a mut Vec<RawGlyph<()>>) -> ContextualSubstitution<'a> {
213
        ContextualSubstitution {
214
            max_ops: MAX_OPS,
215
            glyphs,
216
            next_state: 0,
217
            mark_index: None,
218
        }
219
    }
220

            
221
    fn process_glyphs(
222
        &mut self,
223
        contextual_subtable: &ContextualSubtable<'_>,
224
    ) -> Result<(), ParseError> {
225
        let mut i = 0;
226
        while i <= self.glyphs.len() {
227
            // It appears that no substitutions occur if mark isn't set prior to end-of-text.
228
            if i == self.glyphs.len() && self.mark_index.is_none() {
229
                return Ok(());
230
            }
231

            
232
            let class = get_class(self.glyphs, i, contextual_subtable);
233
            let entry = get_entry(class, self.next_state, contextual_subtable)?;
234
            self.next_state = entry.next_state;
235

            
236
            if entry.mark_index != 0xFFFF {
237
                let lookup_table = contextual_subtable
238
                    .substitution_subtables
239
                    .get(usize::from(entry.mark_index))
240
                    .ok_or(ParseError::BadIndex)?;
241

            
242
                // In the event that a mark isn't set, implicitly mark the first glyph.
243
                let mark_index = self.mark_index.unwrap_or(0);
244

            
245
                let mark_glyph = self.glyphs[mark_index].glyph_index;
246
                if let Some(mark_glyph_subst) = lookup(mark_glyph, lookup_table) {
247
                    self.glyphs[mark_index].glyph_index = mark_glyph_subst;
248
                    self.glyphs[mark_index].glyph_origin = GlyphOrigin::Direct;
249
                }
250
            }
251

            
252
            if entry.current_index != 0xFFFF {
253
                // End-of-text substitutions appear to operate on the end glyph.
254
                let j = cmp::min(i, self.glyphs.len().saturating_sub(1));
255

            
256
                let lookup_table = contextual_subtable
257
                    .substitution_subtables
258
                    .get(usize::from(entry.current_index))
259
                    .ok_or(ParseError::BadIndex)?;
260

            
261
                let current_glyph = self.glyphs[j].glyph_index;
262
                if let Some(current_glyph_subst) = lookup(current_glyph, lookup_table) {
263
                    self.glyphs[j].glyph_index = current_glyph_subst;
264
                    self.glyphs[j].glyph_origin = GlyphOrigin::Direct;
265
                }
266
            }
267

            
268
            if entry.flags.contains(ContextualEntryFlag::SET_MARK) {
269
                self.mark_index = Some(i);
270
            }
271

            
272
            if class == CLASS_CODE_EOT {
273
                break;
274
            }
275

            
276
            self.max_ops -= 1;
277
            if !entry.flags.contains(ContextualEntryFlag::DONT_ADVANCE) || self.max_ops <= 0 {
278
                i += 1;
279
            }
280
        }
281

            
282
        Ok(())
283
    }
284
}
285

            
286
struct LigatureSubstitution<'a> {
287
    max_ops: isize,
288
    glyphs: &'a mut Vec<RawGlyph<()>>,
289
    next_state: u16,
290
    component_stack: TinyVec<[usize; 32]>,
291
}
292

            
293
impl<'a> LigatureSubstitution<'a> {
294
    fn new(glyphs: &'a mut Vec<RawGlyph<()>>) -> LigatureSubstitution<'a> {
295
        let len = glyphs.len();
296
        LigatureSubstitution {
297
            max_ops: MAX_OPS,
298
            glyphs,
299
            next_state: 0,
300
            component_stack: TinyVec::with_capacity(len),
301
        }
302
    }
303

            
304
    fn process_glyphs(
305
        &mut self,
306
        ligature_subtable: &LigatureSubtable<'_>,
307
    ) -> Result<(), ParseError> {
308
        let mut i = 0;
309
        while i <= self.glyphs.len() {
310
            let class = get_class(self.glyphs, i, ligature_subtable);
311
            let entry = get_entry(class, self.next_state, ligature_subtable)?;
312
            self.next_state = entry.next_state_index;
313

            
314
            if entry.flags.contains(LigatureEntryFlag::SET_COMPONENT) {
315
                if class == CLASS_CODE_EOT {
316
                    // `i` points to one past the buffer, so don't push it.
317
                } else if self.component_stack.last() == Some(&i) {
318
                    // When DONT_ADVANCE == true, avoid pushing the same index twice.
319
                } else {
320
                    self.component_stack.push(i);
321
                }
322
            }
323

            
324
            if entry.flags.contains(LigatureEntryFlag::PERFORM_ACTION) {
325
                let mut action_index = usize::from(entry.lig_action_index);
326
                let mut ligature_list_index = 0;
327

            
328
                let mut unicodes = tiny_vec!([char; 32]);
329
                let mut end_i = None;
330
                'stack: loop {
331
                    let popped_i = match self.component_stack.pop() {
332
                        Some(popped_i) => popped_i,
333
                        None => break 'stack, // Stack underflow.
334
                    };
335
                    if end_i.is_none() {
336
                        end_i = Some(popped_i);
337
                    }
338

            
339
                    let glyph = &mut self.glyphs[popped_i];
340
                    let glyph_index = glyph.glyph_index;
341
                    let variation = glyph.variation;
342

            
343
                    // Mark glyph for deletion; copy its `.unicodes` content into a temp buffer.
344
                    glyph.glyph_index = DELETED_GLYPH;
345
                    for &u in glyph.unicodes.iter().rev() {
346
                        unicodes.push(u);
347
                    }
348

            
349
                    let action = &ligature_subtable.action_table.0[action_index];
350
                    action_index += 1;
351

            
352
                    let component_index = i32::from(glyph_index) + action.offset();
353
                    let component_index = usize::try_from(component_index)?;
354

            
355
                    ligature_list_index += &ligature_subtable
356
                        .component_table
357
                        .component_array
358
                        .read_item(component_index)?;
359

            
360
                    // `last` implies `store`.
361
                    if action.last() || action.store() {
362
                        let ligature_glyph_index = ligature_subtable
363
                            .ligature_list
364
                            .get(ligature_list_index)
365
                            .ok_or(ParseError::BadIndex)?;
366

            
367
                        *glyph = RawGlyph {
368
                            unicodes: unicodes.iter().rev().copied().collect(),
369
                            glyph_index: ligature_glyph_index,
370
                            liga_component_pos: 0,
371
                            glyph_origin: GlyphOrigin::Direct,
372
                            flags: RawGlyphFlags::empty(),
373
                            extra_data: (),
374
                            variation,
375
                        };
376

            
377
                        // Push ligature onto stack, only when the next state is non-zero.
378
                        if self.next_state != 0 {
379
                            self.component_stack.push(popped_i);
380
                        }
381

            
382
                        i = end_i.ok_or(ParseError::BadIndex)?;
383
                    }
384

            
385
                    if action.last() {
386
                        break 'stack;
387
                    }
388
                }
389
            }
390

            
391
            if class == CLASS_CODE_EOT {
392
                break;
393
            }
394

            
395
            self.max_ops -= 1;
396
            if !entry.flags.contains(LigatureEntryFlag::DONT_ADVANCE) || self.max_ops <= 0 {
397
                i += 1;
398
            }
399
        }
400

            
401
        Ok(())
402
    }
403
}
404

            
405
fn noncontextual_substitution(
406
    glyphs: &mut [RawGlyph<()>],
407
    noncontextual_subtable: &NonContextualSubtable<'_>,
408
) -> Result<(), ParseError> {
409
    for glyph in glyphs.iter_mut() {
410
        match lookup(glyph.glyph_index, &noncontextual_subtable.lookup_table) {
411
            Some(subst) if subst != glyph.glyph_index => {
412
                glyph.glyph_index = subst;
413
                glyph.glyph_origin = GlyphOrigin::Direct;
414
            }
415
            Some(_) | None => (),
416
        }
417
    }
418
    Ok(())
419
}
420

            
421
struct Insertion<'a> {
422
    max_ops: isize,
423
    glyphs: &'a mut Vec<RawGlyph<()>>,
424
    next_state: u16,
425
    mark_index: Option<usize>,
426
}
427

            
428
impl<'a> Insertion<'a> {
429
    fn new(glyphs: &'a mut Vec<RawGlyph<()>>) -> Insertion<'a> {
430
        Insertion {
431
            max_ops: MAX_OPS,
432
            glyphs,
433
            next_state: 0,
434
            mark_index: None,
435
        }
436
    }
437

            
438
    fn process_glyphs(
439
        &mut self,
440
        insertion_subtable: &InsertionSubtable<'_>,
441
    ) -> Result<(), ParseError> {
442
        let mut i = 0;
443
        while i <= self.glyphs.len() {
444
            let class = get_class(self.glyphs, i, insertion_subtable);
445
            let entry = get_entry(class, self.next_state, insertion_subtable)?;
446
            self.next_state = entry.next_state;
447

            
448
            let mark_pos = i;
449

            
450
            if entry.marked_insert_index != 0xFFFF {
451
                let before = entry.marked_insert_before();
452
                let count = entry.marked_insert_count();
453

            
454
                if self.glyphs.len() + count >= MAX_LEN {
455
                    return Ok(());
456
                }
457

            
458
                let mut mark_index = self.mark_index.unwrap_or(0);
459
                if !before {
460
                    mark_index += 1;
461
                }
462

            
463
                let mut insert_index = usize::from(entry.marked_insert_index);
464
                for j in 0..count {
465
                    let glyph = RawGlyph {
466
                        // Use dotted circle as placeholder character for inserted glyph.
467
                        unicodes: tiny_vec!([char; 1] => '◌'),
468
                        glyph_index: insertion_subtable.action_table.0[insert_index].0,
469
                        liga_component_pos: 0,
470
                        glyph_origin: GlyphOrigin::Direct,
471
                        flags: RawGlyphFlags::empty(),
472
                        extra_data: (),
473
                        variation: None,
474
                    };
475

            
476
                    self.glyphs.insert(j + mark_index, glyph);
477
                    insert_index += 1;
478
                }
479

            
480
                i += count;
481
            }
482

            
483
            if entry.current_insert_index != 0xFFFF {
484
                let before = entry.current_insert_before();
485
                let count = entry.current_insert_count();
486

            
487
                if self.glyphs.len() + count >= MAX_LEN {
488
                    return Ok(());
489
                }
490

            
491
                // End-of-text substitutions appear to operate on the end glyph.
492
                let mut k = cmp::min(i, self.glyphs.len().saturating_sub(1));
493
                if !before {
494
                    k += 1;
495
                }
496

            
497
                let mut insert_index = usize::from(entry.current_insert_index);
498
                for j in 0..count {
499
                    let glyph = RawGlyph {
500
                        // Use dotted circle as placeholder character for inserted glyph.
501
                        unicodes: tiny_vec!([char; 1] => '◌'),
502
                        glyph_index: insertion_subtable.action_table.0[insert_index].0,
503
                        liga_component_pos: 0,
504
                        glyph_origin: GlyphOrigin::Direct,
505
                        flags: RawGlyphFlags::empty(),
506
                        extra_data: (),
507
                        variation: None,
508
                    };
509

            
510
                    self.glyphs.insert(j + k, glyph);
511
                    insert_index += 1;
512
                }
513

            
514
                if entry.dont_advance() {
515
                    // The documentation for DONT_ADVANCE states: "If set, don't update the glyph
516
                    // index before going to the new state."
517
                } else {
518
                    i += count;
519
                }
520
            }
521

            
522
            if entry.set_mark() {
523
                self.mark_index = Some(mark_pos);
524
            }
525

            
526
            if class == CLASS_CODE_EOT {
527
                break;
528
            }
529

            
530
            self.max_ops -= 1;
531
            if !entry.dont_advance() || self.max_ops <= 0 {
532
                i += 1;
533
            }
534
        }
535

            
536
        Ok(())
537
    }
538
}
539

            
540
pub fn apply(
541
    morx_table: &MorxTable<'_>,
542
    glyphs: &mut Vec<RawGlyph<()>>,
543
    feature_mask: FeatureMask,
544
    script_tag: u32,
545
) -> Result<(), ParseError> {
546
    for chain in morx_table.chains.iter() {
547
        apply_chain(chain, glyphs, feature_mask, script_tag)?;
548
    }
549
    Ok(())
550
}
551

            
552
fn apply_chain(
553
    chain: &Chain<'_>,
554
    glyphs: &mut Vec<RawGlyph<()>>,
555
    feature_mask: FeatureMask,
556
    script_tag: u32,
557
) -> Result<(), ParseError> {
558
    let subfeatureflags: u32 = subfeatureflags(chain, feature_mask)?;
559

            
560
    for subtable in chain.subtables.iter() {
561
        if subfeatureflags & subtable.subtable_header.sub_feature_flags != 0 {
562
            apply_subtable(subtable, glyphs, script_tag)?;
563
        }
564
    }
565

            
566
    Ok(())
567
}
568

            
569
fn apply_subtable(
570
    subtable: &Subtable<'_>,
571
    glyphs: &mut Vec<RawGlyph<()>>,
572
    script_tag: u32,
573
) -> Result<(), ParseError> {
574
    let reverse_glyphs = reverse_glyphs(&subtable.subtable_header, script_tag);
575
    if reverse_glyphs {
576
        glyphs.reverse();
577
    }
578

            
579
    match &subtable.subtable_body {
580
        SubtableType::Rearrangement(rearrangement_subtable) => {
581
            let mut rearrangement_trans = RearrangementTransformation::new(glyphs);
582
            rearrangement_trans.process_glyphs(rearrangement_subtable)?;
583
        }
584
        SubtableType::Contextual(contextual_subtable) => {
585
            let mut contextual_subst = ContextualSubstitution::new(glyphs);
586
            contextual_subst.process_glyphs(contextual_subtable)?;
587
        }
588
        SubtableType::Ligature(ligature_subtable) => {
589
            let mut liga_subst = LigatureSubstitution::new(glyphs);
590
            liga_subst.process_glyphs(ligature_subtable)?;
591
        }
592
        SubtableType::NonContextual(noncontextual_subtable) => {
593
            noncontextual_substitution(glyphs, noncontextual_subtable)?
594
        }
595
        SubtableType::Insertion(insertion_subtable) => {
596
            let mut insertion = Insertion::new(glyphs);
597
            insertion.process_glyphs(insertion_subtable)?
598
        }
599
    }
600

            
601
    if reverse_glyphs {
602
        glyphs.reverse();
603
    }
604

            
605
    Ok(())
606
}
607

            
608
// Determines if the glyph buffer should be reversed prior to (and after) applying a subtable.
609
// Note: the glyph buffer is always in logical order.
610
fn reverse_glyphs(subtable_header: &SubtableHeader, script_tag: u32) -> bool {
611
    let descending_order = subtable_header.coverage.descending_order();
612
    let logical_order = subtable_header.coverage.logical_order();
613

            
614
    match (descending_order, logical_order) {
615
        // The subtable is processed in layout order (the same order as the glyphs, which is always
616
        // left-to-right).
617
        (false, false) => match horizontal_text_direction(script_tag) {
618
            TextDirection::LeftToRight => false,
619
            TextDirection::RightToLeft => true,
620
        },
621
        // The subtable is processed in reverse layout order (the order opposite that of the
622
        // glyphs, which is always right-to-left).
623
        (true, false) => match horizontal_text_direction(script_tag) {
624
            TextDirection::LeftToRight => true,
625
            TextDirection::RightToLeft => false,
626
        },
627
        // The subtable is processed in logical order (the same order as the characters, which may
628
        // be left-to-right or right-to-left).
629
        (false, true) => false,
630
        // The subtable is processed in reverse logical order (the order opposite that of the
631
        // characters, which may be right-to-left or left-to-right).
632
        (true, true) => true,
633
    }
634
}
635

            
636
fn subfeatureflags(chain: &Chain<'_>, feature_mask: FeatureMask) -> Result<u32, ParseError> {
637
    let mut subfeature_flags = chain.chain_header.default_flags;
638

            
639
    for entry in chain.feature_array.iter() {
640
        if should_apply_feature(entry, feature_mask) {
641
            subfeature_flags = (subfeature_flags & entry.disable_flags) | entry.enable_flags;
642
        }
643
    }
644
    Ok(subfeature_flags)
645
}
646

            
647
fn should_apply_feature(entry: morx::Feature, mask: FeatureMask) -> bool {
648
    // Feature type:
649
    const LIGATURE_TYPE: u16 = 1;
650
    // Feature selectors:
651
    const COMMON_LIGATURES_ON: u16 = 2;
652
    const COMMON_LIGATURES_OFF: u16 = 3;
653
    const CONTEXTUAL_LIGATURES_ON: u16 = 18;
654
    const CONTEXTUAL_LIGATURES_OFF: u16 = 19;
655
    const HISTORICAL_LIGATURES_ON: u16 = 20;
656
    const HISTORICAL_LIGATURES_OFF: u16 = 21;
657

            
658
    // Feature type:
659
    const NUMBER_CASE_TYPE: u16 = 21;
660
    // Feature selectors:
661
    const OLD_STYLE_NUMBERS: u16 = 0;
662
    const LINING_NUMBERS: u16 = 1;
663

            
664
    // Feature type:
665
    const NUMBER_SPACING_TYPE: u16 = 6;
666
    // Feature selectors:
667
    const TABULAR_NUMBERS: u16 = 0;
668
    const PROPORTIONAL_NUMBERS: u16 = 1;
669

            
670
    // Feature type:
671
    const FRACTION_TYPE: u16 = 11;
672
    // Feature selectors:
673
    const NO_FRACTIONS: u16 = 0;
674
    const FRACTIONS_STACKED: u16 = 1;
675
    const FRACTIONS_DIAGONAL: u16 = 2;
676

            
677
    // Feature type:
678
    const VERTICAL_POSITION_TYPE: u16 = 10;
679
    // Feature selectors:
680
    const ORDINALS: u16 = 3;
681

            
682
    // Feature type:
683
    const TYPOGRAPHIC_EXTRAS_TYPE: u16 = 14;
684
    // Feature selectors:
685
    const SLASHED_ZERO_ON: u16 = 4;
686
    const SLASHED_ZERO_OFF: u16 = 5;
687

            
688
    // Feature type:
689
    const LOWERCASE_TYPE: u16 = 37;
690
    // Feature selectors:
691
    const LOWERCASE_SMALL_CAPS: u16 = 1;
692

            
693
    // Feature type:
694
    const UPPERCASE_TYPE: u16 = 38;
695
    // Feature selectors:
696
    const UPPERCASE_SMALL_CAPS: u16 = 1;
697

            
698
    match (entry.feature_type, entry.feature_setting) {
699
        (NUMBER_CASE_TYPE, LINING_NUMBERS) => mask.contains(Feature::LNUM),
700
        (NUMBER_CASE_TYPE, OLD_STYLE_NUMBERS) => mask.contains(Feature::ONUM),
701
        (NUMBER_SPACING_TYPE, PROPORTIONAL_NUMBERS) => mask.contains(Feature::PNUM),
702
        (NUMBER_SPACING_TYPE, TABULAR_NUMBERS) => mask.contains(Feature::TNUM),
703
        (FRACTION_TYPE, FRACTIONS_DIAGONAL) => mask.contains(Feature::FRAC),
704
        (FRACTION_TYPE, FRACTIONS_STACKED) => mask.contains(Feature::AFRC),
705
        (FRACTION_TYPE, NO_FRACTIONS) => {
706
            !mask.contains(Feature::FRAC) && !mask.contains(Feature::AFRC)
707
        }
708
        (VERTICAL_POSITION_TYPE, ORDINALS) => mask.contains(Feature::ORDN),
709
        (TYPOGRAPHIC_EXTRAS_TYPE, SLASHED_ZERO_ON) => mask.contains(Feature::ZERO),
710
        (TYPOGRAPHIC_EXTRAS_TYPE, SLASHED_ZERO_OFF) => !mask.contains(Feature::ZERO),
711
        (LOWERCASE_TYPE, LOWERCASE_SMALL_CAPS) => {
712
            mask.contains(Feature::SMCP) || mask.contains(Feature::C2SC)
713
        }
714
        (UPPERCASE_TYPE, UPPERCASE_SMALL_CAPS) => mask.contains(Feature::C2SC),
715
        (LIGATURE_TYPE, COMMON_LIGATURES_ON) => mask.contains(Feature::LIGA),
716
        (LIGATURE_TYPE, COMMON_LIGATURES_OFF) => !mask.contains(Feature::LIGA),
717
        (LIGATURE_TYPE, HISTORICAL_LIGATURES_ON) => mask.contains(Feature::HLIG),
718
        (LIGATURE_TYPE, HISTORICAL_LIGATURES_OFF) => !mask.contains(Feature::HLIG),
719
        (LIGATURE_TYPE, CONTEXTUAL_LIGATURES_ON) => mask.contains(Feature::CLIG),
720
        (LIGATURE_TYPE, CONTEXTUAL_LIGATURES_OFF) => !mask.contains(Feature::CLIG),
721
        _ => false,
722
    }
723
}
724

            
725
#[cfg(test)]
726
mod tests {
727
    use super::*;
728
    use crate::font::MatchingPresentation;
729
    use crate::gsub::FeatureMaskExt;
730
    use crate::tables::{FontTableProvider, MaxpTable, OpenTypeFont};
731
    use crate::tests::read_fixture;
732
    use crate::{binary::read::ReadScope, tag, Font};
733

            
734
    mod rearrangement {
735
        use super::*;
736
        use RearrangementVerb::*;
737

            
738
        #[test]
739
        fn test_verb1() {
740
            let mut seq = ['A', 'x'];
741
            rearrange_glyphs(Verb1, &mut seq);
742
            assert_eq!(['x', 'A'], seq);
743

            
744
            let mut seq = ['A'];
745
            rearrange_glyphs(Verb1, &mut seq);
746
            assert_eq!(['A'], seq);
747
        }
748

            
749
        #[test]
750
        fn test_verb2() {
751
            let mut seq = ['x', 'D'];
752
            rearrange_glyphs(Verb2, &mut seq);
753
            assert_eq!(['D', 'x'], seq);
754

            
755
            let mut seq = ['D'];
756
            rearrange_glyphs(Verb2, &mut seq);
757
            assert_eq!(['D'], seq);
758
        }
759

            
760
        #[test]
761
        fn test_verb3() {
762
            let mut seq = ['A', 'x', 'D'];
763
            rearrange_glyphs(Verb3, &mut seq);
764
            assert_eq!(['D', 'x', 'A'], seq);
765

            
766
            let mut seq = ['A', 'D'];
767
            rearrange_glyphs(Verb3, &mut seq);
768
            assert_eq!(['D', 'A'], seq);
769
        }
770

            
771
        #[test]
772
        fn test_verb4() {
773
            let mut seq = ['A', 'B', 'x'];
774
            rearrange_glyphs(Verb4, &mut seq);
775
            assert_eq!(['x', 'A', 'B'], seq);
776

            
777
            let mut seq = ['A', 'B'];
778
            rearrange_glyphs(Verb4, &mut seq);
779
            assert_eq!(['A', 'B'], seq);
780
        }
781

            
782
        #[test]
783
        fn test_verb5() {
784
            let mut seq = ['A', 'B', 'x'];
785
            rearrange_glyphs(Verb5, &mut seq);
786
            assert_eq!(['x', 'B', 'A'], seq);
787

            
788
            let mut seq = ['A', 'B'];
789
            rearrange_glyphs(Verb5, &mut seq);
790
            assert_eq!(['B', 'A'], seq);
791
        }
792

            
793
        #[test]
794
        fn test_verb6() {
795
            let mut seq = ['x', 'C', 'D'];
796
            rearrange_glyphs(Verb6, &mut seq);
797
            assert_eq!(['C', 'D', 'x'], seq);
798

            
799
            let mut seq = ['C', 'D'];
800
            rearrange_glyphs(Verb6, &mut seq);
801
            assert_eq!(['C', 'D'], seq);
802
        }
803

            
804
        #[test]
805
        fn test_verb7() {
806
            let mut seq = ['x', 'C', 'D'];
807
            rearrange_glyphs(Verb7, &mut seq);
808
            assert_eq!(['D', 'C', 'x'], seq);
809

            
810
            let mut seq = ['C', 'D'];
811
            rearrange_glyphs(Verb7, &mut seq);
812
            assert_eq!(['D', 'C'], seq);
813
        }
814

            
815
        #[test]
816
        fn test_verb8() {
817
            let mut seq = ['A', 'x', 'C', 'D'];
818
            rearrange_glyphs(Verb8, &mut seq);
819
            assert_eq!(['C', 'D', 'x', 'A'], seq);
820

            
821
            let mut seq = ['A', 'C', 'D'];
822
            rearrange_glyphs(Verb8, &mut seq);
823
            assert_eq!(['C', 'D', 'A'], seq);
824
        }
825

            
826
        #[test]
827
        fn test_verb9() {
828
            let mut seq = ['A', 'x', 'C', 'D'];
829
            rearrange_glyphs(Verb9, &mut seq);
830
            assert_eq!(['D', 'C', 'x', 'A'], seq);
831

            
832
            let mut seq = ['A', 'C', 'D'];
833
            rearrange_glyphs(Verb9, &mut seq);
834
            assert_eq!(['D', 'C', 'A'], seq);
835
        }
836

            
837
        #[test]
838
        fn test_verb10() {
839
            let mut seq = ['A', 'B', 'x', 'D'];
840
            rearrange_glyphs(Verb10, &mut seq);
841
            assert_eq!(['D', 'x', 'A', 'B'], seq);
842

            
843
            let mut seq = ['A', 'B', 'D'];
844
            rearrange_glyphs(Verb10, &mut seq);
845
            assert_eq!(['D', 'A', 'B'], seq);
846
        }
847

            
848
        #[test]
849
        fn test_verb11() {
850
            let mut seq = ['A', 'B', 'x', 'D'];
851
            rearrange_glyphs(Verb11, &mut seq);
852
            assert_eq!(['D', 'x', 'B', 'A'], seq);
853

            
854
            let mut seq = ['A', 'B', 'D'];
855
            rearrange_glyphs(Verb11, &mut seq);
856
            assert_eq!(['D', 'B', 'A'], seq);
857
        }
858

            
859
        #[test]
860
        fn test_verb12() {
861
            let mut seq = ['A', 'B', 'x', 'C', 'D'];
862
            rearrange_glyphs(Verb12, &mut seq);
863
            assert_eq!(['C', 'D', 'x', 'A', 'B'], seq);
864

            
865
            let mut seq = ['A', 'B', 'C', 'D'];
866
            rearrange_glyphs(Verb12, &mut seq);
867
            assert_eq!(['C', 'D', 'A', 'B'], seq);
868
        }
869

            
870
        #[test]
871
        fn test_verb13() {
872
            let mut seq = ['A', 'B', 'x', 'C', 'D'];
873
            rearrange_glyphs(Verb13, &mut seq);
874
            assert_eq!(['C', 'D', 'x', 'B', 'A'], seq);
875

            
876
            let mut seq = ['A', 'B', 'C', 'D'];
877
            rearrange_glyphs(Verb13, &mut seq);
878
            assert_eq!(['C', 'D', 'B', 'A'], seq);
879
        }
880

            
881
        #[test]
882
        fn test_verb14() {
883
            let mut seq = ['A', 'B', 'x', 'C', 'D'];
884
            rearrange_glyphs(Verb14, &mut seq);
885
            assert_eq!(['D', 'C', 'x', 'A', 'B'], seq);
886

            
887
            let mut seq = ['A', 'B', 'C', 'D'];
888
            rearrange_glyphs(Verb14, &mut seq);
889
            assert_eq!(['D', 'C', 'A', 'B'], seq);
890
        }
891

            
892
        #[test]
893
        fn test_verb15() {
894
            let mut seq = ['A', 'B', 'x', 'C', 'D'];
895
            rearrange_glyphs(Verb15, &mut seq);
896
            assert_eq!(['D', 'C', 'x', 'B', 'A'], seq);
897

            
898
            let mut seq = ['A', 'B', 'C', 'D'];
899
            rearrange_glyphs(Verb15, &mut seq);
900
            assert_eq!(['D', 'C', 'B', 'A'], seq);
901
        }
902
    }
903

            
904
    #[test]
905
    #[cfg(feature = "prince")]
906
    fn zapfino() -> Result<(), ParseError> {
907
        let buffer = read_fixture("../../../tests/data/fonts/morx/Zapfino.ttf");
908
        let otf = ReadScope::new(&buffer).read::<OpenTypeFont<'_>>().unwrap();
909
        let table_provider = otf.table_provider(0).expect("error reading font file");
910

            
911
        let maxp_data = table_provider
912
            .read_table_data(tag::MAXP)
913
            .expect("unable to read maxp table data");
914
        let maxp = ReadScope::new(&maxp_data).read::<MaxpTable>().unwrap();
915
        let morx_data = table_provider
916
            .read_table_data(tag::MORX)
917
            .expect("unable to read morx data");
918
        let morx = ReadScope::new(&morx_data)
919
            .read_dep::<MorxTable<'_>>(maxp.num_glyphs)
920
            .expect("unable to parse morx table");
921

            
922
        let provider = otf.table_provider(0).expect("error reading font file");
923
        let mut font = Font::new(provider)?;
924

            
925
        // Map text to glyphs and then apply font shaping
926
        let script = tag!(b"latn");
927
        let mut glyphs = font.map_glyphs("ptgffigpfl", script, MatchingPresentation::NotRequired);
928
        let feature_mask = FeatureMask::default_mask();
929
        apply(&morx, &mut glyphs, feature_mask, script)?;
930

            
931
        let expected = [
932
            (585, "p"),
933
            (604, "t"),
934
            (541, "g"),
935
            (1086, "ffi"),
936
            (65535, "f"),
937
            (65535, "i"),
938
            (541, "g"),
939
            (1108, "pf"),
940
            (65535, "f"),
941
            (565, "l"),
942
        ];
943
        let actual = glyphs
944
            .iter()
945
            .map(|glyph| (glyph.glyph_index, glyph.unicodes.iter().collect::<String>()))
946
            .collect::<Vec<_>>();
947
        let actual = actual
948
            .iter()
949
            .map(|(gid, text)| (*gid, text.as_str()))
950
            .collect::<Vec<_>>();
951
        assert_eq!(actual, expected);
952

            
953
        let mut glyphs = font.map_glyphs("ptpfgffigpfl", script, MatchingPresentation::NotRequired);
954
        let feature_mask = FeatureMask::default_mask();
955
        apply(&morx, &mut glyphs, feature_mask, script)?;
956

            
957
        let expected = [
958
            (585, "p"),
959
            (604, "t"),
960
            (1108, "pf"),
961
            (65535, "f"),
962
            (541, "g"),
963
            (1086, "ffi"),
964
            (65535, "f"),
965
            (65535, "i"),
966
            (541, "g"),
967
            (1108, "pf"),
968
            (65535, "f"),
969
            (565, "l"),
970
        ];
971
        let actual = glyphs
972
            .iter()
973
            .map(|glyph| (glyph.glyph_index, glyph.unicodes.iter().collect::<String>()))
974
            .collect::<Vec<_>>();
975
        let actual = actual
976
            .iter()
977
            .map(|(gid, text)| (*gid, text.as_str()))
978
            .collect::<Vec<_>>();
979
        assert_eq!(actual, expected);
980

            
981
        // There is a ligature for the whole string Zapfino
982
        let mut glyphs = font.map_glyphs("Zapfino", script, MatchingPresentation::NotRequired);
983
        let feature_mask = FeatureMask::default_mask();
984
        apply(&morx, &mut glyphs, feature_mask, script)?;
985

            
986
        let expected = [
987
            (1059, "Zapfino"),
988
            (65535, "a"),
989
            (65535, "p"),
990
            (65535, "f"),
991
            (65535, "i"),
992
            (65535, "n"),
993
            (65535, "o"),
994
        ];
995
        let actual = glyphs
996
            .iter()
997
            .map(|glyph| (glyph.glyph_index, glyph.unicodes.iter().collect::<String>()))
998
            .collect::<Vec<_>>();
999
        let actual = actual
            .iter()
            .map(|(gid, text)| (*gid, text.as_str()))
            .collect::<Vec<_>>();
        assert_eq!(actual, expected);
        Ok(())
    }
}