Skip to main content
Known Participant
March 8, 2019
Question

Accessing all the layers in all the layer sets

  • March 8, 2019
  • 4 replies
  • 8755 views

Hi Everyone,

I am writing a script that searches through all the layers and changes labels and names based on blend mode, layer types, etc.

I can get all the layers not in sets into an array and act upon them.

I can get all the layersets into an array. ( I want to put layersets in an array so I can do things like count them, search, them, rename later)

I can't figure out how to drill down into the layersets so I can get the contained layers pushed into the layer array.

I thought it might be something like this, but this doesn't work.

var layerSetArray = [ ];

// how I got the layers:

for(var i = 0; i < doc.artLayers.length; i++)

//how I got the layersets:

for( var i = doc.layerSets.length-1; 0 <= i; i--){

   var layerSetName = doc.layerSets[i].toString();

   layerSetArray.push(layerSetName)

}

//what I can't figure out: (doesn't work)

for( var i = doc.layerSetArray.length; 0 < ii++){

   var nestedLayer = layerSetArray[i].artLayers.name

   layerSetArray.push(nestedLayer)

}

Any suggestions would be greatly appreciated.

Thanks for reading!

This topic has been closed for replies.

4 replies

Legend
March 24, 2020

I like getting the list of layers through AM - it's fast and it's one loop:

 

var AM = new ActionManager,
    layerSetArray = app.documents.length ? getAllLayers() : []

function getAllLayers() {
    var output = [],
        from = AM.getDocProperty('hasBackgroundLayer') ? 0 : 1,
        len = AM.getDocProperty('numberOfLayers')

    for (var i = from; i <= len; i++) {
        if (AM.getLayerProperty('layerSection', i) == 'layerSectionEnd') continue;

        var id = AM.getLayerProperty('layerID', i),
            title = AM.getLayerProperty('name', i)

        output.push({ id: id, name: title })
    }
    return output
}

function ActionManager() {

    this.getDocProperty = function (property) {
        property = s2t(property)
        var ref = new ActionReference()
        ref.putProperty(s2t("property"), property)
        ref.putEnumerated(s2t("document"), s2t("ordinal"), s2t("targetEnum"))
        return getDescValue(executeActionGet(ref), property)
    }

    this.getLayerProperty = function (property, index) {
        property = s2t(property)
        var ref = new ActionReference()
        ref.putProperty(s2t("property"), property)
        ref.putIndex(s2t("layer"), index)
        return getDescValue(executeActionGet(ref), property)
    }

    this.selectLayerById = function (id, addToSelection) {
        var desc = new ActionDescriptor()
        var ref = new ActionReference()
        ref.putIdentifier(s2t("layer"), id)
        desc.putReference(s2t("null"), ref)
        if (addToSelection) desc.putEnumerated(s2t("selectionModifier"), s2t("addToSelectionContinuous"), s2t("addToSelection"))
        executeAction(s2t("select"), desc, DialogModes.NO);
    }

    function getDescValue(desc, property) {

        switch (desc.getType(property)) {
            case DescValueType.OBJECTTYPE:
                return (desc.getObjectValue(property));
                break;
            case DescValueType.LISTTYPE:
                return desc.getList(property);
                break;
            case DescValueType.REFERENCETYPE:
                return desc.getReference(property);
                break;
            case DescValueType.BOOLEANTYPE:
                return desc.getBoolean(property);
                break;
            case DescValueType.STRINGTYPE:
                return desc.getString(property);
                break;
            case DescValueType.INTEGERTYPE:
                return desc.getInteger(property);
                break;
            case DescValueType.LARGEINTEGERTYPE:
                return desc.getLargeInteger(property);
                break;
            case DescValueType.DOUBLETYPE:
                return desc.getDouble(property);
                break;
            case DescValueType.ALIASTYPE:
                return desc.getPath(property);
                break;
            case DescValueType.CLASSTYPE:
                return desc.getClass(property);
                break;
            case DescValueType.UNITDOUBLE:
                return (desc.getUnitDoubleValue(property));
                break;
            case DescValueType.ENUMERATEDTYPE:
                return (t2s(desc.getEnumerationValue(property)));
                break;
            case DescValueType.RAWTYPE:
                var tempStr = desc.getData(property);
                var rawData = new Array();
                for (var tempi = 0; tempi < tempStr.length; tempi++) {
                    rawData[tempi] = tempStr.charCodeAt(tempi);
                }
                return rawData;
                break;
            default:
                break;
        };
    }

    function s2t(s) { return stringIDToTypeID(s) }
    function t2s(t) { return typeIDToStringID(t) }
}

 

As a rule, I get all the necessary attributes in this loop (getting the name and ID of the layer in the example below) and put them in an object, the array of which the function returns.
For further processing, i can also use AM code and access to the layer via his ID, or use the DOM through AM.selectLayerById (), for example: 

 

for (var i=0; i<layerSetArray.length; i++){
   AM.selectLayerById (layerSetArray[i].id)
   alert (app.activeDocument.activeLayer.name)
}

 

(I never use .getByName () since a document can contain several layers with the same name)

Maybe it will help you.

schroef
Inspiring
January 17, 2021

@jazz-y  @willcampbell7 

 

sorry to return this late, many thanks for your input. This helps me understand to get cleaner and faster code.

 

Do any of you perhaps have an idea how i would get all layers in a layerset? My intended need is to get the first and last layer in a layerset but also the last and first in a document. That last one should not be that hard. Though i cant seem to figure out a way to get all layers in a layerset.

 

My initial idea is to traverse up in a layerset and if we bump into a layerset we can use hide others. Then i could do a select perhaps select all and get the first and last one.

 

 

after tinkering a bit about it. Prehaps this is a solution, its the only method i can think of using traverse up properly. I would need to get all layers id or layers indexes, then i get the activeLayer index. Then, i think, i can use layer index to move up until up, each time i move up i check if the layer is a layerset, if so then hide all others. Then select all and get the first and last one.

willcampbell7
Legend
January 18, 2021

I'm not sure what you're trying to do.

"Get layer by index" is easy. It's an array. So... object.layers[0] is index 0, object.layers[1] is index 1, etc.

Continuing to however many there are, which is object.layers.length. It's zero-indexed like all arrays so keep in mind the last element is length -1,

Do you have the object reference? See this link... very helpful...

https://www.indesignjs.de/extendscriptAPI/photoshop-latest/#about.html

Here is everything about 'layer' class...

https://www.indesignjs.de/extendscriptAPI/photoshop-latest/#Layer.html

If you're trying to get the parent, that is a property of the layer class. It's not an index, it's a reference to the actual object. So if what you're trying to do is find the parent layer set (or doc) of a layer, that would do it.

Maybe this reply will help, maybe not. You'll need to better explain your objective.


I looked again at your earlier message. You say "Get first and last layer" of a layer set and of the doc, if I read that correctly.

First and last of a doc goes like this...

var doc = app.activeDocument;
var layerFirst = doc.layers[0];
var layerLast = doc.layers[doc.layers.length - 1];

This doesn't yet recognize if the first or last 'layer' is a layer set. Further tests are needed for that. When a layer set is discovered, the same logic applies. Let's say the first 'layer' is actually a layer set. Then...

// (assume var layerFirst set by code above)
var layerSetLayerFirst = layerFirst.layers[0];
var layerSetLayerLast = layerFirst.layers[layerFirst.layers.length - 1];
William Campbell
willcampbell7
Legend
March 10, 2019

You don't need to create additional arrays. The document collections for layers, layer sets, etc., are already arrays. Just access them. You need to anyway, if you want to change anything. Having a separate array of the layer/layer set names is just that, only their names. That won't let you change what the name is -- you need a reference to the actual layer object to do that.

 

Here is a basic example of how to walk through all layers in a document, and dive deeper when it's a layer set. Which also handles layer sets inside layer sets.

 

processLayers(app.activeDocument);

function processLayers(o) {
    var i;
    var layer;
    for (i = 0; i < o.layers.length; i++) {
        layer = o.layers[i];
        if (layer instanceof LayerSet) {
            // If layer set, recurse (call self).
            processLayers(layer);
        }
        changeLayer(layer);
        //
        // If you only want to affect art layers, not layer sets,
        // move call to changeLayer into else, then it will not
        // happen to layer sets, only art layers.
        // } else {
        //    changeLayer(layer)
        // }
    }
}

function changeLayer(layer) {
    layer.name = "x-" + layer.name;
    // Simple change prefix layer name with "x-" to demonstrate.
    // Change to whatever code you want done to each layer.
}

 

William Campbell
JJMack
Community Expert
Community Expert
March 11, 2019

Yes the layers array is a set of layeres and layers Set a top level view of you document.  I normally use only this array for  I normally only deal with layers that are not in any group.  My scripts deal with Layers on the top and bottom on the layers stack not in any group.   Adobe's layers array is what I normally use to address them. Many of the Template PSD I deal with may have no groups at all. So All I need to use in Adobe's layers array.

JJMack
Legend
March 8, 2019

var layers = new Array();

var sets = new Array();

get_layers_and_sets(activeDocument, layers, sets);

alert(layers);

alert(sets);

function get_layers_and_sets(set, l, s)

    {

    function scan(layers, sets)

        {

        for (var i = 0; i < layers.length; i++) l.push(layers);           

        var length = sets.length;

        for (var i = 0; i < length; i++)

            {  

            var set = sets;

            s.push(set);

            scan(set.artLayers, set.layerSets);

            }

        }

    scan(set.artLayers, set.layerSets);

    }

JJMack
Community Expert
Community Expert
March 8, 2019

/* ==========================================================

// 2017  John J. McAssey (JJMack)

// ======================================================= */

// This script is supplied as is. It is provided as freeware.

// The author accepts no liability for any problems arising from its use.

// enable double-clicking from Mac Finder or Windows Explorer

#target photoshop // this command only works in Photoshop CS2 and higher

// bring application forward for double-click events

app.bringToFront();

// ensure at least one document open

if (!documents.length) alert('There are no documents open.', 'No Document');

else {

// declare Global variables

//main(); // at least one document exists proceed

    app.activeDocument.suspendHistory('Some Process Name','main()');

}

///////////////////////////////////////////////////////////////////////////////

//                            main function                                  //

///////////////////////////////////////////////////////////////////////////////

function main() {

// declare local variables

var orig_ruler_units = app.preferences.rulerUnits;

var orig_type_units = app.preferences.typeUnits;

var orig_display_dialogs = app.displayDialogs;

app.preferences.rulerUnits = Units.PIXELS;  // Set the ruler units to PIXELS

app.preferences.typeUnits = TypeUnits.POINTS;   // Set Type units to POINTS

app.displayDialogs = DialogModes.NO;     // Set Dialogs off

try { code(); }

// display error message if something goes wrong

catch(e) { alert(e + ': on line ' + e.line, 'Script Error', true); }

app.displayDialogs = orig_display_dialogs;  // Reset display dialogs

app.preferences.typeUnits  = orig_type_units; // Reset ruler units to original settings

app.preferences.rulerUnits = orig_ruler_units; // Reset units to original settings

}

///////////////////////////////////////////////////////////////////////////////

//                           main function end                               //

///////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////

// The real code is embedded into this function so that at any point it can return //

// to the main line function to let it restore users edit environment and end      //

/////////////////////////////////////////////////////////////////////////////////////

function code() {

processArtLayers(activeDocument) 

}

function processArtLayers(obj) { 

    for( var i = obj.artLayers.length-1; 0 <= i; i--) {processLayers(obj.artLayers);} 

    for( var i = obj.layerSets.length-1; 0 <= i; i--) {processArtLayers(obj.layerSets); } // Process Layer Set Layers 

}

function processLayers(layer) {

switch (layer.kind){

case LayerKind.SMARTOBJECT : processSmartObj(layer); break;

default : break; // none process layer types catch all

}

}

//////////////////////////////////////////////////////////////////////////////////

// Layer Type Functions //

//////////////////////////////////////////////////////////////////////////////////

function processSmartObj(obj) {

var localFilePath = "";

var ref = new ActionReference();   

    ref.putIdentifier(charIDToTypeID('Lyr '), obj.id );   

    var desc = executeActionGet(ref);   

    var smObj = desc.getObjectValue(stringIDToTypeID('smartObject'));  

try {

var localFilePath = smObj.getPath(stringIDToTypeID('link'));  

alert("Layer " + obj.name + ' linked file is"' +  localFilePath + '"');

}

catch(e) {}

return localFilePath;

}

recursion is required for nested groups Layer name need not be unique all layers could have the same name I have seen an Adobe template PSD with 60+ independent smart object layers  and all those smart object layers had the same name "Your Image Here". You can push all layer into an array Id do not know the order the layer would be in the array. I believe layers have an index number that is unique I have never need to use the index numbers. I mostly add layer   to  the top  of the stack or above a required background layer.  Don't have any experience scripting layer groups.

JJMack