rustybuzz/hb/
ot_map.rs

1use alloc::vec::Vec;
2use core::ops::Range;
3
4use ttf_parser::opentype_layout::{
5    FeatureIndex, LanguageIndex, LookupIndex, ScriptIndex, VariationIndex,
6};
7
8use super::buffer::{glyph_flag, hb_buffer_t};
9use super::ot_layout::{LayoutTableExt, TableIndex};
10use super::ot_shape_plan::hb_ot_shape_plan_t;
11use super::{hb_font_t, hb_mask_t, hb_tag_t, tag, Language, Script};
12
13pub struct hb_ot_map_t {
14    found_script: [bool; 2],
15    chosen_script: [Option<hb_tag_t>; 2],
16    global_mask: hb_mask_t,
17    features: Vec<feature_map_t>,
18    lookups: [Vec<lookup_map_t>; 2],
19    stages: [Vec<StageMap>; 2],
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
23pub struct feature_map_t {
24    tag: hb_tag_t,
25    // GSUB/GPOS
26    index: [Option<FeatureIndex>; 2],
27    stage: [usize; 2],
28    shift: u32,
29    mask: hb_mask_t,
30    // mask for value=1, for quick access
31    one_mask: hb_mask_t,
32    auto_zwnj: bool,
33    auto_zwj: bool,
34    random: bool,
35    per_syllable: bool,
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
39pub struct lookup_map_t {
40    pub index: LookupIndex,
41    // TODO: to bitflags
42    pub auto_zwnj: bool,
43    pub auto_zwj: bool,
44    pub random: bool,
45    pub mask: hb_mask_t,
46    pub per_syllable: bool,
47}
48
49#[derive(Clone, Copy)]
50pub struct StageMap {
51    // Cumulative
52    pub last_lookup: usize,
53    pub pause_func: Option<pause_func_t>,
54}
55
56pub type pause_func_t = fn(&hb_ot_shape_plan_t, &hb_font_t, &mut hb_buffer_t);
57
58impl hb_ot_map_t {
59    pub const MAX_BITS: u32 = 8;
60    pub const MAX_VALUE: u32 = (1 << Self::MAX_BITS) - 1;
61
62    #[inline]
63    pub fn found_script(&self, table_index: TableIndex) -> bool {
64        self.found_script[table_index]
65    }
66
67    #[inline]
68    pub fn chosen_script(&self, table_index: TableIndex) -> Option<hb_tag_t> {
69        self.chosen_script[table_index]
70    }
71
72    #[inline]
73    pub fn get_global_mask(&self) -> hb_mask_t {
74        self.global_mask
75    }
76
77    #[inline]
78    pub fn get_mask(&self, feature_tag: hb_tag_t) -> (hb_mask_t, u32) {
79        self.features
80            .binary_search_by_key(&feature_tag, |f| f.tag)
81            .map_or((0, 0), |idx| {
82                (self.features[idx].mask, self.features[idx].shift)
83            })
84    }
85
86    #[inline]
87    pub fn get_1_mask(&self, feature_tag: hb_tag_t) -> hb_mask_t {
88        self.features
89            .binary_search_by_key(&feature_tag, |f| f.tag)
90            .map_or(0, |idx| self.features[idx].one_mask)
91    }
92
93    #[inline]
94    pub fn get_feature_index(
95        &self,
96        table_index: TableIndex,
97        feature_tag: hb_tag_t,
98    ) -> Option<FeatureIndex> {
99        self.features
100            .binary_search_by_key(&feature_tag, |f| f.tag)
101            .ok()
102            .and_then(|idx| self.features[idx].index[table_index])
103    }
104
105    #[inline]
106    pub fn get_feature_stage(
107        &self,
108        table_index: TableIndex,
109        feature_tag: hb_tag_t,
110    ) -> Option<usize> {
111        self.features
112            .binary_search_by_key(&feature_tag, |f| f.tag)
113            .map(|idx| self.features[idx].stage[table_index])
114            .ok()
115    }
116
117    #[inline]
118    pub fn stages(&self, table_index: TableIndex) -> &[StageMap] {
119        &self.stages[table_index]
120    }
121
122    #[inline]
123    pub fn lookup(&self, table_index: TableIndex, index: usize) -> &lookup_map_t {
124        &self.lookups[table_index][index]
125    }
126
127    #[inline]
128    pub fn stage_lookups(&self, table_index: TableIndex, stage: usize) -> &[lookup_map_t] {
129        &self.lookups[table_index][self.stage_lookup_range(table_index, stage)]
130    }
131
132    #[inline]
133    pub fn stage_lookup_range(&self, table_index: TableIndex, stage: usize) -> Range<usize> {
134        let stages = &self.stages[table_index];
135        let lookups = &self.lookups[table_index];
136        let start = stage
137            .checked_sub(1)
138            .map_or(0, |prev| stages[prev].last_lookup);
139        let end = stages
140            .get(stage)
141            .map_or(lookups.len(), |curr| curr.last_lookup);
142        start..end
143    }
144}
145
146pub type hb_ot_map_feature_flags_t = u32;
147pub const F_NONE: u32 = 0x0000;
148pub const F_GLOBAL: u32 = 0x0001; /* Feature applies to all characters; results in no mask allocated for it. */
149pub const F_HAS_FALLBACK: u32 = 0x0002; /* Has fallback implementation, so include mask bit even if feature not found. */
150pub const F_MANUAL_ZWNJ: u32 = 0x0004; /* Don't skip over ZWNJ when matching **context**. */
151pub const F_MANUAL_ZWJ: u32 = 0x0008; /* Don't skip over ZWJ when matching **input**. */
152pub const F_MANUAL_JOINERS: u32 = F_MANUAL_ZWNJ | F_MANUAL_ZWJ;
153pub const F_GLOBAL_MANUAL_JOINERS: u32 = F_GLOBAL | F_MANUAL_JOINERS;
154pub const F_GLOBAL_HAS_FALLBACK: u32 = F_GLOBAL | F_HAS_FALLBACK;
155pub const F_GLOBAL_SEARCH: u32 = 0x0010; /* If feature not found in LangSys, look for it in global feature list and pick one. */
156pub const F_RANDOM: u32 = 0x0020; /* Randomly select a glyph from an AlternateSubstFormat1 subtable. */
157pub const F_PER_SYLLABLE: u32 = 0x0040; /* Contain lookup application to within syllable. */
158
159pub struct hb_ot_map_builder_t<'a> {
160    face: &'a hb_font_t<'a>,
161    found_script: [bool; 2],
162    script_index: [Option<ScriptIndex>; 2],
163    chosen_script: [Option<hb_tag_t>; 2],
164    lang_index: [Option<LanguageIndex>; 2],
165    current_stage: [usize; 2],
166    feature_infos: Vec<feature_info_t>,
167    stages: [Vec<stage_info_t>; 2],
168}
169
170#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
171struct feature_info_t {
172    tag: hb_tag_t,
173    // sequence number, used for stable sorting only
174    seq: usize,
175    max_value: u32,
176    flags: hb_ot_map_feature_flags_t,
177    // for non-global features, what should the unset glyphs take
178    default_value: u32,
179    // GSUB/GPOS
180    stage: [usize; 2],
181}
182
183#[derive(Clone, Copy)]
184struct stage_info_t {
185    index: usize,
186    pause_func: Option<pause_func_t>,
187}
188
189impl<'a> hb_ot_map_builder_t<'a> {
190    pub fn new(
191        face: &'a hb_font_t<'a>,
192        script: Option<Script>,
193        language: Option<&Language>,
194    ) -> Self {
195        // Fetch script/language indices for GSUB/GPOS.  We need these later to skip
196        // features not available in either table and not waste precious bits for them.
197        let (script_tags, lang_tags) = tag::tags_from_script_and_language(script, language);
198
199        let mut found_script = [false; 2];
200        let mut script_index = [None; 2];
201        let mut chosen_script = [None; 2];
202        let mut lang_index = [None; 2];
203
204        for (table_index, table) in face.layout_tables() {
205            if let Some((found, idx, tag)) = table.select_script(&script_tags) {
206                chosen_script[table_index] = Some(tag);
207                found_script[table_index] = found;
208                script_index[table_index] = Some(idx);
209
210                if let Some(idx) = table.select_script_language(idx, &lang_tags) {
211                    lang_index[table_index] = Some(idx);
212                }
213            }
214        }
215
216        Self {
217            face,
218            found_script,
219            script_index,
220            chosen_script,
221            lang_index,
222            current_stage: [0, 0],
223            feature_infos: Vec::new(),
224            stages: [Vec::new(), Vec::new()],
225        }
226    }
227
228    #[inline]
229    pub fn chosen_script(&self, table_index: TableIndex) -> Option<hb_tag_t> {
230        self.chosen_script[table_index]
231    }
232
233    #[inline]
234    pub fn add_feature(&mut self, tag: hb_tag_t, flags: hb_ot_map_feature_flags_t, value: u32) {
235        if !tag.is_null() {
236            let seq = self.feature_infos.len();
237            self.feature_infos.push(feature_info_t {
238                tag,
239                seq,
240                max_value: value,
241                flags,
242                default_value: if flags & F_GLOBAL != 0 { value } else { 0 },
243                stage: self.current_stage,
244            });
245        }
246    }
247
248    #[inline]
249    pub fn enable_feature(&mut self, tag: hb_tag_t, flags: hb_ot_map_feature_flags_t, value: u32) {
250        self.add_feature(tag, flags | F_GLOBAL, value);
251    }
252
253    #[inline]
254    pub fn disable_feature(&mut self, tag: hb_tag_t) {
255        self.add_feature(tag, F_GLOBAL, 0);
256    }
257
258    #[inline]
259    pub fn add_gsub_pause(&mut self, pause: Option<pause_func_t>) {
260        self.add_pause(TableIndex::GSUB, pause);
261    }
262
263    #[inline]
264    pub fn add_gpos_pause(&mut self, pause: Option<pause_func_t>) {
265        self.add_pause(TableIndex::GPOS, pause);
266    }
267
268    fn add_pause(&mut self, table_index: TableIndex, pause: Option<pause_func_t>) {
269        self.stages[table_index].push(stage_info_t {
270            index: self.current_stage[table_index],
271            pause_func: pause,
272        });
273
274        self.current_stage[table_index] += 1;
275    }
276
277    const GLOBAL_BIT_MASK: hb_mask_t = glyph_flag::DEFINED + 1;
278    const GLOBAL_BIT_SHIFT: u32 = glyph_flag::DEFINED.count_ones();
279
280    pub fn compile(&mut self) -> hb_ot_map_t {
281        // We default to applying required feature in stage 0.  If the required
282        // feature has a tag that is known to the shaper, we apply required feature
283        // in the stage for that tag.
284        let mut required_index = [None; 2];
285        let mut required_tag = [None; 2];
286
287        for (table_index, table) in self.face.layout_tables() {
288            if let Some(script) = self.script_index[table_index] {
289                let lang = self.lang_index[table_index];
290                if let Some((idx, tag)) = table.get_required_language_feature(script, lang) {
291                    required_index[table_index] = Some(idx);
292                    required_tag[table_index] = Some(tag);
293                }
294            }
295        }
296
297        let (features, required_stage, global_mask) = self.collect_feature_maps(required_tag);
298
299        self.add_gsub_pause(None);
300        self.add_gpos_pause(None);
301
302        let (lookups, stages) =
303            self.collect_lookup_stages(&features, required_index, required_stage);
304
305        hb_ot_map_t {
306            found_script: self.found_script,
307            chosen_script: self.chosen_script,
308            global_mask,
309            features,
310            lookups,
311            stages,
312        }
313    }
314
315    fn collect_feature_maps(
316        &mut self,
317        required_tag: [Option<hb_tag_t>; 2],
318    ) -> (Vec<feature_map_t>, [usize; 2], hb_mask_t) {
319        let mut map_features = Vec::new();
320        let mut required_stage = [0; 2];
321        let mut global_mask = Self::GLOBAL_BIT_MASK;
322        let mut next_bit = Self::GLOBAL_BIT_SHIFT + 1;
323
324        // Sort features and merge duplicates.
325        self.dedup_feature_infos();
326
327        for info in &self.feature_infos {
328            let bits_needed = if info.flags & F_GLOBAL != 0 && info.max_value == 1 {
329                // Uses the global bit.
330                0
331            } else {
332                // Limit bits per feature.
333                let v = info.max_value;
334                let num_bits = 8 * core::mem::size_of_val(&v) as u32 - v.leading_zeros();
335                hb_ot_map_t::MAX_BITS.min(num_bits)
336            };
337
338            let bits_available = 8 * core::mem::size_of::<hb_mask_t>() as u32;
339            if info.max_value == 0 || next_bit + bits_needed > bits_available {
340                // Feature disabled, or not enough bits.
341                continue;
342            }
343
344            let mut found = false;
345            let mut feature_index = [None; 2];
346
347            for (table_index, table) in self.face.layout_tables() {
348                if required_tag[table_index] == Some(info.tag) {
349                    required_stage[table_index] = info.stage[table_index];
350                }
351
352                if let Some(script) = self.script_index[table_index] {
353                    let lang = self.lang_index[table_index];
354                    if let Some(idx) = table.find_language_feature(script, lang, info.tag) {
355                        feature_index[table_index] = Some(idx);
356                        found = true;
357                    }
358                }
359            }
360
361            if !found && info.flags & F_GLOBAL_SEARCH != 0 {
362                // hb_ot_layout_table_find_feature
363                for (table_index, table) in self.face.layout_tables() {
364                    if let Some(idx) = table.features.index(info.tag) {
365                        feature_index[table_index] = Some(idx);
366                        found = true;
367                    }
368                }
369            }
370
371            if !found && !info.flags & F_HAS_FALLBACK != 0 {
372                continue;
373            }
374
375            let (shift, mask) = if info.flags & F_GLOBAL != 0 && info.max_value == 1 {
376                // Uses the global bit
377                (Self::GLOBAL_BIT_SHIFT, Self::GLOBAL_BIT_MASK)
378            } else {
379                let shift = next_bit;
380                let mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
381                next_bit += bits_needed;
382                global_mask |= (info.default_value << shift) & mask;
383                (shift, mask)
384            };
385
386            map_features.push(feature_map_t {
387                tag: info.tag,
388                index: feature_index,
389                stage: info.stage,
390                shift,
391                mask,
392                one_mask: (1 << shift) & mask,
393                auto_zwnj: info.flags & F_MANUAL_ZWNJ == 0,
394                auto_zwj: info.flags & F_MANUAL_ZWJ == 0,
395                random: info.flags & F_RANDOM != 0,
396                per_syllable: info.flags & F_PER_SYLLABLE != 0,
397            });
398        }
399
400        (map_features, required_stage, global_mask)
401    }
402
403    fn dedup_feature_infos(&mut self) {
404        let feature_infos = &mut self.feature_infos;
405        if feature_infos.is_empty() {
406            return;
407        }
408
409        feature_infos.sort();
410
411        let mut j = 0;
412        for i in 1..feature_infos.len() {
413            if feature_infos[i].tag != feature_infos[j].tag {
414                j += 1;
415                feature_infos[j] = feature_infos[i];
416            } else {
417                if feature_infos[i].flags & F_GLOBAL != 0 {
418                    feature_infos[j].flags |= F_GLOBAL;
419                    feature_infos[j].max_value = feature_infos[i].max_value;
420                    feature_infos[j].default_value = feature_infos[i].default_value;
421                } else {
422                    if feature_infos[j].flags & F_GLOBAL != 0 {
423                        feature_infos[j].flags ^= F_GLOBAL;
424                    }
425                    feature_infos[j].max_value =
426                        feature_infos[j].max_value.max(feature_infos[i].max_value);
427                    // Inherit default_value from j
428                }
429                let flags = feature_infos[i].flags & F_HAS_FALLBACK;
430                feature_infos[j].flags |= flags;
431                feature_infos[j].stage[0] =
432                    feature_infos[j].stage[0].min(feature_infos[i].stage[0]);
433                feature_infos[j].stage[1] =
434                    feature_infos[j].stage[1].min(feature_infos[i].stage[1]);
435            }
436        }
437
438        feature_infos.truncate(j + 1);
439    }
440
441    fn collect_lookup_stages(
442        &self,
443        map_features: &[feature_map_t],
444        required_feature_index: [Option<FeatureIndex>; 2],
445        required_feature_stage: [usize; 2],
446    ) -> ([Vec<lookup_map_t>; 2], [Vec<StageMap>; 2]) {
447        let mut map_lookups = [Vec::new(), Vec::new()];
448        let mut map_stages = [Vec::new(), Vec::new()];
449
450        for table_index in TableIndex::iter() {
451            // Collect lookup indices for features.
452            let mut stage_index = 0;
453            let mut last_lookup = 0;
454
455            let coords = self.face.ttfp_face.variation_coordinates();
456            let variation_index = self
457                .face
458                .layout_table(table_index)
459                .and_then(|t| t.variations?.find_index(coords));
460
461            for stage in 0..self.current_stage[table_index] {
462                if let Some(feature_index) = required_feature_index[table_index] {
463                    if required_feature_stage[table_index] == stage {
464                        self.add_lookups(
465                            &mut map_lookups[table_index],
466                            table_index,
467                            feature_index,
468                            variation_index,
469                            Self::GLOBAL_BIT_MASK,
470                            true,
471                            true,
472                            false,
473                            false,
474                        );
475                    }
476                }
477
478                for feature in map_features {
479                    if let Some(feature_index) = feature.index[table_index] {
480                        if feature.stage[table_index] == stage {
481                            self.add_lookups(
482                                &mut map_lookups[table_index],
483                                table_index,
484                                feature_index,
485                                variation_index,
486                                feature.mask,
487                                feature.auto_zwnj,
488                                feature.auto_zwj,
489                                feature.random,
490                                feature.per_syllable,
491                            );
492                        }
493                    }
494                }
495
496                // Sort lookups and merge duplicates.
497                let lookups = &mut map_lookups[table_index];
498                let len = lookups.len();
499
500                if last_lookup < len {
501                    lookups[last_lookup..].sort();
502
503                    let mut j = last_lookup;
504                    for i in j + 1..len {
505                        if lookups[i].index != lookups[j].index {
506                            j += 1;
507                            lookups[j] = lookups[i];
508                        } else {
509                            lookups[j].mask |= lookups[i].mask;
510                            lookups[j].auto_zwnj &= lookups[i].auto_zwnj;
511                            lookups[j].auto_zwj &= lookups[i].auto_zwj;
512                        }
513                    }
514
515                    lookups.truncate(j + 1);
516                }
517
518                last_lookup = lookups.len();
519
520                if let Some(info) = self.stages[table_index].get(stage_index) {
521                    if info.index == stage {
522                        map_stages[table_index].push(StageMap {
523                            last_lookup,
524                            pause_func: info.pause_func,
525                        });
526
527                        stage_index += 1;
528                    }
529                }
530            }
531        }
532
533        (map_lookups, map_stages)
534    }
535
536    fn add_lookups(
537        &self,
538        lookups: &mut Vec<lookup_map_t>,
539        table_index: TableIndex,
540        feature_index: FeatureIndex,
541        variation_index: Option<VariationIndex>,
542        mask: hb_mask_t,
543        auto_zwnj: bool,
544        auto_zwj: bool,
545        random: bool,
546        per_syllable: bool,
547    ) -> Option<()> {
548        let table = self.face.layout_table(table_index)?;
549
550        let lookup_count = table.lookups.len();
551        let feature = match variation_index {
552            Some(idx) => table
553                .variations
554                .and_then(|var| var.find_substitute(feature_index, idx))
555                .or_else(|| table.features.get(feature_index))?,
556            None => table.features.get(feature_index)?,
557        };
558
559        for index in feature.lookup_indices {
560            if index < lookup_count {
561                lookups.push(lookup_map_t {
562                    mask,
563                    index,
564                    auto_zwnj,
565                    auto_zwj,
566                    random,
567                    per_syllable,
568                });
569            }
570        }
571
572        Some(())
573    }
574}