From ef19f898b04d0d8ed618beef2c401c769e3ff64d Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sun, 19 Jul 2026 17:24:37 +0300 Subject: [PATCH] fix(mtext): size the empty-line caret slot to the text, not the entity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The paragraph-start caret slot used the raw entity height, so a blank line's caret stood entity-tall — many times the visible text when `\H…x;` runs shrink it (5× on budweiser2018.dwg handle 50019). Size it to the line's own text height instead (an empty line inherits the previous line's height, the same rule `per_line_h` already uses for spacing). Co-Authored-By: Claude Opus 4.8 --- src/entities/text_support.rs | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/entities/text_support.rs b/src/entities/text_support.rs index ca6cea10d..257b43322 100644 --- a/src/entities/text_support.rs +++ b/src/entities/text_support.rs @@ -1311,6 +1311,34 @@ pub fn layout_mtext(opts: &MTextRenderOpts) -> MTextLayout { } }) .collect(); + // Per-line text height — the tallest Word on the line, an empty line + // inheriting the previous line's height (same rule as `per_line_h`). Unlike + // `line_max_h` it has no `entity_h` floor, so it reflects the ACTUAL text + // size; used to size the empty-line caret slot to the surrounding text + // rather than the raw entity height (which can be many times the visible + // text when `\H…x;` runs shrink it). + let line_text_h: Vec = { + let mut last = entity_h; + sub_lines + .iter() + .map(|s| { + let mh = s + .atoms + .iter() + .filter_map(|a| match &a.kind { + AtomKind::Word(_) => Some(a.state.height_mul * entity_h), + _ => None, + }) + .fold(0.0_f32, f32::max); + if mh > 0.0 { + last = mh; + mh + } else { + last + } + }) + .collect() + }; let line_y_offset: Vec = { let mut col_y = vec![0.0_f32; cols.count.max(1)]; let mut started = vec![false; cols.count.max(1)]; @@ -1444,10 +1472,14 @@ pub fn layout_mtext(opts: &MTextRenderOpts) -> MTextLayout { // A paragraph break (explicit `\n` / `\P`) that started this line gets // a zero-width caret slot at the line start, so the MText editor can - // place the caret on a fresh/empty line. + // place the caret on a fresh/empty line. Size it to the line's own text + // height (the surrounding text for an empty line), not the raw entity + // height — otherwise a blank line's caret is entity-tall, which is many + // times the visible text when `\H…x;` runs shrink it. if opts.want_glyph_boxes && i > 0 && sub.is_first_in_paragraph { + let caret_h = line_text_h.get(i).copied().unwrap_or(entity_h); let (ax, ay) = to_world(line_base_x, line_base_y, cursor_start, 0.0); - let (_, by) = to_world(line_base_x, line_base_y, cursor_start, entity_h); + let (_, by) = to_world(line_base_x, line_base_y, cursor_start, caret_h); glyph_boxes.push(GlyphBox { vis, xmin: ax,