Your prescription for increased productivity and profitability
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 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.
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
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”.
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
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
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
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.
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.
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.
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.
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.