io: only the first CADCraft xdata string marks the PAPERVIEW viewport (#253)

dxf_ext::is_paper_view (#225) matched any 1000 string in the CADCraft xdata, but later strings
are layer names (VPFROZEN, VPCOLORS). A real viewport with a frozen or recoloured layer named
PAPERVIEW was therefore dropped on read. Only the first string, where the writer puts the
marker, counts now. Regression test in tests/paperview_marker.rs.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Valentin Klein
2026-10-10 15:33:56 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 3ef0edaed9
commit 1bd1c5cfa2
2 changed files with 44 additions and 2 deletions
+4 -2
View File
@@ -310,9 +310,11 @@ pub(crate) fn read_dstyle(tags: &[Tag], styles: &HashMap<String, String>, blocks
/// CADCraft keeps that viewport implicit, so the reader drops it.
pub(crate) const PAPER_VIEW: &str = "PAPERVIEW";
/// True for a VIEWPORT record carrying the [`PAPER_VIEW`] marker.
/// True for a VIEWPORT record carrying the [`PAPER_VIEW`] marker as the first string of its CADCraft
/// xdata, where the writer puts it. Later strings are layer names (VPFROZEN, VPCOLORS), so a layer
/// called PAPERVIEW does not count.
pub(crate) fn is_paper_view(tags: &[Tag]) -> bool {
xdata(tags, APP).iter().any(|t| t.code == 1000 && t.str().trim().eq_ignore_ascii_case(PAPER_VIEW))
xdata(tags, APP).iter().find(|t| t.code == 1000).is_some_and(|t| t.str().trim().eq_ignore_ascii_case(PAPER_VIEW))
}
/// Snap kinds in CADCraft's ASSOC xdata.
+40
View File
@@ -0,0 +1,40 @@
//! The DXF reader drops the paper-space viewport the writer adds (marked `1000 PAPERVIEW` in
//! CADCraft xdata) but keeps a real viewport whose frozen or recoloured layer is named PAPERVIEW.
use cadcraft_color::Color;
use cadcraft_doc::*;
use cadcraft_geom::{Vec2, Vec3};
fn viewport(id: u32, frozen_layers: Vec<String>, layer_colors: Vec<(String, Color)>) -> EntityKind {
EntityKind::Viewport(Viewport {
center: Vec3::new(50.0, 40.0, 0.0),
width: 80.0,
height: 60.0,
view_center: Vec2::new(10.0, 10.0),
view_height: 50.0,
id,
locked: false,
frozen_layers,
layer_colors,
})
}
fn viewports(l: &Layout) -> Vec<Viewport> {
l.entities.iter().filter_map(|e| if let EntityKind::Viewport(v) = &e.kind { Some(v.clone()) } else { None }).collect()
}
#[test]
fn viewport_with_a_layer_named_paperview_survives_dxf_roundtrip() {
let mut d = Drawing::new_metric();
d.ensure_layer("PAPERVIEW");
let sheet = Space::Paper(d.layouts[0].name.clone());
d.add(&sheet, Common::default(), viewport(2, vec!["PAPERVIEW".into()], vec![])).unwrap();
d.add(&sheet, Common::default(), viewport(3, vec![], vec![("PAPERVIEW".into(), Color::Index(1))])).unwrap();
let text = cadcraft_io::write_dxf(&d);
let back = cadcraft_io::read_dxf(text.as_bytes()).unwrap();
let vps = viewports(&back.layouts[0]);
assert_eq!(vps.iter().map(|v| v.id).collect::<Vec<_>>(), [2, 3], "only the added sheet viewport is dropped");
assert_eq!(vps[0].frozen_layers, ["PAPERVIEW"]);
assert_eq!(vps[1].layer_colors, [("PAPERVIEW".to_string(), Color::Index(1))]);
}