Buttons for Multi State Objects

For your EPUB Fixed Layout projects you have the option of creating your own buttons or choosing buttons from InDesign’s Button Library. For this discussion we will create a slideshow that uses buttons from the library to navigate through the images.

The Slideshow

Creating an interactive slideshow for a fixed layout EPUB can be accomplished using the traditional point and click method in InDesign. But, from my perspective, the process involves a number of steps that can be done more efficiently using a script. Of course, if you will never create more than one or two slideshows (multi state objects), then the bother of putting a script together may not be time well spent. On the other hand, if you think you will have the need for producing a number of multi state objects, whether for interactive PDF or Fixed Layout EPUB, having a script on hand to do the work for you can be a real timesaver.

Using the script supplied below, your slideshow will require little more effort than just getting the images ready.

The Slideshow Images

You will want to save your images using the same file format. Size the images the same using a resolution of at least 150 ppi. If your document will be going to print as well as to EPUB, you will want your images to be 300 ppi. Additionally, you may want to name the images using a convention indicating the order in which the pictures will be shown. For this, our sample project named the images slideshow1.psd, slideshow2.psd, and slideshow3.psd.

The Document

Prepare the document for EPUB as normal using web intent. A screen capture of the new document dialog for our sample project is shown below. (Notice that we have created a document preset just for our fixed layout EPUB documents. )

For the slideshow, create an empty rectangle the size of the images.

New Document Dialog

The Script

The script starts by checking to make sure that a rectangle in an active document is selected. It then presents the user with a dialog in which to enter the name for the multi state object and choices for the button color and place point position (relative to the rectangle chosen). It then has the user choose the location of the images prepared as per the discussion above (see The Slideshow Images). For the slideshow buttons, the sample project uses buttons from InDesign’s Button Library. 

 Start a new script in AppleScript Editor. Copy and paste the code below into the script. Add your own comments to the top of the script.

MSObject_button Script

global buttonNumbers
global buttonPositions
global buttonColors
set buttonColors to {"blue", "gray", "green", "red", "black"}
set buttonNumbers to {143, 145, 147, 149, 151}
set buttonPositions to {"left and right center", "top center", "bottom center"}
set dlgName to "MS Object Choices"
set cancelIt to true
set dlgLabel to "MSLabel"
--check for rectangle selected and present user with dialog. Get list of filenames in folder chosen.
try
	set selItem to getSelection()
	set {MSOName, colorIndex, positionIndex} to dialogChoices(dlgName, cancelIt, dlgLabel)
	activate
	set imagePath to (choose folder with prompt "Select folder of images") as string
	set fileNames to list folder imagePath without invisibles
on error errMsg
	activate
	display alert errMsg
	return
end try

--call to handler that places the images and creates the multi state object
set MSObject to makeMSO(selItem, imagePath, fileNames, MSOName)
--makes sure button library is open and calls handler to place the buttons
tell application "Adobe InDesign CC 2014"
	set docRef to document 1
	set pageRef to parent page of MSObject
	if not (exists library "ButtonLibrary.indl") then
		set appPath to file path as string
		set libPath to appPath & "Presets:Button Library:ButtonLibrary.indl"
		set libRef to open libPath
	else
		set libRef to library "ButtonLibrary.indl"
	end if
	my placeButtons(docRef, pageRef, MSObject, libRef, colorIndex, positionIndex)
end tell
--=============
--HANDLERS
--=============
--places images from folder chosen and makes multi state object
on makeMSO(selItem, imagePath, fileNames, MSOName)
	set statesToAdd to (length of fileNames) - 2
	tell application "Adobe InDesign CC 2014"
		set imageBounds to geometric bounds of selItem
		tell document 1
			set layerRef to layer 1
			set MSORef to make multi state object with properties {name:MSOName, geometric bounds:imageBounds, item layer:layerRef}
			set curStates to states of MSORef
			repeat with i from 1 to statesToAdd
				tell MSORef to make state
			end repeat
		end tell
		repeat with i from 1 to length of fileNames
			set nameStr to item i of fileNames
			set fileAlias to (imagePath & nameStr) as alias
			tell state i of MSORef
				if i = 1 then
					set rectRef to selItem
				else
					set rectRef to make rectangle with properties {geometric bounds:imageBounds, name:("State" & i), fill color:"None", stroke weight:0}
				end if
			end tell
			tell rectRef to place fileAlias
		end repeat
	end tell
	return MSORef
end makeMSO
--places buttons and associates to multi state object
on placeButtons(docRef, pageRef, MSObject, libRef, colorIndex, positionIndex)
	set assetNum to item colorIndex of buttonNumbers
	tell application "Adobe InDesign CC 2014"
		set assetRef1 to asset ("" & assetNum) of libRef
		set assetRef2 to asset ("" & (assetNum + 1)) of libRef
		copy geometric bounds of MSObject to {y0, x0, y1, x1}
		if positionIndex > 1 then
			set cx to (x1 - x0) / 2 + x0
		else
			set cx0 to x0 - 24
			set cx1 to x1 + 6
		end if
		if positionIndex is 2 then
			set cy0 to y0 - 24
			set cx0 to cx - 24
			set cx1 to cx + 24
		else if positionIndex is 3 then
			set cy0 to y1 + 6
			set cx0 to cx - 24
			set cx1 to cx + 24
		else if positionIndex is 1 then
			set cy0 to y1 - (y1 - y0) / 2
		end if
		set backButton to my makeButton(docRef, pageRef, assetRef1, cx0, cy0)
		set foreButton to my makeButton(docRef, pageRef, assetRef2, cx1, cy0)
		tell backButton
			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 foreButton
			make goto next state behavior with properties {associated multi state object:MSObject, behavior event:mouse up, enable behavior:true, loops to next or previous:true}
		end tell
	end tell
end placeButtons
--called from placeButtons handler to place and move buttons into position
on makeButton(docRef, pageRef, assetRef, cx, cy)
	set placePt to {cx, cy}
	tell application "Adobe InDesign CC 2014"
		set placedObj to place asset assetRef on docRef
		move placedObj to pageRef
		move placedObj to placePt
	end tell
	return item 1 of placedObj
end makeButton
--checks for rectangle selection. Throws error if rectangle is not selected.
on getSelection()
	set errMsg to "Requires rectangle selection"
	tell application "Adobe InDesign CC 2014"
		set selList to selection
		if length of selList = 0 then
			error errMsg
		end if
		set selItem to item 1 of selList
		if class of selItem is not rectangle then
			error errMsg
		end if
	end tell
	return selItem
end getSelection
--custom dialog for name of multistate object, button color and position
on dialogChoices(dlgName, cancelIt, dlgLabel)
	set wasCancelled to false
	--make sure user interaction level will allow a dialog
	tell application "Adobe InDesign CC 2014"
		set origLevel to user interaction level of script preferences
		set user interaction level of script preferences to interact with all
		set dlgRef to make dialog with properties {name:dlgName, canCancel:cancelIt, label:dlgLabel}
		tell dlgRef
			tell (make dialog column)
				tell (make dialog row)
					make static text with properties {static label:"MS Object Name:"}
					set nameField to make text editbox with properties {min width:144, edit contents:""}
				end tell
				tell (make dialog row)
					make static text with properties {static label:"Button Color:"}
					set colorDrop to make dropdown with properties {min width:144, string list:buttonColors, selected index:0}
				end tell
				tell (make dialog row)
					make static text with properties {static label:"Button Position:"}
					set positionDrop to make dropdown with properties {min width:144, string list:buttonPositions, selected index:0}
				end tell
			end tell --column
		end tell --dialog
		set userResponse to show dlgRef
		if userResponse = true then
			set nameStr to edit contents of nameField
			set colorIndex to (selected index of colorDrop) + 1
			set positionIndex to (selected index of positionDrop) + 1
		else --user cancelled
			set wasCancelled to true
		end if
		destroy dlgRef
		set user interaction level of script preferences to origLevel
		--if cancelled, throw error; otherwise return values
		if wasCancelled then error "User cancelled"
		return {nameStr, colorIndex, positionIndex}
	end tell --application
end dialogChoices 

Run the Script

You can run the script from within AppleScript Editor. For future use you may wish to save the script in the Scripts folder for InDesign. To organize your scripts, create a folder in the Scripts folder just for your EPUB Animation scripts.

When you run the script, you will be presented with the dialog shown in the screen capture below. .

Custom Dialog for entering name and preferences.

Give the slideshow a name and select a color for the buttons and choose one of the options for positioning the buttons (relative to the selected rectangle). 

Next you will be presented the system Choose a Folder window for selecting your slideshow images.

 Sample Slideshow with Buttons

View your slideshow in InDesign’s EPUB Interactivity Preview panel. Click on the arrows to navigate through the slideshow. Optionally, export the document to EPUB Fixed Layout. Make sure to set the Cover dropdown to None if you do not have a cover image prepared. For Conversion Settings, if all of your images are raster images, set the Format to JPEG with a resolution of 150. In the Viewing Apps tab, check both System Default and Applications/iBooks.app. This way you can verify the animation behavior in both applications.

Discussion

Most of the script is fairly self-explanatory. However, placing assets from a library may be new territory especially when the assets are buttons.

The names of the buttons in the Button Library are the numbers shown below the button. For the purpose of the script, we chose the small arrows and placed the name of the back arrow in a list as a number value. Since the forward button is the back button with one (1) added, the number list that corresponds to the button colors simply lists the back button number for the button name having the corresponding color.

The method for placing an asset is place asset and can only have either the document or text as its target. For this reason the makeButton handler places the asset on the document and then moves it to the page and then the place point.

set placedObj to place asset assetRef on docRef
move placedObj to pageRef
move placedObj to placePt 

When an asset is placed, the result returned is a list. For this reason, the makeButton needs to return item 1 of the list.

return item 1 of placedObj 

This reference is then assigned to the variables backButton and foreButton which is used to make the goto behaviors for the buttons.

On Your Own

When working with scripts, you will discover there is more than one way to get from point A to point B. You may want to work through this script and maybe try other approaches for achieving the same result.