Copy link to clipboard
Copied
Hi,
I'm looking for a script to order selected layers alphabetically. I'm familiar with Adobe Photoshop Scripts | Trevor Morris Photographics, but am looking for constraint to selection. I've tried using this script: get array of selected layers in photoshop via extendscript · GitHub in combination, however, I've hit a wall.
Thanks,
Tom
I reproduced the error ).
This should work
...var idx_shift = 0;
try { activeDocument.backgroundLayer } catch (e) { idx_shift = 1; }
while (1)
{
var list = get_selected_layers();
if (!list) { alert("No selected layers"); break; }
var list2 = new Array();
for (var i in list) list2.push(list);
function cmp(a, b)
{
var x = expand_numbers(a[0]);
var y = expand_numbers(b[0]);
if (x > y) return -1;
if (x < y) return 1;
Copy link to clipboard
Copied
Try this code )
while (1)
{
var list = get_selected_layers();
if (!list) { alert("No selected layers"); break; }
var list2 = new Array();
for (var i in list) list2.push(list);
function cmp(a, b)
{
var x = expand_numbers(a[0]);
var y = expand_numbers(b[0]);
if (x > y) return -1;
if (x < y) return 1;
return 0;
}
list.sort(cmp);
var cont = false;
for (var i = 0; i < list.length; i++)
{
if (list2[1] != list[1])
{
set_layers_index(list2[1], list[1]);
cont = true;
break;
}
}
if (!cont) break;
}
alert("Done!");
//////////////////////////////////////////////////////////////////
function set_layers_index(cur_idx, new_idx)
{
try {
var d = new ActionDescriptor();
var r1 = new ActionReference();
r1.putIndex( charIDToTypeID( "Lyr " ), cur_idx);
var r2 = new ActionReference();
r2.putIndex( charIDToTypeID( "Lyr " ), new_idx );
d.putReference( charIDToTypeID( "null" ), r1 );
d.putReference( charIDToTypeID( "T " ), r2 );
executeAction( charIDToTypeID( "move" ), d, DialogModes.NO );
}
catch (e) { alert(e); }
}
//////////////////////////////////////////////////////////////////
function get_selected_layers()
{
try {
var r = new ActionReference();
r.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("targetLayers"));
r.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
var list = executeActionGet(r).getList(stringIDToTypeID("targetLayers"));
var n = 0;
try { activeDocument.backgroundLayer } catch (e) { n = 1; }
var len = list.count;
var selected_layers = new Array();
for (var i = 0; i < len; i++)
{
try
{
var idx = list.getReference(i).getIndex() + n;
var r = new ActionReference();
r.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("name"));
r.putIndex( charIDToTypeID( "Lyr " ), idx);
var ret = executeActionGet(r).getString(stringIDToTypeID("name"));
selected_layers.push([executeActionGet(r).getString(stringIDToTypeID("name")), idx]);
}
catch (e) { alert(e); return null; }
}
return selected_layers;
}
catch (e) { return null; }
}
////////////////////////////////////////////////////////////////////////////////////////////
function expand_numbers(s, max_digit)
{
if (max_digit == undefined) max_digit = 10;
var digit_cnt;
s = s.toString();
var len = s.length;
var s1 = "";
for (var i = len-1; i >= 0; i--)
{
var c = s.charAt(i);
if (c >= "0" && c <="9")
{
if (digit_cnt == undefined)
digit_cnt = 1;
else
++digit_cnt;
s1 += c;
}
else if (digit_cnt != undefined)
{
if (digit_cnt < max_digit) for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0";
s1 += c;
digit_cnt = undefined;
}
else
{
s1 += c;
}
}
if (digit_cnt != undefined && digit_cnt < max_digit)
for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0";
len = s1.length;
var ret = "";
for (var i = len-1; i >= 0; i--) ret += s1.charAt(i);
ret = ret.toUpperCase();
return ret;
}
Copy link to clipboard
Copied
Thanks!
I ran the script and all but one of the selected layers got grouped into the group above them. I undid that and placed the group below all of the selected layers and ran again. Got a script alert: Error: General Photoshop error occurred. This functionality may not be available in this version of Photoshop.
- The command "Move" is not currently available.
I click OK or the x button and a new script alert appears (saying the same thing). I cannot exit Script Alerts...
Copy link to clipboard
Copied
Please post the screenshot of your layers structures and their selection. What version of Photoshop are you running?
upd. change
catch (e) { alert(e); }
to
catch (e) { alert(e); throw(e); }
Copy link to clipboard
Copied
I reproduced the error ).
This should work
var idx_shift = 0;
try { activeDocument.backgroundLayer } catch (e) { idx_shift = 1; }
while (1)
{
var list = get_selected_layers();
if (!list) { alert("No selected layers"); break; }
var list2 = new Array();
for (var i in list) list2.push(list);
function cmp(a, b)
{
var x = expand_numbers(a[0]);
var y = expand_numbers(b[0]);
if (x > y) return -1;
if (x < y) return 1;
return 0;
}
list.sort(cmp);
var cont = false;
for (var i = 0; i < list.length; i++)
{
if (list2[1] != list[1])
{
set_layers_index(list2[1], list[1]);
cont = true;
break;
}
}
if (!cont) break;
}
alert("Done!");
//////////////////////////////////////////////////////////////////
function set_layers_index(cur_idx, new_idx)
{
try {
var d = new ActionDescriptor();
var r1 = new ActionReference();
r1.putIndex( charIDToTypeID( "Lyr " ), cur_idx);
var r2 = new ActionReference();
r2.putIndex( charIDToTypeID( "Lyr " ), new_idx-idx_shift );
d.putReference( charIDToTypeID( "null" ), r1 );
d.putReference( charIDToTypeID( "T " ), r2 );
executeAction( charIDToTypeID( "move" ), d, DialogModes.NO );
}
catch (e) { alert(e); throw(e); }
}
//////////////////////////////////////////////////////////////////
function get_selected_layers()
{
try {
var r = new ActionReference();
r.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("targetLayers"));
r.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
var list = executeActionGet(r).getList(stringIDToTypeID("targetLayers"));
var len = list.count;
var selected_layers = new Array();
for (var i = 0; i < len; i++)
{
try
{
var idx = list.getReference(i).getIndex() + idx_shift;
var r = new ActionReference();
r.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("name"));
r.putIndex( charIDToTypeID( "Lyr " ), idx);
var ret = executeActionGet(r).getString(stringIDToTypeID("name"));
selected_layers.push([executeActionGet(r).getString(stringIDToTypeID("name")), idx]);
}
catch (e) { alert(e); return null; }
}
return selected_layers;
}
catch (e) { return null; }
}
////////////////////////////////////////////////////////////////////////////////////////////
function expand_numbers(s, max_digit)
{
if (max_digit == undefined) max_digit = 10;
var digit_cnt;
s = s.toString();
var len = s.length;
var s1 = "";
for (var i = len-1; i >= 0; i--)
{
var c = s.charAt(i);
if (c >= "0" && c <="9")
{
if (digit_cnt == undefined)
digit_cnt = 1;
else
++digit_cnt;
s1 += c;
}
else if (digit_cnt != undefined)
{
if (digit_cnt < max_digit) for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0";
s1 += c;
digit_cnt = undefined;
}
else
{
s1 += c;
}
}
if (digit_cnt != undefined && digit_cnt < max_digit)
for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0";
len = s1.length;
var ret = "";
for (var i = len-1; i >= 0; i--) ret += s1.charAt(i);
ret = ret.toUpperCase();
return ret;
}
Copy link to clipboard
Copied
Amazing! Thank you very much--I really appreciate it.
Copy link to clipboard
Copied
I compared performance of our scripts and found the more selected layers the longer time your script needs to sort all of them. Only up to 10 selected layers yours is a little faster - here is comparision in seconds (layers number / yours / mine):
10 / 0.12 / 0.17
20 / 2.20 / 0.38
30 / 8.00 / 0.55
40 / 25.4 / 0.80
50 / 57.5 / 1.00
It also does not handle layers with same name like '1' and '1', 'Layer 0' and 'Layer 0' or 'a' and 'A'. It's looping endlessly itself...
Copy link to clipboard
Copied
Here is new version whithout "while" cycle )
var idx_shift = 0;
try { activeDocument.backgroundLayer } catch (e) { idx_shift = 1; }
var list = get_selected_layers();
if (list)
{
var orig_idx = new Array();
for (var i in list) orig_idx.push(list[1]);
function cmp(a, b)
{
var x = expand_numbers(a[0]);
var y = expand_numbers(b[0]);
if (x > y) return -1;
if (x < y) return 1;
return 0;
}
list.sort(cmp);
for (var i = list.length-1; i >= 0; i--) set_layers_index(list[2], orig_idx);
}
alert("Done!");
//////////////////////////////////////////////////////////////////
function set_layers_index(id, idx)
{
try {
var d = new ActionDescriptor();
var r1 = new ActionReference();
r1.putIdentifier( charIDToTypeID( "Lyr " ), id);
var r2 = new ActionReference();
r2.putIndex( charIDToTypeID( "Lyr " ), idx-idx_shift );
d.putReference( charIDToTypeID( "null" ), r1 );
d.putReference( charIDToTypeID( "T " ), r2 );
executeAction( charIDToTypeID( "move" ), d, DialogModes.NO );
}
catch (e) { alert(e); throw(e); }
}
//////////////////////////////////////////////////////////////////
function get_selected_layers()
{
try {
var r = new ActionReference();
r.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("targetLayers"));
r.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
var list = executeActionGet(r).getList(stringIDToTypeID("targetLayers"));
var len = list.count;
var selected_layers = new Array();
for (var i = 0; i < len; i++)
{
try
{
var idx = list.getReference(i).getIndex() + idx_shift;
var r = new ActionReference();
r.putIndex( charIDToTypeID( "Lyr " ), idx);
var ret = executeActionGet(r);
selected_layers.push([ret.getString(stringIDToTypeID("name")), idx, ret.getString(stringIDToTypeID("layerID"))]);
}
catch (e) { alert(e); return null; }
}
return selected_layers;
}
catch (e) { return null; }
}
////////////////////////////////////////////////////////////////////////////////////////////
function expand_numbers(s, max_digit)
{
if (max_digit == undefined) max_digit = 10;
var digit_cnt;
s = s.toString();
var len = s.length;
var s1 = "";
for (var i = len-1; i >= 0; i--)
{
var c = s.charAt(i);
if (c >= "0" && c <="9")
{
if (digit_cnt == undefined)
digit_cnt = 1;
else
++digit_cnt;
s1 += c;
}
else if (digit_cnt != undefined)
{
if (digit_cnt < max_digit) for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0";
s1 += c;
digit_cnt = undefined;
}
else
{
s1 += c;
}
}
if (digit_cnt != undefined && digit_cnt < max_digit)
for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0";
len = s1.length;
var ret = "";
for (var i = len-1; i >= 0; i--) ret += s1.charAt(i);
ret = ret.toUpperCase();
return ret;
}
Copy link to clipboard
Copied
btw now your script is twice faster than mine. Both are exremaly fast though, but yours does not regroup layers - it s why
Copy link to clipboard
Copied
Hey there!
A little late to the party but hopefully you see this.
I am trying to use your script in PS 22.5.6
I receive error "
Illegal argument - Argument 2 - Numerical value expected.
Any ideas?
Thanks!
Copy link to clipboard
Copied
Before you run script go to 'Layers' panel, select 'Panel Oprions' from dropdown menu (you open clicking icon in right upper corner of panel), make 'Add "copy" to copied Layers and Groups' box empty. Script will be working if that won't be done, but all layer(Set)s get 'copy' word in their names. It is possible to remove it by script, but I did not do it as because of Photoshop bug there is need to select each single layer before renaming what increases time of performance. If you want script worked extremaly fast, press 'tab' key to toggle off your panels (it's scriptable but I let user decide of it). Script works for background and all kind of layers (those nasted in all levels groups as well). If any group will be selected before running script it is going to be unselected. Sorting is based on modified order of ASCII characters, so generally it is the same for digits and non letter characters. Difference is that order for letters is like: AaBbCc(...)Zz. Script distinguishes letters case and numbers in names - not as single digits but row of them - so there will be for ex. this order applied: 0, 1, 2, 3, 10, 99, 100 and so on. I didn't sort for any possible case there can happen but probably noone uses so complex layers names it ever was needed to cover unusual namings. It works also for layers having same content and length, while for example 'A' named layer will be sorted before 'a' named layer. I did not play with (disabled) linked layers, however those selected keep their links to each other while lose to those they were unselected and disabled ones gets enabled. These properties are possible to retrieve but as far there is no need to do it I didn't spend time for it. Visibility, effects and transparency stay untouched. It took 7,5 second with toggled off panels for 299 layer with background, so it gives 1 second for 40 layers! If you get some errors let me know so I'll make fixes.
gT = new Date().getTime(), $.level = 0; function sTT(v) {return stringIDToTypeID(v)}
function arr(v) {
(ref1 = new ActionReference()).putProperty(sTT('property'), tL = sTT('targetLayers'))
ref1.putEnumerated(sTT('document'), sTT('ordinal'), sTT('targetEnum'))
if ((dsc1 = executeActionGet(ref1)).hasKey(tL)) {for(lst = dsc1.getList(tL), i = 0; i < lst.count;) {
(ref2 = new ActionReference()).putIndex(sTT('layer'), lst.getReference(i++).getIndex() + bG), s = 'Start'
if (typeIDToStringID((dsc = executeActionGet(ref2)).getEnumerationValue(sTT(lS = 'layerSection'))) == lS + s)
{ref2.putIdentifier(sTT('layer'), dsc.getInteger(sTT('layerID'))), dsc1.putReference(sTT('null'), ref2)
dsc1.putEnumerated(sTT(sM = 'selectionModifier'), sTT(sM + 'Type'), sTT('removeFromSelection'))
dsc1.putBoolean(sTT('makeVisible'), false), executeAction(sTT('select'), dsc1, DialogModes.NO)}
else v.unshift([dsc.getString(sTT('name')), dsc.getInteger(sTT('layerID'))])}
}
return v
}
(ref = new ActionReference()).putIdentifier(sTT('layer'), 1)
try{bG = !(dsc = executeActionGet(ref)).getBoolean
(sTT('background'))} catch (err) {bG = 1}
if ((sL = arr([])).length > 1) {
(srt = Array().slice.call(sL)).sort(function(v1, v2) {
function l(v) {return v.length}; if ((v1 = v1[0]) != v2 = v2[0]) {
for(g = 0; g < l(v1); g++) {
if ((v1g = v1
) != v2g = v2 ) { function dig(v) {return !isNaN(v)}
if (dig(v1g) && dig(v2g)) {
function slc(v) {
return +String(v).slice(g).match(/\d+/)
}
return slc(v1) - slc(v2)
}
function cCA(v) {
return v.charCodeAt() -
(/[a-z]/.test(v) ? 31.5 : 0)
}
if (v1g && v2g) return cCA(v1g) - cCA(v2g)
}
if (!v1g || !v2g) return l(v1) - l(v2)
}
}
});
(ref1 = new ActionReference()).putEnumerated(sTT('layer'), sTT('ordinal'),
sTT('targetEnum')); (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1)
dsc1.putInteger(sTT('version'), 5), executeAction(sTT('duplicate'), dsc1, DialogModes.NO);
dL = arr([]);
(ref1 = new ActionReference()).putClass(sTT('layerSection'));
(dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1);
(ref2 = new ActionReference()).putEnumerated(sTT('layer'),
sTT('ordinal'), sTT('targetEnum')), dsc1.putReference(sTT
('from'), ref2), executeAction(sTT('make'), dsc1, DialogModes.NO)
grp = executeActionGet(ref2).getInteger(sTT('layerID' ))
function del(v1, v2) {
eval("(ref1 = new ActionReference()).put" + v1 + "(sTT('layer'), v2)");
(dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1)
executeAction(sTT('delete'), dsc1, DialogModes.NO)
}
for(i = 0; i < sL.length; i++) {
for(j = 0; j < srt.length; j++) {
if (sL[1] == srt
[1]) { (ref1 = new ActionReference()).putIdentifier(sTT('layer'), dL.shift()[1]);
(dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1);
(ref1 = new ActionReference()).putIdentifier(sTT('layer'), sL
[1]) t = executeActionGet(ref1).getInteger(sTT('itemIndex'));
(ref2 = new ActionReference()).putIndex(sTT('layer'), t)
dsc1.putReference(sTT('to'), ref2), executeAction
(sTT('move'), dsc1, DialogModes.NO);
del('Index', t - (bG ? 0 : 1))
if (!bG && t == 1) {
(ref1 = new ActionReference()).putClass(sTT('backgroundLayer'));
(dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1);
(ref2 = new ActionReference()).putIndex(sTT('layer'), t);
dsc1.putReference(sTT('using'), ref2), executeAction
(sTT('make'), dsc1, DialogModes.NO)
}
break
}
}
}
del('Identifier', grp), new Date().getTime() - gT
}
Copy link to clipboard
Copied
This dosnt work for me.. i simple her an error:
https://www.dropbox.com/s/2rhkl0hmlo8wdpm/Screenshot%202021-02-09%20at%2009.56.23.png?dl=0
- Numerical value expected.
Line:75
it seems to be erroring in the try-catch phase? Note that my document does not have a background layer.
Copy link to clipboard
Copied
You mean the code of r-bin (that contains try..catch statement) or mine you updated of?
Copy link to clipboard
Copied
Yes sorry, the answer posted by r-bin above was throwing the error. I found an alternative script on Github that worked instead in the end.
var layers = activeDocument.layers;
var layersArray = [];
var len = layers.length;
// store all layers in an array
for (var i = 0; i < len; i++) {
layersArray.push(layers[i]);
}
// sort layer top to bottom
layersArray.sort();
for (i = 0; i < len; i++) {
layersArray[i].move(layers[i], ElementPlacement.PLACEBEFORE);
}
Copy link to clipboard
Copied
Hi,
As I'm a bit of a newbie, it would be great if you could post what the script in the end looked like (I'm not entirely sure how you combined the two scripts you mentioned) 🙂
Best,
Christina
Copy link to clipboard
Copied
Same question as Chris201Chris above is asking,
Many Thanks to Members who take the time to respond to Complex Questions.
especially when it is difficult to put into words Apstract ideas.
My question ! ? :
I am quite good with Photoshop, but when it comes to Coding / Scripts,
i am completely in the dark,
and a simple way of Sorting Text Layers Alphabetically is what i need.
Copy link to clipboard
Copied