Rasterize: art longer than 32,768 pixels at the resolution is refused, not cut off

Object > Rasterize and the redrawn path of Crop Image allowed images up to
65,535 pixels a side, but the renderer draws at most 32,768. A 100 x 5569 pt
rectangle rasterized at 600 ppi came back 32,768 pixels tall, and the bottom
29% of the art was gone. Such an image is now refused like one over the
64 megapixel limit, and the art stays as it was.
This commit is contained in:
dotCooCoo
2026-11-11 02:55:59 -08:00
parent 6864cc0944
commit ee40cc10d7
3 changed files with 26 additions and 3 deletions
+4 -2
View File
@@ -115,7 +115,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Rasterize…",
["Object"],
None,
"{ppi?, background?: transparent|white|black|\"#rrggbb\" (#rrggbbaa and #rgba are refused), antiAlias?: none|art|type (text snapped to pixels)|bool, padding?|addAround?: pt (0–1000), colorModel?: \"rgb\"|\"cmyk\"|\"grayscale\"|\"bitmap\", clippingMask?: bool (a clip group with the art's outline)} replace the selection with an embedded image (PNG; in a CMYK document with its own colour model, a CMYK image of the art's inks, its transparency as its opacity mask); each defaults to document.rasterEffectsSettings; a frame of threaded text that is rasterized leaves its thread with the text it shows → {id (the image, or its clip group), width, height, warning?: \"an image couldn't be decoded and is missing from the picture\" (as raster exports say)}",
"{ppi?, background?: transparent|white|black|\"#rrggbb\" (#rrggbbaa and #rgba are refused), antiAlias?: none|art|type (text snapped to pixels)|bool, padding?|addAround?: pt (0–1000), colorModel?: \"rgb\"|\"cmyk\"|\"grayscale\"|\"bitmap\", clippingMask?: bool (a clip group with the art's outline)} replace the selection with an embedded image (PNG; in a CMYK document with its own colour model, a CMYK image of the art's inks, its transparency as its opacity mask); each defaults to document.rasterEffectsSettings; an image larger than 64 Mpx or 32,768 px a side is refused; a frame of threaded text that is rasterized leaves its thread with the text it shows → {id (the image, or its clip group), width, height, warning?: \"an image couldn't be decoded and is missing from the picture\" (as raster exports say)}",
has_selection,
rasterize
),
@@ -670,7 +670,9 @@ fn render_pixels(
opts: &vectorcraft_render::RenderOptions,
color: RasterColorModel,
) -> Result<(vectorcraft_render::Rendered, Option<&'static str>)> {
if region.width() * region.height() * scale * scale > MAX_PIXELS || region.width() * scale > 65535.0 || region.height() * scale > 65535.0 {
// No side longer than the renderer draws: it would cut the image off there.
let side = f64::from(vectorcraft_render::MAX_RASTER_SIDE);
if region.width() * region.height() * scale * scale > MAX_PIXELS || region.width() * scale > side || region.height() * scale > side {
return Err(EngineError::Other("image would be too large; lower the resolution".into()));
}
let mut r = vectorcraft_render::Renderer::new();
+20
View File
@@ -1461,6 +1461,26 @@ fn rasterize_replaces_selection_with_image() {
assert_eq!(selected(&s), vec![img]);
}
/// Rasterize refuses art that would be longer than the renderer draws (32,768 pixels a side), and
/// the art stays as it was; at a resolution that fits, the image covers all of it.
#[test]
fn rasterize_refuses_an_image_longer_than_the_renderer_draws() {
let mut s = session();
let a = rect(&mut s, 10.0, 20.0, 100.0, 5569.0);
sel(&mut s, &[a]);
let vb = s.doc().unwrap().doc.bounds_of(&[a], true).unwrap();
let before = (undo_len(&s), node(&s, a));
let e = s.execute("object.rasterize", &json!({"ppi": 600, "padding": 0})).unwrap_err().to_string();
assert!(e.contains("too large"), "{e}");
assert_eq!((undo_len(&s), node(&s, a)), before);
let img = id_of(&s.execute("object.rasterize", &json!({"ppi": 400, "padding": 0})).unwrap());
let n = node(&s, img);
let NodeKind::Image(im) = &n.kind else { panic!() };
assert!(im.height <= vectorcraft_render::MAX_RASTER_SIDE, "{} pixels", im.height);
let b = n.geometric_bounds().unwrap();
assert!(close(b.y0, vb.y0) && b.y1 >= vb.y1, "{b:?} covers {vb:?}");
}
/// Rasterize refuses a resolution given as text or as another value that is not a number, and the
/// selected art stays as it was.
#[test]
+2 -1
View File
@@ -5169,7 +5169,8 @@ fills or strokes carry effects is written as one piece per fill and stroke, so o
When an image in the art can't be decoded, `object.rasterize` and `object.cropImage` (without `trim`) make their image
without it, and their results carry the `warning` that Flatten Transparency gives. `object.rasterize` takes each frame
of threaded text that it rasterizes out of its thread with the text it shows, as `type.createOutlines` does (see
Threading text from the in and out ports), and the other frames keep the text they hold.
Threading text from the in and out ports), and the other frames keep the text they hold. `object.rasterize`
refuses an image larger than 64 million pixels or longer than 32,768 pixels a side, and the art stays as it was.
```json
{"name":"run_command","arguments":{"command":"document.rasterEffectsSettings","params":{"resolution":"high","background":"white","addAround":36}}}