diff --git a/crates/io/src/dxf_read/entities.rs b/crates/io/src/dxf_read/entities.rs
--- a/crates/io/src/dxf_read/entities.rs
+++ b/crates/io/src/dxf_read/entities.rs
@@ -117,7 +117,9 @@ pub(super) fn entity_with_table_budget(kind: &str, tags: &[Tag], table_cells_lef
         return None;
     }
     let t = T(tags);
-    let c = common(&t);
+    // MTEXT reuses group 48 for column width; embedded objects also have their own groups.
+    // Keep those out of Common without changing the tags used for the entity's contents.
+    let c = common(&T(if kind == "MTEXT" { mtext_dxf::before_columns(tags) } else { tags }));
     let k = match kind {
         "LINE" => EntityKind::Line(Line { a: t.p(10), b: t.p(11) }),
         "POINT" => EntityKind::Point(Point { p: t.p(10), angle: t.fd(50, 0.0).to_radians() }),
diff --git a/crates/io/tests/it/mtext_columns_roundtrip.rs b/crates/io/tests/it/mtext_columns_roundtrip.rs
--- a/crates/io/tests/it/mtext_columns_roundtrip.rs
+++ b/crates/io/tests/it/mtext_columns_roundtrip.rs
@@ -1,9 +1,11 @@
 //! MTEXT columns and paragraph codes survive a DXF round trip (#532): the column groups
 //! 75/76/78/79/48/49/50, tabs as `^I`, carets as `^ `.
+//! Column width must not also become the entity's Common.ltscale (#2247).
 
-use cadcraft_doc::{Common, Drawing, EntityKind, MText, MTextColumnType, MTextColumns, Space};
+use cadcraft_doc::{Common, Drawing, EntityKind, Line, MText, MTextColumnType, MTextColumns, Space};
+use cadcraft_dxf::Tag;
 use cadcraft_geom::Vec3;
-use cadcraft_io::{read_dxf, write_dxf};
+use cadcraft_io::{SaveOptions, Version, entity_from_groups, read_dxf, write_dxf, write_with};
 
 fn mtext(contents: &str, rotation: f64, columns: Option<MTextColumns>) -> MText {
     MText {
@@ -91,3 +93,106 @@ fn hostile_column_groups_do_not_panic() {
         let _ = read_dxf(bad.as_bytes());
     }
 }
+
+type TestResult = Result<(), Box<dyn std::error::Error>>;
+
+/// Compare each read against the authored entity, including Common, rather than a first-save baseline.
+fn assert_ltscale_round_trips(kind: EntityKind, ltscale: f64) -> TestResult {
+    let common = Common { ltscale, color: cadcraft_color::Color::Index(3), visible: false, ..Common::default() };
+    let mut authored = Drawing::new_metric();
+    let handle = authored.add(&Space::Model, common.clone(), kind.clone())?;
+    for version in Version::ALL {
+        for binary in [false, true] {
+            let options = SaveOptions { version: Some(version), binary };
+            let mut drawing = authored.clone();
+            for pass in 1..=2 {
+                let bytes = write_with(&drawing, "group48.dxf", &options)?;
+                drawing = read_dxf(&bytes)?;
+                let entity = drawing.model.get(handle).ok_or("entity lost on DXF read")?;
+                assert_eq!(entity.common.ltscale, ltscale, "{version:?}, binary={binary}, pass={pass}");
+                assert_eq!(entity.common, common, "{version:?}, binary={binary}, pass={pass}");
+                assert_eq!(entity.kind, kind, "{version:?}, binary={binary}, pass={pass}");
+            }
+        }
+    }
+    Ok(())
+}
+
+#[test]
+fn uncolumned_mtext_keeps_common_ltscale() -> TestResult {
+    let t = MText { width: 12.5, ..mtext("plain text", 0.0, None) };
+    for ltscale in [1.0, 2.5] {
+        assert_ltscale_round_trips(EntityKind::MText(t.clone()), ltscale)?;
+    }
+    Ok(())
+}
+
+#[test]
+fn static_column_width_is_not_common_ltscale() -> TestResult {
+    let columns = MTextColumns { kind: MTextColumnType::Static, count: 3, gutter: 1.25, height: 7.5, flow_reversed: true, ..Default::default() };
+    let t = MText { width: 12.5, ..mtext("one\\Ntwo", 0.0, Some(columns)) };
+    for ltscale in [1.0, 2.5] {
+        assert_ltscale_round_trips(EntityKind::MText(t.clone()), ltscale)?;
+    }
+    Ok(())
+}
+
+#[test]
+fn dynamic_column_width_is_not_common_ltscale() -> TestResult {
+    for auto_height in [false, true] {
+        let columns = MTextColumns {
+            kind: MTextColumnType::Dynamic,
+            count: 3,
+            gutter: 1.25,
+            height: 7.5,
+            auto_height,
+            heights: if auto_height { Vec::new() } else { vec![7.5, 5.0, 6.0] },
+            flow_reversed: true,
+        };
+        let t = MText { width: 12.5, ..mtext("dynamic columns", 0.0, Some(columns)) };
+        for ltscale in [1.0, 2.5] {
+            assert_ltscale_round_trips(EntityKind::MText(t.clone()), ltscale)?;
+        }
+    }
+    Ok(())
+}
+
+#[test]
+fn line_keeps_common_ltscale() -> TestResult {
+    for ltscale in [1.0, 2.5] {
+        assert_ltscale_round_trips(EntityKind::Line(Line { a: Vec3::ZERO, b: Vec3::new(1.0, 2.0, 3.0) }), ltscale)?;
+    }
+    Ok(())
+}
+
+#[test]
+fn embedded_groups_do_not_supply_mtext_common_ltscale() -> TestResult {
+    for subclass_markers in [false, true] {
+        for ltscale in [1.0, 2.5] {
+            let mut tags = Vec::new();
+            if subclass_markers {
+                tags.push(Tag::s(100, "AcDbEntity"));
+            }
+            tags.push(Tag::s(8, "0"));
+            if ltscale != 1.0 {
+                tags.push(Tag::f(48, ltscale));
+            }
+            if subclass_markers {
+                tags.push(Tag::s(100, "AcDbMText"));
+            }
+            tags.extend([
+                Tag::f(40, 0.5),
+                Tag::f(41, 12.5),
+                Tag::s(1, "own text"),
+                Tag::s(101, "Embedded Object"),
+                // A reused code in the embedded payload is not an entity-level property.
+                Tag::f(48, 99.0),
+            ]);
+            let (common, kind) = entity_from_groups("MTEXT", &tags).ok_or("MTEXT was not read")?;
+            assert_eq!(common.ltscale, ltscale);
+            assert_eq!(common, Common { ltscale, ..Common::default() });
+            assert!(matches!(kind, EntityKind::MText(t) if t.columns.is_none() && t.width == 12.5 && t.rotation == 0.0));
+        }
+    }
+    Ok(())
+}
