2007-09-18 [colin] 3.0.1cvs7
[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  * 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 "prefs_common.h"
31
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <gtk/gtk.h>
35 #include <pango/pango.h>
36 #include <string.h>
37
38 typedef struct {
39   PangoLayout *layout;
40   PangoContext *pango_context;
41   char *text;
42   GList *page_breaks;
43   GtkTextBuffer *buffer;
44   gint sel_start;
45   gint sel_end;
46   GHashTable *images;
47   gint img_cnt;
48   gboolean print_started;
49   gchar *old_print_preview;
50 } PrintData;
51
52
53 /* callbacks */
54 static void cb_begin_print(GtkPrintOperation*, GtkPrintContext*, gpointer);
55 static void cb_draw_page(GtkPrintOperation*, GtkPrintContext*, gint, gpointer);
56
57 /* variables */
58 static GtkPrintSettings *settings   = NULL;
59 static GtkPageSetup     *page_setup = NULL;
60
61 /* other static functions */
62 static void     printing_layout_set_text_attributes(PrintData*, GtkPrintContext *);
63 static gboolean printing_is_pango_gdk_color_equal(PangoColor*, GdkColor*); 
64 static gint     printing_text_iter_get_offset_bytes(PrintData *, const GtkTextIter*);
65
66 static gboolean claws_draw_page(GtkPrintOperation *op, GtkPrintContext *context, gint page_nr, gpointer user_data);
67
68 #define PREVIEW_SCALE 72
69 static void preview_destroy (GtkWindow *window, GtkPrintOperationPreview *preview)
70 {
71   gtk_print_operation_preview_end_preview (preview);
72 }
73
74 static gboolean preview_close(GtkWidget *widget, GdkEventAny *event,
75                                  gpointer data)
76 {
77         if (event->type == GDK_KEY_PRESS)
78                 if (((GdkEventKey *)event)->keyval != GDK_Escape)
79                         return FALSE;
80
81         gtk_widget_destroy(widget);
82         return FALSE;
83 }
84
85 static gboolean cb_preview (GtkPrintOperation        *operation,
86                                      GtkPrintOperationPreview *preview,
87                                      GtkPrintContext          *context,
88                                      GtkWindow                *parent,
89                                      PrintData                *print_data)
90 {
91   GtkPageSetup    *page_setup = gtk_print_context_get_page_setup (context);
92   GtkPaperSize    *paper_size;
93   gdouble          paper_width;
94   gdouble          paper_height;
95   gdouble          top_margin;
96   gdouble          bottom_margin;
97   gdouble          left_margin;
98   gdouble          right_margin;
99   gint             preview_width;
100   gint             preview_height;
101   gint num_pages = 0, i = 0;
102   cairo_t         *cr;
103   cairo_surface_t *surface;
104   GtkPageOrientation orientation;
105   cairo_status_t   status;
106   gchar           *fname;
107   GtkWidget *dialog = NULL;
108   GtkWidget *image, *notebook;
109   GSList *pages = NULL, *cur;
110
111   paper_size      = gtk_page_setup_get_paper_size    (page_setup);
112   paper_width     = gtk_paper_size_get_width         (paper_size, GTK_UNIT_INCH);
113   paper_height    = gtk_paper_size_get_height        (paper_size,  GTK_UNIT_INCH);
114   top_margin      = gtk_page_setup_get_top_margin    (page_setup, GTK_UNIT_INCH);
115   bottom_margin   = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_INCH);
116   left_margin     = gtk_page_setup_get_left_margin   (page_setup, GTK_UNIT_INCH);
117   right_margin    = gtk_page_setup_get_right_margin  (page_setup, GTK_UNIT_INCH);
118
119   /* the print context does not have the page orientation, it is transformed */
120   orientation     = gtk_page_setup_get_orientation (page_setup);
121
122   if (orientation == GTK_PAGE_ORIENTATION_PORTRAIT ||
123       orientation == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT)
124     {
125       preview_width  = PREVIEW_SCALE * paper_width;
126       preview_height = PREVIEW_SCALE * paper_height;
127     }
128   else
129     {
130       preview_width  = PREVIEW_SCALE * paper_height;
131       preview_height = PREVIEW_SCALE * paper_width;
132     }
133
134
135   num_pages = 1;  
136   for (i = 0; i < num_pages; i++) {
137     surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
138                                               preview_width, preview_height);
139
140     if (CAIRO_STATUS_SUCCESS != cairo_surface_status (surface)) {
141       g_message ("Unable to create preview (not enough memory?)");
142       return TRUE;
143     }
144
145     cr = cairo_create (surface);
146     gtk_print_context_set_cairo_context (context, cr, PREVIEW_SCALE, PREVIEW_SCALE);
147
148     /* fill page with white */
149     cairo_set_source_rgb (cr, 1, 1, 1);
150     cairo_new_path (cr);
151     cairo_rectangle (cr, 0, 0, preview_width, preview_height);
152     cairo_fill (cr);
153
154     cairo_translate (cr, left_margin * PREVIEW_SCALE, right_margin * PREVIEW_SCALE);
155
156     claws_draw_page (operation, context, i, print_data);
157     num_pages = g_list_length(print_data->page_breaks) + 1;
158
159     fname = get_tmp_file();
160     status = cairo_surface_write_to_png (surface, fname);
161     cairo_destroy (cr);
162     cairo_surface_destroy (surface);
163     if (status == CAIRO_STATUS_SUCCESS) {
164         image = gtk_image_new_from_file (fname);
165         g_unlink (fname);
166         g_free (fname);
167         pages = g_slist_prepend(pages, image);
168         debug_print("added one page\n");
169     }
170   }
171   pages = g_slist_reverse(pages);
172   
173   dialog = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "print_preview");
174   gtk_window_set_title(GTK_WINDOW(dialog), _("Print preview"));
175   notebook = gtk_notebook_new();
176   gtk_container_add(GTK_CONTAINER(dialog), notebook);
177   i = 0;
178   for (cur = pages; cur; cur = cur->next) {
179     image = (GtkImage *)cur->data;
180     if (gtk_print_operation_preview_is_selected(preview, i)) {
181       debug_print("page %d sel\n", i);
182       gtk_notebook_append_page(GTK_NOTEBOOK(notebook), image, NULL);
183     }
184     i++;
185   }
186   g_slist_free(pages);
187   
188   gtk_widget_show_all(dialog);
189   
190   g_signal_connect (dialog, "destroy",
191                     G_CALLBACK (preview_destroy), preview);
192   g_signal_connect (dialog, "key_press_event",
193                     G_CALLBACK (preview_close), preview);
194   
195   return TRUE;
196 }
197
198 void printing_print(GtkTextView *text_view, GtkWindow *parent, gint sel_start, gint sel_end)
199 {
200   GtkPrintOperation *op;
201   GtkPrintOperationResult res;
202   PrintData *print_data;
203   GtkTextIter start, end;
204   GtkTextBuffer *buffer;
205
206   op = gtk_print_operation_new();
207
208   print_data = g_new0(PrintData,1);
209
210   print_data->images = g_hash_table_new(g_direct_hash, g_direct_equal);
211   print_data->pango_context=gtk_widget_get_pango_context(GTK_WIDGET(text_view));
212
213   /* get text */
214   buffer = gtk_text_view_get_buffer(text_view);
215   print_data->buffer = buffer;
216   print_data->sel_start = sel_start;
217   print_data->sel_end = sel_end;
218   if (print_data->sel_start < 0 || print_data->sel_end <= print_data->sel_start) {
219     gtk_text_buffer_get_start_iter(buffer, &start);
220     gtk_text_buffer_get_end_iter(buffer, &end);
221   } else {
222     gtk_text_buffer_get_iter_at_offset(buffer, &start, print_data->sel_start);
223     gtk_text_buffer_get_iter_at_offset(buffer, &end, print_data->sel_end);
224   }
225
226   print_data->text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
227
228   if (settings == NULL) {
229     settings = gtk_print_settings_new();
230     gtk_print_settings_set_use_color(settings, prefs_common.print_use_color);
231     gtk_print_settings_set_collate(settings, prefs_common.print_use_collate);
232     gtk_print_settings_set_reverse(settings, prefs_common.print_use_reverse);
233     gtk_print_settings_set_duplex(settings, prefs_common.print_use_duplex);
234   }
235   if (page_setup == NULL) {
236     GtkPaperSize *paper = gtk_paper_size_new(prefs_common.print_paper_type);
237     page_setup = gtk_page_setup_new();
238     gtk_page_setup_set_paper_size(page_setup, paper);
239     gtk_paper_size_free(paper);
240     gtk_page_setup_set_orientation(page_setup, prefs_common.print_paper_orientation);
241   }
242   
243   /* Config for printing */
244   gtk_print_operation_set_print_settings(op, settings);
245   gtk_print_operation_set_default_page_setup(op, page_setup);
246
247   /* signals */
248   g_signal_connect(op, "begin_print", G_CALLBACK(cb_begin_print), print_data);
249   g_signal_connect(op, "draw_page", G_CALLBACK(cb_draw_page), print_data);
250   g_signal_connect(op, "preview", G_CALLBACK(cb_preview), print_data);
251
252   /* Start printing process */
253   res = gtk_print_operation_run(op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
254                                 parent, NULL);
255
256   if(res == GTK_PRINT_OPERATION_RESULT_ERROR) {
257     GError *error = NULL;
258     gtk_print_operation_get_error(op, &error);
259     debug_print("Error printing message: %s",
260             error ? error->message : "no details");
261   }
262   else if(res == GTK_PRINT_OPERATION_RESULT_APPLY) {
263     /* store settings for next printing session */
264     if(settings != NULL)
265       g_object_unref(settings);
266     settings = g_object_ref(gtk_print_operation_get_print_settings(op));
267     prefs_common.print_use_color = gtk_print_settings_get_use_color(settings);
268     prefs_common.print_use_collate = gtk_print_settings_get_collate(settings);
269     prefs_common.print_use_reverse = gtk_print_settings_get_reverse(settings);
270     prefs_common.print_use_duplex = gtk_print_settings_get_duplex(settings);
271   }
272
273   g_hash_table_destroy(print_data->images);
274   if(print_data->text)
275     g_free(print_data->text);
276   g_list_free(print_data->page_breaks);
277   if(print_data->layout)
278     g_object_unref(print_data->layout);
279
280   if (print_data->old_print_preview) {
281     g_object_set(gtk_settings_get_default(),
282                   "gtk-print-preview-command", print_data->old_print_preview, NULL);
283     g_free(print_data->old_print_preview);
284     print_data->old_print_preview = NULL;
285   }
286
287   g_free(print_data);
288
289   g_object_unref(op);
290   debug_print("printing_print finished\n");
291 }
292
293 void printing_page_setup(GtkWindow *parent)
294 {
295   GtkPageSetup *new_page_setup;
296
297   if(settings == NULL) {
298     settings = gtk_print_settings_new();
299     gtk_print_settings_set_use_color(settings, prefs_common.print_use_color);
300     gtk_print_settings_set_collate(settings, prefs_common.print_use_collate);
301     gtk_print_settings_set_reverse(settings, prefs_common.print_use_reverse);
302     gtk_print_settings_set_duplex(settings, prefs_common.print_use_duplex);
303   }
304   if (page_setup == NULL) {
305     GtkPaperSize *paper = gtk_paper_size_new(prefs_common.print_paper_type);
306     page_setup = gtk_page_setup_new();
307     gtk_page_setup_set_paper_size(page_setup, paper);
308     gtk_paper_size_free(paper);
309     gtk_page_setup_set_orientation(page_setup, prefs_common.print_paper_orientation);
310   }
311
312   new_page_setup = gtk_print_run_page_setup_dialog(parent,page_setup,settings);
313
314   if(page_setup)
315     g_object_unref(page_setup);
316
317   page_setup = new_page_setup;
318   
319   g_free(prefs_common.print_paper_type);
320   prefs_common.print_paper_type = g_strdup(gtk_paper_size_get_name(
321                 gtk_page_setup_get_paper_size(page_setup)));
322   prefs_common.print_paper_orientation = gtk_page_setup_get_orientation(page_setup);
323 }
324
325 static void cb_begin_print(GtkPrintOperation *op, GtkPrintContext *context,
326                            gpointer user_data)
327 {
328   double width, height;
329   int num_lines;
330   double page_height;
331   GList *page_breaks;
332   PrintData *print_data;
333   PangoFontDescription *desc;
334   int start, ii;
335   PangoLayoutIter *iter;
336   double start_pos;
337   double line_height =0.;
338
339   print_data = (PrintData*) user_data;
340
341   if (print_data->print_started)
342     return;
343   debug_print("Preparing print job...\n");
344         
345   width  = gtk_print_context_get_width(context);
346   height = gtk_print_context_get_height(context);
347
348   if (print_data->layout == NULL)
349     print_data->layout = gtk_print_context_create_pango_layout(context);
350         
351   if(prefs_common.use_different_print_font)
352     desc = pango_font_description_from_string(prefs_common.printfont);
353   else
354     desc = pango_font_description_copy(
355                 pango_context_get_font_description(print_data->pango_context));
356
357   pango_layout_set_font_description(print_data->layout, desc);
358   pango_font_description_free(desc);
359
360   pango_layout_set_width(print_data->layout, width * PANGO_SCALE);
361   pango_layout_set_text(print_data->layout, print_data->text, -1);
362
363   printing_layout_set_text_attributes(print_data, context);
364
365   num_lines = pango_layout_get_line_count(print_data->layout);
366
367   page_breaks = NULL;
368   page_height = 0;
369   start = 0;
370   ii = 0;
371   start_pos = 0.;
372   iter = pango_layout_get_iter(print_data->layout);
373   do {
374     PangoRectangle logical_rect;
375     PangoLayoutLine *line;
376     PangoAttrShape *attr = NULL;
377     int baseline;
378
379     if(ii >= start) {
380       line = pango_layout_iter_get_line(iter);
381
382       pango_layout_iter_get_line_extents(iter, NULL, &logical_rect);
383       baseline = pango_layout_iter_get_baseline(iter);
384
385       if ((attr = g_hash_table_lookup(print_data->images, GINT_TO_POINTER(pango_layout_iter_get_index(iter)))) != NULL) {
386         line_height = (double)gdk_pixbuf_get_height(GDK_PIXBUF(attr->data));
387       } else {
388         line_height = ((double)logical_rect.height) / PANGO_SCALE;
389       }
390     }
391     if((page_height + line_height) > height) {
392       page_breaks = g_list_prepend(page_breaks, GINT_TO_POINTER(ii));
393       page_height = 0;
394     }
395
396     page_height += line_height;
397     ii++;
398   } while(ii < num_lines && pango_layout_iter_next_line(iter));
399   pango_layout_iter_free(iter);
400
401   page_breaks = g_list_reverse(page_breaks);
402   gtk_print_operation_set_n_pages(op, g_list_length(page_breaks) + 1);
403         
404   print_data->page_breaks = page_breaks;
405
406   debug_print("Starting print job...\n");
407   print_data->print_started = TRUE;
408 }
409
410 static cairo_surface_t *pixbuf_to_surface(GdkPixbuf *pixbuf)
411 {
412           cairo_surface_t *surface;
413           cairo_format_t format;
414           static const cairo_user_data_key_t key;
415           guchar *pixels = g_malloc(
416                                 4*
417                                 gdk_pixbuf_get_width(pixbuf)*
418                                 gdk_pixbuf_get_height(pixbuf));
419           guchar *src_pixels = gdk_pixbuf_get_pixels (pixbuf);
420           gint width = gdk_pixbuf_get_width(pixbuf);
421           gint height = gdk_pixbuf_get_height(pixbuf);
422           gint nchans = gdk_pixbuf_get_n_channels (pixbuf);
423           gint stride = gdk_pixbuf_get_rowstride (pixbuf);
424           gint j;
425
426           if (nchans == 3)
427             format = CAIRO_FORMAT_RGB24;
428           else
429             format = CAIRO_FORMAT_ARGB32;
430           surface = cairo_image_surface_create_for_data (pixels,         
431                                                 format, width, height, 4*width);
432           cairo_surface_set_user_data (surface, &key, 
433                 pixels, (cairo_destroy_func_t)g_free);
434
435           for (j = height; j; j--) {
436                 guchar *p = src_pixels;
437                 guchar *q = pixels;
438
439                 if (nchans == 3) {
440                         guchar *end = p + 3 * width;
441
442                         while (p < end) {
443 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
444                                 q[0] = p[2];
445                                 q[1] = p[1];
446                                 q[2] = p[0];
447 #else
448                                 q[1] = p[0];
449                                 q[2] = p[1];
450                                 q[3] = p[2];
451 #endif
452                                 p += 3;
453                                 q += 4;
454                         }
455                 } else {
456                         guchar *end = p + 4 * width;
457                         guint t1,t2,t3;
458
459 #define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
460
461                         while (p < end) {
462 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
463                                 MULT(q[0], p[2], p[3], t1);
464                                 MULT(q[1], p[1], p[3], t2);
465                                 MULT(q[2], p[0], p[3], t3);
466                                 q[3] = p[3];
467 #else
468                                 q[0] = p[3];
469                                 MULT(q[1], p[0], p[3], t1);
470                                 MULT(q[2], p[1], p[3], t2);
471                                 MULT(q[3], p[2], p[3], t3);
472 #endif
473
474                                 p += 4;
475                                 q += 4;
476                         }
477
478 #undef MULT
479                 }
480
481                 src_pixels += stride;
482                 pixels += 4 * width;
483         }
484                 
485         return surface;
486 }
487
488 static gboolean claws_draw_page(GtkPrintOperation *op, GtkPrintContext *context, gint page_nr, gpointer user_data)
489 {
490   cairo_t *cr;
491   PrintData *print_data;
492   int start, end, ii;
493   GList *pagebreak;
494   PangoLayoutIter *iter;
495   double start_pos;
496   gboolean notlast = TRUE;
497   print_data = (PrintData*) user_data;
498
499   if (print_data->print_started == FALSE)
500     cb_begin_print(op, context, print_data);
501   if (page_nr == 0) {
502     start = 0;
503   } else {
504     pagebreak = g_list_nth(print_data->page_breaks, page_nr - 1);
505     start = GPOINTER_TO_INT(pagebreak->data);
506   }
507
508   pagebreak = g_list_nth(print_data->page_breaks, page_nr);
509   if(pagebreak == NULL)
510     end = pango_layout_get_line_count(print_data->layout);
511   else
512     end = GPOINTER_TO_INT(pagebreak->data);
513
514   cr = gtk_print_context_get_cairo_context(context);
515   cairo_set_source_rgb(cr, 0., 0., 0.);
516
517   ii = 0;
518   start_pos = 0.;
519   iter = pango_layout_get_iter(print_data->layout);
520   do {
521     PangoRectangle logical_rect;
522     PangoLayoutLine *line;
523     PangoAttrShape *attr = NULL;
524     int baseline;
525
526     if(ii >= start) {
527       line = pango_layout_iter_get_line(iter);
528
529       pango_layout_iter_get_line_extents(iter, NULL, &logical_rect);
530       baseline = pango_layout_iter_get_baseline(iter);
531
532       if(ii == start)
533         start_pos = ((double)logical_rect.y) / PANGO_SCALE;
534       
535       cairo_move_to(cr,
536                     ((double)logical_rect.x) / PANGO_SCALE,
537                     ((double)baseline) / PANGO_SCALE - start_pos);
538                     
539       if ((attr = g_hash_table_lookup(print_data->images, GINT_TO_POINTER(pango_layout_iter_get_index(iter)))) != NULL) {
540           cairo_surface_t *surface;
541
542           surface = pixbuf_to_surface(GDK_PIXBUF(attr->data));
543           cairo_set_source_surface (cr, surface, 
544                 ((double)logical_rect.x) / PANGO_SCALE, 
545                 ((double)baseline) / PANGO_SCALE - start_pos);
546           cairo_paint (cr);
547           cairo_surface_destroy (surface);
548           g_object_unref(GDK_PIXBUF(attr->data));
549       } else {
550         pango_cairo_show_layout_line(cr, line);
551       }
552     }
553     ii++;
554   } while(ii < end && (notlast = pango_layout_iter_next_line(iter)));
555   pango_layout_iter_free(iter);
556   return TRUE;
557 }
558
559 static void cb_draw_page(GtkPrintOperation *op, GtkPrintContext *context,
560                          int page_nr, gpointer user_data)
561 {
562   claws_draw_page(op, context, page_nr, user_data);
563   debug_print("Sent page %d to printer\n", page_nr+1);
564 }
565
566 static void printing_layout_set_text_attributes(PrintData *print_data, GtkPrintContext *context)
567 {
568   GtkTextIter iter;
569   PangoAttrList *attr_list;
570   PangoAttribute *attr;
571   GSList *open_attrs, *attr_walk;
572
573   attr_list = pango_attr_list_new();
574   if (print_data->sel_start < 0 || print_data->sel_end <= print_data->sel_start) {
575     gtk_text_buffer_get_start_iter(print_data->buffer, &iter);
576   } else {
577     gtk_text_buffer_get_iter_at_offset(print_data->buffer, &iter, print_data->sel_start);
578   }
579
580   open_attrs = NULL;
581   do {
582     gboolean fg_set, bg_set, under_set, strike_set;
583     GSList *tags, *tag_walk;
584     GtkTextTag *tag;
585     GdkColor *color;
586     PangoUnderline underline;
587     gboolean strikethrough;
588     GdkPixbuf *image;
589
590     if (prefs_common.print_imgs && (image = gtk_text_iter_get_pixbuf(&iter)) != NULL) {
591       PangoRectangle rect = {0, 0, 0, 0};
592       gint startpos = printing_text_iter_get_offset_bytes(print_data, &iter);
593       gint h = gdk_pixbuf_get_height(image);
594       gint w = gdk_pixbuf_get_width(image);
595       gint a_h = gtk_print_context_get_height(context);
596       gint a_w = gtk_print_context_get_width(context);
597       gint r_h, r_w;
598       GdkPixbuf *scaled = NULL;
599       image_viewer_get_resized_size(w, h, a_w, a_h, &r_w, &r_h);
600       rect.x = 0;
601       rect.y = 0;
602       rect.width = r_w * PANGO_SCALE;
603       rect.height = r_h * PANGO_SCALE;
604       
605       scaled = gdk_pixbuf_scale_simple(image, r_w, r_h, GDK_INTERP_BILINEAR);
606       attr = pango_attr_shape_new_with_data (&rect, &rect,
607                scaled, NULL, NULL);
608       attr->start_index = startpos;
609       attr->end_index = startpos+1;
610       pango_attr_list_insert(attr_list, attr);
611       g_hash_table_insert(print_data->images, GINT_TO_POINTER(startpos), attr);
612       print_data->img_cnt++;
613     }
614
615     if(gtk_text_iter_ends_tag(&iter, NULL)) {
616       PangoAttrColor *attr_color;
617       PangoAttrInt   *attr_int;
618
619       tags = gtk_text_iter_get_toggled_tags(&iter, FALSE);
620       for(tag_walk = tags; tag_walk != NULL; tag_walk = tag_walk->next) {
621         gboolean found;
622
623         tag = GTK_TEXT_TAG(tag_walk->data);
624         g_object_get(G_OBJECT(tag),
625                      "background-set", &bg_set,
626                      "foreground-set", &fg_set,
627                      "underline-set",&under_set,
628                      "strikethrough-set", &strike_set,
629                      NULL);
630
631         if(fg_set) {
632           found = FALSE;
633           for(attr_walk = open_attrs; attr_walk != NULL; attr_walk = attr_walk->next) {
634             attr = (PangoAttribute*)attr_walk->data;
635             if(attr->klass->type == PANGO_ATTR_FOREGROUND) {
636               attr_color = (PangoAttrColor*) attr;
637               g_object_get(G_OBJECT(tag), "foreground_gdk", &color, NULL);
638               if(printing_is_pango_gdk_color_equal(&(attr_color->color), color)) {
639                 attr->end_index = printing_text_iter_get_offset_bytes(print_data, &iter);
640                 pango_attr_list_insert(attr_list, attr);
641                 found = TRUE;
642                 open_attrs = g_slist_delete_link(open_attrs, attr_walk);
643                 break;
644               }
645             }
646           }
647           if(!found)
648             debug_print("Error generating attribute list.\n");
649         }
650
651         if(bg_set) {
652           found = FALSE;
653           for(attr_walk = open_attrs; attr_walk != NULL; attr_walk = attr_walk->next) {
654             attr = (PangoAttribute*)attr_walk->data;
655             if(attr->klass->type == PANGO_ATTR_BACKGROUND) {
656               attr_color = (PangoAttrColor*) attr;
657               g_object_get(G_OBJECT(tag), "background-gdk", &color, NULL);
658               if(printing_is_pango_gdk_color_equal(&(attr_color->color), color)) {
659                 attr->end_index = printing_text_iter_get_offset_bytes(print_data, &iter);
660                 pango_attr_list_insert(attr_list, attr);
661                 found = TRUE;
662                 open_attrs = g_slist_delete_link(open_attrs, attr_walk);
663                 break;
664               }
665             }
666           }
667           if(!found)
668             debug_print("Error generating attribute list.\n");
669         }
670
671         if(under_set) {
672           found = FALSE;
673           for(attr_walk = open_attrs; attr_walk != NULL; attr_walk = attr_walk->next) {
674             attr = (PangoAttribute*)attr_walk->data;
675             if(attr->klass->type == PANGO_ATTR_UNDERLINE) {
676               attr_int = (PangoAttrInt*)attr;
677               g_object_get(G_OBJECT(tag), "underline", &underline, NULL);
678               if(attr_int->value == underline) {
679                 attr->end_index = printing_text_iter_get_offset_bytes(print_data, &iter);
680                 pango_attr_list_insert(attr_list, attr);
681                 found = TRUE;
682                 open_attrs = g_slist_delete_link(open_attrs, attr_walk);
683                 break;
684               }
685             }
686           }
687           if(!found)
688             debug_print("Error generating attribute list.\n");
689         }
690
691         if(strike_set) {
692           found = FALSE;
693           for(attr_walk = open_attrs; attr_walk != NULL; attr_walk = attr_walk->next) {
694             attr = (PangoAttribute*)attr_walk->data;
695             if(attr->klass->type == PANGO_ATTR_STRIKETHROUGH) {
696               attr_int = (PangoAttrInt*)attr;
697               g_object_get(G_OBJECT(tag), "strikethrough", &strikethrough, NULL);
698               if(attr_int->value == strikethrough) {
699                 attr->end_index = printing_text_iter_get_offset_bytes(print_data, &iter);
700                 pango_attr_list_insert(attr_list, attr);
701                 found = TRUE;
702                 open_attrs = g_slist_delete_link(open_attrs, attr_walk);
703                 break;
704               }
705             }
706           }
707           if(!found)
708             debug_print("Error generating attribute list.\n");
709         }
710
711       }
712       g_slist_free(tags);
713     }
714
715     if(gtk_text_iter_begins_tag(&iter, NULL)) {
716       tags = gtk_text_iter_get_toggled_tags(&iter, TRUE);
717       for(tag_walk = tags; tag_walk != NULL; tag_walk = tag_walk->next) {
718         tag = GTK_TEXT_TAG(tag_walk->data);
719         g_object_get(G_OBJECT(tag),
720                      "background-set", &bg_set,
721                      "foreground-set", &fg_set,
722                      "underline-set", &under_set,
723                      "strikethrough-set", &strike_set,
724                      NULL);
725         if(fg_set) {
726           g_object_get(G_OBJECT(tag), "foreground-gdk", &color, NULL);
727           attr = pango_attr_foreground_new(color->red,color->green,color->blue);
728           attr->start_index = printing_text_iter_get_offset_bytes(print_data, &iter);
729           open_attrs = g_slist_prepend(open_attrs, attr);
730         }
731         if(bg_set) {
732           g_object_get(G_OBJECT(tag), "background-gdk", &color, NULL);
733           attr = pango_attr_background_new(color->red,color->green,color->blue);
734           attr->start_index = printing_text_iter_get_offset_bytes(print_data, &iter);
735           open_attrs = g_slist_prepend(open_attrs, attr);
736         }
737         if(under_set) {
738           g_object_get(G_OBJECT(tag), "underline", &underline, NULL);
739           attr = pango_attr_underline_new(underline);
740           attr->start_index = printing_text_iter_get_offset_bytes(print_data, &iter);
741           open_attrs = g_slist_prepend(open_attrs, attr);         
742         }
743         if(strike_set) {
744           g_object_get(G_OBJECT(tag), "strikethrough", &strikethrough, NULL);
745           attr = pango_attr_strikethrough_new(strikethrough);
746           attr->start_index = printing_text_iter_get_offset_bytes(print_data, &iter);
747           open_attrs = g_slist_prepend(open_attrs, attr);         
748         }
749       }
750       g_slist_free(tags);
751     }
752     
753   } while(!gtk_text_iter_is_end(&iter) && gtk_text_iter_forward_to_tag_toggle(&iter, NULL));
754           
755   /* close all open attributes */
756   for(attr_walk = open_attrs; attr_walk != NULL; attr_walk = attr_walk->next) {
757     attr = (PangoAttribute*) attr_walk->data;
758     attr->end_index = printing_text_iter_get_offset_bytes(print_data, &iter);
759     pango_attr_list_insert(attr_list, attr);
760   }
761   g_slist_free(open_attrs);
762
763   pango_layout_set_attributes(print_data->layout, attr_list);
764   pango_attr_list_unref(attr_list);
765 }
766
767 static gboolean printing_is_pango_gdk_color_equal(PangoColor *p, GdkColor *g)
768 {
769   return ((p->red == g->red) && (p->green == g->green) && (p->blue == g->blue));
770 }
771
772 /* Pango has it's attribute in bytes, but GtkTextIter gets only an offset
773  * in characters, so here we're returning an offset in bytes. 
774  */
775 static gint printing_text_iter_get_offset_bytes(PrintData *print_data, const GtkTextIter *iter)
776 {
777   gint off_chars;
778   gint off_bytes;
779   gchar *text;
780   GtkTextIter start;
781
782   off_chars = gtk_text_iter_get_offset(iter);
783   if (print_data->sel_start < 0 || print_data->sel_end <= print_data->sel_start) {
784     gtk_text_buffer_get_start_iter(gtk_text_iter_get_buffer(iter), &start);
785   } else {
786     gtk_text_buffer_get_iter_at_offset(gtk_text_iter_get_buffer(iter), &start, print_data->sel_start);
787   }
788   text = gtk_text_iter_get_text(&start, iter);
789   off_bytes = strlen(text);
790   g_free(text);
791   return off_bytes;
792 }
793
794 #endif /* GTK+ >= 2.10.0 */