diff --git a/crates/engine/src/cmd/layer/transform.rs b/crates/engine/src/cmd/layer/transform.rs index 78bd0247f..ad4643ae1 100644 --- a/crates/engine/src/cmd/layer/transform.rs +++ b/crates/engine/src/cmd/layer/transform.rs @@ -89,7 +89,7 @@ pub(super) fn transform_op(s: &mut Session, p: &Value) -> Result { .map(|l| { let (w, h) = effectcraft_render::source_size(&s.project, l); if fitting && (w == 0 || h == 0) { - match effectcraft_render::content_bounds(&ectx, l).filter(|b| b[2] > b[0] && b[3] > b[1]) { + match effectcraft_render::content_bounds(&ectx, l).filter(|b| b.iter().all(|v| v.is_finite()) && b[2] > b[0] && b[3] > b[1]) { Some(b) => (l.id, [b[2] - b[0], b[3] - b[1]], [(b[0] + b[2]) / 2.0, (b[1] + b[3]) / 2.0]), None => (l.id, [0.0; 2], [0.0; 2]), } @@ -127,6 +127,10 @@ pub(super) fn transform_op(s: &mut Session, p: &Value) -> Result { "fitHeight" => (ch / h, ch / h), _ => (cw / w, ch / h), }; + // A tiny shape's bounds can overflow the scale; refuse rather than write infinity. + if !(sx * 100.0).is_finite() || !(sy * 100.0).is_finite() { + return Err(EngineError::Other(format!("layer {} is too small to fit to the composition", lid.0))); + } let z = |m: &str, d: f64| tr.get(m).map(|p| p.value_at(lt).as_vec3()[2]).unwrap_or(d); let (sz, az) = (z("scale", 100.0), z("anchor", 0.0)); set(tr, "scale", KV::Vec3([sx * 100.0, sy * 100.0, sz])); diff --git a/crates/engine/src/tests/menu_cmds.rs b/crates/engine/src/tests/menu_cmds.rs index 214c3c2b9..93f81736f 100644 --- a/crates/engine/src/tests/menu_cmds.rs +++ b/crates/engine/src/tests/menu_cmds.rs @@ -1154,6 +1154,15 @@ fn fit_to_comp_fills_the_comp_with_shape_and_text_bounds() { } } +#[test] +fn fit_to_comp_refuses_a_scale_that_overflows() { + let mut s = comp(); + let a = s.execute("layer.newShape", json!({"kind": "rect", "size": [1e-305, 80]})).unwrap()["layer"].as_u64().unwrap(); + let before = s.project.clone(); + assert!(s.execute("layer.transform", json!({"layers": [a], "op": "fit"})).is_err(), "an infinite scale is refused"); + assert_eq!(s.project, before, "nothing was written"); +} + #[test] fn duplicated_and_pasted_layers_keep_their_links() { let mut s = comp();