diff --git a/examples/shape.rs b/examples/shape.rs index 000e571e..4fd6d277 100644 --- a/examples/shape.rs +++ b/examples/shape.rs @@ -35,6 +35,7 @@ OPTIONS: --show-flags Output glyph flags --single-par Treat the input string as a single paragraph --ned No Extra Data; Do not output clusters or advances + -V, --trace Output interim shaping results ARGS: A font file @@ -67,6 +68,7 @@ struct Args { single_par: bool, ned: bool, free: Vec, + trace: bool, } fn parse_args() -> Result { @@ -105,6 +107,7 @@ fn parse_args() -> Result { show_flags: args.contains("--show-flags"), single_par: args.contains("--single-par"), ned: args.contains("--ned"), + trace: args.contains(["-V", "--trace"]), free: args .finish() .iter() @@ -185,6 +188,13 @@ fn main() { let mut buffer = harfrust::UnicodeBuffer::new(); buffer.push_str(text); + if args.trace { + buffer.set_message_function(Box::new(|_buf, _font, msg| { + println!("trace: {msg}"); + true + })); + } + if let Some(d) = args.direction { buffer.set_direction(d); } diff --git a/src/hb/aat/layout.rs b/src/hb/aat/layout.rs index eacdb8d3..c92b8820 100644 --- a/src/hb/aat/layout.rs +++ b/src/hb/aat/layout.rs @@ -515,6 +515,7 @@ pub fn substitute( } let mut c = AatApplyContext::new(plan, face, buffer); + message_return!(c, "start table morx"); layout_morx_table::apply( &mut c, if features.is_empty() { @@ -523,6 +524,7 @@ pub fn substitute( &aat_map }, ); + message!(c, "end table morx"); } fn is_deleted_glyph(info: &GlyphInfo) -> bool { @@ -545,7 +547,9 @@ pub fn remove_deleted_glyphs(buffer: &mut hb_buffer_t) { #[doc(alias = "hb_aat_layout_position")] pub fn position(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) { let mut c = AatApplyContext::new(plan, face, buffer); + message_return!(c, "start table kerx"); layout_kerx_table::apply(&mut c); + message!(c, "end table kerx"); } /// HB: hb_aat_layout_track diff --git a/src/hb/aat/layout_kerx_table.rs b/src/hb/aat/layout_kerx_table.rs index 07e59fb8..2f46c437 100644 --- a/src/hb/aat/layout_kerx_table.rs +++ b/src/hb/aat/layout_kerx_table.rs @@ -60,11 +60,18 @@ pub(crate) fn apply(c: &mut AatApplyContext) -> Option<()> { c.start_end_safe_to_break = subtable_cache.start_end_safe_to_break; if !c.buffer_intersects_machine() { + message!( + c, + "skipping kerning subtable {} because no glyph matches", + subtable_idx + ); continue; } let reverse = c.buffer.direction.is_backward(); + message_continue!(c, "start subtable {}", subtable_idx); + if !seen_cross_stream && subtable.is_cross_stream() { seen_cross_stream = true; @@ -138,6 +145,8 @@ pub(crate) fn apply(c: &mut AatApplyContext) -> Option<()> { } } } + + message!(c, "end subtable {}", subtable_idx); if c.buffer_is_reversed { c.reverse_buffer(); } diff --git a/src/hb/aat/layout_morx_table.rs b/src/hb/aat/layout_morx_table.rs index 49119972..f0908b94 100644 --- a/src/hb/aat/layout_morx_table.rs +++ b/src/hb/aat/layout_morx_table.rs @@ -123,6 +123,12 @@ pub fn apply<'a>(c: &mut AatApplyContext<'a>, map: &'a AatMap) -> Option<()> { c.start_end_safe_to_break = subtable_cache.start_end_safe_to_break; if !c.buffer_intersects_machine() { + message!( + c, + "skipped chainsubtable {} because no glyph matches", + subtable_idx + ); + continue; } @@ -159,6 +165,8 @@ pub fn apply<'a>(c: &mut AatApplyContext<'a>, map: &'a AatMap) -> Option<()> { subtable.is_backwards() != c.buffer.direction.is_backward() }; + message_continue!(c, "start chainsubtable {}", subtable_idx); + if reverse != c.buffer_is_reversed { c.reverse_buffer(); } @@ -166,6 +174,7 @@ pub fn apply<'a>(c: &mut AatApplyContext<'a>, map: &'a AatMap) -> Option<()> { if let Ok(kind) = subtable.kind() { apply_subtable(kind, c); } + message!(c, "end chainsubtable {}", subtable_idx); } if c.buffer_is_reversed { c.reverse_buffer(); diff --git a/src/hb/buffer.rs b/src/hb/buffer.rs index ed0dbc22..63e41b97 100644 --- a/src/hb/buffer.rs +++ b/src/hb/buffer.rs @@ -396,6 +396,7 @@ pub const HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: u32 = 2; pub const HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES: u32 = 3; pub const HB_BUFFER_CLUSTER_LEVEL_DEFAULT: u32 = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES; +pub type MessageFunction<'a> = Box bool + 'a>; pub struct hb_buffer_t { // Information about how the text in the buffer should be treated. pub flags: BufferFlags, @@ -440,6 +441,17 @@ pub struct hb_buffer_t { pub max_len: usize, /// Maximum allowed operations. pub max_ops: i32, + + pub(crate) message_depth: usize, + + /// Callback used to receive a message + /// + /// The function gets called with the `Buffer`, the `Font` the + /// buffer is shaped with, and a message describing what step of the shaping + /// process will be performed. + /// The function should return `true` to perform the shaping step, or `false` + /// to skip it and move to the next one. + pub message_function: Option>, } impl hb_buffer_t { @@ -481,6 +493,8 @@ impl hb_buffer_t { context_len: [0, 0], digest: hb_set_digest_t::new(), glyph_set: U32Set::default(), + message_depth: 0, + message_function: None, } } @@ -785,6 +799,25 @@ impl hb_buffer_t { true } + #[cfg(feature = "std")] + pub(crate) fn sync_so_far(&mut self) -> usize { + let had_output = self.have_output; + let out_i = self.out_len; + let i = self.idx; + let old_idx = self.idx; + if self.sync() { + self.idx = out_i; + } else { + self.idx = i; + } + if had_output { + self.have_output = true; + self.out_len = self.idx; + } + debug_assert!(self.idx <= self.len); + self.idx - old_idx + } + pub fn clear_output(&mut self) { self.have_output = true; self.have_positions = false; @@ -1604,6 +1637,78 @@ impl hb_buffer_t { lig_id } + + #[inline] + pub fn set_message_function(&mut self, func: MessageFunction<'static>) { + #[cfg(feature = "std")] + { + self.message_function = Some(func); + } + } + + #[inline] + pub(crate) fn message(&mut self, font: &hb_font_t, msg: &str) -> bool { + if let Some(mut func) = self.message_function.take() { + self.message_depth += 1; + let ret = func(self, font, msg); + self.message_depth -= 1; + self.message_function = Some(func); + ret + } else { + true + } + } + + #[cfg(feature = "std")] + #[inline] + pub(crate) fn messaging(&self) -> bool { + self.message_function.is_some() + } +} + +macro_rules! message { + ($ctx:expr, $($arg:tt)*) => { + #[cfg(feature = "std")] + if $ctx.buffer.messaging() { + let msg = format!($($arg)*); + $ctx.buffer.message($ctx.face, &msg); + } + }; +} + +macro_rules! message_sync { + ($ctx:expr, $($arg:tt)*) => { + #[cfg(feature = "std")] + if $ctx.buffer.messaging() { + $ctx.buffer.sync_so_far(); + let msg = format!($($arg)*); + $ctx.buffer.message($ctx.face, &msg); + } + }; +} + +macro_rules! message_return { + ($ctx:expr, $($arg:tt)*) => { + #[cfg(feature = "std")] + if $ctx.buffer.messaging() { + let msg = format!($($arg)*); + if !$ctx.buffer.message($ctx.face, &msg) { + return; + } + } + }; +} + +macro_rules! message_continue { + ($ctx:expr, $($arg:tt)*) => { + #[cfg(feature = "std")] + if $ctx.buffer.messaging() { + let msg = format!($($arg)*); + if !$ctx.buffer.message($ctx.face, &msg) { + continue; + } + } + }; } pub(crate) fn _cluster_group_func(a: &GlyphInfo, b: &GlyphInfo) -> bool { @@ -1852,6 +1957,13 @@ impl UnicodeBuffer { pub fn clear(&mut self) { self.0.clear(); } + + /// Sets a function to be called when HarfBuzz wants to emit a message. + #[cfg(feature = "std")] + #[inline] + pub fn set_message_function(&mut self, func: MessageFunction<'static>) { + self.0.set_message_function(func); + } } impl core::fmt::Debug for UnicodeBuffer { @@ -1900,6 +2012,9 @@ impl GlyphBuffer { /// Get the glyph positions. #[inline] pub fn glyph_positions(&self) -> &[GlyphPosition] { + if self.0.message_depth > 0 { + return &[]; + } &self.0.pos[0..self.0.len] } diff --git a/src/hb/kerning.rs b/src/hb/kerning.rs index 8579c04c..f2b5c392 100644 --- a/src/hb/kerning.rs +++ b/src/hb/kerning.rs @@ -37,6 +37,11 @@ pub fn hb_ot_layout_kern( ) -> Option<()> { let mut c = AatApplyContext::new(plan, face, buffer); + #[cfg(feature = "std")] + if !c.buffer.message(face, "start table kern") { + return None; + } + let (kern, subtable_caches) = c.face.aat_tables.kern.as_ref()?; let mut subtable_idx = 0; @@ -115,6 +120,8 @@ pub fn hb_ot_layout_kern( if c.buffer_is_reversed { c.reverse_buffer(); } + #[cfg(feature = "std")] + c.buffer.message(face, "end table kern"); Some(()) } diff --git a/src/hb/ot/gpos/cursive.rs b/src/hb/ot/gpos/cursive.rs index e408ce1b..e5f6ec84 100644 --- a/src/hb/ot/gpos/cursive.rs +++ b/src/hb/ot/gpos/cursive.rs @@ -45,6 +45,8 @@ impl Apply for CursivePosFormat1<'_> { let j = ctx.buffer.idx; ctx.buffer.unsafe_to_break(Some(i), Some(j + 1)); + message!(ctx, "cursive attaching glyph at {} to glyph at {}", i, j); + let pos = &mut ctx.buffer.pos; match direction { Direction::LeftToRight => { @@ -122,6 +124,8 @@ impl Apply for CursivePosFormat1<'_> { } } + message!(ctx, "cursive attached glyph at {} to glyph at {}", i, j); + ctx.buffer.idx += 1; Some(()) } diff --git a/src/hb/ot/gpos/mark.rs b/src/hb/ot/gpos/mark.rs index 7cd531a6..8b01073f 100644 --- a/src/hb/ot/gpos/mark.rs +++ b/src/hb/ot/gpos/mark.rs @@ -35,6 +35,14 @@ impl MarkArrayExt for MarkArray<'_> { .unsafe_to_break(Some(glyph_pos), Some(ctx.buffer.idx + 1)); let idx = ctx.buffer.idx; + + message!( + ctx, + "attaching mark glyph at {} to glyph at {}", + idx, + glyph_pos + ); + let pos = ctx.buffer.cur_pos_mut(); pos.x_offset = base_x - mark_x; pos.y_offset = base_y - mark_y; @@ -42,6 +50,14 @@ impl MarkArrayExt for MarkArray<'_> { pos.set_attach_chain((glyph_pos as isize - idx as isize) as i16); ctx.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + + message!( + ctx, + "attached mark glyph at {} to glyph at {}", + idx, + glyph_pos + ); + ctx.buffer.idx += 1; Some(()) diff --git a/src/hb/ot/gpos/pair.rs b/src/hb/ot/gpos/pair.rs index 4f5ad18c..f23faeb7 100644 --- a/src/hb/ot/gpos/pair.rs +++ b/src/hb/ot/gpos/pair.rs @@ -42,6 +42,13 @@ impl Apply for PairPosFormat1<'_> { let second_glyph = iter.buffer.info[second_glyph_index].as_glyph(); let finish = |ctx: &mut hb_ot_apply_context_t, iter_index: &mut usize, has_record2| { + message!( + ctx, + "kerned glyphs at {} and {}", + ctx.buffer.idx, + second_glyph_index + ); + if has_record2 { *iter_index += 1; // https://github.com/harfbuzz/harfbuzz/issues/3824 @@ -63,13 +70,20 @@ impl Apply for PairPosFormat1<'_> { let success = |ctx: &mut hb_ot_apply_context_t, iter_index: &mut usize, flag1, flag2, has_record2| { - if flag1 || flag2 { + let ret = if flag1 || flag2 { ctx.buffer .unsafe_to_break(Some(ctx.buffer.idx), Some(second_glyph_index + 1)); finish(ctx, iter_index, has_record2) } else { boring(ctx, iter_index, has_record2) - } + }; + message!( + ctx, + "tried kerning glyphs at {} and {}", + ctx.buffer.idx, + second_glyph_index + ); + ret }; let mut buf_idx = iter.buf_idx; @@ -115,6 +129,12 @@ impl Apply for PairPosFormat1<'_> { record_offset + format1_len + 2, format2, ) == Some(true); + + message!( + ctx, + "try kerning glyphs at {} and {second_glyph_index}", + ctx.buffer.idx + ); return success(ctx, &mut buf_idx, worked1, worked2, has_record2); } } @@ -174,6 +194,11 @@ impl Apply for PairPosFormat2<'_> { let second_glyph = iter.buffer.info[second_glyph_index].as_glyph(); let finish = |ctx: &mut hb_ot_apply_context_t, iter_index: &mut usize, has_record2| { + message!( + ctx, + "kerned glyphs at {} and {second_glyph_index}", + ctx.buffer.idx + ); if has_record2 { *iter_index += 1; // https://github.com/harfbuzz/harfbuzz/issues/3824 @@ -195,13 +220,20 @@ impl Apply for PairPosFormat2<'_> { let success = |ctx: &mut hb_ot_apply_context_t, iter_index: &mut usize, flag1, flag2, has_record2| { - if flag1 || flag2 { + let ret = if flag1 || flag2 { ctx.buffer .unsafe_to_break(Some(ctx.buffer.idx), Some(second_glyph_index + 1)); finish(ctx, iter_index, has_record2) } else { boring(ctx, iter_index, has_record2) - } + }; + message!( + ctx, + "tried kerning glyphs at {} and {}", + ctx.buffer.idx - 1, + second_glyph_index + ); + ret }; let data = self.offset_data(); let (class1, class2) = match external_cache { @@ -246,6 +278,12 @@ impl Apply for PairPosFormat2<'_> { record_offset + format1_len, format2, ) == Some(true); + message!( + ctx, + "try kerning glyphs at {} and {}", + ctx.buffer.idx, + second_glyph_index + ); success(ctx, &mut buf_idx, worked1, worked2, has_record2) } diff --git a/src/hb/ot/gpos/single.rs b/src/hb/ot/gpos/single.rs index a5b3ffb3..f8b95119 100644 --- a/src/hb/ot/gpos/single.rs +++ b/src/hb/ot/gpos/single.rs @@ -8,7 +8,9 @@ impl Apply for SinglePosFormat1<'_> { self.coverage().ok()?.get(glyph)?; let format = self.value_format(); let offset = self.shape().value_record_byte_range().start; + message!(ctx, "positioning glyph at {}", ctx.buffer.idx); super::apply_value(ctx, ctx.buffer.idx, &self.offset_data(), offset, format); + message!(ctx, "positioned glyph at {}", ctx.buffer.idx); ctx.buffer.idx += 1; Some(()) } @@ -21,7 +23,9 @@ impl Apply for SinglePosFormat2<'_> { let format = self.value_format(); let offset = self.shape().value_records_byte_range().start + (format.record_byte_len() * index); + message!(ctx, "positioning glyph at {}", ctx.buffer.idx); super::apply_value(ctx, ctx.buffer.idx, &self.offset_data(), offset, format); + message!(ctx, "positioned glyph at {}", ctx.buffer.idx); ctx.buffer.idx += 1; Some(()) } diff --git a/src/hb/ot/gsub/alternate.rs b/src/hb/ot/gsub/alternate.rs index d093d92a..21a6c174 100644 --- a/src/hb/ot/gsub/alternate.rs +++ b/src/hb/ot/gsub/alternate.rs @@ -25,7 +25,18 @@ impl Apply for AlternateSet<'_> { } let idx = u16::try_from(alt_index).ok()?.checked_sub(1)?; + + message_sync!( + ctx, + "replacing glyph at {} (alternate substitution)", + ctx.buffer.idx, + ); ctx.replace_glyph(alternates.get(idx as usize)?.get().into()); + message!( + ctx, + "replaced glyph at {} (alternate substitution)", + ctx.buffer.idx - 1, + ); Some(()) } diff --git a/src/hb/ot/gsub/ligature.rs b/src/hb/ot/gsub/ligature.rs index 820c8277..11730e1f 100644 --- a/src/hb/ot/gsub/ligature.rs +++ b/src/hb/ot/gsub/ligature.rs @@ -29,7 +29,17 @@ impl Apply for Ligature<'_> { // as a "ligated" substitution. let components = self.component_glyph_ids(); if components.is_empty() { + message_sync!( + ctx, + "replacing glyph at {} (ligature substitution)", + ctx.buffer.idx + ); ctx.replace_glyph(self.ligature_glyph().into()); + message!( + ctx, + "replaced glyph at {} (ligature substitution)", + ctx.buffer.idx - 1, + ); Some(()) } else { let f = |info: &mut GlyphInfo, index| { @@ -52,6 +62,24 @@ impl Apply for Ligature<'_> { return None; } let count = components.len() + 1; + #[cfg(feature = "std")] + let mut pos = 0; + #[cfg(feature = "std")] + if ctx.buffer.messaging() { + let delta = ctx.buffer.sync_so_far(); + pos = ctx.buffer.idx; + let count = components.len(); + match_end += delta; + let mut msg = "Ligating glyphs at ".to_string(); + for i in 0..=count { + ctx.match_positions[i] += delta as u32; + if i > 0 { + msg.push(','); + } + msg.push_str(ctx.match_positions[i].to_string().as_str()); + } + ctx.buffer.message(ctx.face, &msg); + } ligate_input( ctx, count, @@ -59,6 +87,7 @@ impl Apply for Ligature<'_> { total_component_count, self.ligature_glyph().into(), ); + message!(ctx, "ligated glyph at {}", pos); Some(()) } } diff --git a/src/hb/ot/gsub/multiple.rs b/src/hb/ot/gsub/multiple.rs index 3df37932..6bcc5c19 100644 --- a/src/hb/ot/gsub/multiple.rs +++ b/src/hb/ot/gsub/multiple.rs @@ -20,13 +20,38 @@ impl Apply for MultipleSubstFormat1<'_> { match substs.len() { // Spec disallows this, but Uniscribe allows it. // https://github.com/harfbuzz/harfbuzz/issues/253 - 0 => ctx.buffer.delete_glyph(), + 0 => { + message_sync!( + ctx, + "deleting glyph at {} (multiple substitution)", + ctx.buffer.idx + ); + ctx.buffer.delete_glyph(); + message!( + ctx, + "deleted glyph at {} (multiple substitution)", + ctx.buffer.idx, + ); + } // Special-case to make it in-place and not consider this // as a "multiplied" substitution. - 1 => ctx.replace_glyph(substs.first()?.get().into()), + 1 => { + message_sync!( + ctx, + "replacing glyph at {} (multiple substitution)", + ctx.buffer.idx + ); + ctx.replace_glyph(substs.first()?.get().into()); + message!( + ctx, + "replaced glyph at {} (multiple substitution)", + ctx.buffer.idx - 1, + ); + } _ => { + message_sync!(ctx, "multiplying glyph at {}", ctx.buffer.idx); let class = if ctx.buffer.cur(0).is_ligature() { GlyphPropsFlags::BASE_GLYPH } else { @@ -46,6 +71,20 @@ impl Apply for MultipleSubstFormat1<'_> { } ctx.buffer.skip_glyph(); + + #[cfg(feature = "std")] + if ctx.buffer.messaging() { + ctx.buffer.sync_so_far(); + let count = substs.len(); + let mut msg = "Multiplied glyphs at ".to_string(); + for i in (ctx.buffer.idx - count)..=ctx.buffer.idx { + if i > (ctx.buffer.idx - count) { + msg.push(','); + } + msg.push_str(i.to_string().as_str()); + } + ctx.buffer.message(ctx.face, &msg); + } } } Some(()) diff --git a/src/hb/ot/gsub/reverse_chain.rs b/src/hb/ot/gsub/reverse_chain.rs index ed990764..667a9c94 100644 --- a/src/hb/ot/gsub/reverse_chain.rs +++ b/src/hb/ot/gsub/reverse_chain.rs @@ -64,10 +64,19 @@ impl Apply for ReverseChainSingleSubstFormat1<'_> { ctx.buffer.idx + 1, &mut end_index, ) { + message_sync!( + ctx, + "replacing glyph at {} (reverse chaining substitution)", + ctx.buffer.idx + ); ctx.buffer .unsafe_to_break_from_outbuffer(Some(start_index), Some(end_index)); ctx.replace_glyph_inplace(subst.into()); - + message!( + ctx, + "replaced glyph at {} (reverse chaining substitution)", + ctx.buffer.idx + ); // Note: We DON'T decrease buffer.idx. The main loop does it // for us. This is useful for preventing surprises if someone // calls us through a Context lookup. diff --git a/src/hb/ot/gsub/single.rs b/src/hb/ot/gsub/single.rs index 10b2fa77..27b52666 100644 --- a/src/hb/ot/gsub/single.rs +++ b/src/hb/ot/gsub/single.rs @@ -14,7 +14,17 @@ impl Apply for SingleSubstFormat1<'_> { let glyph = ctx.buffer.cur(0).as_glyph(); self.coverage().ok()?.get(glyph)?; let subst = (glyph.to_u32() as i32 + self.delta_glyph_id() as i32) as u16; + message_sync!( + ctx, + "replacing glyph at {} (single substitution)", + ctx.buffer.idx + ); ctx.replace_glyph(subst.into()); + message!( + ctx, + "replaced glyph at {} (single substitution)", + ctx.buffer.idx - 1 + ); Some(()) } } @@ -33,7 +43,17 @@ impl Apply for SingleSubstFormat2<'_> { let glyph = ctx.buffer.cur(0).as_glyph(); let index = self.coverage().ok()?.get(glyph)? as usize; let subst = self.substitute_glyph_ids().get(index)?.get().to_u16(); + message_sync!( + ctx, + "replacing glyph at {} (single substitution)", + ctx.buffer.idx + ); ctx.replace_glyph(subst.into()); + message!( + ctx, + "replaced glyph at {} (single substitution)", + ctx.buffer.idx - 1 + ); Some(()) } } diff --git a/src/hb/ot_layout.rs b/src/hb/ot_layout.rs index 043354cb..4fb3ab91 100644 --- a/src/hb/ot_layout.rs +++ b/src/hb/ot_layout.rs @@ -159,6 +159,14 @@ pub fn apply_layout_table( let Some(lookup) = table.get_lookup(lookup_map.index) else { continue; }; + // XXX no feature tag in lookup map yet + #[cfg(feature = "std")] + if !ctx + .buffer + .message(ctx.face, &format!("start lookup {}", lookup_map.index)) + { + continue; + } if lookup.digest().may_intersect(&ctx.buffer.digest) { ctx.lookup_index = lookup_map.index; @@ -169,8 +177,21 @@ pub fn apply_layout_table( ctx.random = lookup_map.random; ctx.per_syllable = lookup_map.per_syllable; - apply_string::(&mut ctx, lookup); + if !apply_string::(&mut ctx, lookup) { + #[cfg(feature = "std")] + ctx.buffer.message( + ctx.face, + &format!( + "skipped lookup {} because no glyph matches", + lookup_map.index, + ), + ); + } } + + #[cfg(feature = "std")] + ctx.buffer + .message(ctx.face, &format!("end lookup {}", lookup_map.index)); } } @@ -182,11 +203,13 @@ pub fn apply_layout_table( } } -fn apply_string(ctx: &mut OT::hb_ot_apply_context_t, lookup: &LookupInfo) { +fn apply_string(ctx: &mut OT::hb_ot_apply_context_t, lookup: &LookupInfo) -> bool { if ctx.buffer.is_empty() || ctx.lookup_mask() == 0 { - return; + return false; } + let ret; + let subtable_count = lookup.subtables.len(); let lookup_props = lookup.props(); @@ -213,7 +236,7 @@ fn apply_string(ctx: &mut OT::hb_ot_apply_context_t, lookup: &Lo ctx.buffer.clear_output(); } ctx.buffer.idx = 0; - apply_forward(ctx, lookup); + ret = apply_forward(ctx, lookup); if !T::IN_PLACE { ctx.buffer.sync(); @@ -223,8 +246,9 @@ fn apply_string(ctx: &mut OT::hb_ot_apply_context_t, lookup: &Lo debug_assert!(!ctx.buffer.have_output); ctx.buffer.idx = ctx.buffer.len - 1; - apply_backward(ctx, lookup); + ret = apply_backward(ctx, lookup); } + ret } fn apply_forward(ctx: &mut OT::hb_ot_apply_context_t, lookup: &LookupInfo) -> bool { diff --git a/src/hb/ot_layout_gpos_table.rs b/src/hb/ot_layout_gpos_table.rs index 70100920..957c2e09 100644 --- a/src/hb/ot_layout_gpos_table.rs +++ b/src/hb/ot_layout_gpos_table.rs @@ -9,7 +9,18 @@ use super::ot_shape_plan::hb_ot_shape_plan_t; use crate::Direction; pub fn position(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) { + #[cfg(feature = "std")] + let tag = plan + .ot_map + .chosen_script(TableIndex::GPOS) + .map_or("none".to_string(), |x| x.to_string()); + #[cfg(feature = "std")] + if !buffer.message(face, &format!("start table GPOS script tag '{tag}'")) { + return; + } apply_layout_table(plan, face, buffer, face.ot_tables.gpos.as_ref()); + #[cfg(feature = "std")] + buffer.message(face, &format!("end table GPOS script tag '{tag}'")); } pub mod attach_type { diff --git a/src/hb/ot_layout_gsub_table.rs b/src/hb/ot_layout_gsub_table.rs index c71205ef..5f927fc0 100644 --- a/src/hb/ot_layout_gsub_table.rs +++ b/src/hb/ot_layout_gsub_table.rs @@ -4,5 +4,16 @@ use super::ot_layout::*; use super::ot_shape_plan::hb_ot_shape_plan_t; pub fn substitute(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) { + #[cfg(feature = "std")] + let tag = plan + .ot_map + .chosen_script(TableIndex::GSUB) + .map_or("none".to_string(), |x| x.to_string()); + #[cfg(feature = "std")] + if !buffer.message(face, &format!("start table GSUB script tag '{tag}'")) { + return; + } apply_layout_table(plan, face, buffer, face.ot_tables.gsub.as_ref()); + #[cfg(feature = "std")] + buffer.message(face, &format!("end table GSUB script tag '{tag}'")); } diff --git a/src/hb/ot_layout_gsubgpos.rs b/src/hb/ot_layout_gsubgpos.rs index e163a9ea..abb6e238 100644 --- a/src/hb/ot_layout_gsubgpos.rs +++ b/src/hb/ot_layout_gsubgpos.rs @@ -536,10 +536,19 @@ pub(crate) fn apply_lookup( break; } + message_sync!( + ctx, + "recursing to lookup {} at {}", + record.lookup_list_index.get(), + ctx.buffer.idx + ); + if ctx.recurse(record.lookup_list_index.get()).is_none() { continue; } + message_sync!(ctx, "recursed to lookup {}", record.lookup_list_index.get(),); + let new_len = ctx.buffer.backtrack_len() + ctx.buffer.lookahead_len(); let mut delta = new_len as isize - orig_len as isize; if delta == 0 { diff --git a/src/hb/ot_shape.rs b/src/hb/ot_shape.rs index 06cf72fb..b1a68594 100644 --- a/src/hb/ot_shape.rs +++ b/src/hb/ot_shape.rs @@ -335,7 +335,10 @@ pub fn shape_internal(ctx: &mut hb_ot_shape_context_t) { ensure_native_direction(ctx.buffer); if let Some(func) = ctx.plan.shaper.preprocess_text { - func(ctx.plan, ctx.face, ctx.buffer); + if ctx.buffer.message(ctx.face, "start preprocess text") { + func(ctx.plan, ctx.face, ctx.buffer); + message!(ctx, "end preprocess text"); + } } substitute_pre(ctx); @@ -370,7 +373,10 @@ fn substitute_post(ctx: &mut hb_ot_shape_context_t) { hide_default_ignorables(ctx.buffer, ctx.face); if let Some(func) = ctx.plan.shaper.postprocess_glyphs { - func(ctx.plan, ctx.face, ctx.buffer); + if ctx.buffer.message(ctx.face, "start postprocess glyphs") { + func(ctx.plan, ctx.face, ctx.buffer); + message!(ctx, "end postprocess glyphs"); + } } } diff --git a/src/hb/ot_shape_fallback.rs b/src/hb/ot_shape_fallback.rs index 229e7b70..a2eb60d2 100644 --- a/src/hb/ot_shape_fallback.rs +++ b/src/hb/ot_shape_fallback.rs @@ -88,9 +88,12 @@ fn recategorize_combining_class(u: u32, mut class: u8) -> u8 { pub fn _hb_ot_shape_fallback_mark_position_recategorize_marks( _: &hb_ot_shape_plan_t, - _: &hb_font_t, + font: &hb_font_t, buffer: &mut hb_buffer_t, ) { + if !buffer.message(font, "start fallback mark") { + return; + } let len = buffer.len; for info in &mut buffer.info[..len] { if info.general_category() == GeneralCategory::NON_SPACING_MARK { @@ -99,6 +102,7 @@ pub fn _hb_ot_shape_fallback_mark_position_recategorize_marks( info.set_modified_combining_class(class); } } + buffer.message(font, "end fallback mark"); } fn zero_mark_advances( diff --git a/src/hb/ot_shape_normalize.rs b/src/hb/ot_shape_normalize.rs index 52842069..c3691b24 100644 --- a/src/hb/ot_shape_normalize.rs +++ b/src/hb/ot_shape_normalize.rs @@ -401,7 +401,7 @@ pub fn _hb_ot_shape_normalize( } // Second round, reorder (inplace) - if !all_simple { + if !all_simple && buffer.message(face, "start reorder") { let count = buffer.len; let mut i = 0; while i < count { @@ -426,6 +426,7 @@ pub fn _hb_ot_shape_normalize( i = end + 1; } + buffer.message(face, "end reorder"); } if buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CGJ != 0 { // For all CGJ, check if it prevented any reordering at all. diff --git a/src/hb/ot_shaper_indic.rs b/src/hb/ot_shaper_indic.rs index 86a9327a..fcdc8890 100644 --- a/src/hb/ot_shaper_indic.rs +++ b/src/hb/ot_shaper_indic.rs @@ -656,6 +656,9 @@ fn initial_reordering( use super::ot_shaper_indic_machine::SyllableType; let mut ret = false; + if !buffer.message(face, "start reordering indic initial") { + return ret; + } let indic_plan = plan.data::(); @@ -679,6 +682,8 @@ fn initial_reordering( end = buffer.next_syllable(start); } + buffer.message(face, "end reordering indic initial"); + ret } @@ -1339,9 +1344,12 @@ fn final_reordering(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb return false; } - foreach_syllable!(buffer, start, end, { - final_reordering_impl(plan, face, start, end, buffer); - }); + if buffer.message(face, "start reordering indic final") { + foreach_syllable!(buffer, start, end, { + final_reordering_impl(plan, face, start, end, buffer); + }); + buffer.message(face, "end reordering indic final"); + } buffer.deallocate_var(GlyphInfo::INDIC_CATEGORY_VAR); buffer.deallocate_var(GlyphInfo::INDIC_POSITION_VAR); diff --git a/src/hb/ot_shaper_khmer.rs b/src/hb/ot_shaper_khmer.rs index 98601867..b1e3eae7 100644 --- a/src/hb/ot_shaper_khmer.rs +++ b/src/hb/ot_shaper_khmer.rs @@ -147,27 +147,29 @@ fn reorder_khmer(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_bu let mut ret = false; - if insert_dotted_circles( - face, - buffer, - SyllableType::BrokenCluster as u8, - ot_category_t::OT_DOTTEDCIRCLE, - Some(ot_category_t::OT_Repha), - None, - ) { - ret = true; - } + if buffer.message(face, "start reordering khmer") { + if insert_dotted_circles( + face, + buffer, + SyllableType::BrokenCluster as u8, + ot_category_t::OT_DOTTEDCIRCLE, + Some(ot_category_t::OT_Repha), + None, + ) { + ret = true; + } - let khmer_plan = plan.data::(); + let khmer_plan = plan.data::(); - let mut start = 0; - let mut end = buffer.next_syllable(0); - while start < buffer.len { - reorder_syllable_khmer(khmer_plan, start, end, buffer); - start = end; - end = buffer.next_syllable(start); + let mut start = 0; + let mut end = buffer.next_syllable(0); + while start < buffer.len { + reorder_syllable_khmer(khmer_plan, start, end, buffer); + start = end; + end = buffer.next_syllable(start); + } + buffer.message(face, "end reordering khmer"); } - buffer.deallocate_var(GlyphInfo::KHMER_CATEGORY_VAR); ret diff --git a/src/hb/ot_shaper_myanmar.rs b/src/hb/ot_shaper_myanmar.rs index 684ca8d6..24b7c766 100644 --- a/src/hb/ot_shaper_myanmar.rs +++ b/src/hb/ot_shaper_myanmar.rs @@ -136,23 +136,26 @@ fn reorder_myanmar(_: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buf let mut ret = false; - if insert_dotted_circles( - face, - buffer, - SyllableType::BrokenCluster as u8, - ot_category_t::OT_DOTTEDCIRCLE, - None, - None, - ) { - ret = true; - } + if buffer.message(face, "start reordering myanmar") { + if insert_dotted_circles( + face, + buffer, + SyllableType::BrokenCluster as u8, + ot_category_t::OT_DOTTEDCIRCLE, + None, + None, + ) { + ret = true; + } - let mut start = 0; - let mut end = buffer.next_syllable(0); - while start < buffer.len { - reorder_syllable_myanmar(start, end, buffer); - start = end; - end = buffer.next_syllable(start); + let mut start = 0; + let mut end = buffer.next_syllable(0); + while start < buffer.len { + reorder_syllable_myanmar(start, end, buffer); + start = end; + end = buffer.next_syllable(start); + } + buffer.message(face, "end reordering myanmar"); } buffer.deallocate_var(GlyphInfo::MYANMAR_CATEGORY_VAR); diff --git a/src/hb/ot_shaper_syllabic.rs b/src/hb/ot_shaper_syllabic.rs index 8cb338d2..8c5d669c 100644 --- a/src/hb/ot_shaper_syllabic.rs +++ b/src/hb/ot_shaper_syllabic.rs @@ -19,6 +19,14 @@ pub fn insert_dotted_circles( } if (buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE) == 0 { + buffer.message( + face, + "skipped inserting dotted-circles because there is no broken syllables", + ); + return false; + } + + if !buffer.message(face, "start inserting dotted-circles") { return false; } @@ -69,6 +77,8 @@ pub fn insert_dotted_circles( buffer.sync(); + buffer.message(face, "end inserting dotted-circles"); + true } diff --git a/src/hb/ot_shaper_use.rs b/src/hb/ot_shaper_use.rs index 41817a6f..36127892 100644 --- a/src/hb/ot_shaper_use.rs +++ b/src/hb/ot_shaper_use.rs @@ -384,26 +384,27 @@ fn reorder_use(_: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_ use super::ot_shaper_use_machine::SyllableType; let mut ret = false; + if buffer.message(face, "start reordering USE") { + if insert_dotted_circles( + face, + buffer, + SyllableType::BrokenCluster as u8, + category::B, + Some(category::R), + None, + ) { + ret = true; + } - if insert_dotted_circles( - face, - buffer, - SyllableType::BrokenCluster as u8, - category::B, - Some(category::R), - None, - ) { - ret = true; - } - - let mut start = 0; - let mut end = buffer.next_syllable(0); - while start < buffer.len { - reorder_syllable_use(start, end, buffer); - start = end; - end = buffer.next_syllable(start); + let mut start = 0; + let mut end = buffer.next_syllable(0); + while start < buffer.len { + reorder_syllable_use(start, end, buffer); + start = end; + end = buffer.next_syllable(start); + } + buffer.message(face, "end reordering USE"); } - buffer.deallocate_var(GlyphInfo::USE_CATEGORY_VAR); ret