use crate::hb::buffer::HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
use crate::hb::ot_layout_common::lookup_flags;
use crate::hb::ot_layout_gpos_table::attach_type;
use crate::hb::ot_layout_gpos_table::AnchorExt;
use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t;
use crate::hb::ot_layout_gsubgpos::{skipping_iterator_t, Apply};
use crate::{Direction, GlyphPosition};
use ttf_parser::gpos::CursiveAdjustment;
impl Apply for CursiveAdjustment<'_> {
fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
let this = ctx.buffer.cur(0).as_glyph();
let index_this = self.coverage.get(this)?;
let entry_this = self.sets.entry(index_this)?;
let mut iter = skipping_iterator_t::new(ctx, ctx.buffer.idx, false);
let mut unsafe_from = 0;
if !iter.prev(Some(&mut unsafe_from)) {
ctx.buffer
.unsafe_to_concat_from_outbuffer(Some(unsafe_from), Some(ctx.buffer.idx + 1));
return None;
}
let i = iter.index();
let prev = ctx.buffer.info[i].as_glyph();
let index_prev = self.coverage.get(prev)?;
let Some(exit_prev) = self.sets.exit(index_prev) else {
ctx.buffer
.unsafe_to_concat_from_outbuffer(Some(iter.index()), Some(ctx.buffer.idx + 1));
return None;
};
let (exit_x, exit_y) = exit_prev.get(ctx.face);
let (entry_x, entry_y) = entry_this.get(ctx.face);
let direction = ctx.buffer.direction;
let j = ctx.buffer.idx;
ctx.buffer.unsafe_to_break(Some(i), Some(j + 1));
let pos = &mut ctx.buffer.pos;
match direction {
Direction::LeftToRight => {
pos[i].x_advance = exit_x + pos[i].x_offset;
let d = entry_x + pos[j].x_offset;
pos[j].x_advance -= d;
pos[j].x_offset -= d;
}
Direction::RightToLeft => {
let d = exit_x + pos[i].x_offset;
pos[i].x_advance -= d;
pos[i].x_offset -= d;
pos[j].x_advance = entry_x + pos[j].x_offset;
}
Direction::TopToBottom => {
pos[i].y_advance = exit_y + pos[i].y_offset;
let d = entry_y + pos[j].y_offset;
pos[j].y_advance -= d;
pos[j].y_offset -= d;
}
Direction::BottomToTop => {
let d = exit_y + pos[i].y_offset;
pos[i].y_advance -= d;
pos[i].y_offset -= d;
pos[j].y_advance = entry_y;
}
Direction::Invalid => {}
}
let mut child = i;
let mut parent = j;
let mut x_offset = entry_x - exit_x;
let mut y_offset = entry_y - exit_y;
if ctx.lookup_props as u16 & lookup_flags::RIGHT_TO_LEFT == 0 {
core::mem::swap(&mut child, &mut parent);
x_offset = -x_offset;
y_offset = -y_offset;
}
reverse_cursive_minor_offset(pos, child, direction, parent);
pos[child].set_attach_type(attach_type::CURSIVE);
pos[child].set_attach_chain((parent as isize - child as isize) as i16);
ctx.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
if direction.is_horizontal() {
pos[child].y_offset = y_offset;
} else {
pos[child].x_offset = x_offset;
}
if pos[parent].attach_chain() == -pos[child].attach_chain() {
pos[parent].set_attach_chain(0);
if direction.is_horizontal() {
pos[parent].y_offset = 0;
} else {
pos[parent].x_offset = 0;
}
}
ctx.buffer.idx += 1;
Some(())
}
}
fn reverse_cursive_minor_offset(
pos: &mut [GlyphPosition],
i: usize,
direction: Direction,
new_parent: usize,
) {
let chain = pos[i].attach_chain();
let attach_type = pos[i].attach_type();
if chain == 0 || attach_type & attach_type::CURSIVE == 0 {
return;
}
pos[i].set_attach_chain(0);
let j = (i as isize + isize::from(chain)) as _;
if j == new_parent {
return;
}
reverse_cursive_minor_offset(pos, j, direction, new_parent);
if direction.is_horizontal() {
pos[j].y_offset = -pos[i].y_offset;
} else {
pos[j].x_offset = -pos[i].x_offset;
}
pos[j].set_attach_chain(-chain);
pos[j].set_attach_type(attach_type);
}