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