Copy the vanilla engine from dwg-dxf-viewer-sample verbatim (#1): - src/engine/viewer2d/* (Viewer2D.js incl. the 2.5D view, parsers, acadrust-dwg WASM, cadSpaces, slugText, linParser/linData, fixDwgKoreanText, ...) - src/engine/{layerPanel,panelUtils,propertyInspector,optionsStore}.ts The relative layout is kept (engine/viewer2d next to the panel files), so not even import paths changed; `diff -r --strip-trailing-cr` against the sample is empty. public/ (NanumGothic, samples, licenses, module map) is copied as is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
112 lines
5.5 KiB
Diff
112 lines
5.5 KiB
Diff
--- a/src/io/dwg/dwg_stream_readers/object_reader/entities.rs
|
|
+++ b/src/io/dwg/dwg_stream_readers/object_reader/entities.rs
|
|
@@ -1682,20 +1682,49 @@
|
|
MLineData { scale_factor, justification, start_point, normal, openclosed, lines_in_style, vertex_count, vertices, style_handle }
|
|
}
|
|
|
|
+/// Cap an array count against the bits actually left in the object's main
|
|
+/// stream instead of the blanket [`MAX_ARRAY_COUNT`].
|
|
+///
|
|
+/// MESH is the one entity whose arrays legitimately run past 100 000 items: a
|
|
+/// road-surface mesh in a civil drawing carries hundreds of thousands of
|
|
+/// vertices. Clamping those to the blanket cap does not merely truncate the
|
|
+/// mesh — the reader then continues from the wrong bit offset, so the face,
|
|
+/// edge and crease lists that follow decode as garbage (observed: a 100 000
|
|
+/// vertex clamp left a 14-face mesh with face sizes `[64, 0, 64, 23, 0, …]`).
|
|
+/// Bounding by the remaining bits keeps the corrupt-data protection — no
|
|
+/// unbounded allocation is possible — without mis-parsing a valid drawing.
|
|
+///
|
|
+/// `min_bits_per_item` is the shortest legal encoding of one element: a
|
|
+/// BitDouble is 2 bits at minimum (the 0.0 / 1.0 forms), so a 3BD vertex is 6
|
|
+/// and a BitLong is 2.
|
|
+fn stream_bounded_count(reader: &DwgMergedReader, raw: i32, min_bits_per_item: i64) -> i32 {
|
|
+ let raw = raw.max(0) as i64;
|
|
+ let total_bits = (reader.main().data_len() as i64).saturating_mul(8);
|
|
+ // The handle stream follows the main stream; anything past its start is not
|
|
+ // array data. handle_start() is 0 when the framing did not supply it.
|
|
+ let handle_start = reader.handle_start();
|
|
+ let end_bits = if handle_start > 0 { handle_start.min(total_bits) } else { total_bits };
|
|
+ let remaining = (end_bits - reader.position_in_bits()).max(0);
|
|
+ raw.min(remaining / min_bits_per_item.max(1)).min(i32::MAX as i64) as i32
|
|
+}
|
|
+
|
|
pub fn read_mesh(reader: &mut DwgMergedReader) -> MeshData {
|
|
let version = reader.read_bit_short();
|
|
let blend_crease = reader.read_bit();
|
|
let subdivision_level = reader.read_bit_long();
|
|
|
|
- let num_verts = safe_count(reader.read_bit_long());
|
|
+ let raw_verts = reader.read_bit_long();
|
|
+ let num_verts = stream_bounded_count(reader, raw_verts, 6);
|
|
let mut vertices = Vec::with_capacity(num_verts as usize);
|
|
for _ in 0..num_verts { vertices.push(reader.read_3bit_double()); }
|
|
|
|
- let total_face_data = safe_count(reader.read_bit_long());
|
|
+ let raw_face_data = reader.read_bit_long();
|
|
+ let total_face_data = stream_bounded_count(reader, raw_face_data, 2);
|
|
let mut faces = Vec::new();
|
|
let mut i = 0;
|
|
while i < total_face_data {
|
|
- let n = safe_count(reader.read_bit_long());
|
|
+ let raw_n = reader.read_bit_long();
|
|
+ let n = stream_bounded_count(reader, raw_n, 2);
|
|
i += 1;
|
|
let mut face = Vec::new();
|
|
for _ in 0..n {
|
|
@@ -1705,7 +1734,8 @@
|
|
faces.push(face);
|
|
}
|
|
|
|
- let num_edges = safe_count(reader.read_bit_long());
|
|
+ let raw_edges = reader.read_bit_long();
|
|
+ let num_edges = stream_bounded_count(reader, raw_edges, 4);
|
|
let mut edges = Vec::with_capacity(num_edges as usize);
|
|
for _ in 0..num_edges {
|
|
let s = reader.read_bit_long();
|
|
@@ -1713,7 +1743,8 @@
|
|
edges.push((s, e));
|
|
}
|
|
|
|
- let num_creases = safe_count(reader.read_bit_long());
|
|
+ let raw_creases = reader.read_bit_long();
|
|
+ let num_creases = stream_bounded_count(reader, raw_creases, 2);
|
|
let mut crease_values = Vec::with_capacity(num_creases as usize);
|
|
for _ in 0..num_creases { crease_values.push(reader.read_bit_double()); }
|
|
|
|
@@ -2350,14 +2381,31 @@
|
|
pub fn read_ole2frame(reader: &mut DwgMergedReader, version: DwgVersion) -> Ole2FrameData {
|
|
let ver = reader.read_bit_short();
|
|
let mode = if version.r2000_plus() { reader.read_bit_short() } else { 0 };
|
|
- // OLE binary data can be very large (embedded images/documents),
|
|
- // so don't use safe_count (100 KB cap). Use a generous 10 MB cap instead.
|
|
let data_len = (reader.read_bit_long().max(0) as usize).min(10_000_000);
|
|
let data = reader.read_bytes(data_len);
|
|
let trailing_byte = if version.r2000_plus() { reader.read_byte() } else { 3 };
|
|
- let upper_left = reader.read_3bit_double();
|
|
- let lower_right = reader.read_3bit_double();
|
|
+
|
|
+ let mut upper_left = Vector3::new(0.0, 0.0, 0.0);
|
|
+ let mut lower_right = Vector3::new(0.0, 0.0, 0.0);
|
|
+
|
|
+ // Extract 4-corner coordinates stored in the OLE binary data header starting at offset 2
|
|
+ if data.len() >= 66 {
|
|
+ let ul_x = f64::from_le_bytes(data[2..10].try_into().unwrap());
|
|
+ let ul_y = f64::from_le_bytes(data[10..18].try_into().unwrap());
|
|
+ let ul_z = f64::from_le_bytes(data[18..26].try_into().unwrap());
|
|
+ let lr_x = f64::from_le_bytes(data[50..58].try_into().unwrap());
|
|
+ let lr_y = f64::from_le_bytes(data[58..66].try_into().unwrap());
|
|
+ let lr_z = if data.len() >= 74 { f64::from_le_bytes(data[66..74].try_into().unwrap()) } else { 0.0 };
|
|
+
|
|
+ if ul_x.is_finite() && ul_y.is_finite() && lr_x.is_finite() && lr_y.is_finite()
|
|
+ && (ul_x != 0.0 || ul_y != 0.0 || lr_x != 0.0 || lr_y != 0.0) {
|
|
+ upper_left = Vector3::new(ul_x, ul_y, if ul_z.is_finite() { ul_z } else { 0.0 });
|
|
+ lower_right = Vector3::new(lr_x, lr_y, if lr_z.is_finite() { lr_z } else { 0.0 });
|
|
+ }
|
|
+ }
|
|
+
|
|
let ole_type = if version.r2000_plus() { reader.read_byte() } else { 2 };
|
|
+
|
|
Ole2FrameData { version: ver, mode, data, trailing_byte, upper_left, lower_right, ole_type }
|
|
}
|
|
|