Search code examples
javascriptphotoshopextendscriptphotoshop-script

Photoshop renaming script does not iterate through layers within folders


This script works perfectly apart from one aspect: it doesn't loop through layers that are nested within folders. I've tried changing layers to layerSets as per this question, but then it stops working on folder names. I'm using Photoshop 2020 on Mac Catalina.

// JavaScript Document
var doc = app.activeDocument;

// name indexed object
var layernames = {
 'Products':'Working',
 'Product':'Working',
 'Notes':'Note',
 'Background color':'Background Colour',
 'White background':'White Background'
};

// loop through all layers
for (var i = 0; i < doc.layers.length; i++)
{

 //Set up Variable to access layer name
 var currentLayer = app.activeDocument.layers;

 if (layernames[currentLayer.name]) {
     currentLayer.name = layernames[currentLayer.name];
 }
}

Solution

  • Consider the following solution that utilizes a recursive function called renameLayers to traverse all layer nodes in the document layers tree structure.

    A layers typename property may be set to either ArtLayer or LayerSet. Essentially when a layers typename is a LayerSet (i.e. a Folder) we recursively call the function again.


    var doc = app.activeDocument;
    
    /**
      * Rename layers in a Photoshop document.
      *
      * @param {Object} doc A reference to the document to change.
      * @param {Object} layerName Each property key name indicates the original layer
      * name, and its corresponding value indicates the new layer name.
      * @param {Object} options The configuration options.
      * @param {Boolean} [options.includeTextLayers=false] Whether to rename text layers.
      * @param {Boolean} [options.includeLayerSets=false] Whether to rename LayerSets.
      */
    function renameLayers(doc, layerNames, options) {
    
      // Default options
      var opts = {
        includeTextLayers: false,
        includeLayerSets: false
      };
    
      // Overwrite properties in the default `opts` object by properties in the
      // user defined `options` object if they have the same key. Shallow copy only.
      if (typeof options === 'object') {
        for (var key in opts) {
          if (options.hasOwnProperty(key)) {
            opts[key] = options[key];
          }
        }
      }
    
      // Iterate each layer in the document.
      for (var i = 0, max = doc.layers.length; i < max; i++) {
        var currentLayer = doc.layers[i];
    
        if (currentLayer.typename === 'ArtLayer') {
    
          if (layerNames[currentLayer.name]
              && (opts.includeTextLayers || currentLayer.kind !== LayerKind.TEXT)) {
            currentLayer.name = layerNames[currentLayer.name];
          }
    
        } else { // The layers `typename` is a `LayerSet`
    
          if (layerNames[currentLayer.name] && opts.includeLayerSets) {
            currentLayer.name = layerNames[currentLayer.name];
          }
          renameLayers(currentLayer, layerNames, options); // Resursive call
        }
      }
    }
    
    
    // Demo Usage
    
    var layerNames = {
      'Products': 'Working',
      'Product': 'Working',
      'Notes': 'Note',
      'Background color': 'Background Colour',
      'White background': 'White Background'
    };
    
    renameLayers(doc, layerNames);
    

    Usage Notes:

    As you can see in the last line of code (above) we invoke the renameLayers function as follows:

    renameLayers(doc, layerNames);
    

    Here we pass in the doc variable, i.e. a reference to the document to change, and the layerNames object that defines the mappings for the original layer name and the new layer name.

    Running the code shown above (as is) will rename any layer in accordance with the mappings specified in the layerNames object. However it currently does not rename any Text Layer(s) or LayerSet(s).

    How can I also rename Text Layers and/or LayerSets?

    The renameLayers function lists a third optional parameter, named options to allow a custom configuration to be defined.

    The following three function invocations demonstrate how different configurations can be achieved by passing in the optional options argument:

    • Invoking the function as follows with includeLayerSets set to true renames LayerSets too:

      renameLayers(doc, layerNames, { includeLayerSets: true });
      
    • Invoking the function as follows with includeTextLayers set to true renames Text Layers too:

      renameLayers(doc, layerNames, { includeTextLayers: true });
      
    • Invoking the function as follows with includeLayerSets and includeTextLayers set to true renames both LayerSets and Text Layers too:

      renameLayers(doc, layerNames, {
        includeLayerSets: true,
        includeTextLayers: true
      });