Finding and Processing Items in InDesign CC

This blog is written in response to a user request for scripting help in InDesign. The user’s task was to get a list of all objects in a document that had a tint value less than 100% and move that object to a specified layer. In testing a script that would accomplish this task in InDesign CC, I ran into a few issues that I felt needed discussing.

 

When you want to process all InDesign document items matching a filter criteria, both AppleScript and ExtendScript provide several options.

AppleScript

Find Object

Using find object, a script should be able to filter the objects to be found using properties set for find object preferences and find change object options as in the following:

tell application "Adobe InDesign CC"
	set layerRef to layer "Target" of document 1
	tell find change object options
		set include hidden layers to false
	end tell
	set find object preferences to nothing
	set fill tint of find object preferences to 50
	set myFoundItems to find object
	repeat with i from 1 to length of myFoundItems
		set item layer of item i of myFoundItems to layerRef
	end repeat
	set find object preferences to nothing
end tell

This process gives the most flexibility in that virtually any property that can be set for an object can become a property setting for find object preferences. Because find works at the application level, there are some problems in assigning properties such as color (fill color, etc.). Also, this method only works well when a specific value is used for testing; it does not work for a range of values.

Count of Objects

With this process, the script gets a count of all of the specified objects and then uses a repeat loop to process each item by index. One or more tests can be part of the process loop to filter the items.

tell application "Adobe InDesign CC"
	tell document 1
		set layerRef to layer "Target"
		set theCount to count of rectangles
	end tell
	repeat with i from 1 to theCount
		if fill tint of rectangle i of document 1 = 50 then
			set item layer of rectangle i of document 1 to layerRef
		end if
	end repeat
end tell
theCount

There seems to be a problem with InDesign CC when using this method. The result is not consistent. Even though the script found the required number of items, not all of the items were moved to the layer named “Target.” The script works as expected with InDesign CS6.

Every Reference

The every element specifier is actually a form of numerical indexing. It points to all items that meet a test. Without a test, all items are returned in the list.

tell application "Adobe InDesign CC"
	tell document 1
          set layerRef to layer "Target"
	  set findList to every rectangle whose fill tint is not -1 and fill tint is less than 100
	  repeat with everyItem in findList
	      set item layer of everyItem to layerRef
	  end repeat
	end tell
end tell

In many instances, if all items meeting the test are to be processed uniformly, no repeat loop is necessary.

tell application "Adobe InDesign CC"
	tell document 1
            set layerRef to layer "Target"
	    tell (every rectangle whose fill tint is not -1 and fill tint is less than 100) to set item layer to layerRef
	end tell
end tell

As you can see in the every statements above, if a script were to need to filter the found items using more than one or two properties, the whose clause in an every statement can get a little hairy.

ExtendScript

Find Object

As with AppleScript, findObject() works great if you just want to find all items having a specific value. For finding all items with a fill tint less than 100, findObject does not look like a promising prospect.

var docRef = app.documents.item(0);
var layerRef = docRef.layers.itemByName ("Target");
app.findChangeObjectOptions.includeHiddenLayers = false;
app.findObjectPreferences = NothingEnum.nothing;
app.findObjectPreferences.fillTint = 50;
var myFoundItems = app.findObject();
for (var i = 0; i <myFoundItems.length; i++){
     myFoundItems[i].itemLayer=layerRef;
     app.FindObjectPreferences=NothingEnum.nothing;
}

Count of Objects

With this process, the script gets a count of all of the objects as defined and then uses a repeat loop to process each item by its index. One or more tests can be part of the process loop to filter the items. As with AppleScript, the result of this method is not consistent. Although the value for theCount is correct, not all items get reassigned to the layer named “Target.”

var docRef = app.documents.item(0);
var layerRef = docRef.layers.itemByName("Target");
var theCount = docRef.rectangles.count();
var thisItem;
for (var i = 0; i < theCount; i++){
     thisItem=docRef.rectangles.item(i);
     if (thisItem.fillTint==50){
          thisItem.itemLayer=layerRef;
     }
} 

EveryItem()

Similar to the every reference in AppleScript, ExtendScript has an everyItem() method. But this method is for setting properties, not for filtering found items. The object collection can be used instead. As with its AppleScript counterpart, the result of this process is not accurate consistently. (It does work in InDesign CS6 however.)

var docRef = app.documents.item(0);
var layerRef = docRef.layers.itemByName("Target");
var itemArr = docRef.rectangles;
var thisItem;
for (var i = 0; i <itemArr.length; i++){
     thisItem=itemArr[i];
     if (thisItem.fillTint !=-1 && thisItem.fillTint <100) {
          thisItem.itemLayer=layerRef;
     }
}

One work around is to get a list of the items by id and then reference the objects by id within the repeat loop. This seems to work consistently. Notice that the everyItem method can be used here since the script is not filtering by id; just getting a list of the item ids.

var docRef = app.documents.item(0);
var layerRef = docRef.layers.itemByName("Target");
var rectArr = docRef.rectangles.everyItem().id;
var tintVal, rectRef, itemId;
for (var i = 0; i <rectArr.length; i++){
     itemId=rectArr[i]; 
     rectRef=docRef.rectangles.itemByID(itemId);
     tintVal=rectRef.fillTint;
     if (tintVal !=-1 && tintVal < 100) {
          rectRef.itemLayer=layerRef;
     }
}

Notice the if statement that tests the tint value for the rectangle. It first tests for a value of -1 (100%) and then for a value less than 100.

 

I am sure there are a number of ways to get around this little object-reference-in-a-loop glitch for InDesign CC. Hopefully, now that Creative Cloud allows application changes (fixes) to be made within a short programming cycle, the little problems we may find will not be with us long.

On Your Own

You may wish to do some experimenting on your own to evaluate the method you would use to accomplish the user’s goal (move all rectangles having a fill tint less than 100% to a specified layer).