2007-01-05 17:28:16 +05:30
|
|
|
#
|
|
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
|
|
#
|
2007-06-29 10:24:01 +05:30
|
|
|
# Copyright (C) 2000-2007 Donald N. Allingham
|
2007-01-05 17:28:16 +05:30
|
|
|
#
|
|
|
|
# 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
|
|
|
|
#
|
|
|
|
|
2007-07-19 03:09:56 +05:30
|
|
|
# $Id$
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
"""Printing interface based on gtk.Print* classes.
|
2007-08-11 04:44:12 +05:30
|
|
|
"""
|
2007-07-19 03:09:56 +05:30
|
|
|
|
|
|
|
__revision__ = "$Revision$"
|
2007-08-26 19:35:34 +05:30
|
|
|
__author__ = "Zsolt Foldvari"
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Python modules
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
from gettext import gettext as _
|
2007-08-26 19:35:34 +05:30
|
|
|
import os
|
2007-09-07 03:01:33 +05:30
|
|
|
from math import radians
|
2007-08-26 19:35:34 +05:30
|
|
|
##try:
|
|
|
|
##from cStringIO import StringIO
|
|
|
|
##except:
|
|
|
|
##from StringIO import StringIO
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
2007-07-19 03:09:56 +05:30
|
|
|
# Gramps modules
|
2007-01-05 17:28:16 +05:30
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
2007-08-26 19:35:34 +05:30
|
|
|
import const
|
2007-01-05 17:28:16 +05:30
|
|
|
import BaseDoc
|
|
|
|
from PluginUtils import register_text_doc, register_draw_doc, register_book_doc
|
2007-09-07 03:01:33 +05:30
|
|
|
from ReportBase import ReportUtils
|
|
|
|
##rgb_color = ReportUtils.rgb_color
|
2007-01-05 17:28:16 +05:30
|
|
|
import Errors
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Set up logging
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
import logging
|
2007-08-20 19:24:04 +05:30
|
|
|
log = logging.getLogger(".GtkPrint")
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-07-19 03:09:56 +05:30
|
|
|
#-------------------------------------------------------------------------
|
2007-01-05 17:28:16 +05:30
|
|
|
#
|
2007-07-19 03:09:56 +05:30
|
|
|
# GTK modules
|
2007-01-05 17:28:16 +05:30
|
|
|
#
|
2007-07-19 03:09:56 +05:30
|
|
|
#-------------------------------------------------------------------------
|
2007-01-05 17:28:16 +05:30
|
|
|
import gtk
|
|
|
|
import cairo
|
|
|
|
import pango
|
2007-08-12 16:07:11 +05:30
|
|
|
import pangocairo
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-07-19 03:09:56 +05:30
|
|
|
if gtk.pygtk_version < (2,10,0):
|
2007-08-18 23:53:18 +05:30
|
|
|
raise Errors.UnavailableError(_("PyGtk 2.10 or later is required"))
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Constants
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
2007-09-07 17:23:12 +05:30
|
|
|
|
|
|
|
# each element draws some extra information useful for debugging
|
|
|
|
DEBUG = False
|
|
|
|
|
|
|
|
# printer settings (might be needed to align for different platforms)
|
2007-08-26 19:35:34 +05:30
|
|
|
PRINTER_DPI = 72.0
|
|
|
|
PRINTER_SCALE = 1.0
|
2007-08-11 04:44:12 +05:30
|
|
|
|
2007-09-07 17:23:12 +05:30
|
|
|
# minimum spacing around a page in print preview
|
2007-08-29 03:58:58 +05:30
|
|
|
MARGIN = 6
|
|
|
|
|
2007-09-07 17:23:12 +05:30
|
|
|
# zoom modes in print preview
|
2007-08-29 03:58:58 +05:30
|
|
|
(ZOOM_BEST_FIT,
|
|
|
|
ZOOM_FIT_WIDTH,
|
|
|
|
ZOOM_FREE,) = range(3)
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# PrintPreview class
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
class PrintPreview:
|
|
|
|
"""Implement a dialog to show print preview.
|
|
|
|
"""
|
2007-08-29 03:58:58 +05:30
|
|
|
zoom_factors = {
|
|
|
|
0.50: '50%',
|
|
|
|
0.75: '75%',
|
|
|
|
1.00: '100%',
|
|
|
|
1.25: '125%',
|
|
|
|
1.50: '150%',
|
|
|
|
1.75: '175%',
|
|
|
|
2.00: '200%',
|
|
|
|
3.00: '300%',
|
|
|
|
4.00: '400%',
|
|
|
|
}
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
def __init__(self, operation, preview, context, parent):
|
|
|
|
self._operation = operation
|
|
|
|
self._preview = preview
|
|
|
|
self._context = context
|
2007-08-29 03:58:58 +05:30
|
|
|
|
|
|
|
self.__build_window()
|
|
|
|
self._current_page = None
|
2007-08-26 19:35:34 +05:30
|
|
|
|
|
|
|
# Private
|
|
|
|
|
|
|
|
def __build_window(self):
|
|
|
|
"""Build the window from Glade.
|
|
|
|
"""
|
2007-08-29 03:58:58 +05:30
|
|
|
glade_file = os.path.join(os.path.dirname(__file__),
|
|
|
|
'gtkprintpreview.glade')
|
2007-08-26 19:35:34 +05:30
|
|
|
|
2007-09-01 03:38:39 +05:30
|
|
|
window_xml = gtk.glade.XML(glade_file, 'window', 'gramps')
|
|
|
|
self._window = window_xml.get_widget('window')
|
2007-08-26 19:35:34 +05:30
|
|
|
#self._window.set_transient_for(parent)
|
2007-08-29 03:58:58 +05:30
|
|
|
|
|
|
|
# remember active widgets for future use
|
|
|
|
self._swin = window_xml.get_widget('swin')
|
2007-08-26 19:35:34 +05:30
|
|
|
self._drawing_area = window_xml.get_widget('drawingarea')
|
|
|
|
self._first_button = window_xml.get_widget('first')
|
|
|
|
self._prev_button = window_xml.get_widget('prev')
|
|
|
|
self._next_button = window_xml.get_widget('next')
|
|
|
|
self._last_button = window_xml.get_widget('last')
|
2007-09-01 03:38:39 +05:30
|
|
|
self._pages_entry = window_xml.get_widget('entry')
|
|
|
|
self._pages_label = window_xml.get_widget('label')
|
2007-08-29 03:58:58 +05:30
|
|
|
self._zoom_fit_width_button = window_xml.get_widget('zoom_fit_width')
|
2007-09-01 03:38:39 +05:30
|
|
|
self._zoom_fit_width_button.set_stock_id('gramps-zoom-fit-width')
|
2007-08-29 03:58:58 +05:30
|
|
|
self._zoom_best_fit_button = window_xml.get_widget('zoom_best_fit')
|
2007-09-07 02:31:48 +05:30
|
|
|
self._zoom_best_fit_button.set_stock_id('gramps-zoom-best-fit')
|
2007-08-29 03:58:58 +05:30
|
|
|
self._zoom_in_button = window_xml.get_widget('zoom_in')
|
2007-09-07 02:31:48 +05:30
|
|
|
self._zoom_in_button.set_stock_id('gramps-zoom-in')
|
2007-08-29 03:58:58 +05:30
|
|
|
self._zoom_out_button = window_xml.get_widget('zoom_out')
|
2007-09-07 02:31:48 +05:30
|
|
|
self._zoom_out_button.set_stock_id('gramps-zoom-out')
|
2007-08-26 19:35:34 +05:30
|
|
|
|
|
|
|
# connect the signals
|
|
|
|
window_xml.signal_autoconnect({
|
|
|
|
'on_drawingarea_expose_event': self.on_drawingarea_expose_event,
|
2007-08-29 03:58:58 +05:30
|
|
|
'on_swin_size_allocate': self.on_swin_size_allocate,
|
2007-08-26 19:35:34 +05:30
|
|
|
'on_quit_clicked': self.on_quit_clicked,
|
|
|
|
'on_print_clicked': self.on_print_clicked,
|
|
|
|
'on_first_clicked': self.on_first_clicked,
|
|
|
|
'on_prev_clicked': self.on_prev_clicked,
|
|
|
|
'on_next_clicked': self.on_next_clicked,
|
|
|
|
'on_last_clicked': self.on_last_clicked,
|
2007-08-29 03:58:58 +05:30
|
|
|
'on_zoom_fit_width_toggled': self.on_zoom_fit_width_toggled,
|
|
|
|
'on_zoom_best_fit_toggled': self.on_zoom_best_fit_toggled,
|
2007-08-26 19:35:34 +05:30
|
|
|
'on_zoom_in_clicked': self.on_zoom_in_clicked,
|
|
|
|
'on_zoom_out_clicked': self.on_zoom_out_clicked,
|
|
|
|
'on_window_delete_event': self.on_window_delete_event,
|
|
|
|
'on_entry_activate': self.on_entry_activate,
|
|
|
|
})
|
|
|
|
|
|
|
|
##def create_surface(self):
|
|
|
|
##return cairo.PDFSurface(StringIO(),
|
|
|
|
##self._context.get_width(),
|
|
|
|
##self._context.get_height())
|
|
|
|
|
|
|
|
##def get_page(self, page_no):
|
|
|
|
##"""Get the cairo surface of the given page.
|
|
|
|
|
|
|
|
##Surfaces are also cached for instant access.
|
|
|
|
|
|
|
|
##"""
|
|
|
|
##if page_no >= len(self._page_numbers):
|
|
|
|
##log.debug("Page number %d doesn't exist." % page_no)
|
|
|
|
##page_no = 0
|
|
|
|
|
|
|
|
##if not self._page_surfaces.has_key(page_no):
|
|
|
|
##surface = self.create_surface()
|
|
|
|
##cr = cairo.Context(surface)
|
|
|
|
|
|
|
|
##if PRINTER_SCALE != 1.0:
|
|
|
|
##cr.scale(PRINTER_SCALE, PRINTER_SCALE)
|
|
|
|
|
|
|
|
##self._context.set_cairo_context(cr, PRINTER_DPI, PRINTER_DPI)
|
|
|
|
##self._preview.render_page(self._page_numbers[page_no])
|
|
|
|
|
|
|
|
##self._page_surfaces[page_no] = surface
|
|
|
|
|
|
|
|
##return self._page_surfaces[page_no]
|
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
def __set_page(self, page_no):
|
|
|
|
if page_no < 0 or page_no >= self._page_no:
|
|
|
|
return
|
|
|
|
|
|
|
|
if self._current_page != page_no:
|
|
|
|
self._drawing_area.queue_draw()
|
|
|
|
|
|
|
|
self._current_page = page_no
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
self._first_button.set_sensitive(self._current_page)
|
|
|
|
self._prev_button.set_sensitive(self._current_page)
|
|
|
|
self._next_button.set_sensitive(self._current_page < self._page_no - 1)
|
|
|
|
self._last_button.set_sensitive(self._current_page < self._page_no - 1)
|
|
|
|
|
|
|
|
self._pages_entry.set_text('%d' % (self._current_page + 1))
|
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
def __set_zoom(self, zoom):
|
|
|
|
self._zoom = zoom
|
|
|
|
|
|
|
|
screen_width = int(self._paper_width * self._zoom + 2 * MARGIN)
|
|
|
|
screen_height = int(self._paper_height * self._zoom + 2 * MARGIN)
|
|
|
|
self._drawing_area.set_size_request(screen_width, screen_height)
|
|
|
|
self._drawing_area.queue_draw()
|
|
|
|
|
|
|
|
self._zoom_in_button.set_sensitive(self._zoom !=
|
|
|
|
max(self.zoom_factors.keys()))
|
|
|
|
self._zoom_out_button.set_sensitive(self._zoom !=
|
|
|
|
min(self.zoom_factors.keys()))
|
|
|
|
|
|
|
|
def __zoom_in(self):
|
|
|
|
zoom = [z for z in self.zoom_factors.keys() if z > self._zoom]
|
|
|
|
|
|
|
|
if zoom:
|
|
|
|
return min(zoom)
|
|
|
|
else:
|
|
|
|
return self._zoom
|
|
|
|
|
|
|
|
def __zoom_out(self):
|
|
|
|
zoom = [z for z in self.zoom_factors.keys() if z < self._zoom]
|
|
|
|
|
|
|
|
if zoom:
|
|
|
|
return max(zoom)
|
|
|
|
else:
|
|
|
|
return self._zoom
|
|
|
|
|
|
|
|
def __zoom_fit_width(self):
|
|
|
|
width, height, vsb_w, hsb_h = self.__get_view_size()
|
|
|
|
|
|
|
|
zoom = width / self._paper_width
|
|
|
|
if self._paper_height * zoom > height:
|
|
|
|
zoom = (width - vsb_w) / self._paper_width
|
|
|
|
|
|
|
|
return zoom
|
|
|
|
|
|
|
|
def __zoom_best_fit(self):
|
|
|
|
width, height, vsb_w, hsb_h = self.__get_view_size()
|
|
|
|
|
|
|
|
zoom = min(width / self._paper_width, height / self._paper_height)
|
|
|
|
|
|
|
|
return zoom
|
|
|
|
|
|
|
|
def __get_view_size(self):
|
|
|
|
"""Get the dimensions of the scrolled window.
|
|
|
|
"""
|
|
|
|
width = self._swin.allocation.width - 2 * MARGIN
|
|
|
|
height = self._swin.allocation.height - 2 * MARGIN
|
|
|
|
|
|
|
|
if self._swin.get_shadow_type() != gtk.SHADOW_NONE:
|
|
|
|
width -= 2 * self._swin.style.xthickness
|
|
|
|
height -= 2 * self._swin.style.ythickness
|
|
|
|
|
|
|
|
spacing = self._swin.style_get_property('scrollbar-spacing')
|
|
|
|
|
|
|
|
vsb_w, vsb_h = self._swin.get_vscrollbar().size_request()
|
|
|
|
vsb_w += spacing
|
|
|
|
|
|
|
|
hsb_w, hsb_h = self._swin.get_hscrollbar().size_request()
|
|
|
|
hsb_h += spacing
|
|
|
|
|
|
|
|
return width, height, vsb_w, hsb_h
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
def __end_preview(self):
|
|
|
|
self._operation.end_preview()
|
|
|
|
|
|
|
|
# Signal handlers
|
|
|
|
|
|
|
|
def on_drawingarea_expose_event(self, drawing_area, event):
|
|
|
|
cr = drawing_area.window.cairo_create()
|
|
|
|
cr.rectangle(event.area)
|
|
|
|
cr.clip()
|
|
|
|
|
2007-09-01 03:38:39 +05:30
|
|
|
# get the extents of the page and the screen
|
2007-09-07 17:23:12 +05:30
|
|
|
paper_w = int(self._paper_width * self._zoom)
|
|
|
|
paper_h = int(self._paper_height * self._zoom)
|
2007-09-01 03:38:39 +05:30
|
|
|
|
|
|
|
width, height, vsb_w, hsb_h = self.__get_view_size()
|
|
|
|
if paper_h > height:
|
|
|
|
width -= vsb_w
|
|
|
|
if paper_w > width:
|
|
|
|
height -= hsb_h
|
|
|
|
|
|
|
|
# put the paper on the middle of the window
|
|
|
|
xtranslate = MARGIN
|
|
|
|
if paper_w < width:
|
|
|
|
xtranslate += (width - paper_w) / 2
|
|
|
|
|
|
|
|
ytranslate = MARGIN
|
|
|
|
if paper_h < height:
|
|
|
|
ytranslate += (height - paper_h) / 2
|
|
|
|
|
|
|
|
cr.translate(xtranslate, ytranslate)
|
2007-08-26 19:35:34 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
# draw an empty white page
|
2007-08-26 19:35:34 +05:30
|
|
|
cr.set_source_rgb(1.0, 1.0, 1.0)
|
2007-09-01 03:38:39 +05:30
|
|
|
cr.rectangle(0, 0, paper_w, paper_h)
|
2007-08-26 19:35:34 +05:30
|
|
|
cr.fill_preserve()
|
|
|
|
cr.set_source_rgb(0, 0, 0)
|
|
|
|
cr.set_line_width(1)
|
|
|
|
cr.stroke()
|
|
|
|
|
2007-09-07 03:01:33 +05:30
|
|
|
if self._orientation == gtk.PAGE_ORIENTATION_LANDSCAPE:
|
|
|
|
cr.rotate(radians(-90))
|
|
|
|
cr.translate(-paper_h, 0)
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
##page_setup = self._context.get_page_setup()
|
|
|
|
##cr.translate(page_setup.get_left_margin(gtk.UNIT_POINTS),
|
|
|
|
##page_setup.get_top_margin(gtk.UNIT_POINTS))
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
##cr.set_source_surface(self.get_page(0))
|
|
|
|
##cr.paint()
|
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
# draw the content of the currently selected page
|
2007-09-07 17:23:12 +05:30
|
|
|
# Here we use dpi scaling instead of scaling the cairo context,
|
|
|
|
# because it gives better result. In the later case the distance
|
|
|
|
# of glyphs was changing.
|
2007-08-29 03:58:58 +05:30
|
|
|
dpi = PRINTER_DPI * self._zoom
|
|
|
|
self._context.set_cairo_context(cr, dpi, dpi)
|
2007-08-26 19:35:34 +05:30
|
|
|
self._preview.render_page(self._current_page)
|
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
def on_swin_size_allocate(self, scrolledwindow, allocation):
|
|
|
|
if self._zoom_mode == ZOOM_FIT_WIDTH:
|
|
|
|
self.__set_zoom(self.__zoom_fit_width())
|
|
|
|
|
|
|
|
if self._zoom_mode == ZOOM_BEST_FIT:
|
|
|
|
self.__set_zoom(self.__zoom_best_fit())
|
2007-08-26 19:35:34 +05:30
|
|
|
|
|
|
|
def on_print_clicked(self, toolbutton):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def on_first_clicked(self, toolbutton):
|
2007-08-29 03:58:58 +05:30
|
|
|
self.__set_page(0)
|
2007-08-26 19:35:34 +05:30
|
|
|
|
|
|
|
def on_prev_clicked(self, toolbutton):
|
2007-08-29 03:58:58 +05:30
|
|
|
self.__set_page(self._current_page - 1)
|
2007-08-26 19:35:34 +05:30
|
|
|
|
|
|
|
def on_next_clicked(self, toolbutton):
|
2007-08-29 03:58:58 +05:30
|
|
|
self.__set_page(self._current_page + 1)
|
2007-08-26 19:35:34 +05:30
|
|
|
|
|
|
|
def on_last_clicked(self, toolbutton):
|
2007-08-29 03:58:58 +05:30
|
|
|
self.__set_page(self._page_no - 1)
|
2007-08-26 19:35:34 +05:30
|
|
|
|
|
|
|
def on_entry_activate(self, entry):
|
|
|
|
try:
|
|
|
|
new_page = int(entry.get_text()) - 1
|
|
|
|
except ValueError:
|
|
|
|
new_page = self._current_page
|
|
|
|
|
|
|
|
if new_page < 0 or new_page >= self._page_no:
|
|
|
|
new_page = self._current_page
|
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
self.__set_page(new_page)
|
|
|
|
|
|
|
|
def on_zoom_fit_width_toggled(self, toggletoolbutton):
|
|
|
|
if toggletoolbutton.get_active():
|
|
|
|
self._zoom_best_fit_button.set_active(False)
|
|
|
|
self._zoom_mode = ZOOM_FIT_WIDTH
|
|
|
|
self.__set_zoom(self.__zoom_fit_width())
|
|
|
|
else:
|
|
|
|
self._zoom_mode = ZOOM_FREE
|
|
|
|
|
|
|
|
def on_zoom_best_fit_toggled(self, toggletoolbutton):
|
|
|
|
if toggletoolbutton.get_active():
|
|
|
|
self._zoom_fit_width_button.set_active(False)
|
|
|
|
self._zoom_mode = ZOOM_BEST_FIT
|
|
|
|
self.__set_zoom(self.__zoom_best_fit())
|
|
|
|
else:
|
|
|
|
self._zoom_mode = ZOOM_FREE
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
def on_zoom_in_clicked(self, toolbutton):
|
2007-08-29 03:58:58 +05:30
|
|
|
self._zoom_fit_width_button.set_active(False)
|
|
|
|
self._zoom_best_fit_button.set_active(False)
|
|
|
|
self._zoom_mode = ZOOM_FREE
|
|
|
|
self.__set_zoom(self.__zoom_in())
|
2007-08-26 19:35:34 +05:30
|
|
|
|
|
|
|
def on_zoom_out_clicked(self, toolbutton):
|
2007-08-29 03:58:58 +05:30
|
|
|
self._zoom_fit_width_button.set_active(False)
|
|
|
|
self._zoom_best_fit_button.set_active(False)
|
|
|
|
self._zoom_mode = ZOOM_FREE
|
|
|
|
self.__set_zoom(self.__zoom_out())
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
def on_window_delete_event(self, widget, event):
|
|
|
|
self.__end_preview()
|
|
|
|
return False
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
def on_quit_clicked(self, toolbutton):
|
|
|
|
self.__end_preview()
|
|
|
|
self._window.destroy()
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
# Public
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
def start(self):
|
2007-08-29 03:58:58 +05:30
|
|
|
# get paper/page dimensions
|
2007-08-26 19:35:34 +05:30
|
|
|
page_setup = self._context.get_page_setup()
|
|
|
|
self._paper_width = page_setup.get_paper_width(gtk.UNIT_POINTS)
|
|
|
|
self._paper_height = page_setup.get_paper_height(gtk.UNIT_POINTS)
|
|
|
|
self._page_width = page_setup.get_page_width(gtk.UNIT_POINTS)
|
|
|
|
self._page_height = page_setup.get_page_height(gtk.UNIT_POINTS)
|
2007-09-07 03:01:33 +05:30
|
|
|
self._orientation = page_setup.get_orientation()
|
2007-08-29 03:58:58 +05:30
|
|
|
|
|
|
|
# get the total number of pages
|
|
|
|
##self._page_numbers = [0,]
|
|
|
|
##self._page_surfaces = {}
|
|
|
|
self._page_no = self._operation.get_property('n_pages')
|
|
|
|
self._pages_label.set_text('of %d' % self._page_no)
|
|
|
|
|
|
|
|
# set zoom level and initial page number
|
|
|
|
self._zoom_mode = ZOOM_FREE
|
|
|
|
self.__set_zoom(1.0)
|
|
|
|
self.__set_page(0)
|
2007-08-26 19:35:34 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
# let's the show begin...
|
2007-08-26 19:35:34 +05:30
|
|
|
self._window.show()
|
|
|
|
|
2007-08-20 19:24:04 +05:30
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Font selection
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
|
|
|
|
_TTF_FREEFONT = {
|
|
|
|
BaseDoc.FONT_SERIF: 'FreeSerif',
|
|
|
|
BaseDoc.FONT_SANS_SERIF: 'FreeSans',
|
|
|
|
BaseDoc.FONT_MONOSPACE: 'FreeMono',
|
|
|
|
}
|
|
|
|
|
|
|
|
_MS_TTFONT = {
|
|
|
|
BaseDoc.FONT_SERIF: 'Times New Roman',
|
|
|
|
BaseDoc.FONT_SANS_SERIF: 'Arial',
|
|
|
|
BaseDoc.FONT_MONOSPACE: 'Courier New',
|
|
|
|
}
|
|
|
|
|
|
|
|
_GNOME_FONT = {
|
|
|
|
BaseDoc.FONT_SERIF: 'Serif',
|
|
|
|
BaseDoc.FONT_SANS_SERIF: 'Sans',
|
|
|
|
BaseDoc.FONT_MONOSPACE: 'Monospace',
|
|
|
|
}
|
|
|
|
|
|
|
|
font_families = _GNOME_FONT
|
|
|
|
|
|
|
|
def set_font_families(pango_context):
|
|
|
|
"""Set the used font families depending on availability.
|
|
|
|
"""
|
|
|
|
global font_families
|
|
|
|
|
|
|
|
families = pango_context.list_families()
|
|
|
|
family_names = [family.get_name() for family in families]
|
|
|
|
|
|
|
|
fam = [f for f in _TTF_FREEFONT.values() if f in family_names]
|
|
|
|
if len(fam) == len(_TTF_FREEFONT):
|
|
|
|
font_families = _TTF_FREEFONT
|
|
|
|
log.debug('Using FreeFonts: %s' % font_families)
|
|
|
|
return
|
|
|
|
|
|
|
|
fam = [f for f in _MS_TTFONT.values() if f in family_names]
|
|
|
|
if len(fam) == len(_MS_TTFONT):
|
|
|
|
font_families = _MS_TTFONT
|
|
|
|
log.debug('Using MS TrueType fonts: %s' % font_families)
|
|
|
|
return
|
|
|
|
|
|
|
|
fam = [f for f in _GNOME_FONT.values() if f in family_names]
|
|
|
|
if len(fam) == len(_GNOME_FONT):
|
|
|
|
font_families = _GNOME_FONT
|
|
|
|
log.debug('Using Gnome fonts: %s' % font_families)
|
|
|
|
return
|
|
|
|
|
|
|
|
log.debug('No fonts found.')
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Converter functions
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
|
2007-01-05 21:40:08 +05:30
|
|
|
def paperstyle_to_pagesetup(paper_style):
|
2007-07-27 15:54:09 +05:30
|
|
|
"""Convert a BaseDoc.PaperStyle instance into a gtk.PageSetup instance.
|
|
|
|
|
|
|
|
@param paper_style: Gramps paper style object to convert
|
|
|
|
@param type: BaseDoc.PaperStyle
|
|
|
|
@return: page_setup
|
|
|
|
@rtype: gtk.PageSetup
|
2007-01-05 21:40:08 +05:30
|
|
|
"""
|
2007-07-27 15:54:09 +05:30
|
|
|
gramps_to_gtk = {
|
|
|
|
"Letter": "na_letter",
|
|
|
|
"Legal": "na_legal",
|
|
|
|
"A0": "iso_a0",
|
|
|
|
"A1": "iso_a1",
|
|
|
|
"A2": "iso_a2",
|
|
|
|
"A3": "iso_a3",
|
|
|
|
"A4": "iso_a4",
|
|
|
|
"A5": "iso_a5",
|
|
|
|
"B0": "iso_b0",
|
|
|
|
"B1": "iso_b1",
|
|
|
|
"B2": "iso_b2",
|
|
|
|
"B3": "iso_b3",
|
|
|
|
"B4": "iso_b4",
|
|
|
|
"B5": "iso_b5",
|
|
|
|
"B6": "iso_b6",
|
|
|
|
"B": "iso_b",
|
|
|
|
"C": "iso_c",
|
|
|
|
"D": "iso_d",
|
|
|
|
"E": "iso_e",
|
|
|
|
}
|
|
|
|
|
|
|
|
# First set the paper size
|
|
|
|
gramps_paper_size = paper_style.get_size()
|
|
|
|
gramps_paper_name = gramps_paper_size.get_name()
|
|
|
|
|
|
|
|
if gramps_to_gtk.has_key(gramps_paper_name):
|
|
|
|
paper_size = gtk.PaperSize(gramps_to_gtk[gramps_paper_name])
|
2007-07-28 00:41:15 +05:30
|
|
|
elif gramps_paper_name == "Custom Size":
|
2007-08-12 16:07:11 +05:30
|
|
|
paper_width = gramps_paper_size.get_width() * 10
|
|
|
|
paper_height = gramps_paper_size.get_height() * 10
|
2007-08-11 04:44:12 +05:30
|
|
|
paper_size = gtk.paper_size_new_custom("custom",
|
|
|
|
"Custom Size",
|
2007-08-12 16:07:11 +05:30
|
|
|
paper_width,
|
|
|
|
paper_height,
|
2007-07-28 00:41:15 +05:30
|
|
|
gtk.UNIT_MM)
|
2007-07-27 15:54:09 +05:30
|
|
|
else:
|
2007-08-11 04:44:12 +05:30
|
|
|
def_paper_size_name = gtk.paper_size_get_default()
|
|
|
|
paper_size = gtk.PaperSize(def_paper_size_name)
|
|
|
|
log.debug("Unknown paper size, falling back to the default: %s" %
|
|
|
|
def_paper_size_name)
|
2007-07-27 15:54:09 +05:30
|
|
|
|
2007-01-05 21:40:08 +05:30
|
|
|
page_setup = gtk.PageSetup()
|
2007-07-27 15:54:09 +05:30
|
|
|
page_setup.set_paper_size(paper_size)
|
|
|
|
|
|
|
|
# Set paper orientation
|
2007-01-05 21:40:08 +05:30
|
|
|
if paper_style.get_orientation() == BaseDoc.PAPER_PORTRAIT:
|
|
|
|
page_setup.set_orientation(gtk.PAGE_ORIENTATION_PORTRAIT)
|
|
|
|
else:
|
|
|
|
page_setup.set_orientation(gtk.PAGE_ORIENTATION_LANDSCAPE)
|
|
|
|
|
2007-07-27 15:54:09 +05:30
|
|
|
# gtk.PageSize provides default margins for the standard papers.
|
2007-08-11 04:44:12 +05:30
|
|
|
# Anyhow, we overwrite those with the settings from Gramps,
|
|
|
|
# though at the moment all of them are fixed at 1 inch.
|
2007-08-12 16:07:11 +05:30
|
|
|
page_setup.set_top_margin(paper_style.get_top_margin() * 10,
|
|
|
|
gtk.UNIT_MM)
|
|
|
|
page_setup.set_bottom_margin(paper_style.get_bottom_margin() * 10,
|
|
|
|
gtk.UNIT_MM)
|
|
|
|
page_setup.set_left_margin(paper_style.get_left_margin() * 10,
|
|
|
|
gtk.UNIT_MM)
|
|
|
|
page_setup.set_right_margin(paper_style.get_right_margin() * 10,
|
|
|
|
gtk.UNIT_MM)
|
2007-07-27 15:54:09 +05:30
|
|
|
|
2007-01-05 21:40:08 +05:30
|
|
|
return page_setup
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
def fontstyle_to_fontdescription(font_style):
|
|
|
|
"""Convert a BaseDoc.FontStyle instance to a pango.FontDescription one.
|
2007-01-05 21:40:08 +05:30
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
Font color and underline are not implemented in pango.FontDescription,
|
|
|
|
and have to be set with pango.Layout.set_attributes(attrlist) method.
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-08-11 04:44:12 +05:30
|
|
|
"""
|
|
|
|
if font_style.get_bold():
|
2007-08-12 16:07:11 +05:30
|
|
|
f_weight = pango.WEIGHT_BOLD
|
2007-08-11 04:44:12 +05:30
|
|
|
else:
|
2007-08-12 16:07:11 +05:30
|
|
|
f_weight = pango.WEIGHT_NORMAL
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-08-11 04:44:12 +05:30
|
|
|
if font_style.get_italic():
|
2007-08-12 16:07:11 +05:30
|
|
|
f_style = pango.STYLE_ITALIC
|
2007-08-11 04:44:12 +05:30
|
|
|
else:
|
2007-08-12 16:07:11 +05:30
|
|
|
f_style = pango.STYLE_NORMAL
|
2007-08-11 04:44:12 +05:30
|
|
|
|
2007-08-18 23:53:18 +05:30
|
|
|
font_description = pango.FontDescription(font_families[font_style.face])
|
2007-08-20 19:24:04 +05:30
|
|
|
font_description.set_size(font_style.get_size() * pango.SCALE)
|
2007-08-12 16:07:11 +05:30
|
|
|
font_description.set_weight(f_weight)
|
|
|
|
font_description.set_style(f_style)
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
return font_description
|
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
def tabstops_to_tabarray(tab_stops, dpi):
|
|
|
|
"""Convert a list of tabs given in cm to a pango.TabArray.
|
|
|
|
"""
|
|
|
|
tab_array = pango.TabArray(len(tab_stops), False)
|
|
|
|
|
|
|
|
for index in range(len(tab_stops)):
|
2007-08-18 00:45:05 +05:30
|
|
|
location = tab_stops[index] * dpi * pango.SCALE / 2.54
|
2007-08-19 13:52:38 +05:30
|
|
|
tab_array.set_tab(index, pango.TAB_LEFT, int(location))
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
return tab_array
|
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
###------------------------------------------------------------------------
|
|
|
|
###
|
|
|
|
### Table row style
|
|
|
|
###
|
|
|
|
###------------------------------------------------------------------------
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##class RowStyle(list):
|
|
|
|
##"""Specifies the format of a table row.
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##RowStyle extents the available styles in BaseDoc.
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##The RowStyle contains the width of each column as a percentage of the
|
2007-09-05 20:38:22 +05:30
|
|
|
##width of the full row. Note! The width of the row is not known until
|
2007-08-29 03:58:58 +05:30
|
|
|
##divide() or draw() method is called.
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##"""
|
|
|
|
##def __init__(self):
|
|
|
|
##self.columns = []
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##def set_columns(self, columns):
|
|
|
|
##"""Set the number of columns.
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##@param columns: number of columns that should be used.
|
|
|
|
##@param type: int
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##"""
|
|
|
|
##self.columns = columns
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##def get_columns(self):
|
|
|
|
##"""Return the number of columns.
|
|
|
|
##"""
|
|
|
|
##return self.columns
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##def set_column_widths(self, clist):
|
|
|
|
##"""Set the width of all the columns at once.
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##@param clist: list of width of columns in % of the full row.
|
|
|
|
##@param tyle: list
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##"""
|
|
|
|
##self.columns = len(clist)
|
|
|
|
##for i in range(self.columns):
|
|
|
|
##self.colwid[i] = clist[i]
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##def set_column_width(self, index, width):
|
|
|
|
##"""
|
|
|
|
##Sets the width of a specified column to the specified width.
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##@param index: column being set (index starts at 0)
|
|
|
|
##@param width: percentage of the table width assigned to the column
|
|
|
|
##"""
|
|
|
|
##self.colwid[index] = width
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##def get_column_width(self, index):
|
|
|
|
##"""
|
|
|
|
##Returns the column width of the specified column as a percentage of
|
|
|
|
##the entire table width.
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
##@param index: column to return (index starts at 0)
|
|
|
|
##"""
|
|
|
|
##return self.colwid[index]
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-09-05 20:38:22 +05:30
|
|
|
class FrameStyle(object):
|
2007-09-07 17:23:12 +05:30
|
|
|
"""Define the style properties of a Frame.
|
2007-09-05 20:38:22 +05:30
|
|
|
|
|
|
|
- width: Width of the frame in cm.
|
|
|
|
- height: Height of the frame in cm.
|
|
|
|
- align: Horizontal position to entire page.
|
|
|
|
Available values: 'left','center', 'right'.
|
|
|
|
- spacing: Tuple of spacing around the frame in cm. Order of values:
|
|
|
|
(left, right, top, bottom).
|
|
|
|
|
|
|
|
"""
|
|
|
|
def __init__(self, width=0, height=0, align='left', spacing=(0, 0, 0, 0)):
|
|
|
|
self.width = width
|
|
|
|
self.height = height
|
|
|
|
self.align = align
|
|
|
|
self.spacing = spacing
|
|
|
|
|
2007-01-05 17:28:16 +05:30
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
2007-08-11 04:44:12 +05:30
|
|
|
# Document element classes
|
2007-01-05 17:28:16 +05:30
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
class GtkDocBaseElement(object):
|
|
|
|
"""Base of all document elements.
|
|
|
|
|
|
|
|
Support document element structuring and can render itself onto
|
|
|
|
a Cairo surface.
|
|
|
|
|
2007-08-18 00:45:05 +05:30
|
|
|
There are two cathegories of methods:
|
|
|
|
1. hierarchy building methods (add_child, get_children, set_parent,
|
|
|
|
get_parent);
|
|
|
|
2. rendering methods (divide, draw).
|
|
|
|
|
|
|
|
The hierarchy building methods generally don't have to be overriden in
|
|
|
|
the subclass, while the rendering methods (divide, draw) must be
|
|
|
|
implemented in the subclasses.
|
|
|
|
|
2007-08-11 04:44:12 +05:30
|
|
|
"""
|
|
|
|
_type = 'BASE'
|
2007-08-18 23:53:18 +05:30
|
|
|
_allowed_children = []
|
2007-08-11 04:44:12 +05:30
|
|
|
|
2007-08-18 00:45:05 +05:30
|
|
|
def __init__(self, style=None):
|
2007-08-11 04:44:12 +05:30
|
|
|
self._parent = None
|
|
|
|
self._children = []
|
2007-08-18 00:45:05 +05:30
|
|
|
self._style = style
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
def get_type(self):
|
|
|
|
"""Get the type of this element.
|
2007-01-05 21:40:08 +05:30
|
|
|
"""
|
2007-08-11 04:44:12 +05:30
|
|
|
return self._type
|
|
|
|
|
|
|
|
def set_parent(self, parent):
|
|
|
|
"""Set the parent element of this element.
|
|
|
|
"""
|
|
|
|
self._parent = parent
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-08-11 04:44:12 +05:30
|
|
|
def get_parent(self):
|
|
|
|
"""Get the parent element of this element.
|
|
|
|
"""
|
|
|
|
return self._parent
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-08-11 04:44:12 +05:30
|
|
|
def add_child(self, element):
|
|
|
|
"""Add a child element.
|
|
|
|
|
|
|
|
Returns False if the child cannot be added (e.g. not an allowed type),
|
|
|
|
or True otherwise.
|
|
|
|
|
|
|
|
"""
|
|
|
|
# check if it is an allowed child for this type
|
|
|
|
if element.get_type() not in self._allowed_children:
|
|
|
|
log.debug("%r is not an allowed child for %r" %
|
|
|
|
(element.__class__, self.__class__))
|
|
|
|
return False
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-08-11 04:44:12 +05:30
|
|
|
# append the child and set it's parent
|
|
|
|
self._children.append(element)
|
|
|
|
element.set_parent(self)
|
|
|
|
return True
|
|
|
|
|
|
|
|
def get_children(self):
|
2007-08-18 00:45:05 +05:30
|
|
|
"""Get the list of children of this element.
|
2007-08-11 04:44:12 +05:30
|
|
|
"""
|
|
|
|
return self._children
|
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
def divide(self, layout, width, height, dpi_x, dpi_y):
|
2007-08-12 16:07:11 +05:30
|
|
|
"""Divide the element into two depending on available space.
|
|
|
|
|
|
|
|
@param layout: pango layout to write on
|
|
|
|
@param type: pango.Layout
|
|
|
|
@param width: width of available space for this element
|
|
|
|
@param type: device points
|
|
|
|
@param height: height of available space for this element
|
|
|
|
@param type: device points
|
2007-08-18 00:45:05 +05:30
|
|
|
@param dpi_x: the horizontal resolution
|
|
|
|
@param type: dots per inch
|
|
|
|
@param dpi_y: the vertical resolution
|
|
|
|
@param type: dots per inch
|
2007-08-12 16:07:11 +05:30
|
|
|
|
|
|
|
@return: the divided element, and the height of the first part
|
|
|
|
@rtype: (GtkDocXXX-1, GtkDocXXX-2), device points
|
|
|
|
|
2007-08-11 04:44:12 +05:30
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
def draw(self, cairo_context, pango_layout, width, dpi_x, dpi_y):
|
|
|
|
"""Draw itself onto a cairo surface.
|
2007-08-11 04:44:12 +05:30
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
@param cairo_context: context to draw on
|
2007-08-18 00:45:05 +05:30
|
|
|
@param type: cairo.Context class
|
2007-08-15 02:27:26 +05:30
|
|
|
@param pango_layout: pango layout to write on
|
|
|
|
@param type: pango.Layout class
|
2007-08-12 16:07:11 +05:30
|
|
|
@param width: width of available space for this element
|
|
|
|
@param type: device points
|
2007-08-18 00:45:05 +05:30
|
|
|
@param dpi_x: the horizontal resolution
|
|
|
|
@param type: dots per inch
|
|
|
|
@param dpi_y: the vertical resolution
|
|
|
|
@param type: dots per inch
|
2007-08-11 04:44:12 +05:30
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
@return: height of the element
|
|
|
|
@rtype: device points
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
class GtkDocDocument(GtkDocBaseElement):
|
2007-08-12 16:07:11 +05:30
|
|
|
"""The whole document or a page.
|
2007-08-11 04:44:12 +05:30
|
|
|
"""
|
|
|
|
_type = 'DOCUMENT'
|
2007-09-05 20:38:22 +05:30
|
|
|
_allowed_children = ['PARAGRAPH', 'PAGEBREAK', 'TABLE', 'IMAGE', 'FRAME']
|
2007-08-11 04:44:12 +05:30
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
def draw(self, cairo_context, pango_layout, width, dpi_x, dpi_y):
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
x = y = elem_height = 0
|
2007-08-12 16:07:11 +05:30
|
|
|
|
|
|
|
for elem in self._children:
|
2007-08-18 00:45:05 +05:30
|
|
|
cairo_context.translate(x, elem_height)
|
2007-08-15 02:27:26 +05:30
|
|
|
elem_height = elem.draw(cairo_context, pango_layout,
|
2007-08-18 00:45:05 +05:30
|
|
|
width, dpi_x, dpi_y)
|
2007-08-12 16:07:11 +05:30
|
|
|
y += elem_height
|
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
return y
|
2007-08-12 16:07:11 +05:30
|
|
|
|
2007-08-11 04:44:12 +05:30
|
|
|
class GtkDocPagebreak(GtkDocBaseElement):
|
|
|
|
"""Implement a page break.
|
|
|
|
"""
|
|
|
|
_type = 'PAGEBREAK'
|
2007-08-18 23:53:18 +05:30
|
|
|
_allowed_children = []
|
2007-08-11 04:44:12 +05:30
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
def divide(self, layout, width, height, dpi_x, dpi_y):
|
2007-08-11 04:44:12 +05:30
|
|
|
return (None, None), 0
|
|
|
|
|
|
|
|
class GtkDocParagraph(GtkDocBaseElement):
|
|
|
|
"""Paragraph.
|
|
|
|
"""
|
|
|
|
_type = 'PARAGRAPH'
|
2007-08-18 23:53:18 +05:30
|
|
|
_allowed_children = []
|
2007-08-11 04:44:12 +05:30
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
# line spacing is not defined in BaseDoc.ParagraphStyle
|
|
|
|
spacing = 2
|
2007-08-12 16:07:11 +05:30
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
def __init__(self, style, leader=None):
|
2007-08-18 00:45:05 +05:30
|
|
|
GtkDocBaseElement.__init__(self, style)
|
|
|
|
|
|
|
|
if leader:
|
|
|
|
self._text = leader + '\t'
|
|
|
|
# FIXME append new tab to the existing tab list
|
|
|
|
self._style.set_tabs([-1 * self._style.get_first_indent()])
|
|
|
|
else:
|
|
|
|
self._text = ''
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
def add_text(self, text):
|
|
|
|
self._text = self._text + text
|
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
def divide(self, layout, width, height, dpi_x, dpi_y):
|
2007-08-18 00:45:05 +05:30
|
|
|
l_margin = self._style.get_left_margin() * dpi_x / 2.54
|
|
|
|
r_margin = self._style.get_right_margin() * dpi_x / 2.54
|
|
|
|
t_margin = self._style.get_top_margin() * dpi_y / 2.54
|
|
|
|
b_margin = self._style.get_bottom_margin() * dpi_y / 2.54
|
|
|
|
h_padding = self._style.get_padding() * dpi_x / 2.54
|
|
|
|
v_padding = self._style.get_padding() * dpi_y / 2.54
|
|
|
|
f_indent = self._style.get_first_indent() * dpi_x / 2.54
|
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
# calculate real width available for text
|
|
|
|
text_width = width - l_margin - 2 * h_padding - r_margin
|
2007-08-18 00:45:05 +05:30
|
|
|
if f_indent < 0:
|
|
|
|
text_width -= f_indent
|
2007-08-19 13:52:38 +05:30
|
|
|
layout.set_width(int(text_width * pango.SCALE))
|
2007-08-12 16:07:11 +05:30
|
|
|
|
|
|
|
# set paragraph properties
|
|
|
|
layout.set_wrap(pango.WRAP_WORD_CHAR)
|
|
|
|
layout.set_spacing(self.spacing * pango.SCALE)
|
2007-08-19 13:52:38 +05:30
|
|
|
layout.set_indent(int(f_indent * pango.SCALE))
|
2007-08-18 00:45:05 +05:30
|
|
|
layout.set_tabs(tabstops_to_tabarray(self._style.get_tabs(), dpi_x))
|
|
|
|
#
|
|
|
|
align = self._style.get_alignment_text()
|
|
|
|
if align == 'left':
|
|
|
|
layout.set_alignment(pango.ALIGN_LEFT)
|
|
|
|
elif align == 'right':
|
|
|
|
layout.set_alignment(pango.ALIGN_RIGHT)
|
|
|
|
elif align == 'center':
|
|
|
|
layout.set_alignment(pango.ALIGN_CENTER)
|
|
|
|
elif align == 'justify':
|
|
|
|
layout.set_justify(True)
|
2007-09-07 05:23:18 +05:30
|
|
|
else:
|
|
|
|
raise ValueError
|
2007-08-18 00:45:05 +05:30
|
|
|
#
|
2007-08-12 16:07:11 +05:30
|
|
|
font_style = self._style.get_font()
|
|
|
|
layout.set_font_description(fontstyle_to_fontdescription(font_style))
|
|
|
|
|
|
|
|
# calculate the height of one line
|
|
|
|
layout.set_text('Test')
|
|
|
|
layout_width, layout_height = layout.get_size()
|
|
|
|
line_height = layout_height / pango.SCALE + self.spacing
|
|
|
|
# and the number of lines fit on the available height
|
2007-08-18 00:45:05 +05:30
|
|
|
text_height = height - t_margin - 2 * v_padding
|
2007-08-15 02:27:26 +05:30
|
|
|
line_per_height = text_height / line_height
|
2007-08-12 16:07:11 +05:30
|
|
|
|
|
|
|
# if nothing fits
|
|
|
|
if line_per_height < 1:
|
|
|
|
return (None, self), 0
|
|
|
|
|
|
|
|
# calculate where to cut the paragraph
|
|
|
|
layout.set_markup(self._text)
|
|
|
|
layout_width, layout_height = layout.get_size()
|
|
|
|
line_count = layout.get_line_count()
|
|
|
|
|
2007-08-18 00:45:05 +05:30
|
|
|
# if all paragraph fits we don't need to cut
|
2007-08-12 16:07:11 +05:30
|
|
|
if line_count <= line_per_height:
|
2007-08-18 00:45:05 +05:30
|
|
|
paragraph_height = ((layout_height / pango.SCALE) +
|
|
|
|
t_margin +
|
|
|
|
(2 * v_padding))
|
|
|
|
if height - paragraph_height > b_margin:
|
|
|
|
paragraph_height += b_margin
|
|
|
|
return (self, None), paragraph_height
|
2007-08-12 16:07:11 +05:30
|
|
|
|
|
|
|
# get index of first character which doesn't fit on available height
|
2007-08-19 13:52:38 +05:30
|
|
|
layout_line = layout.get_line(int(line_per_height))
|
2007-08-12 16:07:11 +05:30
|
|
|
index = layout_line.start_index
|
2007-08-15 02:27:26 +05:30
|
|
|
# and divide the text, first create the second part
|
2007-08-18 00:45:05 +05:30
|
|
|
new_style = BaseDoc.ParagraphStyle(self._style)
|
|
|
|
new_style.set_top_margin(0)
|
2007-08-15 02:27:26 +05:30
|
|
|
new_paragraph = GtkDocParagraph(new_style)
|
2007-08-18 00:45:05 +05:30
|
|
|
new_paragraph.add_text(self._text.encode('utf-8')[index:])
|
2007-08-15 02:27:26 +05:30
|
|
|
# then update the first one
|
2007-08-18 00:45:05 +05:30
|
|
|
self._text = self._text.encode('utf-8')[:index]
|
|
|
|
self._style.set_bottom_margin(0)
|
2007-08-15 02:27:26 +05:30
|
|
|
|
|
|
|
# FIXME do we need to return the proper height???
|
2007-08-18 00:45:05 +05:30
|
|
|
#paragraph_height = line_height * line_count + t_margin + 2 * v_padding
|
|
|
|
paragraph_height = 0
|
2007-08-15 02:27:26 +05:30
|
|
|
return (self, new_paragraph), paragraph_height
|
|
|
|
|
|
|
|
def draw(self, cr, layout, width, dpi_x, dpi_y):
|
2007-08-18 00:45:05 +05:30
|
|
|
l_margin = self._style.get_left_margin() * dpi_x / 2.54
|
|
|
|
r_margin = self._style.get_right_margin() * dpi_x / 2.54
|
|
|
|
t_margin = self._style.get_top_margin() * dpi_y / 2.54
|
|
|
|
b_margin = self._style.get_bottom_margin() * dpi_y / 2.54
|
|
|
|
h_padding = self._style.get_padding() * dpi_x / 2.54
|
|
|
|
v_padding = self._style.get_padding() * dpi_y / 2.54
|
|
|
|
f_indent = self._style.get_first_indent() * dpi_x / 2.54
|
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
# calculate real width available for text
|
|
|
|
text_width = width - l_margin - 2 * h_padding - r_margin
|
2007-08-18 00:45:05 +05:30
|
|
|
if f_indent < 0:
|
|
|
|
text_width -= f_indent
|
2007-08-19 13:52:38 +05:30
|
|
|
layout.set_width(int(text_width * pango.SCALE))
|
2007-08-12 16:07:11 +05:30
|
|
|
|
|
|
|
# set paragraph properties
|
|
|
|
layout.set_wrap(pango.WRAP_WORD_CHAR)
|
|
|
|
layout.set_spacing(self.spacing * pango.SCALE)
|
2007-08-19 13:52:38 +05:30
|
|
|
layout.set_indent(int(f_indent * pango.SCALE))
|
2007-08-18 00:45:05 +05:30
|
|
|
layout.set_tabs(tabstops_to_tabarray(self._style.get_tabs(), dpi_x))
|
|
|
|
#
|
|
|
|
align = self._style.get_alignment_text()
|
|
|
|
if align == 'left':
|
|
|
|
layout.set_alignment(pango.ALIGN_LEFT)
|
|
|
|
elif align == 'right':
|
|
|
|
layout.set_alignment(pango.ALIGN_RIGHT)
|
|
|
|
elif align == 'center':
|
|
|
|
layout.set_alignment(pango.ALIGN_CENTER)
|
|
|
|
elif align == 'justify':
|
|
|
|
layout.set_justify(True)
|
|
|
|
#
|
|
|
|
font_style = self._style.get_font()
|
2007-08-12 16:07:11 +05:30
|
|
|
layout.set_font_description(fontstyle_to_fontdescription(font_style))
|
2007-08-15 02:27:26 +05:30
|
|
|
|
2007-08-18 00:45:05 +05:30
|
|
|
# layout the text
|
2007-08-12 16:07:11 +05:30
|
|
|
layout.set_markup(self._text)
|
|
|
|
layout_width, layout_height = layout.get_size()
|
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
# render the layout onto the cairo surface
|
2007-08-18 00:45:05 +05:30
|
|
|
x = l_margin + h_padding
|
|
|
|
if f_indent < 0:
|
|
|
|
x += f_indent
|
2007-08-15 02:27:26 +05:30
|
|
|
cr.move_to(x, t_margin + v_padding)
|
2007-09-07 05:23:18 +05:30
|
|
|
# TODO color from the style
|
2007-08-18 00:45:05 +05:30
|
|
|
cr.set_source_rgb(0, 0, 0)
|
2007-08-12 16:07:11 +05:30
|
|
|
cr.show_layout(layout)
|
|
|
|
|
2007-08-18 00:45:05 +05:30
|
|
|
# calculate the full paragraph height
|
|
|
|
height = layout_height/pango.SCALE + t_margin + 2*v_padding + b_margin
|
2007-08-15 02:27:26 +05:30
|
|
|
|
2007-08-18 00:45:05 +05:30
|
|
|
# draw the borders
|
2007-08-12 16:07:11 +05:30
|
|
|
if self._style.get_top_border():
|
2007-08-15 02:27:26 +05:30
|
|
|
cr.move_to(l_margin, t_margin)
|
|
|
|
cr.rel_line_to(width - l_margin - r_margin, 0)
|
2007-08-12 16:07:11 +05:30
|
|
|
if self._style.get_right_border():
|
2007-08-15 02:27:26 +05:30
|
|
|
cr.move_to(width - r_margin, t_margin)
|
|
|
|
cr.rel_line_to(0, height - t_margin - b_margin)
|
2007-08-12 16:07:11 +05:30
|
|
|
if self._style.get_bottom_border():
|
2007-08-15 02:27:26 +05:30
|
|
|
cr.move_to(l_margin, height - b_margin)
|
|
|
|
cr.rel_line_to(width - l_margin - r_margin, 0)
|
2007-08-12 16:07:11 +05:30
|
|
|
if self._style.get_left_border():
|
2007-08-15 02:27:26 +05:30
|
|
|
cr.move_to(l_margin, t_margin)
|
|
|
|
cr.line_to(0, height - t_margin - b_margin)
|
2007-08-12 16:07:11 +05:30
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
cr.set_line_width(1)
|
2007-08-12 16:07:11 +05:30
|
|
|
cr.set_source_rgb(0, 0, 0)
|
|
|
|
cr.stroke()
|
|
|
|
|
2007-08-18 00:45:05 +05:30
|
|
|
if DEBUG:
|
|
|
|
cr.set_line_width(0.1)
|
|
|
|
cr.set_source_rgb(1.0, 0, 0)
|
|
|
|
cr.rectangle(0, 0, width, height)
|
|
|
|
cr.stroke()
|
|
|
|
cr.set_source_rgb(0, 0, 1.0)
|
|
|
|
cr.rectangle(l_margin, t_margin,
|
|
|
|
width-l_margin-r_margin, height-t_margin-b_margin)
|
|
|
|
cr.stroke()
|
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
return height
|
2007-08-12 16:07:11 +05:30
|
|
|
|
2007-08-18 00:45:05 +05:30
|
|
|
class GtkDocTable(GtkDocBaseElement):
|
|
|
|
"""Implement a table.
|
|
|
|
"""
|
|
|
|
_type = 'TABLE'
|
|
|
|
_allowed_children = ['ROW']
|
2007-08-11 04:44:12 +05:30
|
|
|
|
2007-08-18 00:45:05 +05:30
|
|
|
def divide(self, layout, width, height, dpi_x, dpi_y):
|
|
|
|
#calculate real table width
|
|
|
|
table_width = width * self._style.get_width() / 100
|
|
|
|
|
|
|
|
# calculate the height of each row
|
|
|
|
table_height = 0
|
|
|
|
row_index = 0
|
|
|
|
while row_index < len(self._children):
|
|
|
|
row = self._children[row_index]
|
|
|
|
(r1, r2), row_height = row.divide(layout, table_width, height,
|
|
|
|
dpi_x, dpi_y)
|
|
|
|
if table_height + row_height >= height:
|
|
|
|
break
|
|
|
|
table_height += row_height
|
|
|
|
row_index += 1
|
|
|
|
|
|
|
|
# divide the table if any row did not fit
|
|
|
|
new_table = None
|
|
|
|
if row_index < len(self._children):
|
|
|
|
new_table = GtkDocTable(self._style)
|
|
|
|
for row in self._children[row_index:]:
|
|
|
|
new_table.add_child(row)
|
|
|
|
del self._children[row_index:]
|
|
|
|
|
|
|
|
return (self, new_table), table_height
|
|
|
|
|
|
|
|
def draw(self, cr, layout, width, dpi_x, dpi_y):
|
|
|
|
#calculate real table width
|
|
|
|
table_width = width * self._style.get_width() / 100
|
|
|
|
# TODO is a table always left aligned??
|
|
|
|
table_height = 0
|
|
|
|
|
|
|
|
# draw all the rows
|
|
|
|
for row in self._children:
|
|
|
|
cr.save()
|
|
|
|
cr.translate(0, table_height)
|
|
|
|
row_height = row.draw(cr, layout, table_width, dpi_x, dpi_y)
|
|
|
|
cr.restore()
|
|
|
|
table_height += row_height
|
|
|
|
|
|
|
|
if DEBUG:
|
|
|
|
cr.set_line_width(0.1)
|
|
|
|
cr.set_source_rgb(1.0, 0, 0)
|
|
|
|
cr.rectangle(0, 0, table_width, table_height)
|
|
|
|
cr.stroke()
|
|
|
|
|
|
|
|
return table_height
|
|
|
|
|
|
|
|
class GtkDocTableRow(GtkDocBaseElement):
|
|
|
|
"""Implement a row in a table.
|
|
|
|
"""
|
|
|
|
_type = 'ROW'
|
|
|
|
_allowed_children = ['CELL']
|
|
|
|
|
|
|
|
def divide(self, layout, width, height, dpi_x, dpi_y):
|
|
|
|
# the highest cell gives the height of the row
|
|
|
|
cell_heights = []
|
|
|
|
cell_width_iter = self._style.__iter__()
|
|
|
|
for cell in self._children:
|
|
|
|
cell_width = 0
|
|
|
|
for i in range(cell.get_span()):
|
|
|
|
cell_width += cell_width_iter.next()
|
|
|
|
cell_width = cell_width * width / 100
|
|
|
|
(c1, c2), cell_height = cell.divide(layout, cell_width, height,
|
|
|
|
dpi_x, dpi_y)
|
|
|
|
cell_heights.append(cell_height)
|
|
|
|
|
2007-08-18 23:53:18 +05:30
|
|
|
# save height [inch] of the row to be able to draw exact cell border
|
|
|
|
row_height = max(cell_heights)
|
|
|
|
self.height = row_height / dpi_y
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
# a row can't be divided, return the height
|
2007-08-18 23:53:18 +05:30
|
|
|
return (self, None), row_height
|
2007-08-18 00:45:05 +05:30
|
|
|
|
|
|
|
def draw(self, cr, layout, width, dpi_x, dpi_y):
|
|
|
|
cr.save()
|
|
|
|
|
2007-08-18 23:53:18 +05:30
|
|
|
# get the height of this row
|
|
|
|
row_height = self.height * dpi_y
|
|
|
|
|
|
|
|
# draw all the cells in the row
|
2007-08-18 00:45:05 +05:30
|
|
|
cell_width_iter = self._style.__iter__()
|
|
|
|
for cell in self._children:
|
|
|
|
cell_width = 0
|
|
|
|
for i in range(cell.get_span()):
|
|
|
|
cell_width += cell_width_iter.next()
|
|
|
|
cell_width = cell_width * width / 100
|
2007-08-18 23:53:18 +05:30
|
|
|
cell.draw(cr, layout, cell_width, row_height, dpi_x, dpi_y)
|
2007-08-18 00:45:05 +05:30
|
|
|
cr.translate(cell_width, 0)
|
|
|
|
cr.restore()
|
|
|
|
|
|
|
|
if DEBUG:
|
|
|
|
cr.set_line_width(0.1)
|
|
|
|
cr.set_source_rgb(0, 0, 1.0)
|
|
|
|
cr.rectangle(0, 0, width, row_height)
|
|
|
|
cr.stroke()
|
|
|
|
|
|
|
|
return row_height
|
|
|
|
|
|
|
|
class GtkDocTableCell(GtkDocBaseElement):
|
|
|
|
"""Implement a cell in a table row.
|
|
|
|
"""
|
|
|
|
_type = 'CELL'
|
2007-08-18 23:53:18 +05:30
|
|
|
_allowed_children = ['PARAGRAPH', 'IMAGE']
|
2007-08-18 00:45:05 +05:30
|
|
|
|
|
|
|
def __init__(self, style, span=1):
|
|
|
|
GtkDocBaseElement.__init__(self, style)
|
|
|
|
self._span = span
|
|
|
|
|
|
|
|
def get_span(self):
|
|
|
|
return self._span
|
|
|
|
|
|
|
|
def divide(self, layout, width, height, dpi_x, dpi_y):
|
|
|
|
h_padding = self._style.get_padding() * dpi_x / 2.54
|
|
|
|
v_padding = self._style.get_padding() * dpi_y / 2.54
|
|
|
|
|
|
|
|
# calculate real available width
|
|
|
|
width -= 2 * h_padding
|
|
|
|
|
|
|
|
# calculate height of each children
|
|
|
|
cell_height = 0
|
|
|
|
for child in self._children:
|
|
|
|
(e1, e2), child_height = child.divide(layout, width, height,
|
|
|
|
dpi_x, dpi_y)
|
|
|
|
cell_height += child_height
|
|
|
|
|
|
|
|
# calculate real height
|
|
|
|
cell_height += 2 * v_padding
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
# a cell can't be divided, return the heigth
|
2007-08-18 00:45:05 +05:30
|
|
|
return (self, None), cell_height
|
|
|
|
|
2007-08-18 23:53:18 +05:30
|
|
|
def draw(self, cr, layout, width, cell_height, dpi_x, dpi_y):
|
|
|
|
"""Draw a cell.
|
|
|
|
|
|
|
|
This draw method is a bit different from the others, as common
|
|
|
|
cell height of all cells in a row is also given as parameter.
|
|
|
|
This is needed to be able to draw proper vertical borders around
|
|
|
|
each cell, i.e. the border should be as long as the highest cell
|
|
|
|
in the given row.
|
|
|
|
|
|
|
|
"""
|
2007-08-18 00:45:05 +05:30
|
|
|
h_padding = self._style.get_padding() * dpi_x / 2.54
|
|
|
|
v_padding = self._style.get_padding() * dpi_y / 2.54
|
|
|
|
|
|
|
|
# calculate real available width
|
|
|
|
i_width = width - 2 * h_padding
|
|
|
|
|
|
|
|
# draw children
|
|
|
|
cr.save()
|
|
|
|
cr.translate(h_padding, v_padding)
|
|
|
|
for child in self._children:
|
|
|
|
child_height = child.draw(cr, layout, i_width, dpi_x, dpi_y)
|
|
|
|
cr.translate(0, child_height)
|
|
|
|
cr.restore()
|
|
|
|
|
|
|
|
# draw the borders
|
|
|
|
if self._style.get_top_border():
|
|
|
|
cr.move_to(0, 0)
|
|
|
|
cr.rel_line_to(width , 0)
|
|
|
|
if self._style.get_right_border():
|
|
|
|
cr.move_to(width, 0)
|
|
|
|
cr.rel_line_to(0, cell_height)
|
|
|
|
if self._style.get_bottom_border():
|
|
|
|
cr.move_to(0, cell_height)
|
|
|
|
cr.rel_line_to(width, 0)
|
|
|
|
if self._style.get_left_border():
|
|
|
|
cr.move_to(0, 0)
|
|
|
|
cr.line_to(0, cell_height)
|
|
|
|
|
|
|
|
cr.set_line_width(1)
|
|
|
|
cr.set_source_rgb(0, 0, 0)
|
|
|
|
cr.stroke()
|
|
|
|
|
|
|
|
if DEBUG:
|
|
|
|
cr.set_line_width(0.1)
|
|
|
|
cr.set_source_rgb(0, 1.0, 0)
|
|
|
|
cr.rectangle(0, 0, width, cell_height)
|
|
|
|
cr.stroke()
|
|
|
|
|
|
|
|
return cell_height
|
|
|
|
|
2007-08-18 23:53:18 +05:30
|
|
|
class GtkDocPicture(GtkDocBaseElement):
|
|
|
|
"""Implement an image.
|
|
|
|
"""
|
|
|
|
_type = 'IMAGE'
|
|
|
|
_allowed_children = []
|
|
|
|
|
|
|
|
def __init__(self, style, filename, width, height):
|
|
|
|
GtkDocBaseElement.__init__(self, style)
|
|
|
|
self._filename = filename
|
|
|
|
self._width = width
|
|
|
|
self._height = height
|
|
|
|
|
|
|
|
def divide(self, layout, width, height, dpi_x, dpi_y):
|
|
|
|
img_width = self._width * dpi_x / 2.54
|
|
|
|
img_height = self._height * dpi_y / 2.54
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
# image can't be divided, a new page must begin
|
|
|
|
# if it can't fit on the current one
|
2007-08-18 23:53:18 +05:30
|
|
|
if img_height <= height:
|
|
|
|
return (self, None), img_height
|
|
|
|
else:
|
|
|
|
return (None, self), 0
|
|
|
|
|
|
|
|
def draw(self, cr, layout, width, dpi_x, dpi_y):
|
|
|
|
img_width = self._width * dpi_x / 2.54
|
|
|
|
img_height = self._height * dpi_y / 2.54
|
|
|
|
|
|
|
|
if self._style == 'right':
|
|
|
|
l_margin = width - img_width
|
|
|
|
elif self._style == 'center':
|
|
|
|
l_margin = (width - img_width) / 2.0
|
|
|
|
else:
|
|
|
|
l_margin = 0
|
|
|
|
|
|
|
|
# load the image and get its extents
|
|
|
|
pixbuf = gtk.gdk.pixbuf_new_from_file(self._filename)
|
|
|
|
pixbuf_width = pixbuf.get_width()
|
|
|
|
pixbuf_height = pixbuf.get_height()
|
|
|
|
|
|
|
|
# calculate the scale to fit image into the set extents
|
|
|
|
scale = min(img_width / pixbuf_width, img_height / pixbuf_height)
|
|
|
|
|
|
|
|
# draw the image
|
|
|
|
cr.save()
|
|
|
|
cr.translate(l_margin, 0)
|
|
|
|
cr.scale(scale, scale)
|
|
|
|
gcr = gtk.gdk.CairoContext(cr)
|
|
|
|
gcr.set_source_pixbuf(pixbuf,
|
|
|
|
(img_width / scale - pixbuf_width) / 2,
|
|
|
|
(img_height / scale - pixbuf_height) / 2)
|
|
|
|
cr.rectangle(0 , 0, img_width / scale, img_height / scale)
|
|
|
|
##gcr.set_source_pixbuf(pixbuf,
|
|
|
|
##(img_width - pixbuf_width) / 2,
|
|
|
|
##(img_height - pixbuf_height) / 2)
|
|
|
|
##cr.rectangle(0 , 0, img_width, img_height)
|
|
|
|
##cr.scale(scale, scale)
|
|
|
|
cr.fill()
|
|
|
|
cr.restore()
|
|
|
|
|
|
|
|
if DEBUG:
|
|
|
|
cr.set_line_width(0.1)
|
|
|
|
cr.set_source_rgb(1.0, 0, 0)
|
|
|
|
cr.rectangle(l_margin, 0, img_width, img_height)
|
|
|
|
cr.stroke()
|
|
|
|
|
|
|
|
return (img_height)
|
2007-09-05 20:38:22 +05:30
|
|
|
|
|
|
|
class GtkDocFrame(GtkDocBaseElement):
|
|
|
|
"""Implement a frame.
|
|
|
|
"""
|
|
|
|
_type = 'FRAME'
|
|
|
|
_allowed_children = ['LINE', 'POLYGON', 'BOX', 'TEXT']
|
|
|
|
|
|
|
|
def divide(self, layout, width, height, dpi_x, dpi_y):
|
|
|
|
frame_width = self._style.width * dpi_x / 2.54
|
|
|
|
frame_height = self._style.height * dpi_y / 2.54
|
|
|
|
t_margin = self._style.spacing[2] * dpi_y / 2.54
|
|
|
|
b_margin = self._style.spacing[3] * dpi_y / 2.54
|
|
|
|
|
|
|
|
# frame can't be divided, a new page must begin
|
|
|
|
# if it can't fit on the current one
|
|
|
|
if frame_height + t_margin + b_margin <= height:
|
|
|
|
return (self, None), frame_height + t_margin + b_margin
|
|
|
|
elif frame_height + t_margin <= height:
|
|
|
|
return (self, None), height
|
|
|
|
else:
|
|
|
|
return (None, self), 0
|
|
|
|
|
|
|
|
def draw(self, cr, layout, width, dpi_x, dpi_y):
|
|
|
|
frame_width = self._style.width * dpi_x / 2.54
|
|
|
|
frame_height = self._style.height * dpi_y / 2.54
|
|
|
|
t_margin = self._style.spacing[2] * dpi_y / 2.54
|
|
|
|
b_margin = self._style.spacing[3] * dpi_y / 2.54
|
|
|
|
|
2007-09-07 17:23:12 +05:30
|
|
|
if self._style.align == 'left':
|
|
|
|
l_margin = 0
|
|
|
|
elif self._style.align == 'right':
|
2007-09-05 20:38:22 +05:30
|
|
|
l_margin = width - frame_width
|
|
|
|
elif self._style.align == 'center':
|
|
|
|
l_margin = (width - frame_width) / 2.0
|
|
|
|
else:
|
2007-09-07 17:23:12 +05:30
|
|
|
raise ValueError
|
2007-09-05 20:38:22 +05:30
|
|
|
|
|
|
|
# draw each element in the frame
|
|
|
|
cr.save()
|
|
|
|
cr.translate(l_margin, t_margin)
|
|
|
|
cr.rectangle(0, 0, frame_width, frame_height)
|
|
|
|
cr.clip()
|
|
|
|
|
|
|
|
for elem in self._children:
|
|
|
|
elem.draw(cr, layout, frame_width, dpi_x, dpi_y)
|
|
|
|
|
|
|
|
cr.restore()
|
|
|
|
|
|
|
|
if DEBUG:
|
|
|
|
cr.set_line_width(0.1)
|
|
|
|
cr.set_source_rgb(1.0, 0, 0)
|
|
|
|
cr.rectangle(l_margin, t_margin, frame_width, frame_height)
|
|
|
|
cr.stroke()
|
|
|
|
|
|
|
|
return frame_height + t_margin + b_margin
|
|
|
|
|
|
|
|
class GtkDocLine(GtkDocBaseElement):
|
2007-09-07 03:01:33 +05:30
|
|
|
"""Implement a line.
|
2007-09-05 20:38:22 +05:30
|
|
|
"""
|
|
|
|
_type = 'LINE'
|
|
|
|
_allowed_children = []
|
|
|
|
|
|
|
|
def __init__(self, style, x1, y1, x2, y2):
|
|
|
|
GtkDocBaseElement.__init__(self, style)
|
|
|
|
self._start = (x1, y1)
|
|
|
|
self._end = (x2, y2)
|
|
|
|
|
|
|
|
def draw(self, cr, layout, width, dpi_x, dpi_y):
|
|
|
|
start = (self._start[0] * dpi_x / 2.54, self._start[1] * dpi_y / 2.54)
|
|
|
|
end = (self._end[0] * dpi_x / 2.54, self._end[1] * dpi_y / 2.54)
|
2007-09-07 03:01:33 +05:30
|
|
|
line_color = ReportUtils.rgb_color(self._style.get_color())
|
2007-09-05 20:38:22 +05:30
|
|
|
|
|
|
|
cr.save()
|
2007-09-07 03:01:33 +05:30
|
|
|
cr.set_source_rgb(*line_color)
|
2007-09-05 20:38:22 +05:30
|
|
|
cr.set_line_width(self._style.get_line_width())
|
|
|
|
# TODO line style
|
|
|
|
cr.move_to(*start)
|
|
|
|
cr.line_to(*end)
|
|
|
|
cr.stroke()
|
|
|
|
cr.restore()
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
2007-09-07 03:01:33 +05:30
|
|
|
class GtkDocPolygon(GtkDocBaseElement):
|
|
|
|
"""Implement a line.
|
|
|
|
"""
|
|
|
|
_type = 'POLYGON'
|
|
|
|
_allowed_children = []
|
|
|
|
|
|
|
|
def __init__(self, style, path):
|
|
|
|
GtkDocBaseElement.__init__(self, style)
|
|
|
|
self._path = path
|
|
|
|
|
|
|
|
def draw(self, cr, layout, width, dpi_x, dpi_y):
|
|
|
|
path = [(x * dpi_x / 2.54, y * dpi_y / 2.54) for (x, y) in self._path]
|
|
|
|
path_start = path.pop(0)
|
|
|
|
path_stroke_color = ReportUtils.rgb_color(self._style.get_color())
|
|
|
|
path_fill_color = ReportUtils.rgb_color(self._style.get_fill_color())
|
|
|
|
|
|
|
|
cr.save()
|
|
|
|
cr.move_to(*path_start)
|
|
|
|
for (x, y) in path:
|
|
|
|
cr.line_to(x, y)
|
|
|
|
cr.close_path()
|
|
|
|
cr.set_source_rgb(*path_fill_color)
|
|
|
|
cr.fill_preserve()
|
|
|
|
cr.set_source_rgb(*path_stroke_color)
|
|
|
|
cr.set_line_width(self._style.get_line_width())
|
|
|
|
# TODO line style
|
|
|
|
cr.stroke()
|
|
|
|
cr.restore()
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
2007-09-05 20:38:22 +05:30
|
|
|
class GtkDocBox(GtkDocBaseElement):
|
2007-09-07 17:23:12 +05:30
|
|
|
"""Implement a box with optional shadow around it.
|
2007-09-05 20:38:22 +05:30
|
|
|
"""
|
|
|
|
_type = 'BOX'
|
|
|
|
_allowed_children = []
|
|
|
|
|
2007-09-07 17:23:12 +05:30
|
|
|
def __init__(self, style, x, y, width, height):
|
2007-09-05 20:38:22 +05:30
|
|
|
GtkDocBaseElement.__init__(self, style)
|
|
|
|
self._x = x
|
|
|
|
self._y = y
|
|
|
|
self._width = width
|
|
|
|
self._height = height
|
|
|
|
|
|
|
|
def draw(self, cr, layout, width, dpi_x, dpi_y):
|
|
|
|
box_x = self._x * dpi_x / 2.54
|
|
|
|
box_y = self._y * dpi_y / 2.54
|
|
|
|
box_width = self._width * dpi_x / 2.54
|
|
|
|
box_height = self._height * dpi_y / 2.54
|
|
|
|
|
2007-09-07 03:01:33 +05:30
|
|
|
box_stroke_color = ReportUtils.rgb_color((0, 0, 0))
|
|
|
|
box_fill_color = ReportUtils.rgb_color(self._style.get_fill_color())
|
|
|
|
shadow_color = ReportUtils.rgb_color((192, 192, 192))
|
|
|
|
|
2007-09-05 20:38:22 +05:30
|
|
|
cr.save()
|
|
|
|
|
|
|
|
cr.set_line_width(self._style.get_line_width())
|
2007-09-07 03:01:33 +05:30
|
|
|
# TODO line style
|
2007-09-05 20:38:22 +05:30
|
|
|
|
|
|
|
if self._style.get_shadow():
|
|
|
|
shadow_x = box_x + self._style.get_shadow_space() * dpi_x / 2.54
|
|
|
|
shadow_y = box_y + self._style.get_shadow_space() * dpi_y / 2.54
|
|
|
|
|
2007-09-07 03:01:33 +05:30
|
|
|
cr.set_source_rgb(*shadow_color)
|
2007-09-05 20:38:22 +05:30
|
|
|
cr.rectangle(shadow_x, shadow_y, box_width, box_height)
|
|
|
|
cr.fill()
|
|
|
|
|
|
|
|
cr.rectangle(box_x, box_y, box_width, box_height)
|
2007-09-07 03:01:33 +05:30
|
|
|
cr.set_source_rgb(*box_fill_color)
|
2007-09-05 20:38:22 +05:30
|
|
|
cr.fill_preserve()
|
2007-09-07 03:01:33 +05:30
|
|
|
cr.set_source_rgb(*box_stroke_color)
|
2007-09-05 20:38:22 +05:30
|
|
|
cr.stroke()
|
|
|
|
|
|
|
|
cr.restore()
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
2007-09-07 05:23:18 +05:30
|
|
|
class GtkDocText(GtkDocBaseElement):
|
|
|
|
"""Implement a text on graphical reports.
|
|
|
|
"""
|
|
|
|
_type = 'TEXT'
|
|
|
|
_allowed_children = []
|
|
|
|
|
|
|
|
# line spacing is not defined in BaseDoc.ParagraphStyle
|
|
|
|
spacing = 1
|
|
|
|
|
|
|
|
def __init__(self, style, vertical_alignment, text, x, y, angle=0):
|
|
|
|
GtkDocBaseElement.__init__(self, style)
|
|
|
|
self._align_y = vertical_alignment
|
|
|
|
self._text = text
|
|
|
|
self._x = x
|
|
|
|
self._y = y
|
|
|
|
self._angle = angle
|
|
|
|
|
|
|
|
def draw(self, cr, layout, width, dpi_x, dpi_y):
|
|
|
|
text_x = self._x * dpi_x / 2.54
|
|
|
|
text_y = self._y * dpi_y / 2.54
|
|
|
|
|
|
|
|
# turn off text wrapping
|
|
|
|
layout.set_width(-1)
|
|
|
|
|
|
|
|
# set paragraph properties
|
|
|
|
layout.set_spacing(self.spacing * pango.SCALE)
|
|
|
|
#
|
|
|
|
align = self._style.get_alignment_text()
|
|
|
|
if align == 'left':
|
|
|
|
layout.set_alignment(pango.ALIGN_LEFT)
|
|
|
|
elif align == 'right':
|
|
|
|
layout.set_alignment(pango.ALIGN_RIGHT)
|
|
|
|
elif align == 'center':
|
|
|
|
layout.set_alignment(pango.ALIGN_CENTER)
|
|
|
|
elif align == 'justify':
|
|
|
|
layout.set_justify(True)
|
|
|
|
else:
|
|
|
|
raise ValueError
|
|
|
|
#
|
|
|
|
font_style = self._style.get_font()
|
|
|
|
layout.set_font_description(fontstyle_to_fontdescription(font_style))
|
|
|
|
|
|
|
|
# layout the text
|
|
|
|
layout.set_markup(self._text)
|
|
|
|
layout_width, layout_height = layout.get_size()
|
|
|
|
|
|
|
|
# calculate horizontal and vertical alignment shift
|
|
|
|
if align == 'left':
|
|
|
|
align_x = 0
|
|
|
|
elif align == 'right':
|
|
|
|
align_x = - layout_width / pango.SCALE
|
|
|
|
elif align == 'center' or align == 'justify':
|
|
|
|
align_x = - layout_width / pango.SCALE / 2
|
|
|
|
else:
|
|
|
|
raise ValueError
|
|
|
|
|
|
|
|
if self._align_y == 'top':
|
|
|
|
align_y = 0
|
|
|
|
elif self._align_y == 'center':
|
|
|
|
align_y = - layout_height / pango.SCALE / 2
|
|
|
|
elif self._align_y == 'bottom':
|
|
|
|
align_y = - layout_height / pango.SCALE
|
|
|
|
else:
|
|
|
|
raise ValueError
|
|
|
|
|
|
|
|
# render the layout onto the cairo surface
|
|
|
|
cr.save()
|
|
|
|
cr.translate(text_x, text_y)
|
|
|
|
cr.rotate(radians(self._angle))
|
|
|
|
cr.move_to(align_x, align_y)
|
|
|
|
# TODO color from the style
|
|
|
|
cr.set_source_rgb(0, 0, 0)
|
|
|
|
cr.show_layout(layout)
|
|
|
|
cr.restore()
|
|
|
|
|
|
|
|
return layout_height / pango.SCALE
|
|
|
|
|
2007-01-05 17:28:16 +05:30
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
2007-08-11 04:44:12 +05:30
|
|
|
# CairoDoc and GtkPrint class
|
2007-01-05 17:28:16 +05:30
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
2007-08-11 04:44:12 +05:30
|
|
|
class CairoDoc(BaseDoc.BaseDoc, BaseDoc.TextDoc, BaseDoc.DrawDoc):
|
|
|
|
"""Act as an abstract document that can render onto a cairo context.
|
|
|
|
|
|
|
|
Maintains an abstract model of the document. The root of this abstract
|
|
|
|
document is self._doc. The model is build via the subclassed BaseDoc, and
|
|
|
|
the implemented TextDoc, DrawDoc interface methods.
|
|
|
|
|
|
|
|
It can render the model onto cairo context pages, according to the received
|
|
|
|
page style.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2007-07-27 15:54:09 +05:30
|
|
|
# BaseDoc implementation
|
|
|
|
|
|
|
|
def open(self, filename):
|
2007-09-05 20:38:22 +05:30
|
|
|
self._filename = filename
|
2007-08-11 04:44:12 +05:30
|
|
|
self._doc = GtkDocDocument()
|
|
|
|
self._active_element = self._doc
|
|
|
|
self._pages = []
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
def close(self):
|
2007-09-03 17:23:57 +05:30
|
|
|
self.run()
|
|
|
|
|
|
|
|
def run(self):
|
2007-08-26 19:35:34 +05:30
|
|
|
"""End the meta document.
|
|
|
|
|
|
|
|
It must be implemented in the subclasses. The idea is that with
|
|
|
|
different subclass different output could be generated:
|
2007-08-29 03:58:58 +05:30
|
|
|
e.g. Print, PDF, PS, PNG (which are currently supported by Cairo).
|
2007-08-26 19:35:34 +05:30
|
|
|
|
|
|
|
"""
|
2007-08-11 04:44:12 +05:30
|
|
|
raise NotImplementedError
|
|
|
|
|
2007-07-27 15:54:09 +05:30
|
|
|
# TextDoc implementation
|
|
|
|
|
2007-01-05 17:28:16 +05:30
|
|
|
def page_break(self):
|
2007-08-11 04:44:12 +05:30
|
|
|
self._active_element.add_child(GtkDocPagebreak())
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
def start_bold(self):
|
2007-08-11 04:44:12 +05:30
|
|
|
self.write_text('<b>')
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
def end_bold(self):
|
2007-08-11 04:44:12 +05:30
|
|
|
self.write_text('</b>')
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
def start_superscript(self):
|
2007-08-15 02:27:26 +05:30
|
|
|
self.write_text('<small><sup>')
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
def end_superscript(self):
|
2007-08-15 02:27:26 +05:30
|
|
|
self.write_text('</sup></small>')
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-08-15 02:27:26 +05:30
|
|
|
def start_paragraph(self, style_name, leader=None):
|
2007-08-11 04:44:12 +05:30
|
|
|
style_sheet = self.get_style_sheet()
|
|
|
|
style = style_sheet.get_paragraph_style(style_name)
|
|
|
|
|
|
|
|
new_paragraph = GtkDocParagraph(style, leader)
|
|
|
|
self._active_element.add_child(new_paragraph)
|
|
|
|
self._active_element = new_paragraph
|
2007-07-27 15:54:09 +05:30
|
|
|
|
|
|
|
def end_paragraph(self):
|
2007-08-11 04:44:12 +05:30
|
|
|
self._active_element = self._active_element.get_parent()
|
2007-07-27 15:54:09 +05:30
|
|
|
|
|
|
|
def start_table(self, name, style_name):
|
2007-08-18 00:45:05 +05:30
|
|
|
style_sheet = self.get_style_sheet()
|
|
|
|
style = style_sheet.get_table_style(style_name)
|
|
|
|
|
|
|
|
new_table = GtkDocTable(style)
|
|
|
|
self._active_element.add_child(new_table)
|
|
|
|
self._active_element = new_table
|
|
|
|
|
|
|
|
# we need to remember the column width list from the table style.
|
|
|
|
# this is an ugly hack, but got no better idea.
|
|
|
|
self._active_row_style = []
|
|
|
|
for i in range(style.get_columns()):
|
|
|
|
self._active_row_style.append(style.get_column_width(i))
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
def end_table(self):
|
2007-08-18 00:45:05 +05:30
|
|
|
self._active_element = self._active_element.get_parent()
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
def start_row(self):
|
2007-08-18 00:45:05 +05:30
|
|
|
new_row = GtkDocTableRow(self._active_row_style)
|
|
|
|
self._active_element.add_child(new_row)
|
|
|
|
self._active_element = new_row
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
def end_row(self):
|
2007-08-18 00:45:05 +05:30
|
|
|
self._active_element = self._active_element.get_parent()
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-07-27 15:54:09 +05:30
|
|
|
def start_cell(self, style_name, span=1):
|
2007-08-18 00:45:05 +05:30
|
|
|
style_sheet = self.get_style_sheet()
|
|
|
|
style = style_sheet.get_cell_style(style_name)
|
|
|
|
|
|
|
|
new_cell = GtkDocTableCell(style, span)
|
|
|
|
self._active_element.add_child(new_cell)
|
|
|
|
self._active_element = new_cell
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
def end_cell(self):
|
2007-08-18 00:45:05 +05:30
|
|
|
self._active_element = self._active_element.get_parent()
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-07-27 15:54:09 +05:30
|
|
|
def write_note(self, text, format, style_name):
|
2007-08-11 04:44:12 +05:30
|
|
|
if format == 1:
|
|
|
|
for line in text.split('\n'):
|
|
|
|
self.start_paragraph(style_name)
|
|
|
|
self.write_text(line)
|
|
|
|
self.end_paragraph()
|
|
|
|
elif format == 0:
|
|
|
|
for line in text.split('\n\n'):
|
|
|
|
self.start_paragraph(style_name)
|
|
|
|
line = line.replace('\n',' ')
|
|
|
|
line = ' '.join(line.split())
|
|
|
|
self.write_text(line)
|
|
|
|
self.end_paragraph()
|
|
|
|
|
2007-07-27 15:54:09 +05:30
|
|
|
def write_text(self, text, mark=None):
|
2007-08-18 00:45:05 +05:30
|
|
|
# FIXME this is ugly, do we really need it?
|
|
|
|
text = text.replace('<super>', '<small><sup>')
|
|
|
|
text = text.replace('</super>', '</sup></small>')
|
2007-08-11 04:44:12 +05:30
|
|
|
self._active_element.add_text(text)
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-07-27 15:54:09 +05:30
|
|
|
def add_media_object(self, name, pos, x_cm, y_cm):
|
2007-08-18 23:53:18 +05:30
|
|
|
new_image = GtkDocPicture(pos, name, x_cm, y_cm)
|
|
|
|
self._active_element.add_child(new_image)
|
2007-07-27 15:54:09 +05:30
|
|
|
|
|
|
|
# DrawDoc implementation
|
|
|
|
|
2007-01-05 17:28:16 +05:30
|
|
|
def start_page(self):
|
2007-09-05 20:38:22 +05:30
|
|
|
new_frame_style = FrameStyle(width=self.get_usable_width(),
|
|
|
|
height=self.get_usable_height())
|
|
|
|
new_frame = GtkDocFrame(new_frame_style)
|
|
|
|
|
|
|
|
self._active_element.add_child(new_frame)
|
|
|
|
self._active_element = new_frame
|
2007-01-05 17:28:16 +05:30
|
|
|
|
|
|
|
def end_page(self):
|
2007-09-05 20:38:22 +05:30
|
|
|
self._active_element = self._active_element.get_parent()
|
|
|
|
self._active_element.add_child(GtkDocPagebreak())
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-09-07 03:01:33 +05:30
|
|
|
def draw_line(self, style_name, x1, y1, x2, y2):
|
|
|
|
style_sheet = self.get_style_sheet()
|
|
|
|
style = style_sheet.get_draw_style(style_name)
|
|
|
|
|
|
|
|
new_line = GtkDocLine(style, x1, y1, x2, y2)
|
|
|
|
self._active_element.add_child(new_line)
|
|
|
|
|
|
|
|
def draw_path(self, style_name, path):
|
|
|
|
style_sheet = self.get_style_sheet()
|
|
|
|
style = style_sheet.get_draw_style(style_name)
|
|
|
|
|
|
|
|
new_polygon = GtkDocPolygon(style, path)
|
|
|
|
self._active_element.add_child(new_polygon)
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-09-05 20:38:22 +05:30
|
|
|
def draw_box(self, style_name, text, x, y, w, h):
|
2007-09-07 05:23:18 +05:30
|
|
|
# we handle the box and...
|
2007-09-05 20:38:22 +05:30
|
|
|
style_sheet = self.get_style_sheet()
|
|
|
|
style = style_sheet.get_draw_style(style_name)
|
|
|
|
|
2007-09-07 17:23:12 +05:30
|
|
|
new_box = GtkDocBox(style, x, y, w, h)
|
2007-09-05 20:38:22 +05:30
|
|
|
self._active_element.add_child(new_box)
|
2007-09-07 05:23:18 +05:30
|
|
|
|
|
|
|
# ...the text separately
|
|
|
|
paragraph_style_name = style.get_paragraph_style()
|
|
|
|
paragraph_style = style_sheet.get_paragraph_style(paragraph_style_name)
|
|
|
|
paragraph_style.set_alignment(BaseDoc.PARA_ALIGN_LEFT)
|
|
|
|
|
|
|
|
new_text = GtkDocText(paragraph_style, 'center', text,
|
|
|
|
x + 0.2, y + h / 2, angle=0)
|
|
|
|
self._active_element.add_child(new_text)
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-09-07 05:23:18 +05:30
|
|
|
def draw_text(self, style_name, text, x, y):
|
|
|
|
style_sheet = self.get_style_sheet()
|
|
|
|
style = style_sheet.get_draw_style(style_name)
|
|
|
|
paragraph_style_name = style.get_paragraph_style()
|
|
|
|
paragraph_style = style_sheet.get_paragraph_style(paragraph_style_name)
|
|
|
|
paragraph_style.set_alignment(BaseDoc.PARA_ALIGN_LEFT)
|
|
|
|
|
|
|
|
new_text = GtkDocText(paragraph_style, 'top', text, x, y, angle=0)
|
|
|
|
self._active_element.add_child(new_text)
|
|
|
|
|
|
|
|
def center_text(self, style_name, text, x, y):
|
|
|
|
style_sheet = self.get_style_sheet()
|
|
|
|
style = style_sheet.get_draw_style(style_name)
|
|
|
|
paragraph_style_name = style.get_paragraph_style()
|
|
|
|
paragraph_style = style_sheet.get_paragraph_style(paragraph_style_name)
|
|
|
|
paragraph_style.set_alignment(BaseDoc.PARA_ALIGN_CENTER)
|
|
|
|
|
|
|
|
new_text = GtkDocText(paragraph_style, 'top', text, x, y, angle=0)
|
|
|
|
self._active_element.add_child(new_text)
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-09-07 05:23:18 +05:30
|
|
|
def rotate_text(self, style_name, text, x, y, angle):
|
|
|
|
style_sheet = self.get_style_sheet()
|
|
|
|
style = style_sheet.get_draw_style(style_name)
|
|
|
|
paragraph_style_name = style.get_paragraph_style()
|
|
|
|
paragraph_style = style_sheet.get_paragraph_style(paragraph_style_name)
|
|
|
|
paragraph_style.set_alignment(BaseDoc.PARA_ALIGN_CENTER)
|
|
|
|
|
|
|
|
new_text = GtkDocText(paragraph_style, 'center', '\n'.join(text),
|
|
|
|
x, y, angle)
|
|
|
|
self._active_element.add_child(new_text)
|
2007-09-03 17:23:57 +05:30
|
|
|
|
|
|
|
# the print settings to remember between print sessions
|
|
|
|
PRINT_SETTINGS = None
|
|
|
|
|
2007-08-11 04:44:12 +05:30
|
|
|
class GtkPrint(CairoDoc):
|
2007-09-03 17:23:57 +05:30
|
|
|
"""Print document via GtkPrint* interface.
|
2007-01-05 17:28:16 +05:30
|
|
|
|
2007-09-03 17:23:57 +05:30
|
|
|
Requires Gtk+ 2.10.
|
|
|
|
|
|
|
|
"""
|
|
|
|
def run(self):
|
|
|
|
"""Run the Gtk Print operation.
|
|
|
|
"""
|
|
|
|
global PRINT_SETTINGS
|
2007-08-15 02:27:26 +05:30
|
|
|
|
2007-09-03 17:23:57 +05:30
|
|
|
# get a page setup from the paper style we have
|
|
|
|
page_setup = paperstyle_to_pagesetup(self.paper)
|
2007-08-26 19:35:34 +05:30
|
|
|
|
2007-09-03 17:23:57 +05:30
|
|
|
# set up a print operation
|
|
|
|
operation = gtk.PrintOperation()
|
|
|
|
operation.set_default_page_setup(page_setup)
|
|
|
|
operation.connect("begin_print", self.on_begin_print)
|
|
|
|
operation.connect("draw_page", self.on_draw_page)
|
|
|
|
operation.connect("paginate", self.on_paginate)
|
|
|
|
operation.connect("preview", self.on_preview)
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
# set print settings if it was stored previously
|
2007-09-03 17:23:57 +05:30
|
|
|
if PRINT_SETTINGS is not None:
|
|
|
|
operation.set_print_settings(PRINT_SETTINGS)
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
# run print dialog
|
2007-09-03 17:23:57 +05:30
|
|
|
while True:
|
|
|
|
self.preview = None
|
|
|
|
res = operation.run(gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG, None)
|
|
|
|
if self.preview is None:
|
|
|
|
break
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
# store print settings if printing was successful
|
|
|
|
if res == gtk.PRINT_OPERATION_RESULT_APPLY:
|
2007-09-03 17:23:57 +05:30
|
|
|
PRINT_SETTINGS = operation.get_print_settings()
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
def on_begin_print(self, operation, context):
|
2007-08-20 19:24:04 +05:30
|
|
|
"""Setup environment for printing.
|
2007-08-11 04:44:12 +05:30
|
|
|
"""
|
2007-08-18 23:53:18 +05:30
|
|
|
# choose installed font faces
|
|
|
|
set_font_families(context.create_pango_context())
|
|
|
|
|
|
|
|
# get page size
|
2007-08-11 04:44:12 +05:30
|
|
|
self.page_width = context.get_width()
|
|
|
|
self.page_height = context.get_height()
|
|
|
|
|
2007-08-20 19:24:04 +05:30
|
|
|
# get all document level elements and beging a new page
|
2007-09-03 17:23:57 +05:30
|
|
|
self.elements_to_paginate = self._doc.get_children()[:]
|
|
|
|
self._pages = [GtkDocDocument(),]
|
2007-08-11 04:44:12 +05:30
|
|
|
self.available_height = self.page_height
|
|
|
|
|
|
|
|
def on_paginate(self, operation, context):
|
2007-08-20 19:24:04 +05:30
|
|
|
"""Paginate the whole document in chunks.
|
2007-08-11 04:44:12 +05:30
|
|
|
|
2007-08-20 19:24:04 +05:30
|
|
|
Only one document level element is handled at one run.
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
"""
|
2007-08-12 16:07:11 +05:30
|
|
|
layout = context.create_pango_layout()
|
2007-08-18 00:45:05 +05:30
|
|
|
dpi_x = context.get_dpi_x()
|
|
|
|
dpi_y = context.get_dpi_y()
|
2007-08-12 16:07:11 +05:30
|
|
|
|
2007-08-11 04:44:12 +05:30
|
|
|
# try to fit the next element to current page, divide it if needed
|
|
|
|
elem = self.elements_to_paginate.pop(0)
|
2007-08-12 16:07:11 +05:30
|
|
|
(e1, e2), e1_h = elem.divide(layout,
|
|
|
|
self.page_width,
|
2007-08-15 02:27:26 +05:30
|
|
|
self.available_height,
|
2007-08-18 00:45:05 +05:30
|
|
|
dpi_x,
|
|
|
|
dpi_y)
|
2007-08-11 04:44:12 +05:30
|
|
|
|
|
|
|
# if (part of) it fits on current page add it
|
|
|
|
if e1 is not None:
|
|
|
|
self._pages[len(self._pages) - 1].add_child(e1)
|
|
|
|
|
2007-08-12 16:07:11 +05:30
|
|
|
# if elem was divided remember the second half to be processed
|
2007-08-11 04:44:12 +05:30
|
|
|
if e2 is not None:
|
|
|
|
self.elements_to_paginate.insert(0, e2)
|
|
|
|
|
|
|
|
# calculate how much space left on current page
|
|
|
|
self.available_height -= e1_h
|
|
|
|
|
|
|
|
# start new page if needed
|
|
|
|
if (e1 is None) or (e2 is not None):
|
|
|
|
self._pages.append(GtkDocDocument())
|
|
|
|
self.available_height = self.page_height
|
|
|
|
|
|
|
|
# update page number
|
|
|
|
operation.set_n_pages(len(self._pages))
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
# tell operation whether we finished or not and...
|
2007-08-11 04:44:12 +05:30
|
|
|
finished = len(self.elements_to_paginate) == 0
|
2007-08-26 19:35:34 +05:30
|
|
|
# ...start preview if needed
|
|
|
|
if finished and self.preview:
|
|
|
|
self.preview.start()
|
2007-08-11 04:44:12 +05:30
|
|
|
return finished
|
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
def on_draw_page(self, operation, context, page_nr):
|
2007-08-20 19:24:04 +05:30
|
|
|
"""Draw the requested page.
|
2007-08-11 04:44:12 +05:30
|
|
|
"""
|
2007-08-12 16:07:11 +05:30
|
|
|
cr = context.get_cairo_context()
|
2007-08-18 00:45:05 +05:30
|
|
|
layout = context.create_pango_layout()
|
|
|
|
dpi_x = context.get_dpi_x()
|
|
|
|
dpi_y = context.get_dpi_y()
|
2007-08-26 19:35:34 +05:30
|
|
|
width = context.get_width()
|
|
|
|
height = context.get_height()
|
2007-08-18 00:45:05 +05:30
|
|
|
|
|
|
|
if DEBUG:
|
|
|
|
cr.set_line_width(0.1)
|
|
|
|
cr.set_source_rgb(0, 1.0, 0)
|
2007-08-26 19:35:34 +05:30
|
|
|
cr.rectangle(0, 0, width, height)
|
2007-08-18 00:45:05 +05:30
|
|
|
cr.stroke()
|
2007-08-15 02:27:26 +05:30
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
self._pages[page_nr].draw(cr, layout, width, dpi_x, dpi_y)
|
2007-08-18 00:45:05 +05:30
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
def on_preview(self, operation, preview, context, parent):
|
2007-08-20 19:24:04 +05:30
|
|
|
"""Implement custom print preview functionality.
|
|
|
|
"""
|
2007-08-26 19:35:34 +05:30
|
|
|
##if os.sys.platform == 'win32':
|
|
|
|
##return False
|
2007-08-29 03:58:58 +05:30
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
self.preview = PrintPreview(operation, preview, context, parent)
|
|
|
|
|
2007-08-29 03:58:58 +05:30
|
|
|
# give a dummy cairo context to gtk.PrintContext,
|
|
|
|
# PrintPreview will update it with the real one
|
2007-08-26 19:35:34 +05:30
|
|
|
width = int(context.get_width())
|
|
|
|
height = int(context.get_height())
|
|
|
|
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
|
|
|
|
cr = cairo.Context(surface)
|
|
|
|
context.set_cairo_context(cr, PRINTER_DPI, PRINTER_DPI)
|
2007-08-11 04:44:12 +05:30
|
|
|
|
2007-08-26 19:35:34 +05:30
|
|
|
return True
|
2007-09-03 17:23:57 +05:30
|
|
|
|
2007-01-05 17:28:16 +05:30
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Register the document generator with the GRAMPS plugin system
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
2007-08-11 04:44:12 +05:30
|
|
|
register_text_doc(_('Print... (Gtk+)'), GtkPrint, 1, 1, 1, "", None)
|
2007-09-07 05:23:18 +05:30
|
|
|
register_draw_doc(_('Print... (Gtk+)'), GtkPrint, 1, 1, "", None)
|
2007-09-07 17:23:12 +05:30
|
|
|
register_book_doc(_('Print... (Gtk+)'), GtkPrint, 1, 1, 1, "", None)
|