mirror of
https://github.com/oDinZu/callirhoe.git
synced 2025-02-22 00:04:52 -05:00
improved str rendering
implemented layout scheme git-svn-id: https://callirhoe.googlecode.com/svn/trunk@14 81c8bb96-aa45-f2e2-0eef-c4fa4a15c6df
This commit is contained in:
parent
b1670a5649
commit
80208e6a35
114
callirhoe.py
114
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)
|
||||
|
||||
|
@ -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
|
||||
|
336
layouts/classic.py
Normal file
336
layouts/classic.py
Normal file
@ -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()
|
@ -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 ***********
|
||||
|
||||
|
@ -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 "." ]
|
||||
|
190
lib/render.py
190
lib/render.py
@ -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)
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user