Copy link to clipboard
Copied
Great that the latest Photoshop has "full" webp support, but lacking is the ability to export artboards to webp format. I build a lot of my website graphics as artboards, but it looks like I will need an intermediary step of saving the artboards as PSD files first, and then exporting them as webp? Or am I missing something?
Thanks,
Judi
Hi again Judi,
Please try this 1.4 version, I have replaced the function with a different way to call the same code.
/*
Artboards-to-WEBP-PS2022.jsx
14th May 2022, v1.4 - Stephen Marsh
* Only for Photoshop 2022 (v23.x.x) or later!
* Saves artboards as lossy WebP - Quality: 100
* Prompts for a save location and saves using the filename + artboard name
* Existing files will be silently overwritten
Instructions for saving and installing:
https://prepression.blogspot.com/2017/11/downloading-an
...
@Stephen_A_Marsh Solved! Your script works perfectly. The problem was that I had the "Open As Layer" event running in the Script Events Manager. Turning that off solved the errors I was getting, plus a whole heap of other stuff relating to exporting artboards. I rely hugely on Open As Layer to help document my compositing workflow, but hadn't connected the dots to its impact on other scripts. But it's easy to toggle the event on and off as needed. Thank you so much for all your help on this, and
...Copy link to clipboard
Copied
@Art by JLM – The Artboards to Files JavaScript files would need to be updated to support another format.
EDIT: If you make a new post as an "Idea" I would upvote it to add webp support, this is something that should be shipping as standard with the software, IMO.
As this is a script, any user with the knowledge and time could update it to include webp, however, it is often easier said than done for the complex scripts that ship with Photoshop.
Copy link to clipboard
Copied
You can try this script to save the current open doc's artboards to WebP lossy 100% quality using the doc name + artboard name:
/*
Artboards-to-WEBP-PS2022.jsx
14th May 2022, v1.1 - Stephen Marsh
* Only for Photoshop 2022 (v23.x.x) or later!
* Saves artboards as lossy WebP - Quality: 100
* Prompts for a save location and saves using the filename + artboard name
* Existing files will be silently overwritten
Instructions for saving and installing:
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
*/
// Original script author below:
// =========================================================================
// artboardsToPNG.jsx - Adobe Photoshop Script
// Version: 0.6.0
// Requirements: Adobe Photoshop CC 2015, or higher
// Author: Anton Lyubushkin (nvkz.nemo@gmail.com)
// Website: http://lyubushkin.pro/
// =========================================================================
#target photoshop
app.bringToFront();
// Ensure that version 2022 or later is being used
var versionNumber = app.version.split(".");
var versionCheck = parseInt(versionNumber);
if (versionCheck < 23) {
alert("You must use Photoshop 2022 or later to save using native WebP format...");
} else {
if (app.documents.length !== 0) {
var docRef = app.activeDocument,
allArtboards,
artboardsCount = 0,
inputFolder = Folder.selectDialog("Select an output folder to save the artboards as WebP:");
if (inputFolder) {
function getAllArtboards() {
try {
var ab = [];
var theRef = new ActionReference();
theRef.putProperty(charIDToTypeID('Prpr'), stringIDToTypeID("artboards"));
theRef.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
var getDescriptor = new ActionDescriptor();
getDescriptor.putReference(stringIDToTypeID("null"), theRef);
var abDesc = executeAction(charIDToTypeID("getd"), getDescriptor, DialogModes.NO).getObjectValue(stringIDToTypeID("artboards"));
var abCount = abDesc.getList(stringIDToTypeID('list')).count;
if (abCount > 0) {
for (var i = 0; i < abCount; ++i) {
var abObj = abDesc.getList(stringIDToTypeID('list')).getObjectValue(i);
var abTopIndex = abObj.getInteger(stringIDToTypeID("top"));
ab.push(abTopIndex);
}
}
return [abCount, ab];
} catch (e) {
alert(e.line + '\n' + e.message);
}
}
function selectLayerByIndex(index, add) {
add = undefined ? add = false : add
var ref = new ActionReference();
ref.putIndex(charIDToTypeID("Lyr "), index + 1);
var desc = new ActionDescriptor();
desc.putReference(charIDToTypeID("null"), ref);
if (add) desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
desc.putBoolean(charIDToTypeID("MkVs"), false);
executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
}
function ungroupLayers() {
var desc1 = new ActionDescriptor();
var ref1 = new ActionReference();
ref1.putEnumerated(charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
desc1.putReference(charIDToTypeID('null'), ref1);
executeAction(stringIDToTypeID('ungroupLayersEvent'), desc1, DialogModes.NO);
}
function crop() {
var desc1 = new ActionDescriptor();
desc1.putBoolean(charIDToTypeID('Dlt '), true);
executeAction(charIDToTypeID('Crop'), desc1, DialogModes.NO);
}
function saveAsWebP(_name) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
descriptor2.putEnumerated(s2t("compression"), s2t("WebPCompression"), s2t("compressionLossy"));
descriptor2.putInteger(s2t("quality"), 100);
descriptor2.putBoolean(s2t("includeXMPData"), true);
descriptor2.putBoolean(s2t("includeEXIFData"), true);
descriptor2.putBoolean(s2t("includePsExtras"), true);
descriptor.putObject(s2t("as"), s2t("WebPFormat"), descriptor2);
descriptor.putPath(s2t("in"), new File(inputFolder + '/' + _name + '.webp'), true);
descriptor.putInteger(s2t("documentID"), 237);
descriptor.putBoolean(s2t("lowerCase"), true);
descriptor.putEnumerated(s2t("saveStage"), s2t("saveStageType"), s2t("saveSucceeded"));
executeAction(s2t("save"), descriptor, DialogModes.NO);
}
function main(i) {
selectLayerByIndex(allArtboards[1][i]);
// RegEx remove filename extension
var docName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
// RegEx replace illegal filename characters with a hyphen
var artboardName = docName + " - " + app.activeDocument.activeLayer.name.replace(/[:\/\\*\?\"\<\>\|\\\r\\\n.]/g, "-"); // "/\:*?"<>|\r\n" -> "-"
executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
executeAction(stringIDToTypeID("placedLayerEditContents"), undefined, DialogModes.NO);
app.activeDocument.selection.selectAll();
try {
ungroupLayers();
} catch (e) {
alert("There was an unexpected error with the ungroupLayers function!")
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
crop();
saveAsWebP(artboardName);
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
allArtboards = getAllArtboards();
artboardsCount = allArtboards[0];
for (var i = 0; i < artboardsCount; i++) {
docRef.suspendHistory('Artboards to WebP', 'main(' + i + ')');
app.refresh();
app.activeDocument.activeHistoryState = app.activeDocument.historyStates[app.activeDocument.historyStates.length - 2];
}
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
alert('WebP files saved to: ' + '\r' + inputFolder.fsName);
// inputFolder.execute();
} else {
alert('You must have a document open!');
}
}
Quickstart instructions for saving and running:
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html#Photoshop
Copy link to clipboard
Copied
Wow thank you for the script! I got it installed without problem, but when I run it, I get this error when the first psb file (generated from the first artboard) is created. I'm running the latest PS.
I tried again with another document with multiple artboards with the same result. I then created a very basic doc with 2 artboards with just one simple layer on each (no linked files, no smart objects), with the same error.
Hopefully it's something obvious - is perhaps the ungroupLayersEvent a script I need to install too?
Judi
Copy link to clipboard
Copied
I obviously don't get that error... I'm on version 23.3.1 on Mac 11.6.5 (Big Sur).
Copy link to clipboard
Copied
Hi again Judi,
Please try this 1.4 version, I have replaced the function with a different way to call the same code.
/*
Artboards-to-WEBP-PS2022.jsx
14th May 2022, v1.4 - Stephen Marsh
* Only for Photoshop 2022 (v23.x.x) or later!
* Saves artboards as lossy WebP - Quality: 100
* Prompts for a save location and saves using the filename + artboard name
* Existing files will be silently overwritten
Instructions for saving and installing:
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
*/
// Original script author below:
// =========================================================================
// artboardsToPNG.jsx - Adobe Photoshop Script
// Version: 0.6.0
// Requirements: Adobe Photoshop CC 2015, or higher
// Author: Anton Lyubushkin (nvkz.nemo@gmail.com)
// Website: http://lyubushkin.pro/
// =========================================================================
#target photoshop
app.bringToFront();
// Ensure that version 2022 or later is being used
var versionNumber = app.version.split(".");
var versionCheck = parseInt(versionNumber);
if (versionCheck < 23) {
alert("You must use Photoshop 2022 or later to save using native WebP format...");
} else {
if (app.documents.length !== 0) {
var docRef = app.activeDocument,
allArtboards,
artboardsCount = 0,
inputFolder = Folder.selectDialog("Select an output folder to save the artboards as WebP:");
if (inputFolder) {
function getAllArtboards() {
try {
var ab = [];
var theRef = new ActionReference();
theRef.putProperty(charIDToTypeID('Prpr'), stringIDToTypeID("artboards"));
theRef.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
var getDescriptor = new ActionDescriptor();
getDescriptor.putReference(stringIDToTypeID("null"), theRef);
var abDesc = executeAction(charIDToTypeID("getd"), getDescriptor, DialogModes.NO).getObjectValue(stringIDToTypeID("artboards"));
var abCount = abDesc.getList(stringIDToTypeID('list')).count;
if (abCount > 0) {
for (var i = 0; i < abCount; ++i) {
var abObj = abDesc.getList(stringIDToTypeID('list')).getObjectValue(i);
var abTopIndex = abObj.getInteger(stringIDToTypeID("top"));
ab.push(abTopIndex);
}
}
return [abCount, ab];
} catch (e) {
alert(e.line + '\n' + e.message);
}
}
function selectLayerByIndex(index, add) {
add = undefined ? add = false : add
var ref = new ActionReference();
ref.putIndex(charIDToTypeID("Lyr "), index + 1);
var desc = new ActionDescriptor();
desc.putReference(charIDToTypeID("null"), ref);
if (add) desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
desc.putBoolean(charIDToTypeID("MkVs"), false);
executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
}
function ungroupLayers() {
app.runMenuItem(stringIDToTypeID('ungroupLayersEvent'));
}
function crop() {
var desc1 = new ActionDescriptor();
desc1.putBoolean(charIDToTypeID('Dlt '), true);
executeAction(charIDToTypeID('Crop'), desc1, DialogModes.NO);
}
function saveAsWebP(_name) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
descriptor2.putEnumerated(s2t("compression"), s2t("WebPCompression"), s2t("compressionLossy"));
descriptor2.putInteger(s2t("quality"), 100);
descriptor2.putBoolean(s2t("includeXMPData"), true);
descriptor2.putBoolean(s2t("includeEXIFData"), true);
descriptor2.putBoolean(s2t("includePsExtras"), true);
descriptor.putObject(s2t("as"), s2t("WebPFormat"), descriptor2);
descriptor.putPath(s2t("in"), new File(inputFolder + '/' + _name + '.webp'), true);
descriptor.putInteger(s2t("documentID"), 237);
descriptor.putBoolean(s2t("lowerCase"), true);
descriptor.putEnumerated(s2t("saveStage"), s2t("saveStageType"), s2t("saveSucceeded"));
executeAction(s2t("save"), descriptor, DialogModes.NO);
}
function main(i) {
selectLayerByIndex(allArtboards[1][i]);
// RegEx remove filename extension
var docName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
// RegEx replace illegal filename characters with a hyphen
var artboardName = docName + " - " + app.activeDocument.activeLayer.name.replace(/[:\/\\*\?\"\<\>\|\\\r\\\n.]/g, "-"); // "/\:*?"<>|\r\n" -> "-"
executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
executeAction(stringIDToTypeID("placedLayerEditContents"), undefined, DialogModes.NO);
app.activeDocument.selection.selectAll();
try {
ungroupLayers();
} catch (e) {
alert("There was an unexpected error with the ungroupLayers function!")
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
crop();
saveAsWebP(artboardName);
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
allArtboards = getAllArtboards();
artboardsCount = allArtboards[0];
for (var i = 0; i < artboardsCount; i++) {
docRef.suspendHistory('Artboards to WebP', 'main(' + i + ')');
app.refresh();
app.activeDocument.activeHistoryState = app.activeDocument.historyStates[app.activeDocument.historyStates.length - 2];
}
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
alert('WebP files saved to: ' + '\r' + inputFolder.fsName);
// inputFolder.execute();
} else {
alert('You must have a document open!');
}
}
Copy link to clipboard
Copied
Kia ora Stephen
(I'm on Windows 10 running PS 23.2.2)
This time I get this "helpful" error and the script hangs.
The first webp file is created (woohoo!), but not the second, and you can see how the first Artboard is still a smart object.
Ngā mihi nui,
Judi
Copy link to clipboard
Copied
G'day!
You'll need to close the PSD without saving changes.
Looks like I'll need to test on Windows...
@Art by JLM – EDIT:
The previous code ran fine for me in Windows 10/Photoshop 2022.
I have updated the code to a 1.3 version using the ungroup code from Windows, which I believe is the same.
After that I'm out of ideas, perhaps another scripting person will have a suggestion.
Copy link to clipboard
Copied
My last-ditch final attempt v1.4 code posted above... I can't reproduce your issues in Windows it all just works!
Copy link to clipboard
Copied
really appreciate your efforts! v1.4 results in
then the "unexpected error". If I continue, it results in a single webp file with both artboards and the PSD file closes.
Tomorrow I'll take a look at the code and see what I can figure out. I will also consult with the java expert in the household 🙂
Kind regards,
Judi
Copy link to clipboard
Copied
Hi @Stephen_A_Marsh ,
thanks for your script ! I have tried the 1.4 version.
I had also the error The command "Select" is not currently available, then "The was an unexpected error with yhe ungroupLayers function!" and finally the alert in attachment. In my case I had an empty artboard. I've added a solid-color fill layer and it solved !
Thanks,
Raphaël
Copy link to clipboard
Copied
@Stephen_A_Marsh Solved! Your script works perfectly. The problem was that I had the "Open As Layer" event running in the Script Events Manager. Turning that off solved the errors I was getting, plus a whole heap of other stuff relating to exporting artboards. I rely hugely on Open As Layer to help document my compositing workflow, but hadn't connected the dots to its impact on other scripts. But it's easy to toggle the event on and off as needed. Thank you so much for all your help on this, and for indirectly solving my other dramas too 🙂
Judi
Copy link to clipboard
Copied
@Art by JLM – Glad you worked it out! Which of the 4 script versions works?
Copy link to clipboard
Copied
I tested only v1.4 with the 'fix' in place. Likely the others work too.
Copy link to clipboard
Copied
Thank you for the feedback, they all should have produced the same result.
Copy link to clipboard
Copied
Hi Stephen_A_Marsh,
This is awesome! Really helpful, thank you.
One question I had was around the filenaming and wondered if it was possible to add that functionality into this script.
Usually, when we export Artboards to Files, there is an additional pop up (attached) which allows you to add a prefix to the filename, rather than taking the Artboard name. I wondered if it's possible to add that pop up on this export functionality at all? It's not critical but would help the process of filenaming and organisation.
I checked the Artboards to Files jsx file and could see part of the functionality but I'm not saavy enough to work out how to add that to your awesome script.
Really appreciate your work on this!
Thanks so much!
Copy link to clipboard
Copied
Yes, this is possible. Please provide a cropped image of the layers panel with all artboards visible (they can be collapsed) and the final output names, making it clear which is the doc name and which is the added prefix or suffix.
The doc name will be static and the prefix/suffix will also be static, so files would overwrite each other without a sequential number.
Copy link to clipboard
Copied
Hi @Stephen_A_Marsh
Awesome, thank you. I've included 3 files to show the process of how I export currently Artboards to Files with the prefix.
01 - This shows the Artboards and the layers as you requested. The artboards are called, Landscape, Portrait and Sqaure.
02 - This shows the pop up where I specify a prefix for the file name.
03 - This shows the output of the files in Explorer but in PNG format
03 - (3 images) - Correct output filenames but PNG
I tried to upload the webp images or a zip here as well as the output from your script but it wont let me as it does not support the fileformat.
What I need is the output of 03 - but as webP files.
I hope that makes sense! Thank you so much for the support!
Copy link to clipboard
Copied
@Mel30188944k1h7 – Thanks, I think that it makes sense! You have unique artboard names + a static prefix with an underscore separator. Thank you for clarifying. I should be able to modify the previous script easily.
Note: I have been getting failures on the final artboard, which is converted to a smart object! Here is the updated 1.5 code:
/*
Artboards-to-WEBP-PS2022.jsx
14th May 2022, v1.5 - Stephen Marsh
* Only for Photoshop 2022 (v23.x.x) or later!
* Saves artboards as lossy WebP - Quality: 100
* Prompts for a save location and prompts for a prefix to be added to the artboard name
* Existing files will be silently overwritten
Instructions for saving and installing:
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
*/
// Original script author below:
// =========================================================================
// artboardsToPNG.jsx - Adobe Photoshop Script
// Version: 0.6.0
// Requirements: Adobe Photoshop CC 2015, or higher
// Author: Anton Lyubushkin (nvkz.nemo@gmail.com)
// Website: http://lyubushkin.pro/
// =========================================================================
#target photoshop
app.bringToFront();
// Ensure that version 2022 or later is being used
var versionNumber = app.version.split(".");
var versionCheck = parseInt(versionNumber);
if (versionCheck < 23) {
alert("You must use Photoshop 2022 or later to save using native WebP format...");
} else {
if (app.documents.length !== 0) {
var docRef = app.activeDocument,
allArtboards,
artboardsCount = 0,
inputFolder = Folder.selectDialog("Select an output folder to save the artboards as WebP:");
var preFix = prompt("Add the prefix including separator character to the artboard name:");
if (inputFolder) {
function getAllArtboards() {
try {
var ab = [];
var theRef = new ActionReference();
theRef.putProperty(charIDToTypeID('Prpr'), stringIDToTypeID("artboards"));
theRef.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
var getDescriptor = new ActionDescriptor();
getDescriptor.putReference(stringIDToTypeID("null"), theRef);
var abDesc = executeAction(charIDToTypeID("getd"), getDescriptor, DialogModes.NO).getObjectValue(stringIDToTypeID("artboards"));
var abCount = abDesc.getList(stringIDToTypeID('list')).count;
if (abCount > 0) {
for (var i = 0; i < abCount; ++i) {
var abObj = abDesc.getList(stringIDToTypeID('list')).getObjectValue(i);
var abTopIndex = abObj.getInteger(stringIDToTypeID("top"));
ab.push(abTopIndex);
}
}
return [abCount, ab];
} catch (e) {
alert(e.line + '\n' + e.message);
}
}
function selectLayerByIndex(index, add) {
add = undefined ? add = false : add
var ref = new ActionReference();
ref.putIndex(charIDToTypeID("Lyr "), index + 1);
var desc = new ActionDescriptor();
desc.putReference(charIDToTypeID("null"), ref);
if (add) desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
desc.putBoolean(charIDToTypeID("MkVs"), false);
executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
}
function ungroupLayers() {
app.runMenuItem(stringIDToTypeID('ungroupLayersEvent'));
}
function crop() {
var desc1 = new ActionDescriptor();
desc1.putBoolean(charIDToTypeID('Dlt '), true);
executeAction(charIDToTypeID('Crop'), desc1, DialogModes.NO);
}
function saveAsWebP(_name) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
descriptor2.putEnumerated(s2t("compression"), s2t("WebPCompression"), s2t("compressionLossy"));
descriptor2.putInteger(s2t("quality"), 100);
descriptor2.putBoolean(s2t("includeXMPData"), true);
descriptor2.putBoolean(s2t("includeEXIFData"), true);
descriptor2.putBoolean(s2t("includePsExtras"), true);
descriptor.putObject(s2t("as"), s2t("WebPFormat"), descriptor2);
descriptor.putPath(s2t("in"), new File(inputFolder + '/' + _name + '.webp'), true);
descriptor.putBoolean(s2t("lowerCase"), true);
descriptor.putEnumerated(s2t("saveStage"), s2t("saveStageType"), s2t("saveSucceeded"));
executeAction(s2t("save"), descriptor, DialogModes.NO);
}
function main(i) {
selectLayerByIndex(allArtboards[1][i]);
// RegEx replace illegal filename characters with a hyphen
var artboardName = preFix + app.activeDocument.activeLayer.name.replace(/[:\/\\*\?\"\<\>\|\\\r\\\n.]/g, "_"); // "/\:*?"<>|\r\n" -> "-"
executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
executeAction(stringIDToTypeID("placedLayerEditContents"), undefined, DialogModes.NO);
app.activeDocument.selection.selectAll();
try {
ungroupLayers();
} catch (e) {
alert("There was an unexpected error with the ungroupLayers function!")
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
crop();
saveAsWebP(artboardName);
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
allArtboards = getAllArtboards();
artboardsCount = allArtboards[0];
for (var i = 0; i < artboardsCount; i++) {
docRef.suspendHistory('Artboards to WebP', 'main(' + i + ')');
app.refresh();
app.activeDocument.activeHistoryState = app.activeDocument.historyStates[app.activeDocument.historyStates.length - 2];
}
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
alert('WebP files saved to: ' + '\r' + inputFolder.fsName);
// inputFolder.execute();
} else {
alert('You must have a document open!');
}
}
Adobe really does need to update the default scripts that ship with Photoshop.
Copy link to clipboard
Copied
Hey @Stephen_A_Marsh,
This is amazing, thank you so much for your help! Let me know if I can upvote / rate this script anywhere else for you! Works like a dream. Really appreciate your time and support to make our lives easier 🙂
Have a wonderful day!
Copy link to clipboard
Copied
You're welcome!
Copy link to clipboard
Copied
Hello,
Your script is nice, but it happens, many times, if the layout is more complex, to get a very big WEBP file.
It happend to be between the range of 5mb to 150 mb.
The size in pixels for the artboard i try is small, like 600x400px.
If i save the same file "manualy" i get a WEBP of under 100kb.
You have any ideea why?
Also if the PSD file is CMYK instead of RGB the script gives an error and stops.
Copy link to clipboard
Copied
I forgot to reply/update, here is a new 1.6 version that converts to sRGB if the mode isn't RGB:
/*
Artboards-to-WEBP-PS2022.jsx
18th June 2024, v1.8 - Stephen Marsh
https://community.adobe.com/t5/photoshop-ecosystem-discussions/exporting-artboards-to-webp-format/td-p/12937135
* Only for Photoshop 2022 (v23.x.x) or later!
* Saves artboards as lossy WebP - Quality: 100
* Prompts for a save location and prompts for a prefix to be added to the artboard name (uses the doc name by default)
* Existing files will be silently overwritten
* Converts non-RGB to sRGB color space, RGB color mode and 8 bpc
* If RGB the original color space is retained, beware if using ProPhoto RGB!
* Added a "script running" window to provide feedback during the script execution
* Creates a temporary working document
Instructions for saving and installing:
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
*/
// Original script author below:
// =========================================================================
// artboardsToPNG.jsx - Adobe Photoshop Script
// Version: 0.6.0
// Requirements: Adobe Photoshop CC 2015, or higher
// Author: Anton Lyubushkin (nvkz.nemo@gmail.com)
// Website: http://lyubushkin.pro/
// =========================================================================
#target photoshop
(function () {
app.bringToFront();
// Ensure that version 2022 or later is being used
var versionNumber = app.version.split(".");
var versionCheck = parseInt(versionNumber);
if (versionCheck < 23) {
alert("You must use Photoshop 2022 or later to save using native WebP format...");
} else {
if (app.documents.length !== 0) {
var docRef = app.activeDocument,
allArtboards,
artboardsCount = 0;
var outputFolder = Folder.selectDialog("Select an output folder to save the artboards as WebP:");
// Test if Cancel button returns null, then do nothing
if (outputFolder === null) {
app.beep();
return;
}
var preFix = prompt("Add the prefix including separator character to the artboard name:", docRef.name.replace(/\.[^\.]+$/, '') + "_");
// Test if Cancel button returns null, then do nothing
if (preFix === null) {
app.beep();
return;
}
// Hide the Photoshop panels
app.togglePalettes();
if (outputFolder) {
// Dupe to a temp doc
app.activeDocument.duplicate(app.activeDocument.name.replace(/\.[^\.]+$/, ''), false);
// Clean out unwanted metadata
deleteDocumentAncestorsMetadata();
removeCRSmeta();
// If the doc isn't in RGB mode
if (activeDocument.mode !== DocumentMode.RGB)
// Convert to sRGB
activeDocument.convertProfile("sRGB IEC61966-2.1", Intent.RELATIVECOLORIMETRIC, true, false);
// Ensure that the doc mode is RGB (to correctly handle Indexed Color mode)
activeDocument.changeMode(ChangeMode.RGB);
activeDocument.bitsPerChannel = BitsPerChannelType.EIGHT;
allArtboards = getAllArtboards();
artboardsCount = allArtboards[0];
// Loop over the artboards and run the main function
for (var i = 0; i < artboardsCount; i++) {
// Script running notification window - courtesy of William Campbell
// https://www.marspremedia.com/download?asset=adobe-script-tutorial-11.zip
// https://youtu.be/JXPeLi6uPv4?si=Qx0OVNLAOzDrYPB4
var working;
working = new Window("palette");
working.preferredSize = [300, 80];
working.add("statictext");
working.t = working.add("statictext");
working.add("statictext");
working.display = function (message) {
this.t.text = message || "Script running, please wait...";
this.show();
app.refresh();
};
working.display();
// Call the main function
docRef.suspendHistory('Artboards to WebP', 'main(' + i + ')');
app.refresh();
app.activeDocument.activeHistoryState = app.activeDocument.historyStates[app.activeDocument.historyStates.length - 2];
// Ensure Photoshop has focus before closing the running script notification window
app.bringToFront();
working.close();
}
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
// Restore the Photoshop panels
app.togglePalettes();
// End of script notification
app.beep();
alert(artboardsCount + ' WebP files saved to: ' + '\r' + outputFolder.fsName);
// Open the output folder in Windows Explorer or the Mac Finder
// outputFolder.execute();
///// FUNCTIONS /////
function getAllArtboards() {
try {
var ab = [];
var theRef = new ActionReference();
theRef.putProperty(charIDToTypeID('Prpr'), stringIDToTypeID("artboards"));
theRef.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
var getDescriptor = new ActionDescriptor();
getDescriptor.putReference(stringIDToTypeID("null"), theRef);
var abDesc = executeAction(charIDToTypeID("getd"), getDescriptor, DialogModes.NO).getObjectValue(stringIDToTypeID("artboards"));
var abCount = abDesc.getList(stringIDToTypeID('list')).count;
if (abCount > 0) {
for (var i = 0; i < abCount; ++i) {
var abObj = abDesc.getList(stringIDToTypeID('list')).getObjectValue(i);
var abTopIndex = abObj.getInteger(stringIDToTypeID("top"));
ab.push(abTopIndex);
}
}
return [abCount, ab];
} catch (e) {
alert(e.line + '\n' + e.message);
}
}
function selectLayerByIndex(index, add) {
add = undefined ? add = false : add;
var ref = new ActionReference();
ref.putIndex(charIDToTypeID("Lyr "), index + 1);
var desc = new ActionDescriptor();
desc.putReference(charIDToTypeID("null"), ref);
if (add) desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
desc.putBoolean(charIDToTypeID("MkVs"), false);
executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
}
function ungroupLayers() {
app.runMenuItem(stringIDToTypeID('ungroupLayersEvent'));
}
function crop() {
var desc1 = new ActionDescriptor();
desc1.putBoolean(charIDToTypeID('Dlt '), true);
executeAction(charIDToTypeID('Crop'), desc1, DialogModes.NO);
}
function saveAsWebP(_name) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
descriptor2.putEnumerated(s2t("compression"), s2t("WebPCompression"), s2t("compressionLossy"));
descriptor2.putInteger(s2t("quality"), 100);
descriptor2.putBoolean(s2t("includeXMPData"), true);
descriptor2.putBoolean(s2t("includeEXIFData"), true);
descriptor2.putBoolean(s2t("includePsExtras"), true);
descriptor.putObject(s2t("as"), s2t("WebPFormat"), descriptor2);
descriptor.putPath(s2t("in"), new File(outputFolder + '/' + _name + '.webp'), true);
descriptor.putBoolean(s2t("lowerCase"), true);
descriptor.putEnumerated(s2t("saveStage"), s2t("saveStageType"), s2t("saveSucceeded"));
executeAction(s2t("save"), descriptor, DialogModes.NO);
}
// The main working function
function main(i) {
selectLayerByIndex(allArtboards[1][i]);
// RegEx replace illegal filename characters with a hyphen
var artboardName = preFix + app.activeDocument.activeLayer.name.replace(/[:\/\\*\?\"\<\>\|\\\r\\\n.]/g, "_"); // "/\:*?"<>|\r\n" -> "-"
executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
executeAction(stringIDToTypeID("placedLayerEditContents"), undefined, DialogModes.NO);
app.activeDocument.selection.selectAll();
try {
ungroupLayers();
} catch (e) {
alert("There was an unexpected error with the ungroupLayers function!");
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
crop();
saveAsWebP(artboardName);
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
function removeCRSmeta() {
//community.adobe.com/t5/photoshop/remove-crs-metadata/td-p/10306935
if (!documents.length) return;
if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
var xmp = new XMPMeta(app.activeDocument.xmpMetadata.rawData);
XMPUtils.removeProperties(xmp, XMPConst.NS_CAMERA_RAW, "", XMPConst.REMOVE_ALL_PROPERTIES);
app.activeDocument.xmpMetadata.rawData = xmp.serialize();
}
function deleteDocumentAncestorsMetadata() {
whatApp = String(app.name); //String version of the app name
if (whatApp.search("Photoshop") > 0) { //Check for photoshop specifically, or this will cause errors
if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
var xmp = new XMPMeta(activeDocument.xmpMetadata.rawData);
// Begone foul Document Ancestors!
xmp.deleteProperty(XMPConst.NS_PHOTOSHOP, "DocumentAncestors");
app.activeDocument.xmpMetadata.rawData = xmp.serialize();
}
}
} else {
alert('You must have a document open!');
}
}
})();
As far as file size, in my tests, I get the same size either manually or from the script if the same quality and metadata settings are used.
Please provide sample files of the larger files and the smaller files for the same settings and content. Perhaps there is other metadata or something else going on.
Copy link to clipboard
Copied
Hi @Stephen_A_Marsh
Hope all is well with you! It's me again 🙂
The script has been working like a dream and it works super well.
One issue we've encountered with our workflow is that OG images don't support webp in all platforms and this is causing us a bit of an issue, so in fact it seems we need both WebP formats and Jpeg formats.
Ideally we could export both from the same script so it minimises our workflow.
Is there someway the script could be adapted to also output JPG format too? Or is this a bit much?
Worth an ask at least.
Copy link to clipboard
Copied
Hi @Stephen_A_Marsh
Hope all is well with you! It's me again 🙂
The script has been working like a dream and it works super well.
One issue we've encountered with our workflow is that OG images don't support webp in all platforms and this is causing us a bit of an issue, so in fact it seems we need both WebP formats and Jpeg formats.
Ideally we could export both from the same script so it minimises our workflow.
Is there someway the script could be adapted to also output JPG format too? Or is this a bit much?
Worth an ask at least.
By @Mel30188944k1h7
I just stumbled over this post, here is a new version of the script that saves both JPEG and WebP versions:
/*
Artboards-to-JPEG-and-WEBP-PS2022.jsx
18th June 2024, v1.3 - Stephen Marsh
* Only for Photoshop 2022 (v23.x.x) or later!
* Saves artboards as JPEG - Quality: 12 & lossy WebP - Quality: 100
* Prompts for a save location and prompts for a prefix to be added to the artboard name (uses the doc name by default)
* Existing files will be silently overwritten
* Converts non-RGB to sRGB color space, RGB color mode and 8 bpc
* If RGB the original color space is retained, beware if using ProPhoto RGB!
* Added metadata removal for Document Ancestors, Camera Raw Settings and XMP
* Added a "script running" window to provide feedback during the script execution
* Creates a temporary working document
Instructions for saving and installing:
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
*/
// Original script author below:
// =========================================================================
// artboardsToPNG.jsx - Adobe Photoshop Script
// Version: 0.6.0
// Requirements: Adobe Photoshop CC 2015, or higher
// Author: Anton Lyubushkin (nvkz.nemo@gmail.com)
// Website: http://lyubushkin.pro/
// =========================================================================
#target photoshop
(function () {
app.bringToFront();
// Ensure that version 2022 or later is being used
var versionNumber = app.version.split(".");
var versionCheck = parseInt(versionNumber);
if (versionCheck < 23) {
alert("You must use Photoshop 2022 or later to save using native WebP format...");
} else {
if (app.documents.length !== 0) {
var docRef = app.activeDocument,
allArtboards,
artboardsCount = 0;
var outputFolder = Folder.selectDialog("Select an output folder to save the artboards as JPEG & WebP:");
// Test if Cancel button returns null, then do nothing
if (outputFolder === null) {
app.beep();
return;
}
var preFix = prompt("Add the prefix including separator character to the artboard name:", docRef.name.replace(/\.[^\.]+$/, '') + "_");
// Test if Cancel button returns null, then do nothing
if (preFix === null) {
app.beep();
return;
}
// Hide the Photoshop panels
app.togglePalettes();
if (outputFolder) {
// Dupe to a temp doc
app.activeDocument.duplicate(app.activeDocument.name.replace(/\.[^\.]+$/, ''), false);
// Clean out unwanted metadata
deleteDocumentAncestorsMetadata();
removeCRSmeta();
// If the doc isn't in RGB mode
if (activeDocument.mode !== DocumentMode.RGB)
// Convert to sRGB
activeDocument.convertProfile("sRGB IEC61966-2.1", Intent.RELATIVECOLORIMETRIC, true, false);
// Ensure that the doc mode is RGB (to correctly handle Indexed Color mode)
activeDocument.changeMode(ChangeMode.RGB);
activeDocument.bitsPerChannel = BitsPerChannelType.EIGHT;
allArtboards = getAllArtboards();
artboardsCount = allArtboards[0];
// Loop over the artboards and run the main function
for (var i = 0; i < artboardsCount; i++) {
// Script running notification window - courtesy of William Campbell
// https://www.marspremedia.com/download?asset=adobe-script-tutorial-11.zip
// https://youtu.be/JXPeLi6uPv4?si=Qx0OVNLAOzDrYPB4
var working;
working = new Window("palette");
working.preferredSize = [300, 80];
working.add("statictext");
working.t = working.add("statictext");
working.add("statictext");
working.display = function (message) {
this.t.text = message || "Script running, please wait...";
this.show();
app.refresh();
};
working.display();
// Call the main function
docRef.suspendHistory('Artboards to JPEG & WebP', 'main(' + i + ')');
app.refresh();
app.activeDocument.activeHistoryState = app.activeDocument.historyStates[app.activeDocument.historyStates.length - 2];
// Ensure Photoshop has focus before closing the running script notification window
app.bringToFront();
working.close();
}
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
// Restore the Photoshop panels
app.togglePalettes();
// End of script notification
app.beep();
alert(artboardsCount + ' WebP and ' + artboardsCount + ' JPEG files saved to: ' + '\r' + outputFolder.fsName);
// Open the output folder in Windows Explorer or the Mac Finder
// outputFolder.execute();
///// FUNCTIONS /////
// The main working function
function main(i) {
selectLayerByIndex(allArtboards[1][i]);
// RegEx replace illegal filename characters with a hyphen
var artboardName = preFix + app.activeDocument.activeLayer.name.replace(/[:\/\\*\?\"\<\>\|\\\r\\\n.]/g, "_"); // "/\:*?"<>|\r\n" -> "-"
executeAction(stringIDToTypeID("newPlacedLayer"), undefined, DialogModes.NO);
executeAction(stringIDToTypeID("placedLayerEditContents"), undefined, DialogModes.NO);
app.activeDocument.selection.selectAll();
try {
ungroupLayers();
} catch (e) {
alert("There was an unexpected error with the ungroupLayers function!")
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
crop();
saveAsJPEG(artboardName);
saveAsWebP(artboardName);
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
function getAllArtboards() {
try {
var ab = [];
var theRef = new ActionReference();
theRef.putProperty(charIDToTypeID('Prpr'), stringIDToTypeID("artboards"));
theRef.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
var getDescriptor = new ActionDescriptor();
getDescriptor.putReference(stringIDToTypeID("null"), theRef);
var abDesc = executeAction(charIDToTypeID("getd"), getDescriptor, DialogModes.NO).getObjectValue(stringIDToTypeID("artboards"));
var abCount = abDesc.getList(stringIDToTypeID('list')).count;
if (abCount > 0) {
for (var i = 0; i < abCount; ++i) {
var abObj = abDesc.getList(stringIDToTypeID('list')).getObjectValue(i);
var abTopIndex = abObj.getInteger(stringIDToTypeID("top"));
ab.push(abTopIndex);
}
}
return [abCount, ab];
} catch (e) {
alert(e.line + '\n' + e.message);
}
}
function selectLayerByIndex(index, add) {
add = undefined ? add = false : add
var ref = new ActionReference();
ref.putIndex(charIDToTypeID("Lyr "), index + 1);
var desc = new ActionDescriptor();
desc.putReference(charIDToTypeID("null"), ref);
if (add) desc.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
desc.putBoolean(charIDToTypeID("MkVs"), false);
executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
}
function ungroupLayers() {
app.runMenuItem(stringIDToTypeID('ungroupLayersEvent'));
}
function crop() {
var desc1 = new ActionDescriptor();
desc1.putBoolean(charIDToTypeID('Dlt '), true);
executeAction(charIDToTypeID('Crop'), desc1, DialogModes.NO);
}
function saveAsJPEG(_name) {
removeXMP();
var jpgOptns = new JPEGSaveOptions();
jpgOptns.formatOptions = FormatOptions.STANDARDBASELINE;
jpgOptns.embedColorProfile = true;
jpgOptns.matte = MatteType.NONE;
jpgOptns.quality = 12;
activeDocument.saveAs(new File(outputFolder + '/' + _name + '.jpg'), jpgOptns, true, Extension.LOWERCASE);
}
function saveAsWebP(_name) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
descriptor2.putEnumerated(s2t("compression"), s2t("WebPCompression"), s2t("compressionLossy")); // Lossy compression
descriptor2.putInteger(s2t("quality"), 100); // WebP Quality
descriptor2.putBoolean(s2t("includeXMPData"), true); // Include XMP metadata
descriptor2.putBoolean(s2t("includeEXIFData"), true); // Include EXIF metadata
descriptor2.putBoolean(s2t("includePsExtras"), true); // Include Ps Extras metadata
descriptor.putObject(s2t("as"), s2t("WebPFormat"), descriptor2);
descriptor.putPath(s2t("in"), new File(outputFolder + '/' + _name + '.webp'), true);
descriptor.putBoolean(s2t("lowerCase"), true);
descriptor.putEnumerated(s2t("saveStage"), s2t("saveStageType"), s2t("saveSucceeded"));
executeAction(s2t("save"), descriptor, DialogModes.NO);
}
function removeXMP() {
//https://community.adobe.com/t5/photoshop/script-to-remove-all-meta-data-from-the-photo/td-p/10400906
if (!documents.length) return;
if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
var xmp = new XMPMeta(activeDocument.xmpMetadata.rawData);
XMPUtils.removeProperties(xmp, "", "", XMPConst.REMOVE_ALL_PROPERTIES);
app.activeDocument.xmpMetadata.rawData = xmp.serialize();
}
function removeCRSmeta() {
//community.adobe.com/t5/photoshop/remove-crs-metadata/td-p/10306935
if (!documents.length) return;
if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
var xmp = new XMPMeta(app.activeDocument.xmpMetadata.rawData);
XMPUtils.removeProperties(xmp, XMPConst.NS_CAMERA_RAW, "", XMPConst.REMOVE_ALL_PROPERTIES);
app.activeDocument.xmpMetadata.rawData = xmp.serialize();
}
function deleteDocumentAncestorsMetadata() {
whatApp = String(app.name); //String version of the app name
if (whatApp.search("Photoshop") > 0) { //Check for photoshop specifically, or this will cause errors
if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
var xmp = new XMPMeta(activeDocument.xmpMetadata.rawData);
// Begone foul Document Ancestors!
xmp.deleteProperty(XMPConst.NS_PHOTOSHOP, "DocumentAncestors");
app.activeDocument.xmpMetadata.rawData = xmp.serialize();
}
}
} else {
alert('You must have a document open!');
}
}
})();