]> www.fi.muni.cz Git - evince.git/blob - shell/ev-jobs.c
2.23.6
[evince.git] / shell / ev-jobs.c
1 /* this file is part of evince, a gnome document viewer
2  *
3  *  Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
4  *  Copyright (C) 2005 Red Hat, Inc
5  *
6  * Evince 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  * Evince 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, 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-document-forms.h"
28 #include "ev-file-exporter.h"
29 #include "ev-document-factory.h"
30 #include "ev-document-misc.h"
31 #include "ev-file-helpers.h"
32 #include "ev-document-fonts.h"
33 #include "ev-document-security.h"
34 #include "ev-debug.h"
35
36 #include <errno.h>
37 #include <glib/gstdio.h>
38 #include <glib/gi18n.h>
39 #include <unistd.h>
40
41 static void ev_job_init                 (EvJob               *job);
42 static void ev_job_class_init           (EvJobClass          *class);
43 static void ev_job_links_init           (EvJobLinks          *job);
44 static void ev_job_links_class_init     (EvJobLinksClass     *class);
45 static void ev_job_render_init          (EvJobRender         *job);
46 static void ev_job_render_class_init    (EvJobRenderClass    *class);
47 static void ev_job_thumbnail_init       (EvJobThumbnail      *job);
48 static void ev_job_thumbnail_class_init (EvJobThumbnailClass *class);
49 static void ev_job_load_init            (EvJobLoad           *job);
50 static void ev_job_load_class_init      (EvJobLoadClass      *class);
51 static void ev_job_save_init            (EvJobSave           *job);
52 static void ev_job_save_class_init      (EvJobSaveClass      *class);
53 static void ev_job_print_init           (EvJobPrint          *job);
54 static void ev_job_print_class_init     (EvJobPrintClass     *class);
55
56 enum {
57         CANCELLED,
58         FINISHED,
59         LAST_SIGNAL
60 };
61
62 enum {
63         PAGE_READY,
64         RENDER_LAST_SIGNAL
65 };
66
67 enum {
68         UPDATED,
69         FONTS_LAST_SIGNAL
70 };
71
72 static guint job_signals[LAST_SIGNAL] = { 0 };
73 static guint job_render_signals[RENDER_LAST_SIGNAL] = { 0 };
74 static guint job_fonts_signals[FONTS_LAST_SIGNAL] = { 0 };
75
76 G_DEFINE_ABSTRACT_TYPE (EvJob, ev_job, G_TYPE_OBJECT)
77 G_DEFINE_TYPE (EvJobLinks, ev_job_links, EV_TYPE_JOB)
78 G_DEFINE_TYPE (EvJobRender, ev_job_render, EV_TYPE_JOB)
79 G_DEFINE_TYPE (EvJobThumbnail, ev_job_thumbnail, EV_TYPE_JOB)
80 G_DEFINE_TYPE (EvJobFonts, ev_job_fonts, EV_TYPE_JOB)
81 G_DEFINE_TYPE (EvJobLoad, ev_job_load, EV_TYPE_JOB)
82 G_DEFINE_TYPE (EvJobSave, ev_job_save, EV_TYPE_JOB)
83 G_DEFINE_TYPE (EvJobPrint, ev_job_print, EV_TYPE_JOB)
84
85 /* EvJob */
86 static void
87 ev_job_init (EvJob *job)
88 {
89         job->cancellable = g_cancellable_new ();
90 }
91
92 static void
93 ev_job_dispose (GObject *object)
94 {
95         EvJob *job;
96
97         job = EV_JOB (object);
98
99         if (job->document) {
100                 g_object_unref (job->document);
101                 job->document = NULL;
102         }
103
104         if (job->cancellable) {
105                 g_object_unref (job->cancellable);
106                 job->cancellable = NULL;
107         }
108
109         if (job->error) {
110                 g_error_free (job->error);
111                 job->error = NULL;
112         }
113
114         (* G_OBJECT_CLASS (ev_job_parent_class)->dispose) (object);
115 }
116
117 static void
118 ev_job_class_init (EvJobClass *class)
119 {
120         GObjectClass *oclass;
121
122         oclass = G_OBJECT_CLASS (class);
123
124         oclass->dispose = ev_job_dispose;
125
126         job_signals[CANCELLED] =
127                 g_signal_new ("cancelled",
128                               EV_TYPE_JOB,
129                               G_SIGNAL_RUN_LAST,
130                               G_STRUCT_OFFSET (EvJobClass, cancelled),
131                               NULL, NULL,
132                               g_cclosure_marshal_VOID__VOID,
133                               G_TYPE_NONE, 0);
134         job_signals [FINISHED] =
135                 g_signal_new ("finished",
136                               EV_TYPE_JOB,
137                               G_SIGNAL_RUN_FIRST,
138                               G_STRUCT_OFFSET (EvJobClass, finished),
139                               NULL, NULL,
140                               g_cclosure_marshal_VOID__VOID,
141                               G_TYPE_NONE, 0);
142 }
143
144 static gboolean
145 emit_finished (EvJob *job)
146 {
147         ev_debug_message (DEBUG_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
148
149         job->idle_finished_id = 0;
150         
151         if (job->cancelled) {
152                 ev_debug_message (DEBUG_JOBS, "%s (%p) job was cancelled, do not emit finished", EV_GET_TYPE_NAME (job), job);
153         } else {
154                 ev_profiler_stop (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
155                 g_signal_emit (job, job_signals[FINISHED], 0);
156         }
157         
158         return FALSE;
159 }
160
161 static void
162 ev_job_emit_finished (EvJob *job)
163 {
164         ev_debug_message (DEBUG_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
165
166         if (g_cancellable_is_cancelled (job->cancellable)) {
167                 ev_debug_message (DEBUG_JOBS, "%s (%p) job was cancelled, returning", EV_GET_TYPE_NAME (job), job);
168                 return;
169         }
170         
171         job->finished = TRUE;
172         
173         if (job->run_mode == EV_JOB_RUN_THREAD) {
174                 job->idle_finished_id =
175                         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
176                                          (GSourceFunc)emit_finished,
177                                          g_object_ref (job),
178                                          (GDestroyNotify)g_object_unref);
179         } else {
180                 ev_profiler_stop (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
181                 g_signal_emit (job, job_signals[FINISHED], 0);
182         }
183 }
184
185 gboolean
186 ev_job_run (EvJob *job)
187 {
188         EvJobClass *class = EV_JOB_GET_CLASS (job);
189         
190         return class->run (job);
191 }
192
193 void
194 ev_job_cancel (EvJob *job)
195 {
196         if (job->cancelled || (job->finished && job->idle_finished_id == 0))
197                 return;
198
199         ev_debug_message (DEBUG_JOBS, "job %s (%p) cancelled", EV_GET_TYPE_NAME (job), job);
200         ev_profiler_stop (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
201         
202         /* This should never be called from a thread */
203         job->cancelled = TRUE;
204         g_cancellable_cancel (job->cancellable);
205         g_signal_emit (job, job_signals[CANCELLED], 0);
206 }
207
208 void
209 ev_job_failed (EvJob       *job,
210                GQuark       domain,
211                gint         code,
212                const gchar *format,
213                ...)
214 {
215         va_list args;
216         gchar  *message;
217         
218         if (job->failed || job->finished)
219                 return;
220
221         ev_debug_message (DEBUG_JOBS, "job %s (%p) failed", EV_GET_TYPE_NAME (job), job);
222         
223         job->failed = TRUE;
224         
225         va_start (args, format);
226         message = g_strdup_vprintf (format, args);
227         va_end (args);
228         
229         job->error = g_error_new (domain, code, message);
230         g_free (message);
231         
232         ev_job_emit_finished (job);                                                                                                               
233 }
234
235 void
236 ev_job_failed_from_error (EvJob  *job,
237                           GError *error)
238 {
239         if (job->failed || job->finished)
240                 return;
241         
242         ev_debug_message (DEBUG_JOBS, "job %s (%p) failed", EV_GET_TYPE_NAME (job), job);
243
244         job->failed = TRUE;
245         job->error = g_error_copy (error);
246
247         ev_job_emit_finished (job);
248 }
249
250 void
251 ev_job_succeeded (EvJob *job)
252 {
253         if (job->finished)
254                 return;
255
256         ev_debug_message (DEBUG_JOBS, "job %s (%p) succeeded", EV_GET_TYPE_NAME (job), job);
257         
258         job->failed = FALSE;
259         ev_job_emit_finished (job);
260 }
261
262 gboolean
263 ev_job_is_finished (EvJob *job)
264 {
265         return job->finished;
266 }
267
268 gboolean
269 ev_job_is_failed (EvJob *job)
270 {
271         return job->failed;
272 }
273
274 EvJobRunMode
275 ev_job_get_run_mode (EvJob *job)
276 {
277         return job->run_mode;
278 }
279
280 void
281 ev_job_set_run_mode (EvJob       *job,
282                      EvJobRunMode run_mode)
283 {
284         job->run_mode = run_mode;
285 }
286
287 /* EvJobLinks */
288 static void
289 ev_job_links_init (EvJobLinks *job)
290 {
291         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
292 }
293
294 static void
295 ev_job_links_dispose (GObject *object)
296 {
297         EvJobLinks *job;
298
299         ev_debug_message (DEBUG_JOBS, NULL);
300         
301         job = EV_JOB_LINKS (object);
302
303         if (job->model) {
304                 g_object_unref (job->model);
305                 job->model = NULL;
306         }
307
308         (* G_OBJECT_CLASS (ev_job_links_parent_class)->dispose) (object);
309 }
310
311 static gboolean
312 ev_job_links_run (EvJob *job)
313 {
314         EvJobLinks *job_links = EV_JOB_LINKS (job);
315
316         ev_debug_message (DEBUG_JOBS, NULL);
317         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
318         
319         ev_document_doc_mutex_lock ();
320         job_links->model = ev_document_links_get_links_model (EV_DOCUMENT_LINKS (job->document));
321         ev_document_doc_mutex_unlock ();
322         
323         ev_job_succeeded (job);
324         
325         return FALSE;
326 }
327
328 static void
329 ev_job_links_class_init (EvJobLinksClass *class)
330 {
331         GObjectClass *oclass = G_OBJECT_CLASS (class);
332         EvJobClass   *job_class = EV_JOB_CLASS (class);
333
334         oclass->dispose = ev_job_links_dispose;
335         job_class->run = ev_job_links_run;
336 }
337
338 EvJob *
339 ev_job_links_new (EvDocument *document)
340 {
341         EvJob *job;
342
343         ev_debug_message (DEBUG_JOBS, NULL);
344
345         job = g_object_new (EV_TYPE_JOB_LINKS, NULL);
346         job->document = g_object_ref (document);
347         
348         return job;
349 }
350
351 /* EvJobRender */
352 static void
353 ev_job_render_init (EvJobRender *job)
354 {
355         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
356 }
357
358 static void
359 ev_job_render_dispose (GObject *object)
360 {
361         EvJobRender *job;
362
363         job = EV_JOB_RENDER (object);
364
365         if (job->ev_page) {
366                 ev_debug_message (DEBUG_JOBS, "page: %d (%p)", job->ev_page->index, job);
367                 g_object_unref (job->ev_page);
368                 job->ev_page = NULL;
369         }
370         
371         if (job->surface) {
372                 cairo_surface_destroy (job->surface);
373                 job->surface = NULL;
374         }
375
376         if (job->selection) {
377                 cairo_surface_destroy (job->selection);
378                 job->selection = NULL;
379         }
380
381         if (job->selection_region) {
382                 gdk_region_destroy (job->selection_region);
383                 job->selection_region = NULL;
384         }
385
386         (* G_OBJECT_CLASS (ev_job_render_parent_class)->dispose) (object);
387 }
388
389 static gboolean
390 notify_page_ready (EvJobRender *job)
391 {
392         ev_debug_message (DEBUG_JOBS, "%d (%p)", job->ev_page->index, job);
393         ev_profiler_stop (EV_PROFILE_JOBS, "Rendering page %d", job->ev_page->index);
394
395         if (EV_JOB (job)->cancelled) {
396                 ev_debug_message (DEBUG_JOBS, "%s (%p) job was cancelled, do not emit page_ready", EV_GET_TYPE_NAME (job), job);
397         } else {
398                 g_signal_emit (job, job_render_signals[PAGE_READY], 0);
399         }
400
401         return FALSE;
402 }
403
404 static void
405 ev_job_render_page_ready (EvJobRender *job)
406 {
407         ev_debug_message (DEBUG_JOBS, "%d (%p)", job->ev_page->index, job);
408         
409         job->page_ready = TRUE;
410         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
411                          (GSourceFunc)notify_page_ready,
412                          g_object_ref (job),
413                          (GDestroyNotify)g_object_unref);
414 }
415
416 static gboolean
417 ev_job_render_run (EvJob *job)
418 {
419         EvJobRender     *job_render = EV_JOB_RENDER (job);
420         EvRenderContext *rc;
421
422         ev_debug_message (DEBUG_JOBS, "page: %d (%p)", job_render->page, job);
423         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
424         
425         ev_document_doc_mutex_lock ();
426
427         ev_profiler_start (EV_PROFILE_JOBS, "Rendering page %d", job_render->page);
428                 
429         ev_document_fc_mutex_lock ();
430
431         job_render->ev_page = ev_document_get_page (job->document, job_render->page);
432         rc = ev_render_context_new (job_render->ev_page, job_render->rotation, job_render->scale);
433                 
434         job_render->surface = ev_document_render (job->document, rc);
435         /* If job was cancelled during the page rendering,
436          * we return now, so that the thread is finished ASAP
437          */
438         if (g_cancellable_is_cancelled (job->cancellable)) {
439                 ev_document_fc_mutex_unlock ();
440                 ev_document_doc_mutex_unlock ();
441                 g_object_unref (rc);
442
443                 return FALSE;
444         }
445         
446         if ((job_render->flags & EV_RENDER_INCLUDE_SELECTION) && EV_IS_SELECTION (job->document)) {
447                 ev_selection_render_selection (EV_SELECTION (job->document),
448                                                rc,
449                                                &(job_render->selection),
450                                                &(job_render->selection_points),
451                                                NULL,
452                                                job_render->selection_style,
453                                                &(job_render->text), &(job_render->base));
454                 job_render->selection_region =
455                         ev_selection_get_selection_region (EV_SELECTION (job->document),
456                                                            rc,
457                                                            job_render->selection_style,
458                                                            &(job_render->selection_points));
459         }
460
461         ev_job_render_page_ready (job_render);
462                 
463         ev_document_fc_mutex_unlock ();
464                 
465         if ((job_render->flags & EV_RENDER_INCLUDE_TEXT) && EV_IS_SELECTION (job->document))
466                 job_render->text_mapping =
467                         ev_selection_get_selection_map (EV_SELECTION (job->document), rc);
468         if ((job_render->flags & EV_RENDER_INCLUDE_LINKS) && EV_IS_DOCUMENT_LINKS (job->document))
469                 job_render->link_mapping =
470                         ev_document_links_get_links (EV_DOCUMENT_LINKS (job->document), job_render->page);
471         if ((job_render->flags & EV_RENDER_INCLUDE_FORMS) && EV_IS_DOCUMENT_FORMS (job->document))
472                 job_render->form_field_mapping =
473                         ev_document_forms_get_form_fields (EV_DOCUMENT_FORMS (job->document),
474                                                            job_render->ev_page);
475         if ((job_render->flags & EV_RENDER_INCLUDE_IMAGES) && EV_IS_DOCUMENT_IMAGES (job->document))
476                 job_render->image_mapping =
477                         ev_document_images_get_image_mapping (EV_DOCUMENT_IMAGES (job->document),
478                                                               job_render->page);
479         g_object_unref (rc);
480         ev_document_doc_mutex_unlock ();
481         
482         ev_job_succeeded (job);
483         
484         return FALSE;
485 }
486
487 static void
488 ev_job_render_class_init (EvJobRenderClass *class)
489 {
490         GObjectClass *oclass = G_OBJECT_CLASS (class);
491         EvJobClass   *job_class = EV_JOB_CLASS (class);
492
493         job_render_signals [PAGE_READY] =
494                 g_signal_new ("page-ready",
495                               EV_TYPE_JOB_RENDER,
496                               G_SIGNAL_RUN_LAST,
497                               G_STRUCT_OFFSET (EvJobRenderClass, page_ready),
498                               NULL, NULL,
499                               g_cclosure_marshal_VOID__VOID,
500                               G_TYPE_NONE, 0);
501
502         oclass->dispose = ev_job_render_dispose;
503         job_class->run = ev_job_render_run;
504 }
505
506 EvJob *
507 ev_job_render_new (EvDocument   *document,
508                    gint          page,
509                    gint          rotation,
510                    gdouble       scale, 
511                    gint          width,
512                    gint          height,
513                    EvRenderFlags flags)
514 {
515         EvJobRender *job;
516
517         ev_debug_message (DEBUG_JOBS, "page: %d", page);
518         
519         job = g_object_new (EV_TYPE_JOB_RENDER, NULL);
520
521         EV_JOB (job)->document = g_object_ref (document);
522         job->page = page;
523         job->rotation = rotation;
524         job->scale = scale;
525         job->target_width = width;
526         job->target_height = height;
527         job->flags = flags;
528
529         return EV_JOB (job);
530 }
531
532 void
533 ev_job_render_set_selection_info (EvJobRender     *job,
534                                   EvRectangle     *selection_points,
535                                   EvSelectionStyle selection_style,
536                                   GdkColor        *text,
537                                   GdkColor        *base)
538 {
539         job->flags |= EV_RENDER_INCLUDE_SELECTION;
540         
541         job->selection_points = *selection_points;
542         job->selection_style = selection_style;
543         job->text = *text;
544         job->base = *base;
545 }
546
547 /* EvJobThumbnail */
548 static void
549 ev_job_thumbnail_init (EvJobThumbnail *job)
550 {
551         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
552 }
553
554 static void
555 ev_job_thumbnail_dispose (GObject *object)
556 {
557         EvJobThumbnail *job;
558
559         job = EV_JOB_THUMBNAIL (object);
560
561         ev_debug_message (DEBUG_JOBS, "%d (%p)", job->page, job);
562         
563         if (job->thumbnail) {
564                 g_object_unref (job->thumbnail);
565                 job->thumbnail = NULL;
566         }
567
568         (* G_OBJECT_CLASS (ev_job_thumbnail_parent_class)->dispose) (object);
569 }
570
571 static gboolean
572 ev_job_thumbnail_run (EvJob *job)
573 {
574         EvJobThumbnail  *job_thumb = EV_JOB_THUMBNAIL (job);
575         EvRenderContext *rc;
576         EvPage          *page;
577
578         ev_debug_message (DEBUG_JOBS, "%d (%p)", job_thumb->page, job);
579         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
580         
581         ev_document_doc_mutex_lock ();
582
583         page = ev_document_get_page (job->document, job_thumb->page);
584         rc = ev_render_context_new (page, job_thumb->rotation, job_thumb->scale);
585         g_object_unref (page);
586
587         job_thumb->thumbnail = ev_document_thumbnails_get_thumbnail (EV_DOCUMENT_THUMBNAILS (job->document),
588                                                                      rc, TRUE);
589         g_object_unref (rc);
590         ev_document_doc_mutex_unlock ();
591
592         ev_job_succeeded (job);
593         
594         return FALSE;
595 }
596
597 static void
598 ev_job_thumbnail_class_init (EvJobThumbnailClass *class)
599 {
600         GObjectClass *oclass = G_OBJECT_CLASS (class);
601         EvJobClass   *job_class = EV_JOB_CLASS (class);
602
603         oclass->dispose = ev_job_thumbnail_dispose;
604         job_class->run = ev_job_thumbnail_run;
605 }
606
607 EvJob *
608 ev_job_thumbnail_new (EvDocument *document,
609                       gint        page,
610                       gint        rotation,
611                       gdouble     scale)
612 {
613         EvJobThumbnail *job;
614
615         ev_debug_message (DEBUG_JOBS, "%d", page);
616         
617         job = g_object_new (EV_TYPE_JOB_THUMBNAIL, NULL);
618
619         EV_JOB (job)->document = g_object_ref (document);
620         job->page = page;
621         job->rotation = rotation;
622         job->scale = scale;
623
624         return EV_JOB (job);
625 }
626
627 /* EvJobFonts */
628 static void
629 ev_job_fonts_init (EvJobFonts *job)
630 {
631         EV_JOB (job)->run_mode = EV_JOB_RUN_MAIN_LOOP;
632 }
633
634 static gboolean
635 ev_job_fonts_run (EvJob *job)
636 {
637         EvJobFonts      *job_fonts = EV_JOB_FONTS (job);
638         EvDocumentFonts *fonts = EV_DOCUMENT_FONTS (job->document);
639
640         ev_debug_message (DEBUG_JOBS, NULL);
641         
642         /* Do not block the main loop */
643         if (!ev_document_doc_mutex_trylock ())
644                 return TRUE;
645         
646         if (!ev_document_fc_mutex_trylock ())
647                 return TRUE;
648
649 #ifdef EV_ENABLE_DEBUG
650         /* We use the #ifdef in this case because of the if */
651         if (ev_document_fonts_get_progress (fonts) == 0)
652                 ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
653 #endif
654
655         job_fonts->scan_completed = !ev_document_fonts_scan (fonts, 20);
656         g_signal_emit (job_fonts, job_fonts_signals[UPDATED], 0,
657                        ev_document_fonts_get_progress (fonts));
658
659         ev_document_fc_mutex_unlock ();
660         ev_document_doc_mutex_unlock ();
661
662         if (job_fonts->scan_completed)
663                 ev_job_succeeded (job);
664         
665         return !job_fonts->scan_completed;
666 }
667
668 static void
669 ev_job_fonts_class_init (EvJobFontsClass *class)
670 {
671         EvJobClass *job_class = EV_JOB_CLASS (class);
672         
673         job_class->run = ev_job_fonts_run;
674         
675         job_fonts_signals[UPDATED] =
676                 g_signal_new ("updated",
677                               EV_TYPE_JOB_FONTS,
678                               G_SIGNAL_RUN_LAST,
679                               G_STRUCT_OFFSET (EvJobFontsClass, updated),
680                               NULL, NULL,
681                               g_cclosure_marshal_VOID__DOUBLE,
682                               G_TYPE_NONE,
683                               1, G_TYPE_DOUBLE);
684 }
685
686 EvJob *
687 ev_job_fonts_new (EvDocument *document)
688 {
689         EvJobFonts *job;
690
691         ev_debug_message (DEBUG_JOBS, NULL);
692         
693         job = g_object_new (EV_TYPE_JOB_FONTS, NULL);
694
695         EV_JOB (job)->document = g_object_ref (document);
696
697         return EV_JOB (job);
698 }
699
700 /* EvJobLoad */
701 static void
702 ev_job_load_init (EvJobLoad *job)
703 {
704         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
705 }
706
707 static void
708 ev_job_load_dispose (GObject *object)
709 {
710         EvJobLoad *job = EV_JOB_LOAD (object);
711
712         ev_debug_message (DEBUG_JOBS, "%s", job->uri);
713         
714         if (job->uri) {
715                 g_free (job->uri);
716                 job->uri = NULL;
717         }
718
719         if (job->password) {
720                 g_free (job->password);
721                 job->password = NULL;
722         }
723
724         if (job->dest) {
725                 g_object_unref (job->dest);
726                 job->dest = NULL;
727         }
728
729         if (job->search_string) {
730                 g_free (job->search_string);
731                 job->search_string = NULL;
732         }
733
734         (* G_OBJECT_CLASS (ev_job_load_parent_class)->dispose) (object);
735 }
736
737 static gboolean
738 ev_job_load_run (EvJob *job)
739 {
740         EvJobLoad *job_load = EV_JOB_LOAD (job);
741         GError    *error = NULL;
742         
743         ev_debug_message (DEBUG_JOBS, "%s", job_load->uri);
744         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
745         
746         ev_document_fc_mutex_lock ();
747
748         /* This job may already have a document even if the job didn't complete
749            because, e.g., a password is required - if so, just reload rather than
750            creating a new instance */
751         if (job->document) {
752                 if (job_load->password) {
753                         ev_document_security_set_password (EV_DOCUMENT_SECURITY (job->document),
754                                                            job_load->password);
755                 }
756                 
757                 job->failed = FALSE;
758                 job->finished = FALSE;
759                 g_clear_error (&job->error);
760                 
761                 ev_document_load (job->document,
762                                   job_load->uri,
763                                   &error);
764         } else {
765                 job->document = ev_document_factory_get_document (job_load->uri,
766                                                                   &error);
767         }
768
769         ev_document_fc_mutex_unlock ();
770
771         if (error) {
772                 ev_job_failed_from_error (job, error);
773                 g_error_free (error);
774         } else {
775                 ev_job_succeeded (job);
776         }
777
778         return FALSE;
779 }
780
781 static void
782 ev_job_load_class_init (EvJobLoadClass *class)
783 {
784         GObjectClass *oclass = G_OBJECT_CLASS (class);
785         EvJobClass   *job_class = EV_JOB_CLASS (class);
786
787         oclass->dispose = ev_job_load_dispose;
788         job_class->run = ev_job_load_run;
789 }
790
791 EvJob *
792 ev_job_load_new (const gchar    *uri,
793                  EvLinkDest     *dest,
794                  EvWindowRunMode mode,
795                  const gchar    *search_string)
796 {
797         EvJobLoad *job;
798
799         ev_debug_message (DEBUG_JOBS, "%s", uri);
800         
801         job = g_object_new (EV_TYPE_JOB_LOAD, NULL);
802
803         job->uri = g_strdup (uri);
804         job->dest = dest ? g_object_ref (dest) : NULL;
805         job->mode = mode;
806         job->search_string = search_string ? g_strdup (search_string) : NULL;
807
808         return EV_JOB (job);
809 }
810
811 void
812 ev_job_load_set_uri (EvJobLoad *job, const gchar *uri)
813 {
814         ev_debug_message (DEBUG_JOBS, "%s", uri);
815         
816         if (job->uri)
817                 g_free (job->uri);
818         job->uri = g_strdup (uri);
819 }
820
821 void
822 ev_job_load_set_password (EvJobLoad *job, const gchar *password)
823 {
824         ev_debug_message (DEBUG_JOBS, NULL);
825
826         if (job->password)
827                 g_free (job->password);
828         job->password = password ? g_strdup (password) : NULL;
829 }
830
831 /* EvJobSave */
832 static void
833 ev_job_save_init (EvJobSave *job)
834 {
835         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
836 }
837
838 static void
839 ev_job_save_dispose (GObject *object)
840 {
841         EvJobSave *job = EV_JOB_SAVE (object);
842
843         ev_debug_message (DEBUG_JOBS, "%s", job->uri);
844         
845         if (job->uri) {
846                 g_free (job->uri);
847                 job->uri = NULL;
848         }
849
850         if (job->document_uri) {
851                 g_free (job->document_uri);
852                 job->document_uri = NULL;
853         }
854
855         (* G_OBJECT_CLASS (ev_job_save_parent_class)->dispose) (object);
856 }
857
858 static gboolean
859 ev_job_save_run (EvJob *job)
860 {
861         EvJobSave *job_save = EV_JOB_SAVE (job);
862         gint       fd;
863         gchar     *filename;
864         gchar     *tmp_filename;
865         gchar     *local_uri;
866         GError    *error = NULL;
867         
868         ev_debug_message (DEBUG_JOBS, "uri: %s, document_uri: %s", job_save->uri, job_save->document_uri);
869         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
870         
871         filename = ev_tmp_filename ("saveacopy");
872         tmp_filename = g_strdup_printf ("%s.XXXXXX", filename);
873         g_free (filename);
874
875         fd = g_mkstemp (tmp_filename);
876         if (fd == -1) {
877                 gchar *display_name;
878                 gint   save_errno = errno;
879
880                 display_name = g_filename_display_name (tmp_filename);
881                 ev_job_failed (job,
882                                G_FILE_ERROR,
883                                g_file_error_from_errno (save_errno),
884                                _("Failed to create file “%s”: %s"),
885                                display_name, g_strerror (save_errno));
886                 g_free (display_name);
887                 g_free (tmp_filename);
888
889                 return FALSE;
890         }
891
892         ev_document_doc_mutex_lock ();
893
894         /* Save document to temp filename */
895         local_uri = g_filename_to_uri (tmp_filename, NULL, NULL);
896         ev_document_save (job->document, local_uri, &error);
897         close (fd);
898
899         ev_document_doc_mutex_unlock ();
900
901         if (error) {
902                 g_free (local_uri);
903                 ev_job_failed_from_error (job, error);
904                 g_error_free (error);
905                 
906                 return FALSE;
907         }
908
909         /* If original document was compressed,
910          * compress it again before saving
911          */
912         if (g_object_get_data (G_OBJECT (job->document), "uri-uncompressed")) {
913                 EvCompressionType ctype = EV_COMPRESSION_NONE;
914                 const gchar      *ext;
915                 gchar            *uri_comp;
916                 
917                 ext = g_strrstr (job_save->document_uri, ".gz");
918                 if (ext && g_ascii_strcasecmp (ext, ".gz") == 0)
919                         ctype = EV_COMPRESSION_GZIP;
920                 
921                 ext = g_strrstr (job_save->document_uri, ".bz2");
922                 if (ext && g_ascii_strcasecmp (ext, ".bz2") == 0)
923                         ctype = EV_COMPRESSION_BZIP2;
924
925                 uri_comp = ev_file_compress (local_uri, ctype, &error);
926                 g_free (local_uri);
927                 ev_tmp_filename_unlink (tmp_filename);
928
929                 if (!uri_comp || error) {
930                         local_uri = NULL;
931                 } else {
932                         local_uri = uri_comp;
933                 }
934         }
935
936         g_free (tmp_filename);
937         
938         if (error) {
939                 g_free (local_uri);
940                 ev_job_failed_from_error (job, error);
941                 g_error_free (error);
942                 
943                 return FALSE;
944         }
945
946         if (!local_uri)
947                 return FALSE;
948
949         ev_xfer_uri_simple (local_uri, job_save->uri, &error);
950         ev_tmp_uri_unlink (local_uri);
951
952         if (error) {
953                 ev_job_failed_from_error (job, error);
954                 g_error_free (error);
955         } else {
956                 ev_job_succeeded (job);
957         }
958         
959         return FALSE;
960 }
961
962 static void
963 ev_job_save_class_init (EvJobSaveClass *class)
964 {
965         GObjectClass *oclass = G_OBJECT_CLASS (class);
966         EvJobClass   *job_class = EV_JOB_CLASS (class);
967
968         oclass->dispose = ev_job_save_dispose;
969         job_class->run = ev_job_save_run;
970 }
971
972 EvJob *
973 ev_job_save_new (EvDocument  *document,
974                  const gchar *uri,
975                  const gchar *document_uri)
976 {
977         EvJobSave *job;
978
979         ev_debug_message (DEBUG_JOBS, "uri: %s, document_uri: %s", uri, document_uri);
980
981         job = g_object_new (EV_TYPE_JOB_SAVE, NULL);
982
983         EV_JOB (job)->document = g_object_ref (document);
984         job->uri = g_strdup (uri);
985         job->document_uri = g_strdup (document_uri);
986
987         return EV_JOB (job);
988 }
989
990 /* EvJobPrint */
991 static void
992 ev_job_print_init (EvJobPrint *job)
993 {
994         EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
995 }
996
997 static void
998 ev_job_print_dispose (GObject *object)
999 {
1000         EvJobPrint *job;
1001
1002         job = EV_JOB_PRINT (object);
1003
1004         ev_debug_message (DEBUG_JOBS, NULL);
1005         
1006         if (job->temp_file) {
1007                 g_unlink (job->temp_file);
1008                 g_free (job->temp_file);
1009                 job->temp_file = NULL;
1010         }
1011
1012         if (job->ranges) {
1013                 g_free (job->ranges);
1014                 job->ranges = NULL;
1015                 job->n_ranges = 0;
1016         }
1017
1018         (* G_OBJECT_CLASS (ev_job_print_parent_class)->dispose) (object);
1019 }
1020
1021 static gint
1022 ev_print_job_get_first_page (EvJobPrint *job)
1023 {
1024         gint i;
1025         gint first_page = G_MAXINT;
1026         
1027         if (job->n_ranges == 0)
1028                 return 0;
1029
1030         for (i = 0; i < job->n_ranges; i++) {
1031                 if (job->ranges[i].start < first_page)
1032                         first_page = job->ranges[i].start;
1033         }
1034
1035         return MAX (0, first_page);
1036 }
1037
1038 static gint
1039 ev_print_job_get_last_page (EvJobPrint *job)
1040 {
1041         gint i;
1042         gint last_page = G_MININT;
1043         gint max_page;
1044
1045         max_page = ev_document_get_n_pages (EV_JOB (job)->document) - 1;
1046
1047         if (job->n_ranges == 0)
1048                 return max_page;
1049
1050         for (i = 0; i < job->n_ranges; i++) {
1051                 if (job->ranges[i].end > last_page)
1052                         last_page = job->ranges[i].end;
1053         }
1054
1055         return MIN (max_page, last_page);
1056 }
1057
1058 static gboolean
1059 ev_print_job_print_page_in_set (EvJobPrint *job,
1060                                 gint        page)
1061 {
1062         switch (job->page_set) {
1063                 case EV_PRINT_PAGE_SET_EVEN:
1064                         return page % 2 == 0;
1065                 case EV_PRINT_PAGE_SET_ODD:
1066                         return page % 2 != 0;
1067                 case EV_PRINT_PAGE_SET_ALL:
1068                         return TRUE;
1069         }
1070
1071         return FALSE;
1072 }
1073
1074 static gint *
1075 ev_job_print_get_page_list (EvJobPrint *job,
1076                             gint       *n_pages)
1077 {
1078         gint  i, j, page, max_page;
1079         gint  pages = 0;
1080         gint *page_list;
1081
1082         max_page = ev_document_get_n_pages (EV_JOB (job)->document) - 1;
1083
1084         for (i = 0; i < job->n_ranges; i++) {
1085                 gint rsize;
1086                 gint start, end;
1087
1088                 if (job->ranges[i].start == -1)
1089                         job->ranges[i].start = 0;
1090                 if (job->ranges[i].end == -1)
1091                         job->ranges[i].end = max_page;
1092
1093                 if (job->ranges[i].start > max_page)
1094                         continue;
1095                 
1096                 start = job->ranges[i].start + 1;
1097                 end = job->ranges[i].end <= max_page ? job->ranges[i].end + 1 : max_page + 1;
1098                 rsize = end - start + 1;
1099
1100                 switch (job->page_set) {
1101                         case EV_PRINT_PAGE_SET_EVEN:
1102                                 pages += start % 2 == 0 ? (rsize / 2) + (rsize % 2) : (rsize / 2);
1103                                 break;
1104                         case EV_PRINT_PAGE_SET_ODD:
1105                                 pages += start % 2 != 0 ? (rsize / 2) + (rsize % 2) : (rsize / 2);
1106                                 break;
1107                         default:
1108                                 pages += rsize;
1109                                 break;
1110                 }
1111         }
1112
1113         *n_pages = pages;
1114
1115         if (pages == 0)
1116                 return NULL;
1117
1118         page_list = g_new (gint, pages);
1119
1120         page = 0;
1121         for (i = 0; i < job->n_ranges; i++) {
1122                 for (j = job->ranges[i].start; j <= job->ranges[i].end; j++) {
1123                         if (j > max_page)
1124                                 break;
1125                 
1126                         if (ev_print_job_print_page_in_set (job, j + 1))
1127                                 page_list[page++] = j;
1128                 }
1129         }
1130
1131         return page_list;
1132 }
1133
1134 static gboolean
1135 ev_job_print_run (EvJob *job)
1136 {
1137         EvDocument            *document = EV_JOB (job)->document;
1138         EvJobPrint            *job_print = EV_JOB_PRINT (job);
1139         EvFileExporterContext  fc;
1140         EvRenderContext       *rc;
1141         gint                   fd;
1142         gint                  *page_list;
1143         gint                   n_pages;
1144         gint                   last_page;
1145         gint                   first_page;
1146         gint                   i, j;
1147         gchar                 *filename;
1148         GError                *error = NULL;
1149         
1150         ev_debug_message (DEBUG_JOBS, NULL);
1151         ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
1152         
1153         if (job_print->temp_file)
1154                 g_free (job_print->temp_file);
1155         job_print->temp_file = NULL;
1156         
1157         filename = g_strdup_printf ("evince_print.%s.XXXXXX", job_print->format);
1158         fd = g_file_open_tmp (filename, &job_print->temp_file, &error);
1159         g_free (filename);
1160         if (fd <= -1) {
1161                 ev_job_failed_from_error (job, error);
1162                 g_error_free (error);
1163                 
1164                 return FALSE;
1165         }
1166
1167         page_list = ev_job_print_get_page_list (job_print, &n_pages);
1168         if (n_pages == 0) {
1169                 close (fd);
1170                 /* TODO: error */
1171                 ev_job_succeeded (job);
1172                 
1173                 return FALSE;
1174         }
1175
1176         first_page = ev_print_job_get_first_page (job_print);
1177         last_page = ev_print_job_get_last_page (job_print);
1178
1179         fc.format = g_ascii_strcasecmp (job_print->format, "pdf") == 0 ?
1180                 EV_FILE_FORMAT_PDF : EV_FILE_FORMAT_PS;
1181         fc.filename = job_print->temp_file;
1182         fc.first_page = MIN (first_page, last_page);
1183         fc.last_page = MAX (first_page, last_page);
1184         fc.paper_width = job_print->width;
1185         fc.paper_height = job_print->height;
1186         fc.duplex = FALSE;
1187         fc.pages_per_sheet = MAX (1, job_print->pages_per_sheet);
1188
1189         rc = ev_render_context_new (NULL, 0, 1.0);
1190
1191         ev_document_doc_mutex_lock ();
1192         ev_file_exporter_begin (EV_FILE_EXPORTER (document), &fc);
1193
1194         for (i = 0; i < job_print->copies; i++) {
1195                 gint page, step;
1196                 gint n_copies;
1197                 
1198                 step = job_print->reverse ? -1 * job_print->pages_per_sheet : job_print->pages_per_sheet;
1199                 page = job_print->reverse ? ((n_pages - 1) / job_print->pages_per_sheet) * job_print->pages_per_sheet : 0;
1200                 n_copies = job_print->collate ? 1 : job_print->copies;
1201
1202                 while ((job_print->reverse && (page >= 0)) || (!job_print->reverse && (page < n_pages))) {
1203                         gint k;
1204
1205                         for (k = 0; k < n_copies; k++) {
1206                                 ev_file_exporter_begin_page (EV_FILE_EXPORTER (document));
1207                                 
1208                                 for (j = 0; j < job_print->pages_per_sheet; j++) {
1209                                         EvPage *ev_page;
1210                                         
1211                                         gint p = page + j;
1212
1213                                         if (p < 0 || p >= n_pages)
1214                                                 break;
1215
1216                                         ev_page = ev_document_get_page (document, page_list[p]);
1217                                         ev_render_context_set_page (rc, ev_page);
1218                                         g_object_unref (ev_page);
1219                                         
1220                                         ev_file_exporter_do_page (EV_FILE_EXPORTER (document), rc);
1221                                 }
1222
1223                                 ev_file_exporter_end_page (EV_FILE_EXPORTER (document));
1224                         }
1225
1226                         page += step;
1227                 }
1228
1229                 if (!job_print->collate)
1230                         break;
1231         }
1232
1233         ev_file_exporter_end (EV_FILE_EXPORTER (document));
1234         ev_document_doc_mutex_unlock ();
1235         
1236         g_free (page_list);
1237         close (fd);
1238         g_object_unref (rc);
1239         
1240         ev_job_succeeded (job);
1241         
1242         return FALSE;
1243 }
1244
1245 static void
1246 ev_job_print_class_init (EvJobPrintClass *class)
1247 {
1248         GObjectClass *oclass = G_OBJECT_CLASS (class);
1249         EvJobClass   *job_class = EV_JOB_CLASS (class);
1250
1251         oclass->dispose = ev_job_print_dispose;
1252         job_class->run = ev_job_print_run;
1253 }
1254
1255 EvJob *
1256 ev_job_print_new (EvDocument    *document,
1257                   const gchar   *format,
1258                   gdouble        width,
1259                   gdouble        height,
1260                   EvPrintRange  *ranges,
1261                   gint           n_ranges,
1262                   EvPrintPageSet page_set,
1263                   gint           pages_per_sheet,
1264                   gint           copies,
1265                   gdouble        collate,
1266                   gdouble        reverse)
1267 {
1268         EvJobPrint *job;
1269
1270         ev_debug_message (DEBUG_JOBS, "format: %s, width: %f, height:%f,"
1271                           "n_ranges: %d, pages_per_sheet: %d, copies: %d,"
1272                           "collate: %s, reverse: %s",
1273                           format, width, height, n_ranges, pages_per_sheet, copies,
1274                           collate ? "True" : "False", reverse  ? "True" : "False");
1275
1276         job = g_object_new (EV_TYPE_JOB_PRINT, NULL);
1277
1278         EV_JOB (job)->document = g_object_ref (document);
1279
1280         job->format = format;
1281         
1282         job->temp_file = NULL;
1283
1284         job->width = width;
1285         job->height = height;
1286
1287         job->ranges = ranges;
1288         job->n_ranges = n_ranges;
1289
1290         job->page_set = page_set;
1291
1292         job->pages_per_sheet = CLAMP (pages_per_sheet, 1, 16);
1293         
1294         job->copies = copies;
1295         job->collate = collate;
1296         job->reverse = reverse;
1297         
1298         return EV_JOB (job);
1299 }
1300