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

1use crate::hb::ot::layout::GPOS::mark_array::MarkArrayExt;
2use crate::hb::ot_layout::{_hb_glyph_info_get_lig_comp, _hb_glyph_info_get_lig_id};
3use crate::hb::ot_layout_common::lookup_flags;
4use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t;
5use crate::hb::ot_layout_gsubgpos::{match_t, skipping_iterator_t, Apply};
6use ttf_parser::gpos::MarkToLigatureAdjustment;
7
8impl Apply for MarkToLigatureAdjustment<'_> {
9    fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
10        let buffer = &ctx.buffer;
11        let mark_glyph = ctx.buffer.cur(0).as_glyph();
12        let mark_index = self.mark_coverage.get(mark_glyph)?;
13
14        // Due to borrowing rules, we have this piece of code before creating the
15        // iterator, unlike in harfbuzz.
16        if ctx.last_base_until > buffer.idx as u32 {
17            ctx.last_base_until = 0;
18            ctx.last_base = -1;
19        }
20
21        // Now we search backwards for a non-mark glyph
22        let mut iter = skipping_iterator_t::new(ctx, 0, false);
23        iter.set_lookup_props(u32::from(lookup_flags::IGNORE_MARKS));
24
25        let mut j = buffer.idx;
26        while j > ctx.last_base_until as usize {
27            let mut _match = iter.match_(&buffer.info[j - 1]);
28            if _match == match_t::MATCH {
29                ctx.last_base = j as i32 - 1;
30                break;
31            }
32            j -= 1;
33        }
34
35        ctx.last_base_until = buffer.idx as u32;
36
37        if ctx.last_base == -1 {
38            ctx.buffer
39                .unsafe_to_concat_from_outbuffer(Some(0), Some(buffer.idx + 1));
40            return None;
41        }
42
43        let idx = ctx.last_base as usize;
44
45        // Checking that matched glyph is actually a ligature by GDEF is too strong; disabled
46
47        let lig_glyph = buffer.info[idx].as_glyph();
48        let Some(lig_index) = self.ligature_coverage.get(lig_glyph) else {
49            ctx.buffer
50                .unsafe_to_concat_from_outbuffer(Some(idx), Some(buffer.idx + 1));
51            return None;
52        };
53        let lig_attach = self.ligature_array.get(lig_index)?;
54
55        // Find component to attach to
56        let comp_count = lig_attach.rows;
57        if comp_count == 0 {
58            ctx.buffer
59                .unsafe_to_concat_from_outbuffer(Some(idx), Some(buffer.idx + 1));
60            return None;
61        }
62
63        // We must now check whether the ligature ID of the current mark glyph
64        // is identical to the ligature ID of the found ligature.  If yes, we
65        // can directly use the component index.  If not, we attach the mark
66        // glyph to the last component of the ligature.
67        let lig_id = _hb_glyph_info_get_lig_id(&buffer.info[idx]);
68        let mark_id = _hb_glyph_info_get_lig_id(buffer.cur(0));
69        let mark_comp = u16::from(_hb_glyph_info_get_lig_comp(buffer.cur(0)));
70        let matches = lig_id != 0 && lig_id == mark_id && mark_comp > 0;
71        let comp_index = if matches {
72            mark_comp.min(comp_count)
73        } else {
74            comp_count
75        } - 1;
76
77        self.marks
78            .apply(ctx, lig_attach, mark_index, comp_index, idx)
79    }
80}