1 | use crate::hb::buffer::hb_buffer_t; |
2 | use crate::hb::ot::layout::GPOS::mark_array::MarkArrayExt; |
3 | use 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 | }; |
7 | use crate::hb::ot_layout_common::lookup_flags; |
8 | use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; |
9 | use crate::hb::ot_layout_gsubgpos::{match_t, skipping_iterator_t, Apply}; |
10 | use ttf_parser::gpos::MarkToBaseAdjustment; |
11 | |
12 | impl 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 | |
74 | fn 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 | |