Photoshop: Make an existing script work with multiple layers?

I put together a script to rename the (single) selected layer in Photoshop .

tell application id "com.adobe.Photoshop"
	activate
	try
		set currentDocument to the current document
	on error
		display dialog "No document open in Photoshop. ¬
		Nothing to do." giving up after 3 buttons {"Ok"}
		return
	end try
	
	tell currentDocument
		set newLayerName to "This is the new name"
		set selectedLayer to (get current layer)
			set name of selectedLayer to newLayerName
	end tell
end tell

It works fine, and now I’m trying to make a new one that would rename multiple selected layers. Here’s what I’ve tried, but I’m pretty bad with AppleScript & haven’t managed to get to work.


tell application id "com.adobe.Photoshop"
    activate
    try
        set currentDocument to the current document
    on error
        display dialog "No document open in Photoshop. ¬
        Nothing to do." giving up after 3 buttons {"Ok"}
        return
    end try
    
    tell currentDocument
        set newLayerName to "This is the new name"
        set selectedLayers to (get layers as list)
        
        repeat with thisLayer in selectedLayers
            set thisLayer to (get current layer)
            set name of thisLayer to clipboardName
        end repeat
    end tell
    
end tell

Any help would be greatly appreciated.

This one gets tricky fast because Photoshop doesn’t give Applescript access to the selected layers.

The selected layers can be retrieved by name or index via Javascript Action Manager code.

This quick script works in a limited way:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

set selectedLayers to get_PS_layer_names()
set repeatCount to 0
tell application "/Applications/Adobe Photoshop CS6/Adobe Photoshop CS6.app"
	set currentDoc to the current document
	tell currentDoc
		repeat with aLayer in selectedLayers
			set repeatCount to repeatCount + 1
			set the name of layer aLayer to ("New Name " & repeatCount)
		end repeat
	end tell
end tell


on get_PS_layer_names()
	tell application "Adobe Photoshop CS6"
		tell the current document
			set selectedLayers to paragraphs of (do javascript "
var typeDocument        = stringIDToTypeID('document');
var typeItemIndex       = stringIDToTypeID('itemIndex');
var typeLayer           = stringIDToTypeID('layer');
var typeName            = stringIDToTypeID('name');
var typeOrdinal         = stringIDToTypeID('ordinal');
var typeProperty        = stringIDToTypeID('property');
var typeTarget          = stringIDToTypeID('targetEnum');
var typeTargetLayers    = stringIDToTypeID('targetLayers');
var selectedLayers  = new Array();
var actionRef           = new ActionReference();

actionRef.putEnumerated(typeDocument, typeOrdinal, typeTarget);
var actionDesc = executeActionGet(actionRef);

if(actionDesc.hasKey(typeTargetLayers) ){
    actionDesc = actionDesc.getList(typeTargetLayers);
    var c = actionDesc.count

    for(var i=0;i<c;i++){
        try{
            activeDocument.backgroundLayer;
            selectedLayers.push(actionDesc.getReference( i ).getIndex() );
        }catch(e){
            selectedLayers.push(actionDesc.getReference( i ).getIndex()+1 );
        }
    }
}else{
    var actionRef = new ActionReference();
    actionRef.putProperty(typeProperty , typeItemIndex);
    actionRef.putEnumerated(typeLayer, typeOrdinal, typeTarget);
    try{
        activeDocument.backgroundLayer;
        selectedLayers.push( executeActionGet(actionRef).getInteger(typeItemIndex)-1);
    }catch(e){
        selectedLayers.push( executeActionGet(actionRef).getInteger(typeItemIndex));
    }
}

var selectedLayerNames = new Array();

for (var a in selectedLayers){
    var ref = new ActionReference();   
    ref.putIndex(typeLayer, Number(selectedLayers[a]) );  
    var layerName = executeActionGet(ref).getString(typeName);
        selectedLayerNames.push(layerName);  
}

selectedLayerNames.join('\\n');
")
			
		end tell
	end tell
end get_PS_layer_names

But it fails if any of the selected layers are grouped (inside a layer set), or if any of the selected layers already have the same name when the script is run.

Using Action Manager code to get the selected layers by index and using index to target them for renaming would probably be a lot better, but I couldn’t quickly google up the AM code to return the indexes of the selected layers, and I’m not good enough with AM code to write that.

Maybe this works for your needs?

I didn’t know what you wanted to use for the new layer names. Your “clipboardName” variable is undefined.

I found the AM code.

Here’s a version that renames all selected layers, regardless of how they’re nested into layer sets, and it doesn’t matter if there are layers with duplicate names.

Note: I was tired and couldn’t immediately figure out how to do this with a single repeat loop to handle both the initial layer set of the whole document, and the lower level layer sets, in the recursive function. So it’s two functions, which seemed a little redundant as I was writing it. After writing it, I realized I just need to always feed a list of layers into the function, never a single “layer set” as the input. So it could be re-written to be significantly more elegant but… eh, it’s late, and it works. Maybe I’ll fix it later.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

global foundLayer
set foundLayer to false
set selectedLayerIndexes to get_selected_layer_indexes()
set selectedLayerReferences to {}

repeat with aLayerIndex in selectedLayerIndexes
	copy find_layer_by_itemindex(aLayerIndex) to end of selectedLayerReferences
end repeat


tell application "/Applications/Adobe Photoshop CS6/Adobe Photoshop CS6.app"
	set currentDoc to the current document
	tell currentDoc
		set repeatCount to 0
		repeat with i from (count of selectedLayerReferences) to 1 by -1
			set aLayer to item i of selectedLayerReferences
			set repeatCount to repeatCount + 1
			set the name of aLayer to ("New Name " & repeatCount)
		end repeat
	end tell
end tell


on find_layer_by_itemindex(theItemIndex)
	set theItemIndex to theItemIndex as number
	tell application "/Applications/Adobe Photoshop CS6/Adobe Photoshop CS6.app"
		set currentDoc to the current document
		tell currentDoc
			set layerCount to count of the layers
			repeat with i from 1 to layerCount
				set aLayer to layer i
				if the itemindex of aLayer is theItemIndex then
					return aLayer
				else
					if class of aLayer is layer set then
						set foundLayer to false
						set layerReturned to my recurse_layer_sets(aLayer, theItemIndex)
						if layerReturned ≠ false then return layerReturned
					end if
				end if
			end repeat
		end tell
	end tell
end find_layer_by_itemindex

on recurse_layer_sets(layerSet, theItemIndex)
	if foundLayer ≠ false then return foundLayer
	tell application "/Applications/Adobe Photoshop CS6/Adobe Photoshop CS6.app"
		set currentDoc to the current document
		tell currentDoc
			set layersetCount to the count of the layers of layerSet
			repeat with i from 1 to layersetCount
				set aLayer to layer i of layerSet
				if the itemindex of aLayer as number is theItemIndex then
					set foundLayer to aLayer
					exit repeat
				else
					if the class of aLayer is layer set then my recurse_layer_sets(aLayer, theItemIndex)
				end if
			end repeat
			return foundLayer
		end tell
	end tell
end recurse_layer_sets

on get_selected_layer_indexes()
	tell application "/Applications/Adobe Photoshop CS6/Adobe Photoshop CS6.app"
		set selectedLayers to do javascript "
getSelectedLayersIdx()
function getSelectedLayersIdx() {  
    var selectedLayers = new Array;  
    var ref = new ActionReference();  
    ref.putEnumerated(charIDToTypeID(\"Dcmn\"), charIDToTypeID(\"Ordn\"), charIDToTypeID(\"Trgt\"));  
    var desc = executeActionGet(ref);  
    if (desc.hasKey(stringIDToTypeID(\"targetLayers\"))) {  
        desc = desc.getList(stringIDToTypeID(\"targetLayers\"));  
        var c = desc.count  
        var selectedLayers = new Array();  
        for (var i = 0; i < c; i++) {  
            try {  
                docRef.backgroundLayer;  
                selectedLayers.push(desc.getReference(i).getIndex());  
            } catch (e) {  
                selectedLayers.push(desc.getReference(i).getIndex() + 1);  
            }  
        }  
    } else {  
        var ref = new ActionReference();  
        ref.putProperty(charIDToTypeID(\"Prpr\"), charIDToTypeID(\"ItmI\"));  
        ref.putEnumerated(charIDToTypeID(\"Lyr \"), charIDToTypeID(\"Ordn\"), charIDToTypeID(\"Trgt\"));  
        try {  
            docRef.backgroundLayer;  
            selectedLayers.push(executeActionGet(ref).getInteger(charIDToTypeID(\"ItmI\")) - 1);  
        } catch (e) {  
            selectedLayers.push(executeActionGet(ref).getInteger(charIDToTypeID(\"ItmI\")));  
        }  
    }  
    return selectedLayers;  
}  "
	end tell
	return text_to_list(selectedLayers, ",")
end get_selected_layer_indexes

on text_to_list(someText, aSeparator)
	set {delimitHolder, AppleScript's text item delimiters} to {AppleScript's text item delimiters, aSeparator}
	set outputList to the text items of someText
	set AppleScript's text item delimiters to delimitHolder
	return outputList
end text_to_list

Eh, I went ahead and cleaned it up.


use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

global foundLayer -- recursive function to drill down arbitrary layer nesting, setting global to exit function regardless of how deep the calls go
set selectedLayerIndexes to get_selected_layer_indexes() -- get the index numbers for all selected layers
set selectedLayerReferences to {}
tell application "/Applications/Adobe Photoshop CS6/Adobe Photoshop CS6.app" to tell the current document to set allLayers to the layers

-- convert each layer index to a layer reference
repeat with aLayerIndex in selectedLayerIndexes
	set foundLayer to false
	copy recurse_layer_sets(allLayers, aLayerIndex) to end of selectedLayerReferences
end repeat

-- rename each layer
tell application "/Applications/Adobe Photoshop CS6/Adobe Photoshop CS6.app"
	set currentDoc to the current document
	tell currentDoc
		set repeatCount to 0
		repeat with i from (count of selectedLayerReferences) to 1 by -1
			set aLayer to item i of selectedLayerReferences
			set repeatCount to repeatCount + 1
			set the name of aLayer to ("New Name " & repeatCount)
		end repeat
	end tell
end tell

-- drills through all layer sets to look up layers by itemindex and return an Applescript reference to the layer
on recurse_layer_sets(layerList, theItemIndex)
	set theItemIndex to theItemIndex as number -- it's already an integer; not sure why this is necessary
	if foundLayer is not false then return foundLayer
	tell application "/Applications/Adobe Photoshop CS6/Adobe Photoshop CS6.app"
		set currentDoc to the current document
		tell currentDoc
			set layerGroupCount to the count of layerList
			repeat with i from 1 to layerGroupCount
				set aLayer to item i of layerList
				if the (itemindex of aLayer) as number is theItemIndex then
					set foundLayer to aLayer
					exit repeat
				else
					if the class of aLayer is layer set then my recurse_layer_sets((layers of aLayer), theItemIndex)
				end if
			end repeat
		end tell
	end tell
	return foundLayer
end recurse_layer_sets

-- AM code to get the indexes of all selected layers
on get_selected_layer_indexes()
	tell application "/Applications/Adobe Photoshop CS6/Adobe Photoshop CS6.app"
		set selectedLayers to do javascript "
getSelectedLayersIdx()
function getSelectedLayersIdx() {  
    var selectedLayers = new Array;  
    var ref = new ActionReference();  
    ref.putEnumerated(charIDToTypeID(\"Dcmn\"), charIDToTypeID(\"Ordn\"), charIDToTypeID(\"Trgt\"));  
    var desc = executeActionGet(ref);  
    if (desc.hasKey(stringIDToTypeID(\"targetLayers\"))) {  
        desc = desc.getList(stringIDToTypeID(\"targetLayers\"));  
        var c = desc.count  
        var selectedLayers = new Array();  
        for (var i = 0; i < c; i++) {  
            try {  
                docRef.backgroundLayer;  
                selectedLayers.push(desc.getReference(i).getIndex());  
            } catch (e) {  
                selectedLayers.push(desc.getReference(i).getIndex() + 1);  
            }  
        }  
    } else {  
        var ref = new ActionReference();  
        ref.putProperty(charIDToTypeID(\"Prpr\"), charIDToTypeID(\"ItmI\"));  
        ref.putEnumerated(charIDToTypeID(\"Lyr \"), charIDToTypeID(\"Ordn\"), charIDToTypeID(\"Trgt\"));  
        try {  
            docRef.backgroundLayer;  
            selectedLayers.push(executeActionGet(ref).getInteger(charIDToTypeID(\"ItmI\")) - 1);  
        } catch (e) {  
            selectedLayers.push(executeActionGet(ref).getInteger(charIDToTypeID(\"ItmI\")));  
        }  
    }  
    return selectedLayers;  
}  "
	end tell
	set selectedLayersListText to text_to_list(selectedLayers, ",") -- Javascript spits out comma delimited list
	-- Javascript also spits out numbers as strings; switch to integers
	set selectedLayersList to {}
	repeat with anItem in selectedLayersListText
		set selectedLayersList to selectedLayersList & (anItem as number)
	end repeat
	return selectedLayersList
end get_selected_layer_indexes

-- converts text delimited strings to AS lists
on text_to_list(someText, aSeparator)
	set {delimitHolder, AppleScript's text item delimiters} to {AppleScript's text item delimiters, aSeparator}
	set outputList to the text items of someText
	set AppleScript's text item delimiters to delimitHolder
	return outputList
end text_to_list

Thanks a lot for the help. So I guess I was way off with my attempts! :smiley:

I tried the first one but got an error message:

error "Can’t set name of \"And this is the third layer\" to \"New Name 1\"." number -10006 from name of "And this is the third layer"

But the last version works great, just what I wanted! Thanks a lot! :smiley:

Ah, this is the ‘proper’ variable that I’ll use quite often (set clipboardName to the clipboard). E.g. use the name copied to the clipboard to rename the selected layers in Photoshop.