Your prescription for increased productivity and profitability
Few are the scripts that can be written that don’t use a repeat loop. Imagine what writing a script would be without repeats: there would need to be a statement written for each item to be processed. The number of times statements are processed depends on the repeat. The most simple is to repeat a number of times:
This repeat is used when the process within the loop does not change.
repeat aNumber times --aNumber is an integer --statements to repeat end repeat
There are few places where this repeat structure can be used. More often a counting variable is used to determine the number of loops. This variable can be used to make some change within the loop.
One place a counting loop is used to advantage is in adding incremented numbers to text. In the following the variable i is used as a counting variable. Why the letter i? Some say it represents the word integer, but for whatever reason it is probably the most used variable on the planet.
Adding numbered text to the beginning of paragraphs.
set textStr to "This is line 1" & return & "This is line 2" & return & "This is line 3" set baseStr to "Item" tell application "Adobe InDesign CC 2019" tell text frame 1 of page 1 of document 1 set contents to textStr repeat with i from 1 to count of paragraphs set insertion point 1 of paragraph i to baseStr & i & ": " end repeat end tell end tell
Try this with text placed from a text file.
When the value of a variable within the loop is not incremented or cannot be calculated, a list of values is used. As an example you may remember how a list was used to control where guides were created on a master page in the BizCard_6up script.
set vList to {11, 23, 25, 34} tell application "Adobe InDesign CC 2019" set docRef to document 1 end tell masterGuides (docRef, vList) (*Creates guides on page 1 of master spread 1 using position values in vlist and hList*) on masterGuides(docRef, vList) tell application "Adobe InDesign CC 2019" set pageRef to page 1 of master spread 1 of docRef tell pageRef repeat with i from 1 to length of vList make guide with properties {guide type:ruler, location:item i of vList, orientation:vertical} end repeat end tell end tell return pageRef end masterGuides
A repeat loop is often used when working with geometric bounds. For instance, to calculate the width of any number of same-size items within a given space the following formula is used:
itemWidth = (availableSpace - (numberItems - 1) * gutterWidth) / numberItems
Of course, the same formula can be used to calculate the height of same-size items vertically.
The following calculates the width of columns within the width of a page and then creates items using the calculation
set gutter to 12 set numCol to 2 tell application "Adobe InDesign CC 2019" set measurement unit of script preferences to points tell page 1 of document 1 copy bounds to {py0, px0, py1, px1} set avail to px1 - px0 set itemWid to (avail - (numCol - 1) * gutter) / numCol set x0 to px0 repeat with i from 1 to numCol set x1 to x0 + itemWid make rectangle with properties {geometric bounds:{py0, x0, py1, x1}} set x0 to x1 + gutter end repeat end tell end tell itemWid
You might want to experiment with this by changing the value of numCol.
Using the formula above a repeat loop can be used to return a list of the geometric bounds for items to be created in a grid. Here the grid will fit within the margins of the page.
set gutter to 12 set gap to 12 set numCol to 2 set numRow to 2 tell application "Adobe InDesign CC 2019" set measurement unit of script preferences to points tell page 1 of document 1 set pageBounds to bounds tell margin preferences set pageMargins to {top, left, bottom, right} end tell set boundsList to my gridBounds(pageBounds, pageMargins, numCol, numRow, gutter, gap) repeat with i from 1 to length of boundsList make rectangle with properties {geometric bounds:item i of boundsList} end repeat end tell end tell (*calculates geometric bounds for items within the margins of a page*) on gridBounds(pageBounds, pageMargins, numCol, numRow, gutter, gap) set boundsList to {} copy pageBounds to {py0, px0, py1, px1} copy pageMargins to {mTop, mLft, mBot, mRgt} set availW to px1 - (px0 + mLft + mRgt) set itemWid to (availW - (numCol - 1) * gutter) / numCol set availH to py1 - (py0 + mTop + mBot) set itemHgt to (availH - (numRow - 1) * gap) / numRow set x0 to px0 + mLft set y0 to py0 + mTop repeat with i from 1 to numRow set y1 to y0 + itemHgt repeat with j from 1 to numCol set x1 to x0 + itemWid set end of boundsList to {y0, x0, y1, x1} set x0 to x1 + gutter end repeat set x0 to px0 + mLft set y0 to y1 + gap end repeat return boundsList end gridBounds
The gridBounds handler needs a little explanation as it uses a nested repeat loop. First, it determines the size of the items within the grid using the formula from above. Outside the repeat loop it establishes the values for the first top (y0) and left (x0) coordinates. Within the outside loop (the i loop) it sets the value for the bottom (y1) coordinate. The horizontal coordinate for x1 is then calculated within the inner loop (the j loop). With all four coordinates defined the list is added to the boundsList. The x0 and x1 values continue to be set within the j loop until the number of columns is reached.
When the inner (j loop) completes the x0 and y0 values for the first item in the next row is established (the x0 value is reset and gap added to y1 to create the y0 value). The y1 value is calculated with the next iteration of the i loop. The j loop follows to create x1, x0 values and all four values added to the boundsList. This repeats until the number of rows completes.
To finish the script, the page creates rectangles by repeating through the list of lists (boundsList).
How functionality is divided between handlers and the main section of a script is a matter of choice. To make the gridBounds handler more usable, you could calculate the itemWid and itemHgt values in the top portion of the script and pass these values to the handler. Because our BizCard_6up script started with values for the grid items predefined, the getBounds handler could be simplified. The interesting thing about this project is that the document size was determined to cut out 6 cards evenly when margins, gutter, and gap are all 2 picas. Check the math: (the sample document was 48 picas by 44 picas).
itemWid = (48 - 4 - (1 * 2))/2 itemHgt = (44 - 4 - (2 * 2))/3
Here’s a challenge: what would the values for gutter, gap, and margins have to be to cut 8 cards out evenly from letter size (8-1/2 x 11) stock?
The problem with the getBoundslist handler in the BizCard_6up script is that it assumes the top and left margins will be the same as the gutter and gap. This can be a very dangerous assumption. Instead, it would be better to pass the top margin and left margin values to the handler as part of its call (this could be part of the gridList variable).
With the sizes for the items in the grid defined, the following will create the boundsList for an 8-up business card (assumes the document is 8-1/2 x 11 with margins top and bottom:4.5 picas, left and right: 3 picas).
--measurements are in picas set gridList to {4, 2, 3, 3, 21, 12, 4.5, 3} --rows, cols, gut, gap, wid, hgt, top margin, left margin} set marList to {4.5, 3, 4.5, 3} set presetName to "BizCard_8up" (*Parameters used for creating the document preset defined in the propList variable: page width, page height, marginList (marList), facing pages, master frame, number pages, columnCount*) set propList to {51, 66, marList, false, true, 1, 1} set boundsList to getBoundsList (py0, px0 (*Returns a list of lists for creating a grid of page items.*) on getBoundsList(gridList) set boundsList to {} copy gridList to {nRows, nCols, gut, gap, wid, hgt, py0, px0} set y0 to py0 set x0 to px0 repeat with i from 1 to nRows set y1 to y0 + hgt repeat with j from 1 to nCols set x1 to x0 + wid set end of boundsList to {y0, x0, y1, x1} set x0 to x1 + gut end repeat set x0 to px0 set y0 to y1 + gap end repeat return boundsList end getBoundsList
To test the above make sure you have a document open set as above and add the following to the top of the script:
set wordList to {“One”, “Two”, “Three”}
tell application "Adobe InDesign CC 2019" set measurement unit of script preferences to picas set pageRef to page 1 of document 1 tell pageRef repeat with eachItem in boundsList make text frame with properties {geometric bounds:eachItem} end repeat repeat with i from 1 to length of wordList set insertion point 1 of text frame i to item i of wordList end repeat set testit to geometric bounds of text frame 1 end tell end tell testIt
Take note of the first repeat loop above. It uses a variable instead of a loop counter. The second repeat loop uses an incrementing variable (i) to add words to some of the text frames. Try it.
When you ran the TestList script were you surprised to see the text placed starting with the bottom right text frame? When InDesign creates items within a repeat loop, the first item is placed on the bottom of a stack and each succeeding item is placed on top. This puts the item created with the last bounds in the boundsList on top.
The testIt variable was added to verify the geometric bounds of the first indexed item on the page. As expected its values show that this item is located near the bottom right of the page.
To have page items created so that text frames 1 starts at top left, you can use several methods.
Method 1: Use a repeat loop with a counting variable starting from the length of the boundsList backward. Replace the tell pageItem statement block to read:
tell pageRef repeat with i from length of boundsList to 1 by -1 make text frame with properties {geometric bounds:item i of boundsList} end repeat repeat with i from 1 to length of wordList set contents of insertion point 1 of text frame i to item i of wordList end repeat set testIt to geometric bounds of text frame 1 end tell
Method 2: Change how the bounds are added to the boundsList. In the getBoundsList handler change the line that sets the end of boundsList to {y0, x0, y1, x1} to read:
set beginning of boundsList to {y0, x0, y1, x1}
This way the last item calculated becomes the first item of the boundsList and the bottom item for the stacking order.
You can avoid the stacking order problem by naming items as they are created and refer to items by name instead of index. Change the tell application statement block in the above to read as follows:
set imageRef to choose file with prompt "Choose image to place" tell application "Adobe InDesign CC 2019" set pageRef to page 1 of document 1 tell pageRef repeat with i from 1 to length of boundsList set theName to "Image_" & i make rectangle with properties {name:theName, geometric bounds:item i of boundsList} end repeat place imageRef on rectangle "Image_1" end tell end tell
Take note of how the name for the item is created.
One more way to parse a list within a repeat loop is to use rest of list with the repeat while statement. This is particularly efficient when parsing through extra long lists because the list gets shorter each time through the list. For demonstration, change the tell pageRef statement block above to read as follows:
tell pageRef set theCounter to 1 repeat while boundsList is not {} make text frame with properties {geometric bounds:item 1 of boundsList} set boundsList to rest of boundsList end repeat repeat with eachItem in wordList set insertion point 1 of text frame theCounter to eachItem set theCounter to theCounter + 1 end repeat end tell
For the above you will also want to use beginning of to add bounds for grid items to the boundsList as in Method 2 above.
There are a number of other ways repeat loops can be created, but the ones presented above are the most common. When the need arises, use one of these handlers to calculate items needed for a grid. See if you can come up with some creative ways of displaying a number of different sized items within a grid. Hint: The height and width of all items will need to be relative to a base grid value.
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.