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