Your prescription for increased productivity and profitability
When using a script to create an object, one technique is to define the center point of the object and calculate geometric bounds from this point. The advantage is that the object can then be “drawn” anywhere on the page simply by changing the geometric coordinates for the center point. In this post we will explore this technique from the point of view of writing a script for InDesign using AppleScript.
First you will need the script to define a page within a document. One way is to use a reference to the active spread of the active window. In the following, we trap the error in the event there is not an active window.
try tell application "Adobe InDesign CC 2019" set myPage to page 1 of active spread of active window end tell on error errStr activate display alert "Error: " & errStr end try
With the page defined, the script will need to know where within the page to create the item. For example, the center point of the page could be used. Because the script will be doing mathematical calculations, the measurement unit to be used will need to be defined.
One way to define measurement units is to use view preferences. For this, the current horizontal and vertical measurement units for the document’s view preferences is saved to a variable, and then changed. Later, the variable is used to restore the values when the script is done. You will see this technique used in a number of the scripts provided with InDesign. (See CropMarks.applescript as an example.) The advantage of using view preferences is that you can set different measurement units for vertical and horizontal. A simpler method is to use script preferences. The measurement unit for script preferences affects both dimensions but this only affects script processes. We will use this method.
tell application "Adobe InDesign CC 2019" set measurement unit of script preferences to inches decimal end tell
For demonstration, let’s write a script to create a page item in the center of the page. First, the script needs to define the page’s center x and y coordinates. (We’ll ignore error trapping at this stage.)
tell application "Adobe InDesign CC 2019" set measurement unit of script preferences to inches decimal set pageRef to page 1 of active spread of active window set gBounds to bounds of pageRef set myOrigin to {0, 0} set myCenter to my getCenter (gBounds, myOrigin) end tell (*Returns center point given bounds and list of x/y offsets from origin*) on getCenter (gBounds, myOrigin) copy gBounds to {y0, x0, y1, x1} copy myOrigin to {ox, oy} set cx to (x1 - x0)/2 + ox set cy to (y1 - y0)/2 + oy return {cx, cy} end getCenter
In case you are wondering why the need to define the myOrigin variable: It is to allow extendability. It provides the opportunity to consider page margins in determining page center. Or, perhaps to use the getCenter handler for instances when you might want to create a page item in the center of another page item that could be located anywhere on the page.
Now that you have the center point defined, you can create the page item using the following handler:
(*Creates page item given page reference, center point, page item type and parameters*) on pgItemFromCenter(pageRef, myCenter, itemType, itemWid, itemHgt, objStyleRef) set halfWid to itemWid / 2 set halfHgt to itemHgt / 2 copy myCenter to {cx, cy} set gBounds to {cy - halfHgt, cx - halfWid, cy + halfHgt, cx + halfWid} tell application "Adobe InDesign CC 2019" tell pageRef set pageItemRef to make itemType with properties {geometric bounds:gBounds, applied object style:objStyleRef} end tell end tell return pageItemRef end pgItemFromCenter
Change the top portion of the script to read as follows:
set itemWid to 4 set itemHgt to 3 tell application "Adobe InDesign CC 2019" set measurement unit of script preferences to inches decimal set pageRef to page 1 of active spread of active window set gBounds to bounds of pageRef set myOrigin to {0, 0} set myCenter to my getCenter (gBounds, myOrigin) set itemType to rectangle set objStyleRef to object style 2 of document 1 set pgItemRef to my pgItemFromCenter(pageRef, myCenter, itemType, itemWid, itemHgt, objStyleRef) end tell
Make sure your script includes the getCenter handler.
To demonstrate the advantage of creating a page item from center we will be brave and use a random set to define the center point. Using random number, the script will define a random location within the confines of the page.
The center point for our page item can be defined by passing the page’s bounds to a handler that, given minimum and maximum values, returns a random number.
tell application "Adobe InDesign CC 2019" set measurement unit of script preferences to points set pageRef to page 1 of active spread of active window copy bounds of pageRef to {py0, px0, py1, px1} set cX to my randomNumber (px0, px1) set cY to my randomNumber (py0, py1) end tell
At the bottom of the script add the following handler:
(*Returns a rounded random number given minimum and maximum allowed values*) on randomNumber(minValue, maxValue) set rNumber to random number from minValue to maxValue set myRound to round (rNumber) return myRound end randomNumber
Make sure you have a document open and test for the values of cX and cY at the bottom of the script. So far, so good.
Now let’s see how using create from center works for “drawing” a circle using the random coordinates returned from the randomNumber handler. Using our pgItemFromCenter handler, the top of the script can now read as follows (notice measurement units are now in points):
set itemWid to 144 set itemHgt to 144 tell application "Adobe InDesign CC 2019" set measurement unit of script preferences to points set pageRef to page 1 of active spread of active window copy bounds of pageRef to {py0, px0, py1, px1} set itemType to oval set objStyleRef to object style 2 of document 1 set cx to my randomNumber (px0 + itemWid/2, px1 - itemWid/2) set cy to my randomNumber (py0 + itemHgt/2, py1 - itemHgt/2) set pageItemRef to my pgItemFromCenter (pageRef, {cx, cy}, itemType, itemWid, itemHgt, objStyleRef) end tell
Make sure your script includes the following handlers: randomNumber and pgItemFromCenter.
Now for the fun. Test your script with a document open. If all goes well, add a repeat loop to create a number of circles at random locations on the page. Get creative. Modify the script to have circles of random sizes. What about random fill colors? Hmmmmm.
For the scripts above: add a try/on error trap to catch any errors. Add a user dialog handler to the script to have the user define the values needed for the script (item type, item height, item width, object style name). For the Page Item Center Page script add an enabling group for setting myOrigin (offsets for horizontal and vertical). For the Just for Fun script, add an integer editbox for the number of times to repeat. Here’s a handler to get you started. Change the top of your scripts to use the values returned from the user dialog for creating your page item(s) and call the pgItemFromCenter handler.
set dlgName to "Page Item Preferences" set cancelIt to true set itemTypeList to {"rectangle", "oval", "text frame"} set objStyleList to {"[Basic Graphics Frame]", "[Basic Text Frame]"} set userResponse to userDialog(dlgName, cancelIt, itemTypeList, objStyleList) (*Provides widgets for choosing item type and style along with width and height*) on userDialog(dlgName, cancelIt, itemTypeList, objStyleList) tell application "Adobe InDesign CC 2019" activate set wasCancelled to false 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, can cancel:cancelIt} tell dlgRef tell (make dialog column) tell (make dialog row) make static text with properties {static label:"Page Item Type"} set itemField to make dropdown with properties {string list:itemTypeList, selected index:0} end tell tell (make dialog row) tell (make dialog column) make static text with properties {static label:"Width (inches)"} set widField to make measurement editbox with properties ¬ {edit units:inches, min width:72, minimum value:72, maximum value:720} end tell tell (make dialog column) make static text with properties {static label:"Height (inches)"} set hgtField to make measurement editbox with properties ¬ {edit units:inches, min width:72, minimum value:72, maximum value:720} end tell --column end tell --row tell (make dialog row) make static text with properties {static label:"Object Style"} set styleField to make dropdown with properties {string list:objStyleList, selected index:0} end tell --row end tell --column end tell --dlgRef set userResponse to show dlgRef if userResponse is true then set itemTypeIndex to ((selected index of itemField) + 1) set itemWid to edit value of widField set itemHgt to edit value of hgtField set objStyleIndex to ((selected index of styleField) + 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"--single line if statement return {itemTypeIndex, itemWid, itemHgt, objStyleIndex} end tell end userDialog
One thing to keep in mind when using measurement editboxes: the values for minimum and maximum values as well as the value returned is always in points no matter the value set for edit units.
Disclaimer:
Scripts provided are for demonstration and educational purposes. No representation is made as to their accuracy or completeness. Readers are advised to use the code at their own risk.