Your prescription for increased productivity and profitability
In our blog of May 7 (this year) we touched on the subject of using doScript in AppleScript to call an InDesign method in ExtendScript (JavaScript). This was needed to get around a problem with InDesign’s align/distribute method with AppleScript. The focus of this blog was to show how a script residing in an ExtendScript file could be called from AppleScript. The script used do script arguments to pass a number into the script. This number designadesignated which of a list of AlignOptions to use for InDesign’s align method.
The question that remains in the minds of many who are new to writing scripts is exactly how does do script work with arguments. This blog backtracks somewhat to explain the subject further.
Define the script as text (inside quotes):
set myScript to "alert(\"Hello World\")" --tell do script to run the script tell application "Adobe InDesign CC 2015" do script myScript language javascript end tell
Similarly, we can do the same with ExtendScript:
var myScript = 'activate\r'; myScript += 'display alert \"Hello World\"'; app.doScript (myScript, ScriptLanguage.APPLESCRIPT_LANGUAGE);
Of course there is no advantage to calling an alert message from another language, but using an alert dialog keeps the code simple for demonstration,
To pass in values to a do script, we use an argument list. You might think of an argument list as a global variable named arguments. Then consider a do script as a handler (function) that can only see a global variable by the same name. When do script is called, the values of the local variable that defines the script arguments is passed to the variable named arguments. Because arguments has global scope, it can be “seen” by the language specified for the do script. In the following examples the local variable “myArgs” is passed to arguments and used by do script:
AppleScript
set myArgs to {"John"} --myArgs is local set myScript to "var arg1 = arguments[0];" & return set myScript to myScript & "alert (\"Hello \" + arg1);" tell application "Adobe InDesign CC 2015" do script myScript with arguments myArgs language javascript --myArgs passed to arguments list end tell
ExtendScript
var myArgs = ["John"]; var myScript = 'set arg1 to item 1 of arguments\r' myScript += 'activate\r' myScript += 'display alert ("Hello \" & arg1)' app.doScript (myScript, ScriptLanguage.APPLESCRIPT_LANGUAGE, myArgs);
In the above you can combine the variable assignment statement with the display alert statement, and substitute this in the above scripts as in the following:
AppleScript
set myArgs to {"John"} set myScript to "alert (\"Hello \" + arguments[0]);"
ExtendScript
var myArgs = ["john"]; var myScript = 'activate\r'; myScript += 'display alert ("Hello \" & item 1 of arguments)';
With all of the string manipulation, quotes, and backslashes required, putting a script together as a string to be used by do script can be somewhat of a challenge. The biggest problem is in wrapping one’s head around to think in the syntax needed for the other language. For instance, in AppleScript you write: set variableName to variableValue
where in ExtendScript you write: var variableName = variableValue;
And when you are working with a list in AppleScript, it’s an array in ExtendScript. A list in AppleScript uses curly braces set myArgs to {"John", 24}
while ExtendScript uses square brackets. var myArgs = ["john", 24];
In ExtendScript you may be tempted to try to tell an AppleScript to set a value to the index of the array, writing this: set argumentItemValue to arguments[0]; --does not work
instead of what is needed for AppleScript: set argumentItemValue to item 1 of arguments
And, of course, there are the quotes that need to be escaped, as well as line returns and semicolons that need to be added.
Get into the habit of testing the script string before attempting to call it with a do string statement.
The result of the script string value should read just as it would inside of its appropriate editor.
For example: Suppose you want the Macintosh computer to say a message using a given voice. In AppleScript you can write:
set theMessage to "Have a good day" say theMessage using "Junior"
To write this in ExtendScript, you would write:
//write the argument line as normal, you are talking to ExtendScript var myArgs = ["Have a good day"]; //write do script script inside quotes using AppleScript syntax var myScript = "set theMessage to item 1 of myArgs\r" //escape quotation marks using backslash myScript += "say theMessage using \"Junior\" "; myScript // have value of myScript written to the console
Now run the script (in ExtendScript) to test for the value of the variable myScript. The result for this will be:
Result: set theMessage to item 1 of myArgs say theMessage using "Junior"
Read through the result to make sure you don’t have any errors.
Better yet, test the code with AppleScript editor. In a new script for AppleScript Editor, write a statement to set the value of theMessage
set myArgs to {"Have a good day"}
Copy the result returned from running the ExtendScript
set theMessage to item 1 of myArgs say theMessage using "Junior"
Compile the script and run it, if it compiles. If not, fix the error(s) and test until you get it right. Be sure to update your ExtendScript accordingly when it works.
Now all you need to do in ExtendScript is change the reference to the local variable myArgs inside the script to arguments and add the doScript statement. Your finished script should read:
//write this as normal, you are talking to ExtendScript var myArgs = ["Have a good day"]; //write the do script inside quotes using AppleScript syntax var myScript = "set theMessage to item 1 of arguments\r" myScript += "say theMessage using \"Junior\" "; app.doScript (myScript, ScriptLanguage.APPLESCRIPT_LANGUAGE, myArgs)
Be sure that the variable for the argument array in the app.doScript statement matches the variable at the top of the script where the values for the arguments list is declared. Of course you will need to have sound enabled on the computer for the script to work.
A problem can arise when you start working with files and folders. If you can define the path to the folder or file as text inside the script, so much the better. For example: I want my ExtendScript to read as follows where the file is “test.txt” inside a folder named “Test” : (To test the following scripts make sure you have a file named “test.txt” saved on the desktop in a folder named “Test.”)
var deskPath = Folder.desktop; var filePath = deskPath + "/Test/test.txt"; alert ("filePath is " + filePath); filePath
In AppleScript I write:
set myScript to "var deskPath = Folder.desktop;" & return set myScript to myScript & "var filePath = deskPath + \"/Test/test.txt\";" & return set myScript to myScript & "alert(\"filePath is \" + filePath);" tell application "Adobe InDesign CC 2015" do script myScript language javascript end tell
Should I want to define the filePath outside of the script and pass it to the script using arguments, the script gets a little more difficult:
set myArgs to {"Folder.desktop + \"Test/test.txt\""} set myScript to "var filePath = " & " arguments[0];" & return set myScript to myScript & "alert('filePath is '" & "+ filePath);" & return set myScript to myScript & "alert('file name is ' + Folder.decode(File(filePath).name))" tell application "Adobe InDesign CC 2015" do script myScript with arguments myArgs language javascript end tell
Notice that here each script statement is enclosed in double quotes, and quotes inside of a statement are escaped with a backslash (\).
To write a similar script in ExtendScript for AppleScript is not quite as easy. Folder.desktop in ExtendScript needs to translate to “(path to desktop from user domain) as string” in AppleScript.
if you know the name of the user you could pass in the string equivalent for the path in the argument list:
set filePath to "Macintosh HD:Users:[username]:Desktop:" --where [userName] is the user's name
In spite of the above, you might be tempted to try the following in ExtendScript:
var argArr = [Folder.desktop + "Test:test.txt"]; var myScript = 'set filePath to ' + ' item 1 of arguments\r'; myScript += 'display alert (\"filePath is \" & filePath)\r'; myScript += 'tell application "Finder"\r'; myScript += 'set aName to name of file filePath\r'; myScript += 'end tell\r'; myScript += 'display alert (\"file name is \" & aName)'; app.doScript (myScript, ScriptLanguage.APPLESCRIPT_LANGUAGE, argArr);
Sadly this won’t work, as running the script produces an Error message that reads: “Finder got an error: Can’t get file “~Desktop:Test:test.txt”.
There are several ways you can work around this situation. If you know the folder will be on the user’s desktop, you can establish the path to the desktop as part of the do script. This way you only need to pass in the string value of the folder and file name
var myArgs = ["Test:test.txt"]; var myScript = 'set filePath to ((path to desktop from user domain) as string) & item 1 of arguments\r'; myScript += 'tell application \"Finder\"\r'; myScript += 'set aName to name of file filePath\r'; myScript += 'end tell\r'; myScript += 'display alert(\"Name is \" & aName)\r'; app.doScript(myScript, ScriptLanguage.APPLESCRIPT_LANGUAGE, myArgs);
Optionally, you could build a text path to the file using AppleScript’s System Events application. In the following we will set the label index for the file designated to a number. This sets a color Tag for the file that can be used for a number of reasons such as if the file has been processed.
var myArgs = ["Desktop:Test:test.txt", 5]; var myScript = 'tell application \"System Events\"\r'; myScript += 'set userName to name of current user\r'; myScript += 'end tell\r'; myScript += 'set fPath to \"Macintosh HD:Users:\" & userName & \":\" & (item 1 of arguments)\r'; myScript += 'tell application \"Finder\"\r'; myScript += 'set label index of file fPath to item 2 of arguments\r'; myScript += 'end tell\r'; app.doScript(myScript, ScriptLanguage.APPLESCRIPT_LANGUAGE, myArgs); myScript
Naturally, you will want to add code to the above to make sure the file exists before attempting to access it.
For the last option, you could just have the user choose the file to be processed.This is a variant of the preceding script that sets the label index for the file.
var myArgs = [6]; var myScript = 'set fileRef to choose file\r'; myScript += 'tell application \"Finder\"\r'; myScript += 'set label index of fileRef to item 1 of arguments\r'; myScript += 'end tell\r'; app.doScript(myScript, ScriptLanguage.APPLESCRIPT_LANGUAGE, myArgs); myScript
Most of the scripts above are simply for demonstration and provide no benefit over what can be done in your preferred language. For those that do provide some practical application, you will need to add error checking, etc. to the script to make sure the code is bullet proof.
This should give you a good idea of how to use arguments to pass information into a do script. Next, we will look at how to get information back from a do script. Until then, think of ways that you could use do script to make your life with InDesign automated.