2007-08-14 Zsolt Foldvari <zfoldvar@users.sourceforge.net>

* src/docgen/GtkPrint.py: Even more work in progress.



svn: r8817
This commit is contained in:
Zsolt Foldvari 2007-08-14 20:57:26 +00:00
parent 8cf2437f40
commit 90a9e4bd29
2 changed files with 175 additions and 85 deletions

View File

@ -1,3 +1,6 @@
2007-08-14 Zsolt Foldvari <zfoldvar@users.sourceforge.net>
* src/docgen/GtkPrint.py: Even more work in progress.
2007-08-13 Don Allingham <don@gramps-project.org> 2007-08-13 Don Allingham <don@gramps-project.org>
* src/GrampsDbUtils/_WriteGedcom.py: improve FAM structure * src/GrampsDbUtils/_WriteGedcom.py: improve FAM structure

View File

@ -31,6 +31,7 @@ __revision__ = "$Revision$"
# #
#------------------------------------------------------------------------ #------------------------------------------------------------------------
from gettext import gettext as _ from gettext import gettext as _
from math import floor
#------------------------------------------------------------------------ #------------------------------------------------------------------------
# #
@ -284,12 +285,11 @@ def fontstyle_to_fontdescription(font_style):
and has to be set with pango.Layout.set_attributes(attrlist) method. and has to be set with pango.Layout.set_attributes(attrlist) method.
""" """
if font_style.face == BaseDoc.FONT_SERIF: fonts = {
f_family = 'serif' BaseDoc.FONT_SERIF: 'serif',
elif font_style.face == BaseDoc.FONT_SANS_SERIF: BaseDoc.FONT_SANS_SERIF: 'sans',
f_family = 'sans serif' BaseDoc.FONT_MONOSPACE: 'monospace',
else: }
f_family = 'monospace'
if font_style.get_bold(): if font_style.get_bold():
f_weight = pango.WEIGHT_BOLD f_weight = pango.WEIGHT_BOLD
@ -301,13 +301,24 @@ def fontstyle_to_fontdescription(font_style):
else: else:
f_style = pango.STYLE_NORMAL f_style = pango.STYLE_NORMAL
font_description = pango.FontDescription(f_family) font_description = pango.FontDescription(fonts[font_style.face])
font_description.set_size(font_style.get_size() * pango.SCALE) font_description.set_absolute_size(font_style.get_size() * pango.SCALE)
font_description.set_weight(f_weight) font_description.set_weight(f_weight)
font_description.set_style(f_style) font_description.set_style(f_style)
return font_description return font_description
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)):
location = tab_stops[index] * dpi * pango.SCALE / 2.54
tab_array.set_tab(index, pango.TAB_LEFT, location)
return tab_array
##class PrintFacade(gtk.PrintOperation): ##class PrintFacade(gtk.PrintOperation):
##"""Provide the main print operation functions.""" ##"""Provide the main print operation functions."""
@ -474,7 +485,7 @@ class GtkDocBaseElement(object):
""" """
return self._children return self._children
def divide(self, layout, width, height): def divide(self, layout, width, height, dpi_x, dpi_y):
"""Divide the element into two depending on available space. """Divide the element into two depending on available space.
@param layout: pango layout to write on @param layout: pango layout to write on
@ -483,6 +494,10 @@ class GtkDocBaseElement(object):
@param type: device points @param type: device points
@param height: height of available space for this element @param height: height of available space for this element
@param type: device points @param type: device points
@param dpi_x: the horizontal resolution
@param type: dots per inch
@param dpi_y: the vertical resolution
@param type: dots per inch
@return: the divided element, and the height of the first part @return: the divided element, and the height of the first part
@rtype: (GtkDocXXX-1, GtkDocXXX-2), device points @rtype: (GtkDocXXX-1, GtkDocXXX-2), device points
@ -490,16 +505,22 @@ class GtkDocBaseElement(object):
""" """
raise NotImplementedError raise NotImplementedError
def draw(self, width, height): def draw(self, cairo_context, pango_layout, width, dpi_x, dpi_y):
"""Draw itself onto a cairo surface with the given size. """Draw itself onto a cairo surface.
@param cairo_context: context to draw on
@param type: cairo.Context class
@param pango_layout: pango layout to write on
@param type: pango.Layout class
@param width: width of available space for this element @param width: width of available space for this element
@param type: device points @param type: device points
@param height: height of available space for this element @param dpi_x: the horizontal resolution
@param type: device points @param type: dots per inch
@param dpi_y: the vertical resolution
@param type: dots per inch
@return: drawing of this element on a cairo surface @return: height of the element
@rtype: cairo.ImageSurface @rtype: device points
""" """
raise NotImplementedError raise NotImplementedError
@ -510,21 +531,17 @@ class GtkDocDocument(GtkDocBaseElement):
_type = 'DOCUMENT' _type = 'DOCUMENT'
_allowed_children = ['PARAGRAPH', 'PAGEBREAK', 'TABLE', 'MEDIA'] _allowed_children = ['PARAGRAPH', 'PAGEBREAK', 'TABLE', 'MEDIA']
def draw(self, width, height): def draw(self, cairo_context, pango_layout, width, dpi_x, dpi_y):
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
cr = cairo.Context(surface)
x = y = 0 x = y = elem_height = 0
for elem in self._children: for elem in self._children:
elem_surface, elem_height = elem.draw(width, height) cairo_context.translate(x, elem_height)
cr.set_source_surface(elem_surface, x, y) elem_height = elem.draw(cairo_context, pango_layout,
cr.rectangle(x, y, x + width, y + elem_height) width, dpi_x, dpi_y)
cr.fill()
y += elem_height y += elem_height
return surface, y return y
class GtkDocPagebreak(GtkDocBaseElement): class GtkDocPagebreak(GtkDocBaseElement):
"""Implement a page break. """Implement a page break.
@ -532,7 +549,7 @@ class GtkDocPagebreak(GtkDocBaseElement):
_type = 'PAGEBREAK' _type = 'PAGEBREAK'
_allowed_children = None _allowed_children = None
def divide(self, layout, width, height): def divide(self, layout, width, height, dpi_x, dpi_y):
return (None, None), 0 return (None, None), 0
class GtkDocParagraph(GtkDocBaseElement): class GtkDocParagraph(GtkDocBaseElement):
@ -541,36 +558,62 @@ class GtkDocParagraph(GtkDocBaseElement):
_type = 'PARAGRAPH' _type = 'PARAGRAPH'
_allowed_children = None _allowed_children = None
spacing = 2.0 # line spacing is not defined in BaseDoc.ParagraphStyle
spacing = 2
def __init__(self, style, text): def __init__(self, style, leader=None):
GtkDocBaseElement.__init__(self) GtkDocBaseElement.__init__(self)
self._style = style self._style = style
self._text = text if leader:
if self._text: self._text = leader + '\t'
self._text = self._text + ' ' self._style.set_tabs([-1 * self._style.get_first_indent()])
else:
self._text = ''
def add_text(self, text): def add_text(self, text):
self._text = self._text + text self._text = self._text + text
def divide(self, layout, width, height): def divide(self, layout, width, height, dpi_x, dpi_y):
# calculate real width (margins, padding cm->pango) l_margin = self._style.get_left_margin() * dpi_x / 2.54
text_width = width r_margin = self._style.get_right_margin() * dpi_x / 2.54
layout.set_width(text_width * pango.SCALE) 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
# calculate real width available for text
text_width = width - l_margin - 2 * h_padding - r_margin
if f_indent < 0:
text_width -= f_indent
layout.set_width(floor(text_width * pango.SCALE))
# set paragraph properties # set paragraph properties
layout.set_wrap(pango.WRAP_WORD_CHAR) layout.set_wrap(pango.WRAP_WORD_CHAR)
layout.set_spacing(self.spacing * pango.SCALE) layout.set_spacing(self.spacing * pango.SCALE)
layout.set_indent(f_indent * pango.SCALE)
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() font_style = self._style.get_font()
layout.set_font_description(fontstyle_to_fontdescription(font_style)) layout.set_font_description(fontstyle_to_fontdescription(font_style))
# FIXME set alignment and first indent too
# calculate the height of one line # calculate the height of one line
layout.set_text('Test') layout.set_text('Test')
layout_width, layout_height = layout.get_size() layout_width, layout_height = layout.get_size()
line_height = layout_height / pango.SCALE + self.spacing line_height = layout_height / pango.SCALE + self.spacing
# and the number of lines fit on the available height # and the number of lines fit on the available height
line_per_height = height / line_height text_height = height - t_margin - 2 * v_padding
line_per_height = text_height / line_height
# if nothing fits # if nothing fits
if line_per_height < 1: if line_per_height < 1:
@ -581,65 +624,102 @@ class GtkDocParagraph(GtkDocBaseElement):
layout_width, layout_height = layout.get_size() layout_width, layout_height = layout.get_size()
line_count = layout.get_line_count() line_count = layout.get_line_count()
# if all paragraph fits we don't need to cut
if line_count <= line_per_height: if line_count <= line_per_height:
# FIXME return proper paragraph height paragraph_height = ((layout_height / pango.SCALE) +
return (self, None), layout_height / pango.SCALE t_margin +
(2 * v_padding))
if height - paragraph_height > b_margin:
paragraph_height += b_margin
return (self, None), paragraph_height
# get index of first character which doesn't fit on available height # get index of first character which doesn't fit on available height
layout_line = layout.get_line(line_per_height) layout_line = layout.get_line(line_per_height)
index = layout_line.start_index index = layout_line.start_index
# and divide the text # and divide the text, first create the second part
# FIXME new paragraph's style has to be modified new_style = BaseDoc.ParagraphStyle(self._style)
new_paragraph = GtkDocParagraph(self._style) new_style.set_top_margin(0)
new_paragraph = GtkDocParagraph(new_style)
new_paragraph.add_text(self._text[index:]) new_paragraph.add_text(self._text[index:])
# FIXME own style should be modified to # then update the first one
self._text = self._text[:index] self._text = self._text[:index]
self._style.set_bottom_margin(0)
# FIXME return proper paragraph height # FIXME do we need to return the proper height???
return (self, new_paragraph), line_height * line_count #paragraph_height = line_height * line_count + t_margin + 2 * v_padding
paragraph_height = 0
return (self, new_paragraph), paragraph_height
def draw(self, width, height): def draw(self, cr, layout, width, dpi_x, dpi_y):
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) l_margin = self._style.get_left_margin() * dpi_x / 2.54
cr = pangocairo.CairoContext(cairo.Context(surface)) r_margin = self._style.get_right_margin() * dpi_x / 2.54
layout = cr.create_layout() t_margin = self._style.get_top_margin() * dpi_y / 2.54
b_margin = self._style.get_bottom_margin() * dpi_y / 2.54
# calculate real width (margins, padding cm->pango) h_padding = self._style.get_padding() * dpi_x / 2.54
text_width = width v_padding = self._style.get_padding() * dpi_y / 2.54
layout.set_width(text_width * pango.SCALE) f_indent = self._style.get_first_indent() * dpi_x / 2.54
# calculate real width available for text
text_width = width - l_margin - 2 * h_padding - r_margin
if f_indent < 0:
text_width -= f_indent
layout.set_width(floor(text_width * pango.SCALE))
# set paragraph properties # set paragraph properties
layout.set_wrap(pango.WRAP_WORD_CHAR) layout.set_wrap(pango.WRAP_WORD_CHAR)
layout.set_spacing(self.spacing * pango.SCALE) layout.set_spacing(self.spacing * pango.SCALE)
font_style = self._style.get_font() layout.set_indent(f_indent * pango.SCALE)
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()
layout.set_font_description(fontstyle_to_fontdescription(font_style)) layout.set_font_description(fontstyle_to_fontdescription(font_style))
# FIXME set alignment and first indent too
# layout the text
layout.set_markup(self._text) layout.set_markup(self._text)
layout_width, layout_height = layout.get_size() layout_width, layout_height = layout.get_size()
# FIXME move to the proper position (margin, padding) # render the layout onto the cairo surface
cr.move_to(0, 0) x = l_margin + h_padding
if f_indent < 0:
x += f_indent
cr.move_to(x, t_margin + v_padding)
cr.show_layout(layout) cr.show_layout(layout)
# draw the borders # calculate the full paragraph height
if self._style.get_top_border(): height = layout_height/pango.SCALE + t_margin + 2*v_padding + b_margin
cr.move_to(0, 0)
cr.line(width, 0)
if self._style.get_right_border():
cr.move_to(width, 0)
cr.line(width, height)
if self._style.get_bottom_border():
cr.move_to(0, height)
cr.line(width, height)
if self._style.get_left_border():
cr.move_to(0, 0)
cr.line(0, height)
cr.set_line_width(0.1) # draw the borders
if self._style.get_top_border():
cr.move_to(l_margin, t_margin)
cr.rel_line_to(width - l_margin - r_margin, 0)
if self._style.get_right_border():
cr.move_to(width - r_margin, t_margin)
cr.rel_line_to(0, height - t_margin - b_margin)
if self._style.get_bottom_border():
cr.move_to(l_margin, height - b_margin)
cr.rel_line_to(width - l_margin - r_margin, 0)
if self._style.get_left_border():
cr.move_to(l_margin, t_margin)
cr.line_to(0, height - t_margin - b_margin)
#cr.move_to(0, 0)
#cr.line_to(layout_width / pango.SCALE, layout_height / pango.SCALE)
cr.set_line_width(1)
cr.set_source_rgb(0, 0, 0) cr.set_source_rgb(0, 0, 0)
cr.stroke() cr.stroke()
return height
return surface, layout_height / pango.SCALE
#------------------------------------------------------------------------ #------------------------------------------------------------------------
@ -681,12 +761,12 @@ class CairoDoc(BaseDoc.BaseDoc, BaseDoc.TextDoc, BaseDoc.DrawDoc):
self.write_text('</b>') self.write_text('</b>')
def start_superscript(self): def start_superscript(self):
self.write_text('<sup>') self.write_text('<small><sup>')
def end_superscript(self): def end_superscript(self):
self.write_text('</sup>') self.write_text('</sup></small>')
def start_paragraph(self, style_name, leader=''): def start_paragraph(self, style_name, leader=None):
style_sheet = self.get_style_sheet() style_sheet = self.get_style_sheet()
style = style_sheet.get_paragraph_style(style_name) style = style_sheet.get_paragraph_style(style_name)
@ -734,6 +814,9 @@ class CairoDoc(BaseDoc.BaseDoc, BaseDoc.TextDoc, BaseDoc.DrawDoc):
def write_text(self, text, mark=None): def write_text(self, text, mark=None):
log.debug("write_text: %s" % text) log.debug("write_text: %s" % text)
# FIXME this is ugly, do we really need it?
text = text.replace('<super>', '<small><sup>')
text = text.replace('</super>', '</sup></small>')
self._active_element.add_text(text) self._active_element.add_text(text)
def add_media_object(self, name, pos, x_cm, y_cm): def add_media_object(self, name, pos, x_cm, y_cm):
@ -775,8 +858,8 @@ class GtkPrint(CairoDoc):
print_operation.connect("begin_print", self.on_begin_print) print_operation.connect("begin_print", self.on_begin_print)
print_operation.connect("draw_page", self.on_draw_page) print_operation.connect("draw_page", self.on_draw_page)
print_operation.connect("paginate", self.on_paginate) print_operation.connect("paginate", self.on_paginate)
#print_operation.run(gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG, None)
self.print_settings = None self.print_settings = None
self.do_print(print_operation) self.do_print(print_operation)
def do_print(self, operation): def do_print(self, operation):
@ -831,12 +914,16 @@ class GtkPrint(CairoDoc):
""" """
layout = context.create_pango_layout() layout = context.create_pango_layout()
dpi_x = context.get_dpi_x()
dpi_y = context.get_dpi_y()
# try to fit the next element to current page, divide it if needed # try to fit the next element to current page, divide it if needed
elem = self.elements_to_paginate.pop(0) elem = self.elements_to_paginate.pop(0)
(e1, e2), e1_h = elem.divide(layout, (e1, e2), e1_h = elem.divide(layout,
self.page_width, self.page_width,
self.available_height) self.available_height,
dpi_x,
dpi_y)
# if (part of) it fits on current page add it # if (part of) it fits on current page add it
if e1 is not None: if e1 is not None:
@ -875,13 +962,13 @@ class GtkPrint(CairoDoc):
to your needs. to your needs.
""" """
page_surface, real_height = self._pages[page_nr].draw(self.page_width,
self.page_height)
cr = context.get_cairo_context() cr = context.get_cairo_context()
cr.set_source_surface(page_surface, 0, 0) layout = context.create_pango_layout()
cr.rectangle(0, 0, self.page_width, real_height) dpi_x = context.get_dpi_x()
cr.fill() dpi_y = context.get_dpi_y()
self._pages[page_nr].draw(cr, layout, self.page_width, dpi_x, dpi_y)
#def on_preview(self, operation, preview, context, parent, dummy=None): #def on_preview(self, operation, preview, context, parent, dummy=None):
#""" #"""