Skip to main content

harfrust/hb/
ot_layout.rs

1//! OpenType layout.
2
3use core::ops::{Index, IndexMut};
4
5use super::buffer::*;
6use super::font_funcs::FontFuncsDispatch;
7use super::ot::lookup::LookupInfo;
8use super::ot_layout_gsubgpos::OT;
9use super::ot_shape_plan::hb_ot_shape_plan_t;
10use super::unicode::hb_unicode_funcs_t;
11use super::{hb_font_t, GlyphInfo};
12use crate::hb::ot_layout_gsubgpos::OT::check_glyph_property;
13use crate::hb::unicode::GeneralCategory;
14
15impl GlyphInfo {
16    declare_buffer_var!(u16, 1, 0, GLYPH_PROPS_VAR, glyph_props, set_glyph_props);
17    declare_buffer_var!(u8, 1, 2, LIG_PROPS_VAR, lig_props, set_lig_props);
18    declare_buffer_var!(u8, 1, 3, SYLLABLE_VAR, syllable, set_syllable);
19    declare_buffer_var!(
20        u16,
21        2,
22        0,
23        UNICODE_PROPS_VAR,
24        unicode_props,
25        set_unicode_props
26    );
27}
28
29impl hb_buffer_t {
30    pub(crate) fn allocate_unicode_vars(&mut self) {
31        self.allocate_var(GlyphInfo::UNICODE_PROPS_VAR);
32    }
33    pub(crate) fn deallocate_unicode_vars(&mut self) {
34        self.deallocate_var(GlyphInfo::UNICODE_PROPS_VAR);
35    }
36    pub(crate) fn assert_unicode_vars(&mut self) {
37        self.assert_var(GlyphInfo::UNICODE_PROPS_VAR);
38    }
39
40    pub(crate) fn allocate_gsubgpos_vars(&mut self) {
41        self.allocate_var(GlyphInfo::LIG_PROPS_VAR);
42        self.allocate_var(GlyphInfo::GLYPH_PROPS_VAR);
43    }
44    pub(crate) fn deallocate_gsubgpos_vars(&mut self) {
45        self.deallocate_var(GlyphInfo::LIG_PROPS_VAR);
46        self.deallocate_var(GlyphInfo::GLYPH_PROPS_VAR);
47    }
48    pub(crate) fn assert_gsubgpos_vars(&mut self) {
49        self.assert_var(GlyphInfo::LIG_PROPS_VAR);
50        self.assert_var(GlyphInfo::GLYPH_PROPS_VAR);
51    }
52}
53
54pub const MAX_NESTING_LEVEL: usize = 64;
55pub const MAX_CONTEXT_LENGTH: usize = 64;
56
57pub fn hb_ot_layout_has_kerning(face: &hb_font_t) -> bool {
58    face.aat_tables.kern.is_some()
59}
60
61pub fn hb_ot_layout_has_machine_kerning(face: &hb_font_t) -> bool {
62    match face.aat_tables.kern {
63        Some(ref kern) => kern
64            .0
65            .subtables()
66            .filter_map(Result::ok)
67            .any(|s| s.is_state_machine()),
68        None => false,
69    }
70}
71
72pub fn hb_ot_layout_has_cross_kerning(face: &hb_font_t) -> bool {
73    match face.aat_tables.kern {
74        Some(ref kern) => kern
75            .0
76            .subtables()
77            .filter_map(Result::ok)
78            .any(|s| s.is_cross_stream()),
79        None => false,
80    }
81}
82
83// hb_ot_layout_kern
84
85pub fn _hb_ot_layout_set_glyph_props(face: &hb_font_t, buffer: &mut hb_buffer_t) {
86    buffer.assert_gsubgpos_vars();
87
88    let len = buffer.len;
89    for info in &mut buffer.info[..len] {
90        info.set_glyph_props(face.ot_tables.glyph_props(info.as_glyph()));
91        info.set_lig_props(0);
92    }
93}
94
95pub fn hb_ot_layout_has_glyph_classes(face: &hb_font_t) -> bool {
96    face.ot_tables.has_glyph_classes()
97}
98
99// get_gsubgpos_table
100
101#[derive(Clone, Copy, Debug, PartialEq, Eq)]
102pub enum TableIndex {
103    GSUB = 0,
104    GPOS = 1,
105}
106
107impl TableIndex {
108    pub fn iter() -> impl Iterator<Item = TableIndex> {
109        [Self::GSUB, Self::GPOS].iter().copied()
110    }
111}
112
113impl<T> Index<TableIndex> for [T] {
114    type Output = T;
115
116    fn index(&self, table_index: TableIndex) -> &Self::Output {
117        &self[table_index as usize]
118    }
119}
120
121impl<T> IndexMut<TableIndex> for [T] {
122    fn index_mut(&mut self, table_index: TableIndex) -> &mut Self::Output {
123        &mut self[table_index as usize]
124    }
125}
126
127/// A lookup-based layout table (GSUB or GPOS).
128pub trait LayoutTable {
129    /// The index of this table.
130    const INDEX: TableIndex;
131
132    /// Whether lookups in this table can be applied to the buffer in-place.
133    const IN_PLACE: bool;
134
135    /// Get the lookup at the specified index.
136    fn get_lookup(&self, index: u16) -> Option<&LookupInfo>;
137}
138
139/// Called before substitution lookups are performed, to ensure that glyph
140/// class and other properties are set on the glyphs in the buffer.
141pub fn hb_ot_layout_substitute_start(face: &hb_font_t, buffer: &mut hb_buffer_t) {
142    _hb_ot_layout_set_glyph_props(face, buffer);
143}
144
145/// Applies the lookups in the given GSUB or GPOS table.
146pub fn apply_layout_table<T: LayoutTable>(
147    plan: &hb_ot_shape_plan_t,
148    face: &hb_font_t,
149    font_funcs: &mut FontFuncsDispatch,
150    buffer: &mut hb_buffer_t,
151    table: Option<&T>,
152) {
153    let mut ctx = OT::hb_ot_apply_context_t::new(T::INDEX, face, *font_funcs.scale(), buffer);
154
155    for (stage_index, stage) in plan.ot_map.stages(T::INDEX).iter().enumerate() {
156        if let Some(table) = table {
157            for lookup_map in plan.ot_map.stage_lookups(T::INDEX, stage_index) {
158                let Some(lookup) = table.get_lookup(lookup_map.index) else {
159                    continue;
160                };
161
162                if lookup.digest().may_intersect(&ctx.buffer.digest) {
163                    ctx.lookup_index = lookup_map.index;
164                    ctx.set_lookup_mask(lookup_map.mask);
165                    ctx.auto_zwj = lookup_map.auto_zwj;
166                    ctx.auto_zwnj = lookup_map.auto_zwnj;
167
168                    ctx.random = lookup_map.random;
169                    ctx.per_syllable = lookup_map.per_syllable;
170
171                    apply_string::<T>(&mut ctx, lookup);
172                }
173            }
174        }
175
176        if let Some(func) = stage.pause_func {
177            if func(plan, font_funcs, ctx.buffer) {
178                ctx.buffer.update_digest();
179            }
180        }
181    }
182}
183
184fn apply_string<T: LayoutTable>(ctx: &mut OT::hb_ot_apply_context_t, lookup: &LookupInfo) {
185    if ctx.buffer.is_empty() || ctx.lookup_mask() == 0 {
186        return;
187    }
188
189    ctx.lookup_props = lookup.props();
190    ctx.update_matchers();
191
192    if !lookup.is_reverse() {
193        // in/out forward substitution/positioning
194        if !T::IN_PLACE {
195            ctx.buffer.clear_output();
196        }
197        ctx.buffer.idx = 0;
198        apply_forward(ctx, lookup);
199
200        if !T::IN_PLACE {
201            ctx.buffer.sync();
202        }
203    } else {
204        // in-place backward substitution/positioning
205        debug_assert!(!ctx.buffer.have_output);
206
207        ctx.buffer.idx = ctx.buffer.len - 1;
208        apply_backward(ctx, lookup);
209    }
210}
211
212fn apply_forward(ctx: &mut OT::hb_ot_apply_context_t, lookup: &LookupInfo) -> bool {
213    let mut ret = false;
214    let Some(table_data) = ctx.face.ot_tables.table_data(ctx.table_index) else {
215        return false;
216    };
217
218    let use_hot_subtable_cache = lookup.cache_enter(ctx);
219
220    while ctx.buffer.successful {
221        let mut j = ctx.buffer.idx;
222        while j < ctx.buffer.len && {
223            let info = &ctx.buffer.info[j];
224            !(lookup.digest.may_have(info.glyph_id)
225                && (info.mask & ctx.lookup_mask()) != 0
226                && check_glyph_property(ctx.face, info, ctx.lookup_props))
227        } {
228            j += 1;
229        }
230        if j > ctx.buffer.idx {
231            ctx.buffer.next_glyphs(j - ctx.buffer.idx);
232        }
233        if ctx.buffer.idx >= ctx.buffer.len {
234            break;
235        }
236
237        if lookup
238            .apply(ctx, table_data, use_hot_subtable_cache)
239            .is_some()
240        {
241            ret = true;
242        } else {
243            ctx.buffer.next_glyph();
244        }
245    }
246
247    if use_hot_subtable_cache {
248        lookup.cache_leave(ctx);
249    }
250
251    ret
252}
253
254fn apply_backward(ctx: &mut OT::hb_ot_apply_context_t, lookup: &LookupInfo) -> bool {
255    let mut ret = false;
256    let Some(table_data) = ctx.face.ot_tables.table_data(ctx.table_index) else {
257        return false;
258    };
259    loop {
260        let cur = ctx.buffer.cur(0);
261        ret |= lookup.digest.may_have(cur.glyph_id)
262            && (cur.mask & ctx.lookup_mask()) != 0
263            && check_glyph_property(ctx.face, cur, ctx.lookup_props)
264            && lookup.apply(ctx, table_data, false).is_some();
265
266        if ctx.buffer.idx == 0 {
267            break;
268        }
269
270        ctx.buffer.idx -= 1;
271    }
272    ret
273}
274
275/* unicode_props */
276
277/* Design:
278 * unicode_props() is a two-byte number.  The low byte includes:
279 * - Modified General_Category: 5 bits.
280 * - A bit each for:
281 *   * Is it Default_Ignorable(); we have a modified Default_Ignorable().
282 *   * Whether it's one of the four Mongolian Free Variation Selectors,
283 *     CGJ, or other characters that are hidden but should not be ignored
284 *     like most other Default_Ignorable()s do during GSUB matching.
285 *   * Whether it's a grapheme continuation.
286 *
287 * The high-byte has different meanings, switched by the Gen-Cat:
288 * - For Mn,Mc,Me: the modified Combining_Class.
289 * - For Cf: whether it's ZWJ, ZWNJ, or something else.
290 * - For Ws: index of which space character this is, if space fallback
291 *   is needed, ie. we don't set this by default, only if asked to.
292 *
293 * Above I said "modified" General_Category. This is because we need to
294 * remember Variation Selectors, and we don't have bits left. So we
295 * change their Gen_Cat from Mn to Cf, and use a bit of the high byte to
296 * remember them.
297 */
298
299//  enum hb_unicode_props_flags_t {
300//     UPROPS_MASK_GEN_CAT	= 0x001Fu,
301//     UPROPS_MASK_IGNORABLE	= 0x0020u,
302//     UPROPS_MASK_HIDDEN	= 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..4, or TAG characters */
303//     UPROPS_MASK_CONTINUATION=0x0080u,
304
305//     /* If GEN_CAT=FORMAT, top byte masks: */
306//     UPROPS_MASK_Cf_ZWJ	= 0x0100u,
307//     UPROPS_MASK_Cf_ZWNJ	= 0x0200u
308//   };
309//   HB_MARK_AS_FLAG_T (hb_unicode_props_flags_t);
310
311//   static inline void
312//   _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer)
313//   {
314//     hb_unicode_funcs_t *unicode = buffer->unicode;
315//     unsigned int u = info->codepoint;
316//     unsigned int gen_cat = (unsigned int) unicode->general_category (u);
317//     unsigned int props = gen_cat;
318
319//     if (u >= 0x80u)
320//     {
321//       if (unlikely (unicode->is_default_ignorable (u)))
322//       {
323//         buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES;
324//         props |=  UPROPS_MASK_IGNORABLE;
325//         if (u == 0x200Cu) props |= UPROPS_MASK_Cf_ZWNJ;
326//         else if (u == 0x200Du) props |= UPROPS_MASK_Cf_ZWJ;
327//         /* Mongolian Free Variation Selectors need to be remembered
328//          * because although we need to hide them like default-ignorables,
329//          * they need to non-ignorable during shaping.  This is similar to
330//          * what we do for joiners in Indic-like shapers, but since the
331//          * FVSes are GC=Mn, we have use a separate bit to remember them.
332//          * Fixes:
333//          * https://github.com/harfbuzz/harfbuzz/issues/234 */
334//         else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0x180Bu, 0x180Du, 0x180Fu, 0x180Fu))) props |= UPROPS_MASK_HIDDEN;
335//         /* TAG characters need similar treatment. Fixes:
336//          * https://github.com/harfbuzz/harfbuzz/issues/463 */
337//         else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN;
338//         /* COMBINING GRAPHEME JOINER should not be skipped; at least some times.
339//          * https://github.com/harfbuzz/harfbuzz/issues/554 */
340//         else if (unlikely (u == 0x034Fu))
341//         {
342//       buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_CGJ;
343//       props |= UPROPS_MASK_HIDDEN;
344//         }
345//       }
346
347//       if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (gen_cat)))
348//       {
349//         props |= UPROPS_MASK_CONTINUATION;
350//         props |= unicode->modified_combining_class (u)<<8;
351//       }
352//     }
353
354//     info->unicode_props() = props;
355//   }
356
357impl GlyphInfo {
358    /// HB: _hb_glyph_info_set_general_category
359    ///
360    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L260>
361    #[doc(alias = "_hb_glyph_info_set_general_category")]
362    #[inline]
363    pub(crate) fn set_general_category(&mut self, gen_cat: GeneralCategory) {
364        /* Clears top-byte. */
365        let gen_cat = gen_cat.0 as u32;
366        let n = (gen_cat as u16)
367            | (self.unicode_props() & (0xFF & !UnicodeProps::GENERAL_CATEGORY.bits()));
368        self.set_unicode_props(n);
369    }
370
371    /// HB: _hb_glyph_info_get_general_category
372    ///
373    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L268>
374    #[doc(alias = "_hb_glyph_info_get_general_category")]
375    #[inline]
376    pub(crate) fn general_category(&self) -> GeneralCategory {
377        let n = self.unicode_props() & UnicodeProps::GENERAL_CATEGORY.bits();
378        GeneralCategory(n as u8)
379    }
380
381    /// HB: _hb_glyph_info_is_unicode_mark
382    ///
383    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L274>
384    #[doc(alias = "_hb_glyph_info_is_unicode_mark")]
385    #[inline]
386    pub(crate) fn is_unicode_mark(&self) -> bool {
387        self.general_category().is_mark()
388    }
389
390    /// HB: _hb_glyph_info_set_modified_combining_class
391    ///
392    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L279>
393    #[doc(alias = "_hb_glyph_info_set_modified_combining_class")]
394    #[inline]
395    pub(crate) fn set_modified_combining_class(&mut self, modified_class: u8) {
396        if !self.is_unicode_mark() {
397            return;
398        }
399        let n = ((modified_class as u16) << 8) | (self.unicode_props() & 0xFF);
400        self.set_unicode_props(n);
401    }
402
403    /// HB: _hb_glyph_info_get_modified_combining_class
404    ///
405    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L287>
406    #[doc(alias = "_hb_glyph_info_get_modified_combining_class")]
407    #[inline]
408    pub(crate) fn modified_combining_class(&self) -> u8 {
409        if self.is_unicode_mark() {
410            (self.unicode_props() >> 8) as u8
411        } else {
412            0
413        }
414    }
415
416    // TODO: use
417    // #[inline]
418    // pub fn info_cc(info: &hb_glyph_info_t) -> u8 {
419    //     _hb_glyph_info_get_modified_combining_class(info)
420    // }
421
422    /// HB: _hb_glyph_info_is_unicode_space
423    ///
424    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L294>
425    #[doc(alias = "_hb_glyph_info_is_unicode_space")]
426    #[inline]
427    pub(crate) fn is_unicode_space(&self) -> bool {
428        self.general_category() == GeneralCategory::SPACE_SEPARATOR
429    }
430
431    /// HB: _hb_glyph_info_set_unicode_space_fallback_type
432    ///
433    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L300>
434    #[doc(alias = "_hb_glyph_info_set_unicode_space_fallback_type")]
435    #[inline]
436    pub(crate) fn set_unicode_space_fallback_type(&mut self, s: hb_unicode_funcs_t::space_t) {
437        if !self.is_unicode_space() {
438            return;
439        }
440        let n = ((s as u16) << 8) | (self.unicode_props() & 0xFF);
441        self.set_unicode_props(n);
442    }
443
444    /// HB: _hb_glyph_info_get_unicode_space_fallback_type
445    ///
446    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L307>
447    #[doc(alias = "_hb_glyph_info_get_unicode_space_fallback_type")]
448    #[inline]
449    pub(crate) fn unicode_space_fallback_type(&self) -> hb_unicode_funcs_t::space_t {
450        if self.is_unicode_space() {
451            (self.unicode_props() >> 8) as u8
452        } else {
453            hb_unicode_funcs_t::NOT_SPACE
454        }
455    }
456
457    /// HB: _hb_glyph_info_is_variation_selector
458    ///
459    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L314>
460    #[doc(alias = "_hb_glyph_info_is_variation_selector")]
461    #[inline]
462    pub(crate) fn is_variation_selector(&self) -> bool {
463        let a = self.general_category() == GeneralCategory::FORMAT;
464        let b = (self.unicode_props() & UnicodeProps::CF_VS.bits()) != 0;
465        a && b
466    }
467
468    /// HB: _hb_glyph_info_set_variation_selector
469    ///
470    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L321>
471    #[doc(alias = "_hb_glyph_info_set_variation_selector")]
472    #[inline]
473    pub(crate) fn set_variation_selector(&mut self, customize: bool) {
474        if customize {
475            self.set_general_category(GeneralCategory::FORMAT);
476            self.set_unicode_props(self.unicode_props() | UnicodeProps::CF_VS.bits());
477        } else {
478            // Reset to their original condition
479            self.set_general_category(GeneralCategory::NON_SPACING_MARK);
480        }
481    }
482
483    /// HB: _hb_glyph_info_is_default_ignorable
484    ///
485    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L338>
486    #[doc(alias = "_hb_glyph_info_is_default_ignorable")]
487    #[inline]
488    pub(crate) fn is_default_ignorable(&self) -> bool {
489        let n = self.unicode_props() & UnicodeProps::IGNORABLE.bits();
490        n != 0 && !self.substituted()
491    }
492
493    /// HB: _hb_glyph_info_set_default_ignorable
494    ///
495    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L344>
496    #[doc(alias = "_hb_glyph_info_set_default_ignorable")]
497    #[inline]
498    pub(crate) fn _set_default_ignorable(&mut self) {
499        self.set_unicode_props(self.unicode_props() | UnicodeProps::IGNORABLE.bits());
500    }
501
502    /// HB: _hb_glyph_info_clear_default_ignorable
503    ///
504    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L349>
505    #[doc(alias = "_hb_glyph_info_clear_default_ignorable")]
506    #[inline]
507    pub(crate) fn clear_default_ignorable(&mut self) {
508        let mut n = self.unicode_props();
509        n &= !UnicodeProps::IGNORABLE.bits();
510        self.set_unicode_props(n);
511    }
512
513    /// HB: _hb_glyph_info_is_hidden
514    ///
515    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L354>
516    #[doc(alias = "_hb_glyph_info_is_hidden")]
517    #[inline]
518    pub(crate) fn is_hidden(&self) -> bool {
519        (self.unicode_props() & UnicodeProps::HIDDEN.bits()) != 0
520    }
521
522    //   static inline void
523    //   _hb_glyph_info_unhide (hb_glyph_info_t *info)
524    //   {
525    //     info->unicode_props() &= ~ UPROPS_MASK_HIDDEN;
526    //   }
527
528    /// HB: _hb_glyph_info_set_continuation
529    ///
530    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L365>
531    #[doc(alias = "_hb_glyph_info_set_continuation")]
532    #[inline]
533    pub(crate) fn set_continuation(&mut self, scratch_flags: &mut hb_buffer_scratch_flags_t) {
534        *scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS;
535        let mut n = self.unicode_props();
536        n |= UnicodeProps::CONTINUATION.bits();
537        self.set_unicode_props(n);
538    }
539
540    /// HB: _hb_glyph_info_clear_continuation
541    ///
542    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L370>
543    #[doc(alias = "_hb_glyph_info_clear_continuation")]
544    #[inline]
545    pub(crate) fn clear_continuation(&mut self) {
546        let mut n = self.unicode_props();
547        n &= !UnicodeProps::CONTINUATION.bits();
548        self.set_unicode_props(n);
549    }
550
551    /// HB: _hb_glyph_info_is_continuation
552    ///
553    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L375>
554    #[doc(alias = "_hb_glyph_info_is_continuation")]
555    #[inline]
556    pub(crate) fn is_continuation(&self) -> bool {
557        self.unicode_props() & UnicodeProps::CONTINUATION.bits() != 0
558    }
559}
560
561pub(crate) fn _hb_grapheme_group_func(_: &GlyphInfo, b: &GlyphInfo) -> bool {
562    b.is_continuation()
563}
564
565pub fn _hb_ot_layout_reverse_graphemes(buffer: &mut hb_buffer_t) {
566    // MONOTONE_GRAPHEMES was already applied and is taken care of by _hb_grapheme_group_func.
567    // So we just check for MONOTONE_CHARACTERS here.
568    buffer.reverse_groups(
569        _hb_grapheme_group_func,
570        buffer.cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS,
571    );
572}
573
574impl GlyphInfo {
575    /// HB: _hb_glyph_info_is_unicode_format
576    ///
577    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L398>
578    #[doc(alias = "_hb_glyph_info_is_unicode_format")]
579    #[inline]
580    pub(crate) fn is_unicode_format(&self) -> bool {
581        self.general_category() == GeneralCategory::FORMAT
582    }
583
584    /// HB: _hb_glyph_info_is_zwnj
585    ///
586    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L404>
587    #[doc(alias = "_hb_glyph_info_is_zwnj")]
588    #[inline]
589    pub(crate) fn is_zwnj(&self) -> bool {
590        self.is_unicode_format() && (self.unicode_props() & UnicodeProps::CF_ZWNJ.bits() != 0)
591    }
592
593    /// HB: _hb_glyph_info_is_zwj
594    ///
595    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L409>
596    #[doc(alias = "_hb_glyph_info_is_zwnj")]
597    #[inline]
598    pub(crate) fn is_zwj(&self) -> bool {
599        self.is_unicode_format() && (self.unicode_props() & UnicodeProps::CF_ZWJ.bits() != 0)
600    }
601
602    /// HB: _hb_glyph_info_is_aat_deleted
603    ///
604    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L426>
605    #[doc(alias = "_hb_glyph_info_is_aat_deleted")]
606    #[inline]
607    pub(crate) fn is_aat_deleted(&self) -> bool {
608        self.is_unicode_format()
609            && (self.unicode_props() & UnicodeProps::CF_AAT_DELETED.bits() != 0)
610    }
611
612    /// HB: _hb_glyph_info_set_aat_deleted
613    ///
614    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L431>
615    #[doc(alias = "_hb_glyph_info_set_aat_deleted")]
616    #[inline]
617    pub(crate) fn set_aat_deleted(&mut self) {
618        self.set_general_category(GeneralCategory::FORMAT);
619        self.set_unicode_props(
620            self.unicode_props()
621                | UnicodeProps::CF_AAT_DELETED.bits()
622                | UnicodeProps::HIDDEN.bits(),
623        );
624    }
625
626    //   /* lig_props: aka lig_id / lig_comp
627    //    *
628    //    * When a ligature is formed:
629    //    *
630    //    *   - The ligature glyph and any marks in between all the same newly allocated
631    //    *     lig_id,
632    //    *   - The ligature glyph will get lig_num_comps set to the number of components
633    //    *   - The marks get lig_comp > 0, reflecting which component of the ligature
634    //    *     they were applied to.
635    //    *   - This is used in GPOS to attach marks to the right component of a ligature
636    //    *     in MarkLigPos,
637    //    *   - Note that when marks are ligated together, much of the above is skipped
638    //    *     and the current lig_id reused.
639    //    *
640    //    * When a multiple-substitution is done:
641    //    *
642    //    *   - All resulting glyphs will have lig_id = 0,
643    //    *   - The resulting glyphs will have lig_comp = 0, 1, 2, ... respectively.
644    //    *   - This is used in GPOS to attach marks to the first component of a
645    //    *     multiple substitution in MarkBasePos.
646    //    *
647    //    * The numbers are also used in GPOS to do mark-to-mark positioning only
648    //    * to marks that belong to the same component of the same ligature.
649    //    */
650    //   static inline void
651    //   _hb_glyph_info_clear_lig_props (hb_glyph_info_t *info)
652    //   {
653    //     info->lig_props() = 0;
654    //   }
655
656    const IS_LIG_BASE: u8 = 0x10;
657
658    /// HB: _hb_glyph_info_set_lig_props_for_ligature
659    ///
660    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L472>
661    #[doc(alias = "_hb_glyph_info_set_lig_props_for_ligature")]
662    #[inline]
663    pub(crate) fn set_lig_props_for_ligature(&mut self, lig_id: u8, lig_num_comps: u8) {
664        self.set_lig_props((lig_id << 5) | Self::IS_LIG_BASE | (lig_num_comps & 0x0F));
665    }
666
667    /// HB: _hb_glyph_info_set_lig_props_for_mark
668    ///
669    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L480>
670    #[doc(alias = "_hb_glyph_info_set_lig_props_for_mark")]
671    #[inline]
672    pub(crate) fn set_lig_props_for_mark(&mut self, lig_id: u8, lig_comp: u8) {
673        self.set_lig_props((lig_id << 5) | (lig_comp & 0x0F));
674    }
675
676    /// HB: _hb_glyph_info_set_lig_props_for_component
677    ///
678    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L488>
679    #[doc(alias = "_hb_glyph_info_set_lig_props_for_component")]
680    #[inline]
681    pub(crate) fn set_lig_props_for_component(&mut self, comp: u8) {
682        self.set_lig_props_for_mark(0, comp);
683    }
684
685    /// HB: _hb_glyph_info_get_lig_id
686    ///
687    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L494>
688    #[doc(alias = "_hb_glyph_info_get_lig_id")]
689    #[inline]
690    pub(crate) fn lig_id(&self) -> u8 {
691        self.lig_props() >> 5
692    }
693
694    /// HB: _hb_glyph_info_ligated_internal
695    ///
696    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L500>
697    #[doc(alias = "_hb_glyph_info_ligated_internal")]
698    #[inline]
699    pub(crate) fn ligated_internal(&self) -> bool {
700        self.lig_props() & Self::IS_LIG_BASE != 0
701    }
702
703    /// HB: _hb_glyph_info_get_lig_comp
704    ///
705    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L506>
706    #[doc(alias = "_hb_glyph_info_get_lig_comp")]
707    #[inline]
708    pub(crate) fn lig_comp(&self) -> u8 {
709        if self.ligated_internal() {
710            0
711        } else {
712            self.lig_props() & 0x0F
713        }
714    }
715
716    /// HB: _hb_glyph_info_get_lig_num_comps
717    ///
718    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L515>
719    #[doc(alias = "_hb_glyph_info_get_lig_num_comps")]
720    #[inline]
721    pub(crate) fn lig_num_comps(&self) -> u8 {
722        if self.glyph_props() & GlyphPropsFlags::LIGATURE.bits() != 0 && self.ligated_internal() {
723            self.lig_props() & 0x0F
724        } else {
725            1
726        }
727    }
728
729    //   /* glyph_props: */
730    //   static inline void
731    //   _hb_glyph_info_set_glyph_props (hb_glyph_info_t *info, unsigned int props)
732    //   {
733    //     info->glyph_props() = props;
734    //   }
735
736    //   static inline unsigned int
737    //   _hb_glyph_info_get_glyph_props (const hb_glyph_info_t *info)
738    //   {
739    //     return info->glyph_props();
740    //   }
741
742    /// HB: _hb_glyph_info_is_base_glyph
743    ///
744    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L548>
745    #[doc(alias = "_hb_glyph_info_is_base_glyph")]
746    #[inline]
747    pub(crate) fn is_base_glyph(&self) -> bool {
748        self.glyph_props() & GlyphPropsFlags::BASE_GLYPH.bits() != 0
749    }
750
751    /// HB: _hb_glyph_info_is_ligature
752    ///
753    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L554>
754    #[doc(alias = "_hb_glyph_info_is_ligature")]
755    #[inline]
756    pub(crate) fn is_ligature(&self) -> bool {
757        self.glyph_props() & GlyphPropsFlags::LIGATURE.bits() != 0
758    }
759
760    /// HB: _hb_glyph_info_is_mark
761    ///
762    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L560>
763    #[doc(alias = "_hb_glyph_info_is_mark")]
764    #[inline]
765    pub(crate) fn is_mark(&self) -> bool {
766        self.glyph_props() & GlyphPropsFlags::MARK.bits() != 0
767    }
768
769    /// HB: _hb_glyph_info_substituted
770    ///
771    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L566>
772    #[doc(alias = "_hb_glyph_info_substituted")]
773    #[inline]
774    pub(crate) fn substituted(&self) -> bool {
775        self.glyph_props() & GlyphPropsFlags::SUBSTITUTED.bits() != 0
776    }
777
778    /// HB: _hb_glyph_info_ligated
779    ///
780    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L572>
781    #[doc(alias = "_hb_glyph_info_ligated")]
782    #[inline]
783    pub(crate) fn ligated(&self) -> bool {
784        self.glyph_props() & GlyphPropsFlags::LIGATED.bits() != 0
785    }
786
787    /// HB: _hb_glyph_info_multiplied
788    ///
789    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L578>
790    #[doc(alias = "_hb_glyph_info_multiplied")]
791    #[inline]
792    pub(crate) fn multiplied(&self) -> bool {
793        self.glyph_props() & GlyphPropsFlags::MULTIPLIED.bits() != 0
794    }
795
796    /// HB: _hb_glyph_info_ligated_and_didnt_multiply
797    ///
798    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L584>
799    #[doc(alias = "_hb_glyph_info_ligated_and_didnt_multiply")]
800    #[inline]
801    pub(crate) fn ligated_and_didnt_multiply(&self) -> bool {
802        self.ligated() && !self.multiplied()
803    }
804
805    /// HB: _hb_glyph_info_clear_ligated_and_multiplied
806    ///
807    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L590>
808    #[doc(alias = "_hb_glyph_info_clear_ligated_and_multiplied")]
809    #[inline]
810    pub(crate) fn clear_ligated_and_multiplied(&mut self) {
811        let mut n = self.glyph_props();
812        n &= !(GlyphPropsFlags::LIGATED | GlyphPropsFlags::MULTIPLIED).bits();
813        self.set_glyph_props(n);
814    }
815
816    /// HB: _hb_glyph_info_clear_substituted
817    ///
818    /// See <https://github.com/harfbuzz/harfbuzz/blob/368598b5bd9c37a15cb0fd5438b8e617e254609b/src/hb-ot-layout.hh#L597>
819    #[doc(alias = "_hb_glyph_info_clear_substituted")]
820    #[inline]
821    pub(crate) fn clear_substituted(&mut self) {
822        let mut n = self.glyph_props();
823        n &= !GlyphPropsFlags::SUBSTITUTED.bits();
824        self.set_glyph_props(n);
825    }
826}
827
828pub fn _hb_clear_substitution_flags(
829    _: &hb_ot_shape_plan_t,
830    _: &mut FontFuncsDispatch,
831    buffer: &mut hb_buffer_t,
832) -> bool {
833    let len = buffer.len;
834    for info in &mut buffer.info[..len] {
835        info.clear_substituted();
836    }
837
838    false
839}