diff options
Diffstat (limited to '')
-rwxr-xr-x | share/extensions/svgcalendar.py | 548 |
1 files changed, 548 insertions, 0 deletions
diff --git a/share/extensions/svgcalendar.py b/share/extensions/svgcalendar.py new file mode 100755 index 0000000..a21fd49 --- /dev/null +++ b/share/extensions/svgcalendar.py @@ -0,0 +1,548 @@ +#!/usr/bin/env python +# coding=utf-8 +# +# Copyright (C) 2008 Aurelio A. Heckert <aurium(a)gmail.com> +# Week number option added by Olav Vitters and Nicolas Dufour (2012) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +""" +A calendar generator plugin for Inkscape, but also can be used as a standalone +command line application. +# +More on ISO week number calculation on: +http://en.wikipedia.org/wiki/ISO_week_date +(The first week of a year is the week that contains the first Thursdayof the year.) +""" + +__version__ = "0.4" + +import datetime +import calendar +import re +import sys + +import inkex +from inkex import TextElement +from inkex.localization import inkex_gettext as _ + +if sys.version_info[0] > 2: + + def unicode(stringlike, encoding): + """Compatibility for python 2 strings""" + if isinstance(stringlike, bytes): + return stringlike.decode(encoding) + return stringlike + + +class Calendar(inkex.EffectExtension): + """Generate Calendar in SVG""" + + def add_arguments(self, pars): + pars.add_argument("--tab", type=str, dest="tab") + pars.add_argument( + "--month", + type=int, + default=0, + help="Month to be generated. If 0, then the entry year will be generated.", + ) + pars.add_argument( + "--year", + type=int, + default=0, + help="Year to be generated. If 0, then the current year will be generated.", + ) + pars.add_argument( + "--fill-empty-day-boxes", + type=inkex.Boolean, + dest="fill_edb", + default=True, + help="Fill empty day boxes with next month days.", + ) + pars.add_argument( + "--show-week-number", + type=inkex.Boolean, + dest="show_weeknr", + default=False, + help="Include a week number column.", + ) + pars.add_argument( + "--start-day", default="sun", help='Week start day. ("sun" or "mon")' + ) + pars.add_argument( + "--weekend", + default="sat+sun", + help='Define the weekend days. ("sat+sun" or "sat" or "sun")', + ) + pars.add_argument( + "--auto-organize", + type=inkex.Boolean, + dest="auto_organize", + default=True, + help="Automatically set the size and positions.", + ) + pars.add_argument( + "--months-per-line", + type=int, + dest="months_per_line", + default=3, + help="Number of months side by side.", + ) + pars.add_argument( + "--month-width", + type=str, + dest="month_width", + default="6cm", + help="The width of the month days box.", + ) + pars.add_argument( + "--month-margin", + type=str, + dest="month_margin", + default="1cm", + help="The space between the month boxes.", + ) + pars.add_argument( + "--color-year", + type=inkex.Color, + dest="color_year", + default=inkex.Color(0x808080FF), + help="Color for the year header.", + ) + pars.add_argument( + "--color-month", + type=inkex.Color, + dest="color_month", + default=inkex.Color(0x686868FF), + help="Color for the month name header.", + ) + pars.add_argument( + "--color-day-name", + type=inkex.Color, + dest="color_day_name", + default=inkex.Color(0x909090FF), + help="Color for the week day names header.", + ) + pars.add_argument( + "--color-day", + type=inkex.Color, + dest="color_day", + default=inkex.Color(0x000000FF), + help="Color for the common day box.", + ) + pars.add_argument( + "--color-weekend", + type=inkex.Color, + dest="color_weekend", + default=inkex.Color(0x787878FF), + help="Color for the weekend days.", + ) + pars.add_argument( + "--color-nmd", + type=inkex.Color, + dest="color_nmd", + default=inkex.Color(0xB0B0B0FF), + help="Color for the next month day, in empty day boxes.", + ) + pars.add_argument( + "--color-weeknr", + type=inkex.Color, + dest="color_weeknr", + default=inkex.Color(0x787878FF), + help="Color for the week numbers.", + ) + pars.add_argument( + "--font-year", + type=str, + dest="font_year", + default="arial", + help="Font for the year string.", + ) + pars.add_argument( + "--font-month", + type=str, + dest="font_month", + default="arial", + help="Font for the month strings.", + ) + pars.add_argument( + "--font-day-name", + type=str, + dest="font_day_name", + default="arial", + help="Font for the days of the week strings.", + ) + pars.add_argument( + "--font-day", + type=str, + dest="font_day", + default="arial", + help="Font for the day strings.", + ) + pars.add_argument( + "--month-names", + type=str, + dest="month_names", + default="January February March " + "April May June July " + "August September October " + "November December", + help="The month names for localization.", + ) + pars.add_argument( + "--day-names", + type=str, + dest="day_names", + default="Sun Mon Tue Wed Thu Fri Sat", + help="The week day names for localization.", + ) + pars.add_argument( + "--weeknr-name", + type=str, + dest="weeknr_name", + default="Wk", + help="The week number column name for localization.", + ) + pars.add_argument( + "--encoding", + type=str, + dest="input_encode", + default="arabic", + help="The input encoding of the names.", + ) + + def validate_options(self): + """Validity check for various text inputs""" + # inkex.errormsg( self.options.input_encode ) + # Convert string names lists in real lists + month_name = re.match(r"\s*(.*[^\s])\s*", self.options.month_names) + self.options.month_names = re.split(r"\s+", month_name.group(1)) + month_name = re.match(r"\s*(.*[^\s])\s*", self.options.day_names) + self.options.day_names = re.split(r"\s+", month_name.group(1)) + + # Validate names lists + if len(self.options.month_names) != 12: + inkex.errormsg( + 'The month name list "' + + str(self.options.month_names) + + '" is invalid. Using default.' + ) + self.options.month_names = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ] + if len(self.options.day_names) != 7: + inkex.errormsg( + 'The day name list "' + + str(self.options.day_names) + + '" is invalid. Using default.' + ) + self.options.day_names = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] + # Convert year 0 to current year + if self.options.year == 0: + self.options.year = datetime.datetime.today().year + # Year 1 starts it's week at monday, obligatorily + if self.options.year == 1: + self.options.start_day = "mon" + # Set the calendar start day + if self.options.start_day == "sun": + calendar.setfirstweekday(6) + else: + calendar.setfirstweekday(0) + # Convert string numbers with unit to user space float numbers + self.options.month_width = self.svg.unittouu(self.options.month_width) + self.options.month_margin = self.svg.unittouu(self.options.month_margin) + + # initial values + month_x_pos = 0 + month_y_pos = 0 + weeknr = 0 + + def calculate_size_and_positions(self): + """month_margin month_width months_per_line auto_organize""" + self.doc_w = self.svg.unittouu(self.document.getroot().get("width")) + self.doc_h = self.svg.unittouu(self.document.getroot().get("height")) + if self.options.show_weeknr: + self.cols_before = 1 + else: + self.cols_before = 0 + if self.options.auto_organize: + if self.doc_h > self.doc_w: + self.months_per_line = 3 + else: + self.months_per_line = 4 + else: + self.months_per_line = self.options.months_per_line + # self.month_w = self.doc_w / self.months_per_line + if self.options.auto_organize: + self.month_w = (self.doc_w * 0.8) / self.months_per_line + self.month_margin = self.month_w / 10 + else: + self.month_w = self.options.month_width + self.month_margin = self.options.month_margin + self.day_w = self.month_w / (7 + self.cols_before) + self.day_h = self.month_w / 9 + self.month_h = self.day_w * 7 + if self.options.month == 0: + self.year_margin = ( + self.doc_w + + self.day_w + - (self.month_w * self.months_per_line) + - (self.month_margin * (self.months_per_line - 1)) + ) / 2 # - self.month_margin + else: + self.year_margin = (self.doc_w - self.month_w) / 2 + self.style_day = { + "font-size": str(self.day_w / 2), + "font-family": self.options.font_day, + "text-anchor": "middle", + "text-align": "center", + "fill": self.options.color_day, + } + self.style_weekend = self.style_day.copy() + self.style_weekend["fill"] = self.options.color_weekend + self.style_nmd = self.style_day.copy() + self.style_nmd["fill"] = self.options.color_nmd + self.style_month = self.style_day.copy() + self.style_month["fill"] = self.options.color_month + self.style_month["font-family"] = self.options.font_month + self.style_month["font-size"] = str(self.day_w / 1.5) + self.style_month["font-weight"] = "bold" + self.style_day_name = self.style_day.copy() + self.style_day_name["fill"] = self.options.color_day_name + self.style_day_name["font-family"] = self.options.font_day_name + self.style_day_name["font-size"] = str(self.day_w / 3) + self.style_year = self.style_day.copy() + self.style_year["fill"] = self.options.color_year + self.style_year["font-family"] = self.options.font_year + self.style_year["font-size"] = str(self.day_w * 2) + self.style_year["font-weight"] = "bold" + self.style_weeknr = self.style_day.copy() + self.style_weeknr["fill"] = self.options.color_weeknr + self.style_weeknr["font-size"] = str(self.day_w / 3) + + def is_weekend(self, pos): + """Detect weekend days; weekend values: "sat+sun" or "sat" or "sun" """ + if self.options.start_day == "sun": + if self.options.weekend == "sat+sun" and pos == 0: + return True + if self.options.weekend == "sat+sun" and pos == 6: + return True + if self.options.weekend == "sat" and pos == 6: + return True + if self.options.weekend == "sun" and pos == 0: + return True + else: + if self.options.weekend == "sat+sun" and pos == 5: + return True + if self.options.weekend == "sat+sun" and pos == 6: + return True + if self.options.weekend == "sat" and pos == 5: + return True + if self.options.weekend == "sun" and pos == 6: + return True + return False + + def in_line_month(self, cal): + """Lay out days of week in each week""" + cal2 = [] + for week in cal: + for day in week: + if day != 0: + cal2.append(day) + return cal2 + + def write_month_header(self, g, m): + """Write month header""" + txt_atts = { + "style": str(inkex.Style(self.style_month)), + "x": str((self.month_w - self.day_w) / 2), + "y": str(self.day_h / 5), + } + try: + g.add(TextElement(**txt_atts)).text = unicode( + self.options.month_names[m - 1], self.options.input_encode + ) + except: + raise ValueError(_("You must select a correct system encoding.")) + + week_group = g.add(inkex.Group()) + week_x = 0 + if self.options.start_day == "sun": + day_names = self.options.day_names[:] + else: + day_names = self.options.day_names[1:] + day_names.append(self.options.day_names[0]) + + if self.options.show_weeknr: + day_names.insert(0, self.options.weeknr_name) + + for wday in day_names: + txt_atts = { + "style": str(inkex.Style(self.style_day_name)), + "x": str(self.day_w * week_x), + "y": str(self.day_h), + } + try: + week_group.add(TextElement(**txt_atts)).text = unicode( + wday, self.options.input_encode + ) + except: + raise ValueError(_("You must select a correct system encoding.")) + + week_x += 1 + + def create_month(self, m): + """Lay out one month in place on the calendar""" + txt_atts = { + "transform": "translate(" + + str( + self.year_margin + (self.month_w + self.month_margin) * self.month_x_pos + ) + + "," + + str((self.day_h * 4) + (self.month_h * self.month_y_pos)) + + ")", + "id": "month_" + str(m) + "_" + str(self.options.year), + } + g = self.year_g.add(inkex.Group(**txt_atts)) + self.write_month_header(g, m) + gdays = g.add(inkex.Group()) + cal = calendar.monthcalendar(self.options.year, m) + if m == 1: + if self.options.year > 1: + before_month = self.in_line_month( + calendar.monthcalendar(self.options.year - 1, 12) + ) + else: + before_month = self.in_line_month( + calendar.monthcalendar(self.options.year, m - 1) + ) + if m == 12: + next_month = self.in_line_month( + calendar.monthcalendar(self.options.year + 1, 1) + ) + else: + next_month = self.in_line_month( + calendar.monthcalendar(self.options.year, m + 1) + ) + if len(cal) < 6: + # add a line after the last week + cal.append([0, 0, 0, 0, 0, 0, 0]) + if len(cal) < 6: + # add a line before the first week (Feb 2009) + cal.reverse() + cal.append([0, 0, 0, 0, 0, 0, 0]) + cal.reverse() + # How mutch before month days will be showed: + bmd = cal[0].count(0) + cal[1].count(0) + before = True + week_y = 0 + for week in cal: + if ( + self.weeknr != 0 + and ( + (self.options.start_day == "mon" and week[0] != 0) + or (self.options.start_day == "sun" and week[1] != 0) + ) + ) or ( + self.weeknr == 0 + and ( + (self.options.start_day == "mon" and week[3] > 0) + or (self.options.start_day == "sun" and week[4] > 0) + ) + ): + self.weeknr += 1 + week_x = 0 + if self.options.show_weeknr: + # Remove leap week (starting previous year) and empty weeks + if self.weeknr != 0 and not (week[0] == 0 and week[6] == 0): + style = self.style_weeknr + txt_atts = { + "style": str(inkex.Style(style)), + "x": str(self.day_w * week_x), + "y": str(self.day_h * (week_y + 2)), + } + gdays.add(TextElement(**txt_atts)).text = str(self.weeknr) + week_x += 1 + else: + week_x += 1 + for day in week: + style = self.style_day + if self.is_weekend(week_x - self.cols_before): + style = self.style_weekend + if day == 0: + style = self.style_nmd + txt_atts = { + "style": str(inkex.Style(style)), + "x": str(self.day_w * week_x), + "y": str(self.day_h * (week_y + 2)), + } + text = None + if day == 0 and not self.options.fill_edb: + pass # draw nothing + elif day == 0: + if before: + text = str(before_month[-bmd]) + bmd -= 1 + else: + text = str(next_month[bmd]) + bmd += 1 + else: + text = str(day) + before = False + if text: + gdays.add(TextElement(**txt_atts)).text = text + week_x += 1 + week_y += 1 + self.month_x_pos += 1 + if self.month_x_pos >= self.months_per_line: + self.month_x_pos = 0 + self.month_y_pos += 1 + + def effect(self): + self.validate_options() + self.calculate_size_and_positions() + parent = self.document.getroot() + txt_atts = {"id": "year_" + str(self.options.year)} + self.year_g = parent.add(inkex.Group(**txt_atts)) + txt_atts = { + "style": str(inkex.Style(self.style_year)), + "x": str(self.doc_w / 2), + "y": str(self.day_w * 1.5), + } + self.year_g.add(TextElement(**txt_atts)).text = str(self.options.year) + try: + if self.options.month == 0: + for m in range(1, 13): + self.create_month(m) + else: + self.create_month(self.options.month) + except ValueError as err: + return inkex.errormsg(str(err)) + return None + + +if __name__ == "__main__": + Calendar().run() |