Your prescription for increased productivity and profitability
This week we will take a little detour from working with XML to address an issue that was posted to Adobe’s InDesign Scripting forum.
The issue involves a solution that in one form or another may be something that can be useful to just about any hard-working designer: changing the geometry for all page items of a type in a document.
In this case the user wants to reduce the width of all text frames in a document by a fixed unit value; using millimeters for measurement.
The problem is simple enough, but it involves several concepts that new users need to be aware of:
Changing some geometric property for page items is not as simple as it sounds. A script could get the geometric bounds for the items and then subtract (or add) the value(s) wanted from the resulting width (and height). The result would then be used to reset the geometric bounds. But this will only work if the items in question are not rotated or skewed. With this possibility, the geometric bounds solution is not good!.
To avoid this problem, InDesign provides a method that is terribly robust and takes care of any problems having to do with rotation or skew: resize. The problem with resize is that it has so much power that it can be intimidating. The following discussion hopefully will make it easier to understand.
If you open the dictionary for AppleScript and look at the methods available for a text frame, you will see an entry for resize. When you click on the entry, you may be in for a little surprise: there is more to resizing a page item than you might have imagined.
To start with, there are four properties that need to be set, and two optional ones. But don’t try reading the details, as it can be very confusing.
If you browse through the properties for resize, the words coordinate space may jump out at you. This takes some explanation. What we are working with here is a transformation, so we need to know the coordinate space in which the transform operation will occur. This can be one of three values:
<ol”>
To understand the concept of coordinate space, you might want to copy the following script for AppleScript and use it for the exercises below:
set horizontalFactor to 2 set verticalFactor to 1 tell application "Adobe InDesign CC 2015" set docRef to document 1 tell docRef set selList to selection set selItem to item 1 of selList tell selItem resize in inner coordinates from center anchor by multiplying¬ current dimensions by values {horizontalFactor, verticalFactor} end tell end tell end tell
Create a document with a singe page for testing. Create a page item (rectangle or text frame) about one-fourth the size of the page and save the document. Select the page item and run the script. Notice that the page item is scaled horizontally but not vertically.
Undo the last process (Control+Z). Notice the small square at the left of InDesign’s control panel that is divided into nine little squares. That is the transform reference point. Each of the small squares indicates an anchor point around which a page item’s transformation will occur. Select the top left anchor and rotate the page item 45 degrees. With the page item selected. Run the script again. Notice that the page item is scaled horizontally from center because the from parameter for our script’s procedure is set to center anchor.
Undo the last process. Change the value for the in parameter for resize to parent coordinates.
resize in parent coordinates from center anchor ...etc.
Run the script with the page item selected and observe the result.
Undo the last process. Change the in parameter for resize to pasteboard coordinates.
resize in pasteboard coordinates from center anchor ... etc.
Run the script with the page item selected and observe the result.
Now try the script with a page item that has not been rotated.
You might want to experiment with more page items and other values for the anchor point (top left anchor, top center anchor, top right anchor, etc.).
Revert the document back to its original (File > Revert). In our test script above, change the value for horizontalFactor to -100, and the verticalFactor to 0. Change the in parameter in the resize procedure to inner coordinates, the value for from to top left anchor, and the value for by to adding current dimensions to values.
resize in inner coordinates from top left anchor by adding ¬ current dimensions to values {horizontalFactor, verticalFactor}
Run the script with the page item selected and observe the result.
If you were observant when you ran the last exercise, you may have noticed that the page item increased in size by exactly 100 points horizontally. But what if you want to subtract by 70 millimeters as was requested by the user above?
First you need to make sure the measurement units for the document are set to millimeters when the script is run. Next you will need to add the optional parameter considering ruler units to the resize procedure.
resize in inner coordinates from center anchor by multiplying current dimensions ¬ by values {horizontalFactor, verticalFactor} considering ruler units
Finally, return the measurements back to the original values when the script completes.
To handle measurements, we have two handy routines that can take care of this:
(*Sets measures to millimeters, ruler origin to page origin. Returns original values as list.*) on setMeasures(docRef) tell application "Adobe InDesign CC 2015" tell view preferences of docRef set orighm to horizontal measurement units set origvm to vertical measurement units set origro to ruler origin set horizontal measurement units to millimeters set vertical measurement units to millimeters set ruler origin to page origin end tell end tell return {orighm, origvm, origro} end setMeasures (*Resets measures to original units, ruler origin to original origin.*) on resetMeasures(docRef, orighm, origvm, origro) tell application "Adobe InDesign CC 2015" tell view preferences of docRef set horizontal measurement units to orighm set vertical measurement units to origvm set ruler origin to origro end tell end tell end resetMeasures
To use these handlers, the script will need to add code to call each handler at the appropriate location (see finished script below for locations:
--call to handler to set measures; ruler origin to page set {orighm, origvm, origro} to my setMeasures(docRef) --call to handler to reset measures to original values my resetMeasures(docRef, orighm, origvm, origro)
So far, so good. But just to make sure there is a document open in InDesign, the script can use another handler. This one checks to make sure there is a document open and that there is no modal document active.
(*Returns reference to active document; otherwise generates error.*) on getDocRef() tell application "Adobe InDesign CC 2015" if modal state = true then error "Please close dialogs before running script" end if if not (exists document 1) then error "Requires active document" else return document 1 end if end tell end getDocRef
Notice that this handler will throw an error if there is a modal dialog open, or if there is no active document. For this reason, we will surround the call to the handler with a try/on error block.
set horizontalFactor to -70 set verticalFactor to 0 try set docRef to getDocRef() on error errStr display alert errStr return end try
If successful, this code sets the value of the variable docRef to the value returned from the getDocRef handler. For this reason remove the line that establishes the docRef value in the line just below the tell application statement.
--remove the following after tell application set docRef to document 1
Only one thing remains, and that is to get a reference to all of the text frames in the document, and then use a repeat loop to apply the resize to each of the items.
AppleScript provides a handy method for getting a reference to “every” object within a container. The container can be the document, a page, a spread, or page item. For the purpose of this script, the user wants to reference every text frame in a document. The every reference can be used for this.
tell application "Adobe InDesign CC 2015" set docRef to document 1 set frameRef to every text frame of docRef end tell
Once we have the a reference to the objects (frameRef), the script needs to make sure that the value of frameRef is not an empty list (no objects found). If objects are found, the script references the items within a repeat loop and applies the transform.
tell docRef set selList to every text frame if selList is mot {} then repeat with eachItem in selList resize eachItem in inner coordinates from center anchor by adding ¬ current dimensions to values {hFactor, vFactor} with considering ruler units end repeat end if end tell
So there you have the code to get started on your own script. And you thought this would just take a couple of lines of code. If would be fairly simple if you could assume that measurements are set to the unit the user want to use, if an active document exists, and if subtracting 70 millimeters from all text frames can be done. (A frame smaller than 70 millimeters would throw an error.) Further, the script as written does not take into consideration text frames that are part of a group or are embedded in another page item.
With this, we will leave the script (to delete 70 millimeters in width from every text frame in a document) up to readers to finish at their discretion. Following is the script to this point.
set horizontalFactor to -70 set verticalFactor to 0 try set docRef to getDocRef() tell application "Adobe InDesign CC 2015" --call to handler to set measures to points; ruler origin to page set {orighm, origvm, origro} to my setMeasures(docRef) tell docRef set selList to every text frame if selList is not {} then repeat with eachItem in selList resize eachItem in inner coordinates from center anchor by adding¬ current dimensions to values {-70, 0} with considering ruler units end repeat end if --call to handler to reset measures to original values my resetMeasures(docRef, orighm, origvm, origro) end tell end tell on error errStr --call to handler to reset measures to original values my resetMeasures(docRef, orighm, origvm, origro) display alert errStr return end try --======== --HANDLERS --======== (*Sets measures to points, ruler origin to page origin. Returns original values as list.*)
on setMeasures(docRef) tell application "Adobe InDesign CC 2015" tell view preferences of docRef set orighm to horizontal measurement units set origvm to vertical measurement units set origro to ruler origin set horizontal measurement units to millimeters set vertical measurement units to millimeters set ruler origin to page origin end tell end tell return {orighm, origvm, origro} end setMeasures (*Resets measures to original units, ruler origin to original origin.*) on resetMeasures(docRef, orighm, origvm, origro) tell application "Adobe InDesign CC 2015" tell view preferences of docRef set horizontal measurement units to orighm set vertical measurement units to origvm set ruler origin to origro end tell end tell end resetMeasures (*Returns reference to active document; otherwise generates error.*) on getDocRef() tell application "Adobe InDesign CC 2015" if modal state = true then error "Please close dialogs before running script" end if if not (exists document 1) then error "Requires active document" else return document 1 end if end tell end getDocRef
</ol”>