Automating Adobe InDesign CC 2015


InDesign's do script: deeper dive

Wednesday, May 25, 2016

DoScript With Arguments

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.

A Simple AppleScript

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,

Pass in Arguments

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)';

The Script String

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.

Passing In File References

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

Practical Application

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.

Trackback Link
http://www.yourscriptdoctor.com/BlogRetrieve.aspx?BlogID=18153&PostID=1462918&A=Trackback
Trackbacks
Post has no trackbacks.

Recent Posts


Tags

styles Export HTML Choose file do script Fixed Layout ePub CALS tables Sound Preferences Find Text automated workflow AppleScript Templates Placing images transform with timeout Image Placement tutorial doScript Script Preferences Embed Document InDesign template Combobox Media active spread import XML object styles reframe XML structure Excel directory file paths Caption Variable placeable PDF AppleScript Custom CSS Style Sheets transformation matrix Fixed Format ePub save template Text Cleanup Templates repeat loops entire path arrays images integer editboxes import HTML Custom Buttons PDF export preferences ePub for Kindle Autoflow pasteboard XML selection Editbox Create Text Frame coordinates Metadata wild cards BrdgeTalk Publish Online table styles GPS Adobe InDesign CC Glossary System Events scripting Read text file as list Border Panel page items path functions user interaction level cut and paste parent page Choose from list measurement editboxes text editbox Radiobutton Group Find by Color Enabling Groups Index of blogs and scripts for year 2016 Nested Styles say AppleScript dialogs import text parent Adding Captions watermark Apple apps resize Export to PDF PDF security Document Presets Library Folder Automating animations Numbering InDesign dialogs Bullets ePub for iPad InDesign CC resolve lists HTML Export Character Styles dropdowns handlers automation image metadata measurements Mail Find Change Hyperlinks try/on error stylesheets text concatenation GPS metadata CSS Checkbox Control Change Text Interactive Publications Table cells paragraph styles text item delimiters Create Document External Object Scripts enable redraw Find by Attribute metacharacters new document transform again massage text records trimText folder Video Custom Dialog fit Find by Font script templates text placement Repeat With Tables automating InDesign if/then Document from Template find change options script folder XML tags Document stylesheets Styling Text

Archive