#!/usr/bin/python # -*- coding: utf-8 -*- # author: Martin Michel # created: May 2008 # version: 0.1 # requires: # • Mac OS X 10.5 Leopard """ This script produces colorful PDF calendars (in DIN A4 Landscape format). It requires Mac OS X 10.5 Leopard. You can easily: - set the calendar year - set the calendar color - set the calendar language - set the tint value to highlight Sundays and Saturdays - activate/decativate highlighted Saturdays - activate/deactivate display of week numbers - choose your own Sunday The following languages are currently supported: - Danish - Dutch - English - Estonian - Finnish - French - German - Hungarian - Italian - Norwegian - Portuguese - Romanian - Spanish - Swedish The calendar currently uses ISO week numbers, not US week numbers. If you need additional languages or formats: The corresponding dictionaries (self._langdb and self._formatdb) can be easily modified. Just remember, that all entered UTF-8 characters will be converted to Mac Roman text encoding. Therefor not all characters can be used. Sample usage: pdfcal = PDFCalendar() pdfcal.setyear(2013) pdfcal.setlang('English') # RGB values + alpha value pdfcal.setcolor(1, 0, 0, 1) pdfcal.actsaturday() pdfcal.actweeknos() pdfcal.setfilepath('/Users/martin/Desktop/cal2013.pdf') pdfcal.create() """ import os from datetime import * import calendar from CoreGraphics import * class PDFCalendar: def __init__(self): # dictionary storing the available language versions self._langdb = {'German': {'monthnames': { 1 : u'Januar', 2 : u'Februar', 3 : u'März', 4 : u'April', 5 : u'Mai', 6 : u'Juni', 7 : u'Juli', 8 : u'August', 9 : u'September', 10 : u'Oktober', 11 : u'November', 12 : u'Dezember' }, 'daynames': { 1 : u'Mon', 2 : u'Die', 3 : u'Mit', 4 : u'Don', 5 : u'Fre', 6 : u'Sam', 7 : u'Son', }, 'notes': u'Notizen' }, 'English': {'monthnames': { 1 : u'January', 2 : u'February', 3 : u'March', 4 : u'April', 5 : u'May', 6 : u'June', 7 : u'July', 8 : u'August', 9 : u'September', 10 : u'October', 11 : u'November', 12 : u'December' }, 'daynames': { 1 : u'Mon', 2 : u'Tue', 3 : u'Wed', 4 : u'Thu', 5 : u'Fri', 6 : u'Sat', 7 : u'Sun', }, 'notes': u'Notes' }, 'French': {'monthnames': { 1 : u'Janvier', 2 : u'Février', 3 : u'Mars', 4 : u'Avril', 5 : u'Mai', 6 : u'Juin', 7 : u'Juillet', 8 : u'Août', 9 : u'Septembre', 10 : u'Octobre', 11 : u'Novembre', 12 : u'Décembre' }, 'daynames': { 1 : u'Lun', 2 : u'Mar', 3 : u'Mer', 4 : u'Jeu', 5 : u'Ven', 6 : u'Sam', 7 : u'Dim', }, 'notes': u'Notation' }, 'Spanish': {'monthnames': { 1 : u'Enero', 2 : u'Febrero', 3 : u'Marzo', 4 : u'Abril', 5 : u'Mayo', 6 : u'Junio', 7 : u'Julio', 8 : u'Agosto', 9 : u'Septiembre', 10 : u'Octubre', 11 : u'Noviembre', 12 : u'Diciembre' }, 'daynames': { 1 : u'Lun', 2 : u'Mar', 3 : u'Mié', 4 : u'Jue', 5 : u'Vie', 6 : u'Sáb', 7 : u'Dom', }, 'notes': u'Apuntes' }, 'Italian': {'monthnames': { 1 : u'Gennaio', 2 : u'Febbraio', 3 : u'Marzo', 4 : u'Aprile', 5 : u'Può', 6 : u'Giugno', 7 : u'Luglio', 8 : u'Agosto', 9 : u'Settembre', 10 : u'Ottobre', 11 : u'Novembre', 12 : u'Dicembre' }, 'daynames': { 1 : u'Lun', 2 : u'Mar', 3 : u'Mer', 4 : u'Gio', 5 : u'Ven', 6 : u'Sab', 7 : u'Dom', }, 'notes': u'Note' }, 'Finnish': {'monthnames': { 1 : u'Tammikuu', 2 : u'Helmikuu', 3 : u'Maaliskuu', 4 : u'Huhtikuu', 5 : u'Toukokuu', 6 : u'Kesäkuu', 7 : u'Heinäkuu', 8 : u'Elokuu', 9 : u'Syyskuu', 10 : u'Lokakuu', 11 : u'Marraskuu', 12 : u'Joulukuu' }, 'daynames': { 1 : u'Maa', 2 : u'Tii', 3 : u'Kes', 4 : u'Tor', 5 : u'Per', 6 : u'Lau', 7 : u'Sun', }, 'notes': u'Notes' }, 'Swedish': {'monthnames': { 1 : u'Januari', 2 : u'Februari', 3 : u'Mars', 4 : u'April', 5 : u'Maj', 6 : u'Juni', 7 : u'Juli', 8 : u'Augusti', 9 : u'September', 10 : u'Oktober', 11 : u'November', 12 : u'December' }, 'daynames': { 1 : u'Mån', 2 : u'Tis', 3 : u'Ons', 4 : u'Tor', 5 : u'Fre', 6 : u'Lör', 7 : u'Sön', }, 'notes': u'Notes' }, 'Norwegian': {'monthnames': { 1 : u'Januar', 2 : u'Februar', 3 : u'Mars', 4 : u'April', 5 : u'Mai', 6 : u'Juni', 7 : u'Juli', 8 : u'August', 9 : u'September', 10 : u'Oktober', 11 : u'November', 12 : u'Desember' }, 'daynames': { 1 : u'Man', 2 : u'Tir', 3 : u'Ons', 4 : u'Tor', 5 : u'Fre', 6 : u'Lør', 7 : u'Søn', }, 'notes': u'Notizen' }, 'Danish': {'monthnames': { 1 : u'Januar', 2 : u'Februar', 3 : u'Marts', 4 : u'April', 5 : u'Maj', 6 : u'Juni', 7 : u'Juli', 8 : u'August', 9 : u'September', 10 : u'Oktober', 11 : u'November', 12 : u'December' }, 'daynames': { 1 : u'Man.', 2 : u'Tir.', 3 : u'Ons.', 4 : u'Tor.', 5 : u'Fre.', 6 : u'Lør.', 7 : u'Søn.', }, 'notes': u'Noter' }, 'Estonian': {'monthnames': { 1 : u'Jaanuar', 2 : u'Veebruar', 3 : u'Märts', 4 : u'Aprill', 5 : u'Maj', 6 : u'Juuni', 7 : u'Juuli', 8 : u'August', 9 : u'September', 10 : u'Oktoober', 11 : u'November', 12 : u'Detsember' }, 'daynames': { 1 : u'E', 2 : u'T', 3 : u'K', 4 : u'N', 5 : u'R', 6 : u'L', 7 : u'P', }, 'notes': u'' }, 'Hungarian': {'monthnames': { 1 : u'Január', 2 : u'Február', 3 : u'Március', 4 : u'Április', 5 : u'Május', 6 : u'Június', 7 : u'Július', 8 : u'Augusztus', 9 : u'Szeptember', 10 : u'Október', 11 : u'November', 12 : u'December' }, 'daynames': { 1 : u'H', 2 : u'K', 3 : u'Sze', 4 : u'Cs', 5 : u'P', 6 : u'Szo', 7 : u'V', }, 'notes': u'' }, 'Dutch': {'monthnames': { 1 : u'Januari', 2 : u'Februari', 3 : u'Maart', 4 : u'April', 5 : u'Mai', 6 : u'Juni', 7 : u'Juli', 8 : u'Augustus', 9 : u'September', 10 : u'Oktober', 11 : u'November', 12 : u'December' }, 'daynames': { 1 : u'Ma', 2 : u'Di', 3 : u'Wo', 4 : u'Do', 5 : u'Vr', 6 : u'Za', 7 : u'Zo', }, 'notes': u'Notities' }, 'Portuguese': {'monthnames': { 1 : u'Janeiro', 2 : u'Fevereiro', 3 : u'Março', 4 : u'Abril', 5 : u'Maio', 6 : u'Junho', 7 : u'Julho', 8 : u'Agosto', 9 : u'Setembro', 10 : u'Outubro', 11 : u'Novembro', 12 : u'Dezembro' }, 'daynames': { 1 : u'Seg', 2 : u'Ter', 3 : u'Qua', 4 : u'Qui', 5 : u'Sex', 6 : u'Sáb', 7 : u'Dom', }, 'notes': u'Notas' }, 'Romanian': {'monthnames': { 1 : u'Ianuarie', 2 : u'Februarie', 3 : u'Martie', 4 : u'Aprilie', 5 : u'Mai', 6 : u'Iunie', 7 : u'Iulie', 8 : u'August', 9 : u'Septembrie', 10 : u'Octombrie', 11 : u'Noiembrie', 12 : u'Decembrie' }, 'daynames': { 1 : u'L', 2 : u'Ma', 3 : u'Mi', 4 : u'J', 5 : u'V', 6 : u'S', 7 : u'D', }, 'notes': u'Note' } } # dictionary storing formats and their details # all values must be given in points (pt) self._formatdb = {'DIN A4 Landscape': {'pagewidth' : 842, 'pageheight' : 595, 'marginleft' : 14.17, 'margintop' : 14.17, 'monthboxheight' : 17.57, 'monthboxwidth' : 104.88, 'dayboxheight' : 17.57, 'dayboxwidth' : 104.88, 'boxgap' : 12.75, 'monthnameindent' : 11, 'fontname' : 'Andale Mono', 'monthnamesize' : 13, 'monthnamespacing' : -0.8, # default: white color 'monthnamecolor' : (1, 1, 1, 1), 'weeknosize' : 12, 'weeknospacing' : -0.8, # default: grey color 'weeknocolor' : (0.733, 0.733, 0.745, 1), 'weeknoindent' : 45.5, # default: black color 'daylinecolor' : (0, 0, 0, 1), 'daylinewidth' : 0.25, 'daylinesunwidth' : 0.5, 'monthdaysize' : 8, 'monthdayspacing' : -0.8, # default: black color 'monthdaycolor' : (0, 0, 0, 1), 'daynamesize' : 8, 'daynamespacing' : -0.8, 'daynamecolor' : (0, 0, 0, 1), 'daynameindent' : 11, 'daynosize' : 4, 'daynospacing' : -0.2, 'daynocolor' : (0, 0, 0, 1), 'daynoindent' : 98, 'bigyearsize' : 42, 'bigyearspacing' : 1.8, 'notessize' : 8, 'notesspacing' : 1, 'notescolor' : (0, 0, 0, 1) }, 'US Legal Landscape': {'pagewidth' : 1008, 'pageheight' : 612, 'marginleft' : 20, 'margintop' : 20, 'monthboxheight' : 17.57, 'monthboxwidth' : 125, 'dayboxheight' : 17.57, 'dayboxwidth' : 125, 'boxgap' : 14, 'monthnameindent' : 11, 'fontname' : 'Andale Mono', 'monthnamesize' : 13, 'monthnamespacing' : -0.8, # default: white color 'monthnamecolor' : (1, 1, 1, 1), 'weeknosize' : 12, 'weeknospacing' : -0.8, # default: grey color 'weeknocolor' : (0.733, 0.733, 0.745, 1), 'weeknoindent' : 55.62, # default: black color 'daylinecolor' : (0, 0, 0, 1), 'daylinewidth' : 0.25, 'daylinesunwidth' : 0.5, 'monthdaysize' : 8, 'monthdayspacing' : -0.8, # default: black color 'monthdaycolor' : (0, 0, 0, 1), 'daynamesize' : 8, 'daynamespacing' : -0.8, 'daynamecolor' : (0, 0, 0, 1), 'daynameindent' : 11, 'daynosize' : 4, 'daynospacing' : -0.2, 'daynocolor' : (0, 0, 0, 1), 'daynoindent' : 118.12, 'bigyearsize' : 48, 'bigyearspacing' : 5, 'notessize' : 8, 'notesspacing' : 1, 'notescolor' : (0, 0, 0, 1) } } # calendar year self._year = 2008 # calendar language self._lang = 'English' # calendar color self._color = (0, 0, 1, 1) # on which day is sunday? (1=Monday, 7=Sunday) self._sunday = 7 # tint used to highlight sundays self._suntint = 0.3 # are saturdays also highlighted? self._actsaturday = True # on which day is saturday? self._saturday = self._sunday - 1 # tint used to highlight saturdays self._sattint = 0.1 # are week numbers displayed? self._actweeknos = True # week numbers are displayed on which day of the week? self._weeknoday = 3 # calendar format self._format = 'DIN A4 Landscape' # output file path for the PDF self._filepath = None def setyear(self, year): """Sets the year to be used for the calendar. The year must be passed as a four digit integer, e.g. 2008.""" self._year = year def getyear(self): """Returns the currently set calendar year.""" return self._year def setlang(self, lang): """Sets the language used for the calendar.""" self._lang = lang def getlang(self): """Returns the currently set calendar language.""" return self._lang def getlangs(self): """Returns a list of all available calendar languages.""" return self._langdb.keys() def haslang(self, lang): """Indicates if a given calendar language exists.""" return lang in self.getlangs() def setcolor(self, red, green, blue, alpha): """Sets the calendar color (RGB). The values for red, green, blue and alpha must be passed as floating point numbers between 0 and 1.""" self._color = (red, green, blue, alpha) def getcolor(self): """Returns the currently set calendar color (RGB). The color is returned as a tuple: (red, green, blue, alpha)""" return self._color def setformat(self, format): """Sets the format to be used for the calendar (e.g. DINA4L).""" self._format = format def getformat(self): """Returns the currently set calendar format.""" return self._format def getformats(self): """Returns a list of all available calendar formats.""" return self._formatdb.keys() def hasformat(self, format): """Indicates if a given calendar format exists.""" return format in self.getformats() def setsuntint(self, suntint): """Sets the tint value to highlight Sundays. The tint value must be passed as a floating point number between 0 and 1 (e.g. 0.3 = 30%).""" self._suntint = suntint def getsuntint(self): """Returns the currently set tint value to highlight Sundays.""" return self._suntint def setsunday(self, sunday): """Sets the weekday to be used as Sunday. The weekday must be passed as an integer number between 1 and 7 (1=Monday, 7=Sunday).""" self._sunday = sunday self._saturday = sunday - 1 if self._saturday == 0: self._saturday = 7 def getsunday(self): """Returns the weekday currently used as Sunday.""" return self._sunday def actsaturday(self): """Activates additional highlighting of Saturdays.""" self._actsaturday = True def deactsaturday(self): """Deactivates additional highlighting of Saturdays.""" self._actsaturday = False def setsattint(self, sattint): """Sets the tint value to highlight Saturdays. The tint value must be passed as a floating point number between 0 and 1 (e.g. 0.3 = 30%).""" self._sattint = sattint def getsattint(self): """Returns the currently set tint value to highlight Saturdays.""" return self._sattint def actweeknos(self): """Activates display of week numbers.""" self._actweeknos = True def deactweeknos(self): """Deactivates display of week numbers.""" self._actweeknos = False def setweeknoday(self, weeknoday): """Sets the weekday where the week numbers are displayed. The weekday must be passed as an integer number between 1 and 7 (1=Monday, 7=Sunday).""" self._weeknoday = weeknoday def getweeknoday(self): """Returns the weekday on which week numbers are displayed.""" return self._weeknoday def setfilepath(self, filepath): """Sets the output file path for the produced PDF calendar.""" self._filepath = filepath def getfilepath(self): """Returns the currently set output file path.""" return self._filepath def getminyear(self): """Returns the smallest year number allowed.""" return MINYEAR def getmaxyear(self): """Returns the largest year number allowed.""" return MAXYEAR def create(self): """Creates the PDF calendar according to the chosen settings.""" # initializing important format values pagewidth = self._formatdb[self._format] ['pagewidth'] pageheight = self._formatdb[self._format] ['pageheight'] margintop = self._formatdb[self._format] ['marginleft'] marginleft = self._formatdb[self._format] ['marginleft'] monthboxheight = self._formatdb[self._format] ['monthboxheight'] monthboxwidth = self._formatdb[self._format] ['monthboxwidth'] monthnameindent = self._formatdb[self._format] ['monthnameindent'] dayboxheight = self._formatdb[self._format] ['dayboxheight'] dayboxwidth = self._formatdb[self._format] ['dayboxwidth'] # vertical space between the month columns II-II boxgap = self._formatdb[self._format] ['boxgap'] fontname = self._formatdb[self._format] ['fontname'] monthnamesize = self._formatdb[self._format] ['monthnamesize'] monthnamespacing = self._formatdb[self._format] ['monthnamespacing'] monthnamecolor = self._formatdb[self._format] ['monthnamecolor'] weeknosize = self._formatdb[self._format] ['weeknosize'] weeknospacing = self._formatdb[self._format] ['weeknospacing'] weeknocolor = self._formatdb[self._format] ['weeknocolor'] weeknoindent = self._formatdb[self._format] ['weeknoindent'] daylinecolor = self._formatdb[self._format] ['daylinecolor'] daylinewidth = self._formatdb[self._format] ['daylinewidth'] daylinesunwidth = self._formatdb[self._format] ['daylinesunwidth'] monthdaysize = self._formatdb[self._format] ['monthdaysize'] monthdayspacing = self._formatdb[self._format] ['monthdayspacing'] monthdaycolor = self._formatdb[self._format] ['monthdaycolor'] daynamesize = self._formatdb[self._format] ['daynamesize'] daynamespacing = self._formatdb[self._format] ['daynamespacing'] daynamecolor = self._formatdb[self._format] ['daynamecolor'] daynameindent = self._formatdb[self._format] ['daynameindent'] daynosize = self._formatdb[self._format] ['daynosize'] daynospacing = self._formatdb[self._format] ['daynospacing'] daynocolor = self._formatdb[self._format] ['daynocolor'] daynoindent = self._formatdb[self._format] ['daynoindent'] bigyearsize = self._formatdb[self._format] ['bigyearsize'] bigyearspacing = self._formatdb[self._format] ['bigyearspacing'] notessize = self._formatdb[self._format] ['notessize'] notesspacing = self._formatdb[self._format] ['notesspacing'] notescolor = self._formatdb[self._format] ['notescolor'] # monthdays is a list containing the count of days of every month in the chosen calendar year # e.g. [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] for the year 2008 monthdays = [calendar.monthrange(year,month)[1] for year in [self._year] for month in range(1,13)] # creating an empty PDF document pagebox = CGRectMake(0, 0, pagewidth, pageheight) pdfcontext = CGPDFContextCreateWithFilename(self._filepath, pagebox) if not pdfcontext: errmsg = "Could not create PDF file at '%s'" % (self._filepath) raise IOError, errmsg # setting the color space to generic RGB pdfcontext.setFillColorSpace(CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)) # counter needed to enumerate all days of a year (e.g. 1-365) curdaycounter = 0 for page in range(1, 3): if page == 1: firstmonth = 1 lastmonth = 7 elif page == 2: firstmonth = 7 lastmonth = 13 pdfcontext.beginPage(pagebox) pdfcontext.saveGState() # CREATING THE COLORED BOXES CONTAINING THE MONTH NAMES ypos = (pageheight - margintop - monthboxheight) xpos = marginleft # color used for the month boxes red, green, blue, alpha = self._color # we need to create 6 month boxes on every page (January - June, July - December) for i in range(0, 6): pdfcontext.setRGBFillColor(red, green, blue, alpha) pdfcontext.addRect(CGRectMake(xpos, ypos, monthboxwidth, monthboxheight)) pdfcontext.fillPath() # moving on to the next horizontal starting position xpos += (monthboxwidth + boxgap) # INSERTING THE MONTH NAMES INTO THE COLORED MONTH BOXES # horinzontally centering the month name into the colored month box ypos = (pageheight - margintop - (monthboxheight / 2.0) - (monthnamesize / 3.0)) # month names are indented a little bit xpos = marginleft + monthnameindent # font color for the month names red, green, blue, alpha = monthnamecolor for monthno in range(firstmonth, lastmonth): monthname = self._langdb[self._lang] ['monthnames'] [monthno] # month name, e.g. '04 April' monthname = u'%02d %s' % (monthno, monthname) monthname = monthname.encode('macroman') pdfcontext.setTextDrawingMode (kCGTextFill) pdfcontext.setTextMatrix(CGAffineTransformIdentity) pdfcontext.selectFont(fontname, monthnamesize, kCGEncodingMacRoman) pdfcontext.setCharacterSpacing(monthnamespacing) pdfcontext.setRGBFillColor(red, green, blue, alpha) pdfcontext.showTextAtPoint(xpos, ypos, monthname, len(monthname)) # moving on to the next horizontal starting position xpos += (monthboxwidth + boxgap) # INSERTING CONTRASTING COLORED SATURDAYS & SUNDAYS xpos = marginleft # color and tint values to highlight the Sunday & Saturday boxes red, green, blue = self._color[0:3] sunalpha = self._suntint satalpha = self._sattint for monthno in range(firstmonth, lastmonth): ypos = (pageheight - margintop - monthboxheight - dayboxheight) # iterating over all days of the current month for daynum in range(1, monthdays[monthno-1]+1): # getting the weekday number (1-7, 1=Monday, 7=Sunday) weekday = datetime(self._year, monthno , daynum).isoweekday() if weekday == self._sunday: pdfcontext.setRGBFillColor(red, green, blue, sunalpha) pdfcontext.addRect(CGRectMake(xpos, ypos, dayboxwidth, dayboxheight)) pdfcontext.fillPath() elif weekday == self._saturday: if self._actsaturday == True: pdfcontext.setRGBFillColor(red, green, blue, satalpha) pdfcontext.addRect(CGRectMake(xpos, ypos, dayboxwidth, dayboxheight)) pdfcontext.fillPath() # climbing down the current month column ypos -= dayboxheight # moving on to the next month column xpos += dayboxwidth + boxgap # INSERTING WEEK NUMBERS if self._actweeknos: xpos = marginleft + weeknoindent # font color for week numbers red, green, blue, alpha = weeknocolor for monthno in range(firstmonth, lastmonth): ypos = (pageheight - margintop - monthboxheight - (dayboxheight / 2.0) - (12 / 3.0)) for daynum in range(1, monthdays[monthno-1]+1): weekday = date(self._year, monthno , daynum).isoweekday() if weekday == self._weeknoday: weeknum = date(self._year, monthno , daynum).isocalendar() [1] weeknum = '%02d' % (weeknum) pdfcontext.setTextDrawingMode (kCGTextFill) pdfcontext.setTextMatrix(CGAffineTransformIdentity) pdfcontext.selectFont(fontname, weeknosize, kCGEncodingMacRoman) pdfcontext.setCharacterSpacing(weeknospacing) pdfcontext.setRGBFillColor(red, green, blue, alpha) pdfcontext.showTextAtPoint(xpos, ypos, weeknum, len(weeknum)) # climbing down the current month column ypos -= dayboxheight # moving on to the next month column xpos += dayboxwidth + boxgap # INSERTING DAY DELIMITING LINES xpos = marginleft # color used for day delimiting lines red, green, blue, alpha = daylinecolor for monthno in range(firstmonth, lastmonth): ypos = (pageheight - margintop - monthboxheight) for i in range(0, monthdays[monthno-1] + 1): if i > 0: weekday = date(self._year, monthno, i).isoweekday() # the bottom line of a Sunday is slightly stronger if weekday == self._sunday: pdfcontext.setLineWidth(daylinesunwidth) else: pdfcontext.setLineWidth(daylinewidth) else: pdfcontext.setLineWidth(daylinewidth) pdfcontext.setRGBFillColor(red, green, blue, alpha) pdfcontext.beginPath() pdfcontext.moveToPoint(xpos, ypos) pdfcontext.addLineToPoint(dayboxwidth + xpos, ypos) pdfcontext.closePath() pdfcontext.strokePath() # climbing down the current month column ypos -= dayboxheight # moving on to the next month column xpos += dayboxwidth + boxgap # INSERTING NUMBERS (0-31) xpos = marginleft # font color for the month days red, green, blue, alpha = monthdaycolor for monthno in range(firstmonth, lastmonth): # this crazy calculation horizontally centers the day number in the day box ypos = (pageheight - margintop - monthboxheight - (dayboxheight / 2.0) - (monthdaysize / 3.0)) daycounter = 1 for i in range(0, monthdays[monthno-1]): if daycounter < 10: daynum = ' %d' % (daycounter) else: daynum = '%d' % (daycounter) pdfcontext.setTextDrawingMode (kCGTextFill) pdfcontext.setTextMatrix(CGAffineTransformIdentity) pdfcontext.selectFont(fontname, monthdaysize, kCGEncodingMacRoman) pdfcontext.setCharacterSpacing(monthdayspacing) pdfcontext.setRGBFillColor(red, green, blue, alpha) pdfcontext.showTextAtPoint(xpos, ypos, daynum, len(daynum)) daycounter += 1 # climbing down the month column ypos -= dayboxheight # moving on to the next month column xpos += dayboxwidth + boxgap # INSERTING DAY NAMES xpos = marginleft + daynameindent # font color for day names red, green, blue, alpha = daynamecolor for monthno in range(firstmonth, lastmonth): # this crazy calculation horizontally centers the day name in the day box ypos = (pageheight - margintop - monthboxheight - (dayboxheight / 2.0) - (8 / 3.0)) daycounter = 1 for i in range(0, monthdays[monthno-1]): weekday = datetime(self._year, monthno, daycounter).isoweekday() dayname = self._langdb[self._lang] ['daynames'] [weekday] dayname = dayname.encode('macroman') pdfcontext.setTextDrawingMode (kCGTextFill) pdfcontext.setTextMatrix(CGAffineTransformIdentity) pdfcontext.selectFont(fontname, daynamesize, kCGEncodingMacRoman) pdfcontext.setCharacterSpacing(daynamespacing) pdfcontext.setRGBFillColor(red, green, blue, alpha) pdfcontext.showTextAtPoint(xpos, ypos, dayname, len(dayname)) daycounter += 1 # climbing down the current month column ypos -= dayboxheight # moving on to the next month column xpos += dayboxwidth + boxgap # INSERTING DAY NUMBER (0-365) xpos = marginleft + daynoindent # font color for the total day numbers red, green, blue, alpha = daynocolor for monthno in range(firstmonth, lastmonth): ypos = (pageheight - margintop - monthboxheight - (dayboxheight / 2.0) - (4 / 3.0)) for i in range(0, monthdays[monthno-1]): curdaycounter += 1 daynum = "%03d" % (curdaycounter) pdfcontext.setTextDrawingMode (kCGTextFill) pdfcontext.setTextMatrix(CGAffineTransformIdentity) pdfcontext.selectFont(fontname, daynosize, kCGEncodingMacRoman) pdfcontext.setCharacterSpacing(daynospacing) pdfcontext.setRGBFillColor(red, green, blue, alpha) pdfcontext.showTextAtPoint(xpos, ypos, daynum, len(daynum)) daycounter += 1 # climbing down the current month column ypos -= dayboxheight # moving on to the next month column xpos +=dayboxwidth + boxgap # INSERTING THE BIG YEAR NUMBER IN THE UPPER RIGHT CORNER xpos = marginleft + (6 * monthboxwidth) + (6 * boxgap) ypos = pageheight - margintop - bigyearsize + (bigyearsize / 3.36) year = str(self._year) red, green, blue, alpha = self._color pdfcontext.setTextDrawingMode (kCGTextFill) pdfcontext.setTextMatrix(CGAffineTransformIdentity) pdfcontext.selectFont(fontname, bigyearsize, kCGEncodingMacRoman) pdfcontext.setCharacterSpacing(bigyearspacing) pdfcontext.setRGBFillColor(red, green, blue, alpha) pdfcontext.showTextAtPoint(xpos, ypos, year, len(year)) # INSERTING THE NOTES TITLE xpos = marginleft + (6 * monthboxwidth) + (6 * boxgap) ypos = (pageheight - margintop - monthboxheight - (2 * dayboxheight) - (dayboxheight / 2.0) - (notessize / 3.0)) # font color for the notes title red, green, blue, alpha = notescolor notes = self._langdb[self._lang] ['notes'] notes = notes.encode('macroman') pdfcontext.setTextDrawingMode (kCGTextFill) pdfcontext.setTextMatrix(CGAffineTransformIdentity) pdfcontext.selectFont(fontname, notessize, kCGEncodingMacRoman) pdfcontext.setCharacterSpacing(notesspacing) pdfcontext.setRGBFillColor(red, green, blue, alpha) pdfcontext.showTextAtPoint(xpos, ypos, notes, len(notes)) # INSERTING THE NOTES SECTION xpos = marginleft + (6 * monthboxwidth) + (6 * boxgap) ypos = (pageheight - margintop - monthboxheight - (3 * dayboxheight)) red, green, blue, alpha = daylinecolor for i in range(0, 29): pdfcontext.setLineWidth(daylinewidth) pdfcontext.setRGBFillColor(red, green, blue, alpha) pdfcontext.beginPath() pdfcontext.moveToPoint(xpos, ypos) pdfcontext.addLineToPoint(dayboxwidth + xpos, ypos) pdfcontext.closePath() pdfcontext.strokePath() ypos -= dayboxheight pdfcontext.restoreGState() pdfcontext.endPage() pdfcontext.finish() del pdfcontext if __name__ == '__main__': # test routine shortname = os.getlogin() pdfcal = PDFCalendar() langs = pdfcal.getlangs() pdfcal.setyear(2013) pdfcal.setcolor(1, 1, 0, 1) for lang in langs: filepath = '/Users/%s/Desktop/cal%s_%s.pdf' % (shortname, 2013, lang) if not os.path.exists(filepath): pdfcal.setlang(lang) pdfcal.setfilepath(filepath) pdfcal.create()