mirror of
https://github.com/storytold/cadcraft.git
synced 2026-11-10 04:19:59 -06:00
fix(dwg): read kept objects as the file's version on every save, not just the first
The bridge took the version of the kept groups from $ACADVER, but SAVEAS writes the saved version there and the kept groups stay as they were read. After a 2018 drawing was saved as a 2004 DWG, the next save read its mental ray render settings as 2004 (class version 2, which the ODA File Converter rejects) and a 2004 field lost its cached value. Foreign now records the $ACADVER of the file the kept data was read from (Foreign::version), and the DWG save passes that. The new declare_version test also moves out of the doc comment it was pasted into. Both reported by austin62jake on #2471.
This commit is contained in:
@@ -75,6 +75,10 @@ pub struct Foreign {
|
||||
/// surfaces, which from 2013 on those entities don't carry themselves: each `ACDSRECORD`
|
||||
/// names its entity with a 320 handle (see [`Foreign::data_store_entities`]).
|
||||
pub data_store: Vec<RawTag>,
|
||||
/// The `$ACADVER` of the file all this was read from: its groups are in that version's form.
|
||||
/// The drawing's own `$ACADVER` is the version it is saved as, which SAVEAS changes, so a
|
||||
/// later save must not take it for this one (#2471).
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
impl Foreign {
|
||||
|
||||
+15
-15
@@ -1298,21 +1298,6 @@ mod tests {
|
||||
}
|
||||
|
||||
/// A kept version not newer than the declared one, a binary DXF and a DXF without `$ACADVER`
|
||||
/// The header is read as group code and value pairs up to the first `0`/`ENDSEC`: a `$ACADVER`
|
||||
/// name whose value would come after it, or a file that never ends its HEADER, is no version
|
||||
/// to replace, and `\r\n` lines keep the value's offset right.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[test]
|
||||
fn declare_version_reads_the_header_in_pairs_up_to_endsec() {
|
||||
let cut = b" 0\r\nSECTION\r\n 2\r\nHEADER\r\n 9\r\n$ACADVER\r\n 0\r\nENDSEC\r\n 1\r\nAC1015\r\n";
|
||||
assert_eq!(native::declare_version(cut, acadrust::DxfVersion::AC1032), None);
|
||||
let endless = vec![b'\n'; 1 << 20];
|
||||
assert_eq!(native::declare_version(&endless, acadrust::DxfVersion::AC1032), None);
|
||||
let crlf = b" 0\r\nSECTION\r\n 2\r\nHEADER\r\n 9\r\n$ACADVER\r\n 1\r\n AC1015\r\n 0\r\nENDSEC\r\n";
|
||||
let out = native::declare_version(crlf, acadrust::DxfVersion::AC1032).unwrap();
|
||||
assert_eq!(out, b" 0\r\nSECTION\r\n 2\r\nHEADER\r\n 9\r\n$ACADVER\r\n 1\r\n AC1032\r\n 0\r\nENDSEC\r\n");
|
||||
}
|
||||
|
||||
/// are read as they are.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[test]
|
||||
@@ -1328,6 +1313,21 @@ mod tests {
|
||||
assert_eq!(native::declare_version(late, acadrust::DxfVersion::AC1032), None);
|
||||
}
|
||||
|
||||
/// The header is read as group code and value pairs up to the first `0`/`ENDSEC`: a `$ACADVER`
|
||||
/// name whose value would come after it, or a file that never ends its HEADER, is no version
|
||||
/// to replace, and `\r\n` lines keep the value's offset right.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[test]
|
||||
fn declare_version_reads_the_header_in_pairs_up_to_endsec() {
|
||||
let cut = b" 0\r\nSECTION\r\n 2\r\nHEADER\r\n 9\r\n$ACADVER\r\n 0\r\nENDSEC\r\n 1\r\nAC1015\r\n";
|
||||
assert_eq!(native::declare_version(cut, acadrust::DxfVersion::AC1032), None);
|
||||
let endless = vec![b'\n'; 1 << 20];
|
||||
assert_eq!(native::declare_version(&endless, acadrust::DxfVersion::AC1032), None);
|
||||
let crlf = b" 0\r\nSECTION\r\n 2\r\nHEADER\r\n 9\r\n$ACADVER\r\n 1\r\n AC1015\r\n 0\r\nENDSEC\r\n";
|
||||
let out = native::declare_version(crlf, acadrust::DxfVersion::AC1032).unwrap();
|
||||
assert_eq!(out, b" 0\r\nSECTION\r\n 2\r\nHEADER\r\n 9\r\n$ACADVER\r\n 1\r\n AC1032\r\n 0\r\nENDSEC\r\n");
|
||||
}
|
||||
|
||||
/// Declared as 2007 or later, a DXF is UTF-8: code page text is decoded first. Declared as an
|
||||
/// older version, the code page bytes stay as they are.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
||||
@@ -452,6 +452,7 @@ pub(crate) fn read(secs: &[Section], d: &mut Drawing) {
|
||||
f.data_store = ds.tags.iter().take(MAX_DATA_STORE_GROUPS).map(raw).collect();
|
||||
}
|
||||
if !f.is_empty() {
|
||||
f.version = d.header.get("ACADVER").and_then(cadcraft_doc::HVal::as_str).map(str::to_string);
|
||||
d.foreign = Arc::new(f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,11 +102,12 @@ pub fn write_with(d: &Drawing, name: impl AsRef<Path>, opts: &SaveOptions) -> Re
|
||||
// one, at least 2004: R2000 DWG has no true colours, transparency, table styles or
|
||||
// gradients, so a drawing from an R2000 DXF doesn't lose them by default. The objects
|
||||
// CADCraft keeps without modelling them are still in the groups of the file they came
|
||||
// from (`$ACADVER`), and the bridge reads them as that version.
|
||||
// from (`Foreign::version`, not `$ACADVER`, which SAVEAS changes), and the bridge reads
|
||||
// them as that version.
|
||||
Some(Format::Dwg) if !opts.binary => {
|
||||
let version = opts.version.unwrap_or_else(|| version.max(Version::R2004));
|
||||
let bridge = dxf_write::dwg_bridge(d, version);
|
||||
match d.header.get("ACADVER").and_then(cadcraft_doc::HVal::as_str) {
|
||||
match d.foreign.version.as_deref() {
|
||||
Some(kept) => cadcraft_dwg::dxf_to_dwg_kept_as(bridge.as_bytes(), version.code(), kept),
|
||||
None => cadcraft_dwg::dxf_to_dwg_as(bridge.as_bytes(), version.code()),
|
||||
}
|
||||
|
||||
@@ -235,6 +235,31 @@ fn kept_objects_survive(version: &str, target: Version) {
|
||||
assert!(wrong.is_empty(), "{version} → {target:?}:\n{}", wrong.join("\n"));
|
||||
}
|
||||
|
||||
/// A drawing saved once as an older DWG has that version as its `$ACADVER` (SAVEAS sets it, so a
|
||||
/// later QSAVE writes it again), but its kept objects are still in the 2018 file's groups. The
|
||||
/// next save must still read them as 2018: read as the saved version, the mental ray settings
|
||||
/// came out a class version too high (the ODA refuses the DWG) and a 2004 field lost its cached
|
||||
/// value.
|
||||
#[test]
|
||||
fn kept_groups_keep_their_version_across_saves() {
|
||||
let dxf = synthetic("AC1032");
|
||||
for (first, second) in [(Version::R2004, Version::R2004), (Version::R2010, Version::R2018), (Version::R2018, Version::R2018)] {
|
||||
let mut source = acadrust::DxfReader::from_reader(Cursor::new(dxf.clone().into_bytes())).unwrap().read().unwrap();
|
||||
source.version = acad(second);
|
||||
let reference = kept(&acadrust::DwgReader::from_stream(Cursor::new(acadrust::DwgWriter::write_to_vec(&source).unwrap())).read().unwrap());
|
||||
|
||||
let mut d = read(dxf.as_bytes(), "kept.dxf").unwrap();
|
||||
write_with(&d, "kept.dwg", &SaveOptions { version: Some(first), binary: false }).unwrap();
|
||||
// What SAVEAS does to the drawing after writing it.
|
||||
d.header.set_str("ACADVER", first.code());
|
||||
let dwg = write_with(&d, "kept.dwg", &SaveOptions { version: Some(second), binary: false }).unwrap();
|
||||
let written = kept(&acadrust::DwgReader::from_stream(Cursor::new(dwg)).read().unwrap());
|
||||
let field = |k: &Kept| k.field.as_ref().map(|f| (f.value.clone(), f.value_string.clone()));
|
||||
assert_eq!(written.mental_ray, reference.mental_ray, "{first:?} then {second:?}: mental ray render settings");
|
||||
assert_eq!(field(&written), field(&reference), "{first:?} then {second:?}: field");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_2018_files_kept_objects_reach_a_2018_dwg() {
|
||||
kept_objects_survive("AC1032", Version::R2018);
|
||||
|
||||
Reference in New Issue
Block a user