Copy link to clipboard
Copied
Hi,
I am newbie to photoshop scripting but I do have few years experience with Javascript. Looking for clarification as I can't figure out what is going on here.
I was looking for solution how to get array of selected artLayers and layersSets. I found couple solutions on blogs and forums which works, but I can't figure out what that code actually does so may someone could explain it line by line or point me to normal documentation as I found http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/photoshop/pdfs/photoshop_scriptref_ js.pdf doesn't explain anything just gives list of available functions.
How ActionDescriptor, ActionReference works, how returned object by executeActionGet(ref) is structured and how Photoshop stores actions in memory.
var ref = new ActionReference();
var selectedLayers = [];
ref.putEnumerated( // Puts an enumeration type and ID into a reference along with the desired class for the reference.
//why I do need use putEnumerated? what it gives?
charIDToTypeID("Dcmn"), // document
charIDToTypeID("Ordn"), // typeOrdinal - what is ordinal?
charIDToTypeID("Trgt") //enumTarget - what?why?
)
var desc = executeActionGet(ref); // returns ActionDescriptor, how this object structure looks? how it was created?
desc = desc.getList( stringIDToTypeID( 'targetLayers' )); //returns ActionList, but what is targetLayers,who specified targetLayers? or it means it targets all layers?
for(var i=0;i<c;i++){
try{
selectedLayers.push( desc.getReference( i ).getIndex() );//gets action reference index in the ActionList
}catch(e){
// selectedLayers.push( desc.getReference( i ).getIndex()+1 ); //I commented this out as I can't understand why it is used, if we failed for i, how I can be sure that we will not fail for i+1??
}
}
If I understand correctly it gives me indexes of actions but not layers. Is that right? After this code I should somehow get artLayer+layersSets by using action indexes?
--------------------------
--------------------------My structure of layers and groups is
Layer 4
Group4(1)
Layer 3
Group 3(1)
Group 2(1)
Layer 2
Group 1(1)
Layer 5
Layer 1
and when I select all groups ant layers script above which should return array of selected layers/groups indexes returns me this - 1,2,4,5,8,9,10,12,13. As you can see it is missing some numbers so I assume this is not array of layers indexes.
Thank you!
Tomas
Hi Tomas,
welcome to this land of sorrows 😉
Shortly put, Photoshop DOM coverage isn't full. Where the DOM is missing, ActionManager (AM) code fills the gaps.
For instance you can
app.activeDocument.activeLayer.applyGaussianBlur(20);
but when it comes to applying a Curve you just have to:
...var idCrvs = charIDToTypeID( "Crvs" );
var desc8 = new ActionDescriptor();
var idpresetKind = stringIDToTypeID( "presetKind" );
var idpresetKindType = stringIDToTypeID( "presetKindType" );
var idpres
Copy link to clipboard
Copied
Hi Tomas,
welcome to this land of sorrows 😉
Shortly put, Photoshop DOM coverage isn't full. Where the DOM is missing, ActionManager (AM) code fills the gaps.
For instance you can
app.activeDocument.activeLayer.applyGaussianBlur(20);
but when it comes to applying a Curve you just have to:
var idCrvs = charIDToTypeID( "Crvs" );
var desc8 = new ActionDescriptor();
var idpresetKind = stringIDToTypeID( "presetKind" );
var idpresetKindType = stringIDToTypeID( "presetKindType" );
var idpresetKindCustom = stringIDToTypeID( "presetKindCustom" );
desc8.putEnumerated( idpresetKind, idpresetKindType, idpresetKindCustom );
var idAdjs = charIDToTypeID( "Adjs" );
var list1 = new ActionList();
var desc9 = new ActionDescriptor();
var idChnl = charIDToTypeID( "Chnl" );
var ref3 = new ActionReference();
var idChnl = charIDToTypeID( "Chnl" );
var idChnl = charIDToTypeID( "Chnl" );
var idCmps = charIDToTypeID( "Cmps" );
ref3.putEnumerated( idChnl, idChnl, idCmps );
desc9.putReference( idChnl, ref3 );
var idCrv = charIDToTypeID( "Crv " );
var list2 = new ActionList();
var desc10 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
desc10.putDouble( idHrzn, 0.000000 );
var idVrtc = charIDToTypeID( "Vrtc" );
desc10.putDouble( idVrtc, 0.000000 );
var idPnt = charIDToTypeID( "Pnt " );
list2.putObject( idPnt, desc10 );
var desc11 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
desc11.putDouble( idHrzn, 70.000000 );
var idVrtc = charIDToTypeID( "Vrtc" );
desc11.putDouble( idVrtc, 75.000000 );
var idPnt = charIDToTypeID( "Pnt " );
list2.putObject( idPnt, desc11 );
var desc12 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
desc12.putDouble( idHrzn, 179.000000 );
var idVrtc = charIDToTypeID( "Vrtc" );
desc12.putDouble( idVrtc, 212.000000 );
var idPnt = charIDToTypeID( "Pnt " );
list2.putObject( idPnt, desc12 );
var desc13 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
desc13.putDouble( idHrzn, 255.000000 );
var idVrtc = charIDToTypeID( "Vrtc" );
desc13.putDouble( idVrtc, 255.000000 );
var idPnt = charIDToTypeID( "Pnt " );
list2.putObject( idPnt, desc13 );
desc9.putList( idCrv, list2 );
var idCrvA = charIDToTypeID( "CrvA" );
list1.putObject( idCrvA, desc9 );
desc8.putList( idAdjs, list1 );
executeAction( idCrvs, desc8, DialogModes.NO );
The above code is the output of the ScriptingListener plugin made by Adobe's Tom Ruark (available in the devnet section of the PS site) and it's the base of our AM understanding, since the only and closest thing to an official documentation is a 1999 pdf about building automation plugins in C/C++ for Photoshop 5.5.
There are tutorials in the ps-scripts.com website about ActionManager mainly by Mike Hale (who BTW seems to have stopped posting, lately - I hope he's well!) and xBytor.
A couple of threads are http://www.ps-scripts.com/bb/viewtopic.php?f=16&t=5210&sid=8c4436e880b87815b9492377175cac2b and http://ps-scripts.com/bb/viewtopic.php?f=15&t=340&sid=425050a8d59fcc724e5c325f51520b5c
Think about ActionDescriptors more or less like russian dolls that you use to get and/or set properties, or executing actions.
In the snippet you posted, you have built an ActionReference specifying that you're interested in the current Document (the Dcmn, Ordn, Trgt thing - I'm afraid that's the way it is: if you want to use, as I prefer, the more understandable stringIDs, it becomes stringIDToTypeID ("document"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum")).
Then you actually retrieve the Document's Descriptor via executeActionGet, feeding the function with the ActionReference you've built.
Now you have this big fat Descriptor, which contains all sort of stuff (see ps-scripts.com for descriptor inspectors code, which helps you identifying what's in there).
Then you've extracted an ActionList via getList(), specifying that you're interested in the very one which key is stringIDToTypeID( 'targetLayers' ). Etc, etc.
Mind you, when it comes to AM code and layers / layerSets, you can use indexes and IDs - I can be wrong because I don't use layerSets too much myself, but I remember they have a trailing index (that is, a layerSet has a starting index, and a hidden, closing index, which **might** be the reason for the +1 in the catch - others can help you with more accurate information).
Hope this helps!
Kind regards,
Davide Barranca
---
www.davidebarranca.com
www.cs-extensions.com
Copy link to clipboard
Copied
Thank you for all the details, I will definitely check articles that you recommend, I hope it will clarify a lot.
1999 pdf?? Wow, Adobe...Never thought is THAT bad. Well I need to join Adobe team and fix this mess.
Mine customer experience is very bad, as it is so painful to start improving Adobe products eco-system. Even all these reference .pdf documents, try read them on 5" mobile screen...It seems that Adobe still lives in 2004.
Copy link to clipboard
Copied
Just want to add some notes for others if they will look for clarification.
For me really helped this .jsx scrip( http://ps-scripts.cvs.sourceforge.net/viewvc/ps-scripts/xtools/apps/GetterDemo.jsx ) as it gives you Action Descriptor containing data, by digging in to this data and comparing how developers access it clarified a lot!
At start it is really overwhelming and painful but you need to learn to live with it.
Now let's create some extensions!
Copy link to clipboard
Copied
Just some quick notes:
1) GetterDemo is based on a demo program in the PS SDK. I haven't updated it to reflect any changes for several years. There may be more interesting tidbits that can be pulled from newer revs that I am not aware of. I'll take a look when the next major PS rev is dropped.
2) Starting with CC or CC2014, a lot more information was getting dumped by GetterDemo, probably from the Application Descriptor tree. I had to modify GetterDemo to save the data to an XML file because otherwise it would blowup when I tried to stuff it into a static or edit text widget. There is apparently an undocumented limit in there that I was breaking. I'm not sure if the current xtools.zip file has the most recent rev, but the link that kugelis provided should be the most recent version.
3) http://ps-scripts.cvs.sourceforge.net/viewvc/ps-scripts/xtools/xlib/PSConstants.js may help getting your head around some of the terminology.
4) I've been doing this since PS7 and know this stuff about as well as anybody outside of Adobe. They actually made some minor changes to the AM docs based on my input but it is still far from complete. Random people still drop out pieces AM code with magic numbers in it that I have no clue where they found them, probably something from the SDK code. In other words, the is no canonical source for info on AM programming.
5) My best advice is to study ScriptingListener output and to search for pre-existing posts or code examples. There is a lot of stuff in xtools as well as posts here and at ps-scripts.com to look at. But, honestly, it will not be an easy road.
6) Realize that there are some things that cannot be done either through the DOM or AM code. Unfortunately not everything is plugged into the automation framework so there are limitations as to what can be done via JavaScript.
Copy link to clipboard
Copied
Thank you for your input, appreciate it.
Copy link to clipboard
Copied
Also, I would suggest to use the LastLogEntry action from xbytor's XTools to "clean" the ScriptingListener code.
For instance it turns this blob:
var idMk = charIDToTypeID( "Mk " );
var desc2 = new ActionDescriptor();
var idNw = charIDToTypeID( "Nw " );
var desc3 = new ActionDescriptor();
var idNm = charIDToTypeID( "Nm " );
desc3.putString( idNm, """myCopy""" );
var idMd = charIDToTypeID( "Md " );
var idBlnM = charIDToTypeID( "BlnM" );
var idDrkn = charIDToTypeID( "Drkn" );
desc3.putEnumerated( idMd, idBlnM, idDrkn );
var idLyr = charIDToTypeID( "Lyr " );
desc2.putObject( idNw, idLyr, desc3 );
var idUsng = charIDToTypeID( "Usng" );
var idArSl = charIDToTypeID( "ArSl" );
var idSlct = charIDToTypeID( "Slct" );
desc2.putEnumerated( idUsng, idArSl, idSlct );
var idCpy = charIDToTypeID( "Cpy " );
desc2.putBoolean( idCpy, true );
executeAction( idMk, desc2, DialogModes.NO );
into this nicer function:
function ftn1() {
function cTID(s) { return app.charIDToTypeID(s); };
function sTID(s) { return app.stringIDToTypeID(s); };
var desc2 = new ActionDescriptor();
var desc3 = new ActionDescriptor();
desc3.putString( cTID('Nm '), """myCopy""" );
desc3.putEnumerated( cTID('Md '), cTID('BlnM'), cTID('Drkn') );
desc2.putObject( cTID('Nw '), cTID('Lyr '), desc3 );
desc2.putEnumerated( cTID('Usng'), cTID('ArSl'), cTID('Slct') );
desc2.putBoolean( cTID('Cpy '), true );
executeAction( cTID('Mk '), desc2, DialogModes.NO );
};
Even if it's time consuming - in fact not that much using a text editor which supports multiple cursors like SublimeText - I use to "translate" all charIDs to stringIDs since they're more meaningful to me:
$.writeln(typeIDToStringID(charIDToTypeID('Nm '))); // name
$.writeln(typeIDToStringID(charIDToTypeID('Md '))); // mode
$.writeln(typeIDToStringID(charIDToTypeID('BlnM'))); // blendMode
$.writeln(typeIDToStringID(charIDToTypeID('Drkn'))); // darken
$.writeln(typeIDToStringID(charIDToTypeID('Nw '))); // new
$.writeln(typeIDToStringID(charIDToTypeID('Lyr '))); // layer
$.writeln(typeIDToStringID(charIDToTypeID('Usng'))); // using
$.writeln(typeIDToStringID(charIDToTypeID('ArSl'))); // areaSelector
$.writeln(typeIDToStringID(charIDToTypeID('Slct'))); // selectionEnum
$.writeln(typeIDToStringID(charIDToTypeID('Cpy '))); // copy
$.writeln(typeIDToStringID(charIDToTypeID('Mk '))); // make
Finally you can further manipulate the "cleaned" output with stringIDs, for instance this way:
function duplicateLayer(layerName, blendMode) {
function s2t(s) { return app.stringIDToTypeID(s) }
var d1 = new ActionDescriptor();
var d2 = new ActionDescriptor();
d2.putString(s2t("name"), layerName);
d2.putEnumerated(s2t("mode"), s2t("blendMode"), s2t(blendMode));
d1.putObject(s2t("new"), s2t("layer"), d2);
d1.putEnumerated(s2t("using"), s2t("areaSelector"), s2t("selectionEnum"));
d1.putBoolean(s2t("copy"), true);
executeAction(s2t("make"), d1, DialogModes.NO);
};
Over time, patterns start to appear and it gets less ugly than expected. Even if I still fail 1 out of 3 or 4 times with ActionManager, I'd say that it's a puzzle that I enjoy solving!
Besides, lot of people have already covered the basics (layers, layersets, etc) in the past so I'd first look for information in the forums - chances are that you'll find there what you need.
Also, be aware that ActionManager code is usually faster than DOM equivalent - especially when looping through layers - because you can extract from the each layer's Descriptor exactly the chunk of information you're interested into, while DOM as far as I know always include stuff like histogram values etc, which slow down the execution.
Davide
Copy link to clipboard
Copied
Script I wrote to get all strings from chars by Brute Force Attack: charIDToTypeID and stringIDToTypeID - PS-SCRIPTS.COM
Copy link to clipboard
Copied
This will not give complete and reliable information about all charID's and stringID's
Do you know that
function _charIDToTypeID(s) { return s.charCodeAt(3) + 0x100*s.charCodeAt(2) + 0x10000*s.charCodeAt(1) + 0x1000000*s.charCodeAt(0); }
var s = "KcuH" // or any 4 char string
alert(charIDToTypeID(s) - _charIDToTypeID(s)) // always == 0
var s = "HGhajgsjagsfuckingadobeagshagsajgsjhgjh ajsgajsg ajsgjagsja" // or any string
alert(typeIDToStringID(stringIDToTypeID(s)) == s) always == true
In addition, there is a mass of charID without stringID and vice versa
Copy link to clipboard
Copied
Thank you for sharing, but I don't understand how I have to use it. When I use some 4 chars string in line 3 it always gives 0 (so like you wrote). When I use any string in line 7 it always gives true (so again like you wrote). Well no matter what char & string I use it gives each time the same result, so can you give some example?
Ps. yes I know about Chars and Strings without equivalents, but how to get to know about them all?
Copy link to clipboard
Copied
I showed the implementation of charIDToTypeID.
As for stringIDToTypeID.
If Photoshop does not know such strig, it remembers it when calling stringIDToTypeID and then returns the code automatically assigned to it.
Restart photoshop CS6 and run the script for the first time.
alert(typeIDToStringID(2532))
var s = "1234567890123456789012345678901234567890"
alert(stringIDToTypeID(s))
alert(typeIDToStringID(2532))
Remember what was in the alerts.
Run the script second time. Compare the alerts with the first time.
Copy link to clipboard
Copied
You can go through all the 2 ** 32 IDs and call the functions
typeIDToCharID and typeIDToStringID. But it's very long.