From f3b762855bd23ad5a8aaa9e30f02dd7869a50466 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 26 Mar 2026 18:20:54 +0100 Subject: [PATCH] :bug: Fix problem with position data in new render --- frontend/src/app/render_wasm/api.cljs | 69 +++++++++++++-------------- render-wasm/src/shapes/text.rs | 52 ++++++-------------- 2 files changed, 48 insertions(+), 73 deletions(-) diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 0254324696..53d1477c98 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -1631,45 +1631,42 @@ (+ offset POSITION-DATA-U32-SIZE))) (persistent! result))) - result - (into [] - (keep - (fn [{:keys [paragraph span start-pos end-pos direction x y width height]}] - (let [content (:content shape) - element (-> content :children - (get 0) :children ;; paragraph-set - (get paragraph) :children ;; paragraph - (get span)) - element-text (:text element)] + content (:content shape)] - ;; Add comprehensive nil-safety checks - (when (and element - element-text - (>= start-pos 0) - (<= end-pos (count element-text)) - (<= start-pos end-pos)) - (let [text (subs element-text start-pos end-pos)] - (d/patch-object - txt/default-text-attrs - (d/without-nils - {:x x - :y (+ y height) - :width width - :height height - :direction (dr/translate-direction direction) - :font-family (get element :font-family) - :font-size (get element :font-size) - :font-weight (get element :font-weight) - :text-transform (get element :text-transform) - :text-decoration (get element :text-decoration) - :letter-spacing (get element :letter-spacing) - :font-style (get element :font-style) - :fills (get element :fills) - :text text}))))))) - result)] (mem/free) - result))) + (into [] + (keep + (fn [{:keys [paragraph span start-pos end-pos direction x y width height]}] + (let [element (-> content :children + (get 0) :children ;; paragraph-set + (get paragraph) :children ;; paragraph + (get span)) + element-text (:text element)] + + ;; Add comprehensive nil-safety checks + ;; Be aware that for RTL texts `start-pos` can be greatert han `end-pos` + (when (and element element-text) + (let [text (subs element-text start-pos end-pos)] + (d/patch-object + txt/default-text-attrs + (d/without-nils + {:x x + :y (+ y height) + :width width + :height height + :direction (dr/translate-direction direction) + :font-id (get element :font-id) + :font-family (get element :font-family) + :font-size (get element :font-size) + :font-weight (get element :font-weight) + :text-transform (get element :text-transform) + :text-decoration (get element :text-decoration) + :letter-spacing (get element :letter-spacing) + :font-style (get element :font-style) + :fills (get element :fills) + :text text}))))))) + result)))) (defn apply-canvas-blur [] diff --git a/render-wasm/src/shapes/text.rs b/render-wasm/src/shapes/text.rs index 96adb52b28..d5592b591a 100644 --- a/render-wasm/src/shapes/text.rs +++ b/render-wasm/src/shapes/text.rs @@ -1378,67 +1378,45 @@ pub fn calculate_text_layout_data( let current_y = para_layout.y; let text_paragraph = text_paragraphs.get(paragraph_index); if let Some(text_para) = text_paragraph { - let mut span_ranges: Vec<(usize, usize, usize, String, String)> = vec![]; + let mut span_ranges: Vec<(usize, usize, usize)> = vec![]; let mut cur = 0; for (span_index, span) in text_para.children().iter().enumerate() { - let transformed_text: String = span.apply_text_transform(); - let original_text = span.text.clone(); - let text = transformed_text.clone(); - let text_len = text.len(); - span_ranges.push((cur, cur + text_len, span_index, text, original_text)); + let text: String = span.apply_text_transform(); + let text_len = text.encode_utf16().count(); + span_ranges.push((cur, cur + text_len + 1, span_index)); cur += text_len; } - for (start, end, span_index, transformed_text, original_text) in span_ranges { - // Skip empty spans to avoid invalid rect calculations - if start >= end { - continue; - } + for (start, end, span_index) in span_ranges { let rects = para_layout.paragraph.get_rects_for_range( start..end, RectHeightStyle::Tight, RectWidthStyle::Tight, ); + for textbox in rects { let direction = textbox.direct; let mut rect = textbox.rect; let cy = rect.top + rect.height() / 2.0; // Get byte positions from Skia's transformed text layout - let glyph_start = para_layout + let start_pos = para_layout .paragraph .get_glyph_position_at_coordinate((rect.left + 0.1, cy)) - .position as usize; - let glyph_end = para_layout + .position as usize + - start; + + let end_pos = para_layout .paragraph .get_glyph_position_at_coordinate((rect.right - 0.1, cy)) - .position as usize; - - // Convert to byte positions relative to this span - let byte_start = glyph_start.saturating_sub(start); - let byte_end = glyph_end.saturating_sub(start); - - // Convert byte positions to character positions in ORIGINAL text - // This handles multi-byte UTF-8 and text transform differences - let char_start = transformed_text - .char_indices() - .position(|(i, _)| i >= byte_start) - .unwrap_or(0); - let char_end = transformed_text - .char_indices() - .position(|(i, _)| i >= byte_end) - .unwrap_or_else(|| transformed_text.chars().count()); - - // Clamp to original text length for safety - let original_char_count = original_text.chars().count(); - let final_start = char_start.min(original_char_count); - let final_end = char_end.min(original_char_count); + .position as usize + - start; rect.offset((x, current_y)); position_data.push(PositionData { paragraph: paragraph_index as u32, span: span_index as u32, - start_pos: final_start as u32, - end_pos: final_end as u32, + start_pos: start_pos as u32, + end_pos: end_pos as u32, x: rect.x(), y: rect.y(), width: rect.width(),