PDF calendar generation


As an alternative to those Christmas "round robin" letters, my mum likes to create a calendar to send to relatives. This year, I rewrote the original crufty PHP script in Python:

usage:  pdfcalendar [options]

Generates a simple PDF calendar for the specified year, one page per month,
optionally using images in the specified directory.

To use images, use the --dir argument to specify a directory (or leave empty
to use the current directory) with the structure:

    |-- 01
    |   |-- s6000001.jpg
    |-- 02
    |   |-- s6000002.jpg
    |   \\-- s6000003.jpg
    |-- 03
    |   \\-- s6000004.jpg

Images will be included on the appropriate month's page. If multiple images
for a month exist, they are tiled horizontally.

  -h, --help            show this help message and exit
  -d DIR, --dir=DIR     Directory to source images (Default: '.')
  -y YEAR, --year=YEAR  Year of calendar (Default: next year)
  -o FILE, --output=FILE
                        Output file (Default: calendar.pdf)
  -q, --quiet           Don't print status messages to STDERR

GPL3+ source available in Git. Would love a patch that tiles images in a square if anyone has a spare moment.

Comments (18)


Very neat. I need to figure out why it won't localized the calendar though. The calendar module is a bit vauge, but I think it is supposed to give a localized calendar.

Dec. 18, 2007, 5:23 p.m. #

You could simply get a localized version if you add this patch:

diff --git a/pdfcalendar b/pdfcalendar
index eb28368..f13e44f 100755
--- a/pdfcalendar
+++ b/pdfcalendar
@@ -24,6 +24,7 @@ import os.path
import calendar
import commands
import tempfile
+import locale

from glob import glob
from datetime import datetime
@@ -72,6 +73,7 @@ def main():
global options
options, args = parser.parse_args()

+ locale.setlocale(locale.LC_ALL, '')
images = find_images()
return generate_calendar(images)

Dec. 18, 2007, 5:28 p.m. #

I had <a href="http://git.chr…" rel="nofollow">checked that into Git</a> a couple of minutes before you posted. Thanks anyway.

Dec. 18, 2007, 5:52 p.m. #

Now localization works fine, thanks! I think I am going to use this, but then I'll still end up customizing a few things; the font, and then I'll try using two-letter day abbreviations, that's customary in swedish. I'll titlecase month names (the locale specifies lowercase month names, but in this case the names are free-standing meaning you titlecase them). I suspect this thing can't be generalized totally.

Dec. 18, 2007, 8:08 p.m. #

Suggestion: week numbers.

Dec. 19, 2007, 2:25 a.m. #

Hay, this looks cool. I've tried running it but it comes back with an error, can any one help?

<code>$ ./pdfcalendar --year=2008 --dir=images --output=test.pdf
January...Traceback (most recent call last):
File "./pdfcalendar", line 184, in ?
File "./pdfcalendar", line 83, in main
return generate_calendar(images)
File "./pdfcalendar", line 174, in generate_calendar
File "./pdfcalendar", line 120, in add_days
for row_num, row in enumerate(calendar.monthcalendar(options.year, month + 1)):
File "/usr/lib/python2.4/calendar.py", line 119, in monthcalendar
day1, ndays = monthrange(year, month)
File "/usr/lib/python2.4/calendar.py", line 112, in monthrange
day1 = weekday(year, month, 1)
File "/usr/lib/python2.4/calendar.py", line 105, in weekday
return datetime.date(year, month, day).weekday()
TypeError: an integer is required

Dec. 21, 2007, 1:37 p.m. #

Oh, silly, I wasn't coercing the argument --year to an int. Fixed in Git, thanks.

Dec. 21, 2007, 2:21 p.m. #

Implemented yesterday (I think).

Dec. 21, 2007, 2:22 p.m. #

Quick reply! :-)

Just tried running ./pdfcalendar --year=2008 and it makes a calender but doesn't put images into it.

Running ./pdfcalendar --year=2008 --dir=images
gives another error.

January...Traceback (most recent call last):
File "./pdfcalendar", line 184, in ?
File "./pdfcalendar", line 83, in main
return generate_calendar(images)
File "./pdfcalendar", line 176, in generate_calendar
File "./pdfcalendar", line 164, in add_images
img = ImageReader(temp)
File "/usr/lib/python2.4/site-packages/reportlab/lib/utils.py", line 532, in __init__
self._image = PIL.Image.open(self.fp)
File "/usr/lib/python2.4/site-packages/PIL/Image.py", line 1745, in open
raise IOError("cannot identify image file")
IOError: cannot identify image file fileName=/tmp/tmpu7kGXE

Dec. 21, 2007, 2:34 p.m. #

Do you have Imagemagick installed? Try running ``% convert -version``.

Dec. 21, 2007, 2:41 p.m. #

Good point... I forgot to install when rebuildy system.

Running ./pdfcalendar --year=2008 --dir=images works
but ./pdfcalendar --year=2008 does not put any images into the pdf

Dec. 21, 2007, 2:54 p.m. #

Forgot to say.
It would be really good if there was some sort of header and footer on the pages.
Would allow you to link back to a website/organisation that made the calender.

Dec. 21, 2007, 2:55 p.m. #


<code> -d DIR, --dir=DIR Directory to source images (Default: '.')</code>

In other words, without ``--dir=images`` it will not look in your ``images/`` folder.

Regarding, the header and footer, I've just implemented it and pushed it remotely.

Dec. 21, 2007, 3:08 p.m. #

Some of my images seem to have names ending in JPG. I would suggest making it case insensitive in file endings or checking against lower-case version of filename, or adding JPG and JPEG to allowed file types.

I used the latter solution.


Dec. 21, 2007, 3:18 p.m. #

I was using ``glob`` which follows the case-sensitivity of your filesystem. I've rolled my own glob which is case insensitive now and pushed this update.

Dec. 21, 2007, 3:31 p.m. #

Sorry, I didn't read enough...
I assumed as there was a default images folder, it looked in there. Reading above it only looks in '.' by default. May be its worth changing the default to be the images dir.

Header and foots are working well. :-) How difficult would it be to add a logo to the header?

Dec. 21, 2007, 4:21 p.m. #

Well, if you were running with the pdfcalendar binary somewhere in your PATH, then having to put things in an "images" folder as the default would be a little irritating IMHO.

And a logo wouldn't be too complicated.. send me a patch!

Dec. 21, 2007, 4:30 p.m. #

Not sure where to start out with patching. I've not written any code before. But I know python is one of the easier ones. Any pointers?

On another note, I've used a bunch of images (7MB in total) to make a calendar, but the calendar comes out to be 40MB! Any idea what that's all about?

Jan. 13, 2008, 10:08 p.m. #