UXP Imaging - Export pixels as a png file (or base64 data)

Hello all,
I’m currently playing with the imaging API and I’m facing an issue I do not understand. After retrieving a PhotoshopImageData with the getPixels method, I am currently trying to save the related data as a png file (but the png file is currently inoperable).

 const pixels = await imaging.getPixels({
    documentID: app.activeDocument.id,
    layerID: layer.id,
    applyAlpha: false
})

const data = await pixels.imageData.getData()
const file = await fs.getFileForSaving("test.png", { types: [ "png" ]});
await file.write(data, {
    format: storage.formats.binary
})

Do you know how to fix it ? (if not, is it possible to convert it to a base64 data uri?)

Just to mention that I am able to display this image within the photoshop panel with:

const imageBlob = new ImageBlob(data, pixels.imageData)
const dataUrl = URL.createObjectURL(imageBlob);
const myImage = document.getElementById("myImage")
myImage.src = dataUrl

I also tried with the exportSelectionAsFileTypePressed action (batchPlay), but that do not seem to be supported anymore (photoshop 24.7).

As a side note, fixing this issue will prevent me from using document/layer methods (crop, bringToFront, suspendHistory…) to properly export layers as png files.

From what I see. The bitmap is not converted to PNG. You should convert bitmap to PNG in first place and then save it. It will not do the conversion automatically.

How would you do that?

(Saving it as a .bmp file does not change anything)

BMP has a header that you do not save. You would need to save it and open it as raw file and then in Photoshop tell what byte order it is.

How would you do that? Maybe in browser you would use Canvas HTMLCanvasElement: toDataURL() method - Web APIs | MDN

But would it work in UXP? No idea.

That is the beauty of UXP, HTMLCanvasElement: toDataURL() is not supported :smiley:

Ok, add it here please :smiley: HTML Canvas API - research

Ok, will do.

Photoshop refuses to open the generated bmp file. Do you know of an alternative solution?

BMP has a header that you do not save. That is why it can’t open. Try File → Open as → Raw.
Since it does not have header you will need to fill dialog correctly.

I already tried, that is not working. Here is the file that I created (it is just a Lorem Ipsum text): https://file.io/89VgdgAKNEoE

Have you considered creating a blank Photoshop document, dropping your image data there and then do a document save (all programmatically of course)? Photoshop does have the smarts to convert to different file types for its own documents as part of a save/export, but as far as I know ‘fs’ is only meant to write raw binary or text data and you’d be on the hook to essentially compose the PNG file with file header etc. like @Jarda mentioned.

This line here only ensures that the filename contains ‘.png’:

const file = await fs.getFileForSaving("test.png", { types: [ "png" ]});

This line here only tells ‘fs’ to interpret your data array as binary data. Unless your array is structured as .png file with header etc. you’re not gonna automatically get that:

await file.write(data, {
    format: storage.formats.binary
})

Thank you @dotproduct for your answer. I already tried that (and I shared the generated bmp file above, if you would like to test it).

I understand all of what you are saying, but I do not have any solution to tackle this issue properly (if it is from code, it would be better). Do you have any?

I guess what I mean is not to generate a bmp file (and read that back in). Rather I would try creating a clean/blank document, make that your current document and drop your image data into it (via Imaging API’s putPixels) and then use a document save. This new document would just be a sidecar file, just for the purpose of saving out your pixels, you can close it once done, no nead for any undo, etc.

1 Like

Your alternative could be to use: pngjs3 - npm but since UXP is not Node.js …you might need to fill some gaps.

I also tried with the exportSelectionAsFileTypePressed action (batchPlay ), but that do not seem to be supported anymore (photoshop 24.7).

Wait what? Where did you see this cause I’ve been tearing my hair out all day trying to figure out why all the examples online won’t work for me!

Hi @maxplaystudios , well, on my side, I also tried a lot of things (app permissions…) & didn’t get any errors. So I thought this would not work anymore (and I do not remember any official documentation about it.).

1 Like

Aha! I did get it to work! And it was both unintuitive and obvious…
I had to create a session token for the folder I was exporting to, even if I was not going to pass the token to the command. Even though I have full access enabled in the manifest. Once I opened a session token, it exported to the target folder just fine.

const { batchPlay } = require("photoshop").action;
const lfs = require("uxp").storage.localFileSystem;

async function tokenify(url){
  let entry = await lfs.getEntryWithUrl("file:" + url);
  console.log(entry)
  return lfs.createSessionToken(await lfs.getEntryWithUrl("file:" + url));
}

async function exportLayerAsPng(layer, destPath, imageSuffix) {
  
  const { id, name } = layer;
  const token = tokenify(destPath) //even if we don't use this, we need to unlock the path

  console.log("exportLayerAsPng",imageSuffix, name);
  console.log("", destPath)

  const selectCommand = 	  {
    _obj: 'select',
    _target: [
      {
      _ref: 'layer',
       _id: id,
      },
    ],
    makeVisible: false,
    layerID: [id],
    _isCommand: false,
  }
  console.log(selectCommand)

  const exportCommand = {
    _obj: 'exportSelectionAsFileTypePressed',
    _target: { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' },
    fileType: 'png',
    quality: 32,
    metadata: 0,
    destFolder: destPath, //type: string not entry
    sRGB: true,
    openWindow: false,
    _options: { dialogOptions: 'dontDisplay' },
  };
  
  await executeAsModal(
    async () => {
        await batchPlay([exportCommand], { modalBehavior: 'execute', synchronousExecution:false });
    },
    { commandName: `Export ${layer.name} Layer As PNG start` }
  );
};

1 Like