Skip to main content

harfrust/hb/ot/gpos/
cursive.rs

1use crate::hb::buffer::HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
2use crate::hb::ot_layout_common::lookup_flags;
3use crate::hb::ot_layout_gpos_table::attach_type;
4use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t;
5use crate::hb::ot_layout_gsubgpos::{skipping_iterator_t, Apply};
6use crate::{Direction, GlyphPosition};
7use read_fonts::tables::gpos::CursivePosFormat1;
8
9impl Apply for CursivePosFormat1<'_> {
10    fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
11        let this = ctx.buffer.cur(0).as_glyph();
12
13        let coverage = self.coverage().ok()?;
14        let index_this = coverage.get(this)? as usize;
15        let records = self.entry_exit_record();
16        let offset_data = self.offset_data();
17        let entry_this = records.get(index_this)?.entry_anchor(offset_data)?.ok()?;
18
19        let mut iter = skipping_iterator_t::new(ctx, false);
20        iter.reset_fast(iter.buffer.idx);
21
22        let mut unsafe_from = 0;
23        if !iter.prev(Some(&mut unsafe_from)) {
24            ctx.buffer
25                .unsafe_to_concat_from_outbuffer(Some(unsafe_from), Some(ctx.buffer.idx + 1));
26            return None;
27        }
28
29        let i = iter.index();
30        let prev = iter.buffer.info[i].as_glyph();
31        let index_prev = coverage.get(prev)? as usize;
32        let Some(exit_prev) = records
33            .get(index_prev)
34            .and_then(|rec| rec.exit_anchor(offset_data).transpose().ok().flatten())
35        else {
36            iter.buffer
37                .unsafe_to_concat_from_outbuffer(Some(iter.index()), Some(iter.buffer.idx + 1));
38            return None;
39        };
40
41        let (exit_x, exit_y) = ctx.face.ot_tables.resolve_anchor(&exit_prev);
42        let (entry_x, entry_y) = ctx.face.ot_tables.resolve_anchor(&entry_this);
43        let exit_x = ctx.scale_x(exit_x);
44        let exit_y = ctx.scale_y(exit_y);
45        let entry_x = ctx.scale_x(entry_x);
46        let entry_y = ctx.scale_y(entry_y);
47
48        let direction = ctx.buffer.direction;
49        let j = ctx.buffer.idx;
50        ctx.buffer.unsafe_to_break(Some(i), Some(j + 1));
51
52        let pos = &mut ctx.buffer.pos;
53        match direction {
54            Direction::LeftToRight => {
55                pos[i].x_advance = exit_x + pos[i].x_offset;
56                let d = entry_x + pos[j].x_offset;
57                pos[j].x_advance -= d;
58                pos[j].x_offset -= d;
59            }
60            Direction::RightToLeft => {
61                let d = exit_x + pos[i].x_offset;
62                pos[i].x_advance -= d;
63                pos[i].x_offset -= d;
64                pos[j].x_advance = entry_x + pos[j].x_offset;
65            }
66            Direction::TopToBottom => {
67                pos[i].y_advance = exit_y + pos[i].y_offset;
68                let d = entry_y + pos[j].y_offset;
69                pos[j].y_advance -= d;
70                pos[j].y_offset -= d;
71            }
72            Direction::BottomToTop => {
73                let d = exit_y + pos[i].y_offset;
74                pos[i].y_advance -= d;
75                pos[i].y_offset -= d;
76                pos[j].y_advance = entry_y;
77            }
78            Direction::Invalid => {}
79        }
80
81        // Cross-direction adjustment
82
83        // We attach child to parent (think graph theory and rooted trees whereas
84        // the root stays on baseline and each node aligns itself against its
85        // parent.
86        //
87        // Optimize things for the case of RightToLeft, as that's most common in
88        // Arabic.
89        let mut child = i;
90        let mut parent = j;
91        let mut x_offset = entry_x - exit_x;
92        let mut y_offset = entry_y - exit_y;
93
94        // Low bits are lookup flags, so we want to truncate.
95        if ctx.lookup_props as u16 & lookup_flags::RIGHT_TO_LEFT == 0 {
96            core::mem::swap(&mut child, &mut parent);
97            x_offset = -x_offset;
98            y_offset = -y_offset;
99        }
100
101        // If child was already connected to someone else, walk through its old
102        // chain and reverse the link direction, such that the whole tree of its
103        // previous connection now attaches to new parent.  Watch out for case
104        // where new parent is on the path from old chain...
105        reverse_cursive_minor_offset(pos, child, direction, parent);
106
107        pos[child].set_attach_type(attach_type::CURSIVE);
108        pos[child].set_attach_chain((parent as isize - child as isize) as i16);
109
110        ctx.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
111        if direction.is_horizontal() {
112            pos[child].y_offset = y_offset;
113        } else {
114            pos[child].x_offset = x_offset;
115        }
116
117        // If parent was attached to child, separate them.
118        // https://github.com/harfbuzz/harfbuzz/issues/2469
119        if pos[parent].attach_chain() == -pos[child].attach_chain() {
120            pos[parent].set_attach_chain(0);
121
122            if direction.is_horizontal() {
123                pos[parent].y_offset = 0;
124            } else {
125                pos[parent].x_offset = 0;
126            }
127        }
128
129        ctx.buffer.idx += 1;
130        Some(())
131    }
132}
133
134fn reverse_cursive_minor_offset(
135    pos: &mut [GlyphPosition],
136    i: usize,
137    direction: Direction,
138    new_parent: usize,
139) {
140    let chain = pos[i].attach_chain();
141    let attach_type = pos[i].attach_type();
142    if chain == 0 || attach_type & attach_type::CURSIVE == 0 {
143        return;
144    }
145
146    pos[i].set_attach_chain(0);
147
148    // Stop if we see new parent in the chain.
149    let j = (i as isize + isize::from(chain)) as _;
150    if j == new_parent {
151        return;
152    }
153
154    reverse_cursive_minor_offset(pos, j, direction, new_parent);
155
156    if direction.is_horizontal() {
157        pos[j].y_offset = -pos[i].y_offset;
158    } else {
159        pos[j].x_offset = -pos[i].x_offset;
160    }
161
162    pos[j].set_attach_chain(-chain);
163    pos[j].set_attach_type(attach_type);
164}