/usr/share/cherrytree/modules/printing.py is in cherrytree 0.32.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | # -*- coding: UTF-8 -*-
#
# printing.py
#
# Copyright 2009-2014 Giuseppe Penone <giuspen@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
import gtk, gobject, pango, cairo
import copy, cgi
import support, cons
BOX_OFFSET = 4
class PrintData:
"""Print Operation Data"""
text = None
layout = None
layout_is_new_line = None
layout_num_lines = None
page_breaks = None
all_lines_y = None
class PrintHandler:
"""Handler for the CherryTree Prints"""
def __init__(self):
"""Instantiate Variables"""
self.active_prints = []
self.settings = None
self.page_setup = None
self.pdf_filepath = ""
def get_print_operation(self):
"""Return a Print Operation"""
print_operation = gtk.PrintOperation()
print_operation.set_show_progress(True)
if self.page_setup is None: self.page_setup = gtk.PageSetup()
if self.settings is None: self.settings = gtk.PrintSettings()
print_operation.set_default_page_setup(self.page_setup)
print_operation.set_print_settings(self.settings)
return print_operation
def run_print_operation(self, print_operation, parent):
"""Run a Ready Print Operation"""
if self.pdf_filepath: print_operation.set_export_filename(self.pdf_filepath)
print_operation_action = gtk.PRINT_OPERATION_ACTION_EXPORT if self.pdf_filepath else gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG
try: res = print_operation.run(print_operation_action, parent)
except gobject.GError, ex:
support.dialog_error("Error printing file:\n%s (exception catched)" % str(ex), parent)
else:
if res == gtk.PRINT_OPERATION_RESULT_ERROR:
support.dialog_error("Error printing file (bad res)", parent)
elif res == gtk.PRINT_OPERATION_RESULT_APPLY:
self.settings = print_operation.get_print_settings()
if not print_operation.is_finished():
print_operation.connect("status_changed", self.on_print_status_changed)
def on_print_status_changed(self, operation):
"""Print Operation Status Changed"""
if operation.is_finished(): active_prints.remove(operation)
def print_text(self, window, pango_text, text_font, code_font, pixbuf_table_codebox_vector, text_window_width):
"""Start the Print Operations for Text"""
self.pango_font = pango.FontDescription(text_font)
self.codebox_font = pango.FontDescription(code_font)
self.text_window_width = text_window_width
self.table_text_row_height = self.pango_font.get_size()/pango.SCALE
self.table_line_thickness = 6
self.pixbuf_table_codebox_vector = pixbuf_table_codebox_vector
# pixbuf_table_codebox_vector is [ [ "pixbuf"/"table"/"codebox", [offset, pixbuf, alignment] ],... ]
print_data = PrintData()
print_data.text = pango_text
print_operation = self.get_print_operation()
print_operation.connect("begin_print", self.on_begin_print_text, print_data)
print_operation.connect("draw_page", self.on_draw_page_text, print_data)
self.run_print_operation(print_operation, window)
def on_begin_print_text(self, operation, context, print_data):
"""Here we Compute the Lines Positions, the Number of Pages Needed and the Page Breaks"""
self.page_width = context.get_width()
self.page_height = context.get_height() * 1.02 # tolerance at bottom of the page
while 1:
exit_ok = True
print_data.layout = []
print_data.forced_page_break = []
print_data.layout_is_new_line = []
print_data.layout_num_lines = []
print_data.all_lines_y = []
#print "print_data.text", print_data.text
for i, text_slot in enumerate(print_data.text):
print_data.layout.append(context.create_pango_layout())
print_data.layout[-1].set_font_description(self.pango_font)
print_data.layout[-1].set_width(int(self.page_width*pango.SCALE))
is_forced_page_break = text_slot.startswith(2*cons.CHAR_NEWPAGE)
print_data.forced_page_break.append(is_forced_page_break)
print_data.layout[-1].set_markup(text_slot if not is_forced_page_break else text_slot[2:])
if text_slot == cons.CHAR_NEWLINE:
print_data.layout_is_new_line.append(True) # in other case we detect the newline from a following line
else: print_data.layout_is_new_line.append(False) # but here we have a single layout line
print_data.layout_num_lines.append(print_data.layout[-1].get_line_count())
self.y_idx = 0
print_data.page_breaks = []
curr_y = float(0)
inline_pending_height = float(0)
inline_starter = [0, 0]
for i, layout in enumerate(print_data.layout):
#print "layout", i
# text
if print_data.forced_page_break[i] and curr_y > 0:
#print "forced_page_break"
print_data.page_breaks.append(inline_starter)
curr_y = 0;
layout_line_idx = 0
while layout_line_idx < print_data.layout_num_lines[i]:
#print "layout_line_idx", layout_line_idx
layout_line = print_data.layout[i].get_line(layout_line_idx)
line_width, line_height = self.layout_line_get_width_height(layout_line)
# process the line
if line_height > inline_pending_height: inline_pending_height = line_height
if layout_line_idx < print_data.layout_num_lines[i]-1 or print_data.layout_is_new_line[i]:
if curr_y + inline_pending_height > self.page_height:
print_data.page_breaks.append(inline_starter)
#print "added page break", inline_starter
curr_y = 0
if inline_pending_height > self.page_height:
if self.pixbuf_table_codebox_vector[i-1][0] == "codebox"\
and codebox_height > self.page_height:
self.codebox_long_split(i-1, context, print_data)
exit_ok = False
break # go to a new main loop
curr_y += inline_pending_height
print_data.all_lines_y.append(curr_y)
#print "added line y <%s> (%s, %s)" % (curr_y, i, layout_line_idx)
inline_pending_height = 0 # reset the pending elements line to append
inline_starter = [i, layout_line_idx+1]
layout_line_idx += 1
if not exit_ok: break # go to a new main loop
# pixbuf or table or codebox
if i < len(print_data.layout) - 1: # the latest element is supposed to be text
if self.pixbuf_table_codebox_vector[i][0] == "pixbuf":
pixbuf = self.pixbuf_table_codebox_vector[i][1][1]
pixbuf_was_resized = False
pixbuf_width = pixbuf.get_width()
pixbuf_height = pixbuf.get_height()
if pixbuf_width > self.page_width:
image_w_h_ration = float(pixbuf_width)/pixbuf_height
image_width = self.page_width
image_height = image_width / image_w_h_ration
pixbuf = pixbuf.scale_simple(int(image_width),
int(image_height),
gtk.gdk.INTERP_BILINEAR)
pixbuf_was_resized = True
if pixbuf_height > self.page_height:
image_w_h_ration = float(pixbuf_width)/pixbuf_height
image_height = self.page_height
image_width = image_height * image_w_h_ration
pixbuf = pixbuf.scale_simple(int(image_width),
int(image_height),
gtk.gdk.INTERP_BILINEAR)
pixbuf_was_resized = True
if pixbuf_was_resized:
self.pixbuf_table_codebox_vector[i][1][1] = pixbuf
pixbuf_height = pixbuf.get_height() + cons.WHITE_SPACE_BETW_PIXB_AND_TEXT
if inline_pending_height < pixbuf_height: inline_pending_height = pixbuf_height
elif self.pixbuf_table_codebox_vector[i][0] == "table":
table = self.pixbuf_table_codebox_vector[i][1][1]
table['matrix'].insert(0, table['matrix'].pop()) # let's put the title to first row
table_layouts = self.get_table_layouts(context, table)
table_grid = self.get_table_grid(table_layouts, table['col_min'])
table_height = self.get_table_height_from_grid(table_grid)
if inline_pending_height < table_height+BOX_OFFSET: inline_pending_height = table_height+BOX_OFFSET
elif self.pixbuf_table_codebox_vector[i][0] == "codebox":
codebox_dict = self.pixbuf_table_codebox_vector[i][1][1]
codebox_layout = self.get_codebox_layout(context, codebox_dict)
codebox_height = self.get_height_from_layout(codebox_layout)
if inline_pending_height < codebox_height+BOX_OFFSET: inline_pending_height = codebox_height+BOX_OFFSET
if exit_ok: break
operation.set_n_pages(len(print_data.page_breaks) + 1)
def on_draw_page_text(self, operation, context, page_nr, print_data):
"""This Function is Called For Each Page Set in on_begin_print_text"""
if page_nr == 0: start_line_num = [0, 0] # layout num, line num
else: start_line_num = print_data.page_breaks[page_nr - 1]
#print "start_line_num", start_line_num
if page_nr < len(print_data.page_breaks): end_line_num = print_data.page_breaks[page_nr]
else: end_line_num = [len(print_data.layout)-1, print_data.layout_num_lines[-1]]
#print "end_line_num", end_line_num
cairo_context = context.get_cairo_context()
cairo_context.set_source_rgb(0.5, 0.5, 0.5)
cairo_context.set_font_size(12)
page_num_str = "%s/%s" % (page_nr+1, operation.get_property("n-pages"))
cairo_context.move_to(self.page_width/2, self.page_height+17)
cairo_context.show_text(page_num_str)
curr_x = float(0)
i = start_line_num[0]
layout_line_idx = start_line_num[1]
while i <= end_line_num[0]:
# text
cairo_context.set_source_rgb(0, 0, 0)
if i > start_line_num[0]: layout_line_idx = 0 # reset line idx
while layout_line_idx < print_data.layout_num_lines[i]:
layout_line = print_data.layout[i].get_line(layout_line_idx)
line_width, line_height = self.layout_line_get_width_height(layout_line)
# process the line
if line_width > 0:
#print "text (%s, %s) to (%s, %s)" % (line_width, line_height, curr_x, print_data.all_lines_y[self.y_idx])
cairo_context.move_to(curr_x, print_data.all_lines_y[self.y_idx])
cairo_context.show_layout_line(layout_line)
curr_x += line_width
if layout_line_idx < print_data.layout_num_lines[i]-1 or print_data.layout_is_new_line[i]:
curr_x = 0.0
self.y_idx += 1
#print "new index value <%s> (%s, %s)" % (self.y_idx, i, layout_line_idx)
layout_line_idx += 1
if i >= end_line_num[0] and layout_line_idx >= end_line_num[1]: return
# pixbuf or table or codebox
if i < len(print_data.layout) - 1: # the latest element is supposed to be text
if self.pixbuf_table_codebox_vector[i][0] == "pixbuf":
pixbuf = self.pixbuf_table_codebox_vector[i][1][1]
pixbuf_width = pixbuf.get_width()
pixbuf_height = pixbuf.get_height()
#print "pixbuf (%s, %s) to (%s, %s)" % (pixbuf_width, pixbuf_height, curr_x, print_data.all_lines_y[self.y_idx]-pixbuf_height)
cairo_context.set_source_pixbuf(pixbuf, curr_x, print_data.all_lines_y[self.y_idx] - pixbuf_height)
cairo_context.paint()
curr_x += pixbuf_width
elif self.pixbuf_table_codebox_vector[i][0] == "table":
table = self.pixbuf_table_codebox_vector[i][1][1]
table_layouts = self.get_table_layouts(context, table)
table_grid = self.get_table_grid(table_layouts, table['col_min'])
table_width = self.get_table_width_from_grid(table_grid)
table_height = self.get_table_height_from_grid(table_grid)
self.table_draw_grid(cairo_context,
table_grid,
curr_x,
print_data.all_lines_y[self.y_idx] - table_height,
table_width,
table_height)
self.table_draw_text(cairo_context,
table_grid,
table_layouts,
curr_x,
print_data.all_lines_y[self.y_idx] - table_height)
curr_x += table_width
elif self.pixbuf_table_codebox_vector[i][0] == "codebox":
codebox_dict = self.pixbuf_table_codebox_vector[i][1][1]
codebox_layout = self.get_codebox_layout(context, codebox_dict)
codebox_height = self.get_height_from_layout(codebox_layout)
codebox_width = self.get_width_from_layout(codebox_layout)
#print "codebox (%s, %s) to (%s, %s)" % (codebox_width, codebox_height, curr_x, print_data.all_lines_y[self.y_idx]-codebox_height)
self.codebox_draw_box(cairo_context,
curr_x,
print_data.all_lines_y[self.y_idx] - codebox_height,
codebox_width,
codebox_height)
self.codebox_draw_code(cairo_context,
codebox_layout,
curr_x,
print_data.all_lines_y[self.y_idx] - codebox_height)
curr_x += codebox_width
i += 1 # layout increment
def get_codebox_layout(self, context, codebox_dict):
"""Return the CodeBox Layout"""
layout = context.create_pango_layout()
layout.set_font_description(self.codebox_font)
if codebox_dict['width_in_pixels']: codebox_width = codebox_dict['frame_width']
else: codebox_width = self.text_window_width*codebox_dict['frame_width']/100
if codebox_width > self.page_width: codebox_width = self.page_width
layout.set_width(int(codebox_width*pango.SCALE))
layout.set_wrap(pango.WRAP_WORD_CHAR)
layout.set_markup(codebox_dict['fill_text'])
return layout
def get_height_from_layout(self, layout):
"""Returns the Height given the Layout"""
height = 0
num_layout_lines = layout.get_line_count()
for layout_line_idx in range(num_layout_lines):
layout_line = layout.get_line(layout_line_idx)
line_width, line_height = self.layout_line_get_width_height(layout_line)
height += line_height
return height + 2*cons.GRID_SLIP_OFFSET
def get_width_from_layout(self, layout):
"""Returns the Height given the Layout"""
width = 0
num_layout_lines = layout.get_line_count()
for layout_line_idx in range(num_layout_lines):
layout_line = layout.get_line(layout_line_idx)
line_width, line_height = self.layout_line_get_width_height(layout_line)
if line_width > width: width = line_width
return width + 2*cons.GRID_SLIP_OFFSET
def get_table_layouts(self, context, table):
"""Return the Table Cells Layouts"""
table_layouts = []
for i, table_row in enumerate(table['matrix']):
table_layouts.append([])
for j, cell_text in enumerate(table_row):
layout = context.create_pango_layout()
layout.set_font_description(self.pango_font)
cell_text = cgi.escape(cell_text)
if i == 0: cell_text = "<b>" + cell_text + "</b>"
layout.set_width(int(table['col_max']*pango.SCALE))
layout.set_wrap(pango.WRAP_WORD_CHAR)
layout.set_markup(cell_text)
table_layouts[i].append(layout)
return table_layouts
def layout_line_get_width_height(self, layout_line):
"""Returns Width and Height of a layout line"""
ink_rect, logical_rect = layout_line.get_extents()
lx, ly, lwidth, lheight = logical_rect
return [lwidth / 1024.0, lheight / 1024.0]
def get_table_grid(self, table_layouts, col_min):
"""Returns the Dimensions of Rows and Columns"""
rows_h = [0] * len(table_layouts)
cols_w = [col_min] * len(table_layouts[0])
for i, layout_row in enumerate(table_layouts):
for j, layout_cell in enumerate(layout_row):
cell_height = 0
num_layout_lines = layout_cell.get_line_count()
for layout_line_idx in range(num_layout_lines):
layout_line = layout_cell.get_line(layout_line_idx)
line_width, line_height = self.layout_line_get_width_height(layout_line)
cell_height += line_height
if cols_w[j] < line_width: cols_w[j] = line_width
if rows_h[i] < cell_height: rows_h[i] = cell_height
return [rows_h, cols_w]
def get_table_width_from_grid(self, table_grid):
"""Returns the Table Width given the table_grid vector"""
table_width = 0
for col_w in table_grid[1]:
table_width += (col_w + self.table_line_thickness)
return table_width
def get_table_height_from_grid(self, table_grid):
"""Returns the Table Height given the table_grid vector"""
table_height = 0
for row_h in table_grid[0]:
table_height += (row_h + self.table_line_thickness)
return table_height
def codebox_draw_box(self, cairo_context, x0, y0, codebox_width, codebox_height):
"""Draw the CodeBox Box"""
cairo_context.set_source_rgba(0, 0, 0, 0.3)
cairo_context.rectangle(x0, y0, codebox_width, codebox_height)
cairo_context.stroke()
def table_draw_grid(self, cairo_context, table_grid, x0, y0, table_width, table_height):
"""Draw the Table Grid"""
x = x0
y = y0
cairo_context.set_source_rgba(0, 0, 0, 0.3)
# draw lines
cairo_context.move_to(x, y)
cairo_context.line_to(x + table_width, y)
for row_h in table_grid[0]:
y += (row_h + self.table_line_thickness)
cairo_context.move_to(x, y)
cairo_context.line_to(x + table_width, y)
# draw columns
y = y0
cairo_context.move_to(x, y)
cairo_context.line_to(x, y + table_height)
for col_w in table_grid[1]:
x += (col_w + self.table_line_thickness)
cairo_context.move_to(x, y)
cairo_context.line_to(x, y + table_height)
cairo_context.stroke()
def table_draw_text(self, cairo_context, table_grid, table_layouts, x0, y0):
"""Draw the text inside of the Table Cells"""
cairo_context.set_source_rgb(0, 0, 0)
y = y0
for i, row_h in enumerate(table_grid[0]):
x = x0 + cons.GRID_SLIP_OFFSET
for j, col_w in enumerate(table_grid[1]):
layout_cell = table_layouts[i][j]
local_y = y
num_layout_lines = layout_cell.get_line_count()
for layout_line_idx in range(num_layout_lines):
layout_line = layout_cell.get_line(layout_line_idx)
line_width, line_height = self.layout_line_get_width_height(layout_line)
cairo_context.move_to(x, local_y + line_height)
local_y += line_height
cairo_context.show_layout_line(layout_line)
x += col_w + self.table_line_thickness
y += row_h + self.table_line_thickness
def codebox_draw_code(self, cairo_context, codebox_layout, x0, y0):
"""Draw the code inside of the Box"""
cairo_context.set_source_rgb(0, 0, 0)
y = y0
num_layout_lines = codebox_layout.get_line_count()
for layout_line_idx in range(num_layout_lines):
layout_line = codebox_layout.get_line(layout_line_idx)
line_width, line_height = self.layout_line_get_width_height(layout_line)
cairo_context.move_to(x0 + cons.GRID_SLIP_OFFSET, y + line_height)
y += line_height
cairo_context.show_layout_line(layout_line)
def codebox_long_split(self, idx, context, print_data):
"""Split Long CodeBoxes"""
codebox_dict = self.pixbuf_table_codebox_vector[idx][1][1]
original_splitted_pango = codebox_dict['fill_text'].split(cons.CHAR_NEWLINE)
splitted_pango = copy.deepcopy(original_splitted_pango)
codebox_dict_jolly = copy.deepcopy(codebox_dict)
partial_pango_vec = []
while len(splitted_pango) > 1:
splitted_pango = splitted_pango[:-1]
partial_pango = cons.CHAR_NEWLINE.join(splitted_pango)
codebox_dict_jolly['fill_text'] = partial_pango
codebox_layout = self.get_codebox_layout(context, codebox_dict_jolly)
codebox_height = self.get_height_from_layout(codebox_layout)
if codebox_height < self.page_height:
# this slot is done
partial_pango_vec.append(partial_pango)
# let's get ready for the next slot
splitted_pango = original_splitted_pango[len(splitted_pango):]
original_splitted_pango = splitted_pango
partial_pango = cons.CHAR_NEWLINE.join(splitted_pango)
codebox_dict_jolly['fill_text'] = partial_pango
codebox_layout = self.get_codebox_layout(context, codebox_dict_jolly)
codebox_height = self.get_height_from_layout(codebox_layout)
if codebox_height < self.page_height:
# this is the last piece
partial_pango_vec.append(partial_pango)
break
for i, element in enumerate(partial_pango_vec):
if i == 0: codebox_dict['fill_text'] = element
else:
index = idx+i
# add a newline
print_data.text.insert(index, cons.CHAR_NEWLINE)
# add a codebox
new_codebox_dict = copy.deepcopy(codebox_dict)
new_codebox_dict['fill_text'] = element
pixbuf_table_codebox_element = ["codebox", [None, new_codebox_dict, None]]
self.pixbuf_table_codebox_vector.insert(index, pixbuf_table_codebox_element)
|