rustybuzz/hb/
ot_map.rs

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