Copy link to clipboard
Copied
Hello,
TL;DR
I'm looking for a script that exports groups as individual PNGs (with the group name) into a folder with the document name. This is exactly what the "Quick Export as PNG" menu item is suppose to do, but cannot be properly batched.
I've been spinning my wheels on this all morning. Photoshop CC has a really useful feature under the Layers menu (or when Right-Clicking selected groups) called "Quick Export as PNG". It exports the groups as individual images into a folder adjacent to the current document - provided you have that export preference set (eg. file1 images get saved to file1-assets folder). However, it has two major flaws.
The Quora post "Is it possible to use "Quick Export as PNG" in a Photoshop action?" gives a very good rundown as well if you are looking for a different/better explanation.
And some information in this post as well: How do I quickly export a layer as a png file in Photoshop with extendscript
The Generate > Image Assets is actually an excellent solution for automating export, provided you can find quick/easy ways to name your layers and groups accordingly! (See below)
Heh, wow, Stephan, I can't for the life of me figure out why, but if I remove the comments at the top of your script it works fine... So, your solution effectively works!
The complete solution is as follows.
1. First, run this script (to append .png
...Copy link to clipboard
Copied
Copy link to clipboard
Copied
Hi Stephen,
Thanks for these, but I've been Googling all morning - so I've actually visited all of those! Several of these don't work, and none of them specify a folder based on the documents name (at least that I could figure out). The second to last link was almost a good starting point, but it ran really slow compared to "Quick Export as PNG". Literally over 100x's slower (I have a lot of groups).
Copy link to clipboard
Copied
Even if slower, this is better than manual, right?
We can't record Quick Export into an action and I can't get the AM code to run from ScriptListener. When you look at what the script is doing it is a time consuming process.
Perhaps Generator could help with the speed issue? File > Generate > Image Assets (ticked)
Then rename your layer groups as required with a .png extension on the end of the group name and you should end up with a new folder of PNG assets in the same path as the original file.
If you had a script that could automatically select all top level layer sets/groups, then you could use a script to add the .png extension as a suffix to all selected sets.
https://raw.githubusercontent.com/Paul-Riggott/PS-Scripts/master/Layer%20Name%20Edit.jsx
Or perhaps look at these layer/set saving scripts if your previous search did not lead you to them:
https://raw.githubusercontent.com/Paul-Riggott/PS-Scripts/master/Layer%20Saver.jsx
or
https://raw.githubusercontent.com/Paul-Riggott/PS-Scripts/master/Layer%20Saver%20Plus.jsx
Copy link to clipboard
Copied
OK, the following script hack will add .png to all layer set names, which will automatically create PNG image assets via Generator.
// https://forums.adobe.com/message/8519528
// https://forums.adobe.com/message/11092165#11092165
// Script hacked to add .png extension to all layer sets for use with Generator to auto create assets
// Does not work with nested sets/groups inside of sets/groups
#target photoshop;
setLayerSetNames();
function setLayerSetNames(){
var ref = new ActionReference();
ref.putProperty( charIDToTypeID("Prpr") , charIDToTypeID( "NmbL" ));
ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
var count = executeActionGet(ref).getInteger(charIDToTypeID('NmbL')) +1;
try{
activeDocument.backgroundLayer;
var i = 0; }catch(e){ var i = 1; };
for(i;i<count;i++){
if(i == 0) continue;
ref = new ActionReference();
ref.putIndex( charIDToTypeID( 'Lyr ' ), i );
var desc = executeActionGet(ref);
var layerName = desc.getString(charIDToTypeID( 'Nm ' ));
var ID = desc.getInteger(stringIDToTypeID( 'layerID' ));
if(layerName.match(/^<\/Layer group/) ) continue;
var layerType = typeIDToStringID(desc.getEnumerationValue( stringIDToTypeID( 'layerSection' )));
var isLayerSet =( layerType == 'layerSectionContent') ? false:true;
if(isLayerSet) setLayerName(ID,layerName.replace(/$/, '\.png'));
};
};
function setLayerName(ID,Name) {
var desc4 = new ActionDescriptor();
var ref3 = new ActionReference();
ref3.putIdentifier(charIDToTypeID('Lyr '), ID);
desc4.putReference( charIDToTypeID('null'), ref3 );
var desc5 = new ActionDescriptor();
desc5.putString( charIDToTypeID('Nm '), Name);
desc4.putObject( charIDToTypeID('T '), charIDToTypeID('Lyr '), desc5 );
executeAction(charIDToTypeID('slct'), desc4, DialogModes.NO);
executeAction( charIDToTypeID('setd'), desc4, DialogModes.NO );
};
Copy link to clipboard
Copied
Thanks again Stephen! I wasn't familiar with file->generate. I'm away from the computer, but I'll give this a try asap and report back! Cheers!
Copy link to clipboard
Copied
Hi Stephan,
Sorry for the delay, I was scrambling to fix another issue. I tried the script you wrote, but get an error
Error 1220:Illegal Arguement
Line: 36
-> desc4.putObject( charIDToTypeID('T '), charIDToTypeID('Lyr '), desc5 );
I'm digging into it now to see if I can figure it out.
Copy link to clipboard
Copied
It worked fine for me in CS6 and CC2018...
All I did was edit one line of code in the original file, that was line 26!
However, the script is a moot point if you don't like the results of the automagical Generator/Image Assets! Did you test by manually adding .png onto the layer set name, or by using the layer renamer script that I posted earlier?
Copy link to clipboard
Copied
The rename script (that's the one that threw the error). I'm running CC 2019 fwiw.
Copy link to clipboard
Copied
Sorry, post #3 or #4?
Copy link to clipboard
Copied
Post #4, but I'm going to try manually adding .png right now to see how the "automagical Generator" process works (I'm hopeful).
Copy link to clipboard
Copied
Ok. Did a quick test. It works perfect, except... it saves the individual layers, not the groups!
In theory I could just flatten all my groups before the save... but then I'm back to needing the script that you wrote (need a script to append .png to all the layers).
I ran your script again, just on the layers. It didn't give an error this time, but it didn't actually do anything either.
Copy link to clipboard
Copied
The Generate > Image Assets is actually an excellent solution for automating export, provided you can find quick/easy ways to name your layers and groups accordingly! (See below)
Heh, wow, Stephan, I can't for the life of me figure out why, but if I remove the comments at the top of your script it works fine... So, your solution effectively works!
The complete solution is as follows.
1. First, run this script (to append .png to all layers sets) - Credit Stephen_A_Marsh and SuperMerlin (Is it possible to convert layerset names to uppercase? )
#target photoshop;
setLayerSetNames();
function setLayerSetNames(){
var ref = new ActionReference();
ref.putProperty( charIDToTypeID("Prpr") , charIDToTypeID( "NmbL" ));
ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
var count = executeActionGet(ref).getInteger(charIDToTypeID('NmbL')) +1;
try{
activeDocument.backgroundLayer;
var i = 0; }catch(e){ var i = 1; };
for(i;i<count;i++){
if(i == 0) continue;
ref = new ActionReference();
ref.putIndex( charIDToTypeID( 'Lyr ' ), i );
var desc = executeActionGet(ref);
var layerName = desc.getString(charIDToTypeID( 'Nm ' ));
var ID = desc.getInteger(stringIDToTypeID( 'layerID' ));
if(layerName.match(/^<\/Layer group/) ) continue;
var layerType = typeIDToStringID(desc.getEnumerationValue( stringIDToTypeID( 'layerSection' )));
var isLayerSet =( layerType == 'layerSectionContent') ? false:true;
if(isLayerSet) setLayerName(ID,layerName.replace(/$/, '\.png'));
};
};
function setLayerName(ID,Name) {
var desc4 = new ActionDescriptor();
var ref3 = new ActionReference();
ref3.putIdentifier(charIDToTypeID('Lyr '), ID);
desc4.putReference( charIDToTypeID('null'), ref3 );
var desc5 = new ActionDescriptor();
desc5.putString( charIDToTypeID('Nm '), Name);
desc4.putObject( charIDToTypeID('T '), charIDToTypeID('Lyr '), desc5 );
executeAction(charIDToTypeID('slct'), desc4, DialogModes.NO);
executeAction( charIDToTypeID('setd'), desc4, DialogModes.NO );
};
Which renames the groups with a .png extension.
2. Make sure Generate Assets > Image Assets is checked. If you want to do this via an action, there are two ways:
var idAdobeScriptAutomationScripts = stringIDToTypeID( "AdobeScriptAutomation Scripts" );
var desc885 = new ActionDescriptor();
var idjsNm = charIDToTypeID( "jsNm" );
desc885.putString( idjsNm, """Image Assets""" );
executeAction( idAdobeScriptAutomationScripts, desc885, DialogModes.NO );
3. Now save. This will save the document as well as an adjacent folder called [documentname]-assets which will include a PNG for every layer and/or group with the .png extension
All of this can be recorded as actions, and therefore completely automate-able.
Copy link to clipboard
Copied
Ugh, Stephan. There's a critical flaw in this method 😞 😞
File > Generate > Image Assets is not a global setting. It's set per file. And you cannot toggle it on via an action. So it does not work for batch automating. So close. Very frustrating. Let me know if you can think of a way around this.
From the original Generator github
Generate Image Assets Functional Spec · adobe-photoshop/generator-assets Wiki · GitHub
The generation of image assets is enabled on a per document basis and is off by default. However, once the user selects Generate -> Image Assets, the plug-in will remember that choice until the user disables image assets ( by selecting Generate -> Image Assets again ) for that document or disables Generator completely, via the preferences dialog.
Copy link to clipboard
Copied
I tested the following two methods on a new file that had never had Generator used and both methods worked in enabling image assets to be ticked/active:
1) insert the menu item into an action
2) ScriptListener recorded AM code:
var idAdobeScriptAutomationScripts = stringIDToTypeID( "AdobeScriptAutomation Scripts" );
var desc885 = new ActionDescriptor();
var idjsNm = charIDToTypeID( "jsNm" );
desc885.putString( idjsNm, """Image Assets""" );
executeAction( idAdobeScriptAutomationScripts, desc885, DialogModes.NO );
Copy link to clipboard
Copied
Brilliant! Works for me as well. I always forget about menu item. I guess mostly because it rarely works for the things I want it to!
Copy link to clipboard
Copied
Is there a way to customize where generated files save to? or can they only save into a file adjacent to the original file?
**Edit
I reviewed this functionality a bit more - it looks like these assets can be put into named subfolders by renaming layers to "[subfolder name]/[asset/layer name].[asset type]"
One problem I've run into with trying to script using this functionality is that I need to toggle on Generate > Image Assets so that renaming the layers generates the assets. The Action manager code shown earlier allows you to add the menu item to an action, but I'm not sure how to toggle this feature on or off - or even how to check if it is toggled on within a script (I don't want to turn it off it is already on.)
Any ideas as to how I could go about doing this to those of you who have worked using this functionality?
Copy link to clipboard
Copied
Some background info:
Introducing Adobe Generator for Photoshop CC | Photoshop Blog by Adobe
Create image assets from layers in Photoshop (anchored link to modifying settings)
However, outside of the basics, you would need to edit the underlying generator code for the base-directory. There are lots of threads and hints over at the feedback site, examples here:
Photoshop Generator: Create Folders in Image Assets | Photoshop Family Customer Community
However, the best that I was able to dig up in a quick search was the following (there are many repos on GitHub regarding generator, there may be others that are better or easier to implement):
Configuration Options · adobe-photoshop/generator-assets Wiki · GitHub
Good luck!
Copy link to clipboard
Copied
I had that same question/problem. Luckily you can simply insert "Generate>Image Assets as a menu item into the action ("Insert Menu Item..." in the action panel)
Or you can insert this AM code/script (from Script Listener)
var idAdobeScriptAutomationScripts = stringIDToTypeID( "AdobeScriptAutomation Scripts" );
var desc885 = new ActionDescriptor();
var idjsNm = charIDToTypeID( "jsNm" );
desc885.putString( idjsNm, """Image Assets""" );
executeAction( idAdobeScriptAutomationScripts, desc885, DialogModes.NO );
Personally I chose to run the script as the script only turns it on, not off. i.e. it's not a toggle (toggle on/off), it's effectively a "set on" script. So if it's "on" it will stay "on" if you run the script again.
Worth noting, the generate image assets state is saved with the file.
From the original Generator github
Generate Image Assets Functional Spec · adobe-photoshop/generator-assets Wiki · GitHub
The generation of image assets is enabled on a per document basis and is off by default. However, once the user selects Generate -> Image Assets, the plug-in will remember that choice until the user disables image assets ( by selecting Generate -> Image Assets again ) for that document or disables Generator completely, via the preferences dialog.
Copy link to clipboard
Copied
The above works....some of the time
When I was testing out my primary function (unlocking the layer, turning on generator, then renaming the layer) it worked fine.
But when I try to insert this function into a loop, I get an 8800 error
- The command “Scripts” is not currently available.
Line: 297
-> executeAction( sTID( "AdobeScriptAutomation Scripts" ), jsDesc, DialogModes.NO );
The basics of my script is this - it currently gets stuck on the turnOnGenerator() which is the Action Manager code you posted above.
var comps = File.openDialog("Select Avatar Comps", undefined, true);
for (var i = 0; i < comps.length; i++){
var f = comps;
openFile(f);
unlockBG();
turnOnGenerator();
var name = f.name.substring( 0, f.name.indexOf('.') ).toUpperCase();
var folder = Folder( f.path+"/"+name );
if ( !folder.exists ){ folder.create() };
exportAvatars( folder.fullName, name );
}
Copy link to clipboard
Copied
I ran into that once myself, but I believe only when I incorrectly tweaked the script. I can't remember the specifics unfortunately.
However, I've run that script as part of a complex action over hundreds of images since this post without problem. I'm running the action as an automated batch.
What do you mean by "loop" exactly? That could be part of the problem. You could also try installing the Script Listener plugin (if you haven't already) and see if your script (when you turn on generate image assets) is somehow different.
Sorry I can't offer better suggestions. Hopefully one of these guys/girls who are more competent with scripting can chime in.
Copy link to clipboard
Copied
For what its worth, here is the AM code from my post #14 converted through Clean SL:
AdobeScriptAutomationScripts();
function AdobeScriptAutomationScripts() {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
descriptor.putString( s2t( "javaScriptName" ), "Image Assets" );
executeAction( s2t( "AdobeScriptAutomation Scripts" ), descriptor, DialogModes.NO );
}
Copy link to clipboard
Copied
Another method to turn on Generate Image Assets via scripting:
$.evalFile(File(app.path + '/Presets/Scripts/generate.jsx'));
Copy link to clipboard
Copied
After a bit more testing, it looks like this bit of AM code can't run after opening a file during a script. Maybe that might help determine the cause as to why it's resulting in the 8800 error. (if anyone knows why this is happening or how I can fix it - it would be greatly appreciated)
Copy link to clipboard
Copied
This is pretty much the exact same function I have for my "turnOnGenerator" function.
But from what I understand there's something preventing "AdobeScriptAutomation Scripts" from executing when you try to run this function after opening a file within a script.
Find more inspiration, events, and resources on the new Adobe Community
Explore Now