7 Replies Latest reply on Apr 10, 2017 9:04 AM by moluapple

    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.

            1 person 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