71187217e2e2f0ccf4814fd3526dd0653e19d338
[geeqie.git] / src / print.cc
1 /*
2  * Copyright (C) 2018 The Geeqie Team
3  *
4  * Author: Colin Clark
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include "main.h"
22 #include "print.h"
23
24 #include "exif.h"
25 #include "filedata.h"
26 #include "image-load.h"
27 #include "osd.h"
28 #include "pixbuf-util.h"
29 #include "ui-misc.h"
30 #include "ui-fileops.h"
31
32 #define PRINT_SETTINGS "print_settings" // filename save printer settings
33 #define PAGE_SETUP "page_setup" // filename save page setup
34
35 /* padding between objects */
36 #define PRINT_TEXT_PADDING 3.0
37
38 /* method to use when scaling down image data */
39 #define PRINT_MAX_INTERP GDK_INTERP_BILINEAR
40
41 /* reverse order is important */
42 enum TextPosition {
43         FOOTER_2,
44         FOOTER_1,
45         HEADER_2,
46         HEADER_1
47 };
48
49 struct PrintWindow
50 {
51         GtkWidget *vbox;
52         GList *source_selection;
53
54         gint job_page;
55         GtkTextBuffer *page_text;
56         gchar *template_string;
57         GtkWidget *parent;
58         ImageLoader     *job_loader;
59
60         GList *print_pixbuf_queue;
61         gboolean job_render_finished;
62         GSList *image_group;
63         GSList *page_group;
64 };
65
66 static gint print_layout_page_count(PrintWindow *pw)
67 {
68         gint images;
69
70         images = g_list_length(pw->source_selection);
71
72         if (images < 1 ) return 0;
73
74         return images;
75 }
76
77 static gboolean print_job_render_image(PrintWindow *pw);
78
79 static void print_job_render_image_loader_done(ImageLoader *il, gpointer data)
80 {
81         auto pw = static_cast<PrintWindow *>(data);
82         GdkPixbuf *pixbuf;
83
84         pixbuf = image_loader_get_pixbuf(il);
85
86         g_object_ref(pixbuf);
87         pw->print_pixbuf_queue = g_list_append(pw->print_pixbuf_queue, pixbuf);
88
89         image_loader_free(pw->job_loader);
90         pw->job_loader = nullptr;
91
92         pw->job_page++;
93
94         if (!print_job_render_image(pw))
95                 {
96                 pw->job_render_finished = TRUE;
97                 }
98 }
99
100 static gboolean print_job_render_image(PrintWindow *pw)
101 {
102         FileData *fd = nullptr;
103
104         fd = static_cast<FileData *>(g_list_nth_data(pw->source_selection, pw->job_page));
105         if (!fd) return FALSE;
106
107         image_loader_free(pw->job_loader);
108         pw->job_loader = nullptr;
109
110         pw->job_loader = image_loader_new(fd);
111         g_signal_connect(G_OBJECT(pw->job_loader), "done",
112                                                 (GCallback)print_job_render_image_loader_done, pw);
113
114         if (!image_loader_start(pw->job_loader))
115                 {
116                 image_loader_free(pw->job_loader);
117                 pw->job_loader= nullptr;
118                 }
119
120         return TRUE;
121 }
122
123 static void print_set_font_cb(GtkWidget *widget, gpointer data)
124 {
125         gpointer option;
126
127         if (g_strcmp0(static_cast<const gchar *>(data), "Image text font") == 0)
128                 {
129                 option = options->printer.image_font;
130                 }
131         else
132                 {
133                 option = options->printer.page_font;
134                 }
135
136         GtkWidget *dialog;
137         char *font;
138         PangoFontDescription *font_desc;
139
140         dialog = gtk_font_chooser_dialog_new(static_cast<const gchar *>(data), GTK_WINDOW(gtk_widget_get_toplevel(widget)));
141         gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), static_cast<const gchar *>(option));
142
143         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL)
144                 {
145                 font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog));
146                 font = pango_font_description_to_string(font_desc);
147                 g_free(option);
148                 option = g_strdup(font);
149                 g_free(font);
150                 }
151
152         gtk_widget_destroy(dialog);
153 }
154
155 static gint set_toggle(GSList *list, TextPosition pos)
156 {
157         GtkToggleButton *current_sel;
158         GtkToggleButton *new_sel;
159         gint new_pos = - 1;
160
161         current_sel = static_cast<GtkToggleButton *>(g_slist_nth(list, pos)->data);
162         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(current_sel)))
163                 {
164                 new_pos = (pos - 1);
165                 if (new_pos < 0)
166                         {
167                         new_pos = HEADER_1;
168                         }
169                 new_sel = static_cast<GtkToggleButton *>(g_slist_nth(list, new_pos)->data);
170                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(new_sel), TRUE);
171                 }
172         return new_pos;
173 }
174
175 static void image_text_position_h1_cb(GtkWidget *widget, gpointer data)
176 {
177         auto pw = static_cast<PrintWindow *>(data);
178         gint new_set;
179
180         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
181                 {
182                 new_set = set_toggle(pw->page_group, HEADER_1);
183                 if (new_set >= 0)
184                         {
185                         options->printer.page_text_position = new_set;
186                         }
187                 options->printer.image_text_position = HEADER_1;
188                 }
189 }
190
191 static void image_text_position_h2_cb(GtkWidget *widget, gpointer data)
192 {
193         auto pw = static_cast<PrintWindow *>(data);
194         gint new_set;
195
196         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
197                 {
198                 new_set = set_toggle(pw->page_group, HEADER_2);
199                 if (new_set >= 0)
200                         {
201                         options->printer.page_text_position = new_set;
202                         }
203                 options->printer.image_text_position = HEADER_2;
204                 }
205 }
206
207 static void image_text_position_f1_cb(GtkWidget *widget, gpointer data)
208 {
209         auto pw = static_cast<PrintWindow *>(data);
210         gint new_set;
211
212         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
213                 {
214                 new_set = set_toggle(pw->page_group, FOOTER_1);
215                 if (new_set >= 0)
216                         {
217                         options->printer.page_text_position = new_set;
218                         }
219                 options->printer.image_text_position = FOOTER_1;
220                 }
221 }
222
223 static void image_text_position_f2_cb(GtkWidget *widget, gpointer data)
224 {
225         auto pw = static_cast<PrintWindow *>(data);
226         gint new_set;
227
228         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
229                 {
230                 new_set = set_toggle(pw->page_group, FOOTER_2);
231                 if (new_set >= 0)
232                         {
233                         options->printer.page_text_position = new_set;
234                         }
235                 options->printer.image_text_position = FOOTER_2;
236                 }
237 }
238
239 static void page_text_position_h1_cb(GtkWidget *widget, gpointer data)
240 {
241         auto pw = static_cast<PrintWindow *>(data);
242         gint new_set;
243
244         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
245                 {
246                 new_set = set_toggle(pw->image_group, HEADER_1);
247                 if (new_set >= 0)
248                         {
249                         options->printer.image_text_position = new_set;
250                         }
251                 options->printer.page_text_position = HEADER_1;
252                 }
253 }
254
255 static void page_text_position_h2_cb(GtkWidget *widget, gpointer data)
256 {
257         auto pw = static_cast<PrintWindow *>(data);
258         gint new_set;
259
260         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
261                 {
262                 new_set = set_toggle(pw->image_group, HEADER_2);
263                 if (new_set >= 0)
264                         {
265                         options->printer.image_text_position = new_set;
266                         }
267                 options->printer.page_text_position = HEADER_2;
268                 }
269 }
270
271 static void page_text_position_f1_cb(GtkWidget *widget, gpointer data)
272 {
273         auto pw = static_cast<PrintWindow *>(data);
274         gint new_set;
275
276         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
277                 {
278                 new_set = set_toggle(pw->image_group, FOOTER_1);
279                 if (new_set >= 0)
280                         {
281                         options->printer.image_text_position = new_set;
282                         }
283                 options->printer.page_text_position = FOOTER_1;
284                 }
285 }
286
287 static void page_text_position_f2_cb(GtkWidget *widget, gpointer data)
288 {
289         auto pw = static_cast<PrintWindow *>(data);
290         gint new_set;
291
292         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
293                 {
294                 new_set = set_toggle(pw->image_group, FOOTER_2);
295                 if (new_set >= 0)
296                         {
297                         options->printer.image_text_position = new_set;
298                         }
299                 options->printer.page_text_position = FOOTER_2;
300                 }
301 }
302
303 static void set_print_image_text_string(gchar **template_string, const gchar *value)
304 {
305         g_assert(template_string);
306
307         g_free(*template_string);
308         *template_string = g_strdup(value);
309 }
310
311 static void image_text_template_view_changed_cb(GtkWidget *UNUSED(widget), gpointer data)
312 {
313         GtkWidget *pTextView;
314         GtkTextBuffer *pTextBuffer;
315         GtkTextIter iStart;
316         GtkTextIter iEnd;
317
318         pTextView = GTK_WIDGET(data);
319
320         pTextBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pTextView));
321         gtk_text_buffer_get_start_iter(pTextBuffer, &iStart);
322         gtk_text_buffer_get_end_iter(pTextBuffer, &iEnd);
323
324         set_print_image_text_string(&options->printer.template_string,
325                                           gtk_text_buffer_get_text(pTextBuffer, &iStart, &iEnd, TRUE));
326 }
327
328 #define PRE_FORMATTED_COLUMNS 4
329 static void print_text_menu(GtkWidget *box, PrintWindow *pw)
330 {
331         GtkWidget *group;
332         GtkWidget *hbox;
333         GtkWidget *button;
334         GtkWidget *button1;
335         GtkWidget *button2;
336         GtkWidget *image_text_button;
337         GtkWidget *page_text_button;
338         GtkWidget *subgroup;
339         GtkWidget *page_text_view;
340         GtkWidget *image_text_template_view;
341         GtkWidget *scrolled;
342         GtkWidget *scrolled_pre_formatted;
343         GtkTextBuffer *buffer;
344
345         group = pref_group_new(box, FALSE, _("Image text"), GTK_ORIENTATION_VERTICAL);
346
347         image_text_button = pref_checkbox_new_int(group, _("Show image text"),
348                                                                                 options->printer.show_image_text, &options->printer.show_image_text);
349
350         subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
351
352         pref_checkbox_link_sensitivity(image_text_button, subgroup);
353
354         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
355         gtk_box_pack_start(GTK_BOX(subgroup), hbox, FALSE, FALSE, 0);
356
357         /* order is important */
358         button1 = pref_radiobutton_new(hbox, nullptr,  "Header 1",
359                                                         options->printer.image_text_position == HEADER_1,
360                                                         G_CALLBACK(image_text_position_h1_cb), pw);
361         button1 = pref_radiobutton_new(hbox, button1,  "Header 2",
362                                                         options->printer.image_text_position == HEADER_2,
363                                                         G_CALLBACK(image_text_position_h2_cb), pw);
364         button1 = pref_radiobutton_new(hbox, button1, "Footer 1",
365                                                         options->printer.image_text_position == FOOTER_1,
366                                                         G_CALLBACK(image_text_position_f1_cb), pw);
367         button1 = pref_radiobutton_new(hbox, button1, "Footer 2",
368                                                         options->printer.image_text_position == FOOTER_2,
369                                                         G_CALLBACK(image_text_position_f2_cb), pw);
370         gtk_widget_show(hbox);
371         pw->image_group = (gtk_radio_button_get_group(GTK_RADIO_BUTTON(button1)));
372
373         image_text_template_view = gtk_text_view_new();
374
375         scrolled_pre_formatted = osd_new(PRE_FORMATTED_COLUMNS, image_text_template_view);
376         gtk_box_pack_start(GTK_BOX(subgroup), scrolled_pre_formatted, FALSE, FALSE, 0);
377         gtk_widget_show(scrolled_pre_formatted);
378         gtk_widget_show(subgroup);
379
380         gtk_widget_set_tooltip_markup(image_text_template_view,
381                                         _("Extensive formatting options are shown in the Help file"));
382
383         scrolled = gtk_scrolled_window_new(nullptr, nullptr);
384         gtk_widget_set_size_request(scrolled, 200, 50);
385         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
386         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
387                                                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
388         gtk_box_pack_start(GTK_BOX(subgroup), scrolled, TRUE, TRUE, 5);
389         gtk_widget_show(scrolled);
390
391         gtk_container_add(GTK_CONTAINER(scrolled), image_text_template_view);
392         gtk_widget_show(image_text_template_view);
393
394         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(image_text_template_view));
395         if (options->printer.template_string) gtk_text_buffer_set_text(buffer, options->printer.template_string, -1);
396         g_signal_connect(G_OBJECT(buffer), "changed",
397                          G_CALLBACK(image_text_template_view_changed_cb), image_text_template_view);
398
399         hbox = pref_box_new(subgroup, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
400
401         button = pref_button_new(nullptr, GTK_STOCK_SELECT_FONT, _("Font"), FALSE,
402                                  G_CALLBACK(print_set_font_cb), const_cast<char *>("Image text font"));
403
404         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
405         gtk_widget_show(button);
406
407         pref_spacer(group, PREF_PAD_GAP);
408
409         group = pref_group_new(box, FALSE, _("Page text"), GTK_ORIENTATION_VERTICAL);
410
411         page_text_button = pref_checkbox_new_int(group, _("Show page text"),
412                                           options->printer.show_page_text, &options->printer.show_page_text);
413
414         subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
415         pref_checkbox_link_sensitivity(page_text_button, subgroup);
416
417         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
418         gtk_box_pack_start(GTK_BOX(subgroup), hbox, FALSE, FALSE, 0);
419
420         /* order is important */
421         button2 = pref_radiobutton_new(hbox, nullptr, "Header 1",
422                                                         options->printer.page_text_position == HEADER_1,
423                                                         G_CALLBACK(page_text_position_h1_cb), pw);
424         button2 = pref_radiobutton_new(hbox, button2,  "Header 2",
425                                                         options->printer.page_text_position == HEADER_2,
426                                                         G_CALLBACK(page_text_position_h2_cb), pw);
427         button2 = pref_radiobutton_new(hbox, button2, "Footer 1",
428                                                         options->printer.page_text_position == FOOTER_1,
429                                                         G_CALLBACK(page_text_position_f1_cb), pw);
430         button2 = pref_radiobutton_new(hbox, button2, "Footer 2",
431                                                         options->printer.page_text_position == FOOTER_2,
432                                                         G_CALLBACK(page_text_position_f2_cb), pw);
433         gtk_widget_show(hbox);
434         pw->page_group = (gtk_radio_button_get_group(GTK_RADIO_BUTTON(button2)));
435
436         scrolled = gtk_scrolled_window_new(nullptr, nullptr);
437         gtk_widget_set_size_request(scrolled, 50, 50);
438         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
439         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
440                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
441         gtk_box_pack_start(GTK_BOX(subgroup), scrolled, TRUE, TRUE, 5);
442         gtk_widget_show(scrolled);
443
444         page_text_view = gtk_text_view_new();
445         pw->page_text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page_text_view ));
446         gtk_text_buffer_set_text(GTK_TEXT_BUFFER(pw->page_text), options->printer.page_text, -1);
447         g_object_ref(pw->page_text);
448
449         gtk_widget_set_tooltip_markup(page_text_view, (_("Text shown on each page of a single or multi-page print job")));
450         gtk_container_add(GTK_CONTAINER(scrolled), page_text_view);
451         gtk_widget_show(page_text_view);
452
453         hbox = pref_box_new(subgroup, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
454
455         button = pref_button_new(nullptr, GTK_STOCK_SELECT_FONT, _("Font"), FALSE,
456                                  G_CALLBACK(print_set_font_cb), const_cast<char *>("Page text font"));
457
458         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
459         gtk_widget_show(button);
460 }
461
462 static gboolean paginate_cb(GtkPrintOperation *UNUSED(operation),
463                                                                         GtkPrintContext *UNUSED(context),
464                                                                         gpointer data)
465 {
466         auto pw = static_cast<PrintWindow *>(data);
467
468         if (pw->job_render_finished)
469                 {
470                 return TRUE;
471                 }
472         else
473                 {
474                 return FALSE;
475                 }
476 }
477
478 gchar *form_image_text(const gchar *template_string, FileData *fd, PrintWindow *pw, gint page_nr, gint total)
479 {
480         const gchar *name;
481         gchar *text = nullptr;
482         GHashTable *vars;
483         gchar *window_title;
484         gchar *delimiter;
485         gchar *collection_name;
486
487         if (!fd) return nullptr;
488
489         name = fd->name;
490
491         vars = g_hash_table_new_full(g_str_hash, g_str_equal, nullptr, g_free);
492
493         window_title = g_strdup(gtk_window_get_title(GTK_WINDOW(pw->parent)));
494         delimiter = g_strstr_len(window_title, -1, " - Collection - ");
495         if (delimiter)
496                 {
497                 collection_name = g_strndup(window_title, delimiter - window_title);
498                 }
499         else
500                 {
501                 collection_name = nullptr;
502                 }
503         g_free(window_title);
504
505         if (collection_name)
506                 {
507                 osd_template_insert(vars, "collection", collection_name, OSDT_NONE);
508                 }
509
510         osd_template_insert(vars, "number", g_strdup_printf("%d", page_nr + 1), OSDT_NO_DUP);
511         osd_template_insert(vars, "total", g_strdup_printf("%d", total), OSDT_NO_DUP);
512         osd_template_insert(vars, "name", const_cast<gchar *>(name), OSDT_NONE);
513         osd_template_insert(vars, "date", fd ? (const_cast<gchar *>(text_from_time(fd->date))) : "", OSDT_NONE);
514         osd_template_insert(vars, "size", fd ? (text_from_size_abrev(fd->size)) : g_strdup(""), OSDT_FREE);
515
516         if (fd->pixbuf)
517                 {
518                 gint w, h;
519                 w = gdk_pixbuf_get_width(fd->pixbuf);
520                 h = gdk_pixbuf_get_height(fd->pixbuf);
521
522                 osd_template_insert(vars, "width", g_strdup_printf("%d", w), OSDT_NO_DUP);
523                 osd_template_insert(vars, "height", g_strdup_printf("%d", h), OSDT_NO_DUP);
524                 osd_template_insert(vars, "res", g_strdup_printf("%d Ã— %d", w, h), OSDT_FREE);
525                 }
526         else
527                 {
528                 osd_template_insert(vars, "width", nullptr, OSDT_NONE);
529                 osd_template_insert(vars, "height", nullptr, OSDT_NONE);
530                 osd_template_insert(vars, "res", nullptr, OSDT_NONE);
531                 }
532
533         text = image_osd_mkinfo(template_string, fd, vars);
534         g_hash_table_destroy(vars);
535
536         g_free(collection_name);
537
538         return text;
539 }
540
541 static void draw_page(GtkPrintOperation *UNUSED(operation), GtkPrintContext *context,
542                                                                         gint page_nr, gpointer data)
543 {
544         auto pw = static_cast<PrintWindow *>(data);
545         FileData *fd;
546         cairo_t *cr;
547         gdouble context_width, context_height;
548         gdouble pixbuf_image_width, pixbuf_image_height;
549         gdouble width_offset;
550         gdouble height_offset;
551         GdkPixbuf *pixbuf;
552         GdkPixbuf *rotated = nullptr;
553         PangoLayout *layout_image = nullptr;
554         PangoLayout *layout_page = nullptr;
555         PangoFontDescription *desc;
556         GString *image_text = g_string_new(nullptr);
557         GString *page_text = g_string_new(nullptr);
558         PangoRectangle ink_rect, logical_rect;
559         gdouble w, h, scale;
560         gdouble image_text_width, image_text_height, page_text_width, page_text_height;
561         gint image_y;
562         gint incr_y;
563         gdouble pango_height;
564         gdouble pango_image_height;
565         gdouble pango_page_height;
566         GtkTextIter start, end;
567         gchar *tmp;
568         gint total;
569
570         fd = static_cast<FileData *>(g_list_nth_data(pw->source_selection, page_nr));
571         total = g_list_length(pw->source_selection);
572
573         pixbuf = static_cast<GdkPixbuf *>(g_list_nth_data(pw->print_pixbuf_queue, page_nr));
574         if (fd->exif_orientation != EXIF_ORIENTATION_TOP_LEFT)
575                 {
576                 rotated = pixbuf_apply_orientation(pixbuf, fd->exif_orientation);
577                 pixbuf = rotated;
578                 }
579
580         pixbuf_image_width = gdk_pixbuf_get_width(pixbuf);
581         pixbuf_image_height = gdk_pixbuf_get_height(pixbuf);
582
583         if (options->printer.show_image_text)
584                 {
585                 image_text = g_string_append(image_text, form_image_text(options->printer.template_string, fd, pw, page_nr, total));
586                 }
587
588         if (options->printer.show_page_text)
589                 {
590                 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(pw->page_text), &start, &end);
591
592                 tmp = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(pw->page_text), &start, &end, FALSE);
593                 page_text = g_string_append(page_text, tmp);
594
595                 g_free(tmp);
596                 }
597
598         cr = gtk_print_context_get_cairo_context(context);
599         context_width = gtk_print_context_get_width(context);
600         context_height = gtk_print_context_get_height(context);
601
602         pango_image_height = 0;
603         pango_page_height = 0;
604         image_text_width = 0;
605         page_text_width = 0;
606
607         if (image_text->len > 0)
608                 {
609                 layout_image = pango_cairo_create_layout(cr);
610
611                 pango_layout_set_text(layout_image, image_text->str, -1);
612                 desc = pango_font_description_from_string(options->printer.image_font);
613                 pango_layout_set_font_description(layout_image, desc);
614
615                 pango_layout_get_extents(layout_image, &ink_rect, &logical_rect);
616                 image_text_width = (static_cast<gdouble>(logical_rect.width) / PANGO_SCALE) ;
617                 image_text_height = (static_cast<gdouble>(logical_rect.height) / PANGO_SCALE);
618
619                 pango_layout_set_alignment(layout_image, PANGO_ALIGN_CENTER);
620                 pango_layout_set_text(layout_image, image_text->str, -1);
621
622                 pango_image_height = image_text_height + PRINT_TEXT_PADDING * 2;
623
624                 pango_font_description_free(desc);
625                 }
626
627         if (page_text->len > 0)
628                 {
629                 layout_page = pango_cairo_create_layout(cr);
630
631                 pango_layout_set_text(layout_page, page_text->str, -1);
632                 desc = pango_font_description_from_string(options->printer.page_font);
633                 pango_layout_set_font_description(layout_page, desc);
634
635                 pango_layout_get_extents(layout_page, &ink_rect, &logical_rect);
636                 page_text_width = (static_cast<gdouble>(logical_rect.width) / PANGO_SCALE) ;
637                 page_text_height = (static_cast<gdouble>(logical_rect.height) / PANGO_SCALE);
638
639                 pango_layout_set_alignment(layout_page, PANGO_ALIGN_CENTER);
640                 pango_layout_set_text(layout_page, page_text->str, -1);
641
642                 pango_page_height = page_text_height + PRINT_TEXT_PADDING * 2;
643
644                 pango_font_description_free(desc);
645                 }
646
647         pango_height = pango_image_height + pango_page_height;
648
649         if ((context_width / pixbuf_image_width) < ((context_height - pango_height) / pixbuf_image_height))
650                 {
651                 w = context_width;
652                 scale = context_width / pixbuf_image_width;
653                 h = pixbuf_image_height * scale;
654                 height_offset = (context_height - (h + pango_height)) / 2;
655                 width_offset = 0;
656                 }
657         else
658                 {
659                 h = context_height - pango_height ;
660                 scale = (context_height - pango_height) / pixbuf_image_height;
661                 w = pixbuf_image_width * scale;
662                 height_offset = 0;
663                 width_offset = (context_width - (pixbuf_image_width * scale)) / 2;
664                 }
665
666         incr_y = height_offset;
667
668         if (options->printer.page_text_position == HEADER_1 && page_text->len > 0)
669                 {
670                 cairo_move_to(cr, (w / 2) - (page_text_width / 2) + width_offset, incr_y);
671                 pango_cairo_show_layout(cr, layout_page);
672
673                 incr_y = incr_y + pango_page_height;
674                 }
675
676         if (options->printer.image_text_position == HEADER_1 && image_text->len > 0)
677                 {
678                 cairo_move_to(cr, (w / 2) - (image_text_width / 2) + width_offset, incr_y + PRINT_TEXT_PADDING);
679                 pango_cairo_show_layout(cr, layout_image);
680
681                 incr_y = incr_y + pango_image_height;
682                 }
683
684         if (options->printer.page_text_position == HEADER_2 && page_text->len > 0)
685                 {
686                 cairo_move_to(cr, (w / 2) - (page_text_width / 2) + width_offset, incr_y);
687                 pango_cairo_show_layout(cr, layout_page);
688
689                 incr_y = incr_y + pango_page_height;
690                 }
691
692         if (options->printer.image_text_position == HEADER_2 && image_text->len > 0)
693                 {
694                 cairo_move_to(cr, (w / 2) - (image_text_width / 2) + width_offset, incr_y);
695                 pango_cairo_show_layout(cr, layout_image);
696
697                 incr_y = incr_y + pango_image_height;
698                 }
699
700         image_y = incr_y;
701         incr_y = incr_y + h;
702
703         if (options->printer.page_text_position == FOOTER_1 && page_text->len > 0)
704                 {
705                 cairo_move_to(cr, (w / 2) - (page_text_width / 2) + width_offset, incr_y + PRINT_TEXT_PADDING);
706                 pango_cairo_show_layout(cr, layout_page);
707
708                 incr_y = incr_y + pango_page_height;
709                 }
710
711         if (options->printer.image_text_position == FOOTER_1 && image_text->len > 0)
712                 {
713                 cairo_move_to(cr, (w / 2) - (image_text_width / 2) + width_offset, incr_y);
714                 pango_cairo_show_layout(cr, layout_image);
715
716                 incr_y = incr_y + pango_image_height;
717                 }
718
719         if (options->printer.page_text_position == FOOTER_2 && page_text->len > 0)
720                 {
721                 cairo_move_to(cr, (w / 2) - (page_text_width / 2) + width_offset, incr_y);
722                 pango_cairo_show_layout(cr, layout_page);
723
724                 incr_y = incr_y + pango_page_height;
725                 }
726
727         if (options->printer.image_text_position == FOOTER_2 && image_text->len > 0)
728                 {
729                 cairo_move_to(cr, (w / 2) - (image_text_width / 2) + width_offset, incr_y);
730                 pango_cairo_show_layout(cr, layout_image);
731                 }
732
733         cairo_scale(cr, scale, scale);
734
735         cairo_rectangle(cr,  width_offset * scale , image_y, pixbuf_image_width / scale, pixbuf_image_height / scale);
736         gdk_cairo_set_source_pixbuf(cr, pixbuf, width_offset / scale, image_y / scale);
737         cairo_fill(cr);
738
739         if (image_text->len > 0)
740                 {
741                 g_object_unref(layout_image);
742                 g_string_free(image_text, TRUE);
743                 }
744         if (page_text->len > 0)
745                 {
746                 g_object_unref(layout_page);
747                 g_string_free(page_text, TRUE);
748                 }
749
750         if (rotated) g_object_unref(rotated);
751 }
752
753 static void begin_print(GtkPrintOperation *operation,
754                                                 GtkPrintContext *UNUSED(context),
755                                                 gpointer user_data)
756 {
757         auto pw = static_cast<PrintWindow *>(user_data);
758         gint page_count;
759
760         page_count = print_layout_page_count(pw);
761         gtk_print_operation_set_n_pages (operation, page_count);
762
763         print_job_render_image(pw);
764 }
765
766
767 GObject *option_tab_cb(GtkPrintOperation *UNUSED(operation), gpointer user_data)
768 {
769         auto pw = static_cast<PrintWindow *>(user_data);
770
771         return G_OBJECT(pw->vbox);
772 }
773
774 static void print_pref_store(PrintWindow *pw)
775 {
776         gchar *tmp;
777         GtkTextIter start, end;
778
779         gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(pw->page_text), &start, &end);
780         tmp = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(pw->page_text), &start, &end, FALSE);
781         g_free(options->printer.page_text);
782         options->printer.page_text = g_strdup(tmp);
783         g_free(tmp);
784 }
785
786 static void end_print_cb(GtkPrintOperation *operation,
787                                                                 GtkPrintContext *UNUSED(context), gpointer data)
788 {
789         auto pw = static_cast<PrintWindow *>(data);
790         GList *work;
791         GdkPixbuf *pixbuf;
792         gchar *path;
793         GtkPrintSettings *print_settings;
794         GtkPageSetup *page_setup;
795         GError *error = nullptr;
796
797         print_settings = gtk_print_operation_get_print_settings(operation);
798         path = g_build_filename(get_rc_dir(), PRINT_SETTINGS, NULL);
799
800         gtk_print_settings_to_file(print_settings, path, &error);
801         if (error)
802                 {
803                 log_printf("Error: Print settings save failed:\n%s", error->message);
804                 g_error_free(error);
805                 error = nullptr;
806                 }
807         g_free(path);
808         g_object_unref(print_settings);
809
810         page_setup = gtk_print_operation_get_default_page_setup(operation);
811         path = g_build_filename(get_rc_dir(), PAGE_SETUP, NULL);
812
813         gtk_page_setup_to_file(page_setup, path, &error);
814         if (error)
815                 {
816                 log_printf("Error: Print page setup save failed:\n%s", error->message);
817                 g_error_free(error);
818                 error = nullptr;
819                 }
820         g_free(path);
821         g_object_unref(page_setup);
822
823         print_pref_store(pw);
824
825         work = pw->print_pixbuf_queue;
826         while (work)
827                 {
828                 pixbuf = static_cast<GdkPixbuf *>(work->data);
829                 if (pixbuf)
830                         {
831                         g_object_unref(pixbuf);
832                         }
833                 work = work->next;
834                 }
835         g_list_free(pw->print_pixbuf_queue);
836         g_object_unref(pw->page_text);
837         g_free(pw);
838 }
839
840 void print_window_new(FileData *UNUSED(fd), GList *selection, GList *UNUSED(list), GtkWidget *parent)
841 {
842         GtkWidget *vbox;
843         GtkPrintOperation *operation;
844         GtkPageSetup *page_setup;
845         gchar *uri;
846         const gchar *dir;
847         GError *error = nullptr;
848         gchar *path;
849         GtkPrintSettings *settings;
850
851         auto pw = g_new0(PrintWindow, 1);
852
853         pw->source_selection = file_data_process_groups_in_selection(selection, FALSE, nullptr);
854
855         if (print_layout_page_count(pw) == 0)
856                 {
857                 return;
858                 }
859
860         pw->parent = parent;
861
862         vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
863         gtk_container_set_border_width(GTK_CONTAINER(vbox), PREF_PAD_BORDER);
864         gtk_widget_show(vbox);
865
866         print_text_menu(vbox, pw);
867         pw->vbox = vbox;
868
869         pw->print_pixbuf_queue = nullptr;
870         pw->job_render_finished = FALSE;
871         pw->job_page = 0;
872
873         operation = gtk_print_operation_new();
874         settings = gtk_print_settings_new();
875
876         gtk_print_operation_set_custom_tab_label(operation, "Options");
877         gtk_print_operation_set_use_full_page(operation, TRUE);
878         gtk_print_operation_set_unit(operation, GTK_UNIT_POINTS);
879         gtk_print_operation_set_embed_page_setup(operation, TRUE);
880         gtk_print_operation_set_allow_async (operation, TRUE);
881         dir = g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS);
882         if (dir == nullptr)
883                 {
884                 dir = g_get_home_dir();
885                 }
886
887         uri = g_build_filename("file:/", dir, "geeqie-file.pdf", NULL);
888         gtk_print_settings_set(settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);
889         g_free(uri);
890
891         path = g_build_filename(get_rc_dir(), PRINT_SETTINGS, NULL);
892         gtk_print_settings_load_file(settings, path, &error);
893         if (error)
894                 {
895                 log_printf("Error: Printer settings load failed:\n%s", error->message);
896                 g_error_free(error);
897                 error = nullptr;
898                 }
899         gtk_print_operation_set_print_settings(operation, settings);
900         g_free(path);
901
902         page_setup = gtk_page_setup_new();
903         path = g_build_filename(get_rc_dir(), PAGE_SETUP, NULL);
904         gtk_page_setup_load_file(page_setup, path, &error);
905         if (error)
906                 {
907                 log_printf("Error: Print page setup load failed:\n%s", error->message);
908                 g_error_free(error);
909                 error = nullptr;
910                 }
911         gtk_print_operation_set_default_page_setup(operation, page_setup);
912         g_free(path);
913
914         g_signal_connect (G_OBJECT (operation), "begin-print",
915                                         G_CALLBACK (begin_print), pw);
916         g_signal_connect (G_OBJECT (operation), "draw-page",
917                                         G_CALLBACK (draw_page), pw);
918         g_signal_connect (G_OBJECT (operation), "end-print",
919                                         G_CALLBACK (end_print_cb), pw);
920         g_signal_connect (G_OBJECT (operation), "create-custom-widget",
921                                         G_CALLBACK (option_tab_cb), pw);
922         g_signal_connect (G_OBJECT (operation), "paginate",
923                                         G_CALLBACK (paginate_cb), pw);
924
925         gtk_print_operation_set_n_pages(operation, print_layout_page_count(pw));
926
927         gtk_print_operation_run(operation, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
928                                                                                                 GTK_WINDOW (parent), &error);
929
930         if (error)
931                 {
932                 GtkWidget *dialog;
933
934                 dialog = gtk_message_dialog_new(GTK_WINDOW (parent),
935                                                                 GTK_DIALOG_DESTROY_WITH_PARENT,
936                                                                 GTK_MESSAGE_ERROR,
937                                                                 GTK_BUTTONS_CLOSE,
938                                                                 "%s", error->message);
939                 g_error_free (error);
940
941                 g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL);
942
943                 gtk_widget_show (dialog);
944                 }
945
946         g_object_unref(page_setup);
947         g_object_unref(settings);
948 }
949 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */