Skip navigation
Currently Being Moderated

PS3 script to batch rename layers

Feb 12, 2008 4:33 PM

Sometimes, I make copies of layers (for example: copy little diamonds to align into a shape of the letter M). Each time I "alt+drag" the content in that layer (to make a copy of it), PS makes a copy and then appends the name of the layer to something like "diamond copy 3". After making a ton of copies (and because I'm anal about having actual names for each layer), I want each layer to read "diamond". Instead of manually renaming all these layers, is there a way to highlight the affected layers and choose to rename all layers according to what you want?
 
Replies
  • Currently Being Moderated
    Feb 28, 2008 8:14 AM   in reply to (Shandy_Elliott)
    You're in luck. I just finished writing this yesterday. :)

    This will remove " copy", " copy 1"... etc from all open files. Let me know how it works out for you.

    ////////////////////////////////////////////////////////////////////// /////////
    // Script Name: RenameLayers.jsx
    // Version: 1.0
    // Date: 2/27/08
    // Usage: Removes 'copy 1, copy 2, etc' from name of all layers
    ////////////////////////////////////////////////////////////////////// /////////

    #target photoshop
    //$.localize = true;
    var displayDialogMode = app.displayDialogs;

    app.bringToFront();
    app.bringToFront();
    app.displayDialogs = DialogModes.NO;

    processObjects(app.documents, processDocument,function () {alert("You have no documents open")})
    app.displayDialogs = displayDialogMode;

    ////////////////////////////////////////////////////////////////////// /////////
    // Function: processObjects
    // Usage: Runs theMainFunction on every object in list theObjects
    // Input: List of objects (documents, layers, etc.), Function to run on each object, Function to run on parent if there are no object
    // Return: value returned from function used
    ////////////////////////////////////////////////////////////////////// /////////
    function processObjects(theObjects, theMainFunction, theAlternateFunction) {
    var returnValue = null;
    if (theObjects.length ==0) {
    if (!(theAlternateFunction == undefined) && !(theAlternateFunction == null) && !(theAlternateFunction == "")) {
    returnValue = theAlternateFunction (theObjects.parent);
    }
    } else {
    if (!(theMainFunction == undefined) && !(theMainFunction == null) && !(theMainFunction == "")) {
    for (var i = theObjects.length -1; i> -1; i--) {
    returnValue += theMainFunction (theObjects[i], i);
    }
    }
    }
    return returnValue;
    }

    ////////////////////////////////////////////////////////////////////// /////////
    // Function: processDocument
    // Usage: Processes layers of document with 'processLayer' function
    // Input: Document object, Document object index
    // Return: none
    ////////////////////////////////////////////////////////////////////// /////////
    function processDocument(objectRef, i) {
    app.activeDocument = objectRef
    processObjects(objectRef.layers, processLayer, null);
    }

    ////////////////////////////////////////////////////////////////////// /////////
    // Function: processLayer
    // Usage: Removes 'copy' from layer name (processes Layer Set contents)
    // Input: Layer object, Layer Object index
    // Return: none
    ////////////////////////////////////////////////////////////////////// /////////
    function processLayer(objectRef, i) {
    if (objectRef.typename == 'LayerSet') {
    var layerCount = processObjects(objectRef.layers, processLayer);
    }
    renameLayer(objectRef)
    return 0
    }

    ////////////////////////////////////////////////////////////////////// /////////
    // Function: renameLayer
    // Usage: Removes 'copy' from layer name (processes Layer Set contents)
    // Input: Layer object, Layer Object index
    // Return: none
    ////////////////////////////////////////////////////////////////////// /////////
    function renameLayer(objectRef) {
    var theRegEx = new RegExp(/^(.*)(copy(\s(\d)+)*)+$/)
    if (theRegEx.test(objectRef.name)) {
    var indexNumber = 0

    indexnumber = objectRef.name.indexOf(" copy")
    objectRef.name = objectRef.name.substr(0,indexnumber)
    }
    return 0
    }
     
    |
    Mark as:
  • Currently Being Moderated
    Feb 29, 2008 5:39 AM   in reply to (Shandy_Elliott)
    Paste into a text program (notepad on PC, TextEdit on mac) and save into the 'Scripts' directory in the 'Presets' directory of your Photoshop application folder with a '.js' or '.jsx' extension (e.g. "RenameLayers.jsx").

    To activate, select "Scripts" from the"File" menu and select the saved script.
     
    |
    Mark as:
  • Currently Being Moderated
    Mar 12, 2008 1:38 PM   in reply to (Shandy_Elliott)
    Hi!

    I will surely have a look at the script... Does it handle well hierarchical nested layers?

    In my current project, i have some documents that are very heavy (by exemple more than 1000 layers!).

    The automated renaming of around 300 layers in particular document is a process that take as much as 7 hours! (Applescript on a 1.8 G5 computer/4go ram).

    It's not fully clear to me if it's the logic of the script/the scripting api speed or the heavy use of PSD document that is at fault..

    If only i had more time for pluggin developpement! :)

    Eric
     
    |
    Mark as:
  • Currently Being Moderated
    Mar 12, 2008 2:11 PM   in reply to (Shandy_Elliott)
    Yes, it will handle all layers in every layer set. (in every opened file)

    If you have a lot of layers, it could take a while as it opens each layer set and scrolls through every layer. On the files I've used it on, it runs pretty speedy. I think it will also run much faster than an applescript.
     
    |
    Mark as:
  • Currently Being Moderated
    Mar 12, 2008 2:33 PM   in reply to (Shandy_Elliott)
    I have tested it.. it works like advertised (including layers in groups).

    The script is fast on small sample but get quite slow on heavy documents ... so maybe my problem is more related to document size and maybe not worth investigating other scripting alternatives (but i'm pretty sure that on the same set your script would be faster).

    The only bug i have found is that the script dont keep track of the visibility of layers. If a layer is invisible (when you do the renaming), the layer should be invisible when you are done with the processing.

    That "problem" is also visible in a normal user interaction through the interface (select a layer make the layer visible). It's questionable in the user interface and a real problem with automation even if you record and set back the property. It must be clear to the users that if a script is crashing that document need to be reloaded!
     
    |
    Mark as:
  • Currently Being Moderated
    Mar 13, 2008 4:48 AM   in reply to (Shandy_Elliott)
    It looks like changing the name of the layer makes it visible (unless it is the selected layer). I'll see if I can find the time to edit the script to activate each layer before it renames it. This should solve that problem. I'd also like it to restore the state of the layer sets that it opens to select each layer, and if I have it select each layer before renaming, then it should also end with the originally selected layer selected.
     
    |
    Mark as:
  • Currently Being Moderated
    Mar 13, 2008 7:23 AM   in reply to (Shandy_Elliott)
    I must say that in my scripts, i had quite a few problems to get around while working with layers! (and not all where solved!)
     
    |
    Mark as:
  • Currently Being Moderated
    Mar 13, 2008 7:23 AM   in reply to (Shandy_Elliott)
    I made a quick rewrite to the renameLayer function. This should fix the problem with invisible layers. I ran the script on a large document just now, and it did take a while (although MUCH faster than would be manually, plus more accurate). It switches to the layer to rename it, and switches back to the original selected layer afterwards. I will rewrite it later to switch to the layer before renaming (hopefully that should solve that slowdown).

    function renameLayer(objectRef) {
    var theRegEx = new RegExp(/(\s*copy\s*\d*)+$/)

    if (theRegEx.test(objectRef.name)) {
    // save state of layer (visible or invisible)
    var layerVisible = objectRef.visible
    var indexNumber = 0
    indexnumber = objectRef.name.indexOf(" copy")
    objectRef.name = objectRef.name.substr(0,indexnumber)
    objectRef.visible = layerVisible
    }
    return 0
    }
     
    |
    Mark as:
  • Currently Being Moderated
    May 12, 2010 5:30 PM   in reply to MarkWalsh

    Hi Mark,


    thanks for this, worked a treat and saved me a whole lot of 'fun' in removing copy from each layer.

     
    |
    Mark as:
  • Currently Being Moderated
    Jul 13, 2010 11:59 AM   in reply to matt-dd

    Hello Mark,

     

    Also thanks for sharing the script, very usefull. I'm trying to add something to the script so that only selected layers in the document where layers are selected are processed with your renameLayer function. But i can't seem to find the correct code to only get the selected layers and layers sets

    from this document. Is this even possible to do ? I'm not an experienced coder myself but I can read your script (and write fairly basic scripts). I was trying to fill an array with the selected layer and layer-sets that needed to be renamed, but I even got stuck there.

     

    Thanks,

    Fred

     
    |
    Mark as:
  • Currently Being Moderated
    Jul 13, 2010 12:19 PM   in reply to Mothator

    Mothator,

     

    What version of Photoshop are you using. I have a script that removes 'copy' from selected layers but it needs a document descriptor key that is CS4 or higher.

     
    |
    Mark as:
  • Currently Being Moderated
    Jul 13, 2010 1:03 PM   in reply to Michael L Hale

    I'm using CS4 ...

     
    |
    Mark as:
  • Currently Being Moderated
    Jul 13, 2010 1:35 PM   in reply to Mothator

    Try this. Just select the layers you want 'copy' removed from and run this script.

    if( app.documents.length > 0 ){
    app.activeDocument.suspendHistory('Rename selected layers','removeCopyFromSelectedLayersNames()');
    }
    function removeCopyFromLayerName(){
         if( getSelectedLayersIdx().length > 1 ){
              var selectedLayers = getSelectedLayersIdx();
              makeActiveByIndex( selectedLayers[0], false );
         }
       var startLoop = Number( !hasBackground() );
       var endLoop = getNumberOfLayer() + 1; 
       for( var l = startLoop;l < endLoop; l++){
            while( !isValidActiveLayer( l ) ) {
                l++;
            }
              var oldName =  getLayerNameByIndex( l );
              var newName = oldName.replace(/\scopy\s?\d*$/i,'');
              putLayerNameByIndex( l, newName )
         }
         if( selectedLayers != undefined ) makeActiveByIndex( selectedLayers, false );
    }
    function removeCopyFromSelectedLayersNames(){
         var selectedLayers = getSelectedLayersIdx();
         for( var l = 0;l < selectedLayers.length; l++){
              var oldName =  getLayerNameByIndex( selectedLayers[ l ] );
              var newName = oldName.replace(/\scopy.*$/i,'');
              makeActiveByIndex( selectedLayers[ l ], false );
              putLayerNameByIndex( selectedLayers[ l ], newName )
         }
         makeActiveByIndex( selectedLayers, false );
    }
    function getNumberOfLayer(){ 
    var ref = new ActionReference(); 
    ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') ); 
    var desc = executeActionGet(ref); 
    var numberOfLayer = desc.getInteger(charIDToTypeID('NmbL')); 
    return numberOfLayer; 
    }
    function getLayerNameByIndex( idx ) { 
        var ref = new ActionReference(); 
        ref.putProperty( charIDToTypeID('Prpr') , charIDToTypeID( 'Nm  ' )); 
        ref.putIndex( charIDToTypeID( 'Lyr ' ), idx );
        return executeActionGet(ref).getString(charIDToTypeID( 'Nm  ' ));; 
    }
    function putLayerNameByIndex( idx, name ) {
         if( idx == 0 ) return;
        var desc = new ActionDescriptor();
            var ref = new ActionReference();
            ref.putIndex( charIDToTypeID( 'Lyr ' ), idx );
        desc.putReference( charIDToTypeID('null'), ref );
            var nameDesc = new ActionDescriptor();
            nameDesc.putString( charIDToTypeID('Nm  '), name );
        desc.putObject( charIDToTypeID('T   '), charIDToTypeID('Lyr '), nameDesc );
        executeAction( charIDToTypeID('setd'), desc, DialogModes.NO );
    }
    function getActiveLayerIndex() {
         var ref = new ActionReference();
         ref.putProperty( 1349677170 , 1232366921 );
         ref.putEnumerated( 1283027488, 1332896878, 1416783732 );
         var res = executeActionGet(ref).getInteger( 1232366921 )
                                                           - Number( hasBackground() );
         return res;   
    }
    function isValidActiveLayer( idx ) {
         var propName = stringIDToTypeID( 'layerSection' );
         var ref = new ActionReference(); 
         ref.putProperty( 1349677170 , propName);
         ref.putIndex( 1283027488, idx );
         var desc =  executeActionGet( ref );
         var type = desc.getEnumerationValue( propName );
         var res = typeIDToStringID( type ); 
         return res == 'layerSectionEnd' ? false:true;
    }
    function hasBackground(){
        var res = undefined;
        try{
            var ref = new ActionReference();
            ref.putProperty( 1349677170 , 1315774496); 
            ref.putIndex( 1283027488, 0 );
            executeActionGet(ref).getString(1315774496 );; 
            res = true;
        }catch(e){ res = false}
        return res;
    }
    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++){
                   selectedLayers.push(  desc.getReference( i ).getIndex());
              }
         }else{
              var ref = new ActionReference(); 
              ref.putProperty( charIDToTypeID('Prpr') , charIDToTypeID( 'ItmI' )); 
              ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
              selectedLayers.push( executeActionGet(ref).getInteger(charIDToTypeID( 'ItmI' )));
         }
         return selectedLayers;
    }
    function makeActiveByIndex( idx, visible ){
         if( idx.constructor != Array ) idx = [ idx ];
         for( var i = 0; i < idx.length; i++ ){
              var desc = new ActionDescriptor();
              var ref = new ActionReference();
              ref.putIndex(charIDToTypeID( 'Lyr ' ), idx[i])
              desc.putReference( charIDToTypeID( 'null' ), ref );
              if( i > 0 ) {
                   var idselectionModifier = stringIDToTypeID( 'selectionModifier' );
                   var idselectionModifierType = stringIDToTypeID( 'selectionModifierType' );
                   var idaddToSelection = stringIDToTypeID( 'addToSelection' );
                   desc.putEnumerated( idselectionModifier, idselectionModifierType, idaddToSelection );
              }
              desc.putBoolean( charIDToTypeID( 'MkVs' ), visible );
              executeAction( charIDToTypeID( 'slct' ), desc, DialogModes.NO );
         }     
    }
    
     
    |
    Mark as:
  • Currently Being Moderated
    Jul 13, 2010 2:50 PM   in reply to Michael L Hale

    Hi Michael,

     

    It seems to be working perfectly, thanks for sharing this !!

     

    Greetings,

    f,

     
    |
    Mark as:
  • Currently Being Moderated
    Jul 14, 2010 9:04 PM   in reply to Michael L Hale

    Hi,

     

    I copy'n'pasted your script into Text Edit and saved as .jsx, saved that into PS4 > Presets > Scripts and tried it but it didn't work.

     

    I selected the layer I wanted to remove 'copy' from but got the following error.

     

    Picture 2.png

    Any possible solutions? The initial script at the top of this thread worked, but I have too many layers to work through to use it effectively.

     

    Any help appreciated.

     

    --cheers, matt

     
    |
    Mark as:
  • Currently Being Moderated
    Jul 14, 2010 9:42 PM   in reply to matt-dd

    I am not sure why it is not woking for you unless Text Edit did not save it as plain text.

     

    That script is one of two that I have in a Configurator panel I made. One button removes 'copy' from all layers and the other button removes from selected layers. You can get the panel at http://ps-scripts.com/bb/viewtopic.php?f=10&t=3056

     
    |
    Mark as:
  • Currently Being Moderated
    Jul 14, 2010 10:20 PM   in reply to Michael L Hale

    Thanks Michael, that did the trick!

     

    -matt

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 11, 2011 11:21 AM   in reply to (Shandy_Elliott)

    This is very handy, thanks for creating it.

     

    Suggestion: It would be very helpful to be able to rename all your selected files in one shot, ie change 25 layers all named "shape 2" to "button".

     

    Cheers,

     

    J

     
    |
    Mark as:

More Like This

  • Retrieving data ...

Bookmarked By (0)