Action Manager Scripting

Adobe Community Professional ,
May 26, 2020 May 26, 2020

Copy link to clipboard

Copied

Hi Folks,

 

So i searched quite a bit and could not land on any meaningful documentation around this aspect of PS scripting. I am clear that this provides us access to low level functionality not exposed on the DOM but beyond this i am lost. Some questions that might be trivial are listed below

  • What is ActionDescriptor, ActionReference, ActionList etc. What do they actually map to, or in other words which one is used for what purpose
  • Using the scriptlisterner plugin we can get code to automate user actions done on the PS UI. So far so good, but what about the scenarios where we need to get values from the document. Lets take an example of counting the number or artboards in the document and then accessing properties like height. width, rotation for each of these. Where do we start for this?
  • When to use charIDToTypeID vs stringIDToTypeID. How are their arguments determined etc

I see lots of code snippets shared, but none gives an insight as to how it was written, how did you get to know the values used as arguments. Any pointers to a proper documentation would be helpful

 

Thanks

-Manan

TOPICS
Actions and scripting, How to, SDK

Views

3.7K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct Answer

Enthusiast , May 26, 2020 May 26, 2020
I started writing scripts a little over a year ago when I did not find a programmer to solve a specific problem, so maybe my experience will come in handy (I apologize in advance - I use a translator to write all this, so some wording may be incorrect). For most tasks, three things are enough to write Action Manager code: to know the names of all top-level objects, to have at hand a set of functions for each type of objects from the Photoshop JavaScript Reference Guide ADOBE PHOTOSHOP SCRIPTING...

Likes

Translate

Translate
Adobe Community Professional ,
May 26, 2020 May 26, 2020

Copy link to clipboard

Copied

Examine samples in Photoshop Software Development Kit and Presets / Scripts of your Ps folder.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 26, 2020 May 26, 2020

Copy link to clipboard

Copied

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
May 26, 2020 May 26, 2020

Copy link to clipboard

Copied

I started writing scripts a little over a year ago when I did not find a programmer to solve a specific problem, so maybe my experience will come in handy (I apologize in advance - I use a translator to write all this, so some wording may be incorrect).

 

For most tasks, three things are enough to write Action Manager code: to know the names of all top-level objects, to have at hand a set of functions for each type of objects from the Photoshop JavaScript Reference Guide ADOBE PHOTOSHOP SCRIPTING, to have an installed script listener plugin to see examples of performing operations on objects.

 

Names of top-level objects: application, document, layer, channel, path, historyState (snapshotClass), action, actionSet

 

For all these objects (except for the actionSet), we can get the descriptor of the active object using ActionReference (this is something like the path to the desired object) in the same way (only the class name in the first argument changes):

 

 

r = new ActionReference ()
r.putEnumerated (stringIDToTypeID ("document"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum"));
d = executeActionGet (r)

 

 

In this case, the descriptor d stores all the properties of the object. Open the Photoshop JavaScript Reference Guide and see what we can do with this object (without knowing anything about it).

For example, we can get property keys in a simple loop:

2020-05-26_21-37-08.png

 

 

str = ''
for (var i = 0; i <d.count; i++)
{str = typeIDToStringID (d.getKey (i)) '\n'}
alert (str)

 

 

(we can, of course, use ready-made scripts to get the properties of objects (or write yourself), but for a step-by-step example it will be more clear)

We can find the type of each property (and, accordingly, what function we need to use to get this property from the descriptor):

2020-05-26_21-37-28.png

 

 

str = ''
for (var i = 0; i <d.count; i++)
{str = typeIDToStringID (d.getKey (i)) ':' d.getType (d.getKey (i)) '\n'}
alert (str)

 

 

Further, knowing the name and type of the property, we can get its value using one of the functions from the Photoshop JavaScript Reference Guide.

 

For example, we need to get the number and sizes of artboards. We do not know exactly where they are stored, so we open the document, create the artboard, get the document descriptor and look for something related to artboard there:

2020-05-26_21-56-24.png

Nothing of the kind. We get the descriptor of the active layer:

 

 

 

r = new ActionReference ()
r.putEnumerated (stringIDToTypeID ("layer"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum"));
d = executeActionGet (r)

str = ''
for (var i = 0; i <d.count; i++)
{str = typeIDToStringID (d.getKey (i)) ':' d.getType (d.getKey (i)) '\n'}
alert (str)

 

 

 

2020-05-26_21-59-18.png

We see the artboard property, which is of type OBJECTTYPE. We find the function to get the object from the descriptor:

2020-05-26_22-00-53.png

So, we change our function to get this object:

 

 

 

r = new ActionReference ()
r.putEnumerated (stringIDToTypeID ("layer"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum"));
d = executeActionGet (r) .getObjectValue (stringIDToTypeID ("artboard"))

 

 

Since it will return an object to us (another ActionDescriptor), we can again get its properties in the same way:

 

 

str = ''
for (var i = 0; i <d.count; i++)
{str = typeIDToStringID (d.getKey (i)) ':' d.getType (d.getKey (i)) '\n'}
alert (str)

 

 

 

2020-05-26_22-04-23.png

We see artboardRect which again has the OBJECTTYPE type, we again change our function:

 

 

 

r = new ActionReference ()
r.putEnumerated (stringIDToTypeID ("layer"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum"));
d = executeActionGet (r) .getObjectValue (stringIDToTypeID ("artboard")). getObjectValue (stringIDToTypeID ("artboardRect"))

 

 

and get the properties of the object:

2020-05-26_22-05-58.png

Here it is! Since there are only 4 values, we can not bother with receiving each by name, but by index:

 

 

var top = d.getDouble (d.getKey (0)),
left = d.getDouble (d.getKey (1)),
bottom = d.getDouble (d.getKey (2)),
right = d.getDouble (d.getKey (3))

 

 

Now we need to think about how to get all artboards from the document. We return to a couple of levels above to obtain the properties of the layer and see what can be found useful. We remember, all that we see, switch to the usual layer and get the properties again. We see that the set of properties is different. We assume that you can use the "arboard" property which the normal layer does not have in order to understand - we have before us a regular layer or arboard. This can be done using the .hasKey function, that is, if:

 

 

r = new ActionReference ()
r.putEnumerated (stringIDToTypeID ("layer"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum"));
d = executeActionGet (r) .hasKey (stringIDToTypeID ("artboard")) 

 

 

will return true, then this is artboard, if not, then the usual layer

We try on artboard - we get true, we try on an ordinary layer - also true. Error? No. Please note that this property appears in all layers of the document as soon as at least one artboard has been created 😞 This method does not suit us.

 

We get other properties and try to understand how to distinguish an artboard from an ordinary layer. Notice the potentially interesting layerKind property of type INTEGERTYPE.

 

 

r = new ActionReference ()
r.putEnumerated (stringIDToTypeID ("layer"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum"));
d = executeActionGet (r) .getInteger (stringIDToTypeID ('layerKind'))

 

 

we get 7. We switch to a regular layer - we get 1. Create a text layer and everything that can come in
head and regretfully understand that layerKind == 7 can be not only on artboard, but also on layerSet (that is, artboard is a kind of layer group). Not that again.

We go through other properties and notice that BOOLEANTYPE artboardEnabled is false for all layers except artboard itself. I.e:

 

 

r = new ActionReference ()
r.putEnumerated (stringIDToTypeID ("layer"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum"));
d = executeActionGet (r) .getBoolean (stringIDToTypeID ('artboardEnabled'))

 

 

this is what we need.

Now we are trying to iterate over all layers of the document to find out how much artboard we have. We will do this again through ActionReference, but not through .putEnumerated, but through one of the three functions (the meaning can be understood from their names) -

2020-05-26_23-07-33.png

.putIdentifier - getting the path to the layer by ID, putIndex - by the numeric index, putName - by name. Since we don’t know anything about the document and cannot get IDs and names, we basically have only one option - putIndex.

That is, to get not an active layer, but just an n-layer (for example, the first)), instead

r.putEnumerated (stringIDToTypeID ("layer"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum"));

write

r.putIndex (stringIDToTypeID ("layer"), 1)

We return to the document properties and look for some useful property there that will allow us not to iterate over everything in an indefinite loop. We see INTEGERTYPE numberOfLayers - the number of layers (including hidden) in the document.

Here it is necessary to note the features of indexing layers - the index starts with 1 except for those cases when the document has a background layer. If it is, then indexing starts from 0.

We are writing the beginning of our function, in which we find out whether there is a background layer in the document (fortunately, the document property has a BOOLEANTYPE hasBackgroundLayer), if so, then we start enumerating the indices from zero, if not, then from one

 

 

r = new ActionReference ()
r.putEnumerated (stringIDToTypeID ("document"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum"));
var from = executeActionGet (r) .getBoolean (stringIDToTypeID ('hasBackgroundLayer'))? 0: 1

 

 

 

Please note that obtaining the value takes a noticeable time - this is due to the fact that we first get the entire handle of the object, and then select one property from it. This can be slightly optimized if, on the way to the object, you immediately indicate which one we need:

 

 

r = new ActionReference ();
r.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ('hasBackgroundLayer'))
r.putEnumerated (stringIDToTypeID ("document"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum"))
var from = executeActionGet (r) .getBoolean (stringIDToTypeID ('hasBackgroundLayer'))? 0: 1

 

 

Now the code works almost instantly.

Further:

 

 

r = new ActionReference ();
r.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ('numberOfLayers'))
r.putEnumerated (stringIDToTypeID ("document"), stringIDToTypeID ("ordinal"), stringIDToTypeID ("targetEnum"))
var to = executeActionGet (r) .getInteger (stringIDToTypeID ('numberOfLayers'))

 

 

Now we write a loop (here I will allow myself some compact code)

 

 

s2t = stringIDToTypeID;
var artboards = [];
for (var i = from; i <= to; i ++) {
    (r = new ActionReference ()). putProperty (s2t ("property"), p = s2t ('artboardEnabled'));
    r.putIndex (s2t ("layer"), i);
    var artboardEnabled = executeActionGet (r) .getBoolean (p);

    if (artboardEnabled) {
        (r = new ActionReference ()). putProperty (s2t ("property"), p = s2t ('artboard'));
        r.putIndex (s2t ("layer"), i);
        var artboard = executeActionGet (r) .getObjectValue (p),
            artboardRect = artboard.getObjectValue (s2t ("artboardRect")),
            bounds = {
                top: artboardRect.getDouble (s2t ('top')),
                left: artboardRect.getDouble (s2t ('left')),
                bottom: artboardRect.getDouble (s2t ('bottom')),
                right: artboardRect.getDouble (s2t ('right'))
            };
            artboards.push ({layerIndex: i, bounds: bounds})
    }
}

alert (artboards.toSource ())

 

 

Of course, a more complete understanding of Action Manager code comes gradually after reading forums, viewing scripts from other authors, and communicating with more advanced users (I am infinitely grateful to all the people who respond to requests for help and post interesting code examples).

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 27, 2020 May 27, 2020

Copy link to clipboard

Copied

Thanks, this is right what i wanted, really appreciate your writing a detailed answer with supporting code snippets. Now i feel like i can poke around the sdk.

 

P.S. :- Some of the code snippets are broken, i suppose maybe due to translation software. Things like increment in the for loop, + is absent while appending strings and introduction of extra space like \n is converted to \ n.

 

-Manan

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
May 27, 2020 May 27, 2020

Copy link to clipboard

Copied

Yes, I wrote in the translator, including the code. Tried to fix it.

 

While writing, I forgot what the task was - it is clear that width and height are easy to get, knowing the boundaries of the artboard, i.e. height = bottom is top, and width = right is left. Since artboard is essentially an ordinary group, then get the initial state (like it was 0 degrees, and then turned 90, then it means 90 degrees). You can only evaluate orientation by the ratio of height and width (i.e. portrait or landscape).

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 21, 2021 Jul 21, 2021

Copy link to clipboard

Copied

This is absolutely gold mine! Thank you so much for ur post & info! 

 

Also photoshop 2021, appear to no longer have getObjectValue! :- ( Any idea what they replaced it with? 

 

Also... (Im still going over basics with ps 2020 as it still works) if I want to access details on adjustment layer like... contrast, or color lookup, how would I cast the "layer" type in to correct type I need  in order to retrieve its details/options?

 

TIA!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
Jul 22, 2021 Jul 22, 2021

Copy link to clipboard

Copied

quote

Also photoshop 2021, appear to no longer have getObjectValue! :- ( Any idea what they replaced it with? 


By @Dariusz1989

 

Why do you think so? Example? 

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 22, 2021 Jul 22, 2021

Copy link to clipboard

Copied

Because 2020 works with this > and 2021 crashes :- )

Dariusz1989_0-1626969906110.png

 

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
Jul 22, 2021 Jul 22, 2021

Copy link to clipboard

Copied

LATEST

Make sure you created arboard:

 

sTT = stringIDToTypeID; (ref = new ActionReference())
.putProperty(sTT('property'), artbrd = sTT('artboard'))
ref.putEnumerated(sTT('layer'), sTT('ordinal'), sTT('targetEnum'));
(dsc=executeActionGet(ref)).hasKey(artbrd)&&alert(dsc.getObjectValue(artbrd))

 

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 31, 2020 May 31, 2020

Copy link to clipboard

Copied

Hi jazz-y,

You explain in such a way that it is so easy to understand. 🙂

Highly appreciated.

 

Best regards

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 26, 2020 May 26, 2020

Copy link to clipboard

Copied

What is ActionDescriptor, ActionReference, ActionList etc. What do they actually map to, or in other words which one is used for what purpose
 
That's what I think.
These are all components of the Actions that were in Photoshop for centuries, when there were no scripts in sight. Therefore, everything is so crooked and incomprehensible.
When the scripts appeared, they created ActionDescriptor, ActionReference objects, etc. to have access to these structures from which Actions are composed.
By and large, when you form an ActionDescriptor, you form an analogue of Action from the palette of Actions.
When you execute executeAction, you execute the usual Action command. But thanks to the scripts, you can cram any parameters you need there, and not those that are possible when activating the recording.
I don’t know any documentation on ActionDescriptor, except in the official PDF.
All the developments were obtained empirically, by trial and error, or peeped in other scripts mainly from Adobe itself.
 

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 27, 2020 May 27, 2020

Copy link to clipboard

Copied

  • When to use charIDToTypeID vs stringIDToTypeID. How are their arguments determined etc

 

My vague newb understanding is that charID is for special 4 character ID codes. For example:

 

 

 

 

// fit on screen ( same as ctrl-0 )
runMenuItem(app.charIDToTypeID("FtOn")); 

 

 

 

 

Some of these codes require a space "Zm  " to pad-out to four characters...

 

I believe that string ID is used for anything that is not a special four-character code. These values will often be found by inserting a menu item into an action and looking at the AM code recorded by the SL plug-in, or just by looking at the raw AM code captured when working.

 

 

 

// actual pixels menu command
runMenuItem(stringIDToTypeID('actualPixels'));

 

 

 

Photoshop_Constants_Rosetta_Stone

 

I am happy to be corrected if any of the above is incorrect, I'm learning too...

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 27, 2020 May 27, 2020

Copy link to clipboard

Copied

I forgot to mention that the previous example of running a menu command only appears to work on menu items that are "simple" and do not require further input, so it is not all 4 character charID codes that can be used exactly as above (this is just my gut feeling from limited exploration).

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
May 27, 2020 May 27, 2020

Copy link to clipboard

Copied

As I understand it, at first only charIDs were used, then (when new commands became inconvenient to describe with 4 characters) stringID was added to them.


So, almost any charID code can be converted to stringID:

 

alert (typeIDToStringID (charIDToTypeID('FtOn')) == stringIDToTypeID ('fitOnScreen'))

 

and far from all stringIDs, you can get the corresponding charID:

alert (typeIDToCharID(stringIDToTypeID('placedLayerEditContents')))

That is, the use of stringID is preferable in most situations (it is corny easier to read the code).

 

However, there are some situations where it is preferable to use only charID (for example, when specifying the name of the event in notifiers). There are also rare charIDs that do not have a stringID, for example charIDToTypeID ("LqFy")

 

Sometimes it is tempting not to use a charID or stringID, but to immediately indicate a numerical value of a type, however this is an erroneous path - type values may differ not only from version to version of Photoshop, but simply during the execution of the script.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 28, 2020 May 28, 2020

Copy link to clipboard

Copied

Thank you for the info, I built the following for practice:

 

 

//community.adobe.com/t5/photoshop/action-manager-scripting/td-p/11160326
//So, almost any charID code can be converted to stringID
var input = prompt("Enter the 4 character charID code:", 'FtOn');
var converter = (typeIDToStringID(charIDToTypeID(input)));
alert("stringID = " + converter + "\r" + "(stringIDToTypeID ('" + converter + "'))");

 

 

 

//community.adobe.com/t5/photoshop/action-manager-scripting/td-p/11160326
//from all stringIDs, you can get the corresponding charID:
var input = prompt("Enter the stringID code:", 'fitOnScreen');
var converter = (typeIDToCharID(stringIDToTypeID(input)));
alert("charID = '" + converter + "'" + "\r" + "(charIDToTypeID ('" + converter + "'))");

 

 

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Community Professional ,
May 28, 2020 May 28, 2020

Copy link to clipboard

Copied

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jan 20, 2021 Jan 20, 2021

Copy link to clipboard

Copied

I've just started to venture down this rabbit hole.  I found that I wasn't able to understand how the calling convention worked. I kept seeing ActionDescriptors with null key references, but couldn't put it all together.  Reading through the Scripting Listener logs didn't help either.  So I came up with a way to convert the Scripting Listener to a more conventional calling syntax;

save | save (Enumerated<saveStageType>: saveStage, Integer: DocI|documentID, Path: In|in, Object<PNGF>: As)
saveStage: saveStageType.saveBegin
DocI|documentID: (unique document identifier)
In|in: Path("/file")
As|as: PNGF|PNGFormat(Enumerated<PNGMethod>: Mthd|method)
	Mthd|method: PNGMethod.thorough

This captures all the information to make the ActionDescriptor/ActionReference style calling.  I find presents it in a more conventional programming style. As there is quite an unusual type of information, I've had to be a bit creative with the syntax decorations.

This says that the 4 cc code for the executeAction "save" is also the same for the string.  Requires 4 parameters to be placed in the top level Action Descriptor. Of type Enumerated, type Integer, type Path and type Object.  Enumerators have 2 values associated with their key, the key name is "saveStage", which doesn't have a 4cc, and it uses the enumeration type "saveStageType" I didn't put the value into the prototype, but I've include values on the following lines. In this case SaveBegin was the Enumeration value; which looks like this in original JS: desc.putEnumerated(sTT("saveStage"),sTT("saveStageType"),sTT("saveBegin");  The other confusing thing is that objects are also ActionDescriptors. So can have the same appearances as parameter lists. This shows that the PNG Format object requires 1 parameter, an Enumeration type, with 'Mthd' or "method" as its key, "PNGMethod" as the enumeration type and "thorough" as the enumeration value.  Making this template for each Action Manager command I came across, made the interface seem more like a conventional one.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines