rustybuzz/hb/
ot_layout.rs

1//! OpenType layout.
2
3use core::ops::{Index, IndexMut};
4
5use ttf_parser::opentype_layout::{FeatureIndex, LanguageIndex, LookupIndex, ScriptIndex};
6use ttf_parser::GlyphId;
7
8use super::buffer::*;
9use super::common::TagExt;
10use super::ot_layout_gsubgpos::{Apply, OT};
11use super::ot_shape_plan::hb_ot_shape_plan_t;
12use super::unicode::{hb_unicode_funcs_t, hb_unicode_general_category_t, GeneralCategoryExt};
13use super::{hb_font_t, hb_glyph_info_t, hb_tag_t};
14
15pub const MAX_NESTING_LEVEL: usize = 64;
16pub const MAX_CONTEXT_LENGTH: usize = 64;
17
18pub fn hb_ot_layout_has_kerning(face: &hb_font_t) -> bool {
19    face.tables().kern.is_some()
20}
21
22pub fn hb_ot_layout_has_machine_kerning(face: &hb_font_t) -> bool {
23    match face.tables().kern {
24        Some(ref kern) => kern.subtables.into_iter().any(|s| s.has_state_machine),
25        None => false,
26    }
27}
28
29pub fn hb_ot_layout_has_cross_kerning(face: &hb_font_t) -> bool {
30    match face.tables().kern {
31        Some(ref kern) => kern.subtables.into_iter().any(|s| s.has_cross_stream),
32        None => false,
33    }
34}
35
36// hb_ot_layout_kern
37
38// OT::GDEF::is_blocklisted unsupported
39
40pub fn _hb_ot_layout_set_glyph_props(face: &hb_font_t, buffer: &mut hb_buffer_t) {
41    let len = buffer.len;
42    for info in &mut buffer.info[..len] {
43        info.set_glyph_props(face.glyph_props(info.as_glyph()));
44        info.set_lig_props(0);
45        info.set_syllable(0);
46    }
47}
48
49pub fn hb_ot_layout_has_glyph_classes(face: &hb_font_t) -> bool {
50    face.tables()
51        .gdef
52        .map_or(false, |table| table.has_glyph_classes())
53}
54
55// get_gsubgpos_table
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub enum TableIndex {
59    GSUB = 0,
60    GPOS = 1,
61}
62
63impl TableIndex {
64    pub fn iter() -> impl Iterator<Item = TableIndex> {
65        [Self::GSUB, Self::GPOS].iter().copied()
66    }
67}
68
69impl<T> Index<TableIndex> for [T] {
70    type Output = T;
71
72    fn index(&self, table_index: TableIndex) -> &Self::Output {
73        &self[table_index as usize]
74    }
75}
76
77impl<T> IndexMut<TableIndex> for [T] {
78    fn index_mut(&mut self, table_index: TableIndex) -> &mut Self::Output {
79        &mut self[table_index as usize]
80    }
81}
82
83/// A lookup-based layout table (GSUB or GPOS).
84pub trait LayoutTable {
85    /// The index of this table.
86    const INDEX: TableIndex;
87
88    /// Whether lookups in this table can be applied to the buffer in-place.
89    const IN_PLACE: bool;
90
91    /// The kind of lookup stored in this table.
92    type Lookup: LayoutLookup;
93
94    /// Get the lookup at the specified index.
95    fn get_lookup(&self, index: LookupIndex) -> Option<&Self::Lookup>;
96}
97
98/// A lookup in a layout table.
99pub trait LayoutLookup: Apply {
100    /// The lookup's lookup_props.
101    fn props(&self) -> u32;
102
103    /// Whether the lookup has to be applied backwards.
104    fn is_reverse(&self) -> bool;
105
106    /// Whether any subtable of the lookup could apply at a specific glyph.
107    fn covers(&self, glyph: GlyphId) -> bool;
108}
109
110pub trait LayoutTableExt {
111    fn select_script(&self, script_tags: &[hb_tag_t]) -> Option<(bool, ScriptIndex, hb_tag_t)>;
112    fn select_script_language(
113        &self,
114        script_index: ScriptIndex,
115        lang_tags: &[hb_tag_t],
116    ) -> Option<LanguageIndex>;
117    fn get_required_language_feature(
118        &self,
119        script_index: ScriptIndex,
120        lang_index: Option<LanguageIndex>,
121    ) -> Option<(FeatureIndex, hb_tag_t)>;
122    fn find_language_feature(
123        &self,
124        script_index: ScriptIndex,
125        lang_index: Option<LanguageIndex>,
126        feature_tag: hb_tag_t,
127    ) -> Option<FeatureIndex>;
128}
129
130impl LayoutTableExt for ttf_parser::opentype_layout::LayoutTable<'_> {
131    // hb_ot_layout_table_select_script
132    /// Returns true + index and tag of the first found script tag in the given GSUB or GPOS table
133    /// or false + index and tag if falling back to a default script.
134    fn select_script(&self, script_tags: &[hb_tag_t]) -> Option<(bool, ScriptIndex, hb_tag_t)> {
135        for &tag in script_tags {
136            if let Some(index) = self.scripts.index(tag) {
137                return Some((true, index, tag));
138            }
139        }
140
141        for &tag in &[
142            // try finding 'DFLT'
143            hb_tag_t::default_script(),
144            // try with 'dflt'; MS site has had typos and many fonts use it now :(
145            hb_tag_t::default_language(),
146            // try with 'latn'; some old fonts put their features there even though
147            // they're really trying to support Thai, for example :(
148            hb_tag_t::from_bytes(b"latn"),
149        ] {
150            if let Some(index) = self.scripts.index(tag) {
151                return Some((false, index, tag));
152            }
153        }
154
155        None
156    }
157
158    // hb_ot_layout_script_select_language
159    /// Returns the index of the first found language tag in the given GSUB or GPOS table,
160    /// underneath the specified script index.
161    fn select_script_language(
162        &self,
163        script_index: ScriptIndex,
164        lang_tags: &[hb_tag_t],
165    ) -> Option<LanguageIndex> {
166        let script = self.scripts.get(script_index)?;
167
168        for &tag in lang_tags {
169            if let Some(index) = script.languages.index(tag) {
170                return Some(index);
171            }
172        }
173
174        // try finding 'dflt'
175        if let Some(index) = script.languages.index(hb_tag_t::default_language()) {
176            return Some(index);
177        }
178
179        None
180    }
181
182    // hb_ot_layout_language_get_required_feature
183    /// Returns the index and tag of a required feature in the given GSUB or GPOS table,
184    /// underneath the specified script and language.
185    fn get_required_language_feature(
186        &self,
187        script_index: ScriptIndex,
188        lang_index: Option<LanguageIndex>,
189    ) -> Option<(FeatureIndex, hb_tag_t)> {
190        let script = self.scripts.get(script_index)?;
191        let sys = match lang_index {
192            Some(index) => script.languages.get(index)?,
193            None => script.default_language?,
194        };
195        let idx = sys.required_feature?;
196        let tag = self.features.get(idx)?.tag;
197        Some((idx, tag))
198    }
199
200    // hb_ot_layout_language_find_feature
201    /// Returns the index of a given feature tag in the given GSUB or GPOS table,
202    /// underneath the specified script and language.
203    fn find_language_feature(
204        &self,
205        script_index: ScriptIndex,
206        lang_index: Option<LanguageIndex>,
207        feature_tag: hb_tag_t,
208    ) -> Option<FeatureIndex> {
209        let script = self.scripts.get(script_index)?;
210        let sys = match lang_index {
211            Some(index) => script.languages.get(index)?,
212            None => script.default_language?,
213        };
214
215        for i in 0..sys.feature_indices.len() {
216            if let Some(index) = sys.feature_indices.get(i) {
217                if self.features.get(index).map(|v| v.tag) == Some(feature_tag) {
218                    return Some(index);
219                }
220            }
221        }
222
223        None
224    }
225}
226
227/// Called before substitution lookups are performed, to ensure that glyph
228/// class and other properties are set on the glyphs in the buffer.
229pub fn hb_ot_layout_substitute_start(face: &hb_font_t, buffer: &mut hb_buffer_t) {
230    _hb_ot_layout_set_glyph_props(face, buffer)
231}
232
233/// Applies the lookups in the given GSUB or GPOS table.
234pub fn apply_layout_table<T: LayoutTable>(
235    plan: &hb_ot_shape_plan_t,
236    face: &hb_font_t,
237    buffer: &mut hb_buffer_t,
238    table: Option<&T>,
239) {
240    let mut ctx = OT::hb_ot_apply_context_t::new(T::INDEX, face, buffer);
241
242    for (stage_index, stage) in plan.ot_map.stages(T::INDEX).iter().enumerate() {
243        for lookup in plan.ot_map.stage_lookups(T::INDEX, stage_index) {
244            ctx.lookup_index = lookup.index;
245            ctx.lookup_mask = lookup.mask;
246            ctx.auto_zwj = lookup.auto_zwj;
247            ctx.auto_zwnj = lookup.auto_zwnj;
248
249            ctx.random = lookup.random;
250            ctx.per_syllable = lookup.per_syllable;
251
252            if let Some(table) = &table {
253                if let Some(lookup) = table.get_lookup(lookup.index) {
254                    apply_string::<T>(&mut ctx, lookup);
255                }
256            }
257        }
258
259        if let Some(func) = stage.pause_func {
260            func(plan, face, ctx.buffer);
261        }
262    }
263}
264
265fn apply_string<T: LayoutTable>(ctx: &mut OT::hb_ot_apply_context_t, lookup: &T::Lookup) {
266    if ctx.buffer.is_empty() || ctx.lookup_mask == 0 {
267        return;
268    }
269
270    ctx.lookup_props = lookup.props();
271
272    if !lookup.is_reverse() {
273        // in/out forward substitution/positioning
274        if !T::IN_PLACE {
275            ctx.buffer.clear_output();
276        }
277        ctx.buffer.idx = 0;
278        apply_forward(ctx, lookup);
279
280        if !T::IN_PLACE {
281            ctx.buffer.sync();
282        }
283    } else {
284        // in-place backward substitution/positioning
285        assert!(!ctx.buffer.have_output);
286
287        ctx.buffer.idx = ctx.buffer.len - 1;
288        apply_backward(ctx, lookup);
289    }
290}
291
292fn apply_forward(ctx: &mut OT::hb_ot_apply_context_t, lookup: &impl Apply) -> bool {
293    let mut ret = false;
294    while ctx.buffer.idx < ctx.buffer.len && ctx.buffer.successful {
295        let cur = ctx.buffer.cur(0);
296        if (cur.mask & ctx.lookup_mask) != 0
297            && ctx.check_glyph_property(cur, ctx.lookup_props)
298            && lookup.apply(ctx).is_some()
299        {
300            ret = true;
301        } else {
302            ctx.buffer.next_glyph();
303        }
304    }
305    ret
306}
307
308fn apply_backward(ctx: &mut OT::hb_ot_apply_context_t, lookup: &impl Apply) -> bool {
309    let mut ret = false;
310    loop {
311        let cur = ctx.buffer.cur(0);
312        ret |= (cur.mask & ctx.lookup_mask) != 0
313            && ctx.check_glyph_property(cur, ctx.lookup_props)
314            && lookup.apply(ctx).is_some();
315
316        if ctx.buffer.idx == 0 {
317            break;
318        }
319
320        ctx.buffer.idx -= 1;
321    }
322    ret
323}
324
325/* unicode_props */
326
327/* Design:
328 * unicode_props() is a two-byte number.  The low byte includes:
329 * - General_Category: 5 bits.
330 * - A bit each for:
331 *   * Is it Default_Ignorable(); we have a modified Default_Ignorable().
332 *   * Whether it's one of the four Mongolian Free Variation Selectors,
333 *     CGJ, or other characters that are hidden but should not be ignored
334 *     like most other Default_Ignorable()s do during matching.
335 *   * Whether it's a grapheme continuation.
336 *
337 * The high-byte has different meanings, switched by the Gen-Cat:
338 * - For Mn,Mc,Me: the modified Combining_Class.
339 * - For Cf: whether it's ZWJ, ZWNJ, or something else.
340 * - For Ws: index of which space character this is, if space fallback
341 *   is needed, ie. we don't set this by default, only if asked to.
342 */
343
344//  enum hb_unicode_props_flags_t {
345//     UPROPS_MASK_GEN_CAT	= 0x001Fu,
346//     UPROPS_MASK_IGNORABLE	= 0x0020u,
347//     UPROPS_MASK_HIDDEN	= 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..4, or TAG characters */
348//     UPROPS_MASK_CONTINUATION=0x0080u,
349
350//     /* If GEN_CAT=FORMAT, top byte masks: */
351//     UPROPS_MASK_Cf_ZWJ	= 0x0100u,
352//     UPROPS_MASK_Cf_ZWNJ	= 0x0200u
353//   };
354//   HB_MARK_AS_FLAG_T (hb_unicode_props_flags_t);
355
356//   static inline void
357//   _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer)
358//   {
359//     hb_unicode_funcs_t *unicode = buffer->unicode;
360//     unsigned int u = info->codepoint;
361//     unsigned int gen_cat = (unsigned int) unicode->general_category (u);
362//     unsigned int props = gen_cat;
363
364//     if (u >= 0x80u)
365//     {
366//       buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII;
367
368//       if (unlikely (unicode->is_default_ignorable (u)))
369//       {
370//         buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES;
371//         props |=  UPROPS_MASK_IGNORABLE;
372//         if (u == 0x200Cu) props |= UPROPS_MASK_Cf_ZWNJ;
373//         else if (u == 0x200Du) props |= UPROPS_MASK_Cf_ZWJ;
374//         /* Mongolian Free Variation Selectors need to be remembered
375//          * because although we need to hide them like default-ignorables,
376//          * they need to non-ignorable during shaping.  This is similar to
377//          * what we do for joiners in Indic-like shapers, but since the
378//          * FVSes are GC=Mn, we have use a separate bit to remember them.
379//          * Fixes:
380//          * https://github.com/harfbuzz/harfbuzz/issues/234 */
381//         else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0x180Bu, 0x180Du, 0x180Fu, 0x180Fu))) props |= UPROPS_MASK_HIDDEN;
382//         /* TAG characters need similar treatment. Fixes:
383//          * https://github.com/harfbuzz/harfbuzz/issues/463 */
384//         else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN;
385//         /* COMBINING GRAPHEME JOINER should not be skipped; at least some times.
386//          * https://github.com/harfbuzz/harfbuzz/issues/554 */
387//         else if (unlikely (u == 0x034Fu))
388//         {
389//       buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_CGJ;
390//       props |= UPROPS_MASK_HIDDEN;
391//         }
392//       }
393
394//       if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (gen_cat)))
395//       {
396//         props |= UPROPS_MASK_CONTINUATION;
397//         props |= unicode->modified_combining_class (u)<<8;
398//       }
399//     }
400
401//     info->unicode_props() = props;
402//   }
403
404#[inline]
405pub fn _hb_glyph_info_set_general_category(
406    info: &mut hb_glyph_info_t,
407    gen_cat: hb_unicode_general_category_t,
408) {
409    /* Clears top-byte. */
410    let gen_cat = gen_cat.to_rb();
411    let n =
412        (gen_cat as u16) | (info.unicode_props() & (0xFF & !UnicodeProps::GENERAL_CATEGORY.bits()));
413    info.set_unicode_props(n);
414}
415
416#[inline]
417pub fn _hb_glyph_info_get_general_category(
418    info: &hb_glyph_info_t,
419) -> hb_unicode_general_category_t {
420    let n = info.unicode_props() & UnicodeProps::GENERAL_CATEGORY.bits();
421    hb_unicode_general_category_t::from_rb(n as u32)
422}
423
424#[inline]
425pub fn _hb_glyph_info_is_unicode_mark(info: &hb_glyph_info_t) -> bool {
426    _hb_glyph_info_get_general_category(info).is_mark()
427}
428
429#[inline]
430pub(crate) fn _hb_glyph_info_set_modified_combining_class(
431    info: &mut hb_glyph_info_t,
432    modified_class: u8,
433) {
434    if !_hb_glyph_info_is_unicode_mark(info) {
435        return;
436    }
437
438    let n = ((modified_class as u16) << 8) | (info.unicode_props() & 0xFF);
439    info.set_unicode_props(n);
440}
441
442#[inline]
443pub fn _hb_glyph_info_get_modified_combining_class(info: &hb_glyph_info_t) -> u8 {
444    if _hb_glyph_info_is_unicode_mark(info) {
445        (info.unicode_props() >> 8) as u8
446    } else {
447        0
448    }
449}
450
451// TODO: use
452// #[inline]
453// pub fn info_cc(info: &hb_glyph_info_t) -> u8 {
454//     _hb_glyph_info_get_modified_combining_class(info)
455// }
456
457#[inline]
458pub(crate) fn _hb_glyph_info_is_unicode_space(info: &hb_glyph_info_t) -> bool {
459    _hb_glyph_info_get_general_category(info) == hb_unicode_general_category_t::SpaceSeparator
460}
461
462#[inline]
463pub(crate) fn _hb_glyph_info_set_unicode_space_fallback_type(
464    info: &mut hb_glyph_info_t,
465    s: hb_unicode_funcs_t::space_t,
466) {
467    if !_hb_glyph_info_is_unicode_space(info) {
468        return;
469    }
470
471    let n = ((s as u16) << 8) | (info.unicode_props() & 0xFF);
472    info.set_unicode_props(n);
473}
474
475#[inline]
476pub(crate) fn _hb_glyph_info_get_unicode_space_fallback_type(
477    info: &hb_glyph_info_t,
478) -> hb_unicode_funcs_t::space_t {
479    if _hb_glyph_info_is_unicode_space(info) {
480        (info.unicode_props() >> 8) as u8
481    } else {
482        hb_unicode_funcs_t::NOT_SPACE
483    }
484}
485
486#[inline]
487pub(crate) fn _hb_glyph_info_is_default_ignorable(info: &hb_glyph_info_t) -> bool {
488    let n = info.unicode_props() & UnicodeProps::IGNORABLE.bits();
489    n != 0 && !_hb_glyph_info_substituted(info)
490}
491
492//   static inline bool
493//   _hb_glyph_info_is_default_ignorable_and_not_hidden (const hb_glyph_info_t *info)
494//   {
495//     return ((info->unicode_props() & (UPROPS_MASK_IGNORABLE|UPROPS_MASK_HIDDEN))
496//         == UPROPS_MASK_IGNORABLE) &&
497//        !_hb_glyph_info_substituted (info);
498//   }
499
500//   static inline void
501//   _hb_glyph_info_unhide (hb_glyph_info_t *info)
502//   {
503//     info->unicode_props() &= ~ UPROPS_MASK_HIDDEN;
504//   }
505
506#[inline]
507pub(crate) fn _hb_glyph_info_set_continuation(info: &mut hb_glyph_info_t) {
508    let mut n = info.unicode_props();
509    n |= UnicodeProps::CONTINUATION.bits();
510    info.set_unicode_props(n);
511}
512
513#[inline]
514pub(crate) fn _hb_glyph_info_reset_continuation(info: &mut hb_glyph_info_t) {
515    let mut n = info.unicode_props();
516    n &= !UnicodeProps::CONTINUATION.bits();
517    info.set_unicode_props(n);
518}
519
520#[inline]
521pub(crate) fn _hb_glyph_info_is_continuation(info: &hb_glyph_info_t) -> bool {
522    info.unicode_props() & UnicodeProps::CONTINUATION.bits() != 0
523}
524
525pub(crate) fn _hb_grapheme_group_func(_: &hb_glyph_info_t, b: &hb_glyph_info_t) -> bool {
526    _hb_glyph_info_is_continuation(b)
527}
528
529pub fn _hb_ot_layout_reverse_graphemes(buffer: &mut hb_buffer_t) {
530    buffer.reverse_groups(
531        _hb_grapheme_group_func,
532        buffer.cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS,
533    )
534}
535
536#[inline]
537pub(crate) fn _hb_glyph_info_is_unicode_format(info: &hb_glyph_info_t) -> bool {
538    _hb_glyph_info_get_general_category(info) == hb_unicode_general_category_t::Format
539}
540
541#[inline]
542pub(crate) fn _hb_glyph_info_is_zwnj(info: &hb_glyph_info_t) -> bool {
543    _hb_glyph_info_is_unicode_format(info)
544        && (info.unicode_props() & UnicodeProps::CF_ZWNJ.bits() != 0)
545}
546
547#[inline]
548pub(crate) fn _hb_glyph_info_is_zwj(info: &hb_glyph_info_t) -> bool {
549    _hb_glyph_info_is_unicode_format(info)
550        && (info.unicode_props() & UnicodeProps::CF_ZWJ.bits() != 0)
551}
552
553//   static inline bool
554//   _hb_glyph_info_is_joiner (const hb_glyph_info_t *info)
555//   {
556//     return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & (UPROPS_MASK_Cf_ZWNJ|UPROPS_MASK_Cf_ZWJ));
557//   }
558
559//   static inline void
560//   _hb_glyph_info_flip_joiners (hb_glyph_info_t *info)
561//   {
562//     if (!_hb_glyph_info_is_unicode_format (info))
563//       return;
564//     info->unicode_props() ^= UPROPS_MASK_Cf_ZWNJ | UPROPS_MASK_Cf_ZWJ;
565//   }
566
567//   /* lig_props: aka lig_id / lig_comp
568//    *
569//    * When a ligature is formed:
570//    *
571//    *   - The ligature glyph and any marks in between all the same newly allocated
572//    *     lig_id,
573//    *   - The ligature glyph will get lig_num_comps set to the number of components
574//    *   - The marks get lig_comp > 0, reflecting which component of the ligature
575//    *     they were applied to.
576//    *   - This is used in GPOS to attach marks to the right component of a ligature
577//    *     in MarkLigPos,
578//    *   - Note that when marks are ligated together, much of the above is skipped
579//    *     and the current lig_id reused.
580//    *
581//    * When a multiple-substitution is done:
582//    *
583//    *   - All resulting glyphs will have lig_id = 0,
584//    *   - The resulting glyphs will have lig_comp = 0, 1, 2, ... respectively.
585//    *   - This is used in GPOS to attach marks to the first component of a
586//    *     multiple substitution in MarkBasePos.
587//    *
588//    * The numbers are also used in GPOS to do mark-to-mark positioning only
589//    * to marks that belong to the same component of the same ligature.
590//    */
591//   static inline void
592//   _hb_glyph_info_clear_lig_props (hb_glyph_info_t *info)
593//   {
594//     info->lig_props() = 0;
595//   }
596
597const IS_LIG_BASE: u8 = 0x10;
598
599#[inline]
600pub(crate) fn _hb_glyph_info_set_lig_props_for_ligature(
601    info: &mut hb_glyph_info_t,
602    lig_id: u8,
603    lig_num_comps: u8,
604) {
605    info.set_lig_props((lig_id << 5) | IS_LIG_BASE | (lig_num_comps & 0x0F));
606}
607
608#[inline]
609pub(crate) fn _hb_glyph_info_set_lig_props_for_mark(
610    info: &mut hb_glyph_info_t,
611    lig_id: u8,
612    lig_comp: u8,
613) {
614    info.set_lig_props((lig_id << 5) | (lig_comp & 0x0F));
615}
616
617#[inline]
618pub(crate) fn _hb_glyph_info_set_lig_props_for_component(info: &mut hb_glyph_info_t, comp: u8) {
619    _hb_glyph_info_set_lig_props_for_mark(info, 0, comp);
620}
621
622#[inline]
623pub(crate) fn _hb_glyph_info_get_lig_id(info: &hb_glyph_info_t) -> u8 {
624    info.lig_props() >> 5
625}
626
627#[inline]
628pub(crate) fn _hb_glyph_info_ligated_internal(info: &hb_glyph_info_t) -> bool {
629    info.lig_props() & IS_LIG_BASE != 0
630}
631
632#[inline]
633pub(crate) fn _hb_glyph_info_get_lig_comp(info: &hb_glyph_info_t) -> u8 {
634    if _hb_glyph_info_ligated_internal(info) {
635        0
636    } else {
637        info.lig_props() & 0x0F
638    }
639}
640
641#[inline]
642pub(crate) fn _hb_glyph_info_get_lig_num_comps(info: &hb_glyph_info_t) -> u8 {
643    if info.glyph_props() & GlyphPropsFlags::LIGATURE.bits() != 0
644        && _hb_glyph_info_ligated_internal(info)
645    {
646        info.lig_props() & 0x0F
647    } else {
648        1
649    }
650}
651
652//   /* glyph_props: */
653//   static inline void
654//   _hb_glyph_info_set_glyph_props (hb_glyph_info_t *info, unsigned int props)
655//   {
656//     info->glyph_props() = props;
657//   }
658
659//   static inline unsigned int
660//   _hb_glyph_info_get_glyph_props (const hb_glyph_info_t *info)
661//   {
662//     return info->glyph_props();
663//   }
664
665#[inline]
666pub(crate) fn _hb_glyph_info_is_base_glyph(info: &hb_glyph_info_t) -> bool {
667    info.glyph_props() & GlyphPropsFlags::BASE_GLYPH.bits() != 0
668}
669
670#[inline]
671pub(crate) fn _hb_glyph_info_is_ligature(info: &hb_glyph_info_t) -> bool {
672    info.glyph_props() & GlyphPropsFlags::LIGATURE.bits() != 0
673}
674
675#[inline]
676pub(crate) fn _hb_glyph_info_is_mark(info: &hb_glyph_info_t) -> bool {
677    info.glyph_props() & GlyphPropsFlags::MARK.bits() != 0
678}
679
680#[inline]
681pub(crate) fn _hb_glyph_info_substituted(info: &hb_glyph_info_t) -> bool {
682    info.glyph_props() & GlyphPropsFlags::SUBSTITUTED.bits() != 0
683}
684
685#[inline]
686pub(crate) fn _hb_glyph_info_ligated(info: &hb_glyph_info_t) -> bool {
687    info.glyph_props() & GlyphPropsFlags::LIGATED.bits() != 0
688}
689
690#[inline]
691pub(crate) fn _hb_glyph_info_multiplied(info: &hb_glyph_info_t) -> bool {
692    info.glyph_props() & GlyphPropsFlags::MULTIPLIED.bits() != 0
693}
694
695#[inline]
696pub(crate) fn _hb_glyph_info_ligated_and_didnt_multiply(info: &hb_glyph_info_t) -> bool {
697    _hb_glyph_info_ligated(info) && !_hb_glyph_info_multiplied(info)
698}
699
700#[inline]
701pub(crate) fn _hb_glyph_info_clear_ligated_and_multiplied(info: &mut hb_glyph_info_t) {
702    let mut n = info.glyph_props();
703    n &= !(GlyphPropsFlags::LIGATED | GlyphPropsFlags::MULTIPLIED).bits();
704    info.set_glyph_props(n);
705}
706
707#[inline]
708pub(crate) fn _hb_glyph_info_clear_substituted(info: &mut hb_glyph_info_t) {
709    let mut n = info.glyph_props();
710    n &= !GlyphPropsFlags::SUBSTITUTED.bits();
711    info.set_glyph_props(n);
712}
713
714pub fn _hb_clear_substitution_flags(
715    _: &hb_ot_shape_plan_t,
716    _: &hb_font_t,
717    buffer: &mut hb_buffer_t,
718) {
719    let len = buffer.len;
720    for info in &mut buffer.info[..len] {
721        _hb_glyph_info_clear_substituted(info);
722    }
723}