use crate::hb::unicode::Codepoint;
use super::buffer::*;
use super::hb_font_t;
use super::ot_shape_plan::hb_ot_shape_plan_t;
use super::ot_shaper::{ComposeFn, DecomposeFn, MAX_COMBINING_MARKS};
use super::unicode::{hb_unicode_funcs_t, CharExt};
impl GlyphInfo {
declare_buffer_var!(
u32,
1,
0,
NORMALIZER_GLYPH_INDEX_VAR,
normalizer_glyph_index,
set_normalizer_glyph_index
);
}
pub struct hb_ot_shape_normalize_context_t<'a> {
pub plan: &'a hb_ot_shape_plan_t,
pub buffer: &'a mut hb_buffer_t,
pub face: &'a hb_font_t<'a>,
pub decompose: DecomposeFn,
pub compose: ComposeFn,
}
pub type hb_ot_shape_normalization_mode_t = i32;
pub const HB_OT_SHAPE_NORMALIZATION_MODE_NONE: i32 = 0;
pub const HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED: i32 = 1;
pub const HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS: i32 = 2;
pub const HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT: i32 = 3;
pub const HB_OT_SHAPE_NORMALIZATION_MODE_AUTO: i32 = 4;
#[allow(dead_code)]
pub const HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT: i32 = HB_OT_SHAPE_NORMALIZATION_MODE_AUTO;
fn decompose_unicode(
_: &hb_ot_shape_normalize_context_t,
ab: Codepoint,
) -> Option<(Codepoint, Codepoint)> {
super::unicode::decompose(ab)
}
fn compose_unicode(
_: &hb_ot_shape_normalize_context_t,
a: Codepoint,
b: Codepoint,
) -> Option<Codepoint> {
super::unicode::compose(a, b)
}
fn set_glyph(info: &mut GlyphInfo, font: &hb_font_t) {
if let Some(glyph_id) = font.get_nominal_glyph(info.glyph_id) {
info.set_normalizer_glyph_index(u32::from(glyph_id));
}
}
fn output_char(buffer: &mut hb_buffer_t, unichar: u32, glyph: u32) {
buffer.cur_mut(0).set_normalizer_glyph_index(glyph);
buffer.output_glyph(unichar);
let mut flags = buffer.scratch_flags;
buffer.prev_mut().init_unicode_props(&mut flags);
buffer.scratch_flags = flags;
}
fn next_char(buffer: &mut hb_buffer_t, glyph: u32) {
buffer.cur_mut(0).set_normalizer_glyph_index(glyph);
buffer.next_glyph();
}
fn skip_char(buffer: &mut hb_buffer_t) {
buffer.skip_glyph();
}
fn decompose(ctx: &mut hb_ot_shape_normalize_context_t, shortest: bool, ab: Codepoint) -> u32 {
let Some((a, b)) = (ctx.decompose)(ctx, ab) else {
return 0;
};
let a_glyph = ctx.face.get_nominal_glyph(a);
let b_glyph = if b != 0 {
match ctx.face.get_nominal_glyph(b) {
Some(glyph_id) => Some(glyph_id),
None => return 0,
}
} else {
None
};
if let Some(a_glyph) = a_glyph {
if shortest {
output_char(ctx.buffer, a, u32::from(a_glyph));
if let Some(b_glyph) = b_glyph {
output_char(ctx.buffer, b, u32::from(b_glyph));
return 2;
}
return 1;
}
}
let ret = decompose(ctx, shortest, a);
if ret != 0 {
if let Some(b_glyph) = b_glyph {
output_char(ctx.buffer, b, u32::from(b_glyph));
return ret + 1;
}
return ret;
}
if let Some(a_glyph) = a_glyph {
output_char(ctx.buffer, a, u32::from(a_glyph));
if let Some(b_glyph) = b_glyph {
output_char(ctx.buffer, b, u32::from(b_glyph));
return 2;
}
return 1;
}
0
}
fn decompose_current_character(ctx: &mut hb_ot_shape_normalize_context_t, shortest: bool) {
let u = ctx.buffer.cur(0).as_codepoint();
let glyph = ctx.face.get_nominal_glyph(u);
if let Some(glyph) = glyph {
if shortest {
next_char(ctx.buffer, u32::from(glyph));
return;
}
}
if decompose(ctx, shortest, u) > 0 {
skip_char(ctx.buffer);
return;
}
if let Some(glyph) = glyph {
next_char(ctx.buffer, u32::from(glyph));
return;
}
if ctx.buffer.cur(0).is_unicode_space() {
let space_type = u.space_fallback();
if space_type != hb_unicode_funcs_t::NOT_SPACE {
let space_glyph = ctx.face.get_nominal_glyph(0x0020).or(ctx.buffer.invisible);
if let Some(space_glyph) = space_glyph {
ctx.buffer
.cur_mut(0)
.set_unicode_space_fallback_type(space_type);
next_char(ctx.buffer, u32::from(space_glyph));
ctx.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK;
return;
}
}
}
if u == 0x2011 {
if let Some(other_glyph) = ctx.face.get_nominal_glyph(0x2010) {
next_char(ctx.buffer, u32::from(other_glyph));
return;
}
}
next_char(ctx.buffer, 0);
}
fn handle_variation_selector_cluster(
ctx: &mut hb_ot_shape_normalize_context_t,
end: usize,
_: bool,
) {
let face = ctx.face;
let buffer = &mut *ctx.buffer;
while buffer.idx < end - 1 && buffer.successful {
if buffer.cur(1).as_codepoint().is_variation_selector() {
if let Some(glyph_id) = face.get_nominal_variant_glyph(
buffer.cur(0).as_codepoint(),
buffer.cur(1).as_codepoint(),
) {
buffer
.cur_mut(0)
.set_normalizer_glyph_index(u32::from(glyph_id));
let unicode = buffer.cur(0).glyph_id;
buffer.replace_glyphs(2, 1, &[unicode]);
} else {
set_glyph(buffer.cur_mut(0), face);
buffer.next_glyph();
buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK;
buffer.cur_mut(0).set_variation_selector(true);
if buffer.not_found_variation_selector.is_some() {
buffer.cur_mut(0).clear_default_ignorable();
}
set_glyph(buffer.cur_mut(0), face);
buffer.next_glyph();
}
while buffer.idx < end && buffer.cur(0).as_codepoint().is_variation_selector() {
set_glyph(buffer.cur_mut(0), face);
buffer.next_glyph();
}
} else {
set_glyph(buffer.cur_mut(0), face);
buffer.next_glyph();
}
}
if ctx.buffer.idx < end {
set_glyph(ctx.buffer.cur_mut(0), face);
ctx.buffer.next_glyph();
}
}
fn decompose_multi_char_cluster(
ctx: &mut hb_ot_shape_normalize_context_t,
end: usize,
short_circuit: bool,
) {
let mut i = ctx.buffer.idx;
while i < end && ctx.buffer.successful {
if ctx.buffer.info[i].as_codepoint().is_variation_selector() {
handle_variation_selector_cluster(ctx, end, short_circuit);
return;
}
i += 1;
}
while ctx.buffer.idx < end && ctx.buffer.successful {
decompose_current_character(ctx, short_circuit);
}
}
fn compare_combining_class(pa: &GlyphInfo, pb: &GlyphInfo) -> bool {
let a = pa.modified_combining_class();
let b = pb.modified_combining_class();
a > b
}
pub fn _hb_ot_shape_normalize(
plan: &hb_ot_shape_plan_t,
buffer: &mut hb_buffer_t,
face: &hb_font_t,
) {
if buffer.is_empty() {
return;
}
buffer.assert_unicode_vars();
let mut mode = plan.shaper.normalization_preference;
if mode == HB_OT_SHAPE_NORMALIZATION_MODE_AUTO {
if plan.has_gpos_mark {
mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
} else {
mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
}
}
let mut ctx = hb_ot_shape_normalize_context_t {
plan,
buffer,
face,
decompose: plan.shaper.decompose.unwrap_or(decompose_unicode),
compose: plan.shaper.compose.unwrap_or(compose_unicode),
};
let always_short_circuit = mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE;
let might_short_circuit = always_short_circuit
|| (mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED
&& mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT);
let mut all_simple = true;
{
ctx.buffer.clear_output();
let count = ctx.buffer.len;
ctx.buffer.idx = 0;
loop {
let mut end = ctx.buffer.idx + 1;
while end < count && !ctx.buffer.info[end].is_unicode_mark() {
end += 1;
}
if end < count {
end -= 1;
}
if might_short_circuit {
let len = end - ctx.buffer.idx;
let mut done = 0;
while done < len {
let cur = ctx.buffer.cur_mut(done);
cur.set_normalizer_glyph_index(match face.get_nominal_glyph(cur.glyph_id) {
Some(glyph_id) => u32::from(glyph_id),
None => break,
});
done += 1;
}
ctx.buffer.next_glyphs(done);
}
while ctx.buffer.idx < end && ctx.buffer.successful {
decompose_current_character(&mut ctx, might_short_circuit);
}
if ctx.buffer.idx == count || !ctx.buffer.successful {
break;
}
all_simple = false;
end = ctx.buffer.idx + 1;
while end < count && ctx.buffer.info[end].is_unicode_mark() {
end += 1;
}
decompose_multi_char_cluster(&mut ctx, end, always_short_circuit);
if ctx.buffer.idx >= count || !ctx.buffer.successful {
break;
}
}
ctx.buffer.sync();
}
if !all_simple {
let count = ctx.buffer.len;
let mut i = 0;
while i < count {
if ctx.buffer.info[i].modified_combining_class() == 0 {
i += 1;
continue;
}
let mut end = i + 1;
while end < count && ctx.buffer.info[end].modified_combining_class() != 0 {
end += 1;
}
if end - i <= MAX_COMBINING_MARKS {
ctx.buffer.sort(i, end, compare_combining_class);
if let Some(reorder_marks) = ctx.plan.shaper.reorder_marks {
reorder_marks(ctx.plan, ctx.buffer, i, end);
}
}
i = end + 1;
}
}
if ctx.buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CGJ != 0 {
for i in 1..ctx.buffer.len.saturating_sub(1) {
if ctx.buffer.info[i].glyph_id == 0x034F
{
let last = ctx.buffer.info[i - 1].modified_combining_class();
let next = ctx.buffer.info[i + 1].modified_combining_class();
if next == 0 || last <= next {
ctx.buffer.info[i].unhide();
}
}
}
}
if !all_simple
&& ctx.buffer.successful
&& (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS
|| mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT)
{
let count = ctx.buffer.len;
let mut starter = 0;
ctx.buffer.clear_output();
ctx.buffer.next_glyph();
while ctx.buffer.idx < count && ctx.buffer.successful {
let cur = ctx.buffer.cur(0);
if cur.is_unicode_mark() &&
(starter == ctx.buffer.out_len - 1
|| ctx.buffer.prev().modified_combining_class() < cur.modified_combining_class())
{
let a = ctx.buffer.out_info()[starter].as_codepoint();
let b = cur.as_codepoint();
if let Some(composed) = (ctx.compose)(&ctx, a, b) {
if let Some(glyph_id) = face.get_nominal_glyph(composed) {
ctx.buffer.next_glyph();
if !ctx.buffer.successful {
return;
}
ctx.buffer.merge_out_clusters(starter, ctx.buffer.out_len);
ctx.buffer.out_len -= 1;
let mut flags = ctx.buffer.scratch_flags;
let info = &mut ctx.buffer.out_info_mut()[starter];
info.glyph_id = composed;
info.set_normalizer_glyph_index(u32::from(glyph_id));
info.init_unicode_props(&mut flags);
ctx.buffer.scratch_flags = flags;
continue;
}
}
}
ctx.buffer.next_glyph();
if ctx.buffer.prev().modified_combining_class() == 0 {
starter = ctx.buffer.out_len - 1;
}
}
ctx.buffer.sync();
}
}