diff --git a/contributors/people.toml b/contributors/people.toml
--- a/contributors/people.toml
+++ b/contributors/people.toml
@@ -91,3 +91,6 @@ sync_github_name = false
 real_name = "Robert Lee"
 sync_github_name = true
 
+[people.assamaral]
+real_name = "assamaral"
+sync_github_name = false
diff --git a/crates/io/src/dxf_read.rs b/crates/io/src/dxf_read.rs
--- a/crates/io/src/dxf_read.rs
+++ b/crates/io/src/dxf_read.rs
@@ -120,6 +120,7 @@ fn read_budgeted(bytes: &[u8], mut table_cells_left: usize) -> Result<Drawing> {
     let (paper_block_for, first_layout) = setup_layouts(&mut d, &mut layout_objs, &rx);
     place_blocks(&mut d, blocks, &mut rx, &paper_block_for, &first_layout);
     place_entities(&mut d, entities, &rx, &paper_block_for, &first_layout);
+    number_unknown_viewport_ids(&mut d);
     apply_sortents(&mut d, &objs.sortents, &rx.brs, &paper_block_for, &first_layout);
     objs.apply(&mut d, &rx);
     crate::dxf_image::resolve(&secs, &mut d);
@@ -137,6 +138,33 @@ fn read_budgeted(bytes: &[u8], mut table_cells_left: usize) -> Result<Drawing> {
     Ok(d)
 }
 
+/// Viewport ids another program left unknown (ZWCAD writes 69 = -1, read as 0). A layout whose
+/// viewports all lack an id gets them numbered in file order, as DWG readers do; when only
+/// some lack one and none is 1, the first viewport is the sheet and gets id 1. Either way the
+/// sheet keeps id 1, so saving does not add a second sheet viewport ahead of it (#1463).
+fn number_unknown_viewport_ids(d: &mut Drawing) {
+    for l in d.layouts.iter_mut() {
+        let mut vps: Vec<Entity> = l.entities.iter().filter(|e| matches!(&e.kind, EntityKind::Viewport(_))).map(|e| (**e).clone()).collect();
+        let id = |e: &Entity| if let EntityKind::Viewport(v) = &e.kind { v.id } else { 0 };
+        if vps.iter().all(|e| id(e) == 0) {
+            for (i, e) in vps.iter_mut().enumerate() {
+                if let EntityKind::Viewport(v) = &mut e.kind {
+                    v.id = i as u32 + 1;
+                }
+            }
+        } else if vps.iter().all(|e| id(e) != 1) && vps.first().is_some_and(|e| id(e) == 0) {
+            if let Some(EntityKind::Viewport(v)) = vps.first_mut().map(|e| &mut e.kind) {
+                v.id = 1;
+            }
+        } else {
+            continue;
+        }
+        for e in vps {
+            l.entities.replace(e);
+        }
+    }
+}
+
 /// An entity from the groups of a DXF record of type `kind` (without its `0` group), as the
 /// reader makes it. AutoLISP's `entmake` and `entmod` are built on it.
 pub fn entity_from_groups(kind: &str, tags: &[Tag]) -> Option<(Common, EntityKind)> {
diff --git a/crates/io/src/dxf_read/entities.rs b/crates/io/src/dxf_read/entities.rs
--- a/crates/io/src/dxf_read/entities.rs
+++ b/crates/io/src/dxf_read/entities.rs
@@ -232,7 +232,8 @@ pub(super) fn entity_with_table_budget(kind: &str, tags: &[Tag], table_cells_lef
             height: t.fd(41, 1.0),
             view_center: Vec2::new(t.fd(12, 0.0), t.fd(22, 0.0)),
             view_height: t.fd(45, 1.0),
-            id: t.i(69).unwrap_or(0) as u32,
+            // ZWCAD writes -1 (unknown): 0 here, numbered in order by the reader (#1463).
+            id: t.i(69).filter(|i| *i > 0).unwrap_or(0) as u32,
             locked: t.i(90).unwrap_or(0) & 16384 != 0,
             frozen_layers: crate::dxf_ext::read_frozen(tags),
             layer_colors: crate::dxf_ext::read_layer_colors(tags),
diff --git a/crates/io/src/tests.rs b/crates/io/src/tests.rs
--- a/crates/io/src/tests.rs
+++ b/crates/io/src/tests.rs
@@ -604,6 +604,63 @@ fn viewport_frozen_layers_roundtrip() {
     assert!(frozen.contains(&(3, Vec::new())), "{frozen:?}");
 }
 
+/// `Layout1` with viewports 1 (the sheet), 2 and 3, written to DXF and read back with the id (69)
+/// of every viewport `blank` names replaced by -1 (ZWCAD's "unknown"): the ids and widths read.
+fn viewport_ids_read_back(blank: impl Fn(&str) -> bool) -> (Drawing, Vec<(u32, f64)>) {
+    let mut d = sample();
+    let paper = Space::Paper("Layout1".into());
+    for (id, w) in [(1, 420.0), (2, 8.0), (3, 4.0)] {
+        let vp = Viewport {
+            center: Vec3::new(5.0, 4.0, 0.0),
+            width: w,
+            height: 6.0,
+            view_center: Vec2::new(5.0, 4.0),
+            view_height: 6.0,
+            id,
+            ..Viewport::default()
+        };
+        d.add(&paper, Common::default(), EntityKind::Viewport(vp)).unwrap();
+    }
+    let text = write_dxf(&d);
+    let mut lines: Vec<String> = text.lines().map(String::from).collect();
+    for i in (0..lines.len() - 1).step_by(2) {
+        if lines[i].trim() == "69" && blank(lines[i + 1].trim()) {
+            lines[i + 1] = "-1".into();
+        }
+    }
+    let back = read_dxf(lines.join("\n").as_bytes()).unwrap();
+    let ids =
+        back.layouts[0].entities.iter().filter_map(|e| if let EntityKind::Viewport(v) = &e.kind { Some((v.id, v.width)) } else { None }).collect();
+    (back, ids)
+}
+
+/// ZWCAD writes every VIEWPORT with id (69) -1: they are numbered in file order, the first being
+/// the paper-space viewport, so saving adds no second sheet viewport (#1463).
+#[test]
+fn viewport_ids_unknown_are_numbered_in_order() {
+    let (back, ids) = viewport_ids_read_back(|_| true);
+    assert_eq!(ids, [(1, 420.0), (2, 8.0), (3, 4.0)]);
+    assert!(!write_dxf(&back).contains("PAPERVIEW"), "the layout already has its paper-space viewport");
+}
+
+/// A layout that mixes known ids with -1 is left alone: the known ids stay, the unknown one reads
+/// as 0 (not numbered), as before #1463.
+#[test]
+fn viewport_ids_mixed_are_kept() {
+    let (back, ids) = viewport_ids_read_back(|v| v == "2");
+    assert_eq!(ids, [(1, 420.0), (0, 8.0), (3, 4.0)]);
+    assert!(!write_dxf(&back).contains("PAPERVIEW"), "viewport 1 is still the sheet");
+}
+
+/// Only the sheet's id is unknown: no viewport has id 1, so the first one, the sheet, gets it
+/// and saving adds no second sheet (#1463).
+#[test]
+fn viewport_ids_unknown_sheet_becomes_one() {
+    let (back, ids) = viewport_ids_read_back(|v| v == "1");
+    assert_eq!(ids, [(1, 420.0), (2, 8.0), (3, 4.0)]);
+    assert!(!write_dxf(&back).contains("PAPERVIEW"), "the first viewport is the sheet");
+}
+
 /// A layout with viewport 2 frozen on `Walls` and `A B` (both real layers), and its written DXF as
 /// (code, value) pairs.
 fn frozen_vp_dxf() -> (Drawing, Vec<(String, String)>) {
diff --git a/docs/file-format-parity.md b/docs/file-format-parity.md
--- a/docs/file-format-parity.md
+++ b/docs/file-format-parity.md
@@ -141,6 +141,7 @@ our output in AutoCAD black-box.
 | Date | Change | Summary |
 |---|---|---|
 | 2026-10-25 | minor | LAYOUT SAveas (`layout.saveas`) writes a layout as a drawing template: its page setup and paper space objects, no model space objects or other layouts, and only the named objects they use |
+| 2026-10-24 | minor | DXF read: a layout whose VIEWPORTs all carry id (69) -1 (ZWCAD writes no ids) gets them numbered in file order, the first being the paper-space viewport, so a save no longer adds a second sheet viewport that turns the real one into a model viewport and inflates the drawing extents (#1463) |
 | 2026-10-22 | minor | DXF/DWG save: angular, diametric and radial dimensions get standard DIMASSOC objects (one centre reference to the circle for radial/diametric, the definition points for angular), so AutoCAD follows their geometry; the standard objects of those kinds are read back too, without CADCraft's xdata (#1651) |
 | 2026-10-19 | minor | DXF/DWG save: angular dimension block text honors DIMDSEP, including per-dimension overrides (#1646) |
 | 2026-10-19 | minor | A file's own arrowhead blocks with standard names (AutoCAD's `_Dot`, `_DotSmall`), named only by a dimension style, are kept and saved as they were instead of being replaced by CADCraft's solids; PURGE keeps arrowhead blocks dimension styles name |
