# New year's todos: Making a PDF calendar with cairo

In this piece I will tell you about my latest hackish script: Creating a PDF calendar with CHICKEN Scheme's cairo binding. This includes a nice detour about date arithmentic as well as unreadable code for the actual calendar generation.

Still interested? Good! Read on, my dear.

In the past the wife has made a family calendar for our wall in A3 format. This has been done manually (in Adobe Illustrator IIRC) and involved a lot of tedious repetitive entries of all holidays and anniversaries.

The argument has been that computer generated calendars look ugly. A quick research showed that, there are hardly any free software programs out there that would come close to the original.

So I started out to write up my own. What would I need for a calendar? Let‘s count:

• A date representation, that allows one to query the weekday of a given date, and getting to know the date for the easter sunday.
• A graphic backend that can render PDFs so I can go to a copy shop to print the A3 pages.

Not too much! From a previous (unfinished) project I had some bits and pieces for representing dates in a '(year month day) list structure, even possibly representing „partial“ dates as I called them. With partial dates one would leave off the right tail(s) for uncertain dates, i.e. when you want all entries from a given month you‘d use '(year month) and so on. But I digress…

I also implemented the weekday query already (since in said project I have implemented a date-picker widget) using Gauss‘ method. Searching for a method to calculate the date for easter sunday as a lot of german holidays depend on this, I stumbled again over Mr. Gauss for having found a method to properly calculate this date. I am using the enhanced method as told by wikipedia (german only, sorry).

In scheme that beast looks like this (almost verbatim taken from wikipedia):

```(define (easter-sunday date)
(let* ((x (first date))
(k (quotient x 100))
(m (- (+ 15 (quotient (+ (* 3 k) 3) 4))
(quotient (+ (* 8 k) 13) 25)))
(s (- 2 (quotient (+ (* 3 k) 3) 4) ))
(a (modulo x 19))
(d (modulo (+ (* 19 a) m) 30))
(r (quotient
(quotient (+ d a) 11)
29))
(og (- (+ 21 d) r))
(sz (- 7 (modulo (+ x (quotient x 4) s) 7)))
(oe (- 7 (modulo (- og sz) 7)))
(os (+ og oe)))
(if (< os 32)
(list x 3 os)
(list x 4 (modulo os 31)))))```

The only adjustment I have made is that I return a proper date and not March 32nd for April 1st.

So the essential holidays can be created like this:

```;; the name is a bit misleading, it should be called 'days depending
;; on the easter date' but that's also a dumb name
(define (roman-catholic-holidays date)
(let* ((y (first date))
(es (easter-sunday date))
(christmas-day (day-of-week `(,y 12 24)))
`(,y 12 24)
(days+ `(,y 12 24) (- (add1 christmas-day))))))
`(((,y 1 6) . "Heilig. Drei Könige")
(,(days+ es -52) . "Weiberfastnacht")
(,(days+ es -48) . "Rosenmontag")
(,(days+ es -47) . "Faschingsdienstag")
(,(days+ es -46) . "Aschermittwoch")
(,(days+ es -7) . "Palmsonntag")
(,(days+ es -3) . "Gründonnerstag")
(,(days+ es -2) . "Karfreitag")
(,es . "Ostersonntag")
(,(days+ es 1) . "Ostermontag")
(,(days+ es 39) . "Christi Himmelfahrt")
(,(days+ es 49) . "Pfingstsonntag")
(,(days+ es 50) . "Pfingstmontag")
(,(days+ es 60) . "Fronleichnam")
((,y 12 24) . "Heiligabend")
((,y 12 25) . "1. Weihnachtsfeiertag")
((,y 12 26) . "2. Weihnachtsfeiertag"))))```

(Not all of these are bank holidays, but I have included them for good measure, after I got through all this trouble…)

You may note that this may contain more than one entry for a given date. The code recognises this by „querying“ with filter-map instead of alist-ref and concatenating results in a proper way.

So with all this we can think about drawing stuff. In CHICKEN Scheme I have a nice binding to the cairo library available, that does support PDF surfaces, so let‘s use this.

As for drawing primitives I need a rectangle and some nice TTF font rendering, both easily available.

As I had a nice layout to copy from, writing the resulting calendar has been a breeze.

The result looks like this:

See the full image for further details.

The font used in this example is the nice EB Garamond by Georg Duffner.

The code includes a second mechanism for anniversaries/birthdays which have been omitted in this screenshot.

Things you will have to change to make it work for yourself:

• Localisation of weekday names and months
• Check the holidays for your location, even for Germany these differ wildly from state to state