1 /* this file is part of atril, a mate document viewer
2  *
3  *  Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
4  *  Copyright (C) 2005 Red Hat, Inc
5  *
6  * Atril is free software; you can redistribute it and/or modify it
7  * 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  * Atril is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include <config.h>
22 
23 #include "ev-jobs.h"
24 #include "ev-document-thumbnails.h"
25 #include "ev-document-links.h"
26 #include "ev-document-images.h"
27 #include "ev-job-scheduler.h"
28 #include "ev-document-forms.h"
29 #include "ev-file-exporter.h"
30 #include "ev-document-factory.h"
31 #include "ev-document-misc.h"
32 #include "ev-file-helpers.h"
33 #include "ev-document-fonts.h"
34 #include "ev-document-security.h"
35 #include "ev-document-find.h"
36 #include "ev-document-layers.h"
37 #include "ev-document-print.h"
38 #include "ev-document-annotations.h"
39 #include "ev-document-attachments.h"
40 #include "ev-document-text.h"
41 #include "ev-debug.h"
42 
43 #include <gtk/gtk.h>
44 #if ENABLE_EPUB
45 #include <webkit2/webkit2.h>
46 #endif
47 #include <errno.h>
48 #include <glib/gstdio.h>
49 #include <glib/gi18n-lib.h>
50 #include <unistd.h>
51 
52 enum {
53 	CANCELLED,
54 	FINISHED,
55 	LAST_SIGNAL
56 };
57 
58 enum {
59 	FONTS_UPDATED,
60 	FONTS_LAST_SIGNAL
61 };
62 
63 enum {
64 	FIND_UPDATED,
65 	FIND_LAST_SIGNAL
66 };
67 
68 static guint job_signals[LAST_SIGNAL] = { 0 };
69 static guint job_fonts_signals[FONTS_LAST_SIGNAL] = { 0 };
70 static guint job_find_signals[FIND_LAST_SIGNAL] = { 0 };
71 
G_DEFINE_ABSTRACT_TYPE(EvJob,ev_job,G_TYPE_OBJECT)72 G_DEFINE_ABSTRACT_TYPE (EvJob, ev_job, G_TYPE_OBJECT)
73 G_DEFINE_TYPE (EvJobLinks, ev_job_links, EV_TYPE_JOB)
74 G_DEFINE_TYPE (EvJobAttachments, ev_job_attachments, EV_TYPE_JOB)
75 G_DEFINE_TYPE (EvJobAnnots, ev_job_annots, EV_TYPE_JOB)
76 G_DEFINE_TYPE (EvJobRender, ev_job_render, EV_TYPE_JOB)
77 G_DEFINE_TYPE (EvJobPageData, ev_job_page_data, EV_TYPE_JOB)
78 G_DEFINE_TYPE (EvJobThumbnail, ev_job_thumbnail, EV_TYPE_JOB)
79 G_DEFINE_TYPE (EvJobFonts, ev_job_fonts, EV_TYPE_JOB)
80 G_DEFINE_TYPE (EvJobLoad, ev_job_load, EV_TYPE_JOB)
81 G_DEFINE_TYPE (EvJobSave, ev_job_save, EV_TYPE_JOB)
82 G_DEFINE_TYPE (EvJobFind, ev_job_find, EV_TYPE_JOB)
83 G_DEFINE_TYPE (EvJobLayers, ev_job_layers, EV_TYPE_JOB)
84 G_DEFINE_TYPE (EvJobExport, ev_job_export, EV_TYPE_JOB)
85 G_DEFINE_TYPE (EvJobPrint, ev_job_print, EV_TYPE_JOB)
86 
87 /* EvJob */
88 static void
89 ev_job_init (EvJob *job)
90 {
91 	job->cancellable = g_cancellable_new ();
92 }
93 
94 static void
ev_job_dispose(GObject * object)95 ev_job_dispose (GObject *object)
96 {
97 	EvJob *job;
98 
99 	job = EV_JOB (object);
100 
101 	if (job->document) {
102 		g_object_unref (job->document);
103 		job->document = NULL;
104 	}
105 
106 	if (job->cancellable) {
107 		g_object_unref (job->cancellable);
108 		job->cancellable = NULL;
109 	}
110 
111 	if (job->error) {
112 		g_error_free (job->error);
113 		job->error = NULL;
114 	}
115 
116 	(* G_OBJECT_CLASS (ev_job_parent_class)->dispose) (object);
117 }
118 
119 static void
ev_job_class_init(EvJobClass * class)120 ev_job_class_init (EvJobClass *class)
121 {
122 	GObjectClass *oclass;
123 
124 	oclass = G_OBJECT_CLASS (class);
125 
126 	oclass->dispose = ev_job_dispose;
127 
128 	job_signals[CANCELLED] =
129 		g_signal_new ("cancelled",
130 			      EV_TYPE_JOB,
131 			      G_SIGNAL_RUN_LAST,
132 			      G_STRUCT_OFFSET (EvJobClass, cancelled),
133 			      NULL, NULL,
134 			      g_cclosure_marshal_VOID__VOID,
135 			      G_TYPE_NONE, 0);
136 	job_signals [FINISHED] =
137 		g_signal_new ("finished",
138 			      EV_TYPE_JOB,
139 			      G_SIGNAL_RUN_FIRST,
140 			      G_STRUCT_OFFSET (EvJobClass, finished),
141 			      NULL, NULL,
142 			      g_cclosure_marshal_VOID__VOID,
143 			      G_TYPE_NONE, 0);
144 }
145 
146 static gboolean
emit_finished(EvJob * job)147 emit_finished (EvJob *job)
148 {
149 	ev_debug_message (DEBUG_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
150 
151 	job->idle_finished_id = 0;
152 
153 	if (job->cancelled) {
154 		ev_debug_message (DEBUG_JOBS, "%s (%p) job was cancelled, do not emit finished", EV_GET_TYPE_NAME (job), job);
155 	} else {
156 		ev_profiler_stop (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
157 		g_signal_emit (job, job_signals[FINISHED], 0);
158 	}
159 
160 	return FALSE;
161 }
162 
163 static void
ev_job_emit_finished(EvJob * job)164 ev_job_emit_finished (EvJob *job)
165 {
166 	ev_debug_message (DEBUG_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
167 
168 	if (g_cancellable_is_cancelled (job->cancellable)) {
169 		ev_debug_message (DEBUG_JOBS, "%s (%p) job was cancelled, returning", EV_GET_TYPE_NAME (job), job);
170 		return;
171 	}
172 
173 	job->finished = TRUE;
174 
175 	if (job->run_mode == EV_JOB_RUN_THREAD) {
176 		job->idle_finished_id =
177 			g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
178 					 (GSourceFunc)emit_finished,
179 					 g_object_ref (job),
180 					 (GDestroyNotify)g_object_unref);
181 	} else {
182 		ev_profiler_stop (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
183 		g_signal_emit (job, job_signals[FINISHED], 0);
184 	}
185 }
186 
187 gboolean
ev_job_run(EvJob * job)188 ev_job_run (EvJob *job)
189 {
190 	EvJobClass *class = EV_JOB_GET_CLASS (job);
191 
192 	return class->  run (job);
193 }
194 
195 void
ev_job_cancel(EvJob * job)196 ev_job_cancel (EvJob *job)
197 {
198 	if (job->cancelled)
199 		return;
200 
201 	ev_debug_message (DEBUG_JOBS, "job %s (%p) cancelled", EV_GET_TYPE_NAME (job), job);
202 	ev_profiler_stop (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
203 
204 	/* This should never be called from a thread */
205 	job->cancelled = TRUE;
206 	g_cancellable_cancel (job->cancellable);
207 
208         if (job->finished && job->idle_finished_id == 0)
209                 return;
210 
211 	g_signal_emit (job, job_signals[CANCELLED], 0);
212 }
213 
214 void
ev_job_failed(EvJob * job,GQuark domain,gint code,const gchar * format,...)215 ev_job_failed (EvJob       *job,
216 	       GQuark       domain,
217 	       gint         code,
218 	       const gchar *format,
219 	       ...)
220 {
221 	va_list args;
222 	gchar  *message;
223 
224 	if (job->failed || job->finished)
225 		return;
226 
227 	ev_debug_message (DEBUG_JOBS, "job %s (%p) failed", EV_GET_TYPE_NAME (job), job);
228 
229 	job->failed = TRUE;
230 
231 	va_start (args, format);
232 	message = g_strdup_vprintf (format, args);
233 	va_end (args);
234 
235 	job->error = g_error_new_literal (domain, code, message);
236 	g_free (message);
237 
238 	ev_job_emit_finished (job);
239 }
240 
241 /**
242  * ev_job_failed_from_error:
243  * @job: an #EvJob
244  * @error: a #GError
245  *
246  * Rename to: ev_job_failed
247  */
248 void
ev_job_failed_from_error(EvJob * job,GError * error)249 ev_job_failed_from_error (EvJob  *job,
250 			  GError *error)
251 {
252 	if (job->failed || job->finished)
253 		return;
254 
255 	ev_debug_message (DEBUG_JOBS, "job %s (%p) failed", EV_GET_TYPE_NAME (job), job);
256 
257 	job->failed = TRUE;
258 	job->error = g_error_copy (error);
259 
260 	ev_job_emit_finished (job);
261 }
262 
263 void
ev_job_succeeded(EvJob * job)264 ev_job_succeeded (EvJob *job)
265 {
266 	if (job->finished)
267 		return;
268 
269 	ev_debug_message (DEBUG_JOBS, "job %s (%p) succeeded", EV_GET_TYPE_NAME (job), job);
270 
271 	job->failed = FALSE;
272 	ev_job_emit_finished (job);
273 }
274 
275 gboolean
ev_job_is_finished(EvJob * job)276 ev_job_is_finished (EvJob *job)
277 {
278 	return job->finished;
279 }
280 
281 gboolean
ev_job_is_failed(EvJob * job)282 ev_job_is_failed (EvJob *job)
283 {
284 	return job->failed;
285 }
286 
287 EvJobRunMode
ev_job_get_run_mode(EvJob * job)288 ev_job_get_run_mode (EvJob *job)
289 {
290 	return job->run_mode;
291 }
292 
293 void
ev_job_set_run_mode(EvJob * job,EvJobRunMode run_mode)294 ev_job_set_run_mode (EvJob       *job,
295 		     EvJobRunMode run_mode)
296 {
297 	job->run_mode = run_mode;
298 }
299 
300 /* EvJobLinks */
301 static void
ev_job_links_init(EvJobLinks * job)302 ev_job_links_init (EvJobLinks *job)
303 {
304 	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
305 }
306 
307 static void
ev_job_links_dispose(GObject * object)308 ev_job_links_dispose (GObject *object)
309 {
310 	EvJobLinks *job;
311 
312 	ev_debug_message (DEBUG_JOBS, NULL);
313 
314 	job = EV_JOB_LINKS (object);
315 
316 	if (job->model) {
317 		g_object_unref (job->model);
318 		job->model = NULL;
319 	}
320 
321 	(* G_OBJECT_CLASS (ev_job_links_parent_class)->dispose) (object);
322 }
323 
324 static gboolean
fill_page_labels(GtkTreeModel * tree_model,GtkTreePath * path,GtkTreeIter * iter,EvJob * job)325 fill_page_labels (GtkTreeModel   *tree_model,
326 		  GtkTreePath    *path,
327 		  GtkTreeIter    *iter,
328 		  EvJob          *job)
329 {
330 	EvDocumentLinks *document_links;
331 	EvLink          *link;
332 	gchar           *page_label;
333 
334 	gtk_tree_model_get (tree_model, iter,
335 			    EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
336 			    -1);
337 
338 	if (!link)
339 		return FALSE;
340 
341 	document_links = EV_DOCUMENT_LINKS (job->document);
342 	page_label = ev_document_links_get_link_page_label (document_links, link);
343 	if (!page_label)
344 		return FALSE;
345 
346 	gtk_tree_store_set (GTK_TREE_STORE (tree_model), iter,
347 			    EV_DOCUMENT_LINKS_COLUMN_PAGE_LABEL, page_label,
348 			    -1);
349 
350 	g_free (page_label);
351 	g_object_unref (link);
352 
353 	return FALSE;
354 }
355 
356 static gboolean
ev_job_links_run(EvJob * job)357 ev_job_links_run (EvJob *job)
358 {
359 	EvJobLinks *job_links = EV_JOB_LINKS (job);
360 
361 	ev_debug_message (DEBUG_JOBS, NULL);
362 	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
363 
364 	ev_document_doc_mutex_lock ();
365 	job_links->model = ev_document_links_get_links_model (EV_DOCUMENT_LINKS (job->document));
366 	ev_document_doc_mutex_unlock ();
367 
368 	gtk_tree_model_foreach (job_links->model, (GtkTreeModelForeachFunc)fill_page_labels, job);
369 
370 	ev_job_succeeded (job);
371 
372 	return FALSE;
373 }
374 
375 static void
ev_job_links_class_init(EvJobLinksClass * class)376 ev_job_links_class_init (EvJobLinksClass *class)
377 {
378 	GObjectClass *oclass = G_OBJECT_CLASS (class);
379 	EvJobClass   *job_class = EV_JOB_CLASS (class);
380 
381 	oclass->dispose = ev_job_links_dispose;
382 	job_class->run = ev_job_links_run;
383 }
384 
385 EvJob *
ev_job_links_new(EvDocument * document)386 ev_job_links_new (EvDocument *document)
387 {
388 	EvJob *job;
389 
390 	ev_debug_message (DEBUG_JOBS, NULL);
391 
392 	job = g_object_new (EV_TYPE_JOB_LINKS, NULL);
393 	job->document = g_object_ref (document);
394 
395 	return job;
396 }
397 
398 /* EvJobAttachments */
399 static void
ev_job_attachments_init(EvJobAttachments * job)400 ev_job_attachments_init (EvJobAttachments *job)
401 {
402 	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
403 }
404 
405 static void
ev_job_attachments_dispose(GObject * object)406 ev_job_attachments_dispose (GObject *object)
407 {
408 	EvJobAttachments *job;
409 
410 	ev_debug_message (DEBUG_JOBS, NULL);
411 
412 	job = EV_JOB_ATTACHMENTS (object);
413 
414 	if (job->attachments) {
415 		g_list_foreach (job->attachments, (GFunc)g_object_unref, NULL);
416 		g_list_free (job->attachments);
417 		job->attachments = NULL;
418 	}
419 
420 	(* G_OBJECT_CLASS (ev_job_attachments_parent_class)->dispose) (object);
421 }
422 
423 static gboolean
ev_job_attachments_run(EvJob * job)424 ev_job_attachments_run (EvJob *job)
425 {
426 	EvJobAttachments *job_attachments = EV_JOB_ATTACHMENTS (job);
427 
428 	ev_debug_message (DEBUG_JOBS, NULL);
429 	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
430 
431 	ev_document_doc_mutex_lock ();
432 	job_attachments->attachments =
433 		ev_document_attachments_get_attachments (EV_DOCUMENT_ATTACHMENTS (job->document));
434 	ev_document_doc_mutex_unlock ();
435 
436 	ev_job_succeeded (job);
437 
438 	return FALSE;
439 }
440 
441 static void
ev_job_attachments_class_init(EvJobAttachmentsClass * class)442 ev_job_attachments_class_init (EvJobAttachmentsClass *class)
443 {
444 	GObjectClass *oclass = G_OBJECT_CLASS (class);
445 	EvJobClass   *job_class = EV_JOB_CLASS (class);
446 
447 	oclass->dispose = ev_job_attachments_dispose;
448 	job_class->run = ev_job_attachments_run;
449 }
450 
451 EvJob *
ev_job_attachments_new(EvDocument * document)452 ev_job_attachments_new (EvDocument *document)
453 {
454 	EvJob *job;
455 
456 	ev_debug_message (DEBUG_JOBS, NULL);
457 
458 	job = g_object_new (EV_TYPE_JOB_ATTACHMENTS, NULL);
459 	job->document = g_object_ref (document);
460 
461 	return job;
462 }
463 
464 /* EvJobAnnots */
465 static void
ev_job_annots_init(EvJobAnnots * job)466 ev_job_annots_init (EvJobAnnots *job)
467 {
468 	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
469 }
470 
471 static void
ev_job_annots_dispose(GObject * object)472 ev_job_annots_dispose (GObject *object)
473 {
474 	EvJobAnnots *job;
475 
476 	ev_debug_message (DEBUG_JOBS, NULL);
477 
478 	job = EV_JOB_ANNOTS (object);
479 
480 	if (job->annots) {
481 		g_list_foreach (job->annots, (GFunc)ev_mapping_list_unref, NULL);
482 		g_list_free (job->annots);
483 		job->annots = NULL;
484 	}
485 
486 	G_OBJECT_CLASS (ev_job_annots_parent_class)->dispose (object);
487 }
488 
489 static gboolean
ev_job_annots_run(EvJob * job)490 ev_job_annots_run (EvJob *job)
491 {
492 	EvJobAnnots *job_annots = EV_JOB_ANNOTS (job);
493 	gint         i;
494 
495 	ev_debug_message (DEBUG_JOBS, NULL);
496 	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
497 
498 	ev_document_doc_mutex_lock ();
499 	for (i = 0; i < ev_document_get_n_pages (job->document); i++) {
500 		EvMappingList *mapping_list;
501 		EvPage        *page;
502 
503 		page = ev_document_get_page (job->document, i);
504 		mapping_list = ev_document_annotations_get_annotations (EV_DOCUMENT_ANNOTATIONS (job->document),
505 									page);
506 		g_object_unref (page);
507 
508 		if (mapping_list)
509 			job_annots->annots = g_list_prepend (job_annots->annots, mapping_list);
510 	}
511 	ev_document_doc_mutex_unlock ();
512 
513 	job_annots->annots = g_list_reverse (job_annots->annots);
514 
515 	ev_job_succeeded (job);
516 
517 	return FALSE;
518 }
519 
520 static void
ev_job_annots_class_init(EvJobAnnotsClass * class)521 ev_job_annots_class_init (EvJobAnnotsClass *class)
522 {
523 	GObjectClass *oclass = G_OBJECT_CLASS (class);
524 	EvJobClass   *job_class = EV_JOB_CLASS (class);
525 
526 	oclass->dispose = ev_job_annots_dispose;
527 	job_class->run = ev_job_annots_run;
528 }
529 
530 EvJob *
ev_job_annots_new(EvDocument * document)531 ev_job_annots_new (EvDocument *document)
532 {
533 	EvJob *job;
534 
535 	ev_debug_message (DEBUG_JOBS, NULL);
536 
537 	job = g_object_new (EV_TYPE_JOB_ANNOTS, NULL);
538 	job->document = g_object_ref (document);
539 
540 	return job;
541 }
542 
543 /* EvJobRender */
544 static void
ev_job_render_init(EvJobRender * job)545 ev_job_render_init (EvJobRender *job)
546 {
547 	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
548 }
549 
550 static void
ev_job_render_dispose(GObject * object)551 ev_job_render_dispose (GObject *object)
552 {
553 	EvJobRender *job;
554 
555 	job = EV_JOB_RENDER (object);
556 
557 	ev_debug_message (DEBUG_JOBS, "page: %d (%p)", job->page, job);
558 
559 	if (job->surface) {
560 		cairo_surface_destroy (job->surface);
561 		job->surface = NULL;
562 	}
563 
564 	if (job->selection) {
565 		cairo_surface_destroy (job->selection);
566 		job->selection = NULL;
567 	}
568 
569 	if (job->selection_region) {
570 		cairo_region_destroy (job->selection_region);
571 		job->selection_region = NULL;
572 	}
573 
574 	(* G_OBJECT_CLASS (ev_job_render_parent_class)->dispose) (object);
575 }
576 
577 static gboolean
ev_job_render_run(EvJob * job)578 ev_job_render_run (EvJob *job)
579 {
580 	EvJobRender     *job_render = EV_JOB_RENDER (job);
581 	EvPage          *ev_page;
582 	EvRenderContext *rc;
583 
584 	ev_debug_message (DEBUG_JOBS, "page: %d (%p)", job_render->page, job);
585 	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
586 
587 	ev_document_doc_mutex_lock ();
588 
589 	ev_profiler_start (EV_PROFILE_JOBS, "Rendering page %d", job_render->page);
590 
591 	ev_document_fc_mutex_lock ();
592 
593 	ev_page = ev_document_get_page (job->document, job_render->page);
594 
595 	if ( job->document->iswebdocument == TRUE )
596 	{
597 		return TRUE;
598 
599 		if (g_cancellable_is_cancelled (job->cancellable)) {
600 		ev_document_fc_mutex_unlock ();
601 		ev_document_doc_mutex_unlock ();
602 		g_object_unref (rc);
603 
604 		return FALSE;
605 		}
606 
607 		ev_document_fc_mutex_unlock ();
608 		ev_document_doc_mutex_unlock ();
609 		ev_job_succeeded (job);
610 		return FALSE;
611 	}
612 	rc = ev_render_context_new (ev_page, job_render->rotation, job_render->scale);
613 	g_object_unref (ev_page);
614 
615 	if ((job_render->surface = ev_document_render (job->document, rc)) == NULL) {
616 		ev_document_fc_mutex_unlock ();
617 		ev_document_doc_mutex_unlock ();
618 		g_object_unref (rc);
619 		ev_job_failed (job,
620 		               EV_DOCUMENT_ERROR,
621 		               EV_DOCUMENT_ERROR_INVALID,
622 		               _("Failed to render page %d"),
623 		               job_render->page);
624 		return FALSE;
625 	}
626 
627 	/* If job was cancelled during the page rendering,
628 	 * we return now, so that the thread is finished ASAP
629 	 */
630 	if (g_cancellable_is_cancelled (job->cancellable)) {
631 		ev_document_fc_mutex_unlock ();
632 		ev_document_doc_mutex_unlock ();
633 		g_object_unref (rc);
634 
635 		return FALSE;
636 	}
637 
638 	if (job_render->include_selection && EV_IS_SELECTION (job->document)) {
639 		ev_selection_render_selection (EV_SELECTION (job->document),
640 					       rc,
641 					       &(job_render->selection),
642 					       &(job_render->selection_points),
643 					       NULL,
644 					       job_render->selection_style,
645 					       &(job_render->text), &(job_render->base));
646 		job_render->selection_region =
647 			ev_selection_get_selection_region (EV_SELECTION (job->document),
648 							   rc,
649 							   job_render->selection_style,
650 							   &(job_render->selection_points));
651 	}
652 
653 	g_object_unref (rc);
654 
655 	ev_document_fc_mutex_unlock ();
656 	ev_document_doc_mutex_unlock ();
657 
658 	ev_job_succeeded (job);
659 
660 	return FALSE;
661 }
662 
663 static void
ev_job_render_class_init(EvJobRenderClass * class)664 ev_job_render_class_init (EvJobRenderClass *class)
665 {
666 	GObjectClass *oclass = G_OBJECT_CLASS (class);
667 	EvJobClass   *job_class = EV_JOB_CLASS (class);
668 
669 	oclass->dispose = ev_job_render_dispose;
670 	job_class->run = ev_job_render_run;
671 }
672 
673 EvJob *
ev_job_render_new(EvDocument * document,gint page,gint rotation,gdouble scale,gint width,gint height)674 ev_job_render_new (EvDocument   *document,
675 		   gint          page,
676 		   gint          rotation,
677 		   gdouble       scale,
678 		   gint          width,
679 		   gint          height)
680 {
681 	EvJobRender *job;
682 
683 	ev_debug_message (DEBUG_JOBS, "page: %d", page);
684 
685 	job = g_object_new (EV_TYPE_JOB_RENDER, NULL);
686 
687 	EV_JOB (job)->document = g_object_ref (document);
688 	job->page = page;
689 	job->rotation = rotation;
690 	job->scale = scale;
691 	job->target_width = width;
692 	job->target_height = height;
693 
694 	return EV_JOB (job);
695 }
696 
697 void
ev_job_render_set_selection_info(EvJobRender * job,EvRectangle * selection_points,EvSelectionStyle selection_style,GdkColor * text,GdkColor * base)698 ev_job_render_set_selection_info (EvJobRender     *job,
699 				  EvRectangle     *selection_points,
700 				  EvSelectionStyle selection_style,
701 				  GdkColor        *text,
702 				  GdkColor        *base)
703 {
704 	job->include_selection = TRUE;
705 
706 	job->selection_points = *selection_points;
707 	job->selection_style = selection_style;
708 	job->text = *text;
709 	job->base = *base;
710 }
711 
712 /* EvJobPageData */
713 static void
ev_job_page_data_init(EvJobPageData * job)714 ev_job_page_data_init (EvJobPageData *job)
715 {
716 	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
717 }
718 
719 static gboolean
ev_job_page_data_run(EvJob * job)720 ev_job_page_data_run (EvJob *job)
721 {
722 	EvJobPageData *job_pd = EV_JOB_PAGE_DATA (job);
723 	EvPage        *ev_page;
724 
725 	ev_debug_message (DEBUG_JOBS, "page: %d (%p)", job_pd->page, job);
726 	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
727 
728 	ev_document_doc_mutex_lock ();
729 	ev_page = ev_document_get_page (job->document, job_pd->page);
730 
731 	if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_TEXT_MAPPING) && EV_IS_DOCUMENT_TEXT (job->document))
732 		job_pd->text_mapping =
733 			ev_document_text_get_text_mapping (EV_DOCUMENT_TEXT (job->document), ev_page);
734 	if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_TEXT) && EV_IS_DOCUMENT_TEXT (job->document))
735 		job_pd->text =
736 			ev_document_text_get_text (EV_DOCUMENT_TEXT (job->document), ev_page);
737 	if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_TEXT_LAYOUT) && EV_IS_DOCUMENT_TEXT (job->document))
738 		ev_document_text_get_text_layout (EV_DOCUMENT_TEXT (job->document),
739 						  ev_page,
740 						  &(job_pd->text_layout),
741 						  &(job_pd->text_layout_length));
742 	if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_TEXT_ATTRS) && EV_IS_DOCUMENT_TEXT (job->document))
743 		job_pd ->text_attrs =
744 			ev_document_text_get_text_attrs (EV_DOCUMENT_TEXT (job->document),
745 							 ev_page);
746         if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_TEXT_LOG_ATTRS) && job_pd->text) {
747                 job_pd->text_log_attrs_length = g_utf8_strlen (job_pd->text, -1);
748                 job_pd->text_log_attrs = g_new0 (PangoLogAttr, job_pd->text_log_attrs_length + 1);
749 
750                 /* FIXME: We need API to get the language of the document */
751                 pango_get_log_attrs (job_pd->text, -1, -1, NULL, job_pd->text_log_attrs, job_pd->text_log_attrs_length + 1);
752         }
753 	if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_LINKS) && EV_IS_DOCUMENT_LINKS (job->document))
754 		job_pd->link_mapping =
755 			ev_document_links_get_links (EV_DOCUMENT_LINKS (job->document), ev_page);
756 	if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_FORMS) && EV_IS_DOCUMENT_FORMS (job->document))
757 		job_pd->form_field_mapping =
758 			ev_document_forms_get_form_fields (EV_DOCUMENT_FORMS (job->document),
759 							   ev_page);
760 	if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_IMAGES) && EV_IS_DOCUMENT_IMAGES (job->document))
761 		job_pd->image_mapping =
762 			ev_document_images_get_image_mapping (EV_DOCUMENT_IMAGES (job->document),
763 							      ev_page);
764 	if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_ANNOTS) && EV_IS_DOCUMENT_ANNOTATIONS (job->document))
765 		job_pd->annot_mapping =
766 			ev_document_annotations_get_annotations (EV_DOCUMENT_ANNOTATIONS (job->document),
767 								 ev_page);
768 	g_object_unref (ev_page);
769 	ev_document_doc_mutex_unlock ();
770 
771 	ev_job_succeeded (job);
772 
773 	return FALSE;
774 }
775 
776 static void
ev_job_page_data_class_init(EvJobPageDataClass * class)777 ev_job_page_data_class_init (EvJobPageDataClass *class)
778 {
779 	EvJobClass *job_class = EV_JOB_CLASS (class);
780 
781 	job_class->run = ev_job_page_data_run;
782 }
783 
784 EvJob *
ev_job_page_data_new(EvDocument * document,gint page,EvJobPageDataFlags flags)785 ev_job_page_data_new (EvDocument        *document,
786 		      gint               page,
787 		      EvJobPageDataFlags flags)
788 {
789 	EvJobPageData *job;
790 
791 	ev_debug_message (DEBUG_JOBS, "%d", page);
792 
793 	job = g_object_new (EV_TYPE_JOB_PAGE_DATA, NULL);
794 
795 	EV_JOB (job)->document = g_object_ref (document);
796 	job->page = page;
797 	job->flags = flags;
798 
799 	return EV_JOB (job);
800 }
801 
802 /* EvJobThumbnail */
803 static void
ev_job_thumbnail_init(EvJobThumbnail * job)804 ev_job_thumbnail_init (EvJobThumbnail *job)
805 {
806 		EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
807 }
808 
809 static void
ev_job_thumbnail_dispose(GObject * object)810 ev_job_thumbnail_dispose (GObject *object)
811 {
812 	EvJobThumbnail *job;
813 
814 	job = EV_JOB_THUMBNAIL (object);
815 
816 	ev_debug_message (DEBUG_JOBS, "%d (%p)", job->page, job);
817 
818 	if (job->thumbnail) {
819 		g_object_unref (job->thumbnail);
820 		job->thumbnail = NULL;
821 	}
822 
823 	(* G_OBJECT_CLASS (ev_job_thumbnail_parent_class)->dispose) (object);
824 }
825 
826 #if ENABLE_EPUB
827 static void
snapshot_callback(WebKitWebView * webview,GAsyncResult * results,EvJobThumbnail * job_thumb)828 snapshot_callback(WebKitWebView *webview,
829                   GAsyncResult  *results,
830                   EvJobThumbnail *job_thumb)
831 {
832 	GError *error = NULL;
833 
834 	ev_document_doc_mutex_lock ();
835 
836 	EvPage *page = ev_document_get_page (EV_JOB(job_thumb)->document, job_thumb->page);
837 	job_thumb->surface = webkit_web_view_get_snapshot_finish (webview,
838 	                                                          results,
839 	                                                          &error);
840 
841 	if (error) {
842 		g_warning ("Error retrieving a snapshot: %s", error->message);
843 	}
844 
845 	EvRenderContext *rc = ev_render_context_new (page, job_thumb->rotation, job_thumb->scale);
846 	EvPage *screenshotpage;
847 	screenshotpage = ev_page_new(job_thumb->page);
848 	screenshotpage->backend_page = (EvBackendPage)job_thumb->surface;
849 	screenshotpage->backend_destroy_func = (EvBackendPageDestroyFunc)cairo_surface_destroy;
850 	ev_render_context_set_page(rc,screenshotpage);
851 
852 	job_thumb->thumbnail = ev_document_thumbnails_get_thumbnail (EV_DOCUMENT_THUMBNAILS (EV_JOB(job_thumb)->document),
853 	                                                             rc, TRUE);
854 	g_object_unref(screenshotpage);
855 	g_object_unref(rc);
856 
857 	ev_document_doc_mutex_unlock ();
858 	ev_job_succeeded (EV_JOB(job_thumb));
859 
860 	gtk_widget_destroy (gtk_widget_get_toplevel (GTK_WIDGET (webview)));
861 }
862 
863 static void
web_thumbnail_get_screenshot_cb(WebKitWebView * webview,WebKitLoadEvent event,EvJobThumbnail * job_thumb)864 web_thumbnail_get_screenshot_cb (WebKitWebView  *webview,
865                                  WebKitLoadEvent event,
866                                  EvJobThumbnail *job_thumb)
867 {
868 	if (event != WEBKIT_LOAD_FINISHED || ev_job_is_failed (EV_JOB(job_thumb))) {
869 		return;
870 	}
871 
872 	webkit_web_view_get_snapshot (webview,
873 	                              WEBKIT_SNAPSHOT_REGION_VISIBLE,
874 	                              WEBKIT_SNAPSHOT_OPTIONS_NONE,
875 	                              NULL,
876 	                              (GAsyncReadyCallback)snapshot_callback,
877 	                              g_object_ref (job_thumb));
878 }
879 
880 static gboolean
webview_load_failed_cb(WebKitWebView * webview,WebKitLoadEvent event,gchar * failing_uri,gpointer error,EvJobThumbnail * job_thumb)881 webview_load_failed_cb (WebKitWebView  *webview,
882                         WebKitLoadEvent event,
883                         gchar          *failing_uri,
884                         gpointer        error,
885                         EvJobThumbnail *job_thumb)
886 {
887 	GError *e = (GError *) error;
888 	g_warning ("Error loading data from %s: %s", failing_uri, e->message);
889 	ev_job_failed_from_error (EV_JOB(job_thumb), e);
890 
891 	gtk_widget_destroy (gtk_widget_get_toplevel (GTK_WIDGET (webview)));
892 	return TRUE;
893 }
894 
895 #endif  /* ENABLE_EPUB */
896 
897 static gboolean
ev_job_thumbnail_run(EvJob * job)898 ev_job_thumbnail_run (EvJob *job)
899 {
900 	EvJobThumbnail  *job_thumb = EV_JOB_THUMBNAIL (job);
901 	EvRenderContext *rc;
902 	EvPage          *page;
903 	ev_debug_message (DEBUG_JOBS, "%d (%p)", job_thumb->page, job);
904 	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
905 
906 	if (job->document->iswebdocument) {
907 		/* Do not block the main loop */
908 		if (!ev_document_doc_mutex_trylock ())
909 			return TRUE;
910 	} else {
911 		ev_document_doc_mutex_lock ();
912 	}
913 
914 	page = ev_document_get_page (job->document, job_thumb->page);
915 	ev_document_doc_mutex_unlock ();
916 
917 	if (job->document->iswebdocument == TRUE ) {
918 		rc = ev_render_context_new (page, 0, job_thumb->scale);
919 	} else {
920 		rc = ev_render_context_new (page, job_thumb->rotation, job_thumb->scale);
921 	}
922 	g_object_unref (page);
923 
924 #if ENABLE_EPUB
925 	if (job->document->iswebdocument == TRUE) {
926 		GtkWidget *webview;
927 		GtkWidget *offscreenwindow;
928 
929 		webview = webkit_web_view_new ();
930 		offscreenwindow = gtk_offscreen_window_new ();
931 
932 		gtk_container_add (GTK_CONTAINER(offscreenwindow), GTK_WIDGET (webview));
933 		gtk_window_set_default_size (GTK_WINDOW(offscreenwindow), 800, 1080);
934 		gtk_widget_show_all (offscreenwindow);
935 
936 		g_signal_connect (WEBKIT_WEB_VIEW (webview), "load-changed",
937 		                  G_CALLBACK (web_thumbnail_get_screenshot_cb),
938 		                  g_object_ref (job_thumb));
939 		g_signal_connect (WEBKIT_WEB_VIEW(webview), "load-failed",
940 		                  G_CALLBACK(webview_load_failed_cb),
941 		                  g_object_ref (job_thumb));
942 		webkit_web_view_load_uri (WEBKIT_WEB_VIEW (webview), (gchar*) rc->page->backend_page);
943 	}
944 	else
945 #endif  /* ENABLE_EPUB */
946 	{
947 		ev_document_doc_mutex_lock ();
948 		job_thumb->thumbnail = ev_document_thumbnails_get_thumbnail (EV_DOCUMENT_THUMBNAILS (job->document),
949 		                                                             rc, TRUE);
950 		ev_document_doc_mutex_unlock ();
951 		ev_job_succeeded (job);
952 	}
953 	g_object_unref (rc);
954 
955 	return FALSE;
956 }
957 
958 static void
ev_job_thumbnail_class_init(EvJobThumbnailClass * class)959 ev_job_thumbnail_class_init (EvJobThumbnailClass *class)
960 {
961 	GObjectClass *oclass = G_OBJECT_CLASS (class);
962 	EvJobClass   *job_class = EV_JOB_CLASS (class);
963 
964 	oclass->dispose = ev_job_thumbnail_dispose;
965 	job_class->run = ev_job_thumbnail_run;
966 }
967 
968 EvJob *
ev_job_thumbnail_new(EvDocument * document,gint page,gint rotation,gdouble scale)969 ev_job_thumbnail_new (EvDocument *document,
970 		      gint        page,
971 		      gint        rotation,
972 		      gdouble     scale)
973 {
974 	EvJobThumbnail *job;
975 
976 	ev_debug_message (DEBUG_JOBS, "%d", page);
977 
978 	job = g_object_new (EV_TYPE_JOB_THUMBNAIL, NULL);
979 
980 	EV_JOB (job)->document = g_object_ref (document);
981 	job->page = page;
982 	job->rotation = rotation;
983 	job->scale = scale;
984 
985 	return EV_JOB (job);
986 }
987 
988 /* EvJobFonts */
989 static void
ev_job_fonts_init(EvJobFonts * job)990 ev_job_fonts_init (EvJobFonts *job)
991 {
992 	EV_JOB (job)->run_mode = EV_JOB_RUN_MAIN_LOOP;
993 }
994 
995 static gboolean
ev_job_fonts_run(EvJob * job)996 ev_job_fonts_run (EvJob *job)
997 {
998 	EvJobFonts      *job_fonts = EV_JOB_FONTS (job);
999 	EvDocumentFonts *fonts = EV_DOCUMENT_FONTS (job->document);
1000 
1001 	ev_debug_message (DEBUG_JOBS, NULL);
1002 
1003 	/* Do not block the main loop */
1004 	if (!ev_document_doc_mutex_trylock ())
1005 		return TRUE;
1006 
1007 	if (!ev_document_fc_mutex_trylock ()) {
1008 		ev_document_doc_mutex_unlock ();
1009 		return TRUE;
1010 	}
1011 
1012 #ifdef EV_ENABLE_DEBUG
1013 	/* We use the #ifdef in this case because of the if */
1014 	if (ev_document_fonts_get_progress (fonts) == 0)
1015 		ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
1016 #endif
1017 
1018 	job_fonts->scan_completed = !ev_document_fonts_scan (fonts, 20);
1019 	g_signal_emit (job_fonts, job_fonts_signals[FONTS_UPDATED], 0,
1020 		       ev_document_fonts_get_progress (fonts));
1021 
1022 	ev_document_fc_mutex_unlock ();
1023 	ev_document_doc_mutex_unlock ();
1024 
1025 	if (job_fonts->scan_completed)
1026 		ev_job_succeeded (job);
1027 
1028 	return !job_fonts->scan_completed;
1029 }
1030 
1031 static void
ev_job_fonts_class_init(EvJobFontsClass * class)1032 ev_job_fonts_class_init (EvJobFontsClass *class)
1033 {
1034 	EvJobClass *job_class = EV_JOB_CLASS (class);
1035 
1036 	job_class->run = ev_job_fonts_run;
1037 
1038 	job_fonts_signals[FONTS_UPDATED] =
1039 		g_signal_new ("updated",
1040 			      EV_TYPE_JOB_FONTS,
1041 			      G_SIGNAL_RUN_LAST,
1042 			      G_STRUCT_OFFSET (EvJobFontsClass, updated),
1043 			      NULL, NULL,
1044 			      g_cclosure_marshal_VOID__DOUBLE,
1045 			      G_TYPE_NONE,
1046 			      1, G_TYPE_DOUBLE);
1047 }
1048 
1049 EvJob *
ev_job_fonts_new(EvDocument * document)1050 ev_job_fonts_new (EvDocument *document)
1051 {
1052 	EvJobFonts *job;
1053 
1054 	ev_debug_message (DEBUG_JOBS, NULL);
1055 
1056 	job = g_object_new (EV_TYPE_JOB_FONTS, NULL);
1057 
1058 	EV_JOB (job)->document = g_object_ref (document);
1059 
1060 	return EV_JOB (job);
1061 }
1062 
1063 /* EvJobLoad */
1064 static void
ev_job_load_init(EvJobLoad * job)1065 ev_job_load_init (EvJobLoad *job)
1066 {
1067 	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
1068 }
1069 
1070 static void
ev_job_load_dispose(GObject * object)1071 ev_job_load_dispose (GObject *object)
1072 {
1073 	EvJobLoad *job = EV_JOB_LOAD (object);
1074 
1075 	ev_debug_message (DEBUG_JOBS, "%s", job->uri);
1076 
1077 	if (job->uri) {
1078 		g_free (job->uri);
1079 		job->uri = NULL;
1080 	}
1081 
1082 	if (job->password) {
1083 		g_free (job->password);
1084 		job->password = NULL;
1085 	}
1086 
1087 	(* G_OBJECT_CLASS (ev_job_load_parent_class)->dispose) (object);
1088 }
1089 
1090 static gboolean
ev_job_load_run(EvJob * job)1091 ev_job_load_run (EvJob *job)
1092 {
1093 	EvJobLoad *job_load = EV_JOB_LOAD (job);
1094 	GError    *error = NULL;
1095 
1096 	ev_debug_message (DEBUG_JOBS, "%s", job_load->uri);
1097 	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
1098 
1099 	ev_document_fc_mutex_lock ();
1100 
1101 	/* This job may already have a document even if the job didn't complete
1102 	   because, e.g., a password is required - if so, just reload rather than
1103 	   creating a new instance */
1104 	if (job->document) {
1105 		const gchar *uncompressed_uri;
1106 
1107 		if (job_load->password) {
1108 			ev_document_security_set_password (EV_DOCUMENT_SECURITY (job->document),
1109 							   job_load->password);
1110 		}
1111 
1112 		job->failed = FALSE;
1113 		job->finished = FALSE;
1114 		g_clear_error (&job->error);
1115 
1116 		uncompressed_uri = g_object_get_data (G_OBJECT (job->document),
1117 						      "uri-uncompressed");
1118 		ev_document_load (job->document,
1119 				  uncompressed_uri ? uncompressed_uri : job_load->uri,
1120 				  &error);
1121 	} else {
1122 		job->document = ev_document_factory_get_document (job_load->uri,
1123 								  &error);
1124 	}
1125 
1126 	ev_document_fc_mutex_unlock ();
1127 
1128 	if (error) {
1129 		ev_job_failed_from_error (job, error);
1130 		g_error_free (error);
1131 	} else {
1132 		ev_job_succeeded (job);
1133 	}
1134 
1135 	return FALSE;
1136 }
1137 
1138 static void
ev_job_load_class_init(EvJobLoadClass * class)1139 ev_job_load_class_init (EvJobLoadClass *class)
1140 {
1141 	GObjectClass *oclass = G_OBJECT_CLASS (class);
1142 	EvJobClass   *job_class = EV_JOB_CLASS (class);
1143 
1144 	oclass->dispose = ev_job_load_dispose;
1145 	job_class->run = ev_job_load_run;
1146 }
1147 
1148 EvJob *
ev_job_load_new(const gchar * uri)1149 ev_job_load_new (const gchar *uri)
1150 {
1151 	EvJobLoad *job;
1152 
1153 	ev_debug_message (DEBUG_JOBS, "%s", uri);
1154 
1155 	job = g_object_new (EV_TYPE_JOB_LOAD, NULL);
1156 	job->uri = g_strdup (uri);
1157 
1158 	return EV_JOB (job);
1159 }
1160 
1161 void
ev_job_load_set_uri(EvJobLoad * job,const gchar * uri)1162 ev_job_load_set_uri (EvJobLoad *job, const gchar *uri)
1163 {
1164 	ev_debug_message (DEBUG_JOBS, "%s", uri);
1165 
1166 	if (job->uri)
1167 		g_free (job->uri);
1168 	job->uri = g_strdup (uri);
1169 }
1170 
1171 void
ev_job_load_set_password(EvJobLoad * job,const gchar * password)1172 ev_job_load_set_password (EvJobLoad *job, const gchar *password)
1173 {
1174 	ev_debug_message (DEBUG_JOBS, NULL);
1175 
1176 	if (job->password)
1177 		g_free (job->password);
1178 	job->password = password ? g_strdup (password) : NULL;
1179 }
1180 
1181 /* EvJobSave */
1182 static void
ev_job_save_init(EvJobSave * job)1183 ev_job_save_init (EvJobSave *job)
1184 {
1185 	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
1186 }
1187 
1188 static void
ev_job_save_dispose(GObject * object)1189 ev_job_save_dispose (GObject *object)
1190 {
1191 	EvJobSave *job = EV_JOB_SAVE (object);
1192 
1193 	ev_debug_message (DEBUG_JOBS, "%s", job->uri);
1194 
1195 	if (job->uri) {
1196 		g_free (job->uri);
1197 		job->uri = NULL;
1198 	}
1199 
1200 	if (job->document_uri) {
1201 		g_free (job->document_uri);
1202 		job->document_uri = NULL;
1203 	}
1204 
1205 	(* G_OBJECT_CLASS (ev_job_save_parent_class)->dispose) (object);
1206 }
1207 
1208 static gboolean
ev_job_save_run(EvJob * job)1209 ev_job_save_run (EvJob *job)
1210 {
1211 	EvJobSave *job_save = EV_JOB_SAVE (job);
1212 	gint       fd;
1213 	gchar     *tmp_filename = NULL;
1214 	gchar     *local_uri;
1215 	GError    *error = NULL;
1216 
1217 	ev_debug_message (DEBUG_JOBS, "uri: %s, document_uri: %s", job_save->uri, job_save->document_uri);
1218 	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
1219 
1220         fd = ev_mkstemp ("saveacopy.XXXXXX", &tmp_filename, &error);
1221         if (fd == -1) {
1222                 ev_job_failed_from_error (job, error);
1223                 g_error_free (error);
1224 
1225 		return FALSE;
1226 	}
1227 
1228 	ev_document_doc_mutex_lock ();
1229 
1230 	/* Save document to temp filename */
1231 	local_uri = g_filename_to_uri (tmp_filename, NULL, &error);
1232         if (local_uri != NULL) {
1233                 ev_document_save (job->document, local_uri, &error);
1234         }
1235 
1236 	close (fd);
1237 
1238 	ev_document_doc_mutex_unlock ();
1239 
1240 	if (error) {
1241 		g_free (local_uri);
1242 		ev_job_failed_from_error (job, error);
1243 		g_error_free (error);
1244 
1245 		return FALSE;
1246 	}
1247 
1248 	/* If original document was compressed,
1249 	 * compress it again before saving
1250 	 */
1251 	if (g_object_get_data (G_OBJECT (job->document), "uri-uncompressed")) {
1252 		EvCompressionType ctype = EV_COMPRESSION_NONE;
1253 		const gchar      *ext;
1254 		gchar            *uri_comp;
1255 
1256 		ext = g_strrstr (job_save->document_uri, ".gz");
1257 		if (ext && g_ascii_strcasecmp (ext, ".gz") == 0)
1258 			ctype = EV_COMPRESSION_GZIP;
1259 
1260 		ext = g_strrstr (job_save->document_uri, ".bz2");
1261 		if (ext && g_ascii_strcasecmp (ext, ".bz2") == 0)
1262 			ctype = EV_COMPRESSION_BZIP2;
1263 
1264 		uri_comp = ev_file_compress (local_uri, ctype, &error);
1265 		g_free (local_uri);
1266 		g_unlink (tmp_filename);
1267 
1268 		if (!uri_comp || error) {
1269 			local_uri = NULL;
1270 		} else {
1271 			local_uri = uri_comp;
1272 		}
1273 	}
1274 
1275 	g_free (tmp_filename);
1276 
1277 	if (error) {
1278 		g_free (local_uri);
1279 		ev_job_failed_from_error (job, error);
1280 		g_error_free (error);
1281 
1282 		return FALSE;
1283 	}
1284 
1285 	if (!local_uri)
1286 		return FALSE;
1287 
1288 	ev_xfer_uri_simple (local_uri, job_save->uri, &error);
1289 	ev_tmp_uri_unlink (local_uri);
1290 
1291 	if (error) {
1292 		ev_job_failed_from_error (job, error);
1293 		g_error_free (error);
1294 	} else {
1295 		ev_job_succeeded (job);
1296 	}
1297 
1298 	return FALSE;
1299 }
1300 
1301 static void
ev_job_save_class_init(EvJobSaveClass * class)1302 ev_job_save_class_init (EvJobSaveClass *class)
1303 {
1304 	GObjectClass *oclass = G_OBJECT_CLASS (class);
1305 	EvJobClass   *job_class = EV_JOB_CLASS (class);
1306 
1307 	oclass->dispose = ev_job_save_dispose;
1308 	job_class->run = ev_job_save_run;
1309 }
1310 
1311 EvJob *
ev_job_save_new(EvDocument * document,const gchar * uri,const gchar * document_uri)1312 ev_job_save_new (EvDocument  *document,
1313 		 const gchar *uri,
1314 		 const gchar *document_uri)
1315 {
1316 	EvJobSave *job;
1317 
1318 	ev_debug_message (DEBUG_JOBS, "uri: %s, document_uri: %s", uri, document_uri);
1319 
1320 	job = g_object_new (EV_TYPE_JOB_SAVE, NULL);
1321 
1322 	EV_JOB (job)->document = g_object_ref (document);
1323 	job->uri = g_strdup (uri);
1324 	job->document_uri = g_strdup (document_uri);
1325 
1326 	return EV_JOB (job);
1327 }
1328 
1329 /* EvJobFind */
1330 static void
ev_job_find_init(EvJobFind * job)1331 ev_job_find_init (EvJobFind *job)
1332 {
1333 	EV_JOB (job)->run_mode = EV_JOB_RUN_MAIN_LOOP;
1334 }
1335 
1336 static void
ev_job_find_dispose(GObject * object)1337 ev_job_find_dispose (GObject *object)
1338 {
1339 	EvJobFind *job = EV_JOB_FIND (object);
1340 
1341 	ev_debug_message (DEBUG_JOBS, NULL);
1342 
1343 	if (job->text) {
1344 		g_free (job->text);
1345 		job->text = NULL;
1346 	}
1347 
1348 	if (job->pages) {
1349 		gint i;
1350 
1351 		for (i = 0; i < job->n_pages; i++) {
1352 			g_list_foreach (job->pages[i], (GFunc)ev_rectangle_free, NULL);
1353 			g_list_free (job->pages[i]);
1354 		}
1355 
1356 		g_free (job->pages);
1357 		job->pages = NULL;
1358 	}
1359 
1360 	if (job->results) {
1361 		g_free(job->results);
1362 	}
1363 
1364 	(* G_OBJECT_CLASS (ev_job_find_parent_class)->dispose) (object);
1365 }
1366 
1367 static gboolean
ev_job_find_run(EvJob * job)1368 ev_job_find_run (EvJob *job)
1369 {
1370 	EvJobFind      *job_find = EV_JOB_FIND (job);
1371 	EvDocumentFind *find = EV_DOCUMENT_FIND (job->document);
1372 	EvPage         *ev_page;
1373 	GList          *matches;
1374 	ev_debug_message (DEBUG_JOBS, NULL);
1375 
1376 	/* Do not block the main loop */
1377 	if (!ev_document_doc_mutex_trylock ())
1378 		return TRUE;
1379 
1380 #ifdef EV_ENABLE_DEBUG
1381 	/* We use the #ifdef in this case because of the if */
1382 	if (job_find->current_page == job_find->start_page)
1383 		ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
1384 #endif
1385 
1386 	ev_page = ev_document_get_page (job->document, job_find->current_page);
1387 
1388 	if (job->document->iswebdocument) {
1389 		job_find->results[job_find->current_page] = ev_document_find_check_for_hits(find, ev_page, job_find->text,
1390 		                                                        job_find->case_sensitive);
1391 	}else {
1392 		matches = ev_document_find_find_text (find, ev_page, job_find->text,
1393 					      job_find->case_sensitive);
1394 	}
1395 
1396 	g_object_unref (ev_page);
1397 
1398 	ev_document_doc_mutex_unlock ();
1399 
1400 	if (!job_find->has_results && !job->document->iswebdocument) {
1401 		job_find->has_results = (matches != NULL);
1402 	}
1403 	else if (!job_find->has_results && job->document->iswebdocument){
1404 		job_find->has_results = (job_find->results[job_find->current_page] > 0);
1405 	}
1406 
1407 	if (job->document->iswebdocument == FALSE) {
1408 		job_find->pages[job_find->current_page] = matches;
1409 	}
1410 
1411 	g_signal_emit (job_find, job_find_signals[FIND_UPDATED], 0, job_find->current_page);
1412 
1413 	job_find->current_page = (job_find->current_page + 1) % job_find->n_pages;
1414 	if (job_find->current_page == job_find->start_page) {
1415 		ev_job_succeeded (job);
1416 
1417 		return FALSE;
1418 	}
1419 
1420 	return TRUE;
1421 }
1422 
1423 static void
ev_job_find_class_init(EvJobFindClass * class)1424 ev_job_find_class_init (EvJobFindClass *class)
1425 {
1426 	EvJobClass   *job_class = EV_JOB_CLASS (class);
1427 	GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1428 
1429 	job_class->run = ev_job_find_run;
1430 	gobject_class->dispose = ev_job_find_dispose;
1431 
1432 	job_find_signals[FIND_UPDATED] =
1433 		g_signal_new ("updated",
1434 			      EV_TYPE_JOB_FIND,
1435 			      G_SIGNAL_RUN_LAST,
1436 			      G_STRUCT_OFFSET (EvJobFindClass, updated),
1437 			      NULL, NULL,
1438 			      g_cclosure_marshal_VOID__INT,
1439 			      G_TYPE_NONE,
1440 			      1, G_TYPE_INT);
1441 }
1442 
1443 EvJob *
ev_job_find_new(EvDocument * document,gint start_page,gint n_pages,const gchar * text,gboolean case_sensitive)1444 ev_job_find_new (EvDocument  *document,
1445 		 gint         start_page,
1446 		 gint         n_pages,
1447 		 const gchar *text,
1448 		 gboolean     case_sensitive)
1449 {
1450 	EvJobFind *job;
1451 
1452 	ev_debug_message (DEBUG_JOBS, NULL);
1453 
1454 	job = g_object_new (EV_TYPE_JOB_FIND, NULL);
1455 
1456 	EV_JOB (job)->document = g_object_ref (document);
1457 	job->start_page = start_page;
1458 	job->current_page = start_page;
1459 	job->n_pages = n_pages;
1460 
1461 	if (document->iswebdocument) {
1462 		job->results = g_malloc0 (sizeof(guint) *n_pages);
1463 	}
1464 	else {
1465 		job->pages = g_new0 (GList *, n_pages);
1466 	}
1467 	job->text = g_strdup (text);
1468 	job->case_sensitive = case_sensitive;
1469 	job->has_results = FALSE;
1470 
1471 	return EV_JOB (job);
1472 }
1473 
1474 gint
ev_job_find_get_n_results(EvJobFind * job,gint page)1475 ev_job_find_get_n_results (EvJobFind *job,
1476 			   gint       page)
1477 {
1478 	if (EV_JOB(job)->document->iswebdocument) {
1479 		return job->results[page];
1480 	}
1481 	else {
1482 		return g_list_length (job->pages[page]);
1483 	}
1484 }
1485 
1486 gdouble
ev_job_find_get_progress(EvJobFind * job)1487 ev_job_find_get_progress (EvJobFind *job)
1488 {
1489 	gint pages_done;
1490 
1491 	if (ev_job_is_finished (EV_JOB (job)))
1492 		return 1.0;
1493 
1494 	if (job->current_page > job->start_page) {
1495 		pages_done = job->current_page - job->start_page + 1;
1496 	} else if (job->current_page == job->start_page) {
1497 		pages_done = job->n_pages;
1498 	} else {
1499 		pages_done = job->n_pages - job->start_page + job->current_page;
1500 	}
1501 
1502 	return pages_done / (gdouble) job->n_pages;
1503 }
1504 
1505 gboolean
ev_job_find_has_results(EvJobFind * job)1506 ev_job_find_has_results (EvJobFind *job)
1507 {
1508 	return job->has_results;
1509 }
1510 
1511 /**
1512  * ev_job_find_get_results: (skip)
1513  * @job: an #EvJobFind
1514  *
1515  * Returns: a #GList of #GList<!-- -->s containing #EvRectangle<!-- -->s
1516  */
1517 GList **
ev_job_find_get_results(EvJobFind * job)1518 ev_job_find_get_results (EvJobFind *job)
1519 {
1520 	return job->pages;
1521 }
1522 
1523 /* EvJobLayers */
1524 static void
ev_job_layers_init(EvJobLayers * job)1525 ev_job_layers_init (EvJobLayers *job)
1526 {
1527 	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
1528 }
1529 
1530 static void
ev_job_layers_dispose(GObject * object)1531 ev_job_layers_dispose (GObject *object)
1532 {
1533 	EvJobLayers *job;
1534 
1535 	ev_debug_message (DEBUG_JOBS, NULL);
1536 
1537 	job = EV_JOB_LAYERS (object);
1538 
1539 	if (job->model) {
1540 		g_object_unref (job->model);
1541 		job->model = NULL;
1542 	}
1543 
1544 	(* G_OBJECT_CLASS (ev_job_layers_parent_class)->dispose) (object);
1545 }
1546 
1547 static gboolean
ev_job_layers_run(EvJob * job)1548 ev_job_layers_run (EvJob *job)
1549 {
1550 	EvJobLayers *job_layers = EV_JOB_LAYERS (job);
1551 
1552 	ev_debug_message (DEBUG_JOBS, NULL);
1553 	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
1554 
1555 	ev_document_doc_mutex_lock ();
1556 	job_layers->model = ev_document_layers_get_layers (EV_DOCUMENT_LAYERS (job->document));
1557 	ev_document_doc_mutex_unlock ();
1558 
1559 	ev_job_succeeded (job);
1560 
1561 	return FALSE;
1562 }
1563 
1564 static void
ev_job_layers_class_init(EvJobLayersClass * class)1565 ev_job_layers_class_init (EvJobLayersClass *class)
1566 {
1567 	GObjectClass *oclass = G_OBJECT_CLASS (class);
1568 	EvJobClass   *job_class = EV_JOB_CLASS (class);
1569 
1570 	oclass->dispose = ev_job_layers_dispose;
1571 	job_class->run = ev_job_layers_run;
1572 }
1573 
1574 EvJob *
ev_job_layers_new(EvDocument * document)1575 ev_job_layers_new (EvDocument *document)
1576 {
1577 	EvJob *job;
1578 
1579 	ev_debug_message (DEBUG_JOBS, NULL);
1580 
1581 	job = g_object_new (EV_TYPE_JOB_LAYERS, NULL);
1582 	job->document = g_object_ref (document);
1583 
1584 	return job;
1585 }
1586 
1587 /* EvJobExport */
1588 static void
ev_job_export_init(EvJobExport * job)1589 ev_job_export_init (EvJobExport *job)
1590 {
1591 	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
1592 	job->page = -1;
1593 }
1594 
1595 static void
ev_job_export_dispose(GObject * object)1596 ev_job_export_dispose (GObject *object)
1597 {
1598 	EvJobExport *job;
1599 
1600 	ev_debug_message (DEBUG_JOBS, NULL);
1601 
1602 	job = EV_JOB_EXPORT (object);
1603 
1604 	if (job->rc) {
1605 		g_object_unref (job->rc);
1606 		job->rc = NULL;
1607 	}
1608 
1609 	(* G_OBJECT_CLASS (ev_job_export_parent_class)->dispose) (object);
1610 }
1611 
1612 static gboolean
ev_job_export_run(EvJob * job)1613 ev_job_export_run (EvJob *job)
1614 {
1615 	EvJobExport *job_export = EV_JOB_EXPORT (job);
1616 	EvPage      *ev_page;
1617 
1618 	g_assert (job_export->page != -1);
1619 
1620 	ev_debug_message (DEBUG_JOBS, NULL);
1621 	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
1622 
1623 	ev_document_doc_mutex_lock ();
1624 
1625 	ev_page = ev_document_get_page (job->document, job_export->page);
1626 	if (job_export->rc) {
1627 		job->failed = FALSE;
1628 		job->finished = FALSE;
1629 		g_clear_error (&job->error);
1630 
1631 		ev_render_context_set_page (job_export->rc, ev_page);
1632 	} else {
1633 		job_export->rc = ev_render_context_new (ev_page, 0, 1.0);
1634 	}
1635 	g_object_unref (ev_page);
1636 
1637 	ev_file_exporter_do_page (EV_FILE_EXPORTER (job->document), job_export->rc);
1638 
1639 	ev_document_doc_mutex_unlock ();
1640 
1641 	ev_job_succeeded (job);
1642 
1643 	return FALSE;
1644 }
1645 
1646 static void
ev_job_export_class_init(EvJobExportClass * class)1647 ev_job_export_class_init (EvJobExportClass *class)
1648 {
1649 	GObjectClass *oclass = G_OBJECT_CLASS (class);
1650 	EvJobClass   *job_class = EV_JOB_CLASS (class);
1651 
1652 	oclass->dispose = ev_job_export_dispose;
1653 	job_class->run = ev_job_export_run;
1654 }
1655 
1656 EvJob *
ev_job_export_new(EvDocument * document)1657 ev_job_export_new (EvDocument *document)
1658 {
1659 	EvJob *job;
1660 
1661 	ev_debug_message (DEBUG_JOBS, NULL);
1662 
1663 	job = g_object_new (EV_TYPE_JOB_EXPORT, NULL);
1664 	job->document = g_object_ref (document);
1665 
1666 	return job;
1667 }
1668 
1669 void
ev_job_export_set_page(EvJobExport * job,gint page)1670 ev_job_export_set_page (EvJobExport *job,
1671 			gint         page)
1672 {
1673 	job->page = page;
1674 }
1675 
1676 /* EvJobPrint */
1677 static void
ev_job_print_init(EvJobPrint * job)1678 ev_job_print_init (EvJobPrint *job)
1679 {
1680 	EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
1681 	job->page = -1;
1682 }
1683 
1684 static void
ev_job_print_dispose(GObject * object)1685 ev_job_print_dispose (GObject *object)
1686 {
1687 	EvJobPrint *job;
1688 
1689 	ev_debug_message (DEBUG_JOBS, NULL);
1690 
1691 	job = EV_JOB_PRINT (object);
1692 
1693 	if (job->cr) {
1694 		cairo_destroy (job->cr);
1695 		job->cr = NULL;
1696 	}
1697 
1698 	(* G_OBJECT_CLASS (ev_job_print_parent_class)->dispose) (object);
1699 }
1700 
1701 static gboolean
ev_job_print_run(EvJob * job)1702 ev_job_print_run (EvJob *job)
1703 {
1704 	EvJobPrint     *job_print = EV_JOB_PRINT (job);
1705 	EvPage         *ev_page;
1706 	cairo_status_t  cr_status;
1707 
1708 	g_assert (job_print->page != -1);
1709 	g_assert (job_print->cr != NULL);
1710 
1711 	ev_debug_message (DEBUG_JOBS, NULL);
1712 	ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
1713 
1714 	job->failed = FALSE;
1715 	job->finished = FALSE;
1716 	g_clear_error (&job->error);
1717 
1718 	ev_document_doc_mutex_lock ();
1719 
1720 	ev_page = ev_document_get_page (job->document, job_print->page);
1721 	ev_document_print_print_page (EV_DOCUMENT_PRINT (job->document),
1722 				      ev_page, job_print->cr);
1723 	g_object_unref (ev_page);
1724 
1725 	ev_document_doc_mutex_unlock ();
1726 
1727         if (g_cancellable_is_cancelled (job->cancellable))
1728                 return FALSE;
1729 
1730 	cr_status = cairo_status (job_print->cr);
1731 	if (cr_status == CAIRO_STATUS_SUCCESS) {
1732 		ev_job_succeeded (job);
1733 	} else {
1734 		ev_job_failed (job,
1735 			       GTK_PRINT_ERROR,
1736 			       GTK_PRINT_ERROR_GENERAL,
1737 			       _("Failed to print page %d: %s"),
1738 			       job_print->page,
1739 			       cairo_status_to_string (cr_status));
1740 	}
1741 
1742 	return FALSE;
1743 }
1744 
1745 static void
ev_job_print_class_init(EvJobPrintClass * class)1746 ev_job_print_class_init (EvJobPrintClass *class)
1747 {
1748 	GObjectClass *oclass = G_OBJECT_CLASS (class);
1749 	EvJobClass   *job_class = EV_JOB_CLASS (class);
1750 
1751 	oclass->dispose = ev_job_print_dispose;
1752 	job_class->run = ev_job_print_run;
1753 }
1754 
1755 EvJob *
ev_job_print_new(EvDocument * document)1756 ev_job_print_new (EvDocument *document)
1757 {
1758 	EvJob *job;
1759 
1760 	ev_debug_message (DEBUG_JOBS, NULL);
1761 
1762 	job = g_object_new (EV_TYPE_JOB_PRINT, NULL);
1763 	job->document = g_object_ref (document);
1764 
1765 	return job;
1766 }
1767 
1768 void
ev_job_print_set_page(EvJobPrint * job,gint page)1769 ev_job_print_set_page (EvJobPrint *job,
1770 		       gint        page)
1771 {
1772 	job->page = page;
1773 }
1774 
1775 void
ev_job_print_set_cairo(EvJobPrint * job,cairo_t * cr)1776 ev_job_print_set_cairo (EvJobPrint *job,
1777 			cairo_t    *cr)
1778 {
1779 	if (job->cr == cr)
1780 		return;
1781 
1782 	if (job->cr)
1783 		cairo_destroy (job->cr);
1784 	job->cr = cr ? cairo_reference (cr) : NULL;
1785 }
1786