Cap path elements in DXF import across the whole file

Squashed from #3800 onto main.
This commit is contained in:
bflatastic
2026-10-24 01:43:58 +07:00
parent 609132987b
commit 07762956d2
2 changed files with 36 additions and 0 deletions
+12
View File
@@ -25,12 +25,15 @@ const THINNEST: f64 = 0.25;
const MAX_NODES: usize = 2_000_000;
/// The most block visits (one per insert copy) an import makes, whatever they produce.
const MAX_VISITS: usize = 10_000_000;
/// The most path elements an import makes (blocks drawn as art are copied for every placement).
const MAX_ELEMENTS: usize = if cfg!(test) { 1_000_000 } else { 4_000_000 };
/// How far from the origin art may lie (points): the canvas.
pub(crate) const MAX_EXTENT: f64 = 4.0e6;
const NESTED: &str = "blocks nested more than 8 deep, or inside themselves, are left out";
const XREFS: &str = "blocks referencing other drawings (external references) are left out";
const TOO_MANY: &str = "the drawing has more than 2,000,000 objects: the rest are left out";
const TOO_MANY_POINTS: &str = "the drawing has more than 4,000,000 path points: the rest are left out";
/// What a block's art takes from the insert that places it: its colour, lineweight, linetype
/// and opacity where the art says "by block", and its layer for art on layer 0.
@@ -66,6 +69,8 @@ pub(crate) struct Build<'d, 'a> {
lw_scale: f64,
nodes: usize,
visits: usize,
/// Path elements made so far.
elements: usize,
warnings: Vec<String>,
}
@@ -122,6 +127,7 @@ impl<'d, 'a> Build<'d, 'a> {
lw_scale: 1.0,
nodes: 0,
visits: 0,
elements: 0,
warnings: vec![],
}
}
@@ -451,6 +457,12 @@ impl<'d, 'a> Build<'d, 'a> {
}
fn path(&mut self, bp: &BezPath, fill: bool, width: f64, p: &Props, map: Affine, inh: &Inherit) -> Option<Node> {
let n = bp.elements().len();
if self.elements.saturating_add(n) > MAX_ELEMENTS {
self.warn(TOO_MANY_POINTS);
return None;
}
self.elements += n;
let mut bp = bp.clone();
bp.apply_affine(map);
super::curve::bounds(&bp)?;
+24
View File
@@ -817,6 +817,30 @@ fn nested_array_inserts_of_nothing_stop_at_the_work_budget() {
assert!(r.warnings.iter().any(|w| w.contains("more than")), "{:?}", r.warnings);
}
#[test]
fn an_array_of_a_large_anonymous_block_stops_at_the_path_element_budget() {
// A 2,000-vertex polyline in *U1, placed in a 100 × 10 array: 2,000,000 path elements when
// every cell is copied, twice the budget tests import with.
let mut owned: Vec<(i32, String)> = vec![(0, "BLOCK".into()), (2, "*U1".into()), (70, "1".into()), (10, "0".into()), (20, "0".into())];
owned.extend([(0, "LWPOLYLINE".into()), (90, "2000".into()), (70, "0".into())]);
for i in 0..2000 {
owned.extend([(10, i.to_string()), (20, (i % 2).to_string())]);
}
owned.push((0, "ENDBLK".into()));
let blocks: Vec<(i32, &str)> = owned.iter().map(|(c, v)| (*c, v.as_str())).collect();
let inserts = [(0, "INSERT"), (2, "*U1"), (10, "0"), (20, "0"), (70, "100"), (71, "10"), (44, "2000"), (45, "2")];
let r = open(&drawing(&[], &[], &blocks, &inserts), &points());
let mut elements = 0;
r.document.walk(|n| {
if let NodeKind::Path { path, .. } = &n.kind {
elements += path.to_bezpath().elements().len();
}
});
assert!(elements > 0);
assert!(elements <= 1_000_000, "{elements} path elements");
assert!(r.warnings.iter().any(|w| w.contains("path points")), "{:?}", r.warnings);
}
#[test]
fn blocks_inside_themselves_are_left_out_without_hanging() {
let blocks = [