Your prescription for increased productivity and profitability
With this blog post we are wrapping up our journey into the automation of a multi-state-object slideshow with two stylistic options.
The approach for the coding took a little different turn in that the InDesign document was created based on the size and resolution of the images. This provided the opportunity to introduce a handy AppleScript application, Image Events, which provides a quick and effective way to get information about an image file.
This is done in a handler named getImageFilesWithInfo which has the user choose the folder for the files to be used in the slideshow. The handler returns the path to the folder chosen by the user (string), a list of the file names, along with the size and resolution of a representative file.
For review, the script starts by defining values for variables to be used within the script. These are needed to create a custom dialog and handle the user’s response:
set colorList to getColorList() --returns a list of default colors which will be available for the document set buttonColor to {"Blue", "Gray", "Green", "Red", "Black"} set btnNumbers to {143, 145, 147, 149, 151} set dlgName to "Slideshow Style"
This is followed by a call to the getImageFilesWithInfo handler.
--Get path to files, file list, dimension and resolution for sample file set thePrompt to "Choose folder of files for slideshow" set theResult to getImageFilesWithInfo(thePrompt) copy theResult to {folderPath, fileList, theDim, theRes}
Because the resolution of the images can be different than the screen resolution, the script, the script calculates the width and height for the document to be created based on resolution.
--Get path to files, file list, dimension and resolution for sample file set thePrompt to "Choose folder of files for slideshow" set theResult to getImageFilesWithInfo(thePrompt) copy theResult to {folderPath, fileList, theDim, theRes}
The script then calls a handler to have the user choose which of two choices to use for the slideshow presentation: (1) placing the buttons to the sides and over the image, or (2) in a margin area below the image.
--get style information from user - define values needed for the dialog and call handlers set dlgResult to doDialog(dlgName, true, colorList, buttonColor)
Depending on the result from the dialog, the script then creates the document making adjustments for the bottom margin area should the second option be chosen. The geometric bounds for the multi-state object is also defined at this point.
if item 1 of dlgResult is "Style2" then set bottomMargin to 60 else set bottomMargin to 0 end if set pgHgt to (pgHgt + bottomMargin) set marginList to {0, 0, bottomMargin, 0} set gBounds to {0, 0, pgHgt - bottomMargin, pgWid} --create the document set docRef to createDocument(pgWid, pgHgt, marginList)
The multi-state object is then created with a call to the createMultistate handler (createMSO).
--Create the Multi-State Object set MSObject to createMSO(docRef, gBounds, folderPath, fileList)
So far, so good. It gets a little more involved when it comes to adding the buttons. Depending on the slideshow style selected by the user, the position for the buttons is calculated, and the buttons placed or created. The calculation is done in a handler named getPlaceInfo.
(*Calculates path and boundsd for buttons based on value of variable pos*) on getPlaceInfo(pos, pgWid, pgHgt, btnWid, btnHgt, margin) if pos is "sides" then set x0 to margin set b2x0 to pgWid - (margin + btnWid) set cy to round (pgHgt / 2) set y0 to cy - (btnHgt / 2) set y1 to y0 + btnHgt set x1 to x0 + btnWid set gBounds1 to {y0, x0, y1, x1} set gBounds2 to {y0, b2x0, y1, b2x0 + btnWid} set path1 to {{x1, y0}, {x1 - btnWid, cy}, {x1, y1}} set path2 to {{b2x0, y0}, {b2x0 + btnWid, cy}, {b2x0, y1}} return {gBounds1, path1, gBounds2, path2} else if pos = "bottom" then set cx to round (pgWid / 2) set cy to (pgHgt - (margin + (btnHgt))) set placePt1 to {(cx - (margin + (btnWid / 2))), cy} set placePt2 to {(cx + margin), cy} return {placePt1, placePt2} end if end getPlaceInfo
If the user selected the first style (Style1), the getPlaceInfo handler will return geometric bounds and path points for each button {gBounds1, path1, gBounds2, path2}.
These values are passed to the handler that actually creates the button (strokeOnlyButton handler). See below.
The following is added to the code above to get the information needed for placing the buttons and calling the handlers that will create or place the buttons:
if item 1 of dlgResult is "Style1" then set clrIndex to item 2 of dlgResult set btnColor to item clrIndex of colorList --params: btn width, btn height, margin from sides --returns return {gBounds1, path1, gBounds2, path2} set placeInfo to getPlaceInfo("sides", pgWid, pgHgt, 15, 40, 30) copy placeInfo to {gBounds1, path1, gBounds2, path2} set btn1 to strokeOnlyButton("LeftButton", gBounds1, path1, btnColor, false) set btn2 to strokeOnlyButton("RightButton", gBounds2, path2,btnColor, false) else if item 1 of dlgResult is "Style2" then set btnNumber to item 2 of dlgResult set btnNum1 to item btnNumber of btnNumbers set btnNum2 to btnNum1 + 1 --returns {placePt1, placePt2} {{366.5, 618}, {424, 618}} --params: btn Width, btn Height, margin between set placeInfo to getPlaceInfo("bottom", pgWid, pgHgt, 19, 18, 24) --make sure library is open set libRef to getButtonLibrary() --place buttons as library assets; page number, library reference, button name, place point, number of asset set btn1 to placeAsset(1, libRef, "LeftButton", item 1 of placeInfo, btnNum1) set btn2 to placeAsset(1, libRef, "RightButton", item 2 of placeInfo, btnNum2) end if
(*The strokeOnlyButton handler*) on strokeOnlyButton(btnName, btnBounds, pathPoints, btnColor, closePath) tell application "Adobe InDesign CC 2018" if closePath is true then set myPathType to closed path else set myPathType to open path end if set docRef to document 1 tell docRef if not (exists layer "Button Layer") then set layerRef to make layer with properties {name:"Button Layer"} else set layerRef to layer "Button Layer" end if set btnProps to {stroke weight:3} tell page 1 set theBtn to make button with properties {name:btnName, item layer:layerRef, geometric bounds:btnBounds, fill color:"None"} tell state 1 of theBtn set myArrow to make polygon with properties btnProps set fill color of myArrow to "None" set entire path of path 1 of myArrow to pathPoints set stroke color of myArrow to btnColor set path type of path 1 of myArrow to myPathType end tell end tell end tell end tell return theBtn end strokeOnlyButton
Using the Button from InDesign’s Button Library is not as straightforward as you might imagine. First the script needs to ascertain if the library is open, and open it if not. After working with several options, the following handler seemed to be the most reliable:
--make sure button library is open on getButtonLibrary() tell application "Adobe InDesign CC 2018" try set libPanel to library panel "Sample Buttons And Forms" set libRef to associated library of libPanel on error set appPath to file path as string set libPath to appPath & "Presets:Button Library:ButtonLibrary.indl" set libAlias to libPath as alias set libRef to open libAlias end try return libRef end tell end getButtonLibrary
Placing assets from a library is something that might be needed in a number of projects, so the handler was written with that in mind:
(*Places asset to page defined by its index, using a reference to the library, the asset name, the place point (x,y), and the button number*) on placeAsset(pageNumber, libraryRef, assetName, placePt, assetNumber) tell application "Adobe InDesign CC 2018" set pageRef to page pageNumber of document 1 set assetRef to asset ("" & assetNumber) of libraryRef set placedObj to place asset assetRef on document 1 move placedObj to pageRef move placedObj to placePt set theAsset to item 1 of placedObj set name of theAsset to assetName end tell return theAsset end placeAsset
Notice that with assets, the asset is placed on the document then moved to the page, and finally to the place point on the page.
All that is left for the script to do is to wire the buttons up to the multi-state-object. This involves adding behaviors to the buttons. We could put the code in its own handler, but it ca be placed at the bottom of the top portion of our script.
--add Button behaviors tell application "Adobe InDesign CC 2018" tell btn1 make goto previous state behavior with properties {associated multi state object:MSObject, behavior event:mouse down, enable behavior:true, loops to next or previous:true} end tell tell btn2 make goto next state behavior with properties {associated multi state object:MSObject, behavior event:mouse down, enable behavior:true, loops to next or previous:true} end tell end tell
The handlers called from the code above are listed below:
For handlers not listed above, you will find these handlers in our posts for January 16, 24, and 30. You can download the entire script from the Feature page of our website.
Notice, with one exception, all of the code directed to the application was placed inside handlers. This not only makes code reusable, but makes it possible to test each portion as you go. With each piece tested, the script should be fairly bullet proof, but we do need to add a try/on error trap in the event that the user decides to cancel out of the choose folder or the custom dialog. We will leave that up to the reader.
For the acid test, run the script, choose one of the styles from the dialog and see how fast the document and its content is created. Next open the EPUB Interactivity Preview window and click the run button. The slideshow should load almost immediately. Click the buttons and verify the slideshow works as anticipated.
…Previewing the slideshow
You can share your slideshow with the world using InDesign’s Publish to Web. This gives you several options for sharing your slideshow. One is to copy the url providerd when you Publish to Web and share it with your audience.
For example, you can view the sample sllidesow created with the script by clicking on the following link Click Here.
Scripts provided are for demonstration and educational purposes. No representation is made as to their completeness. Users are advised to use the code at their own risk.