]> www.fi.muni.cz Git - evince.git/blob - shell/ev-jobs.c
Add EvPage so that we can hold a reference to the backend page. Form
[evince.git] / shell / ev-jobs.c
1 #include <config.h>
2 #include "ev-jobs.h"
3 #include "ev-job-queue.h"
4 #include "ev-document-thumbnails.h"
5 #include "ev-document-links.h"
6 #include "ev-document-images.h"
7 #include "ev-document-forms.h"
8 #include "ev-file-exporter.h"
9 #include "ev-document-factory.h"
10 #include "ev-document-misc.h"
11 #include "ev-file-helpers.h"
12 #include "ev-document-fonts.h"
13 #include "ev-async-renderer.h"
14
15 #include <errno.h>
16 #include <glib/gstdio.h>
17 #include <glib/gi18n.h>
18 #include <unistd.h>
19
20 static void ev_job_init                 (EvJob               *job);
21 static void ev_job_class_init           (EvJobClass          *class);
22 static void ev_job_links_init           (EvJobLinks          *job);
23 static void ev_job_links_class_init     (EvJobLinksClass     *class);
24 static void ev_job_render_init          (EvJobRender         *job);
25 static void ev_job_render_class_init    (EvJobRenderClass    *class);
26 static void ev_job_thumbnail_init       (EvJobThumbnail      *job);
27 static void ev_job_thumbnail_class_init (EvJobThumbnailClass *class);
28 static void ev_job_load_init            (EvJobLoad           *job);
29 static void ev_job_load_class_init      (EvJobLoadClass      *class);
30 static void ev_job_save_init            (EvJobSave           *job);
31 static void ev_job_save_class_init      (EvJobSaveClass      *class);
32 static void ev_job_print_init           (EvJobPrint          *job);
33 static void ev_job_print_class_init     (EvJobPrintClass     *class);
34
35 enum {
36         FINISHED,
37         LAST_SIGNAL
38 };
39
40 enum {
41         PAGE_READY,
42         RENDER_LAST_SIGNAL
43 };
44
45 static guint job_signals[LAST_SIGNAL] = { 0 };
46 static guint job_render_signals[RENDER_LAST_SIGNAL] = { 0 };
47
48 G_DEFINE_TYPE (EvJob, ev_job, G_TYPE_OBJECT)
49 G_DEFINE_TYPE (EvJobLinks, ev_job_links, EV_TYPE_JOB)
50 G_DEFINE_TYPE (EvJobRender, ev_job_render, EV_TYPE_JOB)
51 G_DEFINE_TYPE (EvJobThumbnail, ev_job_thumbnail, EV_TYPE_JOB)
52 G_DEFINE_TYPE (EvJobFonts, ev_job_fonts, EV_TYPE_JOB)
53 G_DEFINE_TYPE (EvJobLoad, ev_job_load, EV_TYPE_JOB)
54 G_DEFINE_TYPE (EvJobSave, ev_job_save, EV_TYPE_JOB)
55 G_DEFINE_TYPE (EvJobPrint, ev_job_print, EV_TYPE_JOB)
56
57 static void ev_job_init (EvJob *job) { /* Do Nothing */ }
58
59 static void
60 ev_job_dispose (GObject *object)
61 {
62         EvJob *job;
63
64         job = EV_JOB (object);
65
66         if (job->document) {
67                 g_object_unref (job->document);
68                 job->document = NULL;
69         }
70
71         (* G_OBJECT_CLASS (ev_job_parent_class)->dispose) (object);
72 }
73
74 static void
75 ev_job_class_init (EvJobClass *class)
76 {
77         GObjectClass *oclass;
78
79         oclass = G_OBJECT_CLASS (class);
80
81         oclass->dispose = ev_job_dispose;
82
83         job_signals [FINISHED] =
84                 g_signal_new ("finished",
85                               EV_TYPE_JOB,
86                               G_SIGNAL_RUN_LAST,
87                               G_STRUCT_OFFSET (EvJobClass, finished),
88                               NULL, NULL,
89                               g_cclosure_marshal_VOID__VOID,
90                               G_TYPE_NONE, 0);
91 }
92
93
94 static void ev_job_links_init (EvJobLinks *job) { /* Do Nothing */ }
95
96 static void
97 ev_job_links_dispose (GObject *object)
98 {
99         EvJobLinks *job;
100
101         job = EV_JOB_LINKS (object);
102
103         if (job->model) {
104                 g_object_unref (job->model);
105                 job->model = NULL;
106         }
107
108         (* G_OBJECT_CLASS (ev_job_links_parent_class)->dispose) (object);
109 }
110
111 static void
112 ev_job_links_class_init (EvJobLinksClass *class)
113 {
114         GObjectClass *oclass;
115
116         oclass = G_OBJECT_CLASS (class);
117
118         oclass->dispose = ev_job_links_dispose;
119 }
120
121
122 static void ev_job_render_init (EvJobRender *job) { /* Do Nothing */ }
123
124 static void
125 ev_job_render_dispose (GObject *object)
126 {
127         EvJobRender *job;
128
129         job = EV_JOB_RENDER (object);
130
131         if (job->surface) {
132                 cairo_surface_destroy (job->surface);
133                 job->surface = NULL;
134         }
135
136         if (job->rc) {
137                 g_object_unref (job->rc);
138                 job->rc = NULL;
139         }
140
141         if (job->selection) {
142                 cairo_surface_destroy (job->selection);
143                 job->selection = NULL;
144         }
145
146         if (job->selection_region) {
147                 gdk_region_destroy (job->selection_region);
148                 job->selection_region = NULL;
149         }
150
151         (* G_OBJECT_CLASS (ev_job_render_parent_class)->dispose) (object);
152 }
153
154 static void
155 ev_job_render_class_init (EvJobRenderClass *class)
156 {
157         GObjectClass *oclass;
158
159         oclass = G_OBJECT_CLASS (class);
160
161         job_render_signals [PAGE_READY] =
162                 g_signal_new ("page-ready",
163                               EV_TYPE_JOB_RENDER,
164                               G_SIGNAL_RUN_LAST,
165                               G_STRUCT_OFFSET (EvJobRenderClass, page_ready),
166                               NULL, NULL,
167                               g_cclosure_marshal_VOID__VOID,
168                               G_TYPE_NONE, 0);
169
170         oclass->dispose = ev_job_render_dispose;
171 }
172
173 static void ev_job_thumbnail_init (EvJobThumbnail *job) { /* Do Nothing */ }
174
175 static void
176 ev_job_thumbnail_dispose (GObject *object)
177 {
178         EvJobThumbnail *job;
179
180         job = EV_JOB_THUMBNAIL (object);
181
182         if (job->thumbnail) {
183                 g_object_unref (job->thumbnail);
184                 job->thumbnail = NULL;
185         }
186
187         (* G_OBJECT_CLASS (ev_job_thumbnail_parent_class)->dispose) (object);
188 }
189
190 static void
191 ev_job_thumbnail_class_init (EvJobThumbnailClass *class)
192 {
193         GObjectClass *oclass;
194
195         oclass = G_OBJECT_CLASS (class);
196
197         oclass->dispose = ev_job_thumbnail_dispose;
198 }
199
200 static void ev_job_print_init (EvJobPrint *job) { /* Do Nothing */ }
201
202 static void
203 ev_job_print_dispose (GObject *object)
204 {
205         EvJobPrint *job;
206
207         job = EV_JOB_PRINT (object);
208
209         if (job->temp_file) {
210                 g_unlink (job->temp_file);
211                 g_free (job->temp_file);
212                 job->temp_file = NULL;
213         }
214
215         if (job->error) {
216                 g_error_free (job->error);
217                 job->error = NULL;
218         }
219
220         if (job->ranges) {
221                 g_free (job->ranges);
222                 job->ranges = NULL;
223                 job->n_ranges = 0;
224         }
225
226         (* G_OBJECT_CLASS (ev_job_print_parent_class)->dispose) (object);
227 }
228
229 static void
230 ev_job_print_class_init (EvJobPrintClass *class)
231 {
232         GObjectClass *oclass;
233
234         oclass = G_OBJECT_CLASS (class);
235
236         oclass->dispose = ev_job_print_dispose;
237 }
238
239 /* Public functions */
240 void
241 ev_job_finished (EvJob *job)
242 {
243         g_return_if_fail (EV_IS_JOB (job));
244
245         g_signal_emit (job, job_signals[FINISHED], 0);
246 }
247
248 EvJob *
249 ev_job_links_new (EvDocument *document)
250 {
251         EvJob *job;
252
253         job = g_object_new (EV_TYPE_JOB_LINKS, NULL);
254         job->document = g_object_ref (document);
255
256         return job;
257 }
258
259 void
260 ev_job_links_run (EvJobLinks *job)
261 {
262         g_return_if_fail (EV_IS_JOB_LINKS (job));
263
264         ev_document_doc_mutex_lock ();
265         job->model = ev_document_links_get_links_model (EV_DOCUMENT_LINKS (EV_JOB (job)->document));
266         EV_JOB (job)->finished = TRUE;
267         ev_document_doc_mutex_unlock ();
268 }
269
270
271 EvJob *
272 ev_job_render_new (EvDocument      *document,
273                    EvRenderContext *rc,
274                    gint             width,
275                    gint             height,
276                    EvRectangle     *selection_points,
277                    EvSelectionStyle selection_style,
278                    GdkColor        *text,
279                    GdkColor        *base,
280                    gboolean         include_forms,
281                    gboolean         include_links,
282                    gboolean         include_images,
283                    gboolean         include_text,
284                    gboolean         include_selection)
285 {
286         EvJobRender *job;
287
288         g_return_val_if_fail (EV_IS_RENDER_CONTEXT (rc), NULL);
289         if (include_selection)
290                 g_return_val_if_fail (selection_points != NULL, NULL);
291
292         job = g_object_new (EV_TYPE_JOB_RENDER, NULL);
293
294         EV_JOB (job)->document = g_object_ref (document);
295         job->rc = g_object_ref (rc);
296         job->target_width = width;
297         job->target_height = height;
298         job->selection_style = selection_style;
299         job->text = *text;
300         job->base = *base;
301         job->include_forms = include_forms;
302         job->include_links = include_links;
303         job->include_images = include_images;
304         job->include_text = include_text;
305         job->include_selection = include_selection;
306
307         if (include_selection)
308                 job->selection_points = *selection_points;
309
310         if (EV_IS_ASYNC_RENDERER (document)) {  
311                 EV_JOB (job)->async = TRUE;
312         }
313
314         return EV_JOB (job);
315 }
316
317 static void
318 render_finished_cb (EvDocument      *document,
319                     GdkPixbuf       *pixbuf,
320                     EvJobRender     *job)
321 {
322         g_signal_handlers_disconnect_by_func (EV_JOB (job)->document,
323                                               render_finished_cb, job);
324
325         /* FIXME: ps backend should be ported to cairo */
326         job->surface = ev_document_misc_surface_from_pixbuf (pixbuf);
327         job->page_ready = TRUE;
328         g_signal_emit (job, job_render_signals[PAGE_READY], 0);
329         EV_JOB (job)->finished = TRUE;
330         ev_job_finished (EV_JOB (job));
331 }
332
333 static gboolean
334 notify_page_ready (EvJobRender *job)
335 {
336         g_signal_emit (job, job_render_signals[PAGE_READY], 0);
337
338         return FALSE;
339 }
340
341 static void
342 ev_job_render_page_ready (EvJobRender *job)
343 {
344         job->page_ready = TRUE;
345         g_idle_add_full (G_PRIORITY_HIGH_IDLE,
346                          (GSourceFunc)notify_page_ready,
347                          g_object_ref (job),
348                          (GDestroyNotify)g_object_unref);
349 }
350
351 void
352 ev_job_render_run (EvJobRender *job)
353 {
354         g_return_if_fail (EV_IS_JOB_RENDER (job));
355
356         ev_document_doc_mutex_lock ();
357
358         if (EV_JOB (job)->async) {
359                 EvAsyncRenderer *renderer = EV_ASYNC_RENDERER (EV_JOB (job)->document);
360                 ev_async_renderer_render_pixbuf (renderer, job->rc->page->index, job->rc->scale,
361                                                  job->rc->rotation);
362                 g_signal_connect (EV_JOB (job)->document, "render_finished",
363                                   G_CALLBACK (render_finished_cb), job);
364         } else {
365                 ev_document_fc_mutex_lock ();
366                 
367                 job->surface = ev_document_render (EV_JOB (job)->document, job->rc);
368                 if (job->include_selection && EV_IS_SELECTION (EV_JOB (job)->document)) {
369                         ev_selection_render_selection (EV_SELECTION (EV_JOB (job)->document),
370                                                        job->rc,
371                                                        &(job->selection),
372                                                        &(job->selection_points),
373                                                        NULL,
374                                                        job->selection_style,
375                                                        &(job->text), &(job->base));
376                         job->selection_region =
377                                 ev_selection_get_selection_region (EV_SELECTION (EV_JOB (job)->document),
378                                                                    job->rc,
379                                                                    job->selection_style,
380                                                                    &(job->selection_points));
381                 }
382
383                 ev_job_render_page_ready (job);
384                 
385                 ev_document_fc_mutex_unlock ();
386                 
387                 if (job->include_text && EV_IS_SELECTION (EV_JOB (job)->document))
388                         job->text_mapping =
389                                 ev_selection_get_selection_map (EV_SELECTION (EV_JOB (job)->document),
390                                                                 job->rc);
391                 if (job->include_links && EV_IS_DOCUMENT_LINKS (EV_JOB (job)->document))
392                         job->link_mapping =
393                                 ev_document_links_get_links (EV_DOCUMENT_LINKS (EV_JOB (job)->document),
394                                                              job->rc->page->index);
395                 if (job->include_forms && EV_IS_DOCUMENT_FORMS (EV_JOB (job)->document))
396                         job->form_field_mapping =
397                                 ev_document_forms_get_form_fields (EV_DOCUMENT_FORMS (EV_JOB (job)->document),
398                                                                    job->rc->page);
399                 if (job->include_images && EV_IS_DOCUMENT_IMAGES (EV_JOB (job)->document))
400                         job->image_mapping =
401                                 ev_document_images_get_image_mapping (EV_DOCUMENT_IMAGES (EV_JOB (job)->document),
402                                                                       job->rc->page->index);
403                 EV_JOB (job)->finished = TRUE;
404         }
405
406         ev_document_doc_mutex_unlock ();
407 }
408
409 EvJob *
410 ev_job_thumbnail_new (EvDocument *document,
411                       gint        page,
412                       gint        rotation,
413                       gdouble     scale)
414 {
415         EvJobThumbnail *job;
416
417         job = g_object_new (EV_TYPE_JOB_THUMBNAIL, NULL);
418
419         EV_JOB (job)->document = g_object_ref (document);
420         job->page = page;
421         job->rotation = rotation;
422         job->scale = scale;
423
424         return EV_JOB (job);
425 }
426
427 void
428 ev_job_thumbnail_run (EvJobThumbnail *job)
429 {
430         EvRenderContext *rc;
431         EvPage          *page;
432         
433         g_return_if_fail (EV_IS_JOB_THUMBNAIL (job));
434
435         ev_document_doc_mutex_lock ();
436
437         page = ev_document_get_page (EV_JOB (job)->document, job->page);
438         rc = ev_render_context_new (page, job->rotation, job->scale);
439         g_object_unref (page);
440
441         job->thumbnail =
442                 ev_document_thumbnails_get_thumbnail (EV_DOCUMENT_THUMBNAILS (EV_JOB (job)->document),
443                                                       rc, TRUE);
444         g_object_unref (rc);
445         ev_document_doc_mutex_unlock ();
446         
447         EV_JOB (job)->finished = TRUE;
448 }
449
450 static void ev_job_fonts_init (EvJobFonts *job) { /* Do Nothing */ }
451
452 static void ev_job_fonts_class_init (EvJobFontsClass *class) { /* Do Nothing */ }
453
454 EvJob *
455 ev_job_fonts_new (EvDocument *document)
456 {
457         EvJobFonts *job;
458
459         job = g_object_new (EV_TYPE_JOB_FONTS, NULL);
460
461         EV_JOB (job)->document = g_object_ref (document);
462
463         return EV_JOB (job);
464 }
465
466 void
467 ev_job_fonts_run (EvJobFonts *job)
468 {
469         EvDocumentFonts *fonts;
470
471         g_return_if_fail (EV_IS_JOB_FONTS (job));
472
473         ev_document_doc_mutex_lock ();
474         
475         fonts = EV_DOCUMENT_FONTS (EV_JOB (job)->document);
476         ev_document_fc_mutex_lock ();
477         job->scan_completed = !ev_document_fonts_scan (fonts, 20);
478         ev_document_fc_mutex_unlock ();
479         
480         EV_JOB (job)->finished = TRUE;
481
482         ev_document_doc_mutex_unlock ();
483 }
484
485 static void ev_job_load_init (EvJobLoad *job) { /* Do Nothing */ }
486
487 static void
488 ev_job_load_dispose (GObject *object)
489 {
490         EvJobLoad *job = EV_JOB_LOAD (object);
491
492         if (job->uri) {
493                 g_free (job->uri);
494                 job->uri = NULL;
495         }
496
497         if (job->error) {
498                 g_error_free (job->error);
499                 job->error = NULL;
500         }
501
502         if (job->dest) {
503                 g_object_unref (job->dest);
504                 job->dest = NULL;
505         }
506
507         if (job->search_string) {
508                 g_free (job->search_string);
509                 job->search_string = NULL;
510         }
511
512         (* G_OBJECT_CLASS (ev_job_load_parent_class)->dispose) (object);
513 }
514
515 static void
516 ev_job_load_class_init (EvJobLoadClass *class)
517 {
518         GObjectClass *oclass;
519
520         oclass = G_OBJECT_CLASS (class);
521
522         oclass->dispose = ev_job_load_dispose;
523 }
524
525
526 EvJob *
527 ev_job_load_new (const gchar    *uri,
528                  EvLinkDest     *dest,
529                  EvWindowRunMode mode,
530                  const gchar    *search_string)
531 {
532         EvJobLoad *job;
533
534         job = g_object_new (EV_TYPE_JOB_LOAD, NULL);
535
536         job->uri = g_strdup (uri);
537         if (dest)
538                 job->dest = g_object_ref (dest);
539
540         job->mode = mode;
541         if (search_string)
542                 job->search_string = g_strdup (search_string);
543
544         return EV_JOB (job);
545 }
546
547 void
548 ev_job_load_set_uri (EvJobLoad *job, const gchar *uri)
549 {
550         if (job->uri)
551                 g_free (job->uri);
552         job->uri = g_strdup (uri);
553 }
554
555 void
556 ev_job_load_run (EvJobLoad *job)
557 {
558         g_return_if_fail (EV_IS_JOB_LOAD (job));
559         
560         if (job->error) {
561                 g_error_free (job->error);
562                 job->error = NULL;
563         }
564
565         ev_document_fc_mutex_lock ();
566         
567         /* This job may already have a document even if the job didn't complete
568            because, e.g., a password is required - if so, just reload rather than
569            creating a new instance */
570         if (EV_JOB (job)->document) {
571                 ev_document_load (EV_JOB (job)->document,
572                                   job->uri,
573                                   &job->error);
574         } else {
575                 EV_JOB(job)->document =
576                         ev_document_factory_get_document (job->uri,
577                                                           &job->error);
578         }
579
580         ev_document_fc_mutex_unlock ();
581         EV_JOB (job)->finished = TRUE;
582 }
583
584 static void ev_job_save_init (EvJobSave *job) { /* Do Nothing */ }
585
586 static void
587 ev_job_save_dispose (GObject *object)
588 {
589         EvJobSave *job = EV_JOB_SAVE (object);
590
591         if (job->uri) {
592                 g_free (job->uri);
593                 job->uri = NULL;
594         }
595
596         if (job->document_uri) {
597                 g_free (job->document_uri);
598                 job->document_uri = NULL;
599         }
600
601         if (job->error) {
602                 g_error_free (job->error);
603                 job->error = NULL;
604         }
605
606         (* G_OBJECT_CLASS (ev_job_save_parent_class)->dispose) (object);
607 }
608
609 static void
610 ev_job_save_class_init (EvJobSaveClass *class)
611 {
612         GObjectClass *oclass;
613
614         oclass = G_OBJECT_CLASS (class);
615
616         oclass->dispose = ev_job_save_dispose;
617 }
618
619 EvJob *
620 ev_job_save_new (EvDocument  *document,
621                  const gchar *uri,
622                  const gchar *document_uri)
623 {
624         EvJobSave *job;
625
626         job = g_object_new (EV_TYPE_JOB_SAVE, NULL);
627
628         EV_JOB (job)->document = g_object_ref (document);
629         job->uri = g_strdup (uri);
630         job->document_uri = g_strdup (document_uri);
631         job->error = NULL;
632
633         return EV_JOB (job);
634 }
635
636 void
637 ev_job_save_run (EvJobSave *job)
638 {
639         gint   fd;
640         gchar *filename;
641         gchar *tmp_filename;
642         gchar *local_uri;
643         
644         filename = ev_tmp_filename ("saveacopy");
645         tmp_filename = g_strdup_printf ("%s.XXXXXX", filename);
646         g_free (filename);
647
648         fd = g_mkstemp (tmp_filename);
649         if (fd == -1) {
650                 gchar *display_name;
651                 gint   save_errno = errno;
652
653                 display_name = g_filename_display_name (tmp_filename);
654                 g_set_error (&(job->error),
655                              G_FILE_ERROR,
656                              g_file_error_from_errno (save_errno),
657                              _("Failed to create file “%s”: %s"),
658                              display_name, g_strerror (save_errno));
659                 g_free (display_name);
660                 g_free (tmp_filename);
661
662                 return;
663         }
664
665         ev_document_doc_mutex_lock ();
666
667         /* Save document to temp filename */
668         local_uri = g_filename_to_uri (tmp_filename, NULL, NULL);
669         ev_document_save (EV_JOB (job)->document, local_uri, &(job->error));
670         close (fd);
671
672         ev_document_doc_mutex_unlock ();
673
674         if (job->error) {
675                 g_free (local_uri);
676                 return;
677         }
678
679         /* If original document was compressed,
680          * compress it again before saving
681          */
682         if (g_object_get_data (G_OBJECT (EV_JOB (job)->document),
683                                "uri-uncompressed")) {
684                 EvCompressionType ctype = EV_COMPRESSION_NONE;
685                 const gchar      *ext;
686                 gchar            *uri_comp;
687                 
688                 ext = g_strrstr (job->document_uri, ".gz");
689                 if (ext && g_ascii_strcasecmp (ext, ".gz") == 0)
690                         ctype = EV_COMPRESSION_GZIP;
691                 
692                 ext = g_strrstr (job->document_uri, ".bz2");
693                 if (ext && g_ascii_strcasecmp (ext, ".bz2") == 0)
694                         ctype = EV_COMPRESSION_BZIP2;
695
696                 uri_comp = ev_file_compress (local_uri, ctype, &(job->error));
697                 g_free (local_uri);
698                 ev_tmp_filename_unlink (tmp_filename);
699
700                 if (!uri_comp || job->error) {
701                         local_uri = NULL;
702                 } else {
703                         local_uri = uri_comp;
704                 }
705         }
706
707         g_free (tmp_filename);
708         
709         if (job->error) {
710                 g_free (local_uri);
711                 return;
712         }
713
714         if (!local_uri)
715                 return;
716
717         ev_xfer_uri_simple (local_uri, job->uri, &(job->error));
718         ev_tmp_uri_unlink (local_uri);
719 }
720
721 EvJob *
722 ev_job_print_new (EvDocument    *document,
723                   const gchar   *format,
724                   gdouble        width,
725                   gdouble        height,
726                   EvPrintRange  *ranges,
727                   gint           n_ranges,
728                   EvPrintPageSet page_set,
729                   gint           pages_per_sheet,
730                   gint           copies,
731                   gdouble        collate,
732                   gdouble        reverse)
733 {
734         EvJobPrint *job;
735
736         job = g_object_new (EV_TYPE_JOB_PRINT, NULL);
737
738         EV_JOB (job)->document = g_object_ref (document);
739
740         job->format = format;
741         
742         job->temp_file = NULL;
743         job->error = NULL;
744
745         job->width = width;
746         job->height = height;
747
748         job->ranges = ranges;
749         job->n_ranges = n_ranges;
750
751         job->page_set = page_set;
752
753         job->pages_per_sheet = CLAMP (pages_per_sheet, 1, 16);
754         
755         job->copies = copies;
756         job->collate = collate;
757         job->reverse = reverse;
758         
759         return EV_JOB (job);
760 }
761
762 static gint
763 ev_print_job_get_first_page (EvJobPrint *job)
764 {
765         gint i;
766         gint first_page = G_MAXINT;
767         
768         if (job->n_ranges == 0)
769                 return 0;
770
771         for (i = 0; i < job->n_ranges; i++) {
772                 if (job->ranges[i].start < first_page)
773                         first_page = job->ranges[i].start;
774         }
775
776         return MAX (0, first_page);
777 }
778
779 static gint
780 ev_print_job_get_last_page (EvJobPrint *job)
781 {
782         gint i;
783         gint last_page = G_MININT;
784         gint max_page;
785
786         max_page = ev_document_get_n_pages (EV_JOB (job)->document) - 1;
787
788         if (job->n_ranges == 0)
789                 return max_page;
790
791         for (i = 0; i < job->n_ranges; i++) {
792                 if (job->ranges[i].end > last_page)
793                         last_page = job->ranges[i].end;
794         }
795
796         return MIN (max_page, last_page);
797 }
798
799 static gboolean
800 ev_print_job_print_page_in_set (EvJobPrint *job,
801                                 gint        page)
802 {
803         switch (job->page_set) {
804                 case EV_PRINT_PAGE_SET_EVEN:
805                         return page % 2 == 0;
806                 case EV_PRINT_PAGE_SET_ODD:
807                         return page % 2 != 0;
808                 case EV_PRINT_PAGE_SET_ALL:
809                         return TRUE;
810         }
811
812         return FALSE;
813 }
814
815 static gint *
816 ev_job_print_get_page_list (EvJobPrint *job,
817                             gint       *n_pages)
818 {
819         gint  i, j, page, max_page;
820         gint  pages = 0;
821         gint *page_list;
822
823         max_page = ev_document_get_n_pages (EV_JOB (job)->document) - 1;
824
825         for (i = 0; i < job->n_ranges; i++) {
826                 gint rsize;
827                 gint start, end;
828
829                 if (job->ranges[i].start == -1)
830                         job->ranges[i].start = 0;
831                 if (job->ranges[i].end == -1)
832                         job->ranges[i].end = max_page;
833
834                 if (job->ranges[i].start > max_page)
835                         continue;
836                 
837                 start = job->ranges[i].start + 1;
838                 end = job->ranges[i].end <= max_page ? job->ranges[i].end + 1 : max_page + 1;
839                 rsize = end - start + 1;
840
841                 switch (job->page_set) {
842                         case EV_PRINT_PAGE_SET_EVEN:
843                                 pages += start % 2 == 0 ? (rsize / 2) + (rsize % 2) : (rsize / 2);
844                                 break;
845                         case EV_PRINT_PAGE_SET_ODD:
846                                 pages += start % 2 != 0 ? (rsize / 2) + (rsize % 2) : (rsize / 2);
847                                 break;
848                         default:
849                                 pages += rsize;
850                                 break;
851                 }
852         }
853
854         *n_pages = pages;
855
856         if (pages == 0)
857                 return NULL;
858
859         page_list = g_new (gint, pages);
860
861         page = 0;
862         for (i = 0; i < job->n_ranges; i++) {
863                 for (j = job->ranges[i].start; j <= job->ranges[i].end; j++) {
864                         if (j > max_page)
865                                 break;
866                 
867                         if (ev_print_job_print_page_in_set (job, j + 1))
868                                 page_list[page++] = j;
869                 }
870         }
871
872         return page_list;
873 }
874
875 void
876 ev_job_print_run (EvJobPrint *job)
877 {
878         EvDocument            *document = EV_JOB (job)->document;
879         EvFileExporterContext  fc;
880         EvRenderContext       *rc;
881         gint                   fd;
882         gint                  *page_list;
883         gint                   n_pages;
884         gint                   last_page;
885         gint                   first_page;
886         gint                   i, j;
887         gchar                 *filename;
888         
889         g_return_if_fail (EV_IS_JOB_PRINT (job));
890
891         if (job->temp_file)
892                 g_free (job->temp_file);
893         job->temp_file = NULL;
894         
895         if (job->error)
896                 g_error_free (job->error);
897         job->error = NULL;
898
899         filename = g_strdup_printf ("evince_print.%s.XXXXXX", job->format);
900         fd = g_file_open_tmp (filename, &job->temp_file, &job->error);
901         g_free (filename);
902         if (fd <= -1) {
903                 EV_JOB (job)->finished = TRUE;
904                 return;
905         }
906
907         page_list = ev_job_print_get_page_list (job, &n_pages);
908         if (n_pages == 0) {
909                 close (fd);
910                 EV_JOB (job)->finished = TRUE;
911                 return;
912         }
913
914         first_page = ev_print_job_get_first_page (job);
915         last_page = ev_print_job_get_last_page (job);
916
917         fc.format = g_ascii_strcasecmp (job->format, "pdf") == 0 ?
918                 EV_FILE_FORMAT_PDF : EV_FILE_FORMAT_PS;
919         fc.filename = job->temp_file;
920         fc.first_page = MIN (first_page, last_page);
921         fc.last_page = MAX (first_page, last_page);
922         fc.paper_width = job->width;
923         fc.paper_height = job->height;
924         fc.duplex = FALSE;
925         fc.pages_per_sheet = MAX (1, job->pages_per_sheet);
926
927         rc = ev_render_context_new (NULL, 0, 1.0);
928
929         ev_document_doc_mutex_lock ();
930         ev_file_exporter_begin (EV_FILE_EXPORTER (document), &fc);
931
932         for (i = 0; i < job->copies; i++) {
933                 gint page, step;
934                 gint n_copies;
935                 
936                 step = job->reverse ? -1 * job->pages_per_sheet : job->pages_per_sheet;
937                 page = job->reverse ? ((n_pages - 1) / job->pages_per_sheet) * job->pages_per_sheet : 0;
938                 n_copies = job->collate ? 1 : job->copies;
939
940                 while ((job->reverse && (page >= 0)) || (!job->reverse && (page < n_pages))) {
941                         gint k;
942
943                         for (k = 0; k < n_copies; k++) {
944                                 ev_file_exporter_begin_page (EV_FILE_EXPORTER (document));
945                                 
946                                 for (j = 0; j < job->pages_per_sheet; j++) {
947                                         EvPage *ev_page;
948                                         
949                                         gint p = page + j;
950
951                                         if (p < 0 || p >= n_pages)
952                                                 break;
953
954                                         ev_page = ev_document_get_page (document, page_list[p]);
955                                         ev_render_context_set_page (rc, ev_page);
956                                         g_object_unref (ev_page);
957                                         
958                                         ev_file_exporter_do_page (EV_FILE_EXPORTER (document), rc);
959                                 }
960
961                                 ev_file_exporter_end_page (EV_FILE_EXPORTER (document));
962                         }
963
964                         page += step;
965                 }
966
967                 if (!job->collate)
968                         break;
969         }
970
971         ev_file_exporter_end (EV_FILE_EXPORTER (document));
972         ev_document_doc_mutex_unlock ();
973         
974         g_free (page_list);
975         close (fd);
976         g_object_unref (rc);
977         
978         EV_JOB (job)->finished = TRUE;
979 }