From 37059cb0d6ebfd81ba593ee6442f5d1c19b1d5da Mon Sep 17 00:00:00 2001 From: Christopher Overbeck Date: Thu, 17 Sep 2026 10:12:39 -0500 Subject: [PATCH] Repair knot-less splines on every load path load_file, used by headless export, xrefs and block import, finalizes a drawing without purging, so the spline degree repair never ran there and `--export drawing.dxf out.dwg` still hit the knot underflow. Run the repair when any load is finalized. Co-Authored-By: Claude Opus 5 (1M context) --- src/io/mod.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/io/mod.rs b/src/io/mod.rs index d7be16cf8..38a115cbe 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -1000,6 +1000,7 @@ fn finalize_loaded_outcome( ) -> Result { let doc = &mut outcome.document; normalize_block_origins(doc); + normalize_knotless_splines(doc); if outcome.stats.source_format == Some(acadrust::SourceFormat::Dxf) { fix_dxf_dimension_rotations(doc); fix_dxf_layout_plot_settings(doc); @@ -2427,6 +2428,31 @@ mod corrupt_guard_tests { let _ = std::fs::remove_file(&path); } + // `load_file` (headless export, xrefs, block import) does not purge, so + // the spline repair has to happen when any load is finalized. + #[test] + fn knotless_spline_is_repaired_by_every_load_path() { + const DXF: &str = "0\nSECTION\n2\nENTITIES\n0\nSPLINE\n8\n0\n70\n0\n71\n3\n72\n0\n73\n2\n74\n0\n\ +10\n0\n20\n0\n30\n0\n10\n1\n20\n1\n30\n0\n0\nENDSEC\n0\nEOF\n"; + let dir = std::env::temp_dir(); + let source = dir.join(format!("ocs_knotless_load_{}.dxf", std::process::id())); + let target = dir.join(format!("ocs_knotless_load_{}.dwg", std::process::id())); + std::fs::write(&source, DXF).unwrap(); + let doc = load_file(&source).expect("load"); + let _ = std::fs::remove_file(&source); + let spline = doc + .entities() + .find_map(|entity| match entity { + EntityType::Spline(spline) => Some(spline), + _ => None, + }) + .expect("spline"); + assert_eq!(spline.degree, 1); + assert_eq!(spline.knots.len(), 4); + save_as_version(&doc, &target, acadrust::DxfVersion::AC1032).expect("save"); + let _ = std::fs::remove_file(&target); + } + // Small but finite arcs are valid records. Kernel tessellation is bounded, // so opening must retain them instead of treating their size as corruption. #[test]