Skip to main content
Inspiring
October 11, 2019
Answered

Script to set active layers pulled from .txt doc on desktop?

  • October 11, 2019
  • 18 replies
  • 3228 views

Hello,

  I'd like to be able to select layers based off a .txt file located on the desktop. So the text file would have a list of names it would index, which the script would read and index, then select any matching layer names in the open photoshop document. Is this possible to script via java? Thanks in advance. 

This topic has been closed for replies.
Correct answer Chuck Uebele

This one will delete the smart objects:

#target photoshop
var firstSnpStart = 1 // 1= first snapshot other than opening of file. 0 = open file snapshot
var doc = activeDocument;
var hsObj = doc.historyStates;
var hsLength = hsObj.length;
var snpNum = 0
var firstSnp, lastSnp

getFirstSnapshot ();
getLastSnapshot ();

if(firstSnp != lastSnp){
    doc.activeHistoryState = firstSnp;
    var useListFirst = [];
    var layerSets = 0
    try{doc.backgroundLayer}
    catch(e){layerSets=1}
    var layerListFirst = getLayerSetsData();  

    for (var i=0;i<layerListFirst.length;i++){
        if(layerListFirst.type != 13){useListFirst.push(layerListFirst[i].id)};
        } //end for loop to get original layers   
    doc.activeHistoryState = lastSnp;
    var useListLast = [];
    layerSets = 0
    try{doc.backgroundLayer}
    catch(e){layerSets=1}
    var layerListLast = getLayerSetsData();   
    
            for (var i=0;i<layerListLast.length;i++){
                if(layerListLast[i].type != 13&&layerListLast[i].type != 5){
                    var layerOk = true;
                    for(var j=0;j<useListFirst.length;j++){
                        if(layerListLast[i].id == useListFirst[j]){
                            layerOk = false;
                            }
                        }//enf j loop   
                    if(layerOk){useListLast.push(layerListLast[i].id)}
                   }//end if for layer type
                } //end i loop       
    multiSelectByIDs (useListLast)       
    }//end if to run script
else{alert('There are not two snapshots')}


function getLastSnapshot()
{
   for (var i=hsLength - 1;i>-1;i--)
   {
      if(hsObj[i].snapshot) {
          lastSnp = hsObj[i]
         break;
      }      
   }      
}

function getFirstSnapshot()
{
   for (var i=firstSnpStart;i<hsLength;i++)
   {
      if(hsObj[i].snapshot) {
         firstSnp = hsObj[i];
         break;
      }      
   }      
}

function doesIdExists( id ){// function to check if the id exists
   var res = true;
   var ref = new ActionReference();
   ref.putIdentifier(charIDToTypeID('Lyr '), id);
    try{var desc = executeActionGet(ref)}catch(err){res = false};
    return res;
}

function multiSelectByIDs(ids) {
  if( ids.constructor != Array ) ids = [ ids ];
    var layers = new Array();
    var id54 = charIDToTypeID( "slct" );
    var desc12 = new ActionDescriptor();
    var id55 = charIDToTypeID( "null" );
    var ref9 = new ActionReference();
    for (var i = 0; i < ids.length; i++) {
       if(doesIdExists(ids[i]) == true){// a check to see if the id stil exists
           layers[i] = charIDToTypeID( "Lyr " );
           ref9.putIdentifier(layers[i], ids[i]);
       }
    }
    desc12.putReference( id55, ref9 );
    var id58 = charIDToTypeID( "MkVs" );
    desc12.putBoolean( id58, false );
    executeAction( id54, desc12, DialogModes.NO );
}

function getLayerSetsData()
{
    //var count = 0;//set counter for multi-dimensional array
    var lyrSets = [];

    while (true)
    {
        ref = new ActionReference();
        ref.putIndex(charIDToTypeID('Lyr '), layerSets);
        try
            {var d1 = executeActionGet(ref)}
        catch (err){
            break;
            };

        var c2t = function (s){return app.charIDToTypeID(s);};
        var s2t = function (s){return app.stringIDToTypeID(s);};
        var lyrSet = {};

        lyrSet.type = d1.getInteger(s2t("layerKind"));
        lyrSet.name = d1.getString(c2t("Nm  "));
        lyrSet.id = d1.getInteger(s2t("layerID"));
        
        lyrSets.push(lyrSet);
        layerSets++;
    }; 
    return lyrSets;
};

 

 

18 replies

Chuck Uebele
Community Expert
Community Expert
October 12, 2019

You might have to rethink this workflow and scripts. Having duplicate names is a real issue. It might be better to save the layer id to a text file either with or without the layer names, and get select the layers from the id, as they are unique and don't change, so you could limit the selecting layers to only ones below a threshold, as all new layers will have a higher number.

Inspiring
October 14, 2019
I didn't know about the layer Id thing. That would definitely work better assuming those are static and don't change or repeat even with deleting layers and whatnot
Inspiring
October 14, 2019
what would the code for this be?
Chuck Uebele
Community Expert
Community Expert
October 11, 2019

Use this, but change the path and file name to what you want to use.

#target photoshop
var txtFile = new File('~/desktop/text.txt')
// Set active Document variable and decode name for output
var docRef = app.activeDocument;
var docName = decodeURI(activeDocument.name);

// Define pixels as unit of measurement
var defaultRulerUnits = preferences.rulerUnits;
preferences.rulerUnits = Units.PIXELS;

// Define variable for the number of layers in the active document
var layerNum = app.activeDocument.artLayers.length;

// Define variable for the active layer in the active document
var layerRef = app.activeDocument.activeLayer;

var layerNm = "";

// Loop to iterate through all layers
function recurseLayers(currLayers) {
for ( var i = 0; i < currLayers.layers.length; i++ ) {
layerRef = currLayers.layers[i];
layerNm += layerRef.name + "\n";

//test if it's a layer set
if ( isLayerSet(currLayers.layers[i]) ) {
recurseLayers(currLayers.layers[i]);
}
}
}

//a test for a layer set
function isLayerSet(layer) {
try {
if ( layer.layers.length > 0 ) {
return true;
}
}

catch(err) {
return false;
}
}

// Ask the user for the folder to export to
//var FPath = Folder.selectDialog("Save exported coordinates to");

// Detect line feed type
if ( $.os.search(/windows/i) !== -1 ) {
fileLineFeed = "Windows";
}
else {
fileLineFeed = "Macintosh";
}

// Export to txt file
function writeFile(info) {
try {
var f = txtFile;
f.remove();
f.open('a');
f.lineFeed = fileLineFeed;
f.write(info);
f.close();
}
catch(e){}
}

// Run the functions
recurseLayers(docRef);
preferences.rulerUnits = defaultRulerUnits; // Set preferences back to user 's defaults
writeFile(layerNm);
Inspiring
October 12, 2019

So after further testing, Im having a few issues. Basically what I'm trying to achieve is a way that will make all newly generated layers, since opening a file, active. These two pieces of code (plus a third peice of code which simply inverts which layers are active) thus far are going in the right direction and are working in this way:

 

1) open file that already has existing layers

2) add smome adjustent layers, pixel layers, dupe some existing layers, etc.

3) make a snapshot 

4) go back to first history state (top most above our new snapshot)

5) run first script to make .txt file of all layernames

6) go to our snapshot (most recent history) and run second script to read in .txt data and make all corresponding layers active

7) then run another script which inverts which active layers are selected and thus you'll end up with all newly generated layers, since opening the fle, being active

 

The scripts are partially working, but are having below issues:

• if you make a new layer that is named exactly the same as an exisiting layer, it gets made active after running the second script that makes all .txt. layernames active, but it shouldnt be made active since it was a newly made layer

• it would be great if at the end of the three scripts, where now we are left with only the newly generated layers active, if any of those layers happen to be a smart object, either embedded or linked, they would be unselected from our currently active layers...leaving all newly generated layers since opening, minus any smart objects, active. 

Chuck Uebele
Community Expert
Community Expert
October 11, 2019

Try this to export layer names:

// Set active Document variable and decode name for output
var docRef = app.activeDocument;
var docName = decodeURI(activeDocument.name);

// Define pixels as unit of measurement
var defaultRulerUnits = preferences.rulerUnits;
preferences.rulerUnits = Units.PIXELS;

// Define variable for the number of layers in the active document
var layerNum = app.activeDocument.artLayers.length;

// Define variable for the active layer in the active document
var layerRef = app.activeDocument.activeLayer;

var layerNm = "";

// Loop to iterate through all layers
function recurseLayers(currLayers) {
for ( var i = 0; i < currLayers.layers.length; i++ ) {
layerRef = currLayers.layers[i];
layerNm += layerRef.name + "\n";

//test if it's a layer set
if ( isLayerSet(currLayers.layers[i]) ) {
recurseLayers(currLayers.layers[i]);
}
}
}

//a test for a layer set
function isLayerSet(layer) {
try {
if ( layer.layers.length > 0 ) {
return true;
}
}

catch(err) {
return false;
}
}

// Ask the user for the folder to export to
var FPath = Folder.selectDialog("Save exported coordinates to");

// Detect line feed type
if ( $.os.search(/windows/i) !== -1 ) {
fileLineFeed = "Windows";
}
else {
fileLineFeed = "Macintosh";
}

// Export to txt file
function writeFile(info) {
try {
var f = new File(FPath + "/" + docName + ".txt");
f.remove();
f.open('a');
f.lineFeed = fileLineFeed;
f.write(info);
f.close();
}
catch(e){}
}

// Run the functions
recurseLayers(docRef);
preferences.rulerUnits = defaultRulerUnits; // Set preferences back to user 's defaults
writeFile(layerNm);

// Show results
if ( FPath == null ) {
alert("Export aborted", "Canceled");
}
else {
alert("Exported " + layerNum + " layer names to " + FPath + "/" + docName + ".txt " + "using " + fileLineFeed + " line feeds.", "Success!");
}
Inspiring
October 11, 2019
Awesome! Thank you! Works great. Last thing I would want to modify is instead of a bringing up a save to dialog, it would simply save to the same exact folder with the same exact name every time, overwriting the existing .txt file in that folder located on desktop
Chuck Uebele
Community Expert
Community Expert
October 11, 2019

Try this. You will have to change the text file's name and path to fit what you have.

#target photoshop
var txtFile = new File('~/desktop/Text for scripts/select layers.txt');
var layerIdList = new Array();

if(txtFile.exists){
    var doc = activeDocument;
    var layerList = readFile (txtFile).split('\n');
    getLayers (doc)
    multiSelectByIDs (layerIdList)   
    }
else{alert('There is no text file')};

function getLayers(gp){

    for(var i=0;i<gp.layers.length;i++){
        for(var j=0;j<layerList.length;j++){
            if(gp.layers[i].name== layerList[j]){
                layerIdList.push(gp.layers[i].id)
                }                   
            }//end loop to get id
        if(gp.layers[i].typename == 'LayerSet'){getLayers (gp.layers[i])}
        };//end loop
    }//end function

    function doesIdExists( id ){// function to check if the id exists
       var res = true;
       var ref = new ActionReference();
       ref.putIdentifier(charIDToTypeID('Lyr '), id);
        try{var desc = executeActionGet(ref)}catch(err){res = false};
        return res;
    }

    function multiSelectByIDs(ids) {
      if( ids.constructor != Array ) ids = [ ids ];
        var layers = new Array();
        var id54 = charIDToTypeID( "slct" );
        var desc12 = new ActionDescriptor();
        var id55 = charIDToTypeID( "null" );
        var ref9 = new ActionReference();
        for (var i = 0; i < ids.length; i++) {
           if(doesIdExists(ids[i]) == true){// a check to see if the id stil exists
               layers[i] = charIDToTypeID( "Lyr " );
               ref9.putIdentifier(layers[i], ids[i]);
           }
        }
        desc12.putReference( id55, ref9 );
        var id58 = charIDToTypeID( "MkVs" );
        desc12.putBoolean( id58, false );
        executeAction( id54, desc12, DialogModes.NO );
    }

//===============READ/WRITE functions========================================
//=========================================================================
 function readFile(file) {
	if (!file.exists) {
		alert( "Cannot find file: " + deodeURI(file.absoluteURI));
		}
	else{
		file.encoding = "UTF8";
		file.lineFeed = "unix";
		file.open("r", "TEXT", "????");
		var str = file.read();
		file.close();

		return str;
		};
};

function writeFile(file,str) {

		file.encoding = "UTF8";
		file.open("w", "TEXT", "????");
		//unicode signature, this is UTF16 but will convert to UTF8 "EF BB BF"
		file.write("\uFEFF");
		file.lineFeed = "unix";
		file.write(str);
		file.close();
		
	};
 //=========================================================================
Inspiring
October 11, 2019

That worked! Thanks. It takes a little while in a doc with 100's of layers, but it seems to work correctly. The next part, or actually it is the first part that I'm having trouble with now, is having a bit of code that when run will copy all of the layer names to a .txt file. The code I found on the forums does that, but also writes the layer's coordinates after the layer name, which I don't want. Is there a way to edit this existing code below to only write out the layer names? Thanks for your help. I appreciate it. Code to follow

Inspiring
October 11, 2019

// Set active Document variable and decode name for output
var docRef = app.activeDocument;
var docName = decodeURI(activeDocument.name);

// Define pixels as unit of measurement
var defaultRulerUnits = preferences.rulerUnits;
preferences.rulerUnits = Units.PIXELS;

// Define variable for the number of layers in the active document
var layerNum = app.activeDocument.artLayers.length;

// Define variable for the active layer in the active document
var layerRef = app.activeDocument.activeLayer;

// Define varibles for x and y of layers
var x = layerRef.bounds[0].value;
var y = layerRef.bounds[1].value;
var coords = "";

// Loop to iterate through all layers
function recurseLayers(currLayers) {
for ( var i = 0; i < currLayers.layers.length; i++ ) {
layerRef = currLayers.layers[i];
x = layerRef.bounds[0].value;
y = layerRef.bounds[1].value;
coords += layerRef.name + "," + x + "," + y + "\n";

//test if it's a layer set
if ( isLayerSet(currLayers.layers[i]) ) {
recurseLayers(currLayers.layers[i]);
}
}
}

//a test for a layer set
function isLayerSet(layer) {
try {
if ( layer.layers.length > 0 ) {
return true;
}
}

catch(err) {
return false;
}
}

// Ask the user for the folder to export to
var FPath = Folder.selectDialog("Save exported coordinates to");

// Detect line feed type
if ( $.os.search(/windows/i) !== -1 ) {
fileLineFeed = "Windows";
}
else {
fileLineFeed = "Macintosh";
}

// Export to txt file
function writeFile(info) {
try {
var f = new File(FPath + "/" + docName + ".txt");
f.remove();
f.open('a');
f.lineFeed = fileLineFeed;
f.write(info);
f.close();
}
catch(e){}
}

// Run the functions
recurseLayers(docRef);
preferences.rulerUnits = defaultRulerUnits; // Set preferences back to user 's defaults
writeFile(coords);

// Show results
if ( FPath == null ) {
alert("Export aborted", "Canceled");
}
else {
alert("Exported " + layerNum + " layer's coordinates to " + FPath + "/" + docName + ".txt " + "using " + fileLineFeed + " line feeds.", "Success!");
}

Chuck Uebele
Community Expert
Community Expert
October 11, 2019

And are any of the layers in layer sets? Are there duplicate layer names?

Inspiring
October 11, 2019
there are some layers both inside of and outside of layer sets, and there may be duplicate layer names depending case by case
Inspiring
October 11, 2019
thanks for your help, btw
Chuck Uebele
Community Expert
Community Expert
October 11, 2019

Ok, that will require to get the index number of the layers and use Action Manager code to select them all. I'm not on my computer now, so can't check to find the code for that.

Chuck Uebele
Community Expert
Community Expert
October 11, 2019

So if they are separated by a return,  then from the example script in the link tha I posted, you would use a split statement using '\r' rather than '.' 

Do you want all the layers in the text file selected at one time, or in sequence to perform something on each layer?

Inspiring
October 11, 2019
all selected at once
Chuck Uebele
Community Expert
Community Expert
October 11, 2019

Yes, this is pretty easy to do. How is the text file set up? By that I mean how is the information separated: commas, carriage returns, etc.

This link has a script that reads and writes a text file to the desktop. You just need to tweak the returned string to what you want to do with layers.

https://community.adobe.com/t5/Photoshop/Store-foreground-color/td-p/10648492

 

 

Inspiring
October 11, 2019

Here is an example of a .txt file of layer names. Just the layer names separated by returns.

Inspiring
October 11, 2019

does this make sense?