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