use crate::hb::buffer::{hb_buffer_t, 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_gsubgpos::OT::hb_ot_apply_context_t;
use crate::hb::ot_layout_gsubgpos::{match_t, skipping_iterator_t, Apply, MatchSource};
use read_fonts::tables::gpos::{
AnchorTable, MarkArray, MarkBasePosFormat1, MarkLigPosFormat1, MarkMarkPosFormat1,
};
trait MarkArrayExt {
fn apply(
&self,
ctx: &mut hb_ot_apply_context_t,
base_anchor: &AnchorTable,
mark_anchor: &AnchorTable,
glyph_pos: usize,
) -> Option<()>;
}
impl MarkArrayExt for MarkArray<'_> {
fn apply(
&self,
ctx: &mut hb_ot_apply_context_t,
base_anchor: &AnchorTable,
mark_anchor: &AnchorTable,
glyph_pos: usize,
) -> Option<()> {
let (base_x, base_y) = ctx.face.ot_tables.resolve_anchor(base_anchor);
let (mark_x, mark_y) = ctx.face.ot_tables.resolve_anchor(mark_anchor);
ctx.buffer
.unsafe_to_break(Some(glyph_pos), Some(ctx.buffer.idx + 1));
let idx = ctx.buffer.idx;
let pos = ctx.buffer.cur_pos_mut();
pos.x_offset = base_x - mark_x;
pos.y_offset = base_y - mark_y;
pos.set_attach_type(attach_type::MARK);
pos.set_attach_chain((glyph_pos as isize - idx as isize) as i16);
ctx.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
ctx.buffer.idx += 1;
Some(())
}
}
impl Apply for MarkBasePosFormat1<'_> {
fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
let mark_glyph = ctx.buffer.cur(0).as_glyph();
let mark_index = self.mark_coverage().ok()?.get(mark_glyph)?;
let base_coverage = self.base_coverage().ok()?;
let last_base_until = ctx.last_base_until;
let mut last_base = ctx.last_base;
if ctx.last_base_until > ctx.buffer.idx as u32 {
ctx.last_base_until = 0;
ctx.last_base = -1;
}
let mut iter = skipping_iterator_t::new(ctx, false);
iter.set_lookup_props(u32::from(lookup_flags::IGNORE_MARKS));
let mut j = iter.buffer.idx;
while j > last_base_until as usize {
let mut _match = iter.match_at(j - 1, MatchSource::Info);
if _match == match_t::MATCH {
if !accept(iter.buffer, j - 1)
&& base_coverage
.get(iter.buffer.info[j - 1].as_glyph())
.is_none()
{
_match = match_t::SKIP;
}
}
if _match == match_t::MATCH {
last_base = j as i32 - 1;
break;
}
j -= 1;
}
ctx.last_base_until = ctx.buffer.idx as u32;
ctx.last_base = last_base;
if ctx.last_base == -1 {
ctx.buffer
.unsafe_to_concat_from_outbuffer(Some(0), Some(ctx.buffer.idx + 1));
return None;
}
let idx = ctx.last_base as u32;
let info = &ctx.buffer.info;
let base_glyph = info[idx as usize].as_glyph();
let Some(base_index) = self.base_coverage().ok()?.get(base_glyph) else {
ctx.buffer
.unsafe_to_concat_from_outbuffer(Some(idx as usize), Some(ctx.buffer.idx + 1));
return None;
};
let mark_array = self.mark_array().ok()?;
let mark_record = mark_array.mark_records().get(mark_index as usize)?;
let mark_anchor = mark_record.mark_anchor(mark_array.offset_data()).ok()?;
let base_array = self.base_array().ok()?;
let base_record = base_array.base_records().get(base_index as usize).ok()?;
let base_anchor = base_record
.base_anchors(base_array.offset_data())
.get(mark_record.mark_class() as usize)?
.ok()?;
mark_array.apply(ctx, &base_anchor, &mark_anchor, idx as usize)
}
}
fn accept(buffer: &hb_buffer_t, idx: usize) -> bool {
!buffer.info[idx].multiplied()
|| 0 == buffer.info[idx].lig_comp()
|| (idx == 0
|| buffer.info[idx - 1].is_mark()
|| !buffer.info[idx - 1].multiplied()
|| buffer.info[idx].lig_id() != buffer.info[idx - 1].lig_id()
|| buffer.info[idx].lig_comp() != buffer.info[idx - 1].lig_comp() + 1)
}
impl Apply for MarkMarkPosFormat1<'_> {
fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
let mark1_glyph = ctx.buffer.cur(0).as_glyph();
let mark1_index = self.mark1_coverage().ok()?.get(mark1_glyph)?;
let lookup_props = ctx.lookup_props;
let mut iter = skipping_iterator_t::new(ctx, false);
iter.reset_fast(iter.buffer.idx);
iter.set_lookup_props(lookup_props & !u32::from(lookup_flags::IGNORE_FLAGS));
let mut unsafe_from = 0;
if !iter.prev(Some(&mut unsafe_from)) {
iter.buffer
.unsafe_to_concat_from_outbuffer(Some(unsafe_from), Some(iter.buffer.idx + 1));
return None;
}
let iter_idx = iter.index();
if !ctx.buffer.info[iter_idx].is_mark() {
ctx.buffer
.unsafe_to_concat_from_outbuffer(Some(iter_idx), Some(ctx.buffer.idx + 1));
return None;
}
let id1 = ctx.buffer.cur(0).lig_id();
let id2 = ctx.buffer.info[iter_idx].lig_id();
let comp1 = ctx.buffer.cur(0).lig_comp();
let comp2 = ctx.buffer.info[iter_idx].lig_comp();
let matches = if id1 == id2 {
id1 == 0 || comp1 == comp2
} else {
(id1 > 0 && comp1 == 0) || (id2 > 0 && comp2 == 0)
};
if !matches {
ctx.buffer
.unsafe_to_concat_from_outbuffer(Some(iter_idx), Some(ctx.buffer.idx + 1));
return None;
}
let mark2_glyph = ctx.buffer.info[iter_idx].as_glyph();
let mark2_index = self.mark2_coverage().ok()?.get(mark2_glyph)?;
let mark1_array = self.mark1_array().ok()?;
let mark1_record = mark1_array.mark_records().get(mark1_index as usize)?;
let mark1_anchor = mark1_record.mark_anchor(mark1_array.offset_data()).ok()?;
let base_array = self.mark2_array().ok()?;
let base_record = base_array.mark2_records().get(mark2_index as usize).ok()?;
let base_anchor = base_record
.mark2_anchors(base_array.offset_data())
.get(mark1_record.mark_class() as usize)?
.ok()?;
mark1_array.apply(ctx, &base_anchor, &mark1_anchor, iter_idx)
}
}
impl Apply for MarkLigPosFormat1<'_> {
fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
let mark_glyph = ctx.buffer.cur(0).as_glyph();
let mark_index = self.mark_coverage().ok()?.get(mark_glyph)? as usize;
if ctx.last_base_until > ctx.buffer.idx as u32 {
ctx.last_base_until = 0;
ctx.last_base = -1;
}
let last_base_until = ctx.last_base_until;
let mut last_base = ctx.last_base;
let mut iter = skipping_iterator_t::new(ctx, false);
iter.set_lookup_props(u32::from(lookup_flags::IGNORE_MARKS));
let mut j = iter.buffer.idx;
while j > last_base_until as usize {
let mut _match = iter.match_at(j - 1, MatchSource::Info);
if _match == match_t::MATCH {
last_base = j as i32 - 1;
break;
}
j -= 1;
}
ctx.last_base_until = ctx.buffer.idx as u32;
ctx.last_base = last_base;
if ctx.last_base == -1 {
ctx.buffer
.unsafe_to_concat_from_outbuffer(Some(0), Some(ctx.buffer.idx + 1));
return None;
}
let idx = ctx.last_base as usize;
let lig_glyph = ctx.buffer.info[idx].as_glyph();
let Some(lig_index) = self.ligature_coverage().ok()?.get(lig_glyph) else {
ctx.buffer
.unsafe_to_concat_from_outbuffer(Some(idx), Some(ctx.buffer.idx + 1));
return None;
};
let lig_attach = self
.ligature_array()
.ok()?
.ligature_attaches()
.get(lig_index as usize)
.ok()?;
let comp_count = lig_attach.component_count();
if comp_count == 0 {
ctx.buffer
.unsafe_to_concat_from_outbuffer(Some(idx), Some(ctx.buffer.idx + 1));
return None;
}
let lig_id = ctx.buffer.info[idx].lig_id();
let mark_id = ctx.buffer.cur(0).lig_id();
let mark_comp = u16::from(ctx.buffer.cur(0).lig_comp());
let matches = lig_id != 0 && lig_id == mark_id && mark_comp > 0;
let comp_index = if matches {
mark_comp.min(comp_count)
} else {
comp_count
} - 1;
let mark_array = self.mark_array().ok()?;
let mark_record = mark_array.mark_records().get(mark_index)?;
let mark_anchor = mark_record.mark_anchor(mark_array.offset_data()).ok()?;
let base_record = lig_attach
.component_records()
.get(comp_index as usize)
.ok()?;
let base_anchor = base_record
.ligature_anchors(lig_attach.offset_data())
.get(mark_record.mark_class() as usize)?
.ok()?;
mark_array.apply(ctx, &base_anchor, &mark_anchor, idx)
}
}