InDesign scripting : lesson 21

here’s a script to accompany the cool feature covered in InDesign tip : #19 which showed how to quickly place a whole bunch of images into a neat grid. the technique is perfect for creating contact sheets — all we need to do now is label those suckers. this script will add image filenames to the page.

as always, to follow along, just copy the script samples into applescript editor (found in applications > utilities) or, for older OS versions, script editor (applications > applescript). then update the first line to your version of InDesign. hopefully the script will run nicely, unless the syntax has changed between versions.

first we need to do a little setting up — change the view preferences, create a layer and create a paragraph style :

tell application "Adobe InDesign CS4"
  tell active document
    set mgOrigPrefs to properties of view preferences
    set ruler origin of view preferences to spread origin
    
    try
      set mgLayer to layer "Image Labels"
    on error
      set mgLayer to make layer with properties {name:"Image Labels"}
    end try
    
    try
      set mgPara to paragraph style "Image Labels"
    on error
      set mgPara to make paragraph style with properties {name:"Image Labels", justification:center align, point size:12, applied font:"Arial Narrow	Bold", fill color:"Black"}
    end try

    -- other script elements will go in here

    set properties of view preferences to mgOrigPrefs
  end tell
end tell

whenever you use applescript to change preferences, it’s good practice to first capture the current settings so that you can reset the preferences once you’re done — this is what the start and end of that script are doing.

the other bits are pretty self explanatory. if you don’t understand why the make layer and make paragraph style parts are contained in try blocks — just ask.

just one thing : you may not have or want the font “Arial Narrow Bold”. just plonking in another font name may not work (notice the big gap in the script’s font name — this is a necessary element for that font). so what’s the easiest way to get the correct name of the font you DO want to use? just open a new InDesign file with one text frame containing some text in the font you prefer. then run this script :

tell application "Adobe InDesign CS4"
  tell active document
    get applied font of story 1
  end tell
end tell

the correct font name (with any weird spacing and whatnot) will appear in the results window. just copy/paste it across.

ok, let’s keep going. the next part of the script goes after that last end try. first we set up a repeat loop to process every page in the document. for each page we have to get all the page items that could contain an image (mgItems). and, because groups are treated as their own special kind of page item, we use a try block to look through any groups for other frames to add to the mgItems list.

next, we set up another repeat loop to process each page item in the mgItems list. since we don’t yet know if the page item contains a link, we try to get the name of any image (raster) or graphic (vector). then we set up an if/then statement to create a label wherever required :

set mgItemTypes to {rectangle, oval, polygon}
repeat with mgCountPage from 1 to (count pages)
  set mgPage to page mgCountPage
  set mgGroups to (every page item of mgPage whose class is group)
  set mgItems to (every page item of mgPage whose class is in mgItemTypes)
  
  try
    repeat with x from 1 to count of mgGroups
      set mgGroupedItems to (every page item of (item x of mgGroups) whose class is in mgItemTypes)
      repeat with z from 1 to count mgGroupedItems
        set end of mgItems to item z of mgGroupedItems
      end repeat
    end repeat
  end try
  
  repeat with mgCountItems from 1 to (count mgItems)
    set mgPageItem to (item mgCountItems of mgItems)
    set mgLinkName to ""
    try
      set mgLinkName to name of item link of image 1 of mgPageItem as string
    on error
      try
        set mgLinkName to name of item link of graphic 1 of mgPageItem as string
      on error
        set mgCountItems to mgCountItems + 1
      end try
    end try
    
    if mgLinkName is not "" then
      set mgGeoBounds to geometric bounds of mgPageItem
      set mgY2 to text item 3 of mgGeoBounds
      set text item 1 of mgGeoBounds to mgY2
      set text item 3 of mgGeoBounds to (mgY2 + 5)
      
      set mgLinkLabel to make text frame in mgPage with properties {geometric bounds:mgGeoBounds, fill color:swatch 2}
      set contents of mgLinkLabel to mgLinkName
      move mgLinkLabel to layer "Image Labels"
    end if
  end repeat
end repeat

the interesting bits are in that last if/then statement. first we get the geometric bounds (mgGeoBounds) of the page item containing the image or graphic (mgPageItem). for an explanation of geometric bounds, see InDesign scripting : lesson 15. we use these as the basis for creating geometric bounds for the label. items 2 and 4 of mgGeoBounds are left unchanged — these are the left and right edges of the frame. item 1 (top) is reset to match item 3 (bottom) and item 3 is reset to 5mm further down (or 5 of whatever measurement units you’re using). so, we end up with a 5mm high text frame whose top edge aligns exactly with the bottom edge of the image frame. hope that makes sense.

and the final part of the script goes immediately after that last end repeat and contains a number of interesting bits and pieces too — but it should be pretty easy to understand just by reading it — set text frame options, apply the paragraph style we created first up, and deal with any overset text :

tell every text frame of layer "Image Labels"
  set vertical justification of text frame preferences to center align
  set ignore wrap of text frame preferences to true
  set inset spacing of text frame preferences to {0.5, 2, 0.8, 2}
end tell

tell parent story of every text frame of layer "Image Labels"
  set applied paragraph style to "Image Labels"
  set applied character style to "[No character style]"
  clear overrides overrides to clear all
end tell

try
  tell (parent story of every text frame of layer "Image Labels" whose overflows = true)
    set leading to 10
    set point size to 10
  end tell
end try
try
  tell (parent story of every text frame of layer "Image Labels" whose overflows = true)
    set leading to 8
    set point size to 8
  end tell
end try
try
  tell (parent story of every text frame of layer "Image Labels" whose overflows = true)
    set leading to 6
    set point size to 6
  end tell
end try

screen shot showing page of labelled images

putting the labels on a separate layer makes it easy to select and move them if you need to (or delete them) :
screen shot of page after labels have been moved

of course, you’re not restricted to labelling images with just a filename. you could add a file size, filepath, xmp data, whatever. this script can be easily adapted to place captions for each image — if the captions are saved as metadata with the image file. go sick.

go here if you want a copy of the complete image labelling script

macgrunt icon

InDesign scripting : lesson 20

this is a perfect example of how becoming familiar with applescript will save you (well, at least your time). just last week a new problem presented itself. artwork for thirty signs had to be sent to a signwriter who required all text be converted to outlines.

now that’s not too difficult — select all (command-a), create outlines (command-shift-o), repeat for remaining 29 pages. but, when you know a little applescript (and when you know you’ll be doing this for future projects for the same signwriter) it makes sense to create an automated solution.

if you check the scripting dictionary you’ll see that the command create outlines can address a whole bunch of stuff — characters, words, lines, stories, text frames, etc. the first attempt at this script addressed stories :

tell application "Adobe InDesign CS4"
  tell active document
    tell every story
      create outlines
    end tell
  end tell
end tell

… and got more complex with each attempt :

tell application "Adobe InDesign CS4"
  tell active document
    repeat with thisStory in every story
      tell thisStory
        create outlines
      end tell
    end repeat
  end tell
end tell
tell application "Adobe InDesign CS4"
  tell active document
    repeat with x from (count every story) to 1 by -1
      tell story x
        try
          create outlines with delete original
        end try
      end tell
    end repeat
  end tell
end tell

these sort of worked, kind of ok, some of the time — in other words, they were bloody useless.

so it made sense to try a different tact — surprisingly, perhaps (or perhaps not), this simple variation of the original script works much better :

tell application "Adobe InDesign CS4"
  tell active document
    tell every text frame
      create outlines
    end tell
  end tell
end tell

so far, only one other hassle has presented itself — groups. applescript doesn’t recognise text frames if they are part of a group (unless you specify to check inside groups). to check this for yourself, create an InDesign document with three text frames — group two of the text frames together — then run this script :

tell application "Adobe InDesign CS4"
  tell active document
    set theFrames to count text frames
    set theGroups to count text frames of group 1
    
    display dialog "text frames = " & theFrames & return & "text frames in groups = " & theGroups
  end tell
end tell

no worries — for this workflow we could just ungroup all the groups before converting to outlines :

tell application "Adobe InDesign CS4"
  tell active document
    repeat until (count of groups) = 0
      ungroup every group
    end repeat
    tell every text frame
      create outlines
    end tell
  end tell
end tell

the repeat loop ensures we get every group — that means groups within groups within… etc.

your homework for today is to come up with a solution which does not involve ungrouping the groups.

macgrunt icon

InDesign scripting : lesson 15

this is the third of a series of lessons about how to script the contents of documents. so far we’ve looked at how to do things with items on a page. now it’s time to look at how to create those items in the first place.

if you want to follow along — open a new document. then run this script from script editor (found in applications > applescript) — as always, update the script to your version of InDesign :

tell application "Adobe InDesign CS4"
  activate
  tell active document
    make rectangle
  end tell
end tell

pretty simple. eh? but who needs a tiny rectangle in the top left corner of the page? once the rectangle is created, change it to suit your needs :

tell application "Adobe InDesign CS4"
  activate
  tell active document
    make rectangle
    tell rectangle 1
      set stroke color to "Black"
      set stroke weight to 2
      set geometric bounds to {5, 5, 30, 50}
    end tell
  end tell
end tell

notice that you don’t set the width and height of a page item — you set its geometric bounds. those four figures define, in order, the top, left, bottom and right edges of the rectangle. or, if you check your control panel, the Y and X coordinates of the top left corner, then the Y and X coordinates of the bottom right corner.

you can also set all the properties in one hit :

tell application "Adobe InDesign CS4"
  activate
  tell active document
    make rectangle
    tell rectangle 1
      set properties to {stroke color:"Black", stroke weight:2, geometric bounds:{5, 5, 30, 50}}
    end tell
  end tell
end tell

but the easiest way is to set the properties as you make the page item :

tell application "Adobe InDesign CS4"
  activate
  tell active document
    make rectangle with properties {stroke color:"Black", stroke weight:2, geometric bounds:{5, 5, 30, 50}}
  end tell
end tell

there are lots of different properties you can set for rectangles (and other page items). the best way to learn what they are is to check the InDesign scripting dictionary. in script editor go file > open dictionary and choose InDesign from the list. enter ‘rectangle’ into the search field (top right corner) and look for the entry classified as ‘class’. here’s a screen grab of part of the CS2 entry (click to enlarge) :

screen grab of part of 'rectangles' entry in InDesign scripting dictionary

the entries marked ‘r/o’ are read only — meaning you can’t change their value. all the others are at your mercy.

and now for your homework…
those of you who have been studying assiduously will be able to explain why this would fail when run on a blank document :

tell application "Adobe InDesign CS4"
  activate
  tell active document
    make rectangle with properties {stroke color:"Black", stroke weight:2, geometric bounds:{5, 5, 30, 50}}
    set properties of rectangle 1 to {content type:text type}
    select rectangle 1
  end tell
end tell

interestingly, the command that changes the content type could also be written like this in CS2 :

set content type of rectangle 1 to text type

but that functionality is broken in CS4 and CS5 (well, at least as far as script editor is concerned) — go figure.

next we’ll look at colours and swatches.

go to lesson 16

macgrunt icon

InDesign scripting : lesson 14

lesson 13 started talking about how to script the contents of documents — specifically, how to select items on a page. let’s continue by having a look at some things you can do with page items.

if you want to follow along — open a new document and put a bunch of different page items on the first page — make sure you have more than one ‘rectangle’ (text frames are not rectangles — even if they are rectangular). then run this script from script editor (found in applications > applescript) — as always, update the script to your version of Indesign :

tell application "Adobe InDesign CS4"
  activate
  tell active document
    select every rectangle of page 1
    move selection by (5,1)
  end tell
end tell

the numbers in the brackets are the X (horizontal) and Y (vertical) locations and the unit increments will be whatever you have set as your default. you could replace those two lines with this instead if you like :

move rectangles of page 1 by (5,1)

notice the difference? the rectangles are moved without having to select them first. if you want to include items on the pasteboard, use this instead :

move rectangles of spread 1 by (5,1)

contrast the ‘move by’ command with the results of ‘move TO’ :

move rectangles of page 1 to (5,1)

which is similar to when you try to move items to a new page :

move rectangles of page 1 to page 2

that’s a problem if you want your page items to maintain their relative positions. you could get around the first problem like this :

tell application "Adobe InDesign CS4"
  activate
  tell active document
    select rectangles of page 1
    set newGroup to make group with properties {group items:selection}
    move newGroup to {5, 1}
    ungroup newGroup
  end tell
end tell

… and the second problem like this :

tell application "Adobe InDesign CS4"
  activate
  tell active document
    select rectangles of page 1
  end tell
  cut
  tell active document
    set newPage to page 2
    tell layout window 1
      set active page to newPage
    end tell
  end tell
  paste in place
end tell

notice in this one how you can not tell a document to cut and paste — you have to tell the application. when developing your own scripts, once you get a script like this to work — it’s a good idea to go back through it to see if it can be streamlined. here’s one way to simplify the previous script :

tell application "Adobe InDesign CS4"
  activate
  select rectangles of page 1 of active document
  cut
  set newPage to page 2 of active document
  set active page of layout window 1 of active document to newPage
  paste in place
end tell

this next script demonstrates a few other commands. before running it, make sure you have at least two rectangles on page 2. can you work out how InDesign determines which is rectangle 1 and which is rectangle 2? :

tell application "Adobe InDesign CS4"
  activate
  tell page 2 of active document
    set horizontal scale of rectangle 1 to 150
    set rotation angle of rectangle 1 to 10
    set stroke weight of rectangle 1 to 10
    set stroke color of rectangle 1 to "Paper"
    set shadow mode of rectangle 1 to drop
    set vertical scale of rectangle 2 to 200
    set fill color of rectangle 2 to "Black"
    set shear angle of rectangle 2 to 20
    set feather mode of rectangle 2 to standard
    set feather width of rectangle 2 to 5
  end tell
end tell

that’s it for now. the next lesson will show how to make new page items.

go to lesson 15

macgrunt icon

InDesign scripting : lesson 13

the first dozen lessons dealt with scripts that address InDesign documents or the application itself. the next few will focus on addressing the contents of files — the pages and the stuff on them. if you’re unconvinced that scripting the contents of files is useful to you — check out the short videos on youtube — one of those may change your mind.

the things on a page are collectively called page items — these include rectangles, text frames, ovals, polygons, graphic lines and, the frequently problematic, groups. fill a single-page file with different types of page items and then run this from script editor (found in applications > applescript) — as always, update the script to your version of Indesign :

tell application "Adobe InDesign CS4"
  activate
  tell active document
    select every page item
  end tell
end tell

then change “page item” to “rectangle” and run it again. cool eh? you can use “rectangles” instead of “every rectangle” if it makes more sense to you.

if you wanted to select the rectangles and text frames but not the other page items, you could do it like this (notice that you can’t use “select every rectangle and every text frame”) :

tell application "Adobe InDesign CS4"
  activate
  tell active document
    select every rectangle
    select every text frame existing selection add to
  end tell
end tell

well, that all works fine if you’ve only got stuff on one page. but it will fail with most documents because you cannot simultaneously select items on multiple pages — and “every rectangle” means every rectangle in the entire document. most of the time you need to specify a page or spread :

select every page item of page 3

you could also use “…in page 3″ — whatever takes your fancy.

previous lessons have mentioned how torturous applescript syntax can be. well, check this out — the way to select items of a particular layer of a particular page :

tell application "Adobe InDesign CS4"
  activate
  tell active document
    select (page items of page 3 whose (name of item layer is "Layer 2"))
  end tell
end tell

all these examples have used the command “select”. no doubt you can work out what would happen if you substituted another command — such as “delete”, “duplicate” or “bring to front”.

go to lesson 14

macgrunt icon