diff --git a/src/app/update.rs b/src/app/update.rs index 20e94c0ac..e8e2edeec 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -1696,7 +1696,7 @@ impl H7CAD { if let Some(AcadEntityType::Viewport(vp)) = self.tabs[i].scene.document.get_entity(h) { - if vp.id > 1 { Some(h) } else { None } + if Scene::is_user_viewport_id(vp.id) { Some(h) } else { None } } else { None } diff --git a/src/io/mod.rs b/src/io/mod.rs index 6bef9e876..db046eccf 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -58,8 +58,12 @@ pub fn load_file(path: &Path) -> Result { match ext.as_str() { "dwg" => { - let mut reader = DwgReader::from_file(path).map_err(|e| e.to_string())?; - reader.read().map_err(|e| e.to_string()) + let mut doc = DwgReader::from_file(path) + .map_err(|e| e.to_string())? + .read() + .map_err(|e| e.to_string())?; + fix_viewport_status_flags(&mut doc); + Ok(doc) } "dxf" => { let mut doc = DxfReader::from_file(path) @@ -67,6 +71,7 @@ pub fn load_file(path: &Path) -> Result { .read() .map_err(|e| e.to_string())?; fix_dxf_dimension_rotations(&mut doc); + fix_viewport_status_flags(&mut doc); Ok(doc) } _ => Err(format!("Unsupported file format: .{ext}")), @@ -168,7 +173,25 @@ pub fn save_dxf(doc: &CadDocument, path: &Path) -> Result<(), String> { .map_err(|e| e.to_string()) } -// ── DXF post-load fixups ────────────────────────────────────────────────── +// ── Post-load fixups ────────────────────────────────────────────────────── + +/// acadrust's ViewportStatusFlags::from_bits() maps bit 0 → is_on and bit 15 → locked, +/// but the real DXF/DWG spec uses bit 15 (0x8000) → viewport on and bit 14 (0x4000) → locked. +/// Files from AutoCAD and other tools always set bit 15 for active viewports, leaving bit 0 +/// clear, so acadrust reads every such viewport as off. Correct that here after loading. +fn fix_viewport_status_flags(doc: &mut CadDocument) { + for entity in doc.entities_mut() { + if let EntityType::Viewport(vp) = entity { + let bits = vp.status.to_bits(); + // If bit 0 is not set but bit 15 is, this is an external-format viewport: + // treat bit 15 as "on" and bit 14 as "locked". + if (bits & 0x0001) == 0 && (bits & 0x8000) != 0 { + vp.status.is_on = true; + vp.status.locked = (bits & 0x4000) != 0; + } + } + } +} /// The acadrust DXF reader stores several rotation fields directly from DXF /// group code 50 in degrees, while DWG and our own creation code store radians. diff --git a/src/scene/mod.rs b/src/scene/mod.rs index fc31cbcb8..668266b45 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -196,6 +196,14 @@ impl Scene { } } + /// Returns true if this viewport ID identifies a user viewport (not the + /// sheet/overall viewport). The standard sheet viewport has id=1; id=0 + /// means "unset". External DXF exporters sometimes use id=-1 for all + /// viewports, so any negative id is also treated as a user viewport. + pub fn is_user_viewport_id(id: i16) -> bool { + id != 0 && id != 1 + } + /// Public accessor for the block-record handle of the current layout. /// Used by external callers (e.g. `commit_entity`) that need the handle /// without going through private API. @@ -325,7 +333,7 @@ impl Scene { } self.document.entities().find_map(|e| { if let EntityType::Viewport(vp) = e { - if vp.id > 1 && vp.common.owner_handle == layout_block { + if Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == layout_block { let scale = if vp.custom_scale.abs() > 1e-9 { vp.custom_scale } else if vp.view_height.abs() > 1e-9 { @@ -354,7 +362,7 @@ impl Scene { .entities() .filter_map(|e| { if let EntityType::Viewport(vp) = e { - if vp.id > 1 && vp.common.owner_handle == layout_block { + if Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == layout_block { Some((vp.common.handle, vp.id, vp.frozen_layers.clone())) } else { None @@ -365,7 +373,15 @@ impl Scene { }) .collect::>() .into_iter() - .map(|(h, id, frozen)| (h, format!("VP {}", id - 1), frozen)) + .enumerate() + .map(|(i, (h, id, frozen))| { + let label = if id > 1 { + format!("VP {}", id - 1) + } else { + format!("VP {}", i + 1) + }; + (h, label, frozen) + }) .collect(); result.sort_by_key(|(_, label, _)| label.clone()); result @@ -382,7 +398,7 @@ impl Scene { } self.document.entities().filter(|e| { if let EntityType::Viewport(vp) = e { - vp.id > 1 && vp.common.owner_handle == layout_block + Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == layout_block } else { false } @@ -786,7 +802,7 @@ impl Scene { if let EntityType::Viewport(vp) = e { Some(vp) } else { None } }) .filter(|vp| { - vp.id > 1 + Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == paper_block && vp.status.is_on && only_vp.map_or(true, |h| vp.common.handle == h) @@ -1126,7 +1142,7 @@ impl Scene { .entities() .find_map(|e| { let EntityType::Viewport(vp) = e else { return None; }; - if vp.id <= 1 || vp.common.owner_handle != layout_block || !vp.status.is_on { + if !Self::is_user_viewport_id(vp.id) || vp.common.owner_handle != layout_block || !vp.status.is_on { return None; } let hw = (vp.width / 2.0) as f32; @@ -1147,7 +1163,7 @@ impl Scene { let layout_block = self.current_layout_block_handle(); self.document.entities().find_map(|e| { let EntityType::Viewport(vp) = e else { return None; }; - if vp.id > 1 && vp.common.owner_handle == layout_block && vp.status.is_on { + if Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == layout_block && vp.status.is_on { Some(vp.common.handle) } else { None