Copy link to clipboard
Copied
I am trying to write a script that will batch export all smart objects as files.
Like using the menu -> Layer/SmartObjects/Export Contents... but does it for every Smart Object is the photoshop file.
I have written some code that searches all layers and can determine if it is a Smart Object, but I am stuck on how to export the smart objects content as a file.
var oldPath = activeDocument.path;
function ExportSmartObject(layerNode) {
for (var i=0; i<layerNode.length; i++) {
ExportSmartObject(layerNode[i].layerSets);
for(var layerIndex=0; layerIndex < layerNode[i].artLayers.length; layerIndex++) {
var layer=layerNode[i].artLayers[layerIndex];
if (layer.kind == "LayerKind.SMARTOBJECT") {
alert("Smart Object Found")
// Export Smart Object Content
}
}
}
}
ExportSmartObject(app.activeDocument.layerSets);
Copy link to clipboard
Copied
sTT = stringIDToTypeID; (dsc = new ActionDescriptor())
.putPath(sTT('null'), File('~/desktop/' + i + '.psb'))
executeAction(sTT('placedLayerExportContents'), dsc)
Copy link to clipboard
Copied
Being new to scripting, I would have done it as below... This code is only for the active layer, it would need to be looped/iterated over all layers. Note: There is no error checking for duplicate layer names (therefore the lowest layer in the stack with the same name would be saved).
#target photoshop
// Save Active Smart Object Layer as PSD
app.displayDialogs = DialogModes.NO;
var layer = app.activeDocument.activeLayer;
var layerName = app.activeDocument.activeLayer.name;
var docName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
var savePath = activeDocument.path;
var saveName = File(savePath + "/" + docName + "_" + layerName + "_SmartObjectLayer.psd" );
function SavePSD(saveFile){
psdSaveOptions = new PhotoshopSaveOptions();
psdSaveOptions.embedColorProfile = true;
psdSaveOptions.alphaChannels = true;
psdSaveOptions.layers = true;
app.activeDocument.saveAs(saveFile, psdSaveOptions, true, Extension.LOWERCASE);
};
if (layer.kind == "LayerKind.SMARTOBJECT") {
app.runMenuItem(stringIDToTypeID('placedLayerEditContents'));
SavePSD(saveName);
}
else
{
// Skip any layers that are not Smart Object layers
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
Copy link to clipboard
Copied
I can’t believe how hard it is proving to find easily adaptable code examples to loop/iterate over all layers in a file.
OK, I managed to graft on some code that works, the issue was ESTK throwing up an error that does not happen when the script is run directly from Photoshop.
Copy link to clipboard
Copied
Hi! Many thanks for your work and script. Can you please assist with running it? Im struggling with the same issue How to export contents from all smart objects?
How can I export contents of all smart objects in .psd file? When I run your script it closes my Photoshop window and nothing happen.
Copy link to clipboard
Copied
What is the smart object content?
Linked or embedded?
Raster or vector?
Are the SO layers inside a layer group/set?
Copy link to clipboard
Copied
Thanks for the bump, I just realised that I never updated my previous post with the updated code to loop over all layers:
//community.adobe.com/t5/Photoshop/Script-to-Export-Contents-of-all-Smart-Objects-in-a-Photoshop/td-p/10644877
//Script to Export Contents of all Smart Objects in a Photoshop file
// Note: There is no error checking for duplicate layer names (therefore the lowest layer in the stack with the same name would be saved).
#target photoshop
// Start Loop over all layers
var doc = app.activeDocument;
var layerVisble
doc.activeLayer = doc.layers[0];
for(var i=0;i<doc.layers.length;i++){
doc.activeLayer = doc.layers[i];
layerVisible = doc.activeLayer.visible;
doc.activeLayer.visible = true;
run ();
doc.activeLayer.visible = layerVisible;
}
// End Loop over all layers
// Start Main Code
function run(){
app.displayDialogs = DialogModes.NO;
var layer = app.activeDocument.activeLayer;
var layerName = app.activeDocument.activeLayer.name;
var docName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
var savePath = activeDocument.path;
var saveName = File(savePath + "/" + docName + "_" + layerName + "_SmartObjectLayer.psd" );
function SavePSD(saveFile){
psdSaveOptions = new PhotoshopSaveOptions();
psdSaveOptions.embedColorProfile = true;
psdSaveOptions.alphaChannels = true;
psdSaveOptions.layers = true;
app.activeDocument.saveAs(saveFile, psdSaveOptions, true, Extension.LOWERCASE);
};
if (layer.kind == "LayerKind.SMARTOBJECT") {
app.runMenuItem(stringIDToTypeID('placedLayerEditContents'));
SavePSD(saveName);
}
else
{
// Skip any layers that are not Smart Object layers
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
// End Main Code
Copy link to clipboard
Copied
Being new to scripting, my script is very simple...
The main file that holds the SO layers must be saved, there is no error checking in the script for this.
If there are layer sets/groups, then the file will close and nothing will be saved – there is no error checking in the script for this.
The script simply saves a PSD version of each smart object layer to the original document path. If your main file is on the desktop, then that is where each smart object will be saved. This behaviour is easy to change if desired. The SO files could be saved as a PSD to a folder of your choice via a window to select the save folder. Or the files could be saved to a fixed path/location. Or the files could be saved into a sub-folder in the folder where the main file is currently saved.
The saved PSD files will have the following suffix added to the layer name:
_SmartObjectLayer.psd
As mentioned in the script comments, if you have multiple SO layers with exactly the same name, then only the lowest layer in the layer stack would be saved.
Copy link to clipboard
Copied
Many thanks for your work Stephen!
Can you please clarify "If there are layer sets/groups, then the file will close and nothing will be saved – there is no error checking in the script for this."
You mean if I have groups like this?
Copy link to clipboard
Copied
No, layer sets/groups are "folders" that can contain layers or nested folders/layers as in this image:
The problem was my original code was too simplistic and did not take layer groups/sets into account. I have since updated the code and will repost it shortly.
Copy link to clipboard
Copied
This updated code will process standard layers and layers inside layer sets or nested layer sets. All of the previous limitations still apply, such as duplicate layer names are not supported.
/*
//community.adobe.com/t5/Photoshop/Script-to-Export-Contents-of-all-Smart-Objects-in-a-Photoshop/td-p/10644877
Note: There is no error checking for duplicate layer names (therefore the lowest layer in the stack with the same name would be saved).
Original script revised to include smart objects in layer sets (layer groups)... With special thanks to Christoph Pfaffenbichler for the recursive SO processing framework:
feedback.photoshop.com/photoshop_family/topics/rasterize_all_smart_objects_in_a_psd_file_to_shrink_down_its_size
*/
#target photoshop
var myDocument = app.activeDocument;
myDocument.suspendHistory("processAllSmartObjects", "processSOLayers(myDocument)");
////// Function to process all smart objects //////
function processSOLayers(theParent) {
for (var m = theParent.layers.length - 1; m >= 0; m--) {
var theLayer = theParent.layers[m];
// apply the funtion to layersets;
if (theLayer.typename == "ArtLayer") {
if (theLayer.kind == LayerKind.SMARTOBJECT) {
var theVisibility = theLayer.visible;
myDocument.activeLayer = theLayer;
// Run the function to save out the layers
soLayerSaver();
// Start Main Function
function soLayerSaver() {
app.displayDialogs = DialogModes.NO;
var layer = app.activeDocument.activeLayer;
var layerName = app.activeDocument.activeLayer.name;
var docName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
var savePath = activeDocument.path;
var saveName = File(savePath + "/" + docName + "_" + layerName + "_SmartObjectLayer.psd");
function SavePSD(saveFile) {
psdSaveOptions = new PhotoshopSaveOptions();
psdSaveOptions.embedColorProfile = true;
psdSaveOptions.alphaChannels = true;
psdSaveOptions.layers = true;
app.activeDocument.saveAs(saveFile, psdSaveOptions, true, Extension.LOWERCASE);
};
if (layer.kind == "LayerKind.SMARTOBJECT") {
app.runMenuItem(stringIDToTypeID('placedLayerEditContents'));
SavePSD(saveName);
} else {
// Skip any layers that are not Smart Object layers
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
// Reset visibility;
theLayer.visible = theVisibility;
};
} else {
processSOLayers(theLayer)
}
}
return
};
Copy link to clipboard
Copied
I hope you dont mind. I added something parts from "Layer Comps to Files" so when there are layers with the same name it will index those. I added a little timer i found in one of Tom Ruarks script, so i could time the difference. I dint really notice a big impact of the extra check. You can find that check in the beginning of the main function.
Below you see layers with the same name get an index, therefor they wont be overwritten. I thought that would be nice
/*
//community.adobe.com/t5/Photoshop/Script-to-Export-Contents-of-all-Smart-Objects-in-a-Photoshop/td-p/10644877
Note: There is no error checking for duplicate layer names (therefore the lowest layer in the stack with the same name would be saved).
Original script revised to include smart objects in layer sets (layer groups)... With special thanks to Christoph Pfaffenbichler for the recursive SO processing framework:
feedback.photoshop.com/photoshop_family/topics/rasterize_all_smart_objects_in_a_psd_file_to_shrink_down_its_size
*/
#target photoshop
var myDocument = app.activeDocument;
myDocument.suspendHistory("processAllSmartObjects", "processSOLayers(myDocument)");
///////////////////////////////////////////////////////////////////////////////
// Object for timing things in JavaScript
///////////////////////////////////////////////////////////////////////////////
function Timer() {
// member properties
this.startTime = new Date();
this.endTime = new Date();
// member methods
// reset the start time to now
this.start = function () { this.startTime = new Date(); }
// reset the end time to now
this.stop = function () { this.endTime = new Date(); }
// get the difference in milliseconds between start and stop
this.getTime = function () { return (this.endTime.getTime() - this.startTime.getTime()) / 1000; }
// get the current elapsed time from start to now, this sets the endTime
this.getElapsed = function () { this.endTime = new Date(); return this.getTime(); }
}
// Timer Tom Ruark
// Source: Getter.jsx
var totalTime = new Timer();
///////////////////////////////////////////////////////////////////////////////
// Function: countLayerNames
// Usage: Count the comps' names collecting duplicates
// Input: collection of comps
// Return: object with names as keys and as values, an object with total count and an index (for incrementing during file naming)
// Source: Layer-Comps-To-Files.jsx
///////////////////////////////////////////////////////////////////////////////
function countLayerNames(list){
var obj = {};
for(var i = 0; i<list.length; i++){
var name = list[i].name;
if(name in obj){
obj[name].total += 1;
} else {
obj[name] = {total: 1, nameIndex: 1};
}
}
return obj;
}
////// Function to process all smart objects //////
function processSOLayers(theParent) {
var totalTime = new Timer(); // Tom Ruark > Getter.jsx
var count = countLayerNames(theParent.layers);
for (var m = theParent.layers.length - 1; m >= 0; m--) {
var theLayer = theParent.layers[m];
// apply the funtion to layersets;
if (theLayer.typename == "ArtLayer") {
if (theLayer.kind == LayerKind.SMARTOBJECT) {
var theVisibility = theLayer.visible;
myDocument.activeLayer = theLayer;
// Run the function to save out the layers
soLayerSaver();
// Start Main Function
function soLayerSaver() {
app.displayDialogs = DialogModes.NO;
var layer = app.activeDocument.activeLayer;
var layerName = app.activeDocument.activeLayer.name;
var docName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
var savePath = activeDocument.path;
// check for dupli names
var nameEntry = count[theLayer.name];
if(nameEntry.total > 1) { // if > 2 then we add index numbers
layerName += '_' + nameEntry.nameIndex++;
}
var saveName = File(savePath + "/" + docName + "_" + layerName + "_SmartObjectLayer.psd");
function SavePSD(saveFile) {
psdSaveOptions = new PhotoshopSaveOptions();
psdSaveOptions.embedColorProfile = true;
psdSaveOptions.alphaChannels = true;
psdSaveOptions.layers = true;
app.activeDocument.saveAs(saveFile, psdSaveOptions, true, Extension.LOWERCASE);
};
if (layer.kind == "LayerKind.SMARTOBJECT") {
app.runMenuItem(stringIDToTypeID('placedLayerEditContents'));
SavePSD(saveName);
} else {
// Skip any layers that are not Smart Object layers
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
// Reset visibility;
theLayer.visible = theVisibility;
};
} else {
processSOLayers(theLayer)
}
}
alert("Script Time: " + totalTime.getElapsed())
};
Copy link to clipboard
Copied
No, I don't mind if you improve my scripts! Can I notify you whenever I write a script and ask for you to improve it? I am of course (mostly) joking...
Copy link to clipboard
Copied
Sure if i get a coffee every time 🙂
Copy link to clipboard
Copied
I have wrapped the updated script in a try/catch as it would previously silently fail if being run on an unsaved document.
/*
//community.adobe.com/t5/Photoshop/Script-to-Export-Contents-of-all-Smart-Objects-in-a-Photoshop/td-p/10644877
Original script revised to include smart objects in layer sets (layer groups)... With special thanks to Christoph Pfaffenbichler for the recursive SO processing framework:
feedback.photoshop.com/photoshop_family/topics/rasterize_all_smart_objects_in_a_psd_file_to_shrink_down_its_size
January 3rd 2021: Thanks to schroef for adding handling for duplicate named layers!
*/
#target photoshop
/* Start Open/Saved Document Error Check - Part A: Try */
savedDoc();
function savedDoc() {
try {
app.activeDocument.path;
/* Finish Open/Saved Document Error Check - Part A: Try */
/* Main Code Start */
var myDocument = app.activeDocument;
myDocument.suspendHistory("processAllSmartObjects", "processSOLayers(myDocument)");
///////////////////////////////////////////////////////////////////////////////
// Object for timing things in JavaScript
///////////////////////////////////////////////////////////////////////////////
function Timer() {
// member properties
this.startTime = new Date();
this.endTime = new Date();
// member methods
// reset the start time to now
this.start = function () { this.startTime = new Date() };
// reset the end time to now
this.stop = function () { this.endTime = new Date() };
// get the difference in milliseconds between start and stop
this.getTime = function () { return (this.endTime.getTime() - this.startTime.getTime()) / 1000 };
// get the current elapsed time from start to now, this sets the endTime
this.getElapsed = function () { this.endTime = new Date(); return this.getTime() };
}
// Timer Tom Ruark
// Source: Getter.jsx
var totalTime = new Timer();
///////////////////////////////////////////////////////////////////////////////
// Function: countLayerNames
// Usage: Count the comps' names collecting duplicates
// Input: collection of comps
// Return: object with names as keys and as values, an object with total count and an index (for incrementing during file naming)
// Source: Layer-Comps-To-Files.jsx
///////////////////////////////////////////////////////////////////////////////
function countLayerNames(list){
var obj = {};
for(var i = 0; i<list.length; i++){
var name = list[i].name;
if(name in obj){
obj[name].total += 1;
} else {
obj[name] = {total: 1, nameIndex: 1};
}
}
return obj;
}
////// Function to process all smart objects //////
function processSOLayers(theParent) {
var totalTime = new Timer(); // Tom Ruark > Getter.jsx
var count = countLayerNames(theParent.layers);
for (var m = theParent.layers.length - 1; m >= 0; m--) {
var theLayer = theParent.layers[m];
// apply the funtion to layersets;
if (theLayer.typename == "ArtLayer") {
if (theLayer.kind == LayerKind.SMARTOBJECT) {
var theVisibility = theLayer.visible;
myDocument.activeLayer = theLayer;
// Run the function to save out the layers
soLayerSaver();
// Start Main Function
function soLayerSaver() {
app.displayDialogs = DialogModes.NO;
var layer = app.activeDocument.activeLayer;
var layerName = app.activeDocument.activeLayer.name;
var docName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
var savePath = activeDocument.path;
// check for dupli names
var nameEntry = count[theLayer.name];
if(nameEntry.total > 1) { // if > 2 then we add index numbers
layerName += '_' + nameEntry.nameIndex++;
}
var saveName = File(savePath + "/" + docName + "_" + layerName + "_SmartObjectLayer.psd");
function SavePSD(saveFile) {
psdSaveOptions = new PhotoshopSaveOptions();
psdSaveOptions.embedColorProfile = true;
psdSaveOptions.alphaChannels = true;
psdSaveOptions.layers = true;
app.activeDocument.saveAs(saveFile, psdSaveOptions, true, Extension.LOWERCASE);
}
if (layer.kind == "LayerKind.SMARTOBJECT") {
app.runMenuItem(stringIDToTypeID('placedLayerEditContents'));
SavePSD(saveName);
} else {
// Skip any layers that are not Smart Object layers
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
// Reset visibility;
theLayer.visible = theVisibility;
}
} else {
processSOLayers(theLayer);
}
}
alert("Script Time: " + totalTime.getElapsed());
}
/* Main Code Finish */
}
/* Start Open/Saved Document Error Check - Part B: Catch */
catch (err) {
alert("An image must be open and saved before running this script!");
}
}
/* Finish Open/Saved Document Error Check - Part B: Catch */
Copy link to clipboard
Copied
Fantastic. Just what I needed when I found that Smartobject didn't export fully but cropped, if they were bigger than the visible canvas, when using Quick Export or Export As or any of the other options. So I made tiny updates to the script to export png where the filename is just the layername, and deactivated the timer. Thanks!
/*MODIFIED to export png using layer name only, deactivated Timer
//community.adobe.com/t5/Photoshop/Script-to-Export-Contents-of-all-Smart-Objects-in-a-Photoshop/td-p/10644877
Original script revised to include smart objects in layer sets (layer groups)... With special thanks to Christoph Pfaffenbichler for the recursive SO processing framework:
feedback.photoshop.com/photoshop_family/topics/rasterize_all_smart_objects_in_a_psd_file_to_shrink_down_its_size
January 3rd 2021: Thanks to schroef for adding handling for duplicate named layers!
*/
#target photoshop
/* Start Open/Saved Document Error Check - Part A: Try */
savedDoc();
function savedDoc() {
try {
app.activeDocument.path;
/* Finish Open/Saved Document Error Check - Part A: Try */
/* Main Code Start */
var myDocument = app.activeDocument;
myDocument.suspendHistory("processAllSmartObjects", "processSOLayers(myDocument)");
///////////////////////////////////////////////////////////////////////////////
// Object for timing things in JavaScript
///////////////////////////////////////////////////////////////////////////////
function Timer() {
// member properties
this.startTime = new Date();
this.endTime = new Date();
// member methods
// reset the start time to now
this.start = function () { this.startTime = new Date() };
// reset the end time to now
this.stop = function () { this.endTime = new Date() };
// get the difference in milliseconds between start and stop
this.getTime = function () { return (this.endTime.getTime() - this.startTime.getTime()) / 1000 };
// get the current elapsed time from start to now, this sets the endTime
this.getElapsed = function () { this.endTime = new Date(); return this.getTime() };
}
// Timer Tom Ruark
// Source: Getter.jsx
var totalTime = new Timer();
///////////////////////////////////////////////////////////////////////////////
// Function: countLayerNames
// Usage: Count the comps' names collecting duplicates
// Input: collection of comps
// Return: object with names as keys and as values, an object with total count and an index (for incrementing during file naming)
// Source: Layer-Comps-To-Files.jsx
///////////////////////////////////////////////////////////////////////////////
function countLayerNames(list){
var obj = {};
for(var i = 0; i<list.length; i++){
var name = list[i].name;
if(name in obj){
obj[name].total += 1;
} else {
obj[name] = {total: 1, nameIndex: 1};
}
}
return obj;
}
////// Function to process all smart objects //////
function processSOLayers(theParent) {
//var totalTime = new Timer(); // Tom Ruark > Getter.jsx
var count = countLayerNames(theParent.layers);
for (var m = theParent.layers.length - 1; m >= 0; m--) {
var theLayer = theParent.layers[m];
// apply the funtion to layersets;
if (theLayer.typename == "ArtLayer") {
if (theLayer.kind == LayerKind.SMARTOBJECT) {
var theVisibility = theLayer.visible;
myDocument.activeLayer = theLayer;
// Run the function to save out the layers
soLayerSaver();
// Start Main Function
function soLayerSaver() {
app.displayDialogs = DialogModes.NO;
var layer = app.activeDocument.activeLayer;
var layerName = app.activeDocument.activeLayer.name;
var docName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
var savePath = activeDocument.path;
// check for dupli names
var nameEntry = count[theLayer.name];
if(nameEntry.total > 1) { // if > 2 then we add index numbers
layerName += '_' + nameEntry.nameIndex++;
}
var saveName = File(savePath + "/" + layerName + ".png");
function SavePng(saveFile) {
pngSaveOptions = new PNGSaveOptions();
pngSaveOptions.compression = 0;
pngSaveOptions.interlaced = false;
app.activeDocument.saveAs(saveFile, pngSaveOptions, true, Extension.LOWERCASE);
}
if (layer.kind == "LayerKind.SMARTOBJECT") {
app.runMenuItem(stringIDToTypeID('placedLayerEditContents'));
SavePng(saveName);
} else {
// Skip any layers that are not Smart Object layers
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
// Reset visibility;
theLayer.visible = theVisibility;
}
} else {
processSOLayers(theLayer);
}
}
//alert("Script Time: " + totalTime.getElapsed());
}
/* Main Code Finish */
}
/* Start Open/Saved Document Error Check - Part B: Catch */
catch (err) {
alert("An image must be open and saved before running this script!");
}
}
/* Finish Open/Saved Document Error Check - Part B: Catch */
Copy link to clipboard
Copied
Hi - the last code creates very big files (100mb png)
and it crashes photoshop on pdf or AI embedded files.
;(
Copy link to clipboard
Copied
Perhaps check your original file and see what happening in the xdata. See the file info, if in one of tans is a huge list with garbled data, it needs to be cleaned before you save. Otherwise it could be, this is saved as meta data. It could also be it's saving it as a transparent PNG.
Copy link to clipboard
Copied
Thanks so much to all who worked on this script, it is super-helpful! I used the second-to-last iteration as I'm not dealing with pngs and it seems maybe that one had a glitch.
Copy link to clipboard
Copied
This thread is so awesome. Everyone contributing and helping. This script worked great for what I wanted. Thanks everyone! Maybe a GitHub would be good for this kind of thing?