WORKING WITH TEXT STYLES

Our previous blog focused mainly on creating and using paragraph styles. One big advantage of working with paragraph styles is that they can be saved as part of a style file or template to be used in any number of other documents. Here, we will look at some of these applications, but first a short discussion on Character Styles.

CHARACTER STYLES

When setting up a document that may be used as a style sheet or template in addition to setting up paragraph styles you may want to consider creating a set of character styles. The character styles that are used consistently in most documents can be set up at the application level so once set up they will be part of every document created thereafter. The styles you will want to include are “Italic”, “Bold”, and “BoldItalic”. The example script following sets up these styles with the only attribute included being the Font Style. This way the font styling can be applied to any text irrespective of its size or other attributes.

Create Character Styles

set cStyleList to {{"Italic", "Italic"}, {"Bold", "Bold"}, {"BoldItalic", "Bold Italic"}}
tell application "Adobe InDesign CC 2019"
   set x to name of character styles
   repeat with i from 1 to length of cStyleList
      set theName to item 1 of item i of cStyleList
      if not (exists character style theName) then
	 set charStyle to make character style with properties {name:theName}
      else
	 set charStyle to character style theName
      end if
      set attribName to item 2 of item i of cStyleList
      set font style of charStyle to attribName
   end repeat
end tell

For the above, you may ask why a a list of lists was used to define both the name and the font style attribute when {“Italic”, “Bold”, “Bold Italic”} would have sufficed. The reason is that there are other purposes for text styles in which spaces are not permitted, but the font style attribute requires a space.

PARAGRAPH STYLES

In creating paragraph styles, you may also want to create an object style that defines a paragraph style as its first (or only) paragraph style. This is particularly useful when the next style property is used to define a styling pattern in which the styles will be applied. Think of documents such as a newsletter where body text always follows after a headline. (See our previous blog post.) Below is an example script that creaes such an object style. The script allows the user to choose the paragraph style for the object style’s first style.

Create Object Style

(*Creates object style for text frames having a chained paragraph style sequence*)
set badList to {"[No Paragraph Style]", "[Basic Paragraph Style]"}
set stylePrompt to "Choose paragraph style name for first style of object style"
set promptStr to "Enter name for object style"
set falseAsDefault to false
set dAnswer to "Chained Styles"
try
   set styleList to getStyleList(badList)
   set firstStyleName to getNameFromList(styleList, stylePrompt)
   set styleName to getString(promptStr, falseAsDefault, dAnswer)
   set objStyleRef to makeObjectStyle(styleName, firstStyleName 	
on error errStr
   activate
   display alert "Error: " & errStr
end try

(*Returns list of paragraph styles having next style property*)
on getStyleList(badList)
   tell application "Adobe InDesign CC 2019"
      tell document 1
	 set nextStyles to (name of paragraph styles where name of next style is not in badList) as list
	 if length of nextStyles is greater than 1 then
	    set styleList to rest of nextStyles
	 else
	    error "Requires list of styles having next style property"
	 end if
      end tell
   end tell
   return styleList
end getStyleList

(*Creates object style for text frames with paragraph style chaining*)
on makeObjectStyle(styleName, firstStyleName)
   tell application "Adobe InDesign CC 2019"
      tell document 1
	 set basedOnRef to object style "[Basic Text Frame]"
	 set firstParaStyle to paragraph style firstStyleName
	 if not (exists object style (styleName)) then
	    set styleRef to make object style with properties {name:styleName}
	 else
	    set styleRef to object style styleName
	 end if
	 set properties of styleRef to {based on:basedOnRef, applied paragraph style:firstStyleName, enable paragraph style:true, apply next paragraph style:true}
      end tell
   end tell
   return styleRef
end makeObjectStyle

(*Dialog for user to select paragraph style by name for first style of chain*)
on getNameFromList(styleList, thePrompt)
   set userChoice to choose from list styleList with prompt thePrompt
   if userChoice is not false then
      return item 1 of userChoice
   else
      error "User cancelled"
   end if
end getNameFromList


(*Gets string response from user; otherwise throws error*)
on getString(promptStr, falseAsDefault, dAnswer)
   set errStr to ""
   if falseAsDefault then
      set dButton to 2
   else
      set dButton to 1
   end if
   set userResponse to display dialog promptStr default answer dAnswer default button dButton
   if text returned of userResponse = "" then
      error "Valid string not entered"
   end if
   return text returned of userResponse
end getString

Notice this script has a main (top) section that calls four handlers:

   getStyleList returns list of paragraph styles for document having next style property

   getNameFromList presents user with list of paragraph styles to use for the first style in style pattern (“chain”)

   getString presents user with dialog for entering name for object style

   makeObjectStyle creates an object style for text frames with Next Style enabled.

You could make the script somewhat more user friendly by using a custom dialog in place of the two dialogs.

Try the script with a document that has one or more styles defined using the Next Style property. After creating the object style, add a text frame to the document, apply the object style, and enter in some text using a hard return after each paragraph.

APPLYING THE OBJECT STYLE

Instead o selecting the object style from the Object Styles panel (or using Quick Apply), you may want to use a script to apply the object to a text frame. This could be part of a script such as the following which, with a text frame selected, has the user choose a text file. The script then imports the file into the selected text frame, and applies the object style. This script is similar to an example in our previous blog post.

Import and Style Text

set stylePrompt to "Select name of Object Style to use"
set filePrompt to "Choose text file to place"
set classType to «class txtf»
try
   set frameRef to selectedPageItem(classType)
   set styleNames to getStyleNames()
   set objStyleName to getNameFromList(styleNames, stylePrompt)
   importAndStyleText(filePrompt, frameRef, objStyleName)
on error errStr
   activate
   display alert "Error " & errStr
end try
(*Places text file to selected frame and applies object style*)
on importAndStyleText(filePrompt, frameRef, objStyleName)
   set fileRef to choose file with prompt filePrompt
   if (name extension of (info for fileRef) is not "txt") then
      error "Requires file with \"txt\" extension"
   end if
   tell application "Adobe InDesign CC 2019"
      tell document 1
	 set objStyleRef to object style objStyleName
	 tell frameRef
	    place fileRef
	    apply object style using objStyleRef with clearing overrides
	 end tell
      end tell
   end tell
end importAndStyleText
(*Returns reference to text frame selected. If no selection throws error*)
on selectedPageItem(classType)
   tell application "Adobe InDesign CC 2019"
      set selList to selection
      if selection is not {} and (class of item 1 of selList is classType) then
	 set selItem to item 1 of selection
      else
	 activate
         error "Requires page item of class " & classType & " selected"
      end if
   end tell
end selectedPageItem
(*Returns list of object styles in document other than the first four default styes*)
on getStyleNames()
   tell application "Adobe InDesign CC 2019"
      tell document 1
	 if (count of object styles) < 5 then
	    error "Requires document with object styles other than default defined"
	 end if
	 set objStyleNames to (name of object styles where index > 4)
      end tell
   end tell
   return objStyleNames
end getStyleNames

(*Dialog for user to select paragraph style by name for first style of chain*)
on getNameFromList(styleList, thePrompt)
   set userChoice to choose from list styleList with prompt thePrompt
   if userChoice is not false then
      return item 1 of userChoice
   else
      error "User cancelled"
   end if
end getNameFromList

One big difference with this script is that the code for selecting the text frame is part of a handler. Because the script calls the handler from outside of InDesign, it needs to use the four letter code for a text frame («class txtf»). This brings up several items for discussion:

Text field

In more recent versions of InDesign, the class for a text frame is now text field. You can verify this with the following:

tell application "Adobe InDesign CC 2019"
   set selList to selection
   get class of item 1 of selList
end tell

Not sure when this changed, but InDesign CC 2015 (which I still have on an older machine) returns text frame. However, scripts still refer to the page item as a text frame:

tell application "Adobe InDesign CC 2019"
	get geometric bounds of text frame 1 of page 1 of document 1
end tell

Four Letter Codes

Internally, AppleScript targets objects using a class definition inside of chevrons (option and option plus shift on the backslash key). Using the four letter code for a text frame «class txtf» insures the script will work for a text frame class irrespective of which version of InDesign you are using. Additionally, as the above script demonstrates, using a four letter code allows a script to reference a class outside of a tell statement to InDesign.

IMPORT STYLES

Once you have a document with text and object styles (and perhaps table and cell styles), save it in a handy folder somewhere on your computer. The following script uses a folder named “Styles” inside the Application’s folder. You may need to come up with a different folder path if you don’t have administration privileges. The script imports text and object styles to the active document.

Import Text and Object Styles

set thePrompt to "Click button for import style option"
set filePrompt to "Select source file for style import"
try
   set docRef to getDocument()
   set userChoice to display dialog thePrompt buttons {"Text and Object Styles", "All Styles", "Cancel"} default button 1
   set buttonChoice to button returned of userChoice
   set sourceFile to getStyleFile(filePrompt)
   importStyles(docRef, sourceFile, buttonChoice)
on error errStr
   activate
   display alert "Error: " & errStr
end try
(*Choose file used to get reference to file for importing styles*)
on getStyleFile(filePrompt)
   tell application "Adobe InDesign CC 2019"
	set appPath to file path as string
   end tell
   set folderPath to appPath & "Styles:"
   set dLocation to folderPath as alias
   set fileChoice to choose file with prompt filePrompt default location dLocation without invisibles
   return fileChoice
end getStyleFile
(*Imports styles based on choice made in dialog from file chosen*)
on importStyles(docRef, sourceFile, buttonChoice)
   tell application "Adobe InDesign CC 2019"
      tell docRef
         if buttonChoice is "All Styles" then
	    import styles format table styles format from sourceFile
	    import styles format text styles format from sourceFile
	 else --clicking Cancel would throw an error
	    import styles format object styles format from sourceFile
	 end if
      end tell
   end tell
end importStyles

(*Checks for application modal state and returns reference to document if it exists*)
on getDocument()
   tell application "Adobe InDesign CC 2019"
      if modal state is true then
	 error "Please close modal windows and try again"
      end if
      if (count of documents) = 0 then
	  error "Requires an open document."
      end if
      set docRef to document 1
   end tell
   return docRef
end getDocument

ONWARD AND UPWARD

Did you notice in the scripts above that the handlers were written to “handle” specific functionalities. This way, handlers can be mixed and matched to be used in any number of scripts with very little modification, if any. For instance, you might want to create a script that imports styles to a new document and applies the text from a chosen text file to a text frame created by the script. Handlers used in the script above could be part of that script. We will look into this in our next blog post. .