Export JPEG and get file size in UXP

Hi, I need to create a function in UXP which exports a JPEG and returns the filesize.

In ExtendScript, all I had to do was run this function:

function SaveJPEG(saveFilepath, quality) {
    var exportOptions = new ExportOptionsSaveForWeb()
    exportOptions.format = SaveDocumentType.JPEG
    exportOptions.includeProfile = false
    exportOptions.interlaced = false
    exportOptions.optimized = true
    exportOptions.quality = quality //0 - 100
    var saveFile = File(saveFilepath)
    app.activeDocument.exportDocument(saveFile, ExportType.SAVEFORWEB, exportOptions)
    return saveFile.length

}

I cannot see an obvious API in UXP for this, so I’m assuming it now has to be performed via BatchPlay.

I captured this code using Alchemist, but not sure if it is the correct command for exporting a JPEG. Plus none of the commands captured in Alchemist contain any information about the JPEG quality.

const batchPlay = require("photoshop").action.batchPlay;

const result = await batchPlay(
[
   {
      "_obj": "modalHTMLPending",
      "kcanDispatchWhileModal": true,
      "isPending": false,
      "extension": "com.adobe.WEBPA.crema.saveforwebdocument",
      "_isCommand": false,
      "_options": {
         "dialogOptions": "dontDisplay"
      }
   }
],{
   "synchronousExecution": false,
   "modalBehavior": "fail"
});

Any help would be greatly appreciated.

1 Like

When I activate the listener and do a “File > Save As > Jpeg”, I get the following descriptor:

{
   "_obj": "save",
   "as": {
      "_obj": "JPEG",
      "extendedQuality": 9,
      "matteColor": {
         "_enum": "matteColor",
         "_value": "none"
      }
   },
   "in": {
      "_path": *token here*,
      "_kind": "local"
   },
   "documentID": 310,
   "lowerCase": true,
   "saveStage": {
      "_enum": "saveStageType",
      "_value": "saveBegin"
   },
   "_isCommand": false
}

I haven’t tested to execute it though. It might be not executable that way, as it includes _isCommand: false.

Regarding the file size: There’s a size property on the EntryMetadata

1 Like

Thank you, I can confirm that all works! I generated the same descriptor using File > Save a Copy > JPEG.

Hello, could you please tell is there any solution for exporting with parameters available now?
In my case Save As command is not an alternative to Export as I need to resize (make smaller) the PSD file only on export time. PSD has smart objects with Gaussian blur smart filters applied and in case of making PSD image size smaller, the blur amount does not change automatically making invalid output when saving via Save As.

Unfortunatly Alchemist doesn’t record all the parameters applied or I’ve recorded incorrectly?

const batchPlay = require("photoshop").action.batchPlay;

const result = await batchPlay(
[
   {
      "_obj": "modalHTMLPending",
      "kcanDispatchWhileModal": true,
      "isPending": false,
      "extension": "com.adobe.WEBPA.crema.saveforwebdocument",
      "_isCommand": false,
      "_options": {
         "dialogOptions": "dontDisplay"
      }
   }
],{
   "synchronousExecution": false,
   "modalBehavior": "fail"
});

@kerrishotts @DavideBarranca @Jarda please advise.

Regards

you can see that _isCommand is false therefore this event is for your information only and cannot be played.

I think that the new dialog for export is not yet recordable. Use a different ways to save documents if you need to.

1 Like

Hello, the link below might help for saving a file in UXP.
Anything to do with files read/write is needed to be approached differently than in CEP

http://forums.creativeclouddeveloper.com/t/saving-a-jpg-to-a-selected-folder/2838

This link provide essential info about files in UXP:
https://www.adobe.io/photoshop/uxp/uxp/reference-js/Modules/uxp/Persistent%20File%20Storage/File/

Thanks for the quick response. As a workaround I think I’ll have to save as jpeg, then reopen, resize and save for now until this basic functionality will be available in UXP API.

I’m confused now, what exactly are you after?

Actually I am able to save the document with the new workflow (entry, tokens). I just need to export PSD to jpeg setting the width and height. But unfortunately Export feature is not available at all. Do you have suggestion on this?

Typically what I do is that I duplicate the current document PSD, I resize it, sharpen it and what not, and then export that PSD to jpeg file.

This will trigger the save process but saving will be done in the background.
Reading metadata right even after doing await on the descriptor will return “no such file or directory” error.

Unfortunately I didn’t yet find a reliable way to save/export to JPG and get file size.
There also seem to be many strange issues with files overall in UXP: How to overwrite a file?

I agree that the best way would be to have a Promise resolve once the file saving is done.
If that’s not available however (again, I haven’t tested saving JPGs myself), I guess you’ll have to implement some kind of polling mechanism:

  1. Trigger file saving
  2. Try to read the entry in a loop (every X ms)
  3. Once it’s there, read the metadata & filesize

File saving can take a significant amount of time of course, which you have to consider. While JPGs save relatively fast, I already had some large PNGs take 10-30 seconds to write to the disc.

1 Like

I will try something along those lines.
For temporary files waiting 5 seconds didn’t help, file still doesn’t exist

I’m trying to export/save multiple JPEGs using my plugin, the images are for products with white background so quality is not that important.

however, I’m losing my sanity and I’d appreciate any help I can get.

right now I’m using doc.save(myFile, jpegOptions); and the size is way too big for what I’m saving and quality doesn’t have any noticeable effect on size.

while export reduces the size from 1.5MB to 66KB with acceptable quality for my use.
is there ANY way I can use export in my code?

I’m willing to use ExtendScript/JSX/UXP or whatever

While technically doc.save is not the same thing as an export, I’m surprised that modifying the quality or other parameters isn’t getting the file size down to something reasonable.

Can you share the code snippets you’ve used for doc.save along w/ some data on how much (if at all) those shrank the file?

Oh, to answer your specific question:

I’d be surprised if there isn’t a batchPlay command that would invoke the Export process. If you haven’t already, check out @Jarda 's excellent Alchemist plugin to record the events that are getting sent around to get a feel for what that might look like.

(Note: I’m no expert on what is batchplayable and what isn’t – but this feels like something that would be. Maybe @heewoo has an idea here.)

here’s my stripped-down code

let jpegOptions = new Object;
// soQuality is a <sp-slider> with min="0" max="12"
var soQuality = document.getElementById("soQuality")
jpegOptions.quality =  soQuality ? soQuality.value : 10;
jpegOptions.embedColorProfile = true;
jpegOptions.matte = 1;
// requestEntry is a custom function
// I use it to manage (get) and store entries
myEntry = await requestEntry(docPath);
thedoc.save(myEntry, jpegOptions);

I got a file size range from 1,561 KB (jpegOptions.quality = 0)
and up to 1,747 KB (jpegOptions.quality = 12)
with sizes in between for the rest (1-11)

using “Export As” I got a file size of 111 KB with a quality value of 6 (7 being the max)

as others pointed out, Alchemist is unable to listen to Export for some reason. it only catches the invokeCommand Event which is of no use to me since I want the whole process automated.

thank you so much for looking into this!

I think that might mean you are using the version of Alchemist from the marketplace rather than Jarda’s development version:

The marketplace version doesn’t capture all PS events.

that’s what I thought at first (reading the warning message) so I already tried using the UXP Dev Tool to load the Plugin and got the exact same result:
invokeCommand

{
   "_obj": "invokeCommand",
   "commandID": 3443,
   "kcanDispatchWhileModal": true,
   "_isCommand": false
}

featureInfo

{
   "_obj": "featureInfo",
   "active": false,
   "command": "getFeatureActive",
   "dontRecord": true,
   "forceNotify": true,
   "_isCommand": false
}