Code

Translations. French translation minor update.
[inkscape.git] / share / extensions / svgcalendar.py
1 #!/usr/bin/env python
3 '''
4 calendar.py
5 A calendar generator plugin for Inkscape, but also can be used as a standalone
6 command line application.
8 Copyright (C) 2008 Aurelio A. Heckert <aurium(a)gmail.com>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 '''
25 __version__ = "0.2"
27 import inkex, simplestyle, re, calendar
28 from datetime import *
30 class SVGCalendar (inkex.Effect):
32     def __init__(self):
33         inkex.Effect.__init__(self)
34         self.OptionParser.add_option("--tab",
35           action="store", type="string",
36           dest="tab")
37         self.OptionParser.add_option("--month",
38           action="store", type="int",
39           dest="month", default=0,
40           help="Month to be generated. If 0, then the entry year will be generated.")
41         self.OptionParser.add_option("--year",
42           action="store", type="int",
43           dest="year", default=0,
44           help="Year to be generated. If 0, then the current year will be generated.")
45         self.OptionParser.add_option("--fill-empty-day-boxes",
46           action="store", type="inkbool",
47           dest="fill_edb", default=True,
48           help="Fill empty day boxes with next month days.")
49         self.OptionParser.add_option("--start-day",
50           action="store", type="string",
51           dest="start_day", default="sun",
52           help='Week start day. ("sun" or "mon")')
53         self.OptionParser.add_option("--weekend",
54           action="store", type="string",
55           dest="weekend", default="sat+sun",
56           help='Define the weekend days. ("sat+sun" or "sat" or "sun")')
57         self.OptionParser.add_option("--auto-organize",
58           action="store", type="inkbool",
59           dest="auto_organize", default=True,
60           help='Automatically set the size and positions.')
61         self.OptionParser.add_option("--months-per-line",
62           action="store", type="int",
63           dest="months_per_line", default=3,
64           help='Number of months side by side.')
65         self.OptionParser.add_option("--month-width",
66           action="store", type="string",
67           dest="month_width", default="6cm",
68           help='The width of the month days box.')
69         self.OptionParser.add_option("--month-margin",
70           action="store", type="string",
71           dest="month_margin", default="1cm",
72           help='The space between the month boxes.')
73         self.OptionParser.add_option("--color-year",
74           action="store", type="string",
75           dest="color_year", default="#888",
76           help='Color for the year header.')
77         self.OptionParser.add_option("--color-month",
78           action="store", type="string",
79           dest="color_month", default="#666",
80           help='Color for the month name header.')
81         self.OptionParser.add_option("--color-day-name",
82           action="store", type="string",
83           dest="color_day_name", default="#999",
84           help='Color for the week day names header.')
85         self.OptionParser.add_option("--color-day",
86           action="store", type="string",
87           dest="color_day", default="#000",
88           help='Color for the common day box.')
89         self.OptionParser.add_option("--color-weekend",
90           action="store", type="string",
91           dest="color_weekend", default="#777",
92           help='Color for the weekend days.')
93         self.OptionParser.add_option("--color-nmd",
94           action="store", type="string",
95           dest="color_nmd", default="#BBB",
96           help='Color for the next month day, in enpty day boxes.')
97         self.OptionParser.add_option("--month-names",
98           action="store", type="string",
99           dest="month_names", default='January February March April May June '+\
100                               'July August September October November December',
101           help='The month names for localization.')
102         self.OptionParser.add_option("--day-names",
103           action="store", type="string",
104           dest="day_names", default='Sun Mon Tue Wed Thu Fri Sat',
105           help='The week day names for localization.')
106         self.OptionParser.add_option("--encoding",
107           action="store", type="string",
108           dest="input_encode", default='utf-8',
109           help='The input encoding of the names.')
111     def validate_options(self):
112         #inkex.errormsg( self.options.input_encode )
113         # Convert string names lists in real lists:
114         m = re.match( '\s*(.*[^\s])\s*', self.options.month_names )
115         self.options.month_names = re.split( '\s+', m.group(1) )
116         m = re.match( '\s*(.*[^\s])\s*', self.options.day_names )
117         self.options.day_names = re.split( '\s+', m.group(1) )
118         # Validate names lists:
119         if len(self.options.month_names) != 12:
120           inkex.errormsg('The month name list "'+
121                          str(self.options.month_names)+
122                          '" is invalid. Using default.')
123           self.options.month_names = ['January','February','March',
124                                       'April',  'May',     'June',
125                                       'July',   'August',  'September',
126                                       'October','November','December']
127         if len(self.options.day_names) != 7:
128           inkex.errormsg('The day name list "'+
129                          str(self.options.day_names)+
130                          '" is invalid. Using default.')
131           self.options.day_names = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']
132         # Convert year 0 to current year:
133         if self.options.year == 0: self.options.year = datetime.today().year
134         # Year 1 starts it's week at monday, obligatorily
135         if self.options.year == 1: self.options.start_day = 'mon'
136         # Set the calendar start day:
137         if self.options.start_day=='sun':
138           calendar.setfirstweekday(6)
139         else:
140           calendar.setfirstweekday(0)
141         # Convert string numbers with unit to user space float numbers:
142         self.options.month_width  = inkex.unittouu( self.options.month_width )
143         self.options.month_margin = inkex.unittouu( self.options.month_margin )
145     # initial values:
146     month_x_pos = 0
147     month_y_pos = 0
149     def calculate_size_and_positions(self):
150         #month_margin month_width months_per_line auto_organize
151         self.doc_w = inkex.unittouu(self.document.getroot().get('width'))
152         self.doc_h = inkex.unittouu(self.document.getroot().get('height'))
153         if self.options.auto_organize:
154           if self.doc_h > self.doc_w:
155             self.months_per_line = 3
156           else:
157             self.months_per_line = 4
158         else:
159           self.months_per_line = self.options.months_per_line
160         #self.month_w = self.doc_w / self.months_per_line
161         if self.options.auto_organize:
162           self.month_w = (self.doc_w * 0.8) / self.months_per_line
163           self.month_margin = self.month_w / 10
164         else:
165           self.month_w = self.options.month_width
166           self.month_margin = self.options.month_margin
167         self.day_w = self.month_w / 7
168         self.day_h = self.month_w / 9
169         self.month_h = self.day_w * 7
170         if self.options.month == 0:
171           self.year_margin = (( self.doc_w + self.day_w -
172                                (self.month_w*self.months_per_line) -
173                                (self.month_margin*(self.months_per_line-1))
174                              ) / 2 ) #- self.month_margin
175         else:
176           self.year_margin = ( self.doc_w - self.month_w ) / 2
177         self.style_day = {
178           'font-size': str( self.day_w / 2 ),
179           'font-family': 'arial',
180           'text-anchor': 'middle',
181           'text-align': 'center',
182           'fill': self.options.color_day
183           }
184         self.style_weekend = self.style_day.copy()
185         self.style_weekend['fill'] = self.options.color_weekend
186         self.style_nmd = self.style_day.copy()
187         self.style_nmd['fill'] = self.options.color_nmd
188         self.style_month = self.style_day.copy()
189         self.style_month['fill'] = self.options.color_month
190         self.style_month['font-size'] = str( self.day_w / 1.5 )
191         self.style_month['font-weight'] = 'bold'
192         self.style_day_name = self.style_day.copy()
193         self.style_day_name['fill'] = self.options.color_day_name
194         self.style_day_name['font-size'] = str( self.day_w / 3 )
195         self.style_year = self.style_day.copy()
196         self.style_year['fill'] = self.options.color_year
197         self.style_year['font-size'] = str( self.day_w * 2 )
198         self.style_year['font-weight'] = 'bold'
200     def is_weekend(self, pos):
201         # weekend values: "sat+sun" or "sat" or "sun"
202         if self.options.start_day=='sun':
203           if self.options.weekend=='sat+sun' and pos==0: return True
204           if self.options.weekend=='sat+sun' and pos==6: return True
205           if self.options.weekend=='sat' and pos==6: return True
206           if self.options.weekend=='sun' and pos==0: return True
207         else:
208           if self.options.weekend=='sat+sun' and pos==5: return True
209           if self.options.weekend=='sat+sun' and pos==6: return True
210           if self.options.weekend=='sat' and pos==5: return True
211           if self.options.weekend=='sun' and pos==6: return True
212         return False
214     def in_line_month(self, cal):
215         cal2 = []
216         for week in cal:
217           for day in week:
218             if day != 0:
219               cal2.append(day)
220         return cal2
222     def write_month_header(self, g, m):
223         txt_atts = {'style': simplestyle.formatStyle(self.style_month),
224                     'x': str( (self.month_w - self.day_w) / 2 ),
225                     'y': str( self.day_h / 5 ) }
226         try:
227           inkex.etree.SubElement(g, 'text', txt_atts).text = unicode(self.options.month_names[m-1], self.options.input_encode)
228         except:
229           inkex.errormsg('You must select your correct system encode.')
230           exit(1)
231         gw = inkex.etree.SubElement(g, 'g')
232         week_x = 0
233         if self.options.start_day=='sun':
234           for wday in self.options.day_names:
235             txt_atts = {'style': simplestyle.formatStyle(self.style_day_name),
236                         'x': str( self.day_w * week_x ),
237                         'y': str( self.day_h ) }
238             try:
239               inkex.etree.SubElement(gw, 'text', txt_atts).text = unicode(wday, self.options.input_encode)
240             except:
241               inkex.errormsg('You must select your correct system encode.')
242               exit(1)
243             week_x += 1
244         else:
245           w2 = self.options.day_names[1:]
246           w2.append(self.options.day_names[0])
247           for wday in w2:
248             txt_atts = {'style': simplestyle.formatStyle(self.style_day_name),
249                         'x': str( self.day_w * week_x ),
250                         'y': str( self.day_h ) }
251             try:
252               inkex.etree.SubElement(gw, 'text', txt_atts).text = unicode(wday, self.options.input_encode)
253             except:
254               inkex.errormsg('You must select your correct system encode.')
255               exit(1)
256             week_x += 1
258     def create_month(self, m):
259         txt_atts = {
260           'transform': 'translate('+str(self.year_margin +
261                                        (self.month_w + self.month_margin) *
262                                         self.month_x_pos) +
263                                 ','+str((self.day_h * 4) +
264                                        (self.month_h * self.month_y_pos))+')',
265           'id': 'month_'+str(m)+'_'+str(self.options.year) }
266         g = inkex.etree.SubElement(self.year_g, 'g', txt_atts)
267         self.write_month_header(g, m)
268         gdays = inkex.etree.SubElement(g, 'g')
269         cal = calendar.monthcalendar(self.options.year,m)
270         if m == 1:
271           if self.options.year > 1:
272             before_month = \
273               self.in_line_month( calendar.monthcalendar(self.options.year-1, 12) )
274         else:
275           before_month = \
276             self.in_line_month( calendar.monthcalendar(self.options.year, m-1) )
277         if m == 12:
278           next_month = \
279             self.in_line_month( calendar.monthcalendar(self.options.year+1, 1) )
280         else:
281           next_month = \
282             self.in_line_month( calendar.monthcalendar(self.options.year, m+1) )
283         if len(cal) < 6: # add a line after the last week
284           cal.append([0,0,0,0,0,0,0])
285         if len(cal) < 6: # add a line before the first week (Feb 2009)
286           cal.reverse()
287           cal.append([0,0,0,0,0,0,0])
288           cal.reverse()
289         # How mutch before month days will be showed:
290         bmd = cal[0].count(0) + cal[1].count(0)
291         before = True
292         week_y = 0
293         for week in cal:
294           week_x = 0
295           for day in week:
296             style = self.style_day
297             if self.is_weekend(week_x): style = self.style_weekend
298             if day == 0: style = self.style_nmd
299             txt_atts = {'style': simplestyle.formatStyle(style),
300                         'x': str( self.day_w * week_x ),
301                         'y': str( self.day_h * (week_y+2) ) }
302             if day==0 and not self.options.fill_edb:
303               pass # draw nothing
304             elif day==0:
305               if before:
306                 inkex.etree.SubElement(gdays, 'text', txt_atts).text = str( before_month[-bmd] )
307                 bmd -= 1
308               else:
309                 inkex.etree.SubElement(gdays, 'text', txt_atts).text = str( next_month[bmd] )
310                 bmd += 1
311             else:
312               inkex.etree.SubElement(gdays, 'text', txt_atts).text = str(day)
313               before = False
314             week_x += 1
315           week_y += 1
316         self.month_x_pos += 1
317         if self.month_x_pos >= self.months_per_line:
318           self.month_x_pos = 0
319           self.month_y_pos += 1
321     def effect(self):
322         self.validate_options()
323         self.calculate_size_and_positions()
324         parent = self.document.getroot()
325         txt_atts = {
326           'id': 'year_'+str(self.options.year) }
327         self.year_g = inkex.etree.SubElement(parent, 'g', txt_atts)
328         txt_atts = {'style': simplestyle.formatStyle(self.style_year),
329                     'x': str( self.doc_w / 2 ),
330                     'y': str( self.day_w * 1.5 ) }
331         inkex.etree.SubElement(self.year_g, 'text', txt_atts).text = str(self.options.year)
332         if self.options.month == 0:
333           for m in range(1,13):
334             self.create_month(m)
335         else:
336           self.create_month(self.options.month)
339 if __name__ == '__main__':   #pragma: no cover
340     e = SVGCalendar()
341     e.affect()