Skip to main content

harfrust/hb/aat/
mod.rs

1pub mod layout;
2pub mod layout_common;
3pub mod layout_kerx_table;
4pub mod layout_morx_table;
5pub mod layout_trak_table;
6pub mod map;
7
8use crate::hb::aat::layout_kerx_table::KerxSubtableCache;
9use crate::hb::aat::layout_morx_table::MorxSubtableCache;
10use crate::hb::kerning::KernSubtableCache;
11use crate::hb::ot::OtTables;
12use crate::hb::tables::TableRanges;
13use alloc::vec::Vec;
14use read_fonts::{
15    tables::{ankr::Ankr, feat::Feat, kern::Kern, kerx::Kerx, morx::Morx, trak::Trak},
16    FontRef, TableProvider,
17};
18
19#[derive(Default)]
20pub struct AatCache {
21    pub morx: Vec<MorxSubtableCache>,
22    pub kern: Vec<KernSubtableCache>,
23    pub kerx: Vec<KerxSubtableCache>,
24}
25
26impl AatCache {
27    #[allow(unused)]
28    pub fn new<'a>(font: &impl TableProvider<'a>) -> Self {
29        let mut cache = Self::default();
30        let num_glyphs = font
31            .maxp()
32            .map(|maxp| maxp.num_glyphs() as u32)
33            .unwrap_or_default();
34        if let Ok(morx) = font.morx() {
35            let chains = morx.chains();
36            for chain in morx.chains().iter() {
37                let Ok(chain) = chain else {
38                    continue;
39                };
40                for subtable in chain.subtables().iter() {
41                    let Ok(subtable) = subtable else {
42                        continue;
43                    };
44                    cache
45                        .morx
46                        .push(MorxSubtableCache::new(&subtable, num_glyphs));
47                }
48            }
49        }
50        if let Ok(kern) = font.kern() {
51            for subtable in kern.subtables() {
52                let Ok(subtable) = subtable else {
53                    continue;
54                };
55                cache
56                    .kern
57                    .push(KernSubtableCache::new(&subtable, num_glyphs));
58            }
59        }
60        if let Ok(kerx) = font.kerx() {
61            for subtable in kerx.subtables().iter() {
62                let Ok(subtable) = subtable else {
63                    continue;
64                };
65                cache
66                    .kerx
67                    .push(KerxSubtableCache::new(&subtable, num_glyphs));
68            }
69        }
70        cache
71    }
72}
73
74#[derive(Clone, Default)]
75pub struct AatTables<'a> {
76    pub morx: Option<(Morx<'a>, &'a [MorxSubtableCache])>,
77    pub ankr: Option<Ankr<'a>>,
78    pub kern: Option<(Kern<'a>, &'a [KernSubtableCache])>,
79    pub kerx: Option<(Kerx<'a>, &'a [KerxSubtableCache])>,
80    pub trak: Option<Trak<'a>>,
81    pub feat: Option<Feat<'a>>,
82}
83
84use crate::hb::algs::HB_CODEPOINT_ENCODE3 as encode3;
85
86/// Blocklist specific broken morx tables identified by the combination of
87/// morx, GSUB, and GDEF table lengths.
88fn is_morx_blocklisted(morx_len: u32, gsub_len: u32, gdef_len: u32) -> bool {
89    const BLOCKLIST: &[u64] = &[
90        // AALMAGHRIBI.ttf — https://github.com/harfbuzz/harfbuzz/issues/4108
91        encode3(19892, 2794, 340),
92    ];
93    let key = encode3(morx_len, gsub_len, gdef_len);
94    BLOCKLIST.contains(&key)
95}
96
97impl<'a> AatTables<'a> {
98    pub fn new(font: &FontRef<'a>, cache: &'a AatCache, table_ranges: &TableRanges) -> Self {
99        let morx = if is_morx_blocklisted(
100            table_ranges.morx.len(),
101            table_ranges.gsub.len(),
102            table_ranges.gdef.len(),
103        ) {
104            None
105        } else {
106            table_ranges
107                .morx
108                .resolve_table(font)
109                .map(|table| (table, cache.morx.as_slice()))
110        };
111        let ankr = table_ranges.ankr.resolve_table(font);
112        let kern = table_ranges
113            .kern
114            .resolve_table(font)
115            .map(|table| (table, cache.kern.as_slice()));
116        let kerx = table_ranges
117            .kerx
118            .resolve_table(font)
119            .map(|table| (table, cache.kerx.as_slice()));
120        let trak = table_ranges.trak.resolve_table(font);
121        let feat = table_ranges.feat.resolve_table(font);
122        Self {
123            morx,
124            ankr,
125            kern,
126            kerx,
127            trak,
128            feat,
129        }
130    }
131
132    pub fn from_tables(
133        font: &impl TableProvider<'a>,
134        ot_tables: &OtTables,
135        cache: &'a AatCache,
136    ) -> Self {
137        let morx = if let Ok(morx) = font.morx() {
138            let gsub_len = ot_tables
139                .gsub
140                .as_ref()
141                .map_or(0, |table| table.table.offset_data().len() as u32);
142            let gdef_len = ot_tables
143                .gdef
144                .table
145                .as_ref()
146                .map_or(0, |table| table.offset_data().len() as u32);
147            if is_morx_blocklisted(morx.offset_data().len() as u32, gsub_len, gdef_len) {
148                None
149            } else {
150                Some((morx, cache.morx.as_slice()))
151            }
152        } else {
153            None
154        };
155        let ankr = font.ankr().ok();
156        let kern = font.kern().ok().map(|table| (table, cache.kern.as_slice()));
157        let kerx = font.kerx().ok().map(|table| (table, cache.kerx.as_slice()));
158        let trak = font.trak().ok();
159        let feat = font.feat().ok();
160        Self {
161            morx,
162            ankr,
163            kern,
164            kerx,
165            trak,
166            feat,
167        }
168    }
169}