Your prescription for increased productivity and profitability
Need a little something to give to friends and family? How about a personalized calendar? You could create a simple one page calendar printed similar to the one shown below. This one is letter size but you could do it legal size or larger. (Smaller won’t work). Interested? Our sample script for this week will create the calendar for you–well, most of it.
Start with a table style to define the paragraph styles for the table regions. For the script below you will need to name it “Calendar. “You will also want a paragraph style for the header and month titles and whatever you add to your creation.
The styles used in the example are:
Paragraph Styles:
Name | Font | Size and Leading | Alignment | Color |
---|---|---|---|---|
Label | Din Condensed Bold | 14 pt on 14 lead | flush left | black |
Header | Din Condensed Bold | 10 pt on 10 lead | center | paper |
Body | Myriad Pro Normal | 10 pt on 10 lead | flush left | black |
Red | Based on Body | red |
Cell Styles:
Name | Applied Paragraph Style |
---|---|
Body | Body |
Red | Red |
Table Style
Name | Region | Cell Style |
---|---|---|
Calendar | ||
Body Row | Body | |
Left and Right Column | Red | |
Header and Footer | None |
The script to create the calendar is fairly long but not difficult. To keep it as short as possible we will assume that the table style named Calendar exists, and paragraph styles Header and Label are part of styles for the document. The script uses fixed values for variables. To make the script flexible, you would want to add a dialog for entering these values. The sample script below covers the basics. We will go through the code step by step. Test each step as you go.
Step 1: To define the area of the page for the calendar, a text frame needs to be selected. We will also have the script check to make sure it will be large enough for the calendar (minimum width 7.5 inches, minimum height:8 inches).
For the top portion of the script
property docRef: missing value try set frameParams to getFrameSelected() set frameRef to item 1 of frameParams set areaBounds to item 2 of frameParams on error errStr activate display alert "Error: " & errStr end try
The handler
(*Acts to initialize the script. Returns reference to text frame or insertion point in text frame selected. checks to make sure frame is at least 7.5x8.*) on getFrameSelected() set errMsg to "Requires a text frame 7.5x8 or larger to be selected" tell application "Adobe InDesign CC 2018" set measurement unit of script preferences to points set selList to selection if selList is {} then error errMsg set selItem to item 1 of selList if class of selItem is insertion point then set frameRef to item 1 of parent text frames of selItem else if class of selItem is text frame then set frameRef to selItem else error errMsg end if set gBounds to geometric bounds of frameRef copy gBounds to {y0, x0, y1, x1} if y1 - y0 < 8 * 72 or x1 - x0 < 7.5 * 72 then error errMsg end if set docRef to active document end tell return {frameRef, gBounds} end getFrameSelected
Notice how the try/on error/end try statement block in the top portion of the script is designed to catch any errors most notably the error statements in the handlers designed to notify the user.
Step 2: Next we will have the script create a grid for the calendar months. After the call to getFrameSelected, add a call to a handler named calculateGrid. The arguments being passed in the call are the bounds of the selected frame, number of rows, number of columns, gutter (horizontal space between columns), and gap (vertical space between individual calendars).
In the top portion of the script, just above the on error statement, add:
set boundsList to calculateGrid (areaBounds, 6, 2, 18, 6)
Then at the bottom of the script, add the following handler. This is a handy routine that you can use for any number or instances where you need to create a grid.
(*Returns list of bounds for each item in grid defined by values for rows, columns, gutter and gap passed.*) on calculateGrid(areaBounds, rCount, cCount, gut, gap) set boundsList to {} set fullWid to (item 4 of areaBounds) - (item 2 of areaBounds) set fullHgt to (item 3 of areaBounds) - (item 1 of areaBounds) set colWid to (fullWid - ((cCount - 1) * gut)) / cCount set rowHgt to (fullHgt - ((rCount - 1) * gap)) / rCount if class of (rowHgt / 6) = real then set cellHgt to (round (rowHgt / 6)) set rowHgt to cellHgt * 6 set gap to gap - 1 end if set x0 to item 2 of areaBounds set y0 to item 1 of areaBounds repeat with j from 1 to rCount repeat with i from 1 to cCount set x1 to x0 + colWid set y1 to y0 + rowHgt copy {y0, x0, y1, x1} to end of boundsList set x0 to x1 + gut end repeat set x0 to item 2 of areaBounds set y0 to y1 + gap end repeat return boundsList end calculateGrid
Step 3: In this step the script creates text frames with labels. For the labels we need a list of the month names to use for the title. Add the following property to the top of the script:
property monthList : {"December", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "January"}
With this we now have the script create the text frames for the individual calendars with a title frame to its left. The arguments for the makeTextFrames handler are: the boundsList (created above), the width of the title frame (60), the base name of the title frame “month” and the name of the title paragraph style (“Label”). Add the following call just after the call to calculateGrid.
set calFrameSiz to makeTextFrames (boundsList, 60, "month", "Label")
The handler
--create text frames for titles and tables on makeTextFrames(boundsList, titleWid, baseName, titleStyle) tell application "Adobe InDesign CC 2018" tell docRef set properties of text frame preferences to {text column count:1, inset spacing:0} set titleStyleRef to paragraph style titleStyle end tell tell spread 1 of docRef repeat with i from 1 to length of boundsList set thisBounds to item i of boundsList copy thisBounds to {y0, x0, y1, x1} set titleLabel to item (i + 1) of monthList set titleFrame to make text frame with properties ¬{geometric bounds:{y0, x0, y1, x0 + titleWid}, name:"title" & i, label:titleLabel, contents:titleLabel, text frame preferences:{inset spacing:3}} set applied paragraph style of paragraph 1 of titleFrame to titleStyleRef set calFrame to make text frame with properties {stroke color:"None", stroke weight:0.0, fill color:"None", geometric bounds:{y0, x0 + (titleWid - 1), y1, x1}, name:baseName & i} end repeat set calFrameWid to x1 - (x0 + (titleWid - 1)) set calFrameHgt to y1 - y0 end tell end tell return {calFrameWid, calFrameHgt} end makeTextFrames
Notice that our list of month names begins with December. This is because our calendar routine is designed to support a 14 month calendar (incuding the previous month of December and the next year’s month of January. These are not used in this example.
Step 4: To create the headers at the top of the calendar, the call to our next handler is placed below the call to makeTextFrames. Arguments passed are the first two items from the boundsList (creatrd in Step 2 above), the height of the header frame, the width of the label frames, and the name of the paragraph style to use for the text.
set bounds1 to item 1 of boundsList set bounds2 to item 2 of boundsList makeHeaderFrames(bounds1, bounds2, 18, 60, "Header")
And for the handler:
on makeHeaderFrames(bounds1, bounds2, headerHgt, labelWid, parastyleName) set headerList to {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"} copy bounds1 to {y0, x0, y1, x1} tell application "Adobe InDesign CC 2018" set blackColor to swatch "Black" of docRef set parastyleRef to paragraph style parastyleName of docRef repeat 2 times tell spread 1 of docRef set hx0 to x0 + labelWid set headFrame to make text frame with properties {geometric bounds:{y0 - headerHgt, hx0, y0, x1}} end tell tell headFrame set tableRef to make table with properties {body row count:1, column count:7, width:(x1 - hx0)} end tell tell tableRef set contents to headerList tell row 1 set fill color of cells to blackColor set applied paragraph style of paragraph 1 of cells to parastyleRef end tell end tell --table copy bounds2 to {y0, x0, y1, x1} end repeat end tell --app end makeHeaderFrames
Step 5: There are others who will come up with a better method for creating a calendar, but this works good for our purpose. For our calendar routine this step creates a control list that is used for creating the calendars.
The control list is a list of lists. Each list within the list indicates (1) the number of days in the month, (2) the number of table cells that need to be empty at the beginning of the month table, and (3) the number of rows required for the table. Two dates involved are: February 1 and March 1 of the calendar year (to take care of the leap year issue). Add the following after the call to makeHeaderFrames:
set calList to getCalList(2019)
This requires a second handler to calculates number of days for month, and blank day offsets for each month. To accommodate a 14 month calendar the first day is December 1 of the previous year. This example of our one page calendar does not use 14 months, so just adds 1 to the month number.
on getCalList(theYear) set calList to {} set dayList to {31, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31} set theDay to date ("February 1 " & theYear) set nextDay to date ("March 1 " & theYear) set item 3 of dayList to (nextDay - theDay) div days set firstDay to date ("December 1 " & (theYear - 1)) set thefill to (weekday of firstDay as number) - 1 set numDays to (item 1 of dayList) set {numRows, overFill} to getnumRows(thefill, numDays) copy {numDays, thefill, numRows} to the end of calList copy overFill to thefill repeat with i from 2 to count of dayList set numDays to (item i of dayList) set {numRows, overFill} to getnumRows(thefill, numDays) copy {numDays, thefill, numRows} to the end of calList copy overFill to thefill end repeat return calList end getCalList
--helper handler calculates number of rows for each calendar month on getnumRows(thefill, numDays) set totalDays to numDays + thefill set numRows to totalDays div 7 if totalDays mod 7 > 0 then set numRows to numRows + 1 set overFill to 7 - ((numRows * 7) - totalDays) else set overFill to 0 end if return {numRows, overFill} end getnumRows
Step 6. Now the piece de resistance. Using the calList returned from Step 5, this step actually creates the calendars. The last handler call to add to the top of the script passes the following arguments: a list of the calendar width and height, the name of the table style, and the calList created above. Add the call to the handler just above the on error statement.
createCalendarTables(calFrameSiz, "Calendar", calList)
And the handler:
(*Creates tables for calendars, populates, and styles using the table style*) on createCalendarTables(frameSiz, tableStyleName, calList) set blankList to {"", "", "", "", "", "", ""} set numList to {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"} copy frameSiz to {frameWid, frameHgt} tell application "Adobe InDesign CC 2018" set tableStyleRef to table style tableStyleName of docRef repeat with i from 1 to 12 set monthNumber to i set tableframeName to "month" & monthNumber copy item (monthNumber + 1) of calList to {numDays, spaces, numRows} set tableFrame to text frame tableframeName of spread 1 of docRef tell tableFrame set tableRef to make table with properties {width:frameWid, column count:7, header row count:0, body row count:numRows, applied table style:tableStyleRef} end tell tell tableRef --subtract 1.5 to account for calculation round off errors set height of rows 1 thru -1 to ((frameHgt - 1.5) / numRows) try set applied table style to tableStyleRef end try end tell if spaces > 0 then set spaceList to items 1 thru spaces of blankList set tableList to spaceList & items 1 thru numDays of numList else set tableList to items 1 thru numDays of numList end if set contents of tableRef to tableList tell tableRef tell cells to clear cell style overrides set vertical justification of cells to top align end tell end repeat end tell end createCalendarTables
That’s it! For clarification, the top of the script should read as follows:
property docRef : missing value property monthList : {"December", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "January"} try set frameParams to getFrameSelected() set frameRef to item 1 of frameParams set areaBounds to item 2 of frameParams set boundsList to calculateGrid(areaBounds, 6, 2, 18, 6) set calFrameSiz to makeTextFrames(boundsList, 60, "month", "Label") set bounds1 to item 1 of boundsList set bounds2 to item 2 of boundsList makeHeaderFrames(bounds1, bounds2, 18, 60, "Header") set calList to getCalList(2019) createCalendarTables(calFrameSiz, "Calendar", calList) on error errStr activate display alert "Error: " & errStr end try
Add an image and the year at the top. For a legal size document you might want to add a Season’s Greeting at the bottom. Or–Well, the rest is up to you.
To make the script bullet proof, you will need to add code to make sure the document has the required table and paragraph styles. You can change these styles to alter the style of your table as desired. You may also want to replace fixed values with values returned from a custom dialog. We will leave this up to you.
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.