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}
90