HP-GL export: the writer refuses a bad curve tolerance or more than 255 pens

A tolerance outside 0.01 to 10 points, or one that is not finite, and a pen table of more than 255 colors are errors instead of being clamped or cut short. document.export already refuses both.
This commit is contained in:
dotCooCoo
2026-11-12 00:21:44 -08:00
parent 2c9f5a6a11
commit 87ab5b445f
2 changed files with 27 additions and 4 deletions
+11 -4
View File
@@ -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| {
+16
View File
@@ -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)]);