diff --git a/crates/cad/src/hpgl/write.rs b/crates/cad/src/hpgl/write.rs index 22773ac68b..80ac40f866 100644 --- a/crates/cad/src/hpgl/write.rs +++ b/crates/cad/src/hpgl/write.rs @@ -142,9 +142,11 @@ impl Origin { #[derive(Clone, Debug)] pub struct HpglOptions { pub version: Version, - /// How far a flattened curve may lie from the curve, points ([`TOLERANCE_RANGE`]). + /// How far a flattened curve may lie from the curve, points ([`TOLERANCE_RANGE`]; a value + /// outside it is an error). pub tolerance: f64, - /// The pen table: pen 1's color first. Empty: a pen for each color the art draws in. + /// The pen table: pen 1's color first, up to [`MAX_PENS`] (more is an error). Empty: a pen for + /// each color the art draws in. pub pens: Vec<[u8; 3]>, pub origin: Origin, /// Draw each pen's paths nearest first, instead of in paint order. @@ -184,8 +186,13 @@ pub fn export_regions(doc: &Document, opts: &HpglOptions, regions: &[Rect]) -> R if regions.iter().any(|r| ![r.x0, r.y0, r.x1, r.y1].iter().all(|v| v.is_finite()) || r.width() <= 0.0 || r.height() <= 0.0) { return Err("the export region is empty".into()); } - let tolerance = - if opts.tolerance.is_finite() { opts.tolerance.clamp(*TOLERANCE_RANGE.start(), *TOLERANCE_RANGE.end()) } else { DEFAULT_TOLERANCE }; + if !TOLERANCE_RANGE.contains(&opts.tolerance) { + return Err("the curve tolerance must be from 0.01 to 10 points".into()); + } + if opts.pens.len() > MAX_PENS { + return Err("the pen table holds more than 255 pens".into()); + } + let tolerance = opts.tolerance; // Layers the plot leaves out go first: baking can wrap one in art that would be drawn (its // crop marks). let shown = plotted(&doc.layers, false).map(|layers| { diff --git a/crates/cad/src/hpgl/write/tests.rs b/crates/cad/src/hpgl/write/tests.rs index 7ef5105340..0917cf02c9 100644 --- a/crates/cad/src/hpgl/write/tests.rs +++ b/crates/cad/src/hpgl/write/tests.rs @@ -441,6 +441,22 @@ fn an_empty_or_zero_length_plot_has_no_moves() { assert!(export(&d, &nan).is_err()); } +/// A tolerance outside its range and a pen table of more than 255 pens are errors, not changed. +#[test] +fn bad_options_are_errors() { + let d = three(); + for tolerance in [0.0, 0.009, 10.01, f64::NAN, f64::INFINITY] { + let o = HpglOptions { tolerance, ..opts(&d) }; + assert_eq!(export(&d, &o).err().as_deref(), Some("the curve tolerance must be from 0.01 to 10 points"), "{tolerance}"); + } + for tolerance in [0.01, 10.0] { + assert!(export(&d, &HpglOptions { tolerance, ..opts(&d) }).is_ok(), "{tolerance}"); + } + let o = HpglOptions { pens: vec![[0, 0, 0]; MAX_PENS + 1], ..opts(&d) }; + assert_eq!(export(&d, &o).err().as_deref(), Some("the pen table holds more than 255 pens")); + assert!(export(&d, &HpglOptions { pens: vec![[0, 0, 0]; MAX_PENS], ..opts(&d) }).is_ok()); +} + #[test] fn art_past_the_plotter_range_is_cut_off() { let d = doc_with(|d| vec![stroked(d, line((0.0, 50.0), (1e9, 50.0)), Color::BLACK)]);