Files
cadcraft/crates/engine/tests/dim_diameter_sign.rs
T
BooksandBrandon Thomas a94fc0898b Diameter dimensions: the *D block's text keeps the sign as %%c (fixes #1645) (#1696)
The *D block and EXPLODE's MTEXT took the dimension's decoded text, so
%%c arrived as U+2300, which no SHX font has: other programs plotted a
diameter dimension in an SHX style without its sign. They now write
DimGeometry::mtext_contents(): the sign as %%c, which every font draws
(and CADCraft decodes), a % that would run into a code as %%%.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-24 01:53:32 -04:00

71 lines
3.3 KiB
Rust

//! #1645: a diameter dimension's text in its `*D` block, and in the MTEXT EXPLODE leaves, holds
//! the diameter sign as `%%c`, as CADCraft's own TEXT and MTEXT keep it. The decoded ⌀ (U+2300)
//! is in no SHX font, so other programs plotted the dimension in an SHX style ("romans.shx")
//! without its sign. CADCraft still draws and reads the sign.
use cadcraft_engine::Session;
use cadcraft_engine::doc::{EntityKind, Handle};
use serde_json::{Value, json};
fn h(v: &Value) -> Handle {
v["handle"].as_str().and_then(Handle::parse_hex).unwrap()
}
/// The issue's drawing: an SHX text style, a dimension style using it, a diameter dimension
/// of a circle of radius 30.
fn session() -> (Session, Handle) {
let mut s = Session::new();
s.execute("units", &json!({"measurement": "metric"})).ok();
s.execute("style", &json!({"name": "R", "font": "romans.shx", "current": true})).unwrap();
s.execute("dimstyle", &json!({"name": "D", "current": true, "DIMTXSTY": "R", "DIMTXT": 5, "DIMASZ": 5, "DIMDEC": 0})).unwrap();
let c = h(&s.execute("circle", &json!({"center": [0, 0], "radius": 30})).unwrap());
let dim = h(&s.execute("dimdiameter", &json!({"handle": c.hex(), "at": [40, 40]})).unwrap());
(s, dim)
}
/// The MTEXT contents (group 1) inside each `*D…` block of a DXF.
fn dblock_texts(dxf: &str) -> Vec<String> {
let lines: Vec<&str> = dxf.lines().map(str::trim).collect();
let mut out = Vec::new();
let (mut in_d, mut in_mtext) = (false, false);
for pair in lines.chunks(2) {
let [code, value] = pair else { continue };
match (*code, *value) {
("0", "BLOCK") => in_d = false,
("2", name) if !in_mtext && name.starts_with("*D") => in_d = true,
("0", "MTEXT") => in_mtext = in_d,
("0", _) => in_mtext = false,
("1", text) if in_mtext => out.push(text.to_string()),
_ => {}
}
}
out
}
#[test]
fn the_dimension_block_writes_the_sign_as_a_code() {
let (s, dim) = session();
let d = s.doc().unwrap();
let EntityKind::Dimension(dm) = &d.entity(dim).unwrap().kind else { panic!("not a dimension") };
assert!(matches!(dm.kind, cadcraft_engine::doc::DimKind::Diameter));
let texts = dblock_texts(&cadcraft_io::write_dxf(d));
assert_eq!(texts, vec!["%%c60".to_string()], "the *D block's MTEXT");
// Read back, CADCraft still shows the sign.
let read = cadcraft_io::read_dxf(cadcraft_io::write_dxf(d).as_bytes()).unwrap();
let dm = read.model.iter().find_map(|e| if let EntityKind::Dimension(dm) = &e.kind { Some(dm.clone()) } else { None }).unwrap();
let st = read.dim_styles.iter().find(|x| x.name.eq_ignore_ascii_case(&dm.style)).cloned().unwrap_or_default();
assert_eq!(cadcraft_render::dimension_geometry(&dm, &st, 1.0).value, "⌀60");
}
#[test]
fn exploding_keeps_the_sign_as_a_code() {
let (mut s, dim) = session();
s.execute("explode", &json!({"handles": [dim.hex()]})).unwrap();
let d = s.doc().unwrap();
let texts: Vec<String> =
d.model.iter().filter_map(|e| if let EntityKind::MText(m) = &e.kind { Some(m.contents.clone()) } else { None }).collect();
assert_eq!(texts, vec!["%%c60".to_string()]);
let saved = cadcraft_io::write_dxf(d);
assert!(!saved.contains('⌀'), "no U+2300 is saved");
}