2007-10-10 [colin] 3.0.2cvs52
[claws.git] / src / printing.c
1 /* Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
2  * Copyright (C) 2007 Holger Berndt <hb@claws-mail.org>,
3  * Colin Leroy <colin@colino.net>, and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "printing.h"
25 #include "image_viewer.h"
26
27 #if GTK_CHECK_VERSION(2,10,0) && !defined(USE_GNOMEPRINT)
28
29 #include "gtkutils.h"
30 #include "toolbar.h"
31 #include "prefs_common.h"
32
33 #include <glib/gi18n.h>
34 #include <gdk/gdkkeysyms.h>
35 #include <gtk/gtk.h>
36 #include <pango/pango.h>
37 #include <string.h>
38 #include <math.h>
39
40 struct _PrintData {
41   PrintRenderer *renderer;
42   gpointer renderer_data;
43   PangoLayout *layout;
44   PangoContext *pango_context;
45   gpointer to_print;
46   GList *page_breaks;
47   guint npages;
48   gint sel_start;
49   gint sel_end;
50   GHashTable *images;
51   gint img_cnt;
52   gdouble zoom;
53 };
54
55 typedef struct {
56   GtkPrintOperation *op;
57   GtkPrintOperationPreview *preview;
58   GtkWidget         *dialog;
59   GtkWidget         *scrolled_window;
60   GtkWidget         *area;
61   PrintData         *print_data;
62   GtkWidget         *page_nr_label;
63   GList             *pages_to_print;
64   GList             *current_page;
65   GtkWidget *first;
66   GtkWidget *next;
67   GtkWidget *previous;
68   GtkWidget *last;
69   GtkWidget *close;
70   GtkWidget *zoom_100;
71   GtkWidget *zoom_fit;
72   GtkWidget *zoom_in;
73   GtkWidget *zoom_out;
74   gboolean rendering;
75   gint page_width;
76   gint page_height;
77 } PreviewData;
78
79 /* callbacks */
80 static void     printing_textview_cb_begin_print(GtkPrintOperation*, GtkPrintContext*, gpointer);
81 static void     printing_textview_cb_draw_page(GtkPrintOperation*, GtkPrintContext*, gint,
82                              gpointer);
83 static gboolean cb_preview(GtkPrintOperation*, GtkPrintOperationPreview*,
84                            GtkPrintContext*, GtkWindow*, gpointer);
85 static void     cb_preview_destroy(GtkWindow*, gpointer);
86 static gboolean cb_preview_close(GtkWidget*, GdkEventAny*, gpointer);
87 static void     cb_preview_size_allocate(GtkWidget*, GtkAllocation*);
88 static void     cb_preview_ready(GtkPrintOperationPreview*,
89                                  GtkPrintContext*, gpointer);
90 static gboolean cb_preview_expose(GtkWidget*, GdkEventExpose*, gpointer);
91 static void     cb_preview_got_page_size(GtkPrintOperationPreview*,
92                                          GtkPrintContext*,
93                                          GtkPageSetup*, gpointer);
94 static void     cb_preview_go_first(GtkButton*, gpointer);
95 static void     cb_preview_go_previous(GtkButton*, gpointer);
96 static void     cb_preview_go_next(GtkButton*, gpointer);
97 static void     cb_preview_go_last(GtkButton*, gpointer);
98 static void     cb_preview_btn_close(GtkButton*, gpointer);
99 static void     cb_preview_zoom_100(GtkButton*, gpointer);
100 static void     cb_preview_zoom_fit(GtkButton*, gpointer);
101 static void     cb_preview_zoom_in(GtkButton*, gpointer);
102 static void     cb_preview_zoom_out(GtkButton*, gpointer);
103 static void     cb_preview_request_page_setup(GtkPrintOperation*,
104                                               GtkPrintContext*,
105                                               gint,GtkPageSetup*,gpointer);
106
107 static void     printing_preview_update_zoom_sensitivity(PreviewData*);
108
109 /* variables */
110 static GtkPrintSettings *settings   = NULL;
111 static GtkPageSetup     *page_setup = NULL;
112
113 /* other static functions */
114 static void     printing_layout_set_text_attributes(PrintData*, GtkPrintContext *);
115 static gboolean printing_is_pango_gdk_color_equal(PangoColor*, GdkColor*); 
116 static gint     printing_text_iter_get_offset_bytes(PrintData *, const GtkTextIter*);
117
118 #define PREVIEW_SCALE 72
119 #define PREVIEW_SHADOW_OFFSET 3
120 #define PREVIEW_ZOOM_FAC 1.41
121 #define PREVIEW_ZOOM_MAX 10.
122 #define PREVIEW_ZOOM_MIN 0.2
123
124 static void free_pixbuf(gpointer key, gpointer value, gpointer data)
125 {
126   PangoAttrShape *attr = (PangoAttrShape *) value;
127   g_object_unref(G_OBJECT(attr->data));
128 }
129
130 gpointer printing_get_renderer_data(PrintData *print_data)
131 {
132         if (!print_data)
133                 return NULL;
134         return print_data->renderer_data;
135 }
136
137 gdouble printing_get_zoom(PrintData *print_data)
138 {
139         if (!print_data)
140                 return 1.0;
141         return print_data->zoom;
142 }
143
144 void printing_set_n_pages(PrintData *print_data, gint n_pages)
145 {
146         if (!print_data)
147                 return;
148         print_data->npages = n_pages;
149 }
150
151 void printing_print_full(GtkWindow *parent, PrintRenderer *renderer, gpointer renderer_data, 
152                          gint sel_start, gint sel_end)
153 {                        
154   GtkPrintOperation *op;
155   GtkPrintOperationResult res;
156   PrintData *print_data;
157
158   op = gtk_print_operation_new();
159
160   print_data = g_new0(PrintData,1);
161
162   print_data->renderer = renderer;
163   print_data->renderer_data = renderer_data;
164   print_data->sel_start = sel_start;
165   print_data->sel_end = sel_end;
166
167   print_data->zoom = 1.;
168
169   print_data->images = g_hash_table_new(g_direct_hash, g_direct_equal);
170   
171   print_data->pango_context = renderer->get_pango_context(renderer_data);
172
173   print_data->to_print = renderer->get_data_to_print(renderer_data, sel_start, sel_end);
174
175   if (settings == NULL) {
176     settings = gtk_print_settings_new();
177     gtk_print_settings_set_use_color(settings, prefs_common.print_use_color);
178     gtk_print_settings_set_collate(settings, prefs_common.print_use_collate);
179     gtk_print_settings_set_reverse(settings, prefs_common.print_use_reverse);
180     gtk_print_settings_set_duplex(settings, prefs_common.print_use_duplex);
181   }
182   if (page_setup == NULL) {
183     page_setup = gtk_page_setup_new();
184     if (prefs_common.print_paper_type && *prefs_common.print_paper_type) {
185       GtkPaperSize *paper = gtk_paper_size_new(prefs_common.print_paper_type);
186       gtk_page_setup_set_paper_size(page_setup, paper);
187       gtk_paper_size_free(paper);
188     }
189     gtk_page_setup_set_orientation(page_setup, prefs_common.print_paper_orientation);
190   }
191   
192   /* Config for printing */
193   gtk_print_operation_set_print_settings(op, settings);
194   gtk_print_operation_set_default_page_setup(op, page_setup);
195
196   /* signals */
197   g_signal_connect(op, "begin_print", G_CALLBACK(renderer->cb_begin_print), print_data);
198   g_signal_connect(op, "draw_page", G_CALLBACK(renderer->cb_draw_page), print_data);
199   g_signal_connect(op, "preview", G_CALLBACK(cb_preview), print_data);
200
201   /* Start printing process */
202   res = gtk_print_operation_run(op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
203                                 parent, NULL);
204
205   if(res == GTK_PRINT_OPERATION_RESULT_ERROR) {
206     GError *error = NULL;
207     gtk_print_operation_get_error(op, &error);
208     debug_print("Error printing message: %s",
209             error ? error->message : "no details");
210   }
211   else if(res == GTK_PRINT_OPERATION_RESULT_APPLY) {
212     /* store settings for next printing session */
213     if(settings != NULL)
214       g_object_unref(settings);
215     settings = g_object_ref(gtk_print_operation_get_print_settings(op));
216     prefs_common.print_use_color = gtk_print_settings_get_use_color(settings);
217     prefs_common.print_use_collate = gtk_print_settings_get_collate(settings);
218     prefs_common.print_use_reverse = gtk_print_settings_get_reverse(settings);
219     prefs_common.print_use_duplex = gtk_print_settings_get_duplex(settings);
220   }
221
222   g_hash_table_foreach(print_data->images, free_pixbuf, NULL);
223   g_hash_table_destroy(print_data->images);
224   if(print_data->to_print)
225     g_free(print_data->to_print);
226   g_list_free(print_data->page_breaks);
227   if(print_data->layout)
228     g_object_unref(print_data->layout);
229
230   g_free(print_data);
231
232   g_object_unref(op);
233   debug_print("printing_print finished\n");
234 }
235
236 static PangoContext *printing_textview_get_pango_context(gpointer data)
237 {
238         return gtk_widget_get_pango_context(GTK_WIDGET(data));
239 }
240
241 static gpointer printing_textview_get_data_to_print(gpointer data, gint sel_start, gint sel_end)
242 {
243   GtkTextView *text_view = GTK_TEXT_VIEW(data);
244   GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view);
245   GtkTextIter start, end;
246   
247   if (sel_start < 0 || sel_end <= sel_start) {
248     gtk_text_buffer_get_start_iter(buffer, &start);
249     gtk_text_buffer_get_end_iter(buffer, &end);
250   } else {
251     gtk_text_buffer_get_iter_at_offset(buffer, &start, sel_start);
252     gtk_text_buffer_get_iter_at_offset(buffer, &end, sel_end);
253   }
254
255   return gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
256 }
257
258 void printing_print(GtkTextView *text_view, GtkWindow *parent, gint sel_start, gint sel_end)
259 {
260         PrintRenderer *textview_renderer = g_new0(PrintRenderer, 1);
261         
262         textview_renderer->get_pango_context = printing_textview_get_pango_context;
263         textview_renderer->get_data_to_print = printing_textview_get_data_to_print;
264         textview_renderer->cb_begin_print    = printing_textview_cb_begin_print;
265         textview_renderer->cb_draw_page      = printing_textview_cb_draw_page;
266
267         printing_print_full(parent, textview_renderer, text_view, sel_start, sel_end);
268         
269         g_free(textview_renderer);
270 }
271
272 void printing_page_setup(GtkWindow *parent)
273 {
274   GtkPageSetup *new_page_setup;
275
276   if(settings == NULL) {
277     settings = gtk_print_settings_new();
278     gtk_print_settings_set_use_color(settings, prefs_common.print_use_color);
279     gtk_print_settings_set_collate(settings, prefs_common.print_use_collate);
280     gtk_print_settings_set_reverse(settings, prefs_common.print_use_reverse);
281     gtk_print_settings_set_duplex(settings, prefs_common.print_use_duplex);
282   }
283   if (page_setup == NULL) {
284     page_setup = gtk_page_setup_new();
285     if (prefs_common.print_paper_type && *prefs_common.print_paper_type) {
286       GtkPaperSize *paper = gtk_paper_size_new(prefs_common.print_paper_type);
287       gtk_page_setup_set_paper_size(page_setup, paper);
288       gtk_paper_size_free(paper);
289     }
290     gtk_page_setup_set_orientation(page_setup, prefs_common.print_paper_orientation);
291   }
292
293   new_page_setup = gtk_print_run_page_setup_dialog(parent,page_setup,settings);
294
295   if(page_setup)
296     g_object_unref(page_setup);
297
298   page_setup = new_page_setup;
299   
300   g_free(prefs_common.print_paper_type);
301   prefs_common.print_paper_type = g_strdup(gtk_paper_size_get_name(
302                 gtk_page_setup_get_paper_size(page_setup)));
303   prefs_common.print_paper_orientation = gtk_page_setup_get_orientation(page_setup);
304 }
305
306 static gboolean cb_preview(GtkPrintOperation        *operation,
307                            GtkPrintOperationPreview *preview,
308                            GtkPrintContext          *context,
309                            GtkWindow                *parent,
310                            gpointer                 data)
311 {
312   PrintData *print_data;
313   cairo_t *cr;
314   PreviewData *preview_data;
315   GtkWidget *vbox;
316   GtkWidget *toolbar;
317   GtkWidget *da;
318   GtkWidget *sw;
319   GtkWidget *page;
320   GtkToolItem *separator;
321   static GdkGeometry geometry;
322   GtkWidget *dialog = NULL;
323   GtkTooltips *toolbar_tips = gtk_tooltips_new();
324   GtkWidget *statusbar = gtk_hbox_new(2, FALSE);
325   debug_print("Creating internal print preview\n");
326
327   print_data = (PrintData*) data;
328
329   preview_data = g_new0(PreviewData,1);
330   preview_data->print_data = print_data;
331   preview_data->op = g_object_ref(operation);
332   preview_data->preview = preview;
333
334   /* Window */
335   dialog = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "print_preview");
336   preview_data->dialog = dialog;
337   if(!geometry.min_height) {
338     geometry.min_width = 600;
339     geometry.min_height = 400;
340   }
341   gtk_window_set_geometry_hints(GTK_WINDOW(dialog), NULL, &geometry,
342                                 GDK_HINT_MIN_SIZE);
343   gtk_widget_set_size_request(dialog, prefs_common.print_previewwin_width,
344                               prefs_common.print_previewwin_height);
345   gtk_window_set_title(GTK_WINDOW(dialog), _("Print preview"));
346
347   /* vbox */
348   vbox = gtk_vbox_new(FALSE, 0);
349   gtk_container_add(GTK_CONTAINER(dialog), vbox);
350   
351   /* toolbar */
352   toolbar = gtk_toolbar_new();
353   gtk_toolbar_set_orientation(GTK_TOOLBAR(toolbar), GTK_ORIENTATION_HORIZONTAL);
354   switch (prefs_common.toolbar_style) {
355   case TOOLBAR_ICON:
356     gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
357     break;
358   case TOOLBAR_TEXT:
359     gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_TEXT);
360     break;
361   case TOOLBAR_BOTH_HORIZ:
362     gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ);
363     break;
364   case TOOLBAR_BOTH:
365   default:        
366     gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH);
367   }
368   gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar), TRUE);
369         
370   gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
371
372 #define TOOLBAR_ITEM(item,text,tooltip,cb,cbdata) {                                                             \
373         item = GTK_WIDGET(gtk_tool_button_new_from_stock(text));                                        \
374         gtk_tool_item_set_homogeneous(GTK_TOOL_ITEM(item), FALSE);                                      \
375         gtk_tool_item_set_is_important(GTK_TOOL_ITEM(item), TRUE);                                      \
376         gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(item), -1);                              \
377         g_signal_connect (G_OBJECT(item), "clicked", G_CALLBACK(cb), cbdata);                           \
378         gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(item), GTK_TOOLTIPS(toolbar_tips),                      \
379                         tooltip, NULL);                                                                 \
380 }
381
382   TOOLBAR_ITEM(preview_data->first, GTK_STOCK_GOTO_FIRST, _("First page"), cb_preview_go_first, preview_data);
383   TOOLBAR_ITEM(preview_data->previous, GTK_STOCK_GO_BACK, _("Previous page"), cb_preview_go_previous, preview_data);
384   
385   page = gtk_label_new("");
386   gtk_widget_set_size_request(page, 100, -1);
387   preview_data->page_nr_label = page;
388   
389   TOOLBAR_ITEM(preview_data->next, GTK_STOCK_GO_FORWARD, _("Next page"), cb_preview_go_next, preview_data);
390   TOOLBAR_ITEM(preview_data->last, GTK_STOCK_GOTO_LAST, _("Last page"), cb_preview_go_last, preview_data);
391   
392   separator = gtk_separator_tool_item_new();
393   gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(separator), -1);
394
395   TOOLBAR_ITEM(preview_data->zoom_100, GTK_STOCK_ZOOM_100, _("Zoom 100%"), cb_preview_zoom_100, preview_data);
396   TOOLBAR_ITEM(preview_data->zoom_fit, GTK_STOCK_ZOOM_FIT, _("Zoom fit"), cb_preview_zoom_fit, preview_data);
397   TOOLBAR_ITEM(preview_data->zoom_in, GTK_STOCK_ZOOM_IN, _("Zoom in"), cb_preview_zoom_in, preview_data);
398   TOOLBAR_ITEM(preview_data->zoom_out, GTK_STOCK_ZOOM_OUT, _("Zoom out"), cb_preview_zoom_out, preview_data);
399
400   separator = gtk_separator_tool_item_new();
401   gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(separator), -1);
402
403   /* tooltip has to be NULL else it triggers an expose_event */
404   TOOLBAR_ITEM(preview_data->close, GTK_STOCK_CLOSE, NULL, cb_preview_btn_close, preview_data);
405
406   gtk_widget_show(statusbar);
407   gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);
408   gtk_box_pack_start(GTK_BOX(statusbar), page, FALSE, FALSE, 0);
409   /* Drawing area */
410   sw = gtk_scrolled_window_new(NULL, NULL);
411   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
412                                  GTK_POLICY_AUTOMATIC,
413                                  GTK_POLICY_AUTOMATIC);
414   gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
415   da = gtk_drawing_area_new();
416   gtk_widget_set_double_buffered(da, FALSE);
417   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw),
418                                         da);
419   gtk_widget_realize(da);
420   preview_data->scrolled_window = sw;
421   preview_data->area = da;
422
423   /* cairo context */
424   cr = gdk_cairo_create(da->window);
425   gtk_print_context_set_cairo_context(context, cr, PREVIEW_SCALE, PREVIEW_SCALE);
426   cairo_destroy(cr);
427
428   /* signals */
429   g_signal_connect(dialog, "key_press_event",
430                    G_CALLBACK(cb_preview_close), preview_data);
431   g_signal_connect(dialog, "size_allocate",
432                    G_CALLBACK(cb_preview_size_allocate), NULL);
433   g_signal_connect(dialog, "destroy",G_CALLBACK(cb_preview_destroy),
434                    preview_data);
435   g_signal_connect(preview, "ready", G_CALLBACK(cb_preview_ready),
436                    preview_data);
437   g_signal_connect(preview, "got-page-size",
438                    G_CALLBACK(cb_preview_got_page_size), preview_data);
439
440   g_signal_connect(operation, "request-page-setup",
441                    G_CALLBACK(cb_preview_request_page_setup), preview_data);
442
443   gtk_widget_show_all(dialog);
444   return TRUE;
445 }
446
447 static void cb_preview_destroy(GtkWindow *window, gpointer data)
448 {
449   PreviewData *preview_data;
450   preview_data = (PreviewData*) data;
451
452   if (preview_data->rendering)
453     return;
454   debug_print("Preview window destroyed\n");
455
456   gtk_print_operation_preview_end_preview(preview_data->preview);
457   g_object_unref(preview_data->op);
458   g_list_free(preview_data->pages_to_print);
459
460   g_free(preview_data);
461 }
462
463 static gboolean cb_preview_close(GtkWidget *widget, GdkEventAny *event,
464                                  gpointer data)
465 {
466   PreviewData *preview_data = (PreviewData *)data;
467   if(event->type == GDK_KEY_PRESS)
468     if(((GdkEventKey *)event)->keyval != GDK_Escape)
469       return FALSE;
470   if (preview_data->rendering)
471     return FALSE; 
472   gtk_widget_destroy(widget);
473   return FALSE;
474 }
475
476 static void cb_preview_size_allocate(GtkWidget *widget,
477                                      GtkAllocation *allocation)
478 {
479   g_return_if_fail(allocation != NULL);
480
481   prefs_common.print_previewwin_width = allocation->width;
482   prefs_common.print_previewwin_height = allocation->height;
483 }
484
485 static void cb_preview_ready(GtkPrintOperationPreview *preview,
486                              GtkPrintContext          *context,
487                              gpointer                  data)
488 {
489   PreviewData *preview_data;
490   gint iPage;
491   preview_data = (PreviewData*) data;
492   debug_print("preview_ready %d\n", preview_data->print_data->npages);
493   
494   for(iPage = 0; iPage < (preview_data->print_data->npages); iPage++) {
495     if(gtk_print_operation_preview_is_selected(preview_data->preview, iPage)) {
496       preview_data->pages_to_print = 
497         g_list_prepend(preview_data->pages_to_print, GINT_TO_POINTER(iPage));
498       debug_print("want to print page %d\n",iPage+1);
499     }
500   }
501
502   preview_data->pages_to_print = g_list_reverse(preview_data->pages_to_print);
503   preview_data->current_page = preview_data->pages_to_print;
504
505   g_signal_connect(preview_data->area, "expose_event",
506                    G_CALLBACK(cb_preview_expose),
507                    preview_data);
508
509   gtk_widget_queue_draw(preview_data->area);
510 }
511
512 static void cb_preview_got_page_size(GtkPrintOperationPreview *preview,
513                                      GtkPrintContext          *context,
514                                      GtkPageSetup             *page_setup,
515                                      gpointer                  data)
516 {
517   PreviewData *preview_data;
518   GtkPageOrientation orientation;
519   GtkPaperSize *paper_size;
520   gint paper_width;
521   gint paper_height;
522
523   preview_data = (PreviewData*) data;
524   debug_print("got_page_size\n");
525   orientation  = gtk_page_setup_get_orientation(page_setup);
526   paper_size   = gtk_page_setup_get_paper_size(page_setup);
527   paper_width  = (gint)(gtk_paper_size_get_width(paper_size, GTK_UNIT_INCH)  
528                         * PREVIEW_SCALE);
529   paper_height = (gint)(gtk_paper_size_get_height(paper_size,  GTK_UNIT_INCH)
530                         * PREVIEW_SCALE);
531
532   preview_data->page_width  = paper_width;
533   preview_data->page_height = paper_height;
534
535   debug_print("w/h %d/%d\n", paper_width, paper_height);
536   gtk_widget_set_size_request(GTK_WIDGET(preview_data->area), 
537                               paper_width, paper_height);
538 }
539
540 static gboolean cb_preview_expose(GtkWidget *widget, GdkEventExpose *event,
541                                   gpointer data)
542 {
543   PreviewData *preview_data = data;
544   GdkGC *gc;
545   GdkColor white;
546   GdkColor black;
547   GdkColor gray;
548
549   debug_print("preview_expose (current %p)\n", preview_data->current_page);
550   gdk_window_clear(preview_data->area->window);
551
552   white.red   = 65535;
553   white.green = 65535;
554   white.blue  = 65535;
555   black.red   = 0;
556   black.green = 0;
557   black.blue  = 0;
558   gray.red   = 32700;
559   gray.green = 32700;
560   gray.blue  = 32700;
561
562   gc = gdk_gc_new(GDK_DRAWABLE(preview_data->area->window));
563
564   /* background */
565   gdk_gc_set_rgb_fg_color(gc, &gray);
566   gdk_draw_rectangle(preview_data->area->window, gc, TRUE, 0, 0,
567                      preview_data->area->allocation.width,
568                      preview_data->area->allocation.height);
569
570   /* shadow */
571   gdk_gc_set_rgb_fg_color(gc, &black);
572   gdk_draw_rectangle(preview_data->area->window, gc, TRUE,
573                      PREVIEW_SHADOW_OFFSET, PREVIEW_SHADOW_OFFSET,
574                      preview_data->page_width+PREVIEW_SHADOW_OFFSET,
575                      preview_data->page_height+PREVIEW_SHADOW_OFFSET);
576
577   /* paper */
578   gdk_gc_set_rgb_fg_color(gc, &white);
579   gdk_draw_rectangle(preview_data->area->window, gc, TRUE, 0, 0,
580                      preview_data->page_width,
581                      preview_data->page_height);
582
583   g_object_unref(gc);
584
585   if(preview_data->current_page) {
586     preview_data->rendering = TRUE;
587     gtk_widget_set_sensitive(preview_data->close, FALSE);
588     int cur = GPOINTER_TO_INT(preview_data->current_page->data);
589     gchar *str;
590     str = g_strdup_printf(_("Page %d"), cur+1);
591     gtk_label_set_text(GTK_LABEL(preview_data->page_nr_label), str);
592     g_free(str);
593     gtk_print_operation_preview_render_page(preview_data->preview,
594                                             GPOINTER_TO_INT
595                                             (preview_data->current_page->data));
596
597     gtk_widget_set_sensitive(preview_data->first, preview_data->current_page->prev != NULL);
598     gtk_widget_set_sensitive(preview_data->previous, preview_data->current_page->prev != NULL);
599     gtk_widget_set_sensitive(preview_data->next, preview_data->current_page->next != NULL);
600     gtk_widget_set_sensitive(preview_data->last, preview_data->current_page->next != NULL);
601     gtk_widget_set_sensitive(preview_data->close, TRUE);
602     preview_data->rendering = FALSE;
603
604   }
605   return TRUE;
606 }
607
608 static void cb_preview_go_first(GtkButton *button, gpointer data)
609 {
610   PreviewData *preview_data = (PreviewData*) data;
611   preview_data->current_page = preview_data->pages_to_print;
612   gtk_widget_queue_draw(preview_data->area);
613 }
614
615 static void cb_preview_go_previous(GtkButton *button, gpointer data)
616 {
617   GList *next;
618   PreviewData *preview_data = (PreviewData*) data;
619   next = g_list_previous(preview_data->current_page);
620   if(next)
621     preview_data->current_page = next;
622   gtk_widget_queue_draw(preview_data->area);
623 }
624
625 static void cb_preview_go_next(GtkButton *button, gpointer data)
626 {
627   GList *next;
628   PreviewData *preview_data = (PreviewData*) data;
629   next = g_list_next(preview_data->current_page);
630   if(next)
631     preview_data->current_page = next;
632   gtk_widget_queue_draw(preview_data->area);
633 }
634
635 static void cb_preview_go_last(GtkButton *button, gpointer data)
636 {
637   PreviewData *preview_data = (PreviewData*) data;
638   preview_data->current_page = g_list_last(preview_data->current_page);
639   gtk_widget_queue_draw(preview_data->area);
640 }
641
642 static void cb_preview_btn_close(GtkButton *button, gpointer data)
643 {
644   PreviewData *preview_data = (PreviewData *)data;
645   if (preview_data->rendering)
646     return; 
647   gtk_widget_destroy(preview_data->dialog);
648 }
649
650 static void cb_preview_zoom_100(GtkButton *button, gpointer data)
651 {
652   PreviewData *preview_data = (PreviewData*) data;
653   if(preview_data->print_data->zoom != 1.) {
654     preview_data->print_data->zoom = 1.;
655     gtk_widget_queue_draw(preview_data->area);
656     printing_preview_update_zoom_sensitivity(preview_data);
657   }
658 }
659
660 static void cb_preview_zoom_fit(GtkButton *button, gpointer data)
661 {
662   PreviewData *preview_data = (PreviewData*) data;
663   gdouble zoom_w;
664   gdouble zoom_h;
665   
666   zoom_w = ((gdouble)preview_data->scrolled_window->allocation.width) /
667     ((gdouble)preview_data->page_width/preview_data->print_data->zoom +
668      PREVIEW_SHADOW_OFFSET);
669   zoom_h = ((gdouble)preview_data->scrolled_window->allocation.height) /
670     ((gdouble)preview_data->page_height/preview_data->print_data->zoom +
671      PREVIEW_SHADOW_OFFSET);
672
673   preview_data->print_data->zoom = MIN(zoom_w,zoom_h) - 0.01;
674
675   if(preview_data->print_data->zoom > PREVIEW_ZOOM_MAX)
676     preview_data->print_data->zoom = PREVIEW_ZOOM_MAX;
677   else if(preview_data->print_data->zoom < PREVIEW_ZOOM_MIN)
678     preview_data->print_data->zoom = PREVIEW_ZOOM_MIN;
679
680   printing_preview_update_zoom_sensitivity(preview_data);
681   gtk_widget_queue_draw(preview_data->area);
682 }
683
684 static void cb_preview_zoom_in(GtkButton *button, gpointer data)
685 {
686   PreviewData *preview_data = (PreviewData*) data;
687   gdouble new_zoom;
688   new_zoom =  preview_data->print_data->zoom * PREVIEW_ZOOM_FAC;
689   if(new_zoom <= PREVIEW_ZOOM_MAX) {
690     preview_data->print_data->zoom = new_zoom;
691     printing_preview_update_zoom_sensitivity(preview_data);
692     gtk_widget_queue_draw(preview_data->area);
693   }
694 }
695
696 static void cb_preview_zoom_out(GtkButton *button, gpointer data)
697 {
698   PreviewData *preview_data = (PreviewData*) data;
699   gdouble new_zoom;
700   new_zoom =  preview_data->print_data->zoom / PREVIEW_ZOOM_FAC;
701   if(new_zoom >= PREVIEW_ZOOM_MIN) {
702     preview_data->print_data->zoom = new_zoom;
703     printing_preview_update_zoom_sensitivity(preview_data);
704     gtk_widget_queue_draw(preview_data->area);
705   }
706 }
707
708 static void cb_preview_request_page_setup(GtkPrintOperation *op,
709                                           GtkPrintContext *context,
710                                           gint page_nr,
711                                           GtkPageSetup *setup,gpointer data)
712 {
713   GtkPaperSize *paper_size;
714   GtkPaperSize *old_size;
715   gdouble width;
716   gdouble height;
717   gdouble top_margin;
718   gdouble bottom_margin;
719   gdouble left_margin;
720   gdouble right_margin;
721
722   PreviewData *preview_data = (PreviewData*) data;
723
724   old_size = gtk_page_setup_get_paper_size(setup);
725   width  = gtk_paper_size_get_width(old_size,GTK_UNIT_INCH);
726   height = gtk_paper_size_get_height(old_size,GTK_UNIT_INCH);
727
728   top_margin    = gtk_page_setup_get_top_margin(setup,GTK_UNIT_INCH);
729   bottom_margin = gtk_page_setup_get_bottom_margin(setup,GTK_UNIT_INCH);
730   left_margin   = gtk_page_setup_get_left_margin(setup,GTK_UNIT_INCH);
731   right_margin  = gtk_page_setup_get_right_margin(setup,GTK_UNIT_INCH);
732
733   paper_size = gtk_paper_size_new_custom("preview paper", "preview_paper",
734                                          width*preview_data->print_data->zoom,
735                                          height*preview_data->print_data->zoom,
736                                          GTK_UNIT_INCH);
737   gtk_page_setup_set_paper_size(setup, paper_size);
738   gtk_paper_size_free(paper_size);
739
740   gtk_page_setup_set_top_margin(setup,top_margin*preview_data->print_data->zoom,
741                                 GTK_UNIT_INCH);
742   gtk_page_setup_set_bottom_margin(setup,bottom_margin*preview_data->print_data->zoom,
743                                    GTK_UNIT_INCH);
744   gtk_page_setup_set_left_margin(setup,left_margin*preview_data->print_data->zoom,
745                                  GTK_UNIT_INCH);
746   gtk_page_setup_set_right_margin(setup,right_margin*preview_data->print_data->zoom,
747                                   GTK_UNIT_INCH);
748 }
749
750 static void printing_textview_cb_begin_print(GtkPrintOperation *op, GtkPrintContext *context,
751                            gpointer user_data)
752 {
753   double width, height;
754   int num_lines;
755   double page_height;
756   GList *page_breaks;
757   PrintData *print_data;
758   PangoFontDescription *desc;
759   int start, ii;
760   PangoLayoutIter *iter;
761   double start_pos;
762   double line_height =0.;
763
764   print_data = (PrintData*) user_data;
765
766   debug_print("Preparing print job...\n");
767         
768   width  = gtk_print_context_get_width(context);
769   height = gtk_print_context_get_height(context);
770
771   if (print_data->layout == NULL)
772     print_data->layout = gtk_print_context_create_pango_layout(context);
773         
774   if(prefs_common.use_different_print_font)
775     desc = pango_font_description_from_string(prefs_common.printfont);
776   else
777     desc = pango_font_description_copy(
778                 pango_context_get_font_description(print_data->pango_context));
779
780   pango_layout_set_font_description(print_data->layout, desc);
781   pango_font_description_free(desc);
782
783   pango_layout_set_width(print_data->layout, width * PANGO_SCALE);
784   pango_layout_set_text(print_data->layout, (char *)print_data->to_print, -1);
785
786   printing_layout_set_text_attributes(print_data, context);
787
788   num_lines = pango_layout_get_line_count(print_data->layout);
789
790   page_breaks = NULL;
791   page_height = 0;
792   start = 0;
793   ii = 0;
794   start_pos = 0.;
795   iter = pango_layout_get_iter(print_data->layout);
796   do {
797     PangoRectangle logical_rect;
798     PangoLayoutLine *line;
799     PangoAttrShape *attr = NULL;
800     int baseline;
801
802     if(ii >= start) {
803       line = pango_layout_iter_get_line(iter);
804
805       pango_layout_iter_get_line_extents(iter, NULL, &logical_rect);
806       baseline = pango_layout_iter_get_baseline(iter);
807
808       if ((attr = g_hash_table_lookup(print_data->images, GINT_TO_POINTER(pango_layout_iter_get_index(iter)))) != NULL) {
809         line_height = (double)gdk_pixbuf_get_height(GDK_PIXBUF(attr->data));
810       } else {
811         line_height = ((double)logical_rect.height) / PANGO_SCALE;
812       }
813     }
814     if((page_height + line_height) > height) {
815       page_breaks = g_list_prepend(page_breaks, GINT_TO_POINTER(ii));
816       page_height = 0;
817     }
818
819     page_height += line_height;
820     ii++;
821   } while(ii < num_lines && pango_layout_iter_next_line(iter));
822   pango_layout_iter_free(iter);
823
824   page_breaks = g_list_reverse(page_breaks);
825   print_data->npages = g_list_length(page_breaks) + 1;  
826   print_data->page_breaks = page_breaks;
827
828   gtk_print_operation_set_n_pages(op, print_data->npages);
829
830   debug_print("Starting print job...\n");
831 }
832
833 static cairo_surface_t *pixbuf_to_surface(GdkPixbuf *pixbuf)
834 {
835           cairo_surface_t *surface;
836           cairo_format_t format;
837           static const cairo_user_data_key_t key;
838           guchar *pixels = g_malloc(
839                                 4*
840                                 gdk_pixbuf_get_width(pixbuf)*
841                                 gdk_pixbuf_get_height(pixbuf));
842           guchar *src_pixels = gdk_pixbuf_get_pixels (pixbuf);
843           gint width = gdk_pixbuf_get_width(pixbuf);
844           gint height = gdk_pixbuf_get_height(pixbuf);
845           gint nchans = gdk_pixbuf_get_n_channels (pixbuf);
846           gint stride = gdk_pixbuf_get_rowstride (pixbuf);
847           gint j;
848
849           if (nchans == 3)
850             format = CAIRO_FORMAT_RGB24;
851           else
852             format = CAIRO_FORMAT_ARGB32;
853           surface = cairo_image_surface_create_for_data (pixels,         
854                                                 format, width, height, 4*width);
855           cairo_surface_set_user_data (surface, &key, 
856                 pixels, (cairo_destroy_func_t)g_free);
857
858           for (j = height; j; j--) {
859                 guchar *p = src_pixels;
860                 guchar *q = pixels;
861
862                 if (nchans == 3) {
863                         guchar *end = p + 3 * width;
864
865                         while (p < end) {
866 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
867                                 q[0] = p[2];
868                                 q[1] = p[1];
869                                 q[2] = p[0];
870 #else
871                                 q[1] = p[0];
872                                 q[2] = p[1];
873                                 q[3] = p[2];
874 #endif
875                                 p += 3;
876                                 q += 4;
877                         }
878                 } else {
879                         guchar *end = p + 4 * width;
880                         guint t1,t2,t3;
881
882 #define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
883
884                         while (p < end) {
885 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
886                                 MULT(q[0], p[2], p[3], t1);
887                                 MULT(q[1], p[1], p[3], t2);
888                                 MULT(q[2], p[0], p[3], t3);
889                                 q[3] = p[3];
890 #else
891                                 q[0] = p[3];
892                                 MULT(q[1], p[0], p[3], t1);
893                                 MULT(q[2], p[1], p[3], t2);
894                                 MULT(q[3], p[2], p[3], t3);
895 #endif
896
897                                 p += 4;
898                                 q += 4;
899                         }
900
901 #undef MULT
902                 }
903
904                 src_pixels += stride;
905                 pixels += 4 * width;
906         }
907                 
908         return surface;
909 }
910
911 static void printing_textview_cb_draw_page(GtkPrintOperation *op, GtkPrintContext *context,
912                          int page_nr, gpointer user_data)
913 {
914   cairo_t *cr;
915   PrintData *print_data;
916   int start, end, ii;
917   GList *pagebreak;
918   PangoLayoutIter *iter;
919   double start_pos;
920   gboolean notlast = TRUE;
921
922   print_data = (PrintData*) user_data;
923
924   if (page_nr == 0) {
925     start = 0;
926   } else {
927     pagebreak = g_list_nth(print_data->page_breaks, page_nr - 1);
928     start = GPOINTER_TO_INT(pagebreak->data);
929   }
930
931   pagebreak = g_list_nth(print_data->page_breaks, page_nr);
932   if(pagebreak == NULL)
933     end = pango_layout_get_line_count(print_data->layout);
934   else
935     end = GPOINTER_TO_INT(pagebreak->data);
936
937   cr = gtk_print_context_get_cairo_context(context);
938   cairo_scale(cr, print_data->zoom, print_data->zoom);
939   cairo_set_source_rgb(cr, 0., 0., 0.);
940
941   ii = 0;
942   start_pos = 0.;
943   iter = pango_layout_get_iter(print_data->layout);
944   do {
945     PangoRectangle logical_rect;
946     PangoLayoutLine *line;
947     PangoAttrShape *attr = NULL;
948     int baseline;
949
950     if(ii >= start) {
951       line = pango_layout_iter_get_line(iter);
952
953       pango_layout_iter_get_line_extents(iter, NULL, &logical_rect);
954       baseline = pango_layout_iter_get_baseline(iter);
955
956       if(ii == start)
957         start_pos = ((double)logical_rect.y) / PANGO_SCALE;
958       
959       cairo_move_to(cr,
960                     ((double)logical_rect.x) / PANGO_SCALE,
961                     ((double)baseline) / PANGO_SCALE - start_pos);
962                     
963       if ((attr = g_hash_table_lookup(print_data->images, GINT_TO_POINTER(pango_layout_iter_get_index(iter)))) != NULL) {
964           cairo_surface_t *surface;
965
966           surface = pixbuf_to_surface(GDK_PIXBUF(attr->data));
967           cairo_set_source_surface (cr, surface, 
968                 ((double)logical_rect.x) / PANGO_SCALE, 
969                 ((double)baseline) / PANGO_SCALE - start_pos);
970           cairo_paint (cr);
971           cairo_surface_destroy (surface);
972       } else {
973         pango_cairo_show_layout_line(cr, line);
974       }
975     }
976     ii++;
977   } while(ii < end && (notlast = pango_layout_iter_next_line(iter)));
978   pango_layout_iter_free(iter);
979   debug_print("Sent page %d to printer\n", page_nr+1);
980 }
981
982 static void printing_layout_set_text_attributes(PrintData *print_data, GtkPrintContext *context)
983 {
984   GtkTextIter iter;
985   PangoAttrList *attr_list;
986   PangoAttribute *attr;
987   GSList *open_attrs, *attr_walk;
988   GtkTextView *text_view = GTK_TEXT_VIEW(print_data->renderer_data);
989   GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view);
990
991   attr_list = pango_attr_list_new();
992   if (print_data->sel_start < 0 || print_data->sel_end <= print_data->sel_start) {
993     gtk_text_buffer_get_start_iter(buffer, &iter);
994   } else {
995     gtk_text_buffer_get_iter_at_offset(buffer, &iter, print_data->sel_start);
996   }
997
998   open_attrs = NULL;
999   do {
1000     gboolean fg_set, bg_set, under_set, strike_set;
1001     GSList *tags, *tag_walk;
1002     GtkTextTag *tag;
1003     GdkColor *color;
1004     PangoUnderline underline;
1005     gboolean strikethrough;
1006     GdkPixbuf *image;
1007
1008     if (prefs_common.print_imgs && (image = gtk_text_iter_get_pixbuf(&iter)) != NULL) {
1009       PangoRectangle rect = {0, 0, 0, 0};
1010       gint startpos = printing_text_iter_get_offset_bytes(print_data, &iter);
1011       gint h = gdk_pixbuf_get_height(image);
1012       gint w = gdk_pixbuf_get_width(image);
1013       gint a_h = gtk_print_context_get_height(context);
1014       gint a_w = gtk_print_context_get_width(context);
1015       gint r_h, r_w;
1016       GdkPixbuf *scaled = NULL;
1017       image_viewer_get_resized_size(w, h, a_w, a_h, &r_w, &r_h);
1018       rect.x = 0;
1019       rect.y = 0;
1020       rect.width = r_w * PANGO_SCALE;
1021       rect.height = r_h * PANGO_SCALE;
1022       
1023       scaled = gdk_pixbuf_scale_simple(image, r_w, r_h, GDK_INTERP_BILINEAR);
1024       attr = pango_attr_shape_new_with_data (&rect, &rect,
1025                scaled, NULL, NULL);
1026       attr->start_index = startpos;
1027       attr->end_index = startpos+1;
1028       pango_attr_list_insert(attr_list, attr);
1029       g_hash_table_insert(print_data->images, GINT_TO_POINTER(startpos), attr);
1030       print_data->img_cnt++;
1031     }
1032
1033     if(gtk_text_iter_ends_tag(&iter, NULL)) {
1034       PangoAttrColor *attr_color;
1035       PangoAttrInt   *attr_int;
1036
1037       tags = gtk_text_iter_get_toggled_tags(&iter, FALSE);
1038       for(tag_walk = tags; tag_walk != NULL; tag_walk = tag_walk->next) {
1039         gboolean found;
1040
1041         tag = GTK_TEXT_TAG(tag_walk->data);
1042         g_object_get(G_OBJECT(tag),
1043                      "background-set", &bg_set,
1044                      "foreground-set", &fg_set,
1045                      "underline-set",&under_set,
1046                      "strikethrough-set", &strike_set,
1047                      NULL);
1048
1049         if(fg_set) {
1050           found = FALSE;
1051           for(attr_walk = open_attrs; attr_walk != NULL; attr_walk = attr_walk->next) {
1052             attr = (PangoAttribute*)attr_walk->data;
1053             if(attr->klass->type == PANGO_ATTR_FOREGROUND) {
1054               attr_color = (PangoAttrColor*) attr;
1055               g_object_get(G_OBJECT(tag), "foreground_gdk", &color, NULL);
1056               if(printing_is_pango_gdk_color_equal(&(attr_color->color), color)) {
1057                 attr->end_index = printing_text_iter_get_offset_bytes(print_data, &iter);
1058                 pango_attr_list_insert(attr_list, attr);
1059                 found = TRUE;
1060                 open_attrs = g_slist_delete_link(open_attrs, attr_walk);
1061                 break;
1062               }
1063               if (color)
1064                 gdk_color_free(color);
1065             }
1066           }
1067           if(!found)
1068             debug_print("Error generating attribute list.\n");
1069         }
1070
1071         if(bg_set) {
1072           found = FALSE;
1073           for(attr_walk = open_attrs; attr_walk != NULL; attr_walk = attr_walk->next) {
1074             attr = (PangoAttribute*)attr_walk->data;
1075             if(attr->klass->type == PANGO_ATTR_BACKGROUND) {
1076               attr_color = (PangoAttrColor*) attr;
1077               g_object_get(G_OBJECT(tag), "background-gdk", &color, NULL);
1078               if(printing_is_pango_gdk_color_equal(&(attr_color->color), color)) {
1079                 attr->end_index = printing_text_iter_get_offset_bytes(print_data, &iter);
1080                 pango_attr_list_insert(attr_list, attr);
1081                 found = TRUE;
1082                 open_attrs = g_slist_delete_link(open_attrs, attr_walk);
1083                 break;
1084               }
1085               if (color)
1086                 gdk_color_free(color);
1087             }
1088           }
1089           if(!found)
1090             debug_print("Error generating attribute list.\n");
1091         }
1092
1093         if(under_set) {
1094           found = FALSE;
1095           for(attr_walk = open_attrs; attr_walk != NULL; attr_walk = attr_walk->next) {
1096             attr = (PangoAttribute*)attr_walk->data;
1097             if(attr->klass->type == PANGO_ATTR_UNDERLINE) {
1098               attr_int = (PangoAttrInt*)attr;
1099               g_object_get(G_OBJECT(tag), "underline", &underline, NULL);
1100               if(attr_int->value == underline) {
1101                 attr->end_index = printing_text_iter_get_offset_bytes(print_data, &iter);
1102                 pango_attr_list_insert(attr_list, attr);
1103                 found = TRUE;
1104                 open_attrs = g_slist_delete_link(open_attrs, attr_walk);
1105                 break;
1106               }
1107             }
1108           }
1109           if(!found)
1110             debug_print("Error generating attribute list.\n");
1111         }
1112
1113         if(strike_set) {
1114           found = FALSE;
1115           for(attr_walk = open_attrs; attr_walk != NULL; attr_walk = attr_walk->next) {
1116             attr = (PangoAttribute*)attr_walk->data;
1117             if(attr->klass->type == PANGO_ATTR_STRIKETHROUGH) {
1118               attr_int = (PangoAttrInt*)attr;
1119               g_object_get(G_OBJECT(tag), "strikethrough", &strikethrough, NULL);
1120               if(attr_int->value == strikethrough) {
1121                 attr->end_index = printing_text_iter_get_offset_bytes(print_data, &iter);
1122                 pango_attr_list_insert(attr_list, attr);
1123                 found = TRUE;
1124                 open_attrs = g_slist_delete_link(open_attrs, attr_walk);
1125                 break;
1126               }
1127             }
1128           }
1129           if(!found)
1130             debug_print("Error generating attribute list.\n");
1131         }
1132
1133       }
1134       g_slist_free(tags);
1135     }
1136
1137     if(gtk_text_iter_begins_tag(&iter, NULL)) {
1138       tags = gtk_text_iter_get_toggled_tags(&iter, TRUE);
1139       for(tag_walk = tags; tag_walk != NULL; tag_walk = tag_walk->next) {
1140         tag = GTK_TEXT_TAG(tag_walk->data);
1141         g_object_get(G_OBJECT(tag),
1142                      "background-set", &bg_set,
1143                      "foreground-set", &fg_set,
1144                      "underline-set", &under_set,
1145                      "strikethrough-set", &strike_set,
1146                      NULL);
1147         if(fg_set) {
1148           g_object_get(G_OBJECT(tag), "foreground-gdk", &color, NULL);
1149           attr = pango_attr_foreground_new(color->red,color->green,color->blue);
1150           attr->start_index = printing_text_iter_get_offset_bytes(print_data, &iter);
1151           open_attrs = g_slist_prepend(open_attrs, attr);
1152         }
1153         if(bg_set) {
1154           g_object_get(G_OBJECT(tag), "background-gdk", &color, NULL);
1155           attr = pango_attr_background_new(color->red,color->green,color->blue);
1156           attr->start_index = printing_text_iter_get_offset_bytes(print_data, &iter);
1157           open_attrs = g_slist_prepend(open_attrs, attr);
1158         }
1159         if(under_set) {
1160           g_object_get(G_OBJECT(tag), "underline", &underline, NULL);
1161           attr = pango_attr_underline_new(underline);
1162           attr->start_index = printing_text_iter_get_offset_bytes(print_data, &iter);
1163           open_attrs = g_slist_prepend(open_attrs, attr);         
1164         }
1165         if(strike_set) {
1166           g_object_get(G_OBJECT(tag), "strikethrough", &strikethrough, NULL);
1167           attr = pango_attr_strikethrough_new(strikethrough);
1168           attr->start_index = printing_text_iter_get_offset_bytes(print_data, &iter);
1169           open_attrs = g_slist_prepend(open_attrs, attr);         
1170         }
1171       }
1172       g_slist_free(tags);
1173     }
1174     
1175   } while(!gtk_text_iter_is_end(&iter) && gtk_text_iter_forward_to_tag_toggle(&iter, NULL));
1176           
1177   /* close all open attributes */
1178   for(attr_walk = open_attrs; attr_walk != NULL; attr_walk = attr_walk->next) {
1179     attr = (PangoAttribute*) attr_walk->data;
1180     attr->end_index = printing_text_iter_get_offset_bytes(print_data, &iter);
1181     pango_attr_list_insert(attr_list, attr);
1182   }
1183   g_slist_free(open_attrs);
1184
1185   pango_layout_set_attributes(print_data->layout, attr_list);
1186   pango_attr_list_unref(attr_list);
1187 }
1188
1189 static gboolean printing_is_pango_gdk_color_equal(PangoColor *p, GdkColor *g)
1190 {
1191   return ((p->red == g->red) && (p->green == g->green) && (p->blue == g->blue));
1192 }
1193
1194 /* Pango has it's attribute in bytes, but GtkTextIter gets only an offset
1195  * in characters, so here we're returning an offset in bytes. 
1196  */
1197 static gint printing_text_iter_get_offset_bytes(PrintData *print_data, const GtkTextIter *iter)
1198 {
1199   gint off_chars;
1200   gint off_bytes;
1201   gchar *text;
1202   GtkTextIter start;
1203
1204   off_chars = gtk_text_iter_get_offset(iter);
1205   if (print_data->sel_start < 0 || print_data->sel_end <= print_data->sel_start) {
1206     gtk_text_buffer_get_start_iter(gtk_text_iter_get_buffer(iter), &start);
1207   } else {
1208     gtk_text_buffer_get_iter_at_offset(gtk_text_iter_get_buffer(iter), &start, print_data->sel_start);
1209   }
1210   text = gtk_text_iter_get_text(&start, iter);
1211   off_bytes = strlen(text);
1212   g_free(text);
1213   return off_bytes;
1214 }
1215
1216 static void printing_preview_update_zoom_sensitivity(PreviewData *preview_data)
1217 {
1218   if((preview_data->print_data->zoom * PREVIEW_ZOOM_FAC) > PREVIEW_ZOOM_MAX)
1219     gtk_widget_set_sensitive(preview_data->zoom_in, FALSE);
1220   else
1221     gtk_widget_set_sensitive(preview_data->zoom_in, TRUE);
1222
1223   if((preview_data->print_data->zoom / PREVIEW_ZOOM_FAC) < PREVIEW_ZOOM_MIN)
1224     gtk_widget_set_sensitive(preview_data->zoom_out, FALSE);
1225   else
1226     gtk_widget_set_sensitive(preview_data->zoom_out, TRUE);
1227 }
1228
1229 #endif /* GTK+ >= 2.10.0 */