summaryrefslogtreecommitdiffstats
path: root/share/extensions/svgcalendar.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xshare/extensions/svgcalendar.py548
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()