Script to copy Layer Names to text box in Photoshop - javascript

I'm trying to create a script out of 2 working scripts
Goal:
We need a script that will go through all the layers in the current document, find each layer marked in certain color (Red for example), copy the names of only the layers marked in red, then put all names in a text layer one after another (attached example image).
Resources:
I found 2 scripts that each do half of what we need, so how to we put them together?
1) "Select by red" goes through the document and finds how many layers marked in "red" are in the document:
#target photoshop
if (app.documents.length > 0) {
// the file;
var myDocument = app.activeDocument;
// get number of layers;
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var applicationDesc = executeActionGet(ref);
var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
var theLayers = new Array;
var theOthers = new Array;
for (var m = 0; m <= theNumber; m++) {
try {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), m);
var layerDesc = executeActionGet(ref);
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// if not layer group collect values;
if (layerSet != "layerSectionEnd" /*&& layerSet != "layerSectionStart"*/ && isBackground != true) {
var theName = layerDesc.getString(stringIDToTypeID('name'));
var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
var visible = layerDesc.getBoolean(stringIDToTypeID("visible"));
var theColor = layerDesc.getEnumerationValue(stringIDToTypeID("color"));
if (typeIDToStringID(theColor) == "red") {theLayers.push([theName, theID])}
else {theOthers.push([theName, theID])}
};
}
catch (e) {};
};
// if layers are red;
if (theLayers.length > 0) {alert ("there are " + theLayers.length + " Red layers")}
else {alert ("no red layers")}
};
2) "Text box from layer name" takes the name of the currently selected layer, and pastes it into a new text layer called "Comp".
if (app.documents.length > 0) mainScript();
function mainScript() {
try{
var myLayerName = activeDocument.activeLayer.name;
var myLayerText = activeDocument.artLayers.add();
myLayerText.name = "Comp";
myLayerText.kind = LayerKind.TEXT;
var textProperty = myLayerText.textItem;
textProperty.size = 10;
textProperty.font = "Arial";
myLayerText.textItem.contents = myLayerName;
}catch (errStr){
alert(errStr);
}
}
Plan:
From my understanding, we need to start with a loop that's as long as our document size i.e. total number of layers for (var i = 0; i < doc.layers.length; i++).
Then layer by layer the script will check for color ID if (typeIDToStringID(theColor) == "red") . When it finds layer marked in red, it copies the layer name - then either stores it in array (to output later all at once), or creates a new text box and pastes the layer name myLayerText.textItem.contents = myLayerName.
Then for each time it finds another layer marked in red , it copies the layer name, and pastes it in the same text box just a line above/below previous layer name.
Any help is much appreciated!

The only thing you needed to do was to join the names of your theLayers array using the line-break symbol \r:
if (app.documents.length > 0)
{
// the file;
var myDocument = app.activeDocument;
// get number of layers;
var ref = new ActionReference();
ref.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
var applicationDesc = executeActionGet(ref);
var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
var theLayers = new Array;
for (var m = 0; m <= theNumber; m++)
{
try
{
var ref = new ActionReference();
ref.putIndex(charIDToTypeID("Lyr "), m);
var layerDesc = executeActionGet(ref);
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
var isBackground = layerDesc.getBoolean(stringIDToTypeID("background")); // if not layer group collect values; if (layerSet != "layerSectionEnd" /*&& layerSet != "layerSectionStart"*/ && isBackground != true)
{
var theName = layerDesc.getString(stringIDToTypeID('name'));
var theColor = layerDesc.getEnumerationValue(stringIDToTypeID("color"));
if (typeIDToStringID(theColor) == "red")
{
theLayers.push(theName); // we only need names here
}
};
}
catch (e)
{};
};
// got our red layers in theLayers
var myLayerText = activeDocument.artLayers.add();
myLayerText.name = "Result";
myLayerText.kind = LayerKind.TEXT;
var textProperty = myLayerText.textItem;
textProperty.size = 10;
textProperty.font = "Arial";
myLayerText.textItem.contents = theLayers.join('\r'); // joining layers with a line-break: this is going to be textItem text
};
Here's the result:
I'd suggest you to take a course on JS on any learning website (code academy, etc): this won't take you more than an hour or two but you'll get the basic concepts: this will make your life much easier in terms of dealing with questions like this.

Related

JS / JSX Script to generate unique PNGs from all permutations across all Photoshop Layer Groups and their Photoshop Layers

I'm looking into getting into generative art. My idea is to have Photoshop (CS5) generate a unique PNG for all permutations that exist while the script iterates through every LAYER across each LAYER GROUP (layerSets).
As an example, it would be similar to a character generator where:
PARENT layerSets consist of parts or locations across the face/body (A,B)
CHILD layers consist of accessories/styles (1,2,3)
Such that all generated PNG permutations would be: A1B1,A2B1,A3B1;A2B1,A2B2,A2B3;A3B1,A3B2,A3B3 only. (A1A2, A2A3, A1A3; B1B2, B2B3, B1B3 are not necessary; Standalone A1,A2,A3,B1,B2,B3 are not necessary).
I've found some code from #Mr.Online that generates random, and potentially redundant combinations from user-inputted quantity. His script requires that all layers be hidden by the user prior to running.
Thanks so much. Hopefully this'll help:
function Visible() {
var Grps = app.activeDocument.layerSets; // loops through all groups
for(var i = 0; i < Grps.length; i++){
var tmp = app.activeDocument.layerSets[i].layers.length;
app.activeDocument.layerSets[i].visible=true;
var groupChildArr = app.activeDocument.layerSets[i].layers;
var randLays = Math.floor(Math.random() * tmp);
groupChildArr[randLays].visible = true;
Save();
}
Revert();
}
function Save() {
var outFolder = app.activeDocument; // psd name
var outPath = outFolder.path;
var fName = "PNG"; // define folder name
var f = new Folder(outPath + "/" + fName);
if ( ! f.exists ) {
f.create()
}
var saveFile = new File(outPath + "/" + fName +"/" + "Pattern_" + num + ".png");
pngSaveOptions = new PNGSaveOptions();
pngSaveOptions.interlaced = false;
app.activeDocument.saveAs(saveFile, pngSaveOptions, true, Extension.LOWERCASE);
}
// Original code - revert function does not work
// for some users
//function Revert(){
// var idslct = charIDToTypeID( "slct" );
// var desc300 = new ActionDescriptor();
// var idnull = charIDToTypeID( "null" );
// var ref163 = new ActionReference();
// var idSnpS = charIDToTypeID( "SnpS" );
// ref163.putName( idSnpS, "test.psd" );
// desc300.putReference( idnull, ref163 );
// executeAction( idslct, desc300, DialogModes.NO );
//}
function Revert(){
var idRvrt = charIDToTypeID( "Rvrt" );
executeAction( idRvrt, undefined, DialogModes.NO );
}
var count = prompt("How many patterns you want","");
for (var x=0 ; x<count;x++){
var num = x+1;
Visible();
}
As you say, you need to ignore the random layers. However, if you know the number of layers & groups it's quite straight forward:
show_layers("A", "1");
show_layers("B", "1");
function show_layers(g, n)
{
// g is the name of the group (layerset)
// n is the name of the layer (artlayer)
srcDoc.activeLayer = srcDoc.layerSets.getByName(g).artLayers.getByName(n);
srcDoc.activeLayer.visible = true;
}
So, assuming all layers are switched off, you can get the layers by name and then make them visible.
Switching off all the layers is a bit more involved. You have to loop over all groups and then loop over its sublayers. Working with groups is a bit of a learning curve.
function switch_off_all_layers(bool)
{
var numLayers = app.activeDocument.layers.length;
// want to hide the background as well?
// default: background = visible
if (bool == undefined) bool = false;
if (bool == false)
{
numLayers -=1; // -1 for background
}
for(var i = 0 ; i < numLayers; i++)
{
if (app.activeDocument.layers[i].typename == "LayerSet")
{
app.activeDocument.layers[i].visible = true;
var subDoc = app.activeDocument.layers[i];
var numOfSubLayers = subDoc.layers.length;
for (var j = numOfSubLayers -1; j >= 0; j--)
{
var tempSubLayer = subDoc.layers[j];
tempSubLayer.visible = false;
}
}
}
}
Further to my explanation, if you can't supply the group layers & names then you fill want to loop over all groups and all layers.
var groupLayers = [];
var artLayers = [];
var theLayers = collectAllLayers(app.activeDocument, 0);
alert("Group layers\n" + groupLayers);
alert("Art layers\n" + artLayers);
// function collect all layers
function collectAllLayers (theParent, level)
{
for (var m = theParent.layers.length - 1; m >= 0; m--)
{
var theLayer = theParent.layers[m];
// apply the function to layersets;
if (theLayer.typename == "ArtLayer")
{
if (theLayer.isBackgroundLayer == true)
{
// add background layer (if needed)
// artLayers.push(theLayer.name);
}
else
{
// find the art layers
artLayers.push(theLayer.name);
}
}
else
{
// find the group layers
groupLayers.push(theLayer.name);
collectAllLayers(theLayer, level + 1)
}
}
}

Photoshop Scripting JavaScript loop through layers problem

I have a script that does the following:
loops through layers,
saves each layer in a separate folder (name of folder same as layer name)
saved layer images have names "nameofthedocument, nameofthelayer.png"
Now I wanted to add text item on top of those layers while they are saving as separate pngs so that for gods sake you don't have to always look at the file name but instead there would be text in the PNG image rasterized and says "nameofthelayer" variable.
So for each layer of course different text should be inserted in the image.
i encountered weird problems once I tried to do that what seamigly looked easy.
HEre is the link with a video and an explanation of the code as well as what I did and why it messed up the script.
https://drive.google.com/drive/folders/1h2KAiEuruLY_PQ2JVhQAwINDYPvk9xHS?usp=sharing
Thank you folks, please help me out
Code, image and video explanation is all available in the link
CODE:
// NAME:
// SaveLayers
// DESCRIPTION:
// Saves each layer in the active document to a PNG or JPG file named after the layer.
// These files will be created in the current document folder.
// REQUIRES:
// Adobe Photoshop CS2 or higher
// VERSIONS:
// 27 March 2013 by Robin Parmar (robin#robinparmar.com)
// preferences stored in object
// auto-increment file names to prevent collisions
// properly handles layer groups
// header added
// code comments added
// main() now has error catcher
// counts number of layers
// many little code improvements
// 26 Sept 2012 by Johannes on stackexchange
// original version
// enable double-clicking from Finder/Explorer (CS2 and higher)
#target photoshop
app.bringToFront();
//alert(activeDocument.name);
function main() {
// two quick checks
if(!okDocument()) {
alert("Document must be saved and be a layered PSD.");
return;
}
var len = activeDocument.layers.length;
// user preferences
prefs = new Object();
prefs.fileType = "";
prefs.fileQuality = 0;
prefs.filePath = app.activeDocument.path;
prefs.count = 0;
saveLayers(activeDocument);
//toggleVisibility(activeDocument);
}
function saveLayers(ref) {
var len = ref.layers.length;
// rename layers top to bottom
for (var i = 0; i < len; i++) {
var layer = ref.layers[i];
if (layer.typename == 'LayerSet') {
// recurse if current layer is a group
saveLayers(layer);
} else {
// otherwise make sure the layer is visible and save it
layer.visible = true;
saveImage(layer.name);
layer.visible = false;
}
}
}
function getNameWithoutExtension(nameWithExt) {
var nameWithoutExtension = nameWithExt;
return nameWithoutExtension.split(".")[0];
}
function saveImage(layerName) {
var f = new Folder("D:/Process/0/"+layerName);
f.create();
//////////////// BROKEN PART I ADDED
#target photoshop
// Current layer name as text layer
var myDoc = app.activeDocument;
var myRulers = app.preferences.rulerUnits
app.preferences.rulerUnits = Units.PIXELS;
var OriginalLayerName = myDoc.activeLayer.name
var myLayerName = myDoc.activeLayer.name + "text";
var myLayerText = myDoc.artLayers.add()
myLayerText.kind = LayerKind.TEXT
var myText = myLayerText.textItem
myColor = new SolidColor
myColor.rgb.red = 255
myColor.rgb.green = 0
myColor.rgb.blue = 0
myLayerText.textItem.color = myColor
myText.position = [0,20] // Upper Left
myText.justification = Justification.LEFT
myText.size = 12
myText.contents = myLayerName
myLayerText.name = myLayerName // Or add a fixed string in quotes i.e. 'My Great Layer Name!'
app.preferences.rulerUnits = myRulers
//////////////////////////////END OF THE BROKN PART
//var handle = generateName(f.path + "/",layerName, ".png");
//alert(handle);
var handle = getUniqueName(prefs.filePath + "/"+ layerName +"/"+ getNameWithoutExtension(activeDocument.name) + ", " + layerName);
//alert(handle);
prefs.count++;
if(prefs.fileType=="PNG") {
SavePNG(handle);
} else {
SaveJPEG(handle);
}
}
function getUniqueName(fileroot) {
// form a full file name
// if the file name exists, a numeric suffix will be added to disambiguate
var filename = fileroot;
for (var i=1; i<100; i++) {
var handle = File(filename + "." + prefs.fileType);
if(handle.exists) {
filename = fileroot + "-" + padder(i, 3);
} else {
return handle;
}
}
}
function padder(input, padLength) {
// pad the input with zeroes up to indicated length
var result = (new Array(padLength + 1 - input.toString().length)).join('0') + input;
return result;
}
function SavePNG(saveFile) {
pngSaveOptions = new PNGSaveOptions();
activeDocument.saveAs(saveFile, pngSaveOptions, true, Extension.LOWERCASE);
}
function SaveJPEG(saveFile) {
pngSaveOptions = new PNGSaveOptions();
activeDocument.saveAs(saveFile, pngSaveOptions, true, Extension.LOWERCASE);
}
// file type
var saveOpt = [];
saveOpt[0] = "PNG";
saveOpt[1] = "PNG";
// png type
var pngtypeOpt = [1];
pngtypeOpt[0]=24;
pngtypeOpt[1]=24;
function okDocument() {
// check that we have a valid document
if (!documents.length) return false;
var thisDoc = app.activeDocument;
var fileExt = decodeURI(thisDoc.name).replace(/^.*\./,'');
return fileExt.toLowerCase() == 'psd'
}
function wrapper() {
function showError(err) {
alert(err + ': on line ' + err.line, 'Script Error', true);
}
try {
// suspend history for CS3 or higher
if (parseInt(version, 10) >= 10) {
activeDocument.suspendHistory('Save Layers', 'main()');
} else {
main();
}
} catch(e) {
// report errors unless the user cancelled
if (e.number != 8007) showError(e);
}
}
function generateName(filePath, layerName, ext) {
var generatedName = app.activeDocument.name;
var generatedName = generatedName.split(".")[0];
generatedName = generatedName + ", " + layerName;
return filePath + generatedName + ext;
}
wrapper();

Photoshop Javascript List Layers

I want to list layers in json document. After my code :
#include json2.js
var doc = app.activeDocument;
var allLayers = [];
var allLayers = collectAllLayers(doc, allLayers);
function collectAllLayers (doc, allLayers){
for (var m = 0; m < doc.layers.length; m++){
var theLayer = doc.layers[m];
if (theLayer.typename === "ArtLayer"){
allLayers.push(theLayer);
}else{
collectAllLayers(theLayer, allLayers);
}
}
return allLayers;
}
var json = JSON.stringify(allLayers);
alert(json);
I am getting an error General Photoshop error occurred.This functionality may not be avaliable in this version of photoshop
I want to list groups and layers ex. like this :
Group1
> Layer 1
> Layer 2
> Group 2
> > Layer 3
> > Layer 4
> > Group 3
> > > Layer 5
> > > Layer 6
> Layer 3
Have you any ideas to do that?
Thanks for answers and help in advance!
I'd suggest you to read about Action Manager code, using DOM in Photoshop is mostly slow and limited. For example this non-recursive function will traverse through all layers and add layer DOM objects to your allLayers:
var doc = app.activeDocument;
var allLayers = [];
traverseLayersAMFlat(doc);
alert(allLayers);
//non-recursive action manager traversal function
function traverseLayersAMFlat(doc)
{
function _selectLayerById(ID) //select just this layer
{
var ref = new ActionReference();
ref.putIdentifier(charIDToTypeID('Lyr '), ID);
var desc = new ActionDescriptor();
desc.putReference(charIDToTypeID('null'), ref);
desc.putBoolean(charIDToTypeID('MkVs'), false);
executeAction(charIDToTypeID('slct'), desc, DialogModes.NO);
}//_selectLayerById
//how many layers are there in this document?
var ref = new ActionReference();
ref.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
var count = executeActionGet(ref).getInteger(charIDToTypeID('NmbL'));
//traverse the list backwards (does parents first)
for (var i = count; i >= 1; i--)
{
ref = new ActionReference();
ref.putIndex(charIDToTypeID('Lyr '), i);
var desc = executeActionGet(ref); //access layer index #i
var layerID = desc.getInteger(stringIDToTypeID('layerID')); //ID for selecting by ID #
var layerSection = typeIDToStringID(desc.getEnumerationValue(stringIDToTypeID('layerSection'))); //layerSectionStart, layerSectionContent, laterSectionEnd
if (layerSection != 'layerSectionEnd')
{
_selectLayerById(layerID);
allLayers.push(app.activeDocument.activeLayer)
}
}//for i-- countdown
try
{ //if there is a magic background layer, process it, too
app.activeDocument.activeLayer = app.activeDocument.backgroundLayer;
allLayers.push(app.activeDocument.backgroundLayer)
} catch (e) {;}
}

Photoshop Script - Get name of visible Layer in Group/Layerset

i'm stuck with a script that detects which layer is visible in a (sub)layerset (aka Group) with the name "Color".
The script below checks for all visible layers and selects them. I can't get it working to do the same thing ONLY in the mentioned layerset.
Any help would be highly appreciated!
#target photoshop
app.bringToFront();
main();
function main(){
if(!documents.length) return;
var Vis = getVisLayers();
deselectLayers();
for(var a in Vis){
selectLayerById(Number(Vis[a]),true);
}
}
function getVisLayers(){
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
var count = executeActionGet(ref).getInteger(charIDToTypeID('NmbL')) +1;
var Names=[];
try{
activeDocument.backgroundLayer;
var i = 0; }catch(e){ var i = 1; };
for(i;i<count;i++){
if(i == 0) continue;
ref = new ActionReference();
ref.putIndex( charIDToTypeID( 'Lyr ' ), i );
var desc = executeActionGet(ref);
var layerName = desc.getString(charIDToTypeID( 'Nm ' ));
var Id = desc.getInteger(stringIDToTypeID( 'layerID' ));
if(layerName.match(/^<\/Layer group/) ) continue;
var layerType = typeIDToStringID(desc.getEnumerationValue( stringIDToTypeID( 'layerSection' )));
var isLayerSet =( layerType == 'layerSectionContent') ? false:true;
var vis = desc.getBoolean(charIDToTypeID( "Vsbl" ));
if(!isLayerSet && vis) Names.push(Id);
};
return Names;
};
function selectLayerById(ID, add) {
add = (add == undefined) ? add = false : add;
var ref = new ActionReference();
ref.putIdentifier(charIDToTypeID('Lyr '), ID);
var desc = new ActionDescriptor();
desc.putReference(charIDToTypeID('null'), ref);
if (add) {
desc.putEnumerated(stringIDToTypeID('selectionModifier'), stringIDToTypeID('selectionModifierType'), stringIDToTypeID('addToSelection'));
}
desc.putBoolean(charIDToTypeID('MkVs'), false);
executeAction(charIDToTypeID('slct'), desc, DialogModes.NO);
}
function deselectLayers() {
var desc01 = new ActionDescriptor();
var ref01 = new ActionReference();
ref01.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
desc01.putReference( charIDToTypeID('null'), ref01 );
executeAction( stringIDToTypeID('selectNoLayers'), desc01, DialogModes.NO );
};
enter image description here
Trouble with groups is that you have to jump at the deep end. Sadly the way Photoshop keeps track of it's layers isn't intuitive. The best thing to do is have a recursive function and look over ALL the layers - from that you'll be able to determine if it's a group or not. And then work out if it's visible.
var allLayers = new Array();
var theLayers = collectAllLayers(app.activeDocument, 0);
// function collect all layers
function collectAllLayers (theParent, level)
{
for (var m = theParent.layers.length - 1; m >= 0; m--)
{
var theLayer = theParent.layers[m];
if (theLayer.typename == "ArtLayer")
{
// find the art layers
if (theLayer.visible == true)
{
//do stuff here
}
}
else
{
allLayers.push(level + theLayer.name);
collectAllLayers(theLayer, level + 1)
}
}
}
Thanks to #Ghoul Fool i finally managed to get this working. I changed the code a bit as i only need to check which layer is visible and store this name as a variable. Would be great if someone more skilled could correct my code for others who will use it or parts of it.
Thanks again
// Checking which layer is visible in layeset (group) "Colors" in "Mockup.psd" aka mockupDoc and store it's name for later use in the "save" part.
var front = mockupDoc.layerSets.getByName("Front");
var colors = front.layerSets.getByName("Colors");
for (var m = colors.layers.length - 1; m >= 0; m--)
{
var theLayer = colors.layers[m];
if (theLayer.typename == "ArtLayer")
{
if (theLayer.visible == true)
{
// Sets a variable for the name of the visible Layer in the "Colors" Group, so i can use it later as a part of the Filename when saving.
var activeColor = theLayer.name;
}
}
};

InDesign Server CS6 Scripting - Get TextFrame containing DataMergeField

I am unable to determine if a DataMergeField is contained within a TextFrame.
var document = app.open('template.indd');
var dataMerge = document.dataMergeProperties;
var field;
for (field in document.dataMergeTextPlaceholders) {
var story = field.parentStory;
var content = story.contents;
var textFrame = story.textFrames.item(0);
//textFrame is null
}
//....
I am attempting to provide wrap, fit, fill options for the textual contents of any DataMergeFields, adjusting them based on the TextFrame dimensions. Without knowing any of the DataMergeFields or TextFrames properties used in the document.
Something like this should work in JS:
var document = app.open('template.indd');
var hs = document.dataMergeTextPlaceholders;
var n = hs.length, h, tf;
while (n--) {
h = hs[n];
if ( h.storyOffset.parentTextFrames.length ) {
tf = h.storyOffset.parentTextFrames[0];
//do something with tf
}
}
I was able to get this to work with the following. I am guessing that using document as opposed to app.documents.item(0) was the issue.
app.open('template.indd');
var phs = app.documents.item(0).dataMergeTextPlaceholders;
var i, textFrame, ph, story;
for (i = 0; i < phs.length; i++) {
ph = phs.item(i);
if (ph instanceof DataMergeTextPlaceholder) {
story = ph.parentStory;
if (story.textFrames.length > 0) {
textFrame = story.textFrames.item(0);
//...
}
}
}

Categories

Resources