1 /*
2  * This program is free software; you can redistribute it and/or modify it
3  * under the terms of the GNU Lesser General Public License as published by
4  * the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful, but
7  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
9  * for more details.
10  *
11  * You should have received a copy of the GNU Lesser General Public License
12  * along with this program; if not, see <http://www.gnu.org/licenses/>.
13  *
14  * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com>
15  *
16  */
17 
18 #include "evolution-config.h"
19 
20 #include <string.h>
21 #include <glib/gi18n.h>
22 #include <gtk/gtk.h>
23 
24 #include <camel/camel.h>
25 
26 #include "e-util/e-util.h"
27 
28 #include "em-format/e-mail-formatter-print.h"
29 #include "em-format/e-mail-part-utils.h"
30 
31 #include "e-mail-printer.h"
32 #include "e-mail-display.h"
33 #include "e-mail-print-config-headers.h"
34 
35 #define w(x)
36 
37 #define E_MAIL_PRINTER_GET_PRIVATE(obj) \
38 	(G_TYPE_INSTANCE_GET_PRIVATE \
39 	((obj), E_TYPE_MAIL_PRINTER, EMailPrinterPrivate))
40 
41 typedef struct _AsyncContext AsyncContext;
42 
43 struct _EMailPrinterPrivate {
44 	EMailFormatter *formatter;
45 	EMailPartList *part_list;
46 	EMailRemoteContent *remote_content;
47 	EMailFormatterMode mode;
48 
49 	gchar *export_filename;
50 };
51 
52 struct _AsyncContext {
53 	WebKitWebView *web_view;
54 	gulong load_status_handler_id;
55 	GError *error;
56 
57 	GtkPrintOperationResult print_result;
58 };
59 
60 enum {
61 	PROP_0,
62 	PROP_PART_LIST,
63 	PROP_REMOTE_CONTENT
64 };
65 
66 enum {
67 	COLUMN_ACTIVE,
68 	COLUMN_HEADER_NAME,
69 	COLUMN_HEADER_VALUE,
70 	COLUMN_HEADER_STRUCT,
71 	LAST_COLUMN
72 };
73 
74 G_DEFINE_TYPE (
75 	EMailPrinter,
76 	e_mail_printer,
77 	G_TYPE_OBJECT);
78 
79 static void
async_context_free(AsyncContext * async_context)80 async_context_free (AsyncContext *async_context)
81 {
82 	if (async_context->load_status_handler_id > 0)
83 		g_signal_handler_disconnect (
84 			async_context->web_view,
85 			async_context->load_status_handler_id);
86 
87 	g_clear_object (&async_context->web_view);
88 	g_clear_error (&async_context->error);
89 
90 	g_slice_free (AsyncContext, async_context);
91 }
92 
93 #if 0 /* FIXME WK2 */
94 static GtkWidget *
95 mail_printer_create_custom_widget_cb (WebKitPrintOperation *operation,
96                                       AsyncContext *async_context)
97 {
98 	EMailDisplay *display;
99 	EMailPartList *part_list;
100 	EMailPart *part;
101 	GtkWidget *widget;
102 
103 	webkit_print_operation_set_custom_tab_label (operation, _("Headers"));
104 
105 	display = E_MAIL_DISPLAY (async_context->web_view);
106 	part_list = e_mail_display_get_part_list (display);
107 
108 	/* FIXME Hard-coding the part ID works for now but could easily
109 	 *       break silently.  Need a less brittle way of extracting
110 	 *       specific parts by either MIME type or GType. */
111 	part = e_mail_part_list_ref_part (part_list, ".message.headers");
112 
113 	widget = e_mail_print_config_headers_new (E_MAIL_PART_HEADERS (part));
114 
115 	g_object_unref (part);
116 
117 	return widget;
118 }
119 
120 static void
121 mail_printer_custom_widget_apply_cb (WebKitPrintOperation *operation,
122                                      GtkWidget *widget,
123                                      AsyncContext *async_context)
124 {
125 	webkit_web_view_reload (async_context->web_view);
126 }
127 
128 static void
129 mail_printer_draw_footer_cb (GtkPrintOperation *operation,
130                              GtkPrintContext *context,
131                              gint page_nr)
132 {
133 	PangoFontDescription *desc;
134 	PangoLayout *layout;
135 	gint n_pages;
136 	gdouble width, height;
137 	gchar *text;
138 	cairo_t *cr;
139 
140 	cr = gtk_print_context_get_cairo_context (context);
141 	width = gtk_print_context_get_width (context);
142 	height = gtk_print_context_get_height (context);
143 
144 	g_object_get (operation, "n-pages", &n_pages, NULL);
145 	text = g_strdup_printf (_("Page %d of %d"), page_nr + 1, n_pages);
146 
147 	cairo_set_source_rgb (cr, 0.1, 0.1, 0.1);
148 	cairo_fill (cr);
149 
150 	desc = pango_font_description_from_string ("Sans Regular 10");
151 	layout = gtk_print_context_create_pango_layout (context);
152 	pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
153 	pango_layout_set_font_description (layout, desc);
154 	pango_layout_set_text (layout, text, -1);
155 	pango_layout_set_width (layout, width * PANGO_SCALE);
156 	pango_font_description_free (desc);
157 
158 	cairo_move_to (cr, 0, height + 5);
159 	pango_cairo_show_layout (cr, layout);
160 
161 	g_object_unref (layout);
162 	g_free (text);
163 }
164 #endif
165 static void
mail_printer_print_finished_cb(WebKitPrintOperation * print_operation,GTask * task)166 mail_printer_print_finished_cb (WebKitPrintOperation *print_operation,
167                                 GTask *task)
168 {
169 	AsyncContext *async_context;
170 
171 	if (camel_debug ("webkit:preview"))
172 		printf ("%s\n", G_STRFUNC);
173 
174 	async_context = g_task_get_task_data (task);
175 	g_return_if_fail (async_context != NULL);
176 
177 	if (async_context->print_result == GTK_PRINT_OPERATION_RESULT_IN_PROGRESS) {
178 		async_context->print_result = GTK_PRINT_OPERATION_RESULT_APPLY;
179 		g_task_return_boolean (task, TRUE);
180 	} else if (async_context->error) {
181 		g_task_return_error (task, g_error_copy (async_context->error));
182 	} else {
183 		g_task_return_boolean (task, FALSE);
184 	}
185 
186 	g_object_unref (task);
187 }
188 
189 static void
mail_printer_print_failed_cb(WebKitPrintOperation * print_operation,const GError * error,GTask * task)190 mail_printer_print_failed_cb (WebKitPrintOperation *print_operation,
191                               const GError *error,
192                               GTask *task)
193 {
194 	AsyncContext *async_context;
195 
196 	if (camel_debug ("webkit:preview"))
197 		printf ("%s\n", G_STRFUNC);
198 
199 	async_context = g_task_get_task_data (task);
200 	g_return_if_fail (async_context != NULL);
201 	async_context->print_result = GTK_PRINT_OPERATION_RESULT_ERROR;
202 	async_context->error = error ? g_error_copy (error) : NULL;
203 }
204 
205 static gboolean
mail_printer_print_timeout_cb(GTask * task)206 mail_printer_print_timeout_cb (GTask *task)
207 {
208 	AsyncContext *async_context;
209 	gpointer source_object;
210 	const gchar *export_filename;
211 	GtkPrintSettings *print_settings = NULL;
212 	WebKitPrintOperation *print_operation = NULL;
213 	WebKitPrintOperationResponse response;
214 	/* FIXME WK2
215 	gulong draw_page_handler_id;
216 	gulong create_custom_widget_handler_id;
217 	gulong custom_widget_apply_handler_id;*/
218 
219 	async_context = g_task_get_task_data (task);
220 
221 	g_return_val_if_fail (async_context != NULL, G_SOURCE_REMOVE);
222 
223 	source_object = g_task_get_source_object (task);
224 
225 	g_return_val_if_fail (E_IS_MAIL_PRINTER (source_object), G_SOURCE_REMOVE);
226 
227 	print_settings = gtk_print_settings_new ();
228 	export_filename = e_mail_printer_get_export_filename (E_MAIL_PRINTER (source_object));
229 	gtk_print_settings_set (
230 		print_settings,
231 		GTK_PRINT_SETTINGS_OUTPUT_BASENAME,
232 		export_filename);
233 
234 	print_operation = webkit_print_operation_new (async_context->web_view);
235 	webkit_print_operation_set_print_settings (print_operation, print_settings);
236 
237 	g_signal_connect_data (
238 		print_operation, "failed",
239 		G_CALLBACK (mail_printer_print_failed_cb),
240 		g_object_ref (task),
241 		(GClosureNotify) g_object_unref, 0);
242 
243 	g_signal_connect_data (
244 		print_operation, "finished",
245 		G_CALLBACK (mail_printer_print_finished_cb),
246 		g_object_ref (task),
247 		(GClosureNotify) g_object_unref, 0);
248 
249 	/* FIXME WK2
250 	create_custom_widget_handler_id = g_signal_connect (
251 		print_operation, "create-custom-widget",
252 		G_CALLBACK (mail_printer_create_custom_widget_cb),
253 		async_context);
254 
255 	custom_widget_apply_handler_id = g_signal_connect (
256 		print_operation, "custom-widget-apply",
257 		G_CALLBACK (mail_printer_custom_widget_apply_cb),
258 		async_context); */
259 
260 	/* FIXME WK2 - this will be hard to add back to WK2 API.. There is a CSS draft
261 	 * that can be used to add a page numbers, but it is not in WebKit yet.
262 	 * http://www.w3.org/TR/css3-page/
263 	draw_page_handler_id = g_signal_connect (
264 		print_operation, "draw-page",
265 		G_CALLBACK (mail_printer_draw_footer_cb),
266 		async_context->cancellable); */
267 
268 	response = webkit_print_operation_run_dialog (print_operation, NULL);
269 
270 	/* FIXME WK2
271 	g_signal_handler_disconnect (
272 		print_operation, create_custom_widget_handler_id);
273 
274 	g_signal_handler_disconnect (
275 		print_operation, custom_widget_apply_handler_id);
276 
277 	g_signal_handler_disconnect (
278 		print_operation, draw_page_handler_id); */
279 
280 	g_clear_object (&print_operation);
281 	g_clear_object (&print_settings);
282 
283 	if (response == WEBKIT_PRINT_OPERATION_RESPONSE_CANCEL) {
284 		async_context->print_result = GTK_PRINT_OPERATION_RESULT_CANCEL;
285 		g_task_return_boolean (task, TRUE);
286 		g_object_unref (task);
287 	}
288 
289 	return G_SOURCE_REMOVE;
290 }
291 
292 static void
mail_printer_load_changed_cb(WebKitWebView * web_view,WebKitLoadEvent load_event,GTask * task)293 mail_printer_load_changed_cb (WebKitWebView *web_view,
294                               WebKitLoadEvent load_event,
295                               GTask *task)
296 {
297 	AsyncContext *async_context;
298 
299 	/* Note: we disregard WEBKIT_LOAD_FAILED and print what we can. */
300 	if (load_event != WEBKIT_LOAD_FINISHED)
301 		return;
302 
303 	async_context = g_task_get_task_data (task);
304 
305 	g_return_if_fail (async_context != NULL);
306 
307 	/* WebKit reloads the page once more right before starting to print,
308 	 * so disconnect this handler after the first time to avoid starting
309 	 * another print operation. */
310 	g_signal_handler_disconnect (
311 		async_context->web_view,
312 		async_context->load_status_handler_id);
313 	async_context->load_status_handler_id = 0;
314 
315 	/* Check if we've been cancelled. */
316 	if (g_task_return_error_if_cancelled (task)) {
317 		g_object_unref (task);
318 		return;
319 	} else {
320 		GSource *timeout_source;
321 
322 		/* Give WebKit some time to perform layouting and rendering before
323 		 * we start printing. 500ms should be enough in most cases. */
324 		timeout_source = g_timeout_source_new (500);
325 		g_task_attach_source (
326 			task,
327 			timeout_source,
328 			(GSourceFunc) mail_printer_print_timeout_cb);
329 		g_source_unref (timeout_source);
330 	}
331 }
332 
333 static WebKitWebView *
mail_printer_new_web_view(const gchar * charset,const gchar * default_charset,EMailFormatterMode mode)334 mail_printer_new_web_view (const gchar *charset,
335 			   const gchar *default_charset,
336 			   EMailFormatterMode mode)
337 {
338 	WebKitWebView *web_view;
339 	EMailFormatter *formatter;
340 
341 	web_view = g_object_new (
342 		E_TYPE_MAIL_DISPLAY,
343 		"mode", mode, NULL);
344 
345 	/* Do not load remote images, print what user sees in the preview panel */
346 	e_mail_display_set_force_load_images (E_MAIL_DISPLAY (web_view), FALSE);
347 
348 	formatter = e_mail_display_get_formatter (E_MAIL_DISPLAY (web_view));
349 	if (charset != NULL && *charset != '\0')
350 		e_mail_formatter_set_charset (formatter, charset);
351 	if (default_charset != NULL && *default_charset != '\0')
352 		e_mail_formatter_set_default_charset (formatter, default_charset);
353 
354 	return web_view;
355 }
356 
357 static void
mail_printer_set_part_list(EMailPrinter * printer,EMailPartList * part_list)358 mail_printer_set_part_list (EMailPrinter *printer,
359                             EMailPartList *part_list)
360 {
361 	g_return_if_fail (E_IS_MAIL_PART_LIST (part_list));
362 	g_return_if_fail (printer->priv->part_list == NULL);
363 
364 	printer->priv->part_list = g_object_ref (part_list);
365 }
366 
367 static void
mail_printer_set_remote_content(EMailPrinter * printer,EMailRemoteContent * remote_content)368 mail_printer_set_remote_content (EMailPrinter *printer,
369 				 EMailRemoteContent *remote_content)
370 {
371 	g_return_if_fail (E_IS_MAIL_REMOTE_CONTENT (remote_content));
372 	g_return_if_fail (printer->priv->remote_content == NULL);
373 
374 	printer->priv->remote_content = g_object_ref (remote_content);
375 }
376 
377 static void
mail_printer_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)378 mail_printer_set_property (GObject *object,
379                            guint property_id,
380                            const GValue *value,
381                            GParamSpec *pspec)
382 {
383 	switch (property_id) {
384 		case PROP_PART_LIST:
385 			mail_printer_set_part_list (
386 				E_MAIL_PRINTER (object),
387 				g_value_get_object (value));
388 			return;
389 
390 		case PROP_REMOTE_CONTENT:
391 			mail_printer_set_remote_content (
392 				E_MAIL_PRINTER (object),
393 				g_value_get_object (value));
394 			return;
395 	}
396 
397 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
398 }
399 
400 static void
mail_printer_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)401 mail_printer_get_property (GObject *object,
402                            guint property_id,
403                            GValue *value,
404                            GParamSpec *pspec)
405 {
406 	switch (property_id) {
407 		case PROP_PART_LIST:
408 			g_value_take_object (
409 				value,
410 				e_mail_printer_ref_part_list (
411 				E_MAIL_PRINTER (object)));
412 			return;
413 
414 		case PROP_REMOTE_CONTENT:
415 			g_value_take_object (
416 				value,
417 				e_mail_printer_ref_remote_content (
418 				E_MAIL_PRINTER (object)));
419 			return;
420 	}
421 
422 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
423 }
424 
425 static void
mail_printer_dispose(GObject * object)426 mail_printer_dispose (GObject *object)
427 {
428 	EMailPrinterPrivate *priv;
429 
430 	priv = E_MAIL_PRINTER_GET_PRIVATE (object);
431 
432 	g_clear_object (&priv->formatter);
433 	g_clear_object (&priv->part_list);
434 	g_clear_object (&priv->remote_content);
435 	g_free (priv->export_filename);
436 
437 	/* Chain up to parent's dispose() method. */
438 	G_OBJECT_CLASS (e_mail_printer_parent_class)->dispose (object);
439 }
440 
441 static void
e_mail_printer_class_init(EMailPrinterClass * class)442 e_mail_printer_class_init (EMailPrinterClass *class)
443 {
444 	GObjectClass *object_class;
445 
446 	g_type_class_add_private (class, sizeof (EMailPrinterPrivate));
447 
448 	object_class = G_OBJECT_CLASS (class);
449 	object_class->set_property = mail_printer_set_property;
450 	object_class->get_property = mail_printer_get_property;
451 	object_class->dispose = mail_printer_dispose;
452 
453 	g_object_class_install_property (
454 		object_class,
455 		PROP_PART_LIST,
456 		g_param_spec_object (
457 			"part-list",
458 			"Part List",
459 			NULL,
460 			E_TYPE_MAIL_PART_LIST,
461 			G_PARAM_READWRITE |
462 			G_PARAM_CONSTRUCT_ONLY));
463 
464 	g_object_class_install_property (
465 		object_class,
466 		PROP_REMOTE_CONTENT,
467 		g_param_spec_object (
468 			"remote-content",
469 			"Remote Content",
470 			NULL,
471 			E_TYPE_MAIL_REMOTE_CONTENT,
472 			G_PARAM_READWRITE |
473 			G_PARAM_CONSTRUCT_ONLY));
474 }
475 
476 static void
e_mail_printer_init(EMailPrinter * printer)477 e_mail_printer_init (EMailPrinter *printer)
478 {
479 	printer->priv = E_MAIL_PRINTER_GET_PRIVATE (printer);
480 
481 	printer->priv->formatter = e_mail_formatter_print_new ();
482 	printer->priv->mode = E_MAIL_FORMATTER_MODE_PRINTING;
483 }
484 
485 EMailPrinter *
e_mail_printer_new(EMailPartList * part_list,EMailRemoteContent * remote_content)486 e_mail_printer_new (EMailPartList *part_list,
487 		    EMailRemoteContent *remote_content)
488 {
489 	g_return_val_if_fail (E_IS_MAIL_PART_LIST (part_list), NULL);
490 
491 	return g_object_new (E_TYPE_MAIL_PRINTER,
492 		"part-list", part_list,
493 		"remote-content", remote_content,
494 		NULL);
495 }
496 
497 EMailPartList *
e_mail_printer_ref_part_list(EMailPrinter * printer)498 e_mail_printer_ref_part_list (EMailPrinter *printer)
499 {
500 	g_return_val_if_fail (E_IS_MAIL_PRINTER (printer), NULL);
501 
502 	return g_object_ref (printer->priv->part_list);
503 }
504 
505 EMailRemoteContent *
e_mail_printer_ref_remote_content(EMailPrinter * printer)506 e_mail_printer_ref_remote_content (EMailPrinter *printer)
507 {
508 	g_return_val_if_fail (E_IS_MAIL_PRINTER (printer), NULL);
509 
510 	if (!printer->priv->remote_content)
511 		return NULL;
512 
513 	return g_object_ref (printer->priv->remote_content);
514 }
515 
516 void
e_mail_printer_set_mode(EMailPrinter * printer,EMailFormatterMode mode)517 e_mail_printer_set_mode (EMailPrinter *printer,
518 			 EMailFormatterMode mode)
519 {
520 	g_return_if_fail (E_IS_MAIL_PRINTER (printer));
521 
522 	printer->priv->mode = mode;
523 }
524 
525 EMailFormatterMode
e_mail_printer_get_mode(EMailPrinter * printer)526 e_mail_printer_get_mode (EMailPrinter *printer)
527 {
528 	g_return_val_if_fail (E_IS_MAIL_PRINTER (printer), E_MAIL_FORMATTER_MODE_PRINTING);
529 
530 	return printer->priv->mode;
531 }
532 
533 void
e_mail_printer_print(EMailPrinter * printer,GtkPrintOperationAction action,EMailFormatter * formatter,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)534 e_mail_printer_print (EMailPrinter *printer,
535                       GtkPrintOperationAction action, /* unused */
536                       EMailFormatter *formatter,
537                       GCancellable *cancellable,
538                       GAsyncReadyCallback callback,
539                       gpointer user_data)
540 {
541 	GTask *task;
542 	AsyncContext *async_context;
543 	WebKitWebView *web_view;
544 	EMailPartList *part_list;
545 	CamelFolder *folder;
546 	const gchar *message_uid;
547 	const gchar *charset = NULL;
548 	const gchar *default_charset = NULL;
549 	gchar *mail_uri;
550 	gulong handler_id;
551 
552 	g_return_if_fail (E_IS_MAIL_PRINTER (printer));
553 	/* EMailFormatter can be NULL. */
554 
555 	async_context = g_slice_new0 (AsyncContext);
556 	async_context->print_result = GTK_PRINT_OPERATION_RESULT_IN_PROGRESS;
557 	async_context->error = NULL;
558 
559 	part_list = e_mail_printer_ref_part_list (printer);
560 	folder = e_mail_part_list_get_folder (part_list);
561 	message_uid = e_mail_part_list_get_message_uid (part_list);
562 
563 	if (formatter != NULL) {
564 		charset =
565 			e_mail_formatter_get_charset (formatter);
566 		default_charset =
567 			e_mail_formatter_get_default_charset (formatter);
568 	}
569 
570 	if (charset == NULL)
571 		charset = "";
572 	if (default_charset == NULL)
573 		default_charset = "";
574 
575 	task = g_task_new (printer, cancellable, callback, user_data);
576 
577 	web_view = mail_printer_new_web_view (charset, default_charset, e_mail_printer_get_mode (printer));
578 	e_mail_display_set_part_list (E_MAIL_DISPLAY (web_view), part_list);
579 
580 	async_context->web_view = g_object_ref_sink (web_view);
581 
582 	handler_id = g_signal_connect_data (
583 		web_view, "load-changed",
584 		G_CALLBACK (mail_printer_load_changed_cb),
585 		g_object_ref (task),
586 		(GClosureNotify) g_object_unref, 0);
587 	async_context->load_status_handler_id = handler_id;
588 	g_task_set_task_data (task, async_context, (GDestroyNotify) async_context_free);
589 
590 	mail_uri = e_mail_part_build_uri (
591 		folder, message_uid,
592 		"__evo-load-image", G_TYPE_BOOLEAN, TRUE,
593 		"mode", G_TYPE_INT, e_mail_printer_get_mode (printer),
594 		"formatter_default_charset", G_TYPE_STRING, default_charset,
595 		"formatter_charset", G_TYPE_STRING, charset,
596 		NULL);
597 
598 	webkit_web_view_load_uri (web_view, mail_uri);
599 
600 	g_free (mail_uri);
601 	g_object_unref (part_list);
602 }
603 
604 GtkPrintOperationResult
e_mail_printer_print_finish(EMailPrinter * printer,GAsyncResult * result,GError ** error)605 e_mail_printer_print_finish (EMailPrinter *printer,
606                              GAsyncResult *result,
607                              GError **error)
608 {
609 	GTask *task;
610 	GtkPrintOperationResult print_result;
611 	AsyncContext *async_context;
612 
613 	g_return_val_if_fail (g_task_is_valid (result, printer), GTK_PRINT_OPERATION_RESULT_ERROR);
614 
615 	task = G_TASK (result);
616 	async_context = g_task_get_task_data (task);
617 	if (!g_task_propagate_boolean (task, error))
618 		return GTK_PRINT_OPERATION_RESULT_ERROR;
619 
620 	g_return_val_if_fail (async_context != NULL, GTK_PRINT_OPERATION_RESULT_ERROR);
621 
622 	print_result = async_context->print_result;
623 	g_warn_if_fail (print_result != GTK_PRINT_OPERATION_RESULT_ERROR);
624 
625 	return print_result;
626 }
627 
628 const gchar *
e_mail_printer_get_export_filename(EMailPrinter * printer)629 e_mail_printer_get_export_filename (EMailPrinter *printer)
630 {
631 	g_return_val_if_fail (E_IS_MAIL_PRINTER (printer), NULL);
632 
633 	return printer->priv->export_filename;
634 }
635 
636 void
e_mail_printer_set_export_filename(EMailPrinter * printer,const gchar * filename)637 e_mail_printer_set_export_filename (EMailPrinter *printer,
638                                     const gchar *filename)
639 {
640 	g_return_if_fail (E_IS_MAIL_PRINTER (printer));
641 
642 	g_free (printer->priv->export_filename);
643 	printer->priv->export_filename = g_strdup (filename);
644 }
645