Time to think of gift giving. How about a custom calendar for a friend, colleague, or family member? Place a favorite picture that the recipient would appreciate at the top of a custom calendar and print it out. Just a little thoughtful gift to add your personality to the season.

How It’s Done

Calendars for each month of the year are included on a single 8-1/2×11 page. Each of the calendars for the months are themselves tables. So you need to understand how to set up cell styles and table styles. (We will be covering this in future blogs.) For now, you can download the template from our Feature page. The rest is taken care of by a script (included with the download).

…The finished calendar

There is one little problem using tables for calendars in that some months may require six rows to show the dates rather than the normal five. The script will need to take that into consideration.

The template uses a text frame named “Dieline” to identify the area to be used by the calendars. The top portion of the script reserved for an image (566 pts x 140 pts) is named “Image.” The text frame over the oval area for the year is named “YearTitle” which includes styling with paragraph style “Calendar.”


At the top of the calendar area are two frames for the days of the week. These are named “tableHead1” and “tableHead2” respectively and are also used by the script to identify the values for the column widths.

The script assumes that the year to be displayed will be the year following the current year.


At the beginning of the script the user is asked to choose an image for the header portion of the page. To fit nicely in the header frame create an image 566 pts by 140 pts at 300 ppi.

    set imageRef to choose file with prompt ("Choose file for Calendar Header")

The script uses the geometric bounds of the “Dieline” text frame to define the total width and height of the Calendar area. To calculate the height of the individual calendars, the total height minus the space allowed between the calendar frames (gap), is divided by 6.

tell spread 1 of document 1
    set masterFrame to text frame "Dielist"
    copy geometric bounds of masterFrame to {y0, x0, y1, x1}
    set frameWid to x1 - x0
    set frameHgt to y1 - y0
    set calHgt to round ((frameHgt - (5 * gap)) / 6)

Text frames are then created to hold the calendar titles and the calendars.


The text frames for the month titles are named “title” plus a number for the month (as in “title1”.) These frames are also given a label that is the same as the month name. The text frames that hold the month calendars are named “month” plus a number for the month (as in “month1”). These are created in the handler named createFrameswhich takes the width of the calendar title frames and the value for the vertical space between the calendars (gap) as its arguments.

Repeat within a Repeat

To create the text frames for each column, a loop (from 1 to 2) repeats within another loop (1 to 6) to produce the six rows of two columns necessary for the calendars. Notice in the code below how a temporary variable (tx) is used to define the left horizontal coordinate for each column within the loop:

--in the create frames loop
   set tx to x10
   repeat with i from 1 to 6
      set y1 to y0 + calHgt
      repeat with j from 1 to 2
         set monthNum to monthNum + 1
         set theLabel to item (monthNum + 1) of monthList
         set tBounds to {y0, tx, y0 + calHgt, tx + titleWid}
         make text frame with properties {geometric bounds:tBounds, ¬
         name:"title" & monthNum, label:theLabel, applied object style:titleStyle}
         set calFrame to make text frame with properties ¬
         {geometric bounds:{y0, tx + titleWid, y0 + calHgt, tx + titleWid + calWid}, name:"month" & monthNum}
         set tx to x20
      end repeat
      set tx to x10
      set y0 to y1 + gap
   end repeat

The values for the variables calWid (column width) and calHgt (row height) are returned and used in the createCalendarTable handler.

Calendar Control List (calList)

To control the process for creating the calendars, the script creates a list of lists (calList) in the getCalList() handler. This, and the year header at the top of the page take advantage of a number of date methods:

  • current date – returns a date in the form: weekday, month date, year at hour:minute:seconds PM/AM
    as in: date “Thursday, December 7, 2017 at 7:08:44 PM”
  • year (of date) – returns the year as a four-digit number
  • days – the number of days between two dates can be calculated by subtracting the two and then dividing by days
    set theDay to date"February 1 2018"
    set nextDay to date "March 1 2018"
    set daysBetween to (nextDay - theDay) div days
    set yearStr to "" & ((year of (current date)) + 1)
    set calList to getCalList(yearStr) 

The value for the yearStr variable is passed to the getCalList handler which returns the list of list. Each list within the list defines the number of days, number of blank cells needed for the first row, and the total number of rows needed for each month. As each list is created, the number of filled cells in the table’s bottom row (overfill) become the empty cells for the top row for the next month (thefill). A helper handler calculates the number of rows needed for the current month’s calendar plus the number of cells occupied by dates in the bottom row.

on getCalList(theYear)
    set calList to {}
    --days in month Dec previous year to Jan this year with exception of February
    set dayList to {31, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31}
    --get number of days in February
    set theDay to date ("February 1 " & theYear)
    set nextDay to date ("March 1 " & theYear)
    set item 3 of dayList to (nextDay - theDay) div days
    --Get day of week as number for December 1 of previous year
    set firstDay to date ("December 1 " & (theYear - 1))
    --set contents of string for table cells before first day to empty string
    set thefill to (weekday of firstDay as number) - 1
    --number of days in December of prevoius year
    set numDays to (item 1 of dayList)
     (*calculate number of rows, number of empty spaces at front, and filled cells on last row of calendar table*)
    set {numRows, overFill} to getnumRows(thefill, numDays)
    --list containing number days, number of empty cells at front and end of calendar table
    copy {numDays, thefill, numRows} to the end of calList
    (*filled cells of last row for last month becomes the empty frames for fir row of the next month*)
    copy overFill to thefill
    --repeat for remaining months
    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

(*calculates number of rows needed to accommodate the number of days plus the empty
cells at the front of the table. Returns the number of rows required for the table (numRows) and the number of filled cells in the table's last row -overFill.*)
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)
        set overFill to 0
    end if
    return {numRows, overFill}
end getnumRows

The result of this process is a list of lists with each item in the list indicating the number of days, the number of empty cells in first row of the table.and the number of rows needed to display the days. Notice that there are fourteen entries within calList to include the calendar for December of the previous year and January of the next year.

{{31, 5, 6}, {31, 1, 5}, {28, 4, 5}, {31, 4, 5}, {30, 0, 5}, {31, 2, 5}, {30, 5, 5}, {31, 0, 5}, {31, 3, 5}, {30, 6, 6}, {31, 1, 5}, {30, 4, 5}, {31, 6, 6}, {31, 2, 5}}

The Calendars

To populate the cells for the calendar tables, the script uses a list of strings (tableList). The calendar and the string list both depend on the master list (calList). The string is composed using the number of spaces defined by the number of filled cells of the previous month’s calendar. It is composed by extracting the number of elements needed from the blankList and numList properties identified at the top of the script.

if spaces > 0 then
    set spaceList to items 1 thru spaces of blankList
    set tableList to spaceList & items 1 thru numDays of numList
    set tableList to items 1 thru numDays of numList
end if

The calendar text frames (calItem) creates the table which is then populated with the list of strings (tableList).

tell calItem --the text frame for the calendar
   set tableRef to make table with properties {column count:7, header row count:0, body row count:numRows}
end tell
tell tableRef
   set height of rows 1 thru -1 to rowHgt
   set applied table style to calStyle
   tell cells to clear cell style overrides
   set contents to tableList
end tell


You can download the script and the template from the Feature page here or from your browser at http://www.yourscriptdoctor.com/feature


Scripts are provided as examples for instruction. No representation is made for the correctness or completeness of the script. Users are advised to use the script as their own discretion