diff --git a/src/app/mtext_editor.rs b/src/app/mtext_editor.rs index 45abacb9c..a6cc64616 100644 --- a/src/app/mtext_editor.rs +++ b/src/app/mtext_editor.rs @@ -409,6 +409,9 @@ impl super::OpenCADStudio { anno, None, bg, + // Editor preview draws on a 2D canvas with no SDF shader — force the + // glyph outline strokes so the text is visible (#308). + true, ); // Per-character boxes share the preview wires' absolute coordinate frame. let boxes = crate::entities::mtext::glyph_boxes(&mt, &self.tabs[i].scene.document); @@ -509,6 +512,21 @@ impl super::OpenCADStudio { /// Insert text (or replace the current selection) at the preview caret. pub(super) fn mtext_type(&mut self, s: &str) { + // MTEXT stores a hard line break as the `\P` code. A literal newline — + // from Enter or a multi-line paste — is not a break to the layout (only + // the caret logic honours it), so the preview would stay on one line. + // Normalise every literal newline to `\P` so it breaks and the saved + // value stays standard (#308). + let normalized; + let s: &str = if s.contains(['\n', '\r']) { + normalized = s + .replace("\r\n", "\\P") + .replace('\n', "\\P") + .replace('\r', "\\P"); + &normalized + } else { + s + }; if let Some(ed) = self.mtext_editor.as_mut() { let raw0 = ed.content.text(); let raw = raw0.clone(); diff --git a/src/entities/insert.rs b/src/entities/insert.rs index ace4d569a..1a2ff04f4 100644 --- a/src/entities/insert.rs +++ b/src/entities/insert.rs @@ -240,6 +240,7 @@ pub(crate) fn append_insert_attribute_wires( anno_scale, None, bg_color, + false, ); // Use the INSERT's handle so selection / picking groups attribute // text with the parent insert instead of treating it as a stray text. diff --git a/src/entities/multileader.rs b/src/entities/multileader.rs index 66db05819..826500c9a 100644 --- a/src/entities/multileader.rs +++ b/src/entities/multileader.rs @@ -1357,6 +1357,7 @@ impl MultiLeaderTess for MultiLeader { 1.0, None, bg_color, + false, ); for w in &mut sub_wires { w.name = name.clone(); diff --git a/src/scene/cache/block_cache.rs b/src/scene/cache/block_cache.rs index 28c939b63..aac62dcaf 100644 --- a/src/scene/cache/block_cache.rs +++ b/src/scene/cache/block_cache.rs @@ -419,7 +419,7 @@ fn tessellate_sub_local( // before casting to f32 — same precision-preservation trick used for // top-level entities, applied per-defn. let wires_out = tessellate::tessellate( - doc, h, sub, false, sub_color, pat_len, pat, lw_px, anno_scale, None, bg_color, + doc, h, sub, false, sub_color, pat_len, pat, lw_px, anno_scale, None, bg_color, false, ); if wires_out.is_empty() { return vec![]; diff --git a/src/scene/convert/tess.rs b/src/scene/convert/tess.rs index 37233c0df..9bd201af8 100644 --- a/src/scene/convert/tess.rs +++ b/src/scene/convert/tess.rs @@ -204,6 +204,7 @@ pub(crate) fn tessellate_entity( 1.0, world_per_pixel, bg_color, + false, ); let ab = entity_aabb(e); for w in &mut wires { @@ -594,6 +595,7 @@ pub(crate) fn tessellate_entity( anno_scale, world_per_pixel, bg_color, + false, ); for w in &mut wires { w.name = h.value().to_string(); @@ -654,6 +656,7 @@ pub(crate) fn tessellate_entity( anno_scale, world_per_pixel, bg_color, + false, ); for b in &mut bases { b.aci = aci; diff --git a/src/scene/convert/tessellate.rs b/src/scene/convert/tessellate.rs index 88bedf7a8..52d7f5c56 100644 --- a/src/scene/convert/tessellate.rs +++ b/src/scene/convert/tessellate.rs @@ -90,6 +90,11 @@ pub fn tessellate( // (flag 0x02, "use drawing window colour") so the mask erases geometry // behind the text the way a wipeout does. bg_color: [f32; 4], + // When true, TEXT/MTEXT run-groups ALSO emit their glyph outline strokes as + // polyline points (not just SDF quads). The in-app MTEXT editor preview + // draws those strokes on a 2D canvas that can't run the SDF shader; every + // other caller passes false and gets the normal SDF-only text. + force_text_strokes: bool, ) -> Vec { let color = if selected { WireModel::SELECTED @@ -210,7 +215,10 @@ pub fn tessellate( // and text into one Text object (a tolerance frame: box lines = // run-less, cell text = run-groups) keeps the geometry as // strokes and draws only the text as SDF. - for group in stroke_groups.iter().filter(|g| g.run.is_none()) { + for group in stroke_groups + .iter() + .filter(|g| force_text_strokes || g.run.is_none()) + { let lx_v = group.origin[0]; let ly_v = group.origin[1]; let slx_v = (lx_v - ref_lx_v) * anno + ref_lx_v;