1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//! Layout features.

pub use opentype::layout::{Language, Script};

use std::collections::{BTreeMap, BTreeSet};
use std::io::Result;

use opentype::layout::{Directory, Feature};
use typeface::Tape;

use crate::formats::opentype::cache::Cache;

/// Layout features.
pub type Features = BTreeMap<Type, Value>;

/// A type.
pub type Type = Feature;

/// A value.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Value {
    /// The scripts and languages.
    pub scripts: BTreeMap<Script, BTreeSet<Option<Language>>>,
}

pub(crate) fn read<T: Tape>(cache: &mut Cache<T>) -> Result<Features> {
    let mut values = Features::default();
    if let Some(table) = cache.try_glyph_positioning()? {
        populate(&mut values, table);
    }
    if let Some(table) = cache.try_glyph_substitution()? {
        populate(&mut values, table);
    }
    Ok(values)
}

fn populate<T>(values: &mut Features, table: &Directory<T>) {
    for (i, header) in table.scripts.headers.iter().enumerate() {
        let script = Script::from_tag(&header.tag);
        if let Some(record) = table.scripts.records[i].default_language.as_ref() {
            for index in record.feature_indices.iter() {
                if let Some(header) = table.features.headers.get(*index as usize) {
                    let feature = Feature::from_tag(&header.tag);
                    let value = values.entry(feature).or_default();
                    value.scripts.entry(script).or_default().insert(None);
                }
            }
        }
        for (j, header) in table.scripts.records[i].language_headers.iter().enumerate() {
            let language = Language::from_tag(&header.tag);
            let record = &table.scripts.records[i].language_records[j];
            for index in record.feature_indices.iter() {
                if let Some(header) = table.features.headers.get(*index as usize) {
                    let feature = Feature::from_tag(&header.tag);
                    let value = values.entry(feature).or_default();
                    value
                        .scripts
                        .entry(script)
                        .or_default()
                        .insert(Some(language));
                }
            }
        }
    }
}