diff --git a/callirhoe.py b/callirhoe.py new file mode 100755 index 0000000..a0b0104 --- /dev/null +++ b/callirhoe.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python2.7 +# -*- coding: utf-8 -*- + +# callirhoe - high quality calendar rendering +# Copyright (C) 2012 George M. Tzoumas + +# 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 3 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, see http://www.gnu.org/licenses/ + +_version = "0.1" + +import calendar +import sys +import time +import os.path +import optparse +import glob + +from lib.xcairo import * +from lib.render import * + +plugin_path = [ os.path.expanduser("~/.callirhoe"), sys.path[0] if sys.path[0] else "." ] + +def available_files(parent, dir, fmatch = ""): + good = False + res = [] + pattern = parent + "/" + dir + "/*.py" + # TODO: improve matching with __init__ when lang known + for x in glob.glob(pattern): + basex = os.path.basename(x) + if basex == "__init__.py": good = True + else: + base = os.path.splitext(basex)[0] + if base and ((not fmatch) or (fmatch == base)): res.append((base,parent)) + return res if good else [] + +# cat = lang (category) +# longcat = language +# longcat2 = languages +# listopt = --list-lang +# preset = "EN" +def import_plugin(cat, longcat, longcat2, listopt, preset): + try: + found = available_files(plugin_path[0], cat, preset) + available_files(plugin_path[1], cat, preset) + if len(found) == 0: raise ImportError + old = sys.path[0]; + sys.path[0] = found[0][1] + m = __import__("%s.%s" % (cat,preset), globals(), locals(), [ "*" ]) + sys.path[0] = old + return m + except ImportError: + print >> sys.stderr, "%s definition `%s' not found, use %s to see available %s" % (longcat,preset,listopt,longcat2) + sys.exit(1) + +parser = optparse.OptionParser(usage="usage: %prog [options] FILE", + description="High quality calendar rendering with vector graphics.\n" + "By default, a pdf calendar of the current year will be written to FILE. Program version: " + _version) +parser.add_option("-y", "--year", dest="year", type="int", default=-1, + help="select YEAR instead of current one") +parser.add_option("-l", "--lang", dest="lang", default="EN", + help="choose language [EN]") +parser.add_option("-s", "--style", dest="style", default="default", + help="choose style [default]") +parser.add_option("-g", "--geometry", dest="geom", default="default", + help="choose geometry [default]") +parser.add_option("--landscape", action="store_true", dest="landscape", default=False, + help="landscape mode") +parser.add_option("--list-languages", action="store_true", dest="list_languages", default=False, + help="list available languages") +parser.add_option("--list-styles", action="store_true", dest="list_styles", default=False, + help="list available styles") +parser.add_option("--list-geometries", action="store_true", dest="list_geometries", default=False, + help="list available geometries") +parser.add_option("--lang-var", action="append", dest="lang_assign", + help="modify a language variable") +parser.add_option("--style-var", action="append", dest="style_assign", + help="modify a style variable, e.g. dom.frame_thickness=0") +parser.add_option("--geom-var", action="append", dest="geom_assign", + help="modify a geometry variable") + +#for x in sys.argv: +# if x[0] == '-' and not parser.has_option(x): +# print "possibly bad option", x +# ./callirhoe --lang fr year=2010 months=1-6 foo.pdf + +(options,args) = parser.parse_args() + +def plugin_list(cat): + return available_files(plugin_path[0], cat) + available_files(plugin_path[1], cat) +if options.list_languages: + for x in plugin_list("lang"): print x[0], + print +if options.list_styles: + for x in plugin_list("style"): print x[0], + print +if options.list_geometries: + for x in plugin_list("geom"): print x[0], + print +if options.list_languages or options.list_styles or options.list_geometries: sys.exit(0) + +if len(args)==0: + parser.print_help() + sys.exit(0) + +cal_lang = import_plugin("lang", "language", "languages", "--list-languages", options.lang) +cal_style = import_plugin("style", "style", "styles", "--list-styles", options.style) +cal_geom = import_plugin("geom", "geometry", "geometries", "--list-geometries", options.geom) + +if options.lang_assign: + for x in options.lang_assign: exec "cal_lang.%s" % x +if options.style_assign: + for x in options.style_assign: exec "cal_style.%s" % x +if options.geom_assign: + for x in options.geom_assign: exec "cal_geom.%s" % x + +calendar.month_name = cal_lang.month_name +calendar.day_name = cal_lang.day_name + +def get_orthodox_easter(y): + y1, y2, y3 = y - y//4 * 4, y - y//7 * 7, y - y//19 * 19 + a = 19*y3 + 15 + y4 = a - a//30 * 30 + b = 2*y1 + 4*y2 + 6*(y4 + 1) + y5 = b - b/7 * 7 + r = 1 + 3 + y4 + y5; + return (5, r - 30) if r > 30 else (4,r) + +year = options.year if options.year >= 0 else time.localtime()[0] + +p = PDFPage(args[0], options.landscape) + +R0,R1 = rect_vsplit(p.Text_rect, 0.05) +Rcal,Rc = rect_vsplit(R1,0.97) +if options.landscape: + rows,cols = 3,4 +else: + rows,cols = 4,3 +foo = GLayout(Rcal, rows, cols, pad = (p.Text_rect[2]*0.02,)*4) +shad = p.Size[0]*0.01 if cal_style.month.box_shadow else 0 +for i in range(min(foo.count(),12),0,-1): + draw_month(p.cr, foo.item_seq(i-1), month=i, year=year, + style = cal_style, geom = cal_geom, box_shadow = shad) +# render.draw_box(render.cr,*foo.item_seq(i)) +#draw_str(p.cr, str(year), R0, stroke_rgba = (0,0,0,0.3), align=1) +draw_str(p.cr, str(year), R0, stroke_rgba = (0,0,0,0.3), align=2) +#draw_str(p.cr, str(year), R0, stroke_rgba = (0,0,0,0.3), align=3) +draw_str(p.cr, "created by Callirhoe ver.0.1 © 2012 GeoTz", Rc, stroke_rgba = (0,0,0,0.5), stretch=0, align=2, slant=cairo.FONT_SLANT_ITALIC) + +#render.cr.move_to(render.Page.Text_pos[0],render.Page.Text_pos[1]) +#render.cr.line_to(render.Page.Text_size[0]+render.Page.Text_pos[0], render.Page.Text_size[1]+render.Page.Text_pos[1]) +#render.cr.stroke() + +#draw_month(cr, *foo.item(2), m=9) + +#bw, bh = 40,40 +#draw_day_cell(cr, 30, 50, bw, bh, '24', 'Παραμονή Χριστουγ.', 'Ευγενία', default_style) +#draw_day_cell(cr, 30+bw, 50, bw, bh, '25', 'ΧΡΙΣΤΟΥΓΕΝΝΑ', 'Χρήστος, Χριστίνα', weekend_holiday_style) +#draw_day_cell(cr, 30+2*bw, 50, bw, bh, '26', '2η μέρα Χριστουγ.', 'Εμμανουήλ', weekend_style) +#draw_day_cell(cr, 30+3*bw, 50, bw, bh, '27', '', 'Στέφανος', default_style) +#draw_day_page(cr, 30+2*bw, 50, bw, bh, '26', 'Κυριακή', 'Δεκεμβρίου', [1,1,1], [.3,.3,.3]) diff --git a/geom/__init__.py b/geom/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/geom/default.py b/geom/default.py new file mode 100644 index 0000000..d81b561 --- /dev/null +++ b/geom/default.py @@ -0,0 +1,27 @@ +# callirhoe - high quality calendar rendering +# Copyright (C) 2012 George M. Tzoumas + +# 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 3 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, see http://www.gnu.org/licenses/ + +# --- geom.default --- + +class dom: + num_size = (0.5,0.5) + header_size = footer_size = (0.8,0.1) + top_margin = bottom_margin = 0.05 + +class month: + fuzzy_rot = 0 + fuzzy_dx = 0 + fuzzy_dy = 0 diff --git a/geom/fuzzy.py b/geom/fuzzy.py new file mode 100644 index 0000000..b9c95a1 --- /dev/null +++ b/geom/fuzzy.py @@ -0,0 +1,27 @@ +# callirhoe - high quality calendar rendering +# Copyright (C) 2012 George M. Tzoumas + +# 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 3 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, see http://www.gnu.org/licenses/ + +# --- geom.default --- + +class dom: + num_size = (0.5,0.5) + header_size = footer_size = (0.8,0.1) + top_margin = bottom_margin = 0.05 + +class month: + fuzzy_rot = 0.25 + fuzzy_dx = 0 + fuzzy_dy = 0 diff --git a/lang/EL.py b/lang/EL.py new file mode 100644 index 0000000..1a87f03 --- /dev/null +++ b/lang/EL.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +# callirhoe - high quality calendar rendering +# Copyright (C) 2012 George M. Tzoumas + +# 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 3 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, see http://www.gnu.org/licenses/ + +day_name = [ u'Δευτέρα', u'Τρίτη', u'Τετάρτη', + u'Πέμπτη', u'Παρασκευή', u'Σάββατο', u'Κυριακή' ] + +short_day_name = [ u'Δευ', u'Τρι', u'Τετ', u'Πεμ', u'Παρ', u'Σαβ', u'Κυρ' ] + +month_name = [ '', u'Ιανουάριος', u'Φεβρουάριος', u'Μάρτιος', u'Απρίλιος', + u'Μάιος', u'Ιούνιος', u'Ιούλιος', u'Αύγουστος', + u'Σεπτέμβριος', u'Οκτώβριος', u'Νοέμβριος', u'Δεκέμβριος' ] + +short_month_name = [ '', u'Ιαν', u'Φεβ', u'Μαρ', u'Απρ', u'Μαϊ', u'Ιον', u'Ιολ', + u'Αυγ', u'Σεπ', u'Οκτ', u'Νοε', u'Δεκ' ] diff --git a/lang/EN.py b/lang/EN.py new file mode 100644 index 0000000..5dbb642 --- /dev/null +++ b/lang/EN.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +# callirhoe - high quality calendar rendering +# Copyright (C) 2012 George M. Tzoumas + +# 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 3 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, see http://www.gnu.org/licenses/ + +day_name = [ u'Monday', u'Tuesday', u'Wednesday', + u'Thursday', u'Friday', u'Saturday', u'Sunday' ] + +short_day_name = [ u'Mon', u'Tue', u'Wed', u'Thu', u'Fri', u'Sat', u'Sun' ] + +month_name = [ '', + u'January', u'February', u'March', u'April', + u'May', u'June', u'July', u'August', + u'September', u'October', u'November', u'December' ] + +short_month_name = [ '', + u'Jan', u'Feb', u'Mar', u'Apr', u'May', u'Jun', + u'Jul', u'Aug', u'Sep', u'Oct', u'Nov', u'Dec' ] diff --git a/lang/FR.py b/lang/FR.py new file mode 100644 index 0000000..854297d --- /dev/null +++ b/lang/FR.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +# callirhoe - high quality calendar rendering +# Copyright (C) 2012 George M. Tzoumas + +# 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 3 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, see http://www.gnu.org/licenses/ + +day_name = [ u'Lundi', u'Mardi', u'Mercredi', + u'Jeudi', u'Vendredi', u'Samedi', u'Dimanche' ] + +short_day_name = [ u'Lun', u'Mar', u'Mer', u'Jeu', u'Ven', u'Sam', u'Dim' ] + +month_name = [ '', + u'Janvier', u'Février', u'Mars', u'Avril', + u'Mai', u'Juin', u'Juillet', u'Août', + u'Septembre', u'Octobre', u'Novembre', u'Décembre' ] + +short_month_name = [ '', u'Jan', u'Fév', u'Mar', u'Avr', u'Mai', u'Jun', u'Jul', + u'Aoû', u'Sep', u'Oct', u'Nov', u'Déc' ] diff --git a/lang/__init__.py b/lang/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/geom.py b/lib/geom.py new file mode 100644 index 0000000..a53538a --- /dev/null +++ b/lib/geom.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- + +# callirhoe - high quality calendar rendering +# Copyright (C) 2012 George M. Tzoumas + +# 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 3 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, see http://www.gnu.org/licenses/ + +# ***************************************** +# # +# general-purpose geometry routines # +# # +# ***************************************** + +def rect_rel_scale(r, fw, fh): + x, y, w, h = r + return (x + w*(1 - fw)/2.0, y + h*(1 - fh)/2.0, w*fw, h*fh) + +def rect_to_abs(r): + x, y, w, h = r + return (x, y, x + w, y + h) + +def abs_to_rect(a): + x1, y1, x2, y2 = a + return (x1, y1, x2 - x1, y2 - y1) + +def rect_hull(r1,r2): + x1, y1, x2, y2 = rect_to_abs(r1) + x3, y3, x4, y4 = rect_to_abs(r2) + return abs_to_rect((min(x1,x3), min(y1,y3), max(x2,x4), max(y2,y4))) + +def rect_hsplit(r, f = 0.5): + x, y, w, h = r + r1 = (x, y, w*f, h) + r2 =(x + w*f, y, w*(1 - f), h) + return (r1, r2) + +def rect_vsplit(r, f = 0.5): + x, y, w, h = r + r1 = (x, y, w, h*f) + r2 = (x, y + h*f, w, h*(1 - f)) + return (r1, r2) + +def rect_qsplit(r, fv = 0.5, fh = 0.5): + x, y, w, h = r + rv = rect_vsplit(r, fv) + return rect_hsplit(rv[0], fh) + rect_hsplit(rv[1], fh) + +def color_mix(a, b, frac): + k = min(len(a), len(b)) + return map(lambda (x,y): x*frac + y*(1 - frac), zip(a,b)) + +def color_auto_fg(bg, light = (1,1,1), dark = (0,0,0)): + return light if bg[0] + bg[1] + bg[2] < 1.5 else dark + +# ********* layout managers *********** + +class VLayout: + def __init__(self, rect, nitems = 1, pad = (0.0,0.0,0.0,0.0)): # TLBR + self.pos = (rect[0], rect[1]) + self.size = (rect[2], rect[3]) + self.items = nitems + self.pad = pad + + def count(self): + return self.items + + def resize(self, k): + self.items = k + + def grow(self, delta = 1): + self.items += delta + + def move(newpos): + self.pos = newpos; + + def item(self, i = 0): + vinter = (self.pad[0] + self.pad[2])/2.0; + vsize = (self.size[1] - vinter)/self.items; + return (self.pos[0] + self.pad[1], self.pos[1] + self.pad[0] + i*vsize, + self.size[0] -self.pad[1] - self.pad[3], vsize - vinter) + + def item_span(self, n, k = -1): + if k < 0: k = (self.count() - n) // 2 + return rect_hull(self.item(k), self.item(k + n - 1)) + +class HLayout: + def __init__(self, rect, nitems = 1, pad = (0.0,0.0,0.0,0.0)): # TLBR + self.pos = (rect[0],rect[1]) + self.rep = VLayout((0.0, 0.0, rect[3], rect[2]), nitems, (pad[1], pad[0], pad[3], pad[2])) + + def count(self): + return self.rep.count() + + def resize(self, k): + self.rep.resize(k) + + def move(newpos): + self.rep.move(newpos) + + def item(self, i = 0): + t = self.rep.item(i) + return (t[1] + self.pos[0], t[0] + self.pos[1], t[3], t[2]) + + def item_span(self, n, k = -1): + if k < 0: k = (self.rep.count() - n) // 2 + return rect_hull(self.item(k), self.item(k + n - 1)) + +class GLayout: + def __init__(self, rect, nrows = 1, ncols = 1, pad = (0.0,0.0,0.0,0.0)): # TLBR + self.vrep = VLayout(rect, nrows, (pad[0], 0.0, pad[2], 0.0)) + t = self.vrep.item(0) + self.hrep = HLayout((rect[0], rect[1], t[2], t[3]), ncols, (0.0, pad[1], 0.0, pad[3])) + + def row_count(self): + return self.vrep.count() + + def col_count(self): + return self.hrep.count() + + def count(self): + return self.row_count()*self.col_count() + + def resize(self, rows, cols): + self.vrep.resize(rows) + t = self.vrep.item(0) + self.hrep = HLayout(t[0:2], t[2:4], cols, (0.0, pad[1], 0.0, pad[3])) + + def move(newpos): + self.vrep.move(newpos) + self.hrep.move(newpos) + + def item(self, row, col): + ty = self.vrep.item(row) + tx = self.hrep.item(col) + return (tx[0], ty[1], tx[2], tx[3]) + + def item_seq(self, k, column_wise = False): + if not column_wise: + row, col = k // self.col_count(), k % self.col_count() + else: + col, row = k // self.row_count(), k % self.row_count() + return self.item(row, col) + + def item_span(self, nr, nc, row = -1, col = -1): + if row < 0: row = (self.row_count() - nr) // 2 + if col < 0: col = (self.col_count() - nc) // 2 + return rect_hull(self.item(row, col), self.item(row + nr - 1, col + nc - 1)) diff --git a/lib/render.py b/lib/render.py new file mode 100644 index 0000000..1737596 --- /dev/null +++ b/lib/render.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- + +# callirhoe - high quality calendar rendering +# Copyright (C) 2012 George M. Tzoumas + +# 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 3 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, see http://www.gnu.org/licenses/ + +# ***************************************** +# # +# calendar drawing routines # +# # +# ***************************************** + +import math +import calendar +import random +from xcairo import * +from geom import * + +# TODO: replace rects with rect_rel_scale() +def draw_day_cell(cr, rect, day_of_month, header, footer, style, G): + x, y, w, h = rect + draw_box(cr, rect, style.frame, style.bg, style.frame_thickness) + R = rect_rel_scale(rect, G.num_size[0], G.num_size[1]) + draw_str(cr, day_of_month, R, -1, style.fg, 2, + font = style.font, measure = "55") + if header: + R = (x + (1 - G.header_size[0])*w/2, y + G.top_margin*h, + w*G.header_size[0], h*G.header_size[1]) + draw_str(cr, header, R, -1, style.header, 2, font = style.header_font) + if footer: + R = (x + (1 - G.footer_size[0])*w/2, y + (1 - G.bottom_margin - G.footer_size[1])*h, + w*G.footer_size[0], h*G.footer_size[1]) + draw_str(cr, footer, R, -1, style.footer, 2, font = style.footer_font) + +def draw_day_page(cr, rect, day_of_month, header, footer, fill = (1,1,1), stroke = (0,0,0)): + x, y, w, h = rect + draw_box(cr, rect, (0,0,0), fill) + draw_str(cr, day_of_month, (x + w*.382/2, y + h*.382/2, w*.618, h*.618), 3, stroke) + draw_str(cr, header, (x +0.1*w, y + 0.05*h, w*0.8, 0.1*h), -1, (0,0,0), 2) + draw_str(cr, footer, (x +0.1*w, y + 0.85*h, w*0.8, 0.1*h), -1, (0,0,0), 2) + +# TODO: implement GEOMETRY, STYLE, DATA SOURCES... +# TODO: use named options... +def draw_month(cr, rect, month, year, style, geom, box_shadow = 0): + x, y, w, h = rect + cw = w/7.0; + ch = h/7.0; + cr.save() + cr.translate(x,y) + + cr.translate(w/2, h/2) + cr.translate(w*(random.random() - 0.5)*geom.month.fuzzy_dx, + h*(random.random() - 0.5)*geom.month.fuzzy_dy) + cr.rotate((random.random() - 0.5)*geom.month.fuzzy_rot) + cr.translate(-w/2.0, -h/2.0) + + cal = calendar.monthrange(year, month) + day = cal[0] + dom = -day + 1; + wmeasure = 'A'*max(map(len,calendar.day_name)) + mmeasure = 'A'*max(map(len,calendar.month_name)) + + for col in range(7): + R = (col*cw, ch*3.0/5, cw, ch*2.0/5) + draw_box(cr, R, style.dom.frame, + style.dom.bg if col < 5 else style.dom_weekend.bg, style.dow.frame_thickness) + R = (col*cw, ch*3.0/5 + ch*2.0/5*1.0/4, cw, ch*2.0/5*1/2) + draw_str(cr, calendar.day_name[col], R, -1, style.dow.fg, 2, + font = style.dow.font, measure = wmeasure) + # TODO: use grid layout + for row in range(6): + for col in range(7): + day_style = style.dom_weekend if col >= 5 else style.dom + R = (col*cw, (row + 1)*ch, cw, ch) + if dom > 0 and dom <= cal[1]: + draw_day_cell(cr, R, str(dom), '', '', day_style, geom.dom) + else: + draw_box(cr, R, day_style.frame, day_style.bg, day_style.frame_thickness) + dom += 1 + + mcolor = style.month.color_map[month] + mcolor_fg = color_auto_fg(mcolor) + draw_box(cr, (0, 0, w, h), style.month.frame, (), 2, box_shadow) + draw_box(cr, (0, 0, w, ch*3.0/5), style.month.frame, mcolor) + R = (0, ch*3.0/5*1/4, w, ch*3.0/5*1/2) + draw_str(cr, calendar.month_name[month], R, -1, mcolor_fg, 2, False, + style.month.font, measure=mmeasure, shadow=style.month.text_shadow) + cr.restore() + \ No newline at end of file diff --git a/lib/xcairo.py b/lib/xcairo.py new file mode 100644 index 0000000..8fb38a8 --- /dev/null +++ b/lib/xcairo.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- + +# callirhoe - high quality calendar rendering +# Copyright (C) 2012 George M. Tzoumas + +# 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 3 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, see http://www.gnu.org/licenses/ + +# ***************************************** +# # +# general-purpose drawing routines # +# higher-level CAIRO routines # +# # +# ***************************************** + +import cairo +import math + +class PDFPage: + def __init__(self, filename, landscape = False, w = 210.0, h = 297.0, dpi = 72.0): + self.DPI = dpi # default dpi + if not landscape: + self.Size_mm = (w, h) # (width, height) in mm + else: + self.Size_mm = (h, w) + self.Size = (self.Size_mm[0]/25.4 * self.DPI, self.Size_mm[1]/25.4 * self.DPI) # size in dots + Mag = 72.0/self.DPI; + self.Margins = (self.Size[1]*0.025, self.Size[0]*0.025, + self.Size[1]*0.025, self.Size[0]*0.025) # top left bottom right + txs = (self.Size[0] - self.Margins[1] - self.Margins[3], + self.Size[1] - self.Margins[0] - self.Margins[2]) + self.Text_rect = (self.Margins[1], self.Margins[0], txs[0], txs[1]) + if not landscape: + self.Surface = cairo.PDFSurface (filename, self.Size[0]*Mag, self.Size[1]*Mag) + else: + self.Surface = cairo.PDFSurface (filename, self.Size[1]*Mag, self.Size[0]*Mag) + self.cr = cairo.Context (self.Surface) + self.cr.scale (Mag, Mag) # Normalizing the canvas + if landscape: + self.cr.translate(0,self.Size[0]) + self.cr.rotate(-math.pi/2) + +def default_width(cr, factor = 1.0): + return max(cr.device_to_user_distance(0.1*factor, 0.1*factor)) + +def set_color(cr, rgba): + if len(rgba) == 3: + cr.set_source_rgb(*rgba) + else: + cr.set_source_rgba(*rgba) + +def draw_shadow(cr, rect, thickness, shadow_color = (0,0,0,0.3)): + if thickness <= 0: return + f = thickness + x, y, w, h = rect + cr.move_to(x + f, y + h) + cr.rel_line_to(0, f); cr.rel_line_to(w, 0); cr.rel_line_to(0, -h) + cr.rel_line_to(-f, 0); cr.rel_line_to(0, h - f); cr.rel_line_to(-w + f, 0); + set_color(cr, shadow_color) + cr.close_path(); cr.fill(); + +def draw_box(cr, rect, stroke_rgba = (), fill_rgba = (), width_scale = 1.0, shadow = 0): + if (width_scale <= 0): return + draw_shadow(cr, rect, shadow) + x, y, w, h = rect + cr.move_to(x, y) + cr.rel_line_to(w, 0) + cr.rel_line_to(0, h) + cr.rel_line_to(-w, 0) + cr.close_path() + if fill_rgba: + set_color(cr, fill_rgba) + cr.fill_preserve() + if stroke_rgba: + set_color(cr, stroke_rgba) + cr.set_line_width(default_width(cr, width_scale)) + cr.stroke() + +def draw_str(cr, text, rect, stretch = -1, stroke_rgba = (), align = 0, bbox = False, + font = "Times", slant = cairo.FONT_SLANT_NORMAL, weight = cairo.FONT_WEIGHT_NORMAL, + measure = '', shadow = False): + x, y, w, h = rect + cr.save() + cr.select_font_face(font, slant, weight) + if not measure: measure = text + te = cr.text_extents(measure) + tw, th = te[2], te[3] + cr.translate(x, y + h) + ratio, tratio = w*1.0/h, tw*1.0/th; + xratio, yratio = tw*1.0/w, th*1.0/h; + if stretch < 0: stretch = 1 if xratio >= yratio else 2 + if stretch == 0: bbw, bbh = w, h; + elif stretch == 1: bbw, bbh = tw, tw/ratio; cr.scale(1.0/xratio, 1.0/xratio) + elif stretch == 2: bbw, bbh = th*ratio, th; cr.scale(1.0/yratio, 1.0/yratio) + elif stretch == 3: bbw, bbh = tw, th; cr.scale(1.0/xratio, 1.0/yratio) + te = cr.text_extents(text) + tw,th = te[2], te[3] + if align == 1: px, py = bbw - tw, 0 + elif align == 2: px, py = (bbw-tw)/2.0, 0 + else: px, py = 0, 0 + cr.set_line_width(default_width(cr)) + if shadow: + cr.set_source_rgba(0, 0, 0, 0.5) + cr.move_to(px + default_width(cr, 5), py + default_width(cr, 5)) + cr.show_text(text) + cr.move_to(px, py) + if stroke_rgba: set_color(cr, stroke_rgba) + cr.show_text(text) + if bbox: draw_box(cr, (0, 0, bbw, -bbh), stroke_rgba) + cr.restore() diff --git a/style/__init__.py b/style/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/style/bw.py b/style/bw.py new file mode 100644 index 0000000..7b66c8a --- /dev/null +++ b/style/bw.py @@ -0,0 +1,58 @@ +# callirhoe - high quality calendar rendering +# Copyright (C) 2012 George M. Tzoumas + +# 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 3 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, see http://www.gnu.org/licenses/ + +# --- style.default --- + +# day of week +class dow: + fg = (.33,.33,.33) + frame_thickness = 1.0 + frame = (0.8,0.8,0.8) + font = "Arial" + +# day of month +class dom: + bg = (1,1,1) + frame = (0.8,0.8,0.8) + frame_thickness = 1.0 + fg = (0.2,0.2,0.2) + font = "Times New Roman" + header = (0.5,0.5,0.5) + footer = header + header_font = footer_font = "Arial" + +class dom_weekend(dom): + bg = (0.95,0.95,0.95) + fg = (0,0,0) + font = "Times New Roman Bold" + +class dom_holiday(dom): + fg = (0,0,0) + header = (0,0,0) + font = "Times New Roman Bold" + +class dom_weekend_holiday_style(dom_holiday): + bg = (0,0,0) + +from lib.render import color_mix + +class month: + font = "Times New Roman Bold" + frame = (0,0,0) + bg = (1,1,1) + color_map = ((1,1,1),)*13 + box_shadow = False + text_shadow = False diff --git a/style/default.py b/style/default.py new file mode 100644 index 0000000..7dc8f74 --- /dev/null +++ b/style/default.py @@ -0,0 +1,64 @@ +# callirhoe - high quality calendar rendering +# Copyright (C) 2012 George M. Tzoumas + +# 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 3 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, see http://www.gnu.org/licenses/ + +# --- style.default --- + +# day of week +class dow: + fg = (0,0,1) + frame_thickness = 1.0 + frame = (0.8,0.8,0.8) + font = "Arial" + +# day of month +class dom: + bg = (1,1,1) + frame = (0.8,0.8,0.8) + frame_thickness = 1.0 + fg = (0.2,0.2,0.2) + font = "GFS Bodoni" + header = (0.5,0.5,0.5) + footer = header + header_font = footer_font = "Arial" + +class dom_weekend(dom): + bg = (0.7,1,1) + fg = (0,0,1) + +class dom_holiday(dom): + fg = (1,0,0) + header = (1,0,0) + +class dom_weekend_holiday_style(dom_holiday): + bg = (0.7,1,1) + +from lib.render import color_mix + +class month: + font = "GFS Artemisia Bold" + frame = (0,0,0) + bg = (1,1,1) + winter = (0,0.4,1) + spring = (0.0,0.5,0.0) + summer = (1,0.3,0) + autumn = (0.9,0.9,0) + color_map = ((0,0,0), winter, + color_mix(winter,spring,0.66), color_mix(winter,spring,0.33), spring, # april + color_mix(spring,summer,0.66), color_mix(spring,summer,0.33), summer, # july + color_mix(summer,autumn,0.66), color_mix(summer,autumn,0.33), autumn, # october + color_mix(autumn,winter,0.66), color_mix(autumn,winter,0.33)) # december + text_shadow = True + box_shadow = True