rustybuzz/hb/ot/layout/GPOS/
mark_base_pos.rs

1use crate::hb::buffer::hb_buffer_t;
2use crate::hb::ot::layout::GPOS::mark_array::MarkArrayExt;
3use crate::hb::ot_layout::{
4    _hb_glyph_info_get_lig_comp, _hb_glyph_info_get_lig_id, _hb_glyph_info_is_mark,
5    _hb_glyph_info_multiplied,
6};
7use crate::hb::ot_layout_common::lookup_flags;
8use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t;
9use crate::hb::ot_layout_gsubgpos::{match_t, skipping_iterator_t, Apply};
10use ttf_parser::gpos::MarkToBaseAdjustment;
11
12impl Apply for MarkToBaseAdjustment<'_> {
13    fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
14        let buffer = &ctx.buffer;
15        let mark_glyph = ctx.buffer.cur(0).as_glyph();
16        let mark_index = self.mark_coverage.get(mark_glyph)?;
17
18        // Due to borrowing rules, we have this piece of code before creating the
19        // iterator, unlike in harfbuzz.
20        if ctx.last_base_until > buffer.idx as u32 {
21            ctx.last_base_until = 0;
22            ctx.last_base = -1;
23        }
24
25        // Now we search backwards for a non-mark glyph
26        // We don't use skippy_iter.prev() to avoid O(n^2) behavior.
27        let mut iter = skipping_iterator_t::new(ctx, 0, false);
28        iter.set_lookup_props(u32::from(lookup_flags::IGNORE_MARKS));
29
30        let mut j = buffer.idx;
31        while j > ctx.last_base_until as usize {
32            let mut _match = iter.match_(&buffer.info[j - 1]);
33            if _match == match_t::MATCH {
34                // https://github.com/harfbuzz/harfbuzz/issues/4124
35                if !accept(buffer, j - 1)
36                    && !self.base_coverage.contains(buffer.info[j - 1].as_glyph())
37                {
38                    _match = match_t::SKIP;
39                }
40            }
41
42            if _match == match_t::MATCH {
43                ctx.last_base = j as i32 - 1;
44                break;
45            }
46
47            j -= 1;
48        }
49        ctx.last_base_until = buffer.idx as u32;
50
51        if ctx.last_base == -1 {
52            ctx.buffer
53                .unsafe_to_concat_from_outbuffer(Some(0), Some(buffer.idx + 1));
54            return None;
55        }
56
57        let idx = ctx.last_base as u32;
58
59        let info = &buffer.info;
60
61        // Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled
62        let base_glyph = info[idx as usize].as_glyph();
63        let Some(base_index) = self.base_coverage.get(base_glyph) else {
64            ctx.buffer
65                .unsafe_to_concat_from_outbuffer(Some(idx as usize), Some(buffer.idx + 1));
66            return None;
67        };
68
69        self.marks
70            .apply(ctx, self.anchors, mark_index, base_index, idx as usize)
71    }
72}
73
74fn accept(buffer: &hb_buffer_t, idx: usize) -> bool {
75    /* We only want to attach to the first of a MultipleSubst sequence.
76     * https://github.com/harfbuzz/harfbuzz/issues/740
77     * Reject others...
78     * ...but stop if we find a mark in the MultipleSubst sequence:
79     * https://github.com/harfbuzz/harfbuzz/issues/1020 */
80    !_hb_glyph_info_multiplied(&buffer.info[idx])
81        || 0 == _hb_glyph_info_get_lig_comp(&buffer.info[idx])
82        || (idx == 0
83            || _hb_glyph_info_is_mark(&buffer.info[idx - 1])
84            || !_hb_glyph_info_multiplied(&buffer.info[idx - 1])
85            || _hb_glyph_info_get_lig_id(&buffer.info[idx])
86                != _hb_glyph_info_get_lig_id(&buffer.info[idx - 1])
87            || _hb_glyph_info_get_lig_comp(&buffer.info[idx])
88                != _hb_glyph_info_get_lig_comp(&buffer.info[idx - 1]) + 1)
89}