From 80208e6a3595bac3b1d3ef0ac8979fa35387ffee Mon Sep 17 00:00:00 2001 From: "geortz@gmail.com" Date: Fri, 3 Aug 2012 12:00:30 +0000 Subject: [PATCH] improved str rendering implemented layout scheme git-svn-id: https://callirhoe.googlecode.com/svn/trunk@14 81c8bb96-aa45-f2e2-0eef-c4fa4a15c6df --- callirhoe.py | 114 ++++------ geom/default.py | 2 +- {templates => layouts}/__init__.py | 0 layouts/classic.py | 336 +++++++++++++++++++++++++++++ lib/geom.py | 6 +- lib/plugin.py | 5 + lib/render.py | 190 ---------------- lib/xcairo.py | 46 ++-- style/bw.py | 2 - style/default.py | 2 +- style/screenshot.py | 2 +- templates/tiles.py | 0 12 files changed, 421 insertions(+), 284 deletions(-) rename {templates => layouts}/__init__.py (100%) create mode 100644 layouts/classic.py delete mode 100644 lib/render.py delete mode 100644 templates/tiles.py diff --git a/callirhoe.py b/callirhoe.py index 507bdfe..a044a47 100755 --- a/callirhoe.py +++ b/callirhoe.py @@ -19,15 +19,15 @@ # TODO: -# maybe add warning when last arg looks like an int # allow /usr/bin/date-like formatting %x... # improve file matching with __init__ when lang known -# wrap around years +# odd/even year coloring +# fix message style not found --> could not load... +# auto-landscape ? should aim for matrix or bars? # optparse ... --version, epilog with examples # python source documentation -# .callirhoe/config : default values for plugins (styles/templates/lang...) -# RENDER: -# implement GEOMETRY, STYLE, DATA SOURCES... +# .callirhoe/config : default values for plugins (styles/layouts/lang...) +# implement DATA SOURCES # styles and geometries could be merged, css-like # then we can apply a chain of --style a --style b ... @@ -36,23 +36,15 @@ # CANNOT UPGRADE TO argparse !!! -- how to handle [[month] year] form? -_version = "0.1.13" +_version = "0.2.0.r14" import calendar import sys import time import optparse -from lib.xcairo import * -from lib.render import * from lib.plugin import * - -# cat = lang (category) -# longcat = language -# longcat2 = languages -# listopt = --list-lang -# preset = "EN" -# TODO: CHECK WHY CANNOT BE MOVED INTO lib.plugin, maybe needs ".." +# TODO: SEE IF IT CAN BE MOVED INTO lib.plugin ... def import_plugin(cat, longcat, longcat2, listopt, preset): try: found = available_files(plugin_path[0], cat, preset) + available_files(plugin_path[1], cat, preset) @@ -74,8 +66,10 @@ parser = optparse.OptionParser(usage="usage: %prog [options] [[MONTH[-MONTH2|:SP "or for SPAN months.", version="callirhoe " + _version) parser.add_option("-l", "--lang", dest="lang", default="EN", help="choose language [EN]") -parser.add_option("-t", "--template", dest="template", default="tiles", - help="choose template [tiles]") +parser.add_option("-t", "--layout", dest="layout", default="classic", + help="choose layout [classic]") +parser.add_option("-H", "--layout-help", dest="layouthelp", action="store_true", default=False, + help="show layout-specific help") parser.add_option("-s", "--style", dest="style", default="default", help="choose style [default]") parser.add_option("-g", "--geometry", dest="geom", default="default", @@ -86,7 +80,7 @@ parser.add_option("--landscape", action="store_true", dest="landscape", default= def add_list_option(parser, opt): parser.add_option("--list-%s" % opt, action="store_true", dest="list_%s" % opt, default=False, help="list available %s" % opt) -for x in ["languages", "templates", "styles", "geometries"]: +for x in ["languages", "layouts", "styles", "geometries"]: add_list_option(parser, x) parser.add_option("--lang-var", action="append", dest="lang_assign", @@ -96,10 +90,14 @@ parser.add_option("--style-var", action="append", dest="style_assign", 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 +argv1 = [] +argv2 = [] +for x in sys.argv: + if x[0] == '-' and not parser.has_option(x): + argv2.append(x) + else: + argv1.append(x) +sys.argv = argv1 (options,args) = parser.parse_args() @@ -112,19 +110,36 @@ if options.list_styles: if options.list_geometries: for x in plugin_list("geom"): print x[0], print -if options.list_templates: - for x in plugin_list("templates"): print x[0], +if options.list_layouts: + for x in plugin_list("layouts"): print x[0], print if (options.list_languages or options.list_styles or - options.list_geometries or options.list_templates): sys.exit(0) - -if len(args) < 1 or len(args) > 3: - parser.print_help() - sys.exit(0) + options.list_geometries or options.list_layouts): sys.exit(0) Language = import_plugin("lang", "language", "languages", "--list-languages", options.lang) Style = import_plugin("style", "style", "styles", "--list-styles", options.style) Geometry = import_plugin("geom", "geometry", "geometries", "--list-geometries", options.geom) +Layout = import_plugin("layouts", "layout", "layouts", "--list-layouts", options.layout) + +for x in argv2: + if '=' in x: x = x[0:x.find('=')] + if not Layout.parser.has_option(x): + parser.error("invalid option %s; use --help (-h) or --layout-help (-H) to see available options" % x) + +(Layout.options,largs) = Layout.parser.parse_args(argv2) +if options.layouthelp: + #print "Help for layout:", options.layout + Layout.parser.print_help() + sys.exit(0) + + +# we can put it separately together with Layout; but we load Layout *after* lang,style,geom +if len(args) < 1 or len(args) > 3: + parser.print_help() + sys.exit(0) + +if (len(args[-1]) == 4 and args[-1].isdigit()): + print "WARNING: file name '%s' looks like a year, writing anyway..." % args[-1] # the usual "beware of exec()" crap applies here... but come on, # this is a SCRIPTING language, you can always hack the source code!!! @@ -192,44 +207,7 @@ elif len(args) == 3: Year = parse_year(args[1]) Outfile = args[2] -p = PDFPage(Outfile, options.landscape) - - -#1 1 1 -#2 2 1 -#3 3 1 - -#4 2 2 -#5 3 2 -#6 3 2 -#7 4 2 -#8 4 2 - -#9 3 3 -#10 4 3 -#11 4 3 -#12 4 3 -if MonthSpan < 4: cols = 1; rows = MonthSpan -elif MonthSpan < 9: cols = 2; rows = int(math.ceil(MonthSpan/2.0)) -else: cols = 3; rows = int(math.ceil(MonthSpan/3.0)) - -# else x = floor(sqrt(span))... consider x^2, (x+1)*x, (x+1)^2 - -R0,R1 = rect_vsplit(p.Text_rect, 0.05, 0.01) -Rcal,Rc = rect_vsplit(R1,0.97) -if options.landscape: rows,cols = cols,rows Geometry.landscape = options.landscape -Geometry.box_shadow_size = 6 -#rows = 1 -#cols = 6 -foo = GLayout(Rcal, rows, cols, pad = (p.Text_rect[2]*0.005,)*4) -for i in range(min(foo.count(),MonthSpan),0,-1): -#for i in range(1,min(foo.count(),MonthSpan)+1): - draw_month(p.cr, foo.item_seq(i-1), month=i+Month-1, year=Year, - theme = (Style, Geometry)) -draw_str(p.cr, text = str(Year), rect = R0, stroke_rgba = (0,0,0,0.3), align = 2, - font = (extract_font_name(Style.month.font),0,0)) -draw_str(p.cr, text = "rendered by Callirhoe ver. %s" % _version, - rect = Rc, stroke_rgba = (0,0,0,0.5), stretch = 0, align = 1, - font = (extract_font_name(Style.month.font),1,0)) +Layout.draw_calendar(Outfile, Year, Month, MonthSpan, (Style,Geometry), _version) + diff --git a/geom/default.py b/geom/default.py index 39c19ae..a4129b3 100644 --- a/geom/default.py +++ b/geom/default.py @@ -18,7 +18,7 @@ class dom: size = (0.5,0.5,0.8,0.5) # short 0-1, long 2-3 - mw_split = (0.618,0.1) + mw_split = (0.7,0.2) header_size = footer_size = (0.8,0.1) header_align = 0.1 diff --git a/templates/__init__.py b/layouts/__init__.py similarity index 100% rename from templates/__init__.py rename to layouts/__init__.py diff --git a/layouts/classic.py b/layouts/classic.py new file mode 100644 index 0000000..67c033e --- /dev/null +++ b/layouts/classic.py @@ -0,0 +1,336 @@ +# -*- 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/ + +# --- layouts.classic --- + +from lib.xcairo import * +from lib.geom import * +from math import floor, ceil, sqrt +import calendar +import optparse + +parser = optparse.OptionParser(usage="%prog (...) --layout classic [options] (...)",add_help_option=False) +parser.add_option("--rows", type="int", default=0, help="force grid rows [0]") +parser.add_option("--cols", type="int", default=0, + help="force grid columns [0]; if ROWS and COLS are both non-zero, " + "calendar will span multiple pages as needed; if one value is zero, it " + "will be computed automatically in order to fill exactly 1 page") +parser.add_option("--padding", type="float", default=4, help="padding (in mm) around month boxes [4]") +parser.add_option("--grid-order", choices=["row","column"],default="row", + help="either 'row' or 'column' to set grid placing order row-wise or column-wise [row]") +parser.add_option("--z-order", choices=["auto", "increasing", "decreasing"], default="auto", + help="either 'increasing' or 'decreasing' to set whether next month (in grid order) " + "lies above or below the previously drawn month; this affects shadow casting, " + "since rendering is always performed in increasing z-order; specifying 'auto' " + "selects increasing order if and only if sloppy boxes are enabled [auto]") +parser.add_option("--month-with-year", action="store_true", default=False, + help="displays year together with month name, e.g. January 1980; suppresses year from footer line") +parser.add_option("--long-daycells", action="store_const", const=0.0, dest="short_daycell_ratio", + help="force use of only long daycells") +parser.add_option("--short-daycells", action="store_const", const=1.0e6, dest="short_daycell_ratio", + help="force use of only short daycells") +parser.add_option("--bar", action="store_const", const=1.0e6, dest="month_bar_ratio", + help="force month drawing in bar mode") +parser.add_option("--matrix", action="store_const", const=0, dest="month_bar_ratio", + help="force month drawing in matrix mode") +parser.add_option("--short-daycell-ratio", type="float", default=2.5, + help="ratio threshold for day cells below which short version is drawn [2.5]") +parser.add_option("--month-bar-ratio", type="float", default=0.7, + help="ratio threshold for month box, below which bar is drawn [0.7]") +parser.add_option("--no-footer", action="store_true", default=False, + help="disable footer line (with year and rendered-by message)") + + +def weekrows_of_month(year, month): + day,span = calendar.monthrange(year, month) + if day == 0 and span == 28: return 4 + if day == 5 and span == 31: return 6 + if day == 6 and span >= 30: return 6 + return 5 + +def _draw_day_cell_short(cr, rect, day, header, footer, theme, show_day_name): + S,G = theme + x, y, w, h = rect + day_of_month, day_of_week = day + draw_box(cr, rect, S.frame, S.bg, S.frame_thickness) + R = rect_rel_scale(rect, G.size[0], G.size[1]) + if show_day_name: + Rdom, Rdow = rect_hsplit(R, *G.mw_split) + else: + Rdom = R + valign = 0 if show_day_name else 2 + # draw day of month (number) + draw_str(cr, text = str(day_of_month), rect = Rdom, stretch = -1, stroke_rgba = S.fg, + align = (2,valign), font = S.font, measure = "88") + # draw name of day + if show_day_name: + draw_str(cr, text = calendar.day_name[day_of_week][0], rect = Rdow, stretch = -1, stroke_rgba = S.fg, + align = (2,valign), font = S.font, measure = "88") + # draw header + if header: + R = rect_rel_scale(rect, G.header_size[0], G.header_size[1], 0, -1.0 + G.header_align) + draw_str(cr, text = header, rect = R, stretch = -1, stroke_rgba = S.header, font = S.header_font) + # draw footer + if footer: + R = rect_rel_scale(rect, G.footer_size[0], G.footer_size[1], 0, 1.0 - G.footer_align) + draw_str(cr, text = footer, rect = R, stretch = -1, stroke_rgba = S.footer, font = S.footer_font) + +def _draw_day_cell_long(cr, rect, day, header, footer, theme, show_day_name): + S,G = theme + x, y, w, h = rect + day_of_month, day_of_week = day + draw_box(cr, rect, S.frame, S.bg, S.frame_thickness) + R1, Rhf = rect_hsplit(rect, *G.hf_hsplit) + if show_day_name: + R = rect_rel_scale(R1, G.size[2], G.size[3]) + Rdom, Rdow = rect_hsplit(R, *G.mw_split) + else: + Rdom = rect_rel_scale(R1, G.size[0], G.size[1]) + valign = 0 if show_day_name else 2 + # draw day of month (number) + draw_str(cr, text = str(day_of_month), rect = Rdom, stretch = -1, stroke_rgba = S.fg, + align = (2,valign), font = S.font, measure = "88") + # draw name of day + if show_day_name: + draw_str(cr, text = calendar.day_name[day_of_week][0], rect = Rdow, stretch = -1, stroke_rgba = S.fg, + align = (2,valign), font = S.font, measure = "M") + Rh, Rf = rect_vsplit(Rhf, *G.hf_vsplit) + # draw header + if header: + draw_str(cr, text = header, rect = Rh, stretch = -1, stroke_rgba = S.header, align = (1,2), + font = S.header_font) + # draw footer + if footer: + draw_str(cr, text = footer, rect = Rf, stretch = -1, stroke_rgba = S.footer, align = (1,2), + font = S.footer_font) + +def draw_day_cell(cr, rect, day, header, footer, theme, show_day_name, short_thres): + if rect_ratio(rect) < short_thres: + _draw_day_cell_short(cr, rect, day, header, footer, theme, show_day_name) + else: + _draw_day_cell_long(cr, rect, day, header, footer, theme, show_day_name) + + +def draw_month_matrix(cr, rect, month, year, theme, daycell_thres): + S,G = theme + apply_rect(cr, rect, G.month.sloppy_dx, G.month.sloppy_dy, G.month.sloppy_rot) + + day, span = calendar.monthrange(year, month) + weekrows = weekrows_of_month(year, month) if G.month.asymmetric else 6 + dom = -day + 1; + wmeasure = 'A'*max(map(len,calendar.day_name)) + mmeasure = 'A'*max(map(len,calendar.month_name)) + + grid = GLayout(rect_from_origin(rect), weekrows+1, 7) + # 61.8% - 38.2% split (golden) + R_mb, R_db = rect_vsplit(grid.item_span(1, 7, 0, 0), 0.618) # month name bar, day name bar + R_dnc = HLayout(R_db, 7) # day name cells = 1/7-th of day name bar + + # draw box shadow + if S.month.box_shadow: + f = G.box_shadow_size + shad = (f,-f) if G.landscape else (f,f) + draw_shadow(cr, rect_from_origin(rect), shad) + + # draw day names + for col in range(7): + R = R_dnc.item(col) + draw_box(cr, rect = R, stroke_rgba = S.dom.frame, + fill_rgba = S.dom.bg if col < 5 else S.dom_weekend.bg, + width_scale = S.dow.frame_thickness) + R_text = rect_rel_scale(R, 1, 0.5) + draw_str(cr, text = calendar.day_name[col], rect = R_text, stretch = -1, stroke_rgba = S.dow.fg, + align = (2,0), font = S.dow.font, measure = wmeasure) + + # draw day cells + for row in range(weekrows): + for col in range(7): + day_style = S.dom_weekend if col >= 5 else S.dom + R = grid.item(row + 1, col) + if dom > 0 and dom <= span: + draw_day_cell(cr, rect = R, day = (dom, col), + header = None, footer = None, theme = (day_style, G.dom), show_day_name = False, + short_thres = daycell_thres) + else: + draw_box(cr, rect = R, stroke_rgba = day_style.frame, fill_rgba = day_style.bg, + width_scale = day_style.frame_thickness) + dom += 1 + + # draw month title (name) + mcolor = S.month.color_map[month] +# if year % 2 == 1: mcolor = color_scale(mcolor, 0.75) +# else: mcolor = color_scale(mcolor, 1.33) + mcolor_fg = color_auto_fg(mcolor) + draw_box(cr, rect = rect_from_origin(rect), stroke_rgba = S.month.frame, fill_rgba = (), + width_scale = S.month.frame_thickness) + draw_box(cr, rect = R_mb, stroke_rgba = S.month.frame, fill_rgba = mcolor) + R_text = rect_rel_scale(R_mb, 1, 0.5) + mshad = None + if S.month.text_shadow: + mshad = (0.5,-0.5) if G.landscape else (0.5,0.5) + title_str = calendar.month_name[month] + if options.month_with_year: title_str += ' ' + str(year) + draw_str(cr, text = title_str, rect = R_text, stretch = -1, stroke_rgba = mcolor_fg, + align = (2,0), font = S.month.font, measure = mmeasure, shadow = mshad) + cr.restore() + +def draw_month_bar(cr, rect, month, year, theme, daycell_thres): + S,G = theme + apply_rect(cr, rect, G.month.sloppy_dx, G.month.sloppy_dy, G.month.sloppy_rot) + + day, span = calendar.monthrange(year, month) + wmeasure = 'A'*max(map(len,calendar.day_name)) + mmeasure = 'A'*max(map(len,calendar.month_name)) + + rows = (span + 1) if G.month.asymmetric else 32 + grid = VLayout(rect_from_origin(rect), rows) + + # draw box shadow + if S.month.box_shadow: + f = G.box_shadow_size + shad = (f,-f) if G.landscape else (f,f) + draw_shadow(cr, rect_from_origin(rect), shad) + + # draw day cells + for dom in range(1,rows): + day_style = S.dom_weekend if day >= 5 and dom <= span else S.dom + R = grid.item(dom) + if dom <= span: + draw_day_cell(cr, rect = R, day = (dom, day), header = None, footer = None, + theme = (day_style, G.dom), show_day_name = True, short_thres = daycell_thres) + else: + draw_box(cr, rect = R, stroke_rgba = day_style.frame, fill_rgba = day_style.bg, + width_scale = day_style.frame_thickness) + day = (day + 1) % 7 + + # draw month title (name) + mcolor = S.month.color_map[month] + mcolor_fg = color_auto_fg(mcolor) + draw_box(cr, rect = rect_from_origin(rect), stroke_rgba = S.month.frame, fill_rgba = (), + width_scale = S.month.frame_thickness) + R_mb = grid.item(0) + draw_box(cr, rect = R_mb, stroke_rgba = S.month.frame, fill_rgba = mcolor) + R_text = rect_rel_scale(R_mb, 1, 0.5) + mshad = None + if S.month.text_shadow: + mshad = (0.5,-0.5) if G.landscape else (0.5,0.5) + draw_str(cr, text = calendar.month_name[month], rect = R_text, stretch = -1, stroke_rgba = mcolor_fg, + align = (2,0), font = S.month.font, measure = mmeasure, shadow = mshad) + cr.restore() + +def draw_month(cr, rect, month, year, theme, bar_thres = 0.7, daycell_thres = 2.5): + if rect_ratio(rect) >= bar_thres: + draw_month_matrix(cr, rect, month, year, theme, daycell_thres) + else: + draw_month_bar(cr, rect, month, year, theme, daycell_thres) + +#1 1 1 +#2 2 1 +#3 3 1 + +#4 2 2 +#5 3 2 +#6 3 2 +#7 4 2 +#8 4 2 + +#9 3 3 +#10 4 3 +#11 4 3 +#12 4 3 + +#rows = 0 +#cols = 0 + +def draw_calendar(Outfile, Year, Month, MonthSpan, Theme, version_string): + S,G = Theme + G.box_shadow_size = 6 + rows, cols = options.rows, options.cols + + page = PDFPage(Outfile, G.landscape) + + if rows == 0 and cols == 0: +# if MonthSpan < 4: +# cols = 1; rows = MonthSpan +# elif MonthSpan < 9: +# cols = 2; rows = int(math.ceil(MonthSpan/2.0)) +# else: + # TODO: improve this heuristic + cols = int(floor(sqrt(MonthSpan))) + rows = cols + if rows*cols < MonthSpan: rows += 1 + if rows*cols < MonthSpan: rows += 1 + if rows*cols < MonthSpan: cols += 1; rows -= 1 + if G.landscape: rows, cols = cols, rows + elif rows == 0: + rows = int(ceil(MonthSpan*1.0/cols)) + elif cols == 0: + cols = int(ceil(MonthSpan*1.0/rows)) + + if not options.no_footer: + V0 = VLayout(page.Text_rect, 40, (1,)*4) + Rcal = V0.item_span(39,0) + Rc = V0.item(39) + else: + Rcal = page.Text_rect + + grid = GLayout(Rcal, rows, cols, pad = (options.padding,)*4) + mpp = grid.count() # months per page + num_pages = int(ceil(MonthSpan*1.0/mpp)) + cur_month = Month + cur_year = Year + num_placed = 0 + page_layout = [] + for k in xrange(num_pages): + page_layout.append([]) + for i in xrange(mpp): + page_layout[k].append((cur_month,cur_year)) + num_placed += 1 + cur_month += 1 + if cur_month > 12: cur_month = 1; cur_year += 1 + if num_placed >= MonthSpan: break + + num_pages_written = 0 + + z_order = options.z_order + if z_order == "auto": + if G.month.sloppy_dx != 0 or G.month.sloppy_dy != 0 or G.month.sloppy_rot != 0: + z_order = "decreasing" + else: + z_order = "increasing" + for p in page_layout: + num_placed = 0 + yy = [p[0][1]] + if z_order == "decreasing": p.reverse() + for (m,y) in p: + k = len(p) - num_placed - 1 if z_order == "decreasing" else num_placed + draw_month(page.cr, grid.item_seq(k, options.grid_order == "column"), + month=m, year=y, theme = Theme, + bar_thres = options.month_bar_ratio, daycell_thres = options.short_daycell_ratio) + num_placed += 1 + if (y > yy[-1]): yy.append(y) + if not options.month_with_year and not options.no_footer: + year_str = str(yy[0]) if yy[0] == yy[-1] else "%s – %s" % (yy[0],yy[-1]) + draw_str(page.cr, text = year_str, rect = Rc, stroke_rgba = (0,0,0,0.5), stretch = 0, + align = (0,0), font = (extract_font_name(S.month.font),0,0)) + if not options.no_footer: + draw_str(page.cr, text = "rendered by Callirhoe ver. %s" % version_string, + rect = Rc, stroke_rgba = (0,0,0,0.5), stretch = 0, align = (1,0), + font = (extract_font_name(S.month.font),1,0)) + num_pages_written += 1 + if num_pages_written < num_pages: + page.cr.show_page() diff --git a/lib/geom.py b/lib/geom.py index 13397f4..c7c8d00 100644 --- a/lib/geom.py +++ b/lib/geom.py @@ -66,11 +66,13 @@ def rect_vsplit(r, f = 0.5, fdist = 0.0): return (r1, r2) 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_scale(a, frac): + return map(lambda x: min(1.0,x*frac), a) + 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 + return light if (1*bg[0] + 2*bg[1] + 3*bg[2])/6.0 < 0.24 else dark # ********* layout managers *********** diff --git a/lib/plugin.py b/lib/plugin.py index 4c0db41..6dc5554 100644 --- a/lib/plugin.py +++ b/lib/plugin.py @@ -41,5 +41,10 @@ def available_files(parent, dir, fmatch = ""): def plugin_list(cat): return available_files(plugin_path[0], cat) + available_files(plugin_path[1], cat) +# cat = lang (category) +# longcat = language +# longcat2 = languages +# listopt = --list-lang +# preset = "EN" plugin_path = [ os.path.expanduser("~/.callirhoe"), sys.path[0] if sys.path[0] else "." ] diff --git a/lib/render.py b/lib/render.py deleted file mode 100644 index 9ce2cc7..0000000 --- a/lib/render.py +++ /dev/null @@ -1,190 +0,0 @@ -# -*- 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 * - -def weekrows_of_month(year, month): - day,span = calendar.monthrange(year, month) - if day == 0 and span == 28: return 4 - if day == 5 and span == 31: return 6 - if day == 6 and span >= 30: return 6 - return 5 - -def _draw_day_cell_short(cr, rect, day, header, footer, theme, show_day_name): - S,G = theme - x, y, w, h = rect - day_of_month, day_of_week = day - draw_box(cr, rect, S.frame, S.bg, S.frame_thickness) - R = rect_rel_scale(rect, G.size[0], G.size[1], 0, -0.1) - if show_day_name: - Rdom, Rdow = rect_hsplit(R, *G.mw_split) - else: - Rdom = R - draw_str(cr, text = str(day_of_month), rect = Rdom, stretch = -1, stroke_rgba = S.fg, align = 2, - font = S.font, measure = "55") - if show_day_name: - draw_str(cr, text = calendar.day_name[day_of_week][0], rect = Rdow, stretch = -1, stroke_rgba = S.fg, align = 2, - font = S.font, measure = "55") - - if header: - R = rect_rel_scale(rect, G.header_size[0], G.header_size[1], 0, -1.0 + G.header_align) - draw_str(cr, text = header, rect = R, stretch = -1, stroke_rgba = S.header, align = 2, - font = S.header_font) - if footer: - R = rect_rel_scale(rect, G.footer_size[0], G.footer_size[1], 0, 1.0 - G.footer_align) - draw_str(cr, text = footer, rect = R, stretch = -1, stroke_rgba = S.footer, align = 2, - font = S.footer_font) - -def _draw_day_cell_long(cr, rect, day, header, footer, theme, show_day_name): - S,G = theme - x, y, w, h = rect - day_of_month, day_of_week = day - draw_box(cr, rect, S.frame, S.bg, S.frame_thickness) - R1, Rhf = rect_hsplit(rect, *G.hf_hsplit) - if show_day_name: - R = rect_rel_scale(R1, G.size[2], G.size[3]) - Rdom, Rdow = rect_hsplit(R, *G.mw_split) - else: - Rdom = rect_rel_scale(R1, G.size[0], G.size[1]) - draw_str(cr, text = str(day_of_month), rect = Rdom, stretch = -1, stroke_rgba = S.fg, align = 2, - font = S.font, measure = "55", bbox = False) - if show_day_name: - draw_str(cr, text = calendar.day_name[day_of_week][0], rect = Rdow, stretch = -1, stroke_rgba = S.fg, align = 2, - font = S.font, measure = "M", bbox = False) - Rh, Rf = rect_vsplit(Rhf, *G.hf_vsplit) - if header: - draw_str(cr, text = header, rect = Rh, stretch = -1, stroke_rgba = S.header, align = 1, - font = S.header_font, bbox = False) - if footer: - draw_str(cr, text = footer, rect = Rf, stretch = -1, stroke_rgba = S.footer, align = 1, - font = S.footer_font, bbox = False) - -def draw_day_cell(cr, rect, day, header, footer, theme, show_day_name): - if rect_ratio(rect) > 2: - _draw_day_cell_long(cr, rect, day, header, footer, theme, show_day_name) - else: - _draw_day_cell_short(cr, rect, day, header, footer, theme, show_day_name) - - -def _draw_month_matrix(cr, rect, month, year, theme): - S,G = theme - apply_rect(cr, rect, G.month.sloppy_dx, G.month.sloppy_dy, G.month.sloppy_rot) - - day, span = calendar.monthrange(year, month) - weekrows = weekrows_of_month(year, month) if G.month.asymmetric else 6 - dom = -day + 1; - wmeasure = 'A'*max(map(len,calendar.day_name)) - mmeasure = 'A'*max(map(len,calendar.month_name)) - - grid = GLayout(rect_from_origin(rect), weekrows+1, 7) - # 61.8% - 38.2% split (golden) - R_mb, R_db = rect_vsplit(grid.item_span(1, 7, 0, 0), 0.618) # month name bar, day name bar - R_dnc = HLayout(R_db, 7) # day name cells = 1/7-th of day name bar - - if S.month.box_shadow: - f = G.box_shadow_size - shad = (f,-f) if G.landscape else (f,f) - draw_shadow(cr, rect_from_origin(rect), shad) - for col in range(7): - R = R_dnc.item(col) - draw_box(cr, rect = R, stroke_rgba = S.dom.frame, - fill_rgba = S.dom.bg if col < 5 else S.dom_weekend.bg, - width_scale = S.dow.frame_thickness) - R_text = rect_rel_scale(R, 1, 0.5) - draw_str(cr, text = calendar.day_name[col], rect = R_text, stretch = -1, stroke_rgba = S.dow.fg, - align = 2, font = S.dow.font, measure = wmeasure) - for row in range(weekrows): - for col in range(7): - day_style = S.dom_weekend if col >= 5 else S.dom - R = grid.item(row + 1, col) - if dom > 0 and dom <= span: - draw_day_cell(cr, rect = R, day = (dom, col), - header = None, footer = None, theme = (day_style, G.dom), show_day_name = False) - else: - draw_box(cr, rect = R, stroke_rgba = day_style.frame, fill_rgba = day_style.bg, - width_scale = day_style.frame_thickness) - dom += 1 - - mcolor = S.month.color_map[month] - mcolor_fg = color_auto_fg(mcolor) - draw_box(cr, rect = rect_from_origin(rect), stroke_rgba = S.month.frame, fill_rgba = (), - width_scale = S.month.frame_thickness) - draw_box(cr, rect = R_mb, stroke_rgba = S.month.frame, fill_rgba = mcolor) - R_text = rect_rel_scale(R_mb, 1, 0.5) - mshad = None - if S.month.text_shadow: - mshad = (0.5,-0.5) if G.landscape else (0.5,0.5) - draw_str(cr, text = calendar.month_name[month], rect = R_text, stretch = -1, stroke_rgba = mcolor_fg, - align = 2, font = S.month.font, measure = mmeasure, shadow = mshad) - cr.restore() - -def _draw_month_bar(cr, rect, month, year, theme): - S,G = theme - apply_rect(cr, rect, G.month.sloppy_dx, G.month.sloppy_dy, G.month.sloppy_rot) - - day, span = calendar.monthrange(year, month) - wmeasure = 'A'*max(map(len,calendar.day_name)) - mmeasure = 'A'*max(map(len,calendar.month_name)) - - rows = (span + 1) if G.month.asymmetric else 32 - grid = VLayout(rect_from_origin(rect), rows) - - if S.month.box_shadow: - f = G.box_shadow_size - shad = (f,-f) if G.landscape else (f,f) - draw_shadow(cr, rect_from_origin(rect), shad) - for dom in range(1,rows): - day_style = S.dom_weekend if day >= 5 and dom <= span else S.dom - R = grid.item(dom) - if dom <= span: - draw_day_cell(cr, rect = R, day = (dom, day), - header = None, footer = None, theme = (day_style, G.dom), show_day_name = True) - else: - draw_box(cr, rect = R, stroke_rgba = day_style.frame, fill_rgba = day_style.bg, - width_scale = day_style.frame_thickness) - day = (day + 1) % 7 - - mcolor = S.month.color_map[month] - mcolor_fg = color_auto_fg(mcolor) - draw_box(cr, rect = rect_from_origin(rect), stroke_rgba = S.month.frame, fill_rgba = (), - width_scale = S.month.frame_thickness) - R_mb = grid.item(0) - draw_box(cr, rect = R_mb, stroke_rgba = S.month.frame, fill_rgba = mcolor) - R_text = rect_rel_scale(R_mb, 1, 0.5) - mshad = None - if S.month.text_shadow: - mshad = (0.5,-0.5) if G.landscape else (0.5,0.5) - draw_str(cr, text = calendar.month_name[month], rect = R_text, stretch = -1, stroke_rgba = mcolor_fg, - align = 2, font = S.month.font, measure = mmeasure, shadow = mshad) - cr.restore() - -def draw_month(cr, rect, month, year, theme): - if rect_ratio(rect) > 0.75: - _draw_month_matrix(cr, rect, month, year, theme) - else: - _draw_month_bar(cr, rect, month, year, theme) diff --git a/lib/xcairo.py b/lib/xcairo.py index e7605c9..ded526e 100644 --- a/lib/xcairo.py +++ b/lib/xcairo.py @@ -37,8 +37,7 @@ class Page(object): 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 self._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 + self.Margins = (15,)*4 # 15 mm 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]) @@ -116,8 +115,8 @@ def draw_box(cr, rect, stroke_rgba = (), fill_rgba = (), width_scale = 1.0, shad 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", measure = '', shadow = None): +def draw_str(cr, text, rect, stretch = -1, stroke_rgba = (), align = (2,0), bbox = False, + font = "Times", measure = None, shadow = None): x, y, w, h = rect cr.save() slant = weight = 0 @@ -126,32 +125,41 @@ def draw_str(cr, text, rect, stretch = -1, stroke_rgba = (), align = 0, bbox = F elif len(font) == 2: fontname, slant = font elif len(font) == 1: fontname = font[0] cr.select_font_face(fontname, slant, weight) - if not measure: measure = text + if measure is None: 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; + mw, mh = te[2], te[3] + ratio, tratio = w*1.0/h, mw*1.0/mh; + xratio, yratio = mw*1.0/w, mh*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) + if stretch == 0: crs = (1,1) + elif stretch == 1: crs = (1.0/xratio, 1.0/xratio) + elif stretch == 2: crs = (1.0/yratio, 1.0/yratio) + elif stretch == 3: crs = (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 + tw *= crs[0] + th *= crs[1] + px, py = x, y + h + if align[0] == 1: px += w - tw + elif align[0] == 2: px += (w-tw)/2.0 + if align[1] == 1: py -= h - th + elif align[1] == 2: py -= (h-th)/2.0 + cr.set_line_width(default_width(cr)) + cr.translate(px,py) + cr.scale(*crs) if shadow is not None: cr.set_source_rgba(0, 0, 0, 0.5) - u1, v1 = cr.user_to_device(px, py) + u1, v1 = cr.user_to_device(0, 0) u1 += shadow[0]; v1 += shadow[1] x1, y1 = cr.device_to_user(u1, v1) cr.move_to(x1, y1) cr.show_text(text) - cr.move_to(px, py) + cr.move_to(0, 0) 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() + if bbox: + draw_box(cr, (x, y, w, h), stroke_rgba) + #draw_box(cr, (x, y+h, mw*crs[0], -mh*crs[1]), stroke_rgba) + draw_box(cr, (px, py, tw, -th), stroke_rgba) diff --git a/style/bw.py b/style/bw.py index 2e2212a..30b13f2 100644 --- a/style/bw.py +++ b/style/bw.py @@ -47,8 +47,6 @@ class dom_holiday(dom): class dom_weekend_holiday_style(dom_holiday): bg = (0,0,0) -from lib.render import color_mix - class month: font = ("Times New Roman", 0, 1) frame = (0,0,0) diff --git a/style/default.py b/style/default.py index e710012..557b6fa 100644 --- a/style/default.py +++ b/style/default.py @@ -45,7 +45,7 @@ class dom_holiday(dom): class dom_weekend_holiday_style(dom_holiday): bg = (0.7,1,1) -from lib.render import color_mix +from lib.geom import color_mix class month: font = ("Times New Roman", 0, 1) diff --git a/style/screenshot.py b/style/screenshot.py index cba6598..5e5c1a6 100644 --- a/style/screenshot.py +++ b/style/screenshot.py @@ -45,7 +45,7 @@ class dom_holiday(dom): class dom_weekend_holiday_style(dom_holiday): bg = (0.7,1,1) -from lib.render import color_mix +from lib.geom import color_mix class month: font = "GFS Artemisia Bold" diff --git a/templates/tiles.py b/templates/tiles.py deleted file mode 100644 index e69de29..0000000