mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-15 21:57:04 +00:00
Place: a plain image replacing a clipped PSD keeps its scale and rotation
Replace measured the image inside a PSD or PSB file's clip group only when the new file brought its own clipping path. A plain image placed with `replace` over such a clip group took only the group's center and came in at 100%, losing the image's scale and rotation. The image in the group is now measured whenever the new art is an image, clipped or not.
This commit is contained in:
@@ -386,10 +386,32 @@ fn replacing_a_clipped_psd_keeps_its_scale() {
|
||||
assert_eq!((r["width"].as_f64(), r["height"].as_f64()), (Some(200.0), Some(100.0)), "{r}");
|
||||
let (anchors, _, _) = clip_group(&s.doc().unwrap().doc, NodeId(r["ids"][0].as_u64().unwrap()));
|
||||
assert_eq!(anchors, [[200.0, 150.0], [300.0, 250.0], [100.0, 250.0]]);
|
||||
// Other art replacing the clip group takes its centre only, as before.
|
||||
// A plain image replacing the clip group keeps its scale too.
|
||||
let plain = vectorcraft_format::base64_encode(&red_psd(&[]));
|
||||
let r = s.execute("file.place", &json!({"name": "plain.psd", "dataBase64": plain, "replace": true})).unwrap();
|
||||
assert_eq!((r["width"].as_f64(), r["height"].as_f64()), (Some(100.0), Some(50.0)), "{r}");
|
||||
assert_eq!((r["width"].as_f64(), r["height"].as_f64()), (Some(200.0), Some(100.0)), "{r}");
|
||||
}
|
||||
|
||||
/// A plain image replacing a clipped PSD file takes the clipped image's scale and rotation.
|
||||
#[test]
|
||||
fn a_plain_image_replacing_a_clipped_psd_keeps_its_rotation() {
|
||||
let data = vectorcraft_format::base64_encode(&red_psd(&triangle_resources()));
|
||||
let plain = vectorcraft_format::base64_encode(&red_psd(&[]));
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 400, "height": 400})).unwrap();
|
||||
let r = s.execute("file.place", &json!({"name": "cut.psd", "dataBase64": data, "at": [200, 200]})).unwrap();
|
||||
let group = NodeId(r["ids"][0].as_u64().unwrap());
|
||||
s.execute("object.scale", &json!({"sx": 200, "sy": 150})).unwrap();
|
||||
s.execute("object.rotate", &json!({"angle": 30})).unwrap();
|
||||
let xf = |s: &Session, id: NodeId| match s.doc().unwrap().doc.node(id).map(|n| &n.kind) {
|
||||
Some(NodeKind::Image(im)) => im.xf.as_coeffs(),
|
||||
other => panic!("not an image: {other:?}"),
|
||||
};
|
||||
let (_, _, image) = clip_group(&s.doc().unwrap().doc, group);
|
||||
let before = xf(&s, image);
|
||||
let r = s.execute("file.place", &json!({"name": "plain.psd", "dataBase64": plain, "replace": true})).unwrap();
|
||||
let after = xf(&s, NodeId(r["ids"][0].as_u64().unwrap()));
|
||||
assert!(after.iter().zip(before).all(|(a, b)| (a - b).abs() < 1e-9), "{after:?} != {before:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -489,14 +489,14 @@ fn at_param(p: &Value) -> Result<Option<Point>> {
|
||||
|
||||
/// The transform that puts new art with the natural box `natural` where `old` is: an image's scale
|
||||
/// and rotation relative to its 100% size (its resolution's) and its centre are kept (with
|
||||
/// `image_xf`), also for an image alone in a clip group when the new art is an image clipped by
|
||||
/// its file's clipping path (`clipped`); other objects keep their centre.
|
||||
fn replace_xf(d: &Document, old: &Node, natural: Rect, image_xf: bool, clipped: bool) -> Affine {
|
||||
/// `image_xf`), also for an image alone in a clip group when the new art is an image, clipped by
|
||||
/// its file's clipping path or not (`new_image`); other objects keep their centre.
|
||||
fn replace_xf(d: &Document, old: &Node, natural: Rect, image_xf: bool, new_image: bool) -> Affine {
|
||||
// An image alone in a clip group (as a PSD or PSB file's clipping path places it,
|
||||
// [`fileio::file_clipped`]) is measured as the image when the new art brings its own clip
|
||||
// group; a DCS file's plates are not.
|
||||
// [`fileio::file_clipped`]) is measured as the image when the new art is an image; a DCS
|
||||
// file's plates are not.
|
||||
let image = match &old.kind {
|
||||
NodeKind::Group { children, clip: true } if clipped => match &children[..] {
|
||||
NodeKind::Group { children, clip: true } if new_image => match &children[..] {
|
||||
[path, image] if matches!(path.kind, NodeKind::Path { clipping: true, .. }) => match &image.kind {
|
||||
NodeKind::Image(im) if d.images.get(&im.key).is_none_or(|b| b.separated().is_none()) => image,
|
||||
_ => old,
|
||||
@@ -675,7 +675,7 @@ fn apply(s: &mut Session, read: PlaceRead) -> Result<Value> {
|
||||
}
|
||||
let natural = loaded.natural;
|
||||
let m = match (old_node, rect) {
|
||||
(Some(o), _) => replace_xf(&st.doc, o, natural, !is_text, matches!(loaded.art, Art::Image(_, Some(_)))),
|
||||
(Some(o), _) => replace_xf(&st.doc, o, natural, !is_text, matches!(loaded.art, Art::Image(..))),
|
||||
(None, Some(r)) => fit(natural, r),
|
||||
// Its artboard's bottom-left corner on the target's.
|
||||
(None, None) if let (Some(src), Some(to)) = (loaded.board, target) => Affine::translate((to.x0 - src.x0, to.y1 - src.y1)),
|
||||
|
||||
+2
-1
@@ -8356,7 +8356,8 @@ that can be edited or released. `file.place` answers `image`, the id of the imag
|
||||
[import options help page](https://helpx.adobe.com/illustrator/desktop/add-and-import-files/import-from-other-apps/photoshop-import-options.html)
|
||||
says that a file imported as one flattened image keeps the document's clipping path when it has one. `links.update`
|
||||
and `links.relink` read the file's pixels again and leave the clip group and its clipping path as they are. Files that name no clipping path, or name a path
|
||||
they don't have, come in as a plain image.
|
||||
they don't have, come in as a plain image. `file.place` with `replace` on such a clip group puts a new image, clipped or
|
||||
not, at the scale and rotation of the image in the group.
|
||||
|
||||
`document.open` converts a PSD or PSB file's layers to objects. In
|
||||
[PSD import options](https://helpx.adobe.com/illustrator/desktop/add-and-import-files/import-from-other-apps/photoshop-import-options.html),
|
||||
|
||||
Reference in New Issue
Block a user