Skip to main content

harfrust/hb/
ot_map.rs

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