'one_page' option in the Ancestor/Descendant graphical repororts did not change the page type to 'custom'. also landscape orientation did not switch the page.height and page.width properties which made items print outside the page and give infinate loop errors. svn: r16708
905 lines
33 KiB
Python
905 lines
33 KiB
Python
#
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
#
|
|
# Copyright (C) 2008-2010 Craig J. Anderson
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
|
|
# $Id: Descendant.py .... ander882 $
|
|
|
|
"""Reports/Graphical Reports/Tree_Base"""
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# python modules
|
|
#
|
|
#------------------------------------------------------------------------
|
|
from gen.ggettext import sgettext as _
|
|
|
|
from gen.plug.report import utils as ReportUtils
|
|
|
|
from gen.display.name import displayer as name_displayer
|
|
|
|
from libsubstkeyword import SubstKeywords
|
|
|
|
PT2CM = ReportUtils.pt2cm
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Class Calc_Lines
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class CalcLines(object):
|
|
""" wrapper for libsubstkeyword and added functionality for
|
|
replacements.
|
|
|
|
Receive: Individual and family handle, and display format [string]
|
|
return: [Text] ready for a box.
|
|
"""
|
|
def __init__(self, dbase, repl):
|
|
self.database = dbase
|
|
self.display_repl = repl
|
|
#self.default_string = default_str
|
|
|
|
def calc_lines(self, _indi_handle, _fams_handle, workinglines):
|
|
"""
|
|
In this pass we will:
|
|
1. make our text and do our replacements
|
|
2. remove any extra (unwanted) lines with the compres option
|
|
"""
|
|
|
|
####################
|
|
#1.1 Get our line information here
|
|
subst = SubstKeywords(self.database, _indi_handle, _fams_handle)
|
|
lines = subst.replace_and_clean(workinglines)
|
|
|
|
####################
|
|
#1.2 do our replacements
|
|
lns = []
|
|
for line in lines:
|
|
for pair in self.display_repl:
|
|
if pair.count("/") == 1:
|
|
repl = pair.split("/", 1)
|
|
line = line.replace(repl[0], repl[1])
|
|
lns.append(line)
|
|
|
|
return lns
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Class Canvas/Pages
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class Page(object):
|
|
""" This class is a printable page.
|
|
Offsets from the canvas, Page numbers
|
|
boxes and lines
|
|
"""
|
|
def __init__(self, doc, canvas):
|
|
#parts from canvas
|
|
self.doc = doc
|
|
self.canvas = canvas
|
|
|
|
#parts about the page
|
|
self.page_x_offset = 0
|
|
self.page_y_offset = 0
|
|
self.x_page_num = 0
|
|
self.y_page_num = 0
|
|
self.boxes = [] #All object must derive from BoxBase
|
|
self.lines = [] #All must derive form Linebase
|
|
self.note = None
|
|
|
|
def is_blank(self):
|
|
""" Am I a blank page? Notes and Titles are boxes too """
|
|
return self.boxes == [] and self.lines == []
|
|
|
|
def add_box(self, box):
|
|
""" The box must derive from class Box_Base(Object): """
|
|
self.boxes.append(box)
|
|
box.page = self
|
|
|
|
def add_line(self, line):
|
|
""" Add a line onto this page """
|
|
self.lines.append(line)
|
|
|
|
def draw_border(self, line_name):
|
|
if self.y_page_num == 0:
|
|
self.doc.draw_line(line_name, 0, 0,
|
|
self.doc.get_usable_width(), 0)
|
|
if self.x_page_num == 0:
|
|
self.doc.draw_line(line_name, 0, 0, 0,
|
|
self.doc.get_usable_height())
|
|
if self.y_page_num == self.canvas.y_pages-1:
|
|
self.doc.draw_line(line_name, 0,
|
|
self.doc.get_usable_height(),
|
|
self.doc.get_usable_width(),
|
|
self.doc.get_usable_height())
|
|
if self.x_page_num == self.canvas.x_pages-1:
|
|
self.doc.draw_line(line_name, self.doc.get_usable_width(),
|
|
0, self.doc.get_usable_width(),
|
|
self.doc.get_usable_height())
|
|
|
|
def display(self):
|
|
""" Display all boxes and lines that are on this page """
|
|
for box in self.boxes:
|
|
box.display()
|
|
for line in self.lines:
|
|
line.display(self)
|
|
|
|
|
|
class Canvas(Page):
|
|
""" The Canvas is two things.
|
|
The all in one canvas. a canvas is a page of unlimited size
|
|
a group of pages. each page is set is size and shows only a
|
|
part of what is on the entire canvas
|
|
"""
|
|
def __init__(self, doc):
|
|
Page.__init__(self, doc, self)
|
|
self.doc = doc
|
|
self.report_opts = None
|
|
|
|
#How many pages are there in the report. one more than real.
|
|
self.x_pages = 1
|
|
self.y_pages = 1
|
|
self.__pages = {(0, 0): self} #set page 0,0 to me.
|
|
self.__fonts = {} #keep a list of fonts so we don't have to lookup.
|
|
self.title = None
|
|
self.note = None
|
|
|
|
def __new_page(self, x_page, y_page, x_offset, y_offset):
|
|
""" Make a new page. This will only happen if we are
|
|
paginating (making new pages to hold parts of the canvas) """
|
|
if x_page >= self.x_pages:
|
|
self.x_pages = x_page + 1
|
|
new_page = Page(self.doc, self)
|
|
new_page.x_page_num = x_page
|
|
new_page.y_page_num = y_page
|
|
new_page.page_x_offset = x_offset
|
|
new_page.page_y_offset = y_offset
|
|
self.__pages[x_page, y_page] = new_page
|
|
return new_page
|
|
|
|
def sort_boxes_on_y_cm(self):
|
|
""" sorts the list of boxes on the canvas by .y_cm (top down) """
|
|
self.boxes.sort( key=lambda box: box.y_cm)
|
|
|
|
def add_title(self, title):
|
|
""" The title must derive from class TitleBox(BoxBase): """
|
|
self.title = title
|
|
|
|
def add_note(self, note):
|
|
""" The note must derive from class NoteBox(BoxBase, NoteType) """
|
|
self.note = note
|
|
self.set_box_height_width(self.note)
|
|
|
|
def __get_font(self, box):
|
|
""" returns the font used by a box. makes a list of all seen fonts
|
|
to be faster. If a new is found, run through the process to get it """
|
|
if not self.__fonts.has_key(box.boxstr):
|
|
style_sheet = self.doc.get_style_sheet()
|
|
style_name = style_sheet.get_draw_style(box.boxstr)
|
|
style_name = style_name.get_paragraph_style()
|
|
self.__fonts[box.boxstr] = \
|
|
style_sheet.get_paragraph_style(style_name).get_font()
|
|
|
|
return self.__fonts[box.boxstr]
|
|
|
|
def get_report_height_width(self):
|
|
""" returns the (max width, max height) of the report
|
|
This does not take into account any shadows """
|
|
max_width = 0
|
|
max_height = 0
|
|
for box in self.boxes:
|
|
tmp = box.x_cm + box.width
|
|
if tmp > max_width:
|
|
max_width = tmp
|
|
tmp = box.y_cm + box.height
|
|
if tmp > max_height:
|
|
max_height = tmp
|
|
max_width += self.doc.report_opts.box_shadow
|
|
max_width += self.doc.report_opts.littleoffset
|
|
max_height += self.doc.report_opts.box_shadow
|
|
max_height += self.doc.report_opts.littleoffset
|
|
return (max_width, max_height)
|
|
|
|
def __scale_canvas(self, scale_amount):
|
|
""" scales everything up/down depending upon scale_amount """
|
|
self.doc.report_opts.scale_everything(scale_amount)
|
|
self.title.scale(scale_amount)
|
|
if self.note is not None:
|
|
self.note.scale(scale_amount)
|
|
#scale down everyone!
|
|
for box in self.boxes:
|
|
box.scale(scale_amount)
|
|
|
|
def set_box_height_width(self, box):
|
|
""" Sets the .width and .height of a box. """
|
|
if box.boxstr == "None":
|
|
box.height = box.width = 0
|
|
return
|
|
|
|
font = self.__get_font(box)
|
|
#####################
|
|
#Get the width
|
|
for line in box.text:
|
|
width = self.doc.string_width(font, line)
|
|
width = PT2CM(width)
|
|
if width > box.width:
|
|
box.width = width
|
|
|
|
#####################
|
|
#Get the height
|
|
height = len(box.text) * font.get_size() * 1.5
|
|
height += 1.0/2.0 * font.get_size() #funny number(s) based upon font.
|
|
box.height = PT2CM(height)
|
|
|
|
def page_count(self, incblank):
|
|
count = 0
|
|
if incblank:
|
|
return self.y_pages * self.x_pages
|
|
|
|
for y_p in range(self.y_pages):
|
|
for x_p in range(self.x_pages):
|
|
if self.__pages.has_key((x_p, y_p)):
|
|
count += 1
|
|
return count
|
|
|
|
def page_iter_gen(self, incblank):
|
|
""" generate the pages of the report. do so in a left to right
|
|
up down approach. incblank asks to include blank pages """
|
|
blank = Page(self.doc, self)
|
|
for y_p in range(self.y_pages):
|
|
for x_p in range(self.x_pages):
|
|
if self.__pages.has_key((x_p, y_p)):
|
|
yield self.__pages[(x_p, y_p)]
|
|
else:
|
|
if incblank:
|
|
blank.x_page_num = x_p
|
|
blank.y_page_num = y_p
|
|
yield blank
|
|
|
|
def __add_box_to_page(self, x_page, y_page, x_offset, y_offset, box):
|
|
""" adds a box to a page. If the page is not there, make it first """
|
|
if not self.__pages.has_key((x_page, y_page)):
|
|
#Add the new page into the dictionary
|
|
self.__new_page(x_page, y_page, x_offset, y_offset)
|
|
|
|
#Add the box into the page
|
|
self.__pages[x_page, self.y_pages-1].add_box(box)
|
|
|
|
def scale_report(self, one_page, scale_to_width, scale_to_height):
|
|
""" We have a report in its full size (on the canvas
|
|
and pages to print on. scale one or both as needed/desired.
|
|
|
|
- one_page, boolean. Whether to make the page(or parts of) the size
|
|
of the report
|
|
- scale_to_width, boolean. Scale the report width to the page size?
|
|
- scale_to_height, boolean. Scale the report height to page size?
|
|
"""
|
|
|
|
if scale_to_width or scale_to_height:
|
|
max_width, max_height = self.canvas.get_report_height_width()
|
|
#max_width += self.doc.report_opts.littleoffset
|
|
#max_height += self.doc.report_opts.littleoffset
|
|
|
|
"""
|
|
calc - Calculate the scale amount (if any).
|
|
<1 everything is smaller to fit on the page
|
|
1 == no scaling
|
|
>1 make everything bigger to fill out the page
|
|
"""
|
|
scale = 1
|
|
scaled_report_to = None
|
|
|
|
#####################
|
|
#scale the report option - width
|
|
if scale_to_width:
|
|
#Check the width of the title
|
|
title_width = self.title.width
|
|
title_width += self.doc.report_opts.littleoffset * 2
|
|
|
|
max_width = max(title_width, max_width)
|
|
|
|
#This will be our base amount and
|
|
#then we will decrease only as needed from here.
|
|
|
|
scale = self.doc.get_usable_width() / max_width
|
|
scaled_report_to = "width"
|
|
|
|
#####################
|
|
#scale the report option - height
|
|
if scale_to_height:
|
|
tmp = self.doc.get_usable_height() / max_height
|
|
if not scale_to_width or tmp < scale:
|
|
scale = tmp
|
|
scaled_report_to = "height"
|
|
|
|
#Now I have the scale amount
|
|
if scale != 1: #scale everything on the canvas
|
|
self.__scale_canvas(scale)
|
|
|
|
#####################
|
|
#Scale the page option
|
|
if one_page:
|
|
|
|
#user wants PAGE to be the size of the report.
|
|
size = self.doc.paper.get_size()
|
|
size.name = 'custom'
|
|
|
|
max_width, max_height = \
|
|
self.canvas.get_report_height_width()
|
|
|
|
if scaled_report_to != "width":
|
|
#calculate the width of the report
|
|
#max_width += self.doc.report_opts.littleoffset
|
|
max_width += self.doc.paper.get_left_margin()
|
|
max_width += self.doc.paper.get_right_margin()
|
|
|
|
#calculate the width of the title
|
|
title_width = self.canvas.title.width
|
|
title_width += self.doc.paper.get_left_margin()
|
|
title_width += self.doc.paper.get_right_margin()
|
|
title_width += self.doc.report_opts.littleoffset
|
|
max_width = max(title_width, max_width)
|
|
|
|
size.set_width(max_width)
|
|
|
|
if scaled_report_to != "height":
|
|
#calculate the height of the report
|
|
max_height += self.doc.paper.get_top_margin()
|
|
max_height += self.doc.paper.get_bottom_margin()
|
|
#max_height += self.doc.report_opts.littleoffset
|
|
size.set_height(max_height)
|
|
|
|
return scale
|
|
|
|
|
|
def __paginate_x_offsets(self, colsperpage):
|
|
""" Go through the boxes and get the x page offsets """
|
|
#fix soon. should not use .level
|
|
liloffset = self.doc.report_opts.littleoffset
|
|
x_page_offsets = {0:0} #change me to [] ???
|
|
for box in self.boxes:
|
|
x_index = box.level[0]
|
|
x_page = x_index / colsperpage
|
|
if x_page not in x_page_offsets and x_index % colsperpage == 0:
|
|
x_page_offsets[x_page] = box.x_cm - liloffset
|
|
if x_page >= self.x_pages:
|
|
self.x_pages = x_page+1
|
|
return x_page_offsets
|
|
|
|
def __paginate_y_pages(self, colsperpage, x_page_offsets):
|
|
""" Go through the boxes and put each one in a page
|
|
note that the self.boxes needs to be sorted by .y_cm """
|
|
page_y_top = [0]
|
|
page_y_height = [self.doc.get_usable_height()]
|
|
liloffset = self.doc.report_opts.littleoffset
|
|
|
|
for box in self.boxes:
|
|
#check to see if this box cross over to the next (y) page
|
|
height = box.y_cm + liloffset + box.height #+ box.shadow/2
|
|
|
|
if height > page_y_height[-1]:
|
|
#we went off the end
|
|
page_y_height.append(box.y_cm - liloffset + page_y_height[0])
|
|
page_y_top.append(box.y_cm - liloffset)
|
|
self.y_pages = len(page_y_height)
|
|
|
|
#Calculate my (x) page
|
|
#fix soon. should not use .level
|
|
x_page = box.level[0] / colsperpage
|
|
|
|
self.__add_box_to_page(x_page, self.y_pages-1,
|
|
x_page_offsets[x_page],
|
|
page_y_top[self.y_pages-1],
|
|
box)
|
|
#if not self.__pages.has_key((x_page, self.y_pages-1)):
|
|
# #Add the new page into the dictionary
|
|
# self.__new_page(x_page, self.y_pages-1,
|
|
# )
|
|
#
|
|
##Add the box into the page
|
|
#self.__pages[x_page, self.y_pages-1].add_box(box)
|
|
return page_y_top
|
|
|
|
def __paginate_note(self, x_page_offsets, page_y_top):
|
|
""" Put the note on first. it can be overwritten by other
|
|
boxes but it CAN NOT overwrite a box. """
|
|
x_page, y_page = self.note.set_on_page(self)
|
|
if not self.__pages.has_key((x_page, y_page)):
|
|
#Add the new page into the dictionary
|
|
self.__new_page(x_page, y_page,
|
|
x_page_offsets[x_page], page_y_top[y_page])
|
|
#Add the box into the page
|
|
self.__pages[x_page, y_page].boxes.insert(0, self.note)
|
|
self.note.doc = self.doc
|
|
self.note.page = self
|
|
|
|
def __paginate_lines(self, x_page_offsets, page_y_top):
|
|
""" Step three go through the lines and put each in page(s) """
|
|
for box1 in self.boxes:
|
|
if not box1.line_to:
|
|
continue
|
|
|
|
line = box1.line_to
|
|
|
|
pages = [box1.page.y_page_num]
|
|
|
|
end = line.start + line.end
|
|
|
|
x_page = box1.page.x_page_num
|
|
start_y_page = end[0].page.y_page_num
|
|
end_y_page = end[0].page.y_page_num
|
|
|
|
for box in end:
|
|
y_page = box.page.y_page_num
|
|
if y_page not in pages:
|
|
if not self.__pages.has_key((x_page, y_page)):
|
|
#Add the new page into the dictionary
|
|
self.__new_page(x_page, y_page,
|
|
x_page_offsets[x_page],
|
|
page_y_top[y_page])
|
|
self.__pages[x_page, y_page].add_line(box1.line_to)
|
|
pages.append(y_page)
|
|
|
|
if y_page < start_y_page:
|
|
start_y_page = y_page
|
|
if y_page > end_y_page:
|
|
end_y_page = y_page
|
|
|
|
#if len(end) = 2 & end[0].y_page = 0 & end[1].y_page = 4
|
|
#the line will not print on y_pages 1,2,3. Fix that here.
|
|
#x_page = start_x_page
|
|
for y_page in range(start_y_page, end_y_page+1):
|
|
if y_page not in pages:
|
|
if not self.__pages.has_key((x_page, y_page)):
|
|
#Add the new page into the dictionary
|
|
self.__new_page(x_page, y_page,
|
|
x_page_offsets[x_page],
|
|
page_y_top[y_page])
|
|
self.__pages[x_page, y_page].add_line(box1.line_to)
|
|
|
|
def __paginate_title(self, x_page_offsets):
|
|
#step four work with the title
|
|
if self.title.boxstr == "None":
|
|
return
|
|
#x_page_offsets[page] tells me the widths I can use
|
|
if len(x_page_offsets) > 1:
|
|
title_list = self.title.text.split(" ")
|
|
title_font = self.__get_font(self.title)
|
|
#space_width = PT2CM(self.doc.string_width(title_font," "))
|
|
|
|
list_title = [title_list.pop(0)]
|
|
while len(title_list):
|
|
tmp = list_title[-1] + " " + title_list[0]
|
|
if PT2CM(self.doc.string_width(title_font, tmp)) > \
|
|
x_page_offsets[1]:
|
|
list_title.append("")
|
|
if list_title[-1] != "":
|
|
list_title[-1] += " "
|
|
list_title[-1] += title_list.pop(0)
|
|
|
|
start_page = (len(x_page_offsets) - len(list_title)) / 2
|
|
for tmp in range(start_page):
|
|
list_title.insert(0, "")
|
|
list_title.append("")
|
|
#one extra for security. doesn't hurt.
|
|
list_title.append("")
|
|
|
|
x_page = 0
|
|
for title in list_title:
|
|
if title == "":
|
|
x_page += 1
|
|
continue
|
|
if not self.__pages.has_key((x_page, 0)):
|
|
#Add the new page into the dictionary
|
|
self.__new_page(x_page, 0, x_page_offsets[1], 0)
|
|
|
|
title_part = TitleBox(self.doc, self.title.boxstr)
|
|
title_part.text = list_title[x_page]
|
|
title_part.width = x_page_offsets[1]
|
|
|
|
#Add the box into the page
|
|
self.__pages[x_page, 0].add_box(title_part)
|
|
x_page = x_page + 1
|
|
else:
|
|
self.title.width = self.doc.get_usable_width()
|
|
self.__pages[0, 0].add_box(self.title)
|
|
|
|
def __paginate(self, colsperpage):
|
|
""" take the boxes on the canvas and put them into separate pages.
|
|
The boxes need to be sorted by y_cm """
|
|
liloffset = self.doc.report_opts.littleoffset
|
|
self.__pages = {}
|
|
x_page_offsets = self.__paginate_x_offsets(colsperpage)
|
|
page_y_top = self.__paginate_y_pages(colsperpage, x_page_offsets)
|
|
|
|
if self.note is not None:
|
|
self.__paginate_note(x_page_offsets, page_y_top)
|
|
self.__paginate_lines(x_page_offsets, page_y_top)
|
|
self.__paginate_title(x_page_offsets)
|
|
|
|
|
|
def paginate(self, colsperpage, one_page_report):
|
|
""" self.boxes must be sorted by box.y_cm for this to work. """
|
|
if one_page_report:
|
|
#self.canvas.add_box(self.canvas.title)
|
|
title_part = TitleBox(self.doc, self.title.boxstr)
|
|
title_part.text = self.title.text
|
|
title_part.width = self.doc.get_usable_width()
|
|
self.add_box(title_part)
|
|
|
|
if self.note is not None:
|
|
self.note.set_on_page(self)
|
|
self.boxes.insert(0, self.note)
|
|
self.note.doc = self.doc
|
|
self.note.page = self
|
|
else:
|
|
self.__paginate(colsperpage)
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Class Box_Base
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class BoxBase(object):
|
|
""" boxes are always in/on a Page
|
|
Needed to print are: boxstr, text, x_cm, y_cm, width, height
|
|
"""
|
|
def __init__(self):
|
|
self.page = None
|
|
|
|
#'None' will cause an error. Sub-classes will init
|
|
self.boxstr = "None"
|
|
self.text = ""
|
|
#level requires ...
|
|
# (# - which column am I in (zero based)
|
|
# ,# - Am I a (0)direct descendant/ancestor or (>0)other
|
|
# , ) - anything else the report needs to run
|
|
self.level = (0,0)
|
|
self.x_cm = 0.0
|
|
self.y_cm = 0.0
|
|
self.width = 0.0
|
|
self.height = 0.0
|
|
self.line_to = None
|
|
|
|
def scale(self, scale_amount):
|
|
""" Scale the amounts """
|
|
self.x_cm *= scale_amount
|
|
self.y_cm *= scale_amount
|
|
self.width *= scale_amount
|
|
self.height *= scale_amount
|
|
|
|
def display(self):
|
|
""" display the box accounting for page x, y offsets
|
|
Ignore any box with 'None' is boxstr """
|
|
if self.boxstr == "None":
|
|
return
|
|
|
|
text = '\n'.join(self.text)
|
|
xbegin = self.x_cm - self.page.page_x_offset
|
|
ybegin = self.y_cm - self.page.page_y_offset
|
|
|
|
self.page.doc.draw_box(self.boxstr,
|
|
text,
|
|
xbegin, ybegin,
|
|
self.width, self.height)
|
|
|
|
#I am responsible for my own lines. Do them here.
|
|
if self.line_to:
|
|
#draw my line out here.
|
|
self.line_to.display(self.page)
|
|
if self.page.x_page_num > 0 and self.level[1] == 0 and \
|
|
xbegin < self.page.doc.report_opts.littleoffset*2:
|
|
#I am a child on the first column
|
|
yme = ybegin + self.height/2
|
|
self.page.doc.draw_line(self.page.doc.report_opts.line_str, \
|
|
0, yme, xbegin, yme)
|
|
|
|
|
|
|
|
class TitleBox(BoxBase):
|
|
"""
|
|
Holds information about the Title that will print on a page
|
|
"""
|
|
def __init__(self, doc, boxstr):
|
|
""" initalize the title box """
|
|
BoxBase.__init__(self)
|
|
self.doc = doc
|
|
self.boxstr = boxstr
|
|
if boxstr == "None":
|
|
return
|
|
self.cm_y = self.doc.report_opts.littleoffset
|
|
|
|
style_sheet = self.doc.get_style_sheet()
|
|
style_name = style_sheet.get_draw_style(self.boxstr)
|
|
style_name = style_name.get_paragraph_style()
|
|
self.font = style_sheet.get_paragraph_style(style_name).get_font()
|
|
|
|
def set_box_height_width(self):
|
|
if self.boxstr == "None":
|
|
return
|
|
#fix me. width should be the printable area
|
|
self.width = PT2CM(self.doc.string_width(self.font, self.text))
|
|
self.height = PT2CM(self.font.get_size() * 1.2)
|
|
|
|
def _get_names(self, persons):
|
|
""" A helper function that receives a list of persons and
|
|
returns their names in a list """
|
|
return [name_displayer.display(person) for person in persons]
|
|
|
|
def display(self):
|
|
""" display the title box. """
|
|
if self.page.y_page_num or self.boxstr == "None":
|
|
return
|
|
if self.text:
|
|
self.doc.center_text(self.boxstr, self.text,
|
|
self.width/2, self.y_cm)
|
|
|
|
class PageNumberBox(BoxBase):
|
|
"""
|
|
Calculates information about the page numbers that will print on a page
|
|
do not put in a value for PageNumberBox.text. this will be calculated for
|
|
each page """
|
|
|
|
def __init__(self, doc, boxstr):
|
|
""" initalize the page number box """
|
|
BoxBase.__init__(self)
|
|
self.doc = doc
|
|
self.boxstr = boxstr
|
|
|
|
def __calc_position(self, page):
|
|
""" calculate where I am to print on the page(s) """
|
|
self.text = "(%d,%d)"
|
|
|
|
style_sheet = self.doc.get_style_sheet()
|
|
style_name = style_sheet.get_draw_style(self.boxstr)
|
|
style_name = style_name.get_paragraph_style()
|
|
font = style_sheet.get_paragraph_style(style_name).get_font()
|
|
|
|
#calcualate how much space is needed
|
|
if page.canvas.x_pages > 10:
|
|
tmp = "00"
|
|
else:
|
|
tmp = "0"
|
|
if page.canvas.y_pages > 10:
|
|
tmp += "00"
|
|
else:
|
|
tmp += "0"
|
|
|
|
width = self.doc.string_width(font, '(,)'+tmp)
|
|
width = PT2CM(width)
|
|
self.width = width
|
|
|
|
height = font.get_size() * 1.4
|
|
height += 0.5/2.0 * font.get_size() #funny number(s) based upon font.
|
|
self.height = PT2CM(height)
|
|
|
|
self.x_cm = self.doc.get_usable_width() - self.width
|
|
self.y_cm = self.doc.get_usable_height() - self.height
|
|
|
|
def display(self, page):
|
|
""" If this is the first time I am ran, get my position
|
|
then display the page number """
|
|
if self.text == "":
|
|
self.__calc_position(page)
|
|
|
|
self.doc.draw_text(self.boxstr,
|
|
self.text % (page.x_page_num+1,page.y_page_num+1),
|
|
self.x_cm, self.y_cm)
|
|
|
|
class NoteType(object):
|
|
""" Provide the different options (corners) to place the note """
|
|
|
|
TOPLEFT = 0
|
|
TOPRIGHT = 1
|
|
BOTTOMLEFT = 2
|
|
BOTTOMRIGHT = 3
|
|
|
|
_DEFAULT = BOTTOMRIGHT
|
|
|
|
_DATAMAP = [
|
|
(TOPLEFT, _("Top Left"), "Top Left"),
|
|
(TOPRIGHT, _("Top Right"), "Top Right"),
|
|
(BOTTOMLEFT, _("Bottom Left"), "Bottom Left"),
|
|
(BOTTOMRIGHT, _("Bottom Right"), "Bottom Right"),
|
|
]
|
|
|
|
def __init__(self, value, exclude=None):
|
|
""" initalize GrampsType """
|
|
self.value = value
|
|
self.exclude = exclude
|
|
#GrampsType.__init__(self, value)
|
|
|
|
def note_locals(self, start=0):
|
|
""" generates an int of all the options """
|
|
for tuple in self._DATAMAP:
|
|
if tuple[0] != self.exclude:
|
|
yield tuple[0], tuple[1]
|
|
|
|
class NoteBox(BoxBase, NoteType):
|
|
""" Box that will hold the note to display on the page """
|
|
|
|
def __init__(self, doc, boxstr, locale, exclude=None):
|
|
""" initalize the NoteBox """
|
|
BoxBase.__init__(self)
|
|
NoteType.__init__(self, locale, exclude)
|
|
self.doc = doc
|
|
self.boxstr = boxstr
|
|
|
|
def set_on_page(self, canvas):
|
|
""" set the x_cm and y_cm given
|
|
self.doc, leloffset, and title_height """
|
|
|
|
liloffset = self.doc.report_opts.littleoffset
|
|
#left or right side
|
|
if self.value == NoteType.BOTTOMLEFT or \
|
|
self.value == NoteType.TOPLEFT:
|
|
self.x_cm = liloffset
|
|
else:
|
|
self.x_cm = self.doc.get_usable_width() - self.width - liloffset
|
|
#top or bottom
|
|
if self.value == NoteType.TOPRIGHT or \
|
|
self.value == NoteType.TOPLEFT:
|
|
self.y_cm = canvas.title.height + liloffset*2
|
|
else:
|
|
self.y_cm = self.doc.get_usable_height() - self.height - liloffset
|
|
|
|
""" helper function for canvas.paginate().
|
|
return the (x, y) page I want to print on """
|
|
if self.value == NoteType.TOPLEFT:
|
|
return (0, 0)
|
|
elif self.value == NoteType.TOPRIGHT:
|
|
return (canvas.x_pages-1, 0)
|
|
elif self.value == NoteType.BOTTOMLEFT:
|
|
return (0, canvas.y_pages-1)
|
|
elif self.value == NoteType.BOTTOMRIGHT:
|
|
return (canvas.x_pages-1, canvas.y_pages-1)
|
|
|
|
def display(self):
|
|
""" position the box and display """
|
|
title = self.page.canvas.title
|
|
title_height = 0
|
|
if title is not None:
|
|
title_height = title.height
|
|
text = '\n'.join(self.text)
|
|
self.doc.draw_box(self.boxstr, text,
|
|
self.x_cm, self.y_cm,
|
|
self.width, self.height)
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Class Line_base
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class LineBase(object):
|
|
""" A simple line class.
|
|
self.start is the box that we are drawing a line from
|
|
self.end are the boxes that we are drawing lines to.
|
|
"""
|
|
def __init__(self, start):
|
|
#self.linestr = "None"
|
|
self.start = [start]
|
|
self.end = []
|
|
|
|
def add_to(self, person):
|
|
""" add destination boxes to draw this line to """
|
|
self.end.append(person)
|
|
|
|
def display(self, page):
|
|
""" display the line. left to right line. one start, multiple end.
|
|
page will tell us what parts of the line we can print """
|
|
if self.end == [] and len(self.start) == 1:
|
|
return
|
|
|
|
# y_cm and x_cm start points - take into account page offsets
|
|
#yme = self.start.y_cm + self.start.height/2 - page.page_y_offset
|
|
#if type(self.start) != type([]):
|
|
# self.start = [self.start]
|
|
start = self.start[0]
|
|
doc = start.page.doc
|
|
linestr = doc.report_opts.line_str
|
|
|
|
xbegin = start.x_cm + start.width - page.page_x_offset
|
|
# out 3/4 of the way and x_cm end point(s)
|
|
x34 = xbegin + (doc.report_opts.col_width * 3/4)
|
|
xend = xbegin + doc.report_opts.col_width
|
|
|
|
if x34 > 0: # > 0 tell us we are printing on this page.
|
|
usable_height = doc.get_usable_height()
|
|
#1 - Line from start box out
|
|
for box in self.start:
|
|
yme = box.y_cm + box.height/2 - page.page_y_offset
|
|
if box.page.y_page_num == page.y_page_num:
|
|
# and 0 < yme < usable_height and \
|
|
doc.draw_line(linestr, xbegin, yme, x34, yme)
|
|
|
|
#2 - veritcal line
|
|
mid = []
|
|
for box in self.start + self.end:
|
|
tmp = box.y_cm + box.height/2
|
|
mid.append(tmp)
|
|
mid.sort()
|
|
mid = [mid[0]-page.page_y_offset, mid[-1]-page.page_y_offset]
|
|
if mid[0] < 0:
|
|
mid[0] = 0
|
|
if mid[1] > usable_height:
|
|
mid[1] = usable_height
|
|
#draw the connecting vertical line.
|
|
doc.draw_line(linestr, x34, mid[0], x34, mid[1])
|
|
else:
|
|
x34 = 0
|
|
|
|
#3 - horizontal line(s)
|
|
for box in self.end:
|
|
if box.page.y_page_num == page.y_page_num:
|
|
yme = box.y_cm + box.height/2 - box.page.page_y_offset
|
|
doc.draw_line(linestr, x34, yme, xend, yme)
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Class report_options
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class ReportOptions(object):
|
|
"""
|
|
A simple class to hold various report information
|
|
Calculates
|
|
the gap between persons,
|
|
the column width, for lines,
|
|
the left hand spacing for spouses (Descendant report only)
|
|
"""
|
|
|
|
def __init__(self, doc, normal_font, normal_line):
|
|
""" initalize various report variables that are used """
|
|
self.box_pgap = PT2CM(1.25*normal_font.get_size()) #gap between persons
|
|
self.box_mgap = self.box_pgap /2 #gap between marriage information
|
|
self.box_shadow = PT2CM(normal_font.get_size()) * .6 #normal text
|
|
self.spouse_offset = PT2CM(doc.string_width(normal_font, "0"))
|
|
|
|
self.col_width = PT2CM(doc.string_width(normal_font, "(000,0)"))
|
|
self.littleoffset = PT2CM(1)
|
|
self.x_cm_cols = [self.littleoffset]
|
|
|
|
self.line_str = normal_line
|
|
|
|
#Things that will get added later
|
|
self.max_box_width = 0
|
|
self.max_box_height = 0
|
|
|
|
self.scale = 1
|
|
|
|
def scale_everything(self, amount):
|
|
""" Scale the amounts that are needed to generate a report """
|
|
self.scale = amount
|
|
|
|
self.col_width *= amount
|
|
self.littleoffset *= amount
|
|
|
|
self.max_box_width *= amount #box_width
|
|
self.spouse_offset *= amount
|
|
self.box_shadow *= amount
|
|
|
|
#=====================================
|
|
#"And Jesus said unto them ... , "If ye have faith as a grain of mustard
|
|
#seed, ye shall say unto this mountain, Remove hence to younder place; and
|
|
#it shall remove; and nothing shall be impossible to you."
|
|
#Romans 1:17
|