rustybuzz/hb/
aat_map.rs

1use alloc::vec::Vec;
2
3use super::aat_layout::*;
4use super::{hb_font_t, hb_mask_t, hb_tag_t};
5
6#[derive(Default)]
7pub struct hb_aat_map_t {
8    pub chain_flags: Vec<hb_mask_t>,
9}
10
11#[derive(Copy, Clone)]
12pub struct feature_info_t {
13    pub kind: u16,
14    pub setting: u16,
15    pub is_exclusive: bool,
16}
17
18#[derive(Default)]
19pub struct hb_aat_map_builder_t {
20    pub features: Vec<feature_info_t>,
21}
22
23impl hb_aat_map_builder_t {
24    pub fn add_feature(&mut self, face: &hb_font_t, tag: hb_tag_t, value: u32) -> Option<()> {
25        const FEATURE_TYPE_CHARACTER_ALTERNATIVES: u16 = 17;
26
27        let feat = face.tables().feat?;
28
29        if tag == hb_tag_t::from_bytes(b"aalt") {
30            let exposes_feature = feat
31                .names
32                .find(FEATURE_TYPE_CHARACTER_ALTERNATIVES)
33                .map(|f| f.setting_names.len() != 0)
34                .unwrap_or(false);
35
36            if !exposes_feature {
37                return Some(());
38            }
39
40            self.features.push(feature_info_t {
41                kind: FEATURE_TYPE_CHARACTER_ALTERNATIVES,
42                setting: value as u16,
43                is_exclusive: true,
44            });
45        }
46
47        let idx = feature_mappings
48            .binary_search_by(|map| map.ot_feature_tag.cmp(&tag))
49            .ok()?;
50        let mapping = &feature_mappings[idx];
51
52        let mut feature = feat.names.find(mapping.aat_feature_type as u16);
53
54        match feature {
55            Some(feature) if feature.setting_names.len() != 0 => {}
56            _ => {
57                // Special case: Chain::compile_flags will fall back to the deprecated version of
58                // small-caps if necessary, so we need to check for that possibility.
59                // https://github.com/harfbuzz/harfbuzz/issues/2307
60                if mapping.aat_feature_type == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE
61                    && mapping.selector_to_enable
62                        == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS
63                {
64                    feature = feat
65                        .names
66                        .find(HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE as u16);
67                }
68            }
69        }
70
71        match feature {
72            Some(feature) if feature.setting_names.len() != 0 => {
73                let setting = if value != 0 {
74                    mapping.selector_to_enable
75                } else {
76                    mapping.selector_to_disable
77                } as u16;
78
79                self.features.push(feature_info_t {
80                    kind: mapping.aat_feature_type as u16,
81                    setting,
82                    is_exclusive: feature.exclusive,
83                });
84            }
85            _ => {}
86        }
87
88        Some(())
89    }
90
91    pub fn has_feature(&self, kind: u16, setting: u16) -> bool {
92        self.features
93            .binary_search_by(|probe| {
94                if probe.kind != kind {
95                    probe.kind.cmp(&kind)
96                } else {
97                    probe.setting.cmp(&setting)
98                }
99            })
100            .is_ok()
101    }
102
103    pub fn compile(&mut self, face: &hb_font_t) -> hb_aat_map_t {
104        // Sort features and merge duplicates.
105        self.features.sort_by(|a, b| {
106            if a.kind != b.kind {
107                a.kind.cmp(&b.kind)
108            } else if !a.is_exclusive && (a.setting & !1) != (b.setting & !1) {
109                a.setting.cmp(&b.setting)
110            } else {
111                core::cmp::Ordering::Equal
112            }
113        });
114
115        let mut j = 0;
116        for i in 0..self.features.len() {
117            // Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
118            // respectively, so we mask out the low-order bit when checking for "duplicates"
119            // (selectors referring to the same feature setting) here.
120            let non_exclusive = !self.features[i].is_exclusive
121                && (self.features[i].setting & !1) != (self.features[j].setting & !1);
122
123            if self.features[i].kind != self.features[j].kind || non_exclusive {
124                j += 1;
125                self.features[j] = self.features[i];
126            }
127        }
128        self.features.truncate(j + 1);
129
130        super::aat_layout_morx_table::compile_flags(face, self).unwrap_or_default()
131    }
132}