FROM ONE

In our previous post we saw how taking advantage of an XML document structure can automate the process of placing elements into a page. We saw that an XML file that has only one main element can be placed into a document using the place statement while defining the place point. More commonly, documents built with XML focus on a number of repeating elements. Pages for these documents are set up with page items to tagged to “map” to the XML. Suppose, rather than having one main XML element with one or more child elements, the design calls for multiple elements all having the same or similar structure. This is one place that XML really shines.

TO MANY

To tackle the design requirement for a number of similar items, this week’s demonstration script will create a one page document from scratch, and set the page up as a grid. A chosen template provides the text and object styles while the script does the majority of the work. Some of the handlers may be familiar to you, as they are often used for many other applications.

GetDocPreset

To create a document from scratch, a document preset is most often used. The parameters for the getDocPreset handler are most often provided by the user in a custom dialog. To keep our script manageable, we will define these values at the top of the script.

set presetName to "Web1024_XML"
set pageWid to 1024
set pageHgt to 768
set marginList to {24, 36, 24, 36} --top, left, bottom, right
tell application "Adobe InDesign CC 2019"
   set measurement unit of script preferences to pixels
   --get document preset for creating document
   set presetRef to my getDocPreset(presetName, pageHgt, pageWid, marginList)
   --create the document
   set docRef to make document with properties {document preset:presetRef}
end tell
 
(*Creates preset if not existant; sets properties for the preset in either event*)
on getDocPreset(presetName, pageHgt, pageWid, marginList)
   copy marginList to {tMar, lMar, bMar, rMar}
   tell application "Adobe InDesign CC 2019"
	if modal state = true then
	   error "Please close dialogs before running script"
	end if
	set presetPrefs to {intent:web intent, page height:pageHgt, page width:pageWid, pages per document:1, facing pages:false, create primary text frame:true, top:tMar, left:lMar, bottom:bMar, right:rMar}
	if (exists document preset presetName) then
	   set presetRef to document preset presetName
	else
	   set presetRef to make document preset with properties {name:presetName}
	end if
        set properties of presetRef to presetPrefs
   end tell
   return presetRef
end getDocPreset

ImportStyles

To provide the styling for our document, the script will import text and object styles from a standing template. Add the following to the top of the script just before the end tell statement

my importStyles (docRef)

–and place the following handler at the bottom of the script

(*Imports styles from template chosen from files in InDesign's Templates folder*)
on importStyles(docRef)
   set promptStr to "Choose template from which to import styles"
   tell application "Adobe InDesign CC 2019"
	set appPath to file path as string
	set templatePath to (appPath & "Templates") as alias
	set fileRef to choose file with prompt promptStr default location templatePath without multiple selections allowed
	tell docRef
	   import styles format text styles format from fileRef
	   import styles format object styles format from fileRef
	end tell
   end tell
end importStyles

Important: The file chosen for the style import needs to include paragraph styles defined for the XML. For this example it will need a paragraph style for “Name” and “Desc”.

xmlImportPrefs

A necessary handler for working with XML is the xmlImportPrefs handler. How the properties for this document preference are set up is big topic. For now we will use settings that are fairly standard.
–Add to the top of the script just before the end tell statement,

my xmlImportPrefs (docRef, false) --boolean indicates if text elements will be repeated

–Place the handler at the bottom of the script

(*Sets up xml import preferences for merge import*)
on xmlImportPrefs(docRef, doRepeat)
   tell application "Adobe InDesign CC 2019"
	tell XML import preferences of docRef
	   set create link to XML to false
	   set allow transform to false
	   set import style to merge import
	   set repeat text elements to doRepeat
	   set import to selected to false
	   set ignore whitespace to false
	   set ignore unmatched incoming to false
	   set import CALS tables to false
	   set import text into tables to false
	end tell
   end tell
   return docRef
end xmlImportPrefs

GetGridBounds

We have modified this handler to provide geometric bounds for a banner (rectangle) at the top of the page and a list of lists defining a grid of text frames. The values for the handler’s parameters needs to be added to the variable declarations at the top of the script.

set bannerHgt to 100
set mainItem to "Item"
set numRows to 2
set numCols to 3
set gutter to 20
set gap to 12

These are passed to the handler getGridBounds with the following (place it at the bottom of the top main portion of the script just before the end tell statement):

--calculate geometric bounds
   set pageRef to page 1 of docRef
   set boundsList to my getGridBounds(pageRef, bannerHgt, marginList, numRows, numCols, gutter, gap)

Again, place the handler at the bottom of the script

(*Calculates geometric bounds for a banner box and a grid of text frames*)
on getGridBounds(pageRef, bannerHgt, marginList, numRows, numCols, gut, gap)
   set bannerBounds to {}
   tell application "Adobe InDesign CC 2019"
	copy bounds of pageRef to {py0, px0, py1, px1}
	set y0 to py0 + (item 1 of marginList) + bannerHgt
	set x0 to px0 + (item 2 of marginList)
	set y1 to py1 - (item 3 of marginList)
	set x1 to px1 - (item 4 of marginList)
	if bannerHgt > 0 then
	   set bannerBounds to {5, x0, y0, x1}
	end if
	set celWid to ((x1 - x0) - (numCols - 1) * gut) / numCols
	set celHgt to ((y1 - y0) - (numRows - 1) * gap) / numRows
	set firstX to x0
	set boundsList to {}
	repeat with i from 1 to numRows
	   repeat with j from 1 to numCols
		set x1 to x0 + celWid
		set y1 to y0 + celHgt
		copy {y0, x0, y1, x1} to the end of boundsList
		set x0 to x1 + gut
	   end repeat
	   set x0 to firstX
	   set y0 to y1 + gap
	end repeat
   end tell
   return {bannerBounds, boundsList}
end getGridBounds

With these values we should be able to create our page items, but first we will need to define our styles and XML tags. Add the following with the variable declarations at the top of the script:

set tagList to {"Headers", "Banner", "Items", "Item"}
set paraList to {"Name", "Desc"}

Add the following to the main portion of the script before the end tell statement:

   --make sure styles exist
   set styleProps to {fill color:none, stroke weight:0}
   set cStyle to my checkObjectStyle(docRef, "Container", styleProps)
   if "Banner" is in tagList then
	set bannerProps to {fill color:none, stroke weight:0}
	set bannerStyle to my checkObjectStyle(docRef, "Banner", bannerProps)
   end if
   --make sure tags are defined for the document
   my checkTags(docRef, tagList)
   --create xml tag to style mapping
   my mapTagsToStyles(docRef, paraList)

These statements call a number of handlers which need to be placed at the bottom of the script:

(*Checks for object style, if not exists then style is created*)

on checkObjectStyle(docRef, styleName, styleProps)
   tell application "Adobe InDesign CC 2019"
	tell docRef
	   if not (exists (object style styleName)) then --«class ObSt»
	      set styleRef to make object style with properties {name:styleName}
	   else
	      set styleRef to object style styleName
	   end if
	   set properties of styleRef to styleProps
	end tell
   end tell
   return styleRef
end checkObjectStyle
(*Checks to make sure tags are established in the document*)
on checkTags(docRef, tagList)
   tell application "Adobe InDesign CC 2019"
      tell docRef
	 repeat with i from 1 to length of tagList
	    set tagName to item i of tagList
	    if not (exists XML tag tagName) then
		make XML tag with properties {name:tagName}
	    end if
	 end repeat
      end tell
   end tell
end checkTags
(*Associates paragraph styles to XML tags to automatically style text*)
on mapTagsToStyles(docRef, paraList)
   tell application "Adobe InDesign CC 2019"
      tell docRef
	 repeat with i from 1 to length of paraList
	    set styleName to item i of paraList
	    if not (exists paragraph style styleName) then
		set styleRef to make paragraph style with properties {name:styleName}
	    else
		set styleRef to paragraph style styleName
	    end if
	    if not (exists XML tag styleName) then
		set tagRef to make XML tag with properties {name:styleName}
	    else
		set tagRef to XML tag styleName
	    end if
		make XML import map with properties {mapped style:styleRef, markup tag:tagRef}
	    end repeat
	end tell
   end tell
end mapTagsToStyles 

setupPage

With the geometric bounds defined, and styles and tags part of the document, the script can now create the page items. Add this before the end tell statement in the main portion of the script.

my setupPage(docRef, pageRef, boundsList, bannerStyle, cStyle)

The handler needs to be placed in the script with the other handlers at the bottom of the script.

(*Adds XML elements to the document root XML element and associates these elements to the page items as they are created*)
on setupPage(docRef, pageRef, boundsList, bannerStyle, cStyle)
   tell application "Adobe InDesign CC 2019"
      tell document 1
	 set rootElement to XML element 1
      end tell
      tell rootElement
	 set headElement to make XML element with properties {markup tag:"Headers"}
	 set itemElement to make XML element with properties {markup tag:"Items"}
      end tell
      tell pageRef
	 set bannerBounds to item 1 of boundsList
	 if bannerBounds is not {} then
	    set bannerRef to make rectangle with properties {geometric bounds:bannerBounds, applied object style:bannerStyle}
	 markup bannerRef using headElement
	 end if
	 set bList to item 2 of boundsList
	 repeat with i from 1 to length of bList
	    set thisFrame to make text frame with properties {name:("Item" & i), geometric bounds:item i of bList, applied object style:cStyle}
	    markup thisFrame using itemElement
            if i > 1 then
		set previous text frame of thisFrame to lastFrame
	    end if
	        set lastFrame to thisFrame
	    end repeat
	end tell
    end tell
end setupPage

THE ACID TEST

If you followed along without error the script should compile. When you run the script, the document is created. With Show Tagged Frames enabled (View menu > Structure > Show Tagged Frames) the page should look like the screenshot below.

…Page items added and associated with XML elements

If you open the structure panel (View > Structure > Show Structure) you will see the two XML elements that were added to the Root element. These XML elements are associated with the page containers and will cause the tagged XML items to flow in when the XML text file is imported.

THE XML FILE

A plain text document can be used to create an XML text file. You just need to remember to save the file with the .xml file extension. For the document structure created above, the XML file will need to read similar to the following:

<?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes”?> <Root> <Headers> <Banner><Image href=”file://Images/Banner.png”></Image></Banner> </Headers> <Items><Item><Image href=”file://Images/Image_01.jpg”></Image> <Name>Item 1. This is the name for the item</Name> <Desc>This is the description for the item. Uptas reiurepercia invello resentur sus namus alibusae dessimos moluptatem solorios et as autem ilitatest et rehenem faci ipsandae dem vit quunduntiis quatur susdaecearum ea imilitem qui dendae con pelibus apicaest, ut aliqui nis essimus est, ius qui con nia nullia poriorrum quia volecati am explab ius ex exerehe nducidem fugias eum re nonsedi psaped et eum et eatiassust.</Desc> </Item>

–Copy the Item, Name and Desc tagged text and paste for all other Item page elements (6 in all). Change the file reference for the Image, the text for the item Name, and the description text for Desc. Be sure to leave the tags inside the left and right tag brackets undisturbed. Make sure you include an </Items> and </Root> closing tags at the end. Double check to make sure that each item block includes a beginning and ending tag for Image, Name, and Desc inside a beginning and ending Item tag. (Remember ending tags include a forward slash.) Save the file with the .xml filename extension.

THE IMAGES

Notice how the tag for the image is written. There is no text information for an image, so the information area between the beginning and ending Image tag is empty. The path to the file itself is part of the beginning tag, for example: <Image href=”file://Images/Image_06.jpg”>

The two forward slashes indicate that the file will be found relative to the XML text file inside an Images folder which is at the same level (inside the same parent folder).

For the document created above, the images need to be sized 1225 px by 700 px at 300 ppi. You also need an image for the Banner sized approximately 100 pixels high. Make sure the images are saved in a folder named Images inside the same folder as the XML text file.

TEST IT

Save the document created with the script above. Now import the XML text file into the document (Select Import XML… from InDesign’s File menu and select the XML text file.) Your document should look similar to the following.

 

…The final page

What makes it all work is the fact that the document is set up to have the primary text frame and the text frame containers are threaded to create a single story flow.

ABOVE AND BEYOND

To make your script really useful, add a try/on error trap and a a custom dialog for the user to enter the values for the page and page item parameters.

Experiment with different page sizes and different XML structures (Maybe add a price and/or item number to each item.) Make sure that the template chosen for the stylesheet has the paragraph styles needed to work with the XML. Hint:Name the paragraph styles the same as the XML tags: No spaces, no special characters. Makes automating with XML so much easier.

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.