diff --git a/crates/io/src/dxf_write.rs b/crates/io/src/dxf_write.rs index 8ff7e3625..8eb3ab91f 100644 --- a/crates/io/src/dxf_write.rs +++ b/crates/io/src/dxf_write.rs @@ -152,17 +152,5 @@ pub fn entity_groups(d: &Drawing, e: &Entity) -> (String, Vec) { entity(&mut w, d, e, "0", paper, &cx); let mut tags = w.t.into_iter(); let kind = tags.next().filter(|t| t.code == 0).map(|t| t.value.as_str()).unwrap_or_default(); - // Strings as AutoLISP sees them: `10^2`, not the file's `10^ 2`. Only the groups written - // escaped: the text (1) and an ATTDEF's prompt (3), not a DIMENSION's style name (3). - let escaped = |code: i32| match kind.as_str() { - "TEXT" | "ATTRIB" | "DIMENSION" => code == 1, - "ATTDEF" => matches!(code, 1 | 3), - _ => false, - }; - let tags = tags - .take_while(|t| t.code != 0) - .filter(|t| t.code != 330) - .map(|t| if escaped(t.code) { Tag::s(t.code, crate::dxf_read::mtext_dxf::unescape_carets(&t.value.as_str())) } else { t }) - .collect(); - (kind, tags) + (kind, tags.take_while(|t| t.code != 0).filter(|t| t.code != 330).collect()) } diff --git a/crates/io/tests/it/text_carets_roundtrip.rs b/crates/io/tests/it/text_carets_roundtrip.rs index 6e2052689..33f3f900c 100644 --- a/crates/io/tests/it/text_carets_roundtrip.rs +++ b/crates/io/tests/it/text_carets_roundtrip.rs @@ -55,21 +55,21 @@ fn drawing() -> Drawing { modes: Default::default(), }; d.add(&Space::Model, Common::default(), EntityKind::AttDef(attdef)).unwrap(); - let dm = Dimension { style: "A^ B".into(), ..dimension("<> x10^3") }; - d.add(&Space::Model, Common::default(), EntityKind::Dimension(dm)).unwrap(); + d.add(&Space::Model, Common::default(), EntityKind::Dimension(dimension("<> x10^3"))).unwrap(); d } -fn strings(d: &Drawing) -> Vec { - d.model - .iter() - .flat_map(|e| match &e.kind { - EntityKind::Text(t) => vec![t.value.clone()], - EntityKind::AttDef(a) => vec![a.text.value.clone(), a.prompt.clone()], - EntityKind::Dimension(dm) => vec![dm.text.clone()], - _ => Vec::new(), - }) - .collect() +fn strings(k: &EntityKind) -> Vec { + match k { + EntityKind::Text(t) => vec![t.value.clone()], + EntityKind::AttDef(a) => vec![a.text.value.clone(), a.prompt.clone()], + EntityKind::Dimension(dm) => vec![dm.text.clone()], + _ => Vec::new(), + } +} + +fn all_strings(d: &Drawing) -> Vec { + d.model.iter().flat_map(|e| strings(&e.kind)).collect() } #[test] @@ -80,20 +80,19 @@ fn carets_are_written_escaped_and_read_back() { assert!(dxf.contains(escaped), "{escaped}"); } let back = read_dxf(dxf.as_bytes()).unwrap(); - assert_eq!(strings(&back), ["10^2 m, A^JB", "1^3", "Area in m^2", "<> x10^3"]); + assert_eq!(all_strings(&back), ["10^2 m, A^JB", "1^3", "Area in m^2", "<> x10^3"]); // Older files have bare carets: kept as they are. let old = read_dxf(dxf.replace("10^ 2 m", "10^2 m").as_bytes()).unwrap(); - assert_eq!(strings(&old)[0], "10^2 m, A^JB"); + assert_eq!(all_strings(&old)[0], "10^2 m, A^JB"); } #[test] -fn entget_shows_plain_carets() { +fn entmod_of_entget_keeps_the_strings() { + // entget gives the groups as the file has them (as for MTEXT), and entmod reads them back. let d = drawing(); - let groups = |i: usize| cadcraft_io::entity_groups(&d, d.model.iter().nth(i).unwrap()).1; - let has = |g: &[cadcraft_dxf::Tag], code: i32, value: &str| g.iter().any(|t| t.code == code && t.value.as_str() == value); - assert!(has(&groups(0), 1, "10^2 m, A^JB")); - assert!(has(&groups(1), 3, "Area in m^2")); - // A DIMENSION's group 3 is its style name, written as it is. - let dim = groups(2); - assert!(has(&dim, 1, "<> x10^3") && has(&dim, 3, "A^ B"), "{dim:?}"); + for e in d.model.iter() { + let (kind, groups) = cadcraft_io::entity_groups(&d, e); + let (_, back) = cadcraft_io::entity_from_groups(&kind, &groups).unwrap(); + assert_eq!(strings(&back), strings(&e.kind), "{kind}"); + } } diff --git a/docs/file-format-parity.md b/docs/file-format-parity.md index aebb5f96e..b1f88a4cd 100644 --- a/docs/file-format-parity.md +++ b/docs/file-format-parity.md @@ -140,7 +140,7 @@ our output in AutoCAD black-box. | Date | Change | Summary | |---|---|---| -| 2026-10-31 | minor | A caret in a TEXT, ATTDEF, ATTRIB or DIMENSION string is written as `^` and a space and read back, as the DXF Reference has it: AutoCAD read the bare `^2` of `10^2` as a control character; AutoLISP's entget shows the plain caret | +| 2026-10-31 | minor | A caret in a TEXT, ATTDEF, ATTRIB or DIMENSION string is written as `^` and a space and read back, as the DXF Reference has it: AutoCAD read the bare `^2` of `10^2` as a control character | | 2026-10-29 | minor | R2000 DXF: CLASS records written without group 91 (instance count), which came with the 2004 format; AutoCAD LT 2026 stopped responding on every R2000 DXF CADCraft saved (checked black-box in AutoCAD LT 2026) | | 2026-10-26 | minor | CC-046: ATTEDIT and -ATTEDIT value changes invalidate derived justified baselines before DXF/DWG export; preserve alignment anchors, unchanged values and Aligned/Fit endpoints. Review follow-up: baseline recomputation preserves insertion elevation independently of alignment elevation. The shared writer correction also applies to justified TEXT. Independent of attribute editor UI; reverting the follow-up restores elevation loss on recomputation, while reverting the value setter restores stale XY baselines. | | 2026-10-25 | minor | LAYOUT SAveas (`layout.saveas`) writes a layout as a drawing template: its page setup and paper space objects, no model space objects or other layouts, and only the named objects they use |