19 Replies Latest reply on Apr 24, 2018 11:36 AM by Silly-V

    Creating a dynamic action to use with app.doScript() method.

    Silly-V Adobe Community Professional

      Since building dynamic actions is a topic unto itself, we often search for a reference thread to show somebody how to do this - and we often do so while trying to discuss work which is already involved and has nothing to do with actions or loading and playing them. Therefore I'm creating this thread to show an example and I'll paste the url to it when such questions arise.

       

      Sometimes in Illustrator scripting we need to accomplish tasks which (sometimes counterintuitively) do not have scripting access via the DOM.

      Fortunately since Illustrator CS6, they gave us the ability to play an action from a script with the app.doScript() command - which is not at all the same as Indesign's function of the same name. This one lets you play an action that exists inside the Actions panel. Immediately this may seem disappointing as users cannot be counted on to have specific actions at any given time.

      However, they also gave the ability to load and remove actions from .aia action files. Your .aia file just needs to be somewhere and app.loadAction() can read it right into the Actions panel. Same with app.unloadAction(setName, actionName) - where (thanks to qwertyfly) using (setName, "") an empty string argument for the action name will remove the entire set. And when you try to remove an action that does not exist, well, it throws an error - which is how you can check for making absolutely sure that an action set is completely removed.

      This may seem like a lot of work - to write a file, read it, play it and then discard it, often to do something that you'd think scripting should do in the first place.

       

      Sometimes the action alone is enough to satisfy an objective - such as changing the document color mode. Other times it is necessary to alter actions in order to get use out of them - such as when you try to create a routine for saving a high-resolution "Export" JPG that is different in output and options form the "Save for Web" JPG. You may want to change some of the parameters such as resolution or the actual destination of the file.

      Here is how you can do this.

       

      First, record your action as you would normally: record a new set with a new action, call them what you need and then in the Actions flyout menu, save out a .aia file where you can find it. You can open the file in a text editor and read the contents - they are lines of special Actions 'code' which contains some cryptic text.

      There are lines which look like a bunch of gibberish characters, they are hex-encoded strings for some parameter item which are strings, and they contain above them a number to signify the amount of characters inside the encoded string. (this is important later because this number also needs to be set properly when you put your own value in there) Other parameter items are simple numbers, but their keys are still obscured.

      The truth is, while the string parameters are hexadecimal-encoded, the keys are both hexadecimal and decimal encoded! So if you wanted to know the special 4-letter keys, you'll have to run those through two decoder routines.

       

      Next, you will need to put this entire string into your script and use some string-replacement or string-building to put your own data in those places of the action string where they matter. For example, putting your own file path into a save action.

       

      And, after that you need to write a procedure for writing this new altered string to the file system and loading it into your Actions panel. Mind you, to leave things "as they were" you would need to remove the .aia file and the action that you have loaded.

       

      Let's try with the save-a-jpeg workaround!

      Here is the .aia string which is recorded from an Export JPEG action.

       

      Screen Shot 2017-02-10 at 3.04.33 PM.png

      /version 3

      /name [ 8

        5475746f7269616c

      ]

      /isOpen 1

      /actionCount 1

      /action-1 {

        /name [ 11

        4578706f7274204a504547

        ]

        /keyIndex 0

        /colorIndex 0

        /isOpen 1

        /eventCount 1

        /event-1 {

        /useRulersIn1stQuadrant 0

        /internalName (adobe_exportDocument)

        /localizedName [ 9

        4578706f7274204173

        ]

        /isOpen 1

        /isOn 1

        /hasDialog 1

        /showDialog 0

        /parameterCount 7

        /parameter-1 {

        /key 1885434477

        /showInPalette 0

        /type (raw)

        /value < 100

        0a00000001000000030000000200000000002c01020000000000000001000000

        69006d006100670065006d006100700000006f00630000000000000000000000

        0000000000000000000000000000000000000000000000000000000000000000

        00000100

        >

        /size 100

        }

        /parameter-2 {

        /key 1851878757

        /showInPalette 4294967295

        /type (ustring)

        /value [ 25

        2f55736572732f566173696c7948616c6c2f4465736b746f70

        ]

        }

        /parameter-3 {

        /key 1718775156

        /showInPalette 4294967295

        /type (ustring)

        /value [ 16

        4a5045472066696c6520666f726d6174

        ]

        }

        /parameter-4 {

        /key 1702392942

        /showInPalette 4294967295

        /type (ustring)

        /value [ 12

        6a70672c6a70652c6a706567

        ]

        }

        /parameter-5 {

        /key 1936548194

        /showInPalette 4294967295

        /type (boolean)

        /value 1

        }

        /parameter-6 {

        /key 1935764588

        /showInPalette 4294967295

        /type (boolean)

        /value 1

        }

        /parameter-7 {

        /key 1936875886

        /showInPalette 4294967295

        /type (ustring)

        /value [ 1

        32

        ]

        }

        }

      }

       

      We can see many parameters and their various cryptic blocks, but what you want to do is decode as many /type (ustring) elements as possible to get a sense of what the action is doing. At this website, you can do this fairly easily although tediously: Convert Hexadecimal To String Online

      For example: "4a5045472066696c6520666f726d6174" turns into "JPEG file format".

      In this action example, I am not worried about changing the other parameters dynamically - I'm assuming the settings used in my action are suitable for my purposes such as resolution being 300 for all time. The part I'd like to change is my file path so that my JPEG goes to the right place.

      /value [ 25
      2f55736572732f566173696c7948616c6c2f4465736b746f70
      ]

      This line yields: "/Users/VasilyHall/Desktop"

      So this is the line I'll need to replace.

       

      Before anything else - here is how I'd embed the action string with ease. Using a text editor like Sublime text which lets you put many cursors down at one time, I can paste the action string in and find every nextline character. Then it's easy to highlight each line and put quotes around it as well as a comma in the end, or plusses - depending if you want to store the string as an array or a plain string in the script.

      Screen Shot 2017-02-10 at 3.16.48 PM.png

      I find the plusses a little cluttering so I opt to use this format:
      var actionString = [     "string",

           "string"

      ].join("\n");

       

      So my dynamic portion of the string which will be used with string replacement would look like this:

       

      "  /value [ {{number_of_characters}}",

      "  {{hex_encoded_path}}",

      "  ]",

       

      When the action is ready to be dispatched, a string replacement would look like this:

      var myNewPath = Folder.myDocuments.toString() + "/Destination";

      var myNewPathEncoded = hexEncode(myNewPath); // find a hex encode function via google
      var thisActionString = actionString.replace("{{hex_encoded_path}}", myNewPathEncoded).replace("{{number_of_characters}}", myNewPath.length);

       

      Now it's time to write the file.

      var f = File(actionFileLocation);

      f.open('w');

      f.write(thisActionString);

      f.close();

       

      And now it's time to load the action.

      app.loadAction(actionFileLocation);

       

      Now we can play the action.

      app.doScript("Export JPEG", "Tutorial");

       

      This should save your jpeg, and as of this writing, this is the only way to get the JPEG export which isn't the Save-for-Web variety.

      But, let us not forget the cleanup.

       

      Remove the .aia file:

      f.remove();

       

      Remove the single-use dynamic action:

      app.unloadAction("Tutorial", "");

       

      There you have it: building dynamic actions to do simple and non-simple things, which regular scripting just can't do.

        • 1. Re: Creating a dynamic action to use with app.doScript() method.
          williamadowling Level 4

          Despite google translate repeatedly telling me otherwise, I'm becoming more and more convinced that 'Vasily' is Russian for 'Wizard'.

           

          This is extremely helpful. Thanks a ton for taking the time to put this together. It's going to help a ton of people, I'm sure.

          • 2. Re: Creating a dynamic action to use with app.doScript() method.
            Silly-V Adobe Community Professional

            No problem, I hope it helps others too and it helps me when I can point to this thread.

            2 people found this helpful
            • 3. Re: Creating a dynamic action to use with app.doScript() method.
              Silly-V Adobe Community Professional

              Oh yes, and my apologies for incorrectly implying that the export JPEG settings are actually recorded into an action. This is not the case; the export settings are actually kept in the Prefs file and can be manipulated via this syntax: app.preferences.setRealPreference("plugin/JPEGFormat/DPI", 450);

               

              Okay, correction - the resolution settings get baked into the action when it's recorded! And what's more, they change your preferences when they are ran. You can have set the preferences with script to have resolution of Jpegs to 200 and if you play an action which exported them at 300, the prefs get updated to the 300.

              I am suspecting this kind of info is hidden inside this block in the action text:

              /key 1885434477// parm
              /showInPalette 0
              /type (raw)
              /value < 96
              e0d1955c010000000000000000000000e0810699010000000300000000005e01
              02000000000000000200000000000000c0cf9c17010000000200000000000000
              01000000000000000000000000000000b0f17738010000000007975c01000000
              >

               

               

              Anyone know how to decode or encode it?

              • 4. Re: Creating a dynamic action to use with app.doScript() method.
                Silly-V Adobe Community Professional

                I recorded and analyzed a Tiff of a different resolution and the difference was this bit: "5e01"

                • 5. Re: Creating a dynamic action to use with app.doScript() method.
                  moluapple Level 4

                  Hi, check this:

                  5e01: 0x5e = 94, 0x01 = 1, 94 +1 + 255 * 1 = 350

                  More tests:

                  2c01: 44 + 1 + 255 * 1 = 300

                  c800: 200 + 0 + 255 * 0 = 200

                  5802: 88 + 2 + 255 * 2 = 600

                  2 people found this helpful
                  • 6. Re: Creating a dynamic action to use with app.doScript() method.
                    Silly-V Adobe Community Professional

                    Wow most excellent work! I would have never figured this out. This is just like one of those pictures where you have to find a pattern in the numbers - but in real life! I am going to make an image like that.

                    So, it follows that the following function should work to change this bit of string. Maybe there's a way to get the information on all of the parameters in this raw data string body, but it would take tedious work to map all of these out, right?

                     

                    #target illustrator

                    function test(){

                     

                      var helper = {

                        hexToString : function (hex) {

                            var str = '';

                            for (var i = 0; i < hex.length; i += 2) str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));

                            return str;

                        },

                        decToHex : function (dec){

                          return parseInt(dec).toString(16);

                        },

                        decToHexToString : function (dec){

                          return this.hexToString(this.decToHex(dec));

                        }

                      };

                      function getResolutionString(resNum){

                      var remainder = resNum % 255;

                      if(remainder == 0){

                      remainder = 255;

                      }

                      var multiplier = (resNum - remainder) / 255;

                      return helper.decToHex(Math.abs(remainder - multiplier)).replace("0x", "") + "0" + multiplier.toString();

                      };

                      var str = getResolutionString(300);

                      alert(str);//###

                     

                    };

                    test();

                    • 7. Re: Creating a dynamic action to use with app.doScript() method.
                      moluapple Level 4

                      Well, not so much works as thought, it turns out the last line is always the same, and other values are much more simple than dpi: 01,02,03 or 00,01.

                       

                      d0037405000000000000000000000000c0660f1c00000000{antialias::start:49, length:2, value:[01,02,03]}0000000000{dpi::start:61, length:4, value:encoded hex }
                      {color_type::start:65, length:2, value:[01,02,03]}000000{lzw::start:73, length:2, value:[00,01]}00000002000000000000008028560d00000000{icc::start:113, length:2, value:[00,01]}00000000000000
                      01000000000000000000000000000000a0b5791d0000000030b7791d00000000
                      

                       

                      I noticed that the data are different from your sample, may be it's relative to version and locale, I just don't care about it.

                      1 person found this helpful
                      • 8. Re: Creating a dynamic action to use with app.doScript() method.
                        Silly-V Adobe Community Professional

                        Two corrections on this:

                         

                        1. Using an Action is not the only way to save a high-res JPEG, you can also use the imageCapture function with the right preferences or file-name endings.
                        2. Extendscript has a special sweet feature (kind of in the same league as being able to write <xmlTags></xmlTags> inside an extendscript), the ability to use triple quotes (''') or (""") to set apart entire blocks of text as a string, regardless of nextlines and nested quotes.

                         

                         

                        I couldn't help myself, here's the XML example:

                         

                        #target illustrator

                        function test(){

                        var stuff = <root>

                                <innerTag>HELLO!</innerTag>

                            </root>;

                            alert(stuff.innerTag); // HELLO!

                        };

                        test();

                         

                        And here's the triple-quote example:

                         

                        #target illustrator

                        function test(){

                            var stuff = """

                                The cow jumped over-

                                the moon. "Isn't that wonderful",

                                -- said the artifical intelligence, right before it took everyone'"s''"'"'"' job's's's's's""''

                            """;

                            alert(stuff);

                        };

                        test();

                        Screen Shot 2018-02-19 at 12.17.04 PM.png

                        • 9. Re: Creating a dynamic action to use with app.doScript() method.
                          Silly-V Adobe Community Professional

                          Awesome bug alert! A 'magic undo' bug is discovered.

                           

                          But first, a very important notice first: do not use a script that writes in and plays actions, from inside an Action in the first place. This causes Illustrator to go unresponsive.

                           

                          Here is my exciting story: using Mac, AI 22.1, I set out to practice this very technique to save a copy in PDF format on a document which my script would edit first, doing such things as removing layers, duplicating and outlining text, etc.

                           

                          Using ESTK to launch the script which included using the action with the load-play-remove methodology, the script worked as expected.

                          However, when using File > Other Scripts, the script worked just fine except all the items it edited in the document instantly reverted at the end of the script! It just jumped back to the state of being, before my script's editing came into play. Basically, all the layers it was meant to delete permanently, were deleted in a flash and just as quickly re-appeared, with all art back in place. I even checked my action to see if I did not accidentally add an "undo" there. It appeared to do the same thing as if I was pushing Cmd Z at the end of the script run!

                           

                          The solution for me was to put in an app.redraw() command before my action played: now the document state became fixed and did not instantly and magically revert.

                           

                          While I pondered on the usefulness of this 'feature', in the end my thoughts are to avoid it if possible and to treat it as an anomaly and not the standard behavior to be relied upon.

                          • 10. Re: Creating a dynamic action to use with app.doScript() method.
                            q3player Level 3

                            Just a thought that comes to my mind after enjoying this must-read article above:

                             

                            You can't call a script through an action, because it will only work once and then be forgotten by AI.

                            You can't assign a hotkey to a script.

                             

                            But:

                             

                            You can do a script that creates a dynamic action (as mentioned above).

                            You can assign a hotkey to that dynamic action.

                            You can use a script as startup item to run when AI is started.

                             

                            So my idea would be to create a dynamic action with a hotkey assigned and use it as a startup script. The result would be an action that could call the script more than once and by use of a hotkey.

                             

                            I would appreciate any suggestions about that from the pro's around here.

                            • 11. Re: Creating a dynamic action to use with app.doScript() method.
                              williamadowling Level 4

                              I think that's a great idea and a good use of this functionality. I do believe that the newer versions of AI have solved the issue of scripts in the actions panel not being persistent, however, your solution makes it much more portable because the action wouldn't have to exist on anybody's local machine. you'd just need access to the script itself so you can copy it into the applications folder.

                               

                              For additional administrative control (assuming you were sharing this with coworkers, subordinates or clients, instead of just using it for yourself) you could create a generic "action_creator.jsx" that accessed an outside text file that contained the action text. That way you could make updates to the action without any need for people to re-install the script in their Startup Scripts folder.

                               

                              Kudos

                              1 person found this helpful
                              • 12. Re: Creating a dynamic action to use with app.doScript() method.
                                pixxxel schubser MVP & Adobe Community Professional

                                q3player wrote:

                                "You can't call a script through an action, because it will only work once and then be forgotten by AI."

                                 

                                In CC 2018: Yes we can

                                 

                                1 person found this helpful
                                • 13. Re: Creating a dynamic action to use with app.doScript() method.
                                  Silly-V Adobe Community Professional

                                  To hotkey scripts it's going to reach into the realm of OS automation, and with AutoHotkey or Snap.app on Mac you can hotkey all kinds of stuff.

                                   

                                  The issue as william points out, has been fixed for persisting the action to hotkeys in the latest versions.

                                   

                                  As for maintaining a central Actions repository - you can simply have your .aia file that users can load into their Actions panel.

                                  This can be automatically done via startup script.

                                  The actions can also be updated at any time by re-loading that .aia file.

                                  2 people found this helpful
                                  • 14. Re: Creating a dynamic action to use with app.doScript() method.
                                    williamadowling Level 4

                                    I beleive what q3player is saying is that you can assign a hotkey to the action, and the action calls the script. so this would actually give you easy access to script hotkeys, as long as your hotkey is f2, f3, ... with an optional cmd/ctrl and/or option/alt modifier.

                                     

                                    Not a particularly robust way of using hotkeys, but it's a step in the right direction i think.

                                    1 person found this helpful
                                    • 15. Re: Creating a dynamic action to use with app.doScript() method.
                                      q3player Level 3

                                      *sigh*

                                       

                                      still forced to work with cs6

                                       

                                      ... and if you hit the like button on this post it's like salt in an open wound ;-)

                                      • 16. Re: Creating a dynamic action to use with app.doScript() method.
                                        Silly-V Adobe Community Professional

                                        So, in this case you can do an action-creator script, or something like I've done in the past - which is a script that you have to go to File > Scripts to play, but it would re-load a pre-determined and pre-made Action file that would have been made with the script menu items and shortcut keys.

                                         

                                        However all this is just too much junk, when you might as well do Snap.app or Keyboard Maestro or Automator Services on a Mac or AutoHotKey on Winows. With those there's a little figuring out, but I'd venture to say, not as much as what we're talking about even. Plus, with those once you can hotstring some application or action for Illustrator, it's done the exact same way you'd do that for any other app - which makes me want to just point to those solutions as it's something that you can use for all time in all things!

                                        • 17. Re: Creating a dynamic action to use with app.doScript() method.
                                          Silly-V Adobe Community Professional

                                          Note: I discovered something new today - I was practicing putting a regular script into the script folder in the Ai app folder so I could run it from the Actions panel via shortcut key to try our methods here.

                                          I found out that in CC2018 at least, while a script can be invoked via the File > Scripts menu and run without the application having to have a document open, doing so from the Actions panel will result in the "Object not available" error.

                                          So we can see they made it so for some reason.

                                           

                                          But it can be worked around by putting a file-open command into an action and then running the script and then closing the opened file with the script as the first or last step - depending on what is being done.

                                          2 people found this helpful
                                          • 18. Re: Creating a dynamic action to use with app.doScript() method.
                                            williamadowling Level 4

                                            so even if line 1 of the script you were calling from your action was "app.documents.add();" you'd get an error with running the action?

                                            • 19. Re: Creating a dynamic action to use with app.doScript() method.
                                              Silly-V Adobe Community Professional

                                              That's right - just tried that. It's no script at all - and this is on my work's Mac High Sierra, CC2018 Ai.