Agamnentzar / ag-psd

Javascript library for reading and writing PSD files
Other
489 stars 66 forks source link

How to replace an image within a Smart Object? #173

Open DEV-Devound opened 4 months ago

DEV-Devound commented 4 months ago

like this: image

image

i want to replace the image within it so that photoshop does its own filters by itself. I didnt found any reference on the documentation or maybe im too lackadaisical

Agamnentzar commented 4 months ago

See section on smart objects: https://github.com/Agamnentzar/ag-psd/blob/master/README_PSD.md#smart-objects

Be aware that updating smart objects has the same issues that updating text or vector layers, PSD file keep s prerendered version of the layer and you have to update that bitmap yourself, the library will not do that for you.

DEV-Devound commented 4 months ago

See section on smart objects: https://github.com/Agamnentzar/ag-psd/blob/master/README_PSD.md#smart-objects

Be aware that updating smart objects has the same issues that updating text or vector layers, PSD file keep s prerendered version of the layer and you have to update that bitmap yourself, the library will not do that for you.

and how can i update it?

DEV-Devound commented 4 months ago

See section on smart objects: https://github.com/Agamnentzar/ag-psd/blob/master/README_PSD.md#smart-objects

Be aware that updating smart objects has the same issues that updating text or vector layers, PSD file keep s prerendered version of the layer and you have to update that bitmap yourself, the library will not do that for you.

i saw that but i cant figure out how can i insert a PNG image into that code

Agamnentzar commented 4 months ago

You need to add the file to psd.linkedFiles like this:

  psd.linkedFiles = [
    {
      "id": "20953ddb-9391-11ec-b4f1-c15674f50bc4",
      "name": "cat.png"
      "data": fileContentsAsUint8Array,
    }
  ];

and then refer to that file by the id in placedLayer field like this:

  layer.placedLayer = {
    "id": "20953ddb-9391-11ec-b4f1-c15674f50bc4", // id that matches linkedFiles ID
    "placed": "20953dda-9391-11ec-b4f1-c15674f50bc4", // unique id for this object
    "type": "raster", // one of the 'unknown', 'vector', 'raster' or 'image stack'
    "transform": [ // x, y of 4 corners of the transform box
      29,
      28,
      83,
      28,
      83,
      82,
      29,
      82
    ],
    "width": 32, // width and height of the target image
    "height": 32,
    "resolution": {
      "value": 299.99940490722656,
      "units": "Density"
    }
  };

You also need to update layer.canvas by drawing your PNG image onto it with correct transform from placedLayer.transform.

DEV-Devound commented 4 months ago

You need to add the file to psd.linkedFiles like this:

  psd.linkedFiles = [
    {
      "id": "20953ddb-9391-11ec-b4f1-c15674f50bc4",
      "name": "cat.png"
      "data": fileContentsAsUint8Array,
    }
  ];

and then refer to that file by the id in placedLayer field like this:

  layer.placedLayer = {
    "id": "20953ddb-9391-11ec-b4f1-c15674f50bc4", // id that matches linkedFiles ID
    "placed": "20953dda-9391-11ec-b4f1-c15674f50bc4", // unique id for this object
    "type": "raster", // one of the 'unknown', 'vector', 'raster' or 'image stack'
    "transform": [ // x, y of 4 corners of the transform box
      29,
      28,
      83,
      28,
      83,
      82,
      29,
      82
    ],
    "width": 32, // width and height of the target image
    "height": 32,
    "resolution": {
      "value": 299.99940490722656,
      "units": "Density"
    }
  };

You also need to update layer.canvas by drawing your PNG image onto it with correct transform from placedLayer.transform.

Id can be whatever i want?

Agamnentzar commented 4 months ago

I don't know, it seems photoshop puts GUIDS there, so I'd advise to use something in the same format, in case Photoshop expects it to be like that.

DEV-Devound commented 4 months ago

You need to add the file to psd.linkedFiles like this:

  psd.linkedFiles = [
    {
      "id": "20953ddb-9391-11ec-b4f1-c15674f50bc4",
      "name": "cat.png"
      "data": fileContentsAsUint8Array,
    }
  ];

and then refer to that file by the id in placedLayer field like this:

  layer.placedLayer = {
    "id": "20953ddb-9391-11ec-b4f1-c15674f50bc4", // id that matches linkedFiles ID
    "placed": "20953dda-9391-11ec-b4f1-c15674f50bc4", // unique id for this object
    "type": "raster", // one of the 'unknown', 'vector', 'raster' or 'image stack'
    "transform": [ // x, y of 4 corners of the transform box
      29,
      28,
      83,
      28,
      83,
      82,
      29,
      82
    ],
    "width": 32, // width and height of the target image
    "height": 32,
    "resolution": {
      "value": 299.99940490722656,
      "units": "Density"
    }
  };

You also need to update layer.canvas by drawing your PNG image onto it with correct transform from placedLayer.transform.

A friend of mine helped me with the code but when i debugged it, it didnt worked, what maybe wrong here?

const newImageBuffer = fs.readFileSync('Profile.png');
const newImageId = "20953ddb-9391-11ec-b4f1-c15674f50bc4"; // Este ID debe ser único
psd.linkedFiles = [{
    id: newImageId,
    name: "Profile.png",
    data: newImageBuffer
}];
const layerToUpdate = psd.children.find(layer => layer.name === "profile");

layerToUpdate.placedLayer.id = newImageId;
Agamnentzar commented 4 months ago

data should be Uint8Array, you also will not see the new image unless you update the smart object in Photoshop. To see the change right away you need to update layerToUpdate.canvas

DEV-Devound commented 4 months ago

data should be Uint8Array, you also will not see the new image unless you update the smart object in Photoshop. To see the change right away you need to update layerToUpdate.canvas

new code

` const newImageBuffer = fs.readFileSync('Profile.png'); const newImage8Array = new Uint8Array(newImageBuffer); console.log(newImage8Array) const newImageId = "20953ddb-9391-11ec-b4f1-c15674f50bc4"; // Este ID debe ser único psd.linkedFiles = [{ id: newImageId, name: "Profile.png", data: newImage8Array }]; const layerToUpdate = psd.children.find(layer => layer.name === "profile");

layerToUpdate.placedLayer.id = newImageId;
layerToUpdate.canvas

`

and this is the new error

Server started on port 3000 Michael Jackson Uint8Array(64757) [ 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 4, 176, 0, 0, 4, 176, 8, 6, 0, 0, 0, 235, 33, 179, 207, 0, 0, 0, 4, 103, 65, 77, 65, 0, 0, 177, 143, 11, 252, 97, 5, 0, 0, 0, 32, 99, 72, 82, 77, 0, 0, 122, 38, 0, 0, 128, 132, 0, 0, 250, 0, 0, 0, 128, 232, 0, 0, 117, 48, 0, 0, 234, 96, 0, 0, 58, 152, 0, 0, 23, 112, 156, 186, 81, 60, 0, 0, 0, 6, 98, 75, 71, ... 64657 more items ] TypeError: Cannot read properties of undefined (reading 'placedLayer')

Agamnentzar commented 4 months ago

are you sure layerToUpdate is not undefined ?

DEV-Devound commented 4 months ago

are you sure layerToUpdate is not undefined ?

thanks, that was the problem, but now its having the same error of the text layer, can something be done to solve this? I did the layerToUpdate.canvas but isnt working like intended

DEV-Devound commented 4 months ago

data should be Uint8Array, you also will not see the new image unless you update the smart object in Photoshop. To see the change right away you need to update layerToUpdate.canvas

what method can i use to update layerToUpdate.canvas?

Agamnentzar commented 4 months ago

You just assign it a new canvas, node-canvas has the same api as regular javascript canvas: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API

DEV-Devound commented 4 months ago

You just assign it a new canvas, node-canvas has the same api as regular javascript canvas: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API should this work?

`const newImageBuffer = fs.readFileSync('Profile.png'); const newImage8Array = new Uint8Array(newImageBuffer); console.log(newImage8Array) const newImageId = "20953ddb-9391-11ec-b4f1-c15674f50bc4"; // Este ID debe ser único psd.linkedFiles = [{ id: newImageId, name: "Profile.png", data: newImage8Array }]; const layerToUpdate = psd.children.find(layer => layer.name === "profile");

layerToUpdate.placedLayer.id = newImageId;
canvasUpdated = layerToUpdate

`

DEV-Devound commented 4 months ago

You just assign it a new canvas, node-canvas has the same api as regular javascript canvas: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API

do you have any code doing so? Or how can i implement what you just said into my code?

DEV-Devound commented 4 months ago

should it work like this? image

houxiaohou commented 4 months ago

You also need to update layer.canvas by drawing your PNG image onto it with correct transform from placedLayer.transform.

hi @Agamnentzar , can you explain how to draw png to layer canvas with transform please ?

Agamnentzar commented 4 months ago

@DEV-Devound You need to assign new canvas to layer.canvas:

const newCanvas = createCanvas(width, height);
// TODO: draw image to newCanvas with correct transform
layer.canvas = newCanvas;
Agamnentzar commented 4 months ago

@houxiaohou placedLayer.transform field specifies 4 corners of transformed image, there's no ready-made operation in regular canvas API that handles that. In a lot of cases transform is just move+scale+rotation, so in that situation you can just use standard operations on canvas with translate, rotate and scale functions. But it free transform or perspective transform was used then you can't do that and you'd have to write the transform yourself or find some library that does that.

Ilanox commented 1 month ago

@DEV-Devound did you figure it out?