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:
geortz@gmail.com 2012-08-03 12:00:30 +00:00
parent b1670a5649
commit 80208e6a35
12 changed files with 421 additions and 284 deletions

View File

@ -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)

View File

@ -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
View 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()

View File

@ -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 ***********

View File

@ -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 "." ]

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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"

View File