ORGANIZING A SCRIPT

In our previous blog (July 21), we put a demonstration script together using AppleScript. This script allows the user to choose a PDF export preset from a list of presets. With the preset chosen, it exports the PDF to a fixed file path on the user’s desktop. This is OK for a demonstration script, but for a real world application, your script should either:

  • have the user choose the location for the script
  • save the PDF export to the same folder as the document

SAME FOLDER AS DOCUMENT

To save the exported PDF to the same folder as the document, you can use the document’s full name property. To see how this would work, enter the following into a new edit window for AppleScript Editor (File > New)

   tell application "Adobe InDesign CC 2015"
	set docRef to document 1
        set docName to name of docRef --returns a string reference
	set docPath to full name of docRef --returns a file reference
   end tell
   docPath

Notice that the value returned for the variable docPath is a file reference. Having a file reference, we can use the Finder to return the parent folder (container) for the document. But first we must change the file reference to an alias reference. Add the following to your test script above:

   set aliasRef to docPath as alias
   tell application "Finder"
	set parentFolder to get container of aliasRef as string
   end tell

Notice that here we change the value returned from the Finder from a folder reference to a string (text). This is so we can add the name of our PDF export file to its folder path.

To add this bit of code to our previous script would make it even more cumbersome. Instead, lets revisit our original script and break it down into pieces of code that are more easily managed, more robust, and reusable.

SCRIPT FUNCTIONS

The first thing we had our previous script do was to check to make sure there was a document open. If there is more than 1 document, we have it place the top-most document into a variable docRef. This is something a great number of your scripts may need to do. Similarly, we can see that other functionality included in the script could possibly be found in other scripts. Let’s put each of the major functions of our script into a piece of code that, once called, can run all by itself. In AppleScript, this is referred to as a handler; in other languages it is called a function.

You might want to think of a handler (or function) as a subcontractor: Someone who is specialized to perform a specific job as part of a larger project. This contractor could be a plumber or electrician when it comes to building a house. When you want a particular part of a project to be done, you call the subcontractor and send him the specifications for the job. This is similar to how handlers work in AppleScript. From the main section of the script you call a handler to do its job and pass it specifications (parameters) for completing the task.

HANDLERS

A handler begins with the word on or to. We will use the word on. It also ends with a parentheses pair.

   on doSomething ()
      --steps to be done go here
      --return the result of the process, if there is one
   end doSomething

Get Reference to Document

Our script needs to have a way to get a reference to the active document. For this we can create a handler named getDocRef. To get a reference to the document and return it to the main part of the script, you can use the following. Copy and paste it into a new document for the AppleScript Editor.

   (*Returns reference to active document; otherwise generates error.*)
   on getDocRef()
	tell application "Adobe InDesign CC 2015"
	   if (count of documents) is greater than 0 then
		set docRef to document 1
                if saved of docRef is false then
                   error "Please save document before running the script"
                end if
	   else
		error "Script requires an active document"
	   end if
	   return docRef
	end tell
   end getDocRef

To run the handler, the AppleScript script refers to it by name. Place the following at the top of your new script:

   set docRef to getDocRef()

If, no active document is open, the handler will generate an error. Notice the nested if statement that also checks to make sure the document has been saved. To alert our user to a problem, we need to provide some way to catch the error so we can give the user the message and exit gracefully from the script. We can do this with a try/on error statement block. As part of the block, a display alert statement is used to give the user the error message after which it exits using the return statement. Change the top of your new script to read as follows:

   try
      set docRef to getDocRef()
   on error errStr
      activate --makes sure the error message displays above the InDesign document
      display alert errStr
      return 
   end try 

Get User’s Choice for PDF Export Preset

The next thing our original script did was to get a list of PDF export preset names and have the user choose the one to use by name. If the user does not choose an item from the list, the script needs to alert the user that the script cannot continue without a valid PDF preset. The process of choosing a PDF Export preset from a list is something that can find its way into any number of scripts. Again, let’s put this process into another handler, called ChoosePDFPreset. Copy and paste the following below the getDocRef handler. 

 (*Returns user chose from list of PDF Export Presets
   If no preset is chosen, an error is generated*) 
   on choosePDFPreset()
      set myPrompt to "Choose PDF Export Preset from list"
	tell application "Adobe InDesign CC 2015"
	   set presetList to get name of PDF export presets
	   set presetList to choose from list presetList with prompt myPrompt without multiple selections allowed
	   if presetList is false then
		error "Can't continue without PDF export preset choice"
	   else
		set presetName to item 1 of presetList --only 1 choice was allowed
		set myExportPreset to PDF export preset presetName
	   end if
	end tell
	return myExportPreset
   end choosePDFPreset 

Because this handler can also generate an error, we can place its call inside our try/on error statement block. Place this directly below the line that calls getDocRef()

   set myExportPreset to choosePDFPreset()

Path for Saving the PDF Export

To place the exported PDF into the same folder as the document, we need to get the path to its parent folder. Again, this is another process that could be used in any number of other scripts. Let’s write this up as another handler getSavePath. For this, we will need a reference to the document. We can pass this information to our handler by placing the variable docRef inside the parentheses provided as part of the handler call. Similarly, the parentheses that is part of the handler will receive the value. You might want to think of the parentheses for the handler in the call and in the handler as in and out baskets. We put the information needed in the out basket for the call which is then received by the in basket for the handler.

   set savePath to getSavePath (docRef) --passes value for docRef to handler

For the handler, we need to add the name of the document to the path to its folder (the parent of its full name path)

   (*Returns path for PDF export based on document path and name*)   
   on getSavePath(docRef)
      tell application "Adobe InDesign CC 2015"
	set docPath to full name of docRef as alias
	set docName to name of docRef
	   if docName ends with ".indd" then
	      set docName to text 1 thru -6 of docName
	   end if
      end tell
      tell application "Finder"
	   set parentFolder to container of docPath as string
      end tell
      set savePath to parentFolder & docName & ".pdf"
      return savePath
   end getSavePath

Even though this handler should not generate an error, we can place its call inside the try/on error statement block just to be sure. Place the following directly below the call to choosePDFPreset().

   set savePath to getSavePath (docRef) --passes value for docRef to handler

Export to PDF

The only thing left is to actually export the document. We can do this directly below the call to getSavePath(docRef). But before you do this, run the script as it is to make sure that the value for savePath is as expected. To do this, place the variable savePath at the end of the script. If the value that shows in the Result panel for Script Editor is as expected, add the following code to export the document as PDF to this path.

   --export the document as PDF
    tell application "Adobe InDesign CC 2015"
	tell docRef
	   asynchronous export file format PDF type to savePath using myExportPreset without showing options
	end tell
    end tell

Of course, there may be other functionality you may want to add to your script before it performs the PDF export. Remember that PDF export presets do not set security preferences, so you may want to add these. Also, you might want to put a watermark on your document to keep users from copying or otherwise using your document without permission.

SECURITY

PDF export preferences are the domain of the application. Security preferences can only be set if the property for use security is set to true. By default, the security preferences are all set to false. The property change security password is accessible and can only be set, if use security is set to true.

To add security settings to our ExportPDF_Preset script, we can add the following handler. This handler wil be effective only if all security settings, with exception of use security and security password, will be set uniformly–all will be either true or false.

   (Sets all security settings to the value for the boolean myBool.
   Sets security password for the PDF export to value of variable tempPassword*)  
   on doSecurity(tempPassword, myBool)
      tell application "Adobe InDesign CC 2015"
	tell PDF export preferences
	   set use security to true
	   set change security password to tempPassword
	   set disallow changing to myBool
	   set disallow copying to myBool
	   set disallow document assembly to myBool
	   set disallow extraction for accessibility to myBool
	   set disallow form fill in to myBool
	   set disallow hi res printing to myBool
	   set disallow plaintext metadata to myBool
	   set disallow printing to myBool
	end tell
      end tell
   end doSecurity

The call to the doSecurity handler will require a value for the proposed document password and a boolean (true or false) for use in setting the security settings. For a more usable script, these values will need to be provided by the user as part of a custom dialog. For demonstration, we will hard-code this at the top of the script.

   --define the value for the variable docPassword:
   set docPassword to "myPassword"

Then inside the try/on error statement block, add the following after the set savePath statement:

   doSecurity(docPassword, true)

It would also be a good idea to reset the security settings back to their defaults when the script is completed. For this, set the password to an empty string (“”) and all other values to false (assuming that the defaults for these values are all set to false).

WATERMARK

InDesign has its own way for placing a watermark on a document. This is a document preference: watermark preference. The properties for this preference are self explanatory with perhaps the value for the font color. Instead of using a swatch found in the document, a list of RGB values, or one of the system colors is used. The system colors are listed in the AppleScript dictionary for InDesign which include among others red, green, gold, dark blue, and white.

In the minimum, the handler for creating a watermark will require a reference to the document and the values for the watermark visibility, printing, text, font size, color, and opacity. The values for text, font, size, color, and opacity could be hard-wired to the script if the values would never change (which is unlikely). Better, these should be returned as the result of user choices in a custom dialog box. For simplicity sake, however, our values will be hard wired at the top of our demonstration script.

   set wText to "DRAFT"
   set wColor to {255, 0, 0}
   set wOpacity to 40
   set wSize to 120
   set wPrint to true
   set wShow to true 

Then inside the try/on error statement block, after the doSecurity statement, add:

   doWatermark (docRef, wText, wColor, wOpacity, wSize, wPrint, wShow)

At the bottom of the script, add the handler for doWatermark

   (*doWatermark handler requires reference to the document, and values for watermark
   text, color, opacity, size, and boolean values for do print and visibility*)
   on doWatermark (docRef, wText, wColor, wOpacity, wSize, wPrint, wShow)
      tell application "Adobe InDesign CC 2015"
         tell watermark preferences of docRef
            set watermark font color to wColor
	    set watermark draw in back to false
	    set watermark font family to "Minion Pro"
	    set watermark font style to "Bold"
	    set watermark horizontal position to watermark h center
	    set watermark opacity to wOpacity
	    set watermark do print to wPrint
	    set watermark font point size to wSize
	    set watermark rotation to 0
	    set watermark text to wText
	    set watermark vertical position to watermark v center
	    set watermark visibility to wShow
	    set watermark vertical offset to 0
         end tell
      end tell
   end doWatermark

Remember that these values are preferences for the document and will remain as set until set otherwise. To export your document as PDF without the watermark you will need to set the values for watermark text to an empty string (“”) with watermark visibility and watermark do print set to false.

THE FINAL SCRIPT

The top of your script should now look similar to the following:

   set wText to "DRAFT"
   set wColor to {255, 0, 0}
   set wOpacity to 40
   set wSize to 120
   set wPrint to true
   set wShow to true
   set docPassword to "myPassword"
   try
      set docRef to getDocRef()
      set myExportPreset to choosePDFPreset()
      set savePath to getSavePath(docRef)
      doSecurity(docPassword, true)
      doWatermark(docRef, wText, wColor, wOpacity, wSize, wPrint, wShow)
      tell application "Adobe InDesign CC 2015"
         tell docRef
            asynchronous export file format PDF type to savePath using myExportPreset without showing options
         end tell
   end tell
   doSecurity("", false) --resets all security settings to false, password to empty string
   on error errStr
      display alert errStr
      return
   end try

This will be followed by your handlers which can be in no particular order.

TEST YOUR SCRIPT

Put a sample document together in InDesign. Compile and then run your script from within the AppleScript editor. Hopefully, your script will compile and run without a hitch. Select a PDF Export Preset from the list presented and return. All the rest is done by the script. Below is our sample document (5 in by 7 in) as exported with the settings established in our sample script.

 PDF of sample document with watermark

ONWARD AND UPWARD

With the majority of a script’s functionality stored in handlers, the script becomes well organized and readable. More importantly, handlers provide the ability for reuse in any number of other scripts often with no more than a simple copy and paste. Start saving handlers that you think you may want to use in a folder as a library resource.