RESIZING PAGE ITEMS

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:

  1. resizing a page item
  2. working with measurement units
  3. getting a reference with “every”
  4. using a repeat

RESIZE

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.

PROPERTIES FOR RESIZE

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.

Coordinate Spaces

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”>

  • pasteboard coordinates: uses points as units and extends across all spreads in a document. It does not correspond to InDesign’s rulers or zero point. Transformations applied to objects have no effect on this coordinate space which means that the angle of the horizontal and vertical axes do not change.
  • parent coordinates: This is the coordinate space of the object’s parent. Any transformations applied to the parent affect the parent coordinates. For example, rotating the parent object changes the angle of the horizontal and vertical axes of this coordinate space. In this case, the parent object refers to the group or page item containing the object. If the parent of the object is a page or spread, parent coordinates are the same as pasteboard coordinates.
  • inner coordinates: The coordinate space is based on the object itself.

To understand the concept of coordinate space, you might want to copy the following script for AppleScript and use it for the exercises below:

Test Script

   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

Exercise 1:

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.

Exercise 2:

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.

Exercise 3:

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.

Exercise 4:

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.).

Exercise 5:

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.

Measurement Units

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)

DOCUMENT

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

GET REFERENCE TO TARGET ITEMS

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.

Using Every

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

Repeat With

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

THE (ALMOST) FINISHED SCRIPT

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.

Onward and Upward

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”>