One of the great scripts provided with InDesign is BreakFrame. In a nutshell, it is designed to break the thread between selected text frames. It first checks to make sure there are text frames or text items selected. If at least one text frame or item is in the selection list, it passes the list to the BreakFrames handler which does the work. Interesting is the way the threads are broken. The handler first duplicates the frame, then it deletes the text from the original frame and then deletes the original frame itself.

RETHREADING BROKEN THREADS

A recent issue posted in the InDesign Forum sought a solution for re-threading text frames which had been broken with the BreakFrames script. I am sure there are several solutions for this. The one I came up with was to modify the BreakFrames script to preserve the ids of the duplicated frames in a list.

A small change to the script’s myBreakFrames handler might be all that would be needed.

(*Partial listing shows line of code added to repeat loop that saves the item id to a list*)			
   set procList to {}
   ...
   tell myObj
	set myDup to duplicate
	copy id of myDup to end of procList 
   end tell

Parsing a list such as this, the frames could be rethreaded by setting the next text frame property for each item in the list to the next. To demonstrate, given an id list, the following code structure could be used.

Rethread Test

set linkList to {511, 534, 557, 580, 603, 626}
set itemCount to (count linkList)
tell application "Adobe InDesign 2020"
   set docRef to document 1
   repeat with i from 1 to (itemCount - 1)
	set thisId to item i of linkList
	set nextId to item (i + 1) of linkList
	set thisFrame to text frame id thisId of docRef
	set nextFrame to text frame id nextId of docRef
	set next text frame of thisFrame to nextFrame
   end repeat
end tell

ID LIST

The problem then becomes how to store the ids for the duplicated items when the threads are broken. One way this can be done is to use the label property of the document. The label property is a handy place to store any string value. However, the Ids of page items are numerical values. Our breakFrame handler could save the values as a comma-delimited string. Instead, a list was used. To convert the list to a string, AppleScript’s text item delimiters are used.

For example:

set linkList to {511, 534, 557, 580, 603, 626}
set linkStr to listToText(linkList)
linkStr
(*listToText handler converts a list to a comma-delimited string*)
on listToText(theList)
   set AppleScript's text item delimiters to ", "
   set theStr to theList as text
   set AppleScript's text item delimiters to ""
   return theStr
end listToText

PROBLEMS

In testing our BreakList script we discovered several problems. For one, the script throws an error if the user selects an empty text frame. The error is created in trying to delete text that doesn’t exist from the original text frame, To fix this we added a try/end try trap.

myBreakFrames Handler

on myBreakFrames(myObjectList)
   set procList to {}
   tell application "Adobe InDesign 2020"
   	repeat with i from 1 to length of myObjectList
	   set myObj to item i of myObjectList
	   tell myObj
		set myDup to duplicate
		copy id of myDup to end of procList
  	   end tell
	   try
	   	tell text 1 of myObj to delete
	   end try
	   tell myObj to delete
	end repeat
   end tell
   return procList
end myBreakFrames

If you compare this handler with the original myBreakFrames handler you will see the following test has been removed:

   if class of myObject is not text frame then
	set myObject to item 1 of parent text frames of myObject
   end if

The reason for this change is another problem we discovered. If the user accidentally selects a text frame that is not part of the thread, it is included in the id list. For this, the script also needs a test to make sure each text frame is a threaded frame. Considering this and the other tests that need to be performed, the decision was to put all of the tests in the main portion of the script. Rather than have a bunch of nested if statements, you will see in the code below each test is done individually within a try/on error structure.

BREAKFRAMES

The top portion of the script is now rewritten as follows:

set myObjectList to {}
try
   tell application "Adobe InDesign 2020"
	set myTextObjects to {text, text frame, insertion point, character, word, text style range, line, paragraph, text column}
	if (count documents) is 0 then
	   error "Requires document with text frame selection"
	end if
	set mySelection to selection
	if (count mySelection) is 0 then
	   error ("No items selected")
	end if
	repeat with i from 1 to (count mySelection)
	   if class of item i of mySelection is not in myTextObjects then
		error ("No text items or frames selected")
	   end if
	   if class of item i of mySelection is not text frame then
		set myObject to item 1 of parent text frames of item i of mySelection
	   else
		set myObject to item i of mySelection
	   end if
	   set isLinked to (next text frame of myObject is not nothing or previous text frame of myObject is not nothing)
	   if (isLinked) then
	    	copy myObject to end of myObjectList
	   end if
	end repeat
	if (count myObjectList) is greater than 0 then
	   set procList to my myBreakFrames(myObjectList)
	end if
	tell document 1
	   set label to ""
	   set label to my listToText(procList)
	end tell
   end tell
   procList
on error errStr
   activate
   display alert "Error " & errStr
end try
 (*listToText handler converts a list to a comma-delimited string*)
on listToText(theList)
   set AppleScript's text item delimiters to ", "
   set theStr to theList as text
   set AppleScript's text item delimiters to ""
   return theStr
end listToText

Note: Add the myBreakFrames handler from above to complete the script.

RE-THREAD TEXT FRAMES

The following script is then used to re-thread the “broken” frames.

(*Gets list of text frames in document label to rethread broken text threads*)
try
   tell application "Adobe InDesign 2020"
	tell document 1
	   set theLabel to label
	   if theLabel is "" then
		error "Document does not have information in label"
	   end if
	   set theList to my textToList(theLabel)
	   repeat with i from 1 to ((length of theList) - 1)
		set thisId to item i of theList as number
		set nextId to item (i + 1) of theList as number
		set thisFrame to text frame id thisId
		set nextFrame to text frame id nextId
		set next text frame of thisFrame to nextFrame
	   end repeat
	end tell
   end tell
on error errStr
   activate
   display alert ("Error " & errStr)
end try
(*Converts comma-delimited string to a list*)
on textToList(theLabel)
   set AppleScript's text item delimiters to ","
   set theList to text items of theLabel
   set AppleScript's text item delimiters to ""
   return theList
end textToList

ONWARD AND UPWARD

Similar to BreakFrames, InDesign includes a script named SplitStory. For this script any text frame within a thread can be selected.. As with BreakFrames the selection is tested for text frames and uses the parent story as its text frame reference. This solution takes care of non-threaded text frames accidentally being selected. It doesn’t help much when it comes to our wanting to get the text frame ids for later re-threading. Let’s explore. But a scripted break/re-thread frames workflow can take advantage of using the story reference. We will see how this can be put together in a later week’s blog post.