]> www.fi.muni.cz Git - evince.git/blob - shell/ev-jobs.c
Allow printing multiple pages per sheet. Fixes bug #395573.
[evince.git] / shell / ev-jobs.c
1 #include "ev-jobs.h"
2 #include "ev-job-queue.h"
3 #include "ev-document-thumbnails.h"
4 #include "ev-document-links.h"
5 #include "ev-document-images.h"
6 #include "ev-document-forms.h"
7 #include "ev-document-factory.h"
8 #include "ev-document-misc.h"
9 #include "ev-file-helpers.h"
10 #include "ev-document-fonts.h"
11 #include "ev-selection.h"
12 #include "ev-async-renderer.h"
13 #include "ev-file-exporter.h"
14 #include "ev-window.h"
15
16 #include <glib/gstdio.h>
17 #include <unistd.h>
18 #include <libgnomevfs/gnome-vfs-uri.h>
19 #include <libgnomevfs/gnome-vfs-utils.h>
20 #include <libgnomevfs/gnome-vfs-ops.h>
21
22 static void ev_job_init                 (EvJob               *job);
23 static void ev_job_class_init           (EvJobClass          *class);
24 static void ev_job_links_init           (EvJobLinks          *job);
25 static void ev_job_links_class_init     (EvJobLinksClass     *class);
26 static void ev_job_render_init          (EvJobRender         *job);
27 static void ev_job_render_class_init    (EvJobRenderClass    *class);
28 static void ev_job_thumbnail_init       (EvJobThumbnail      *job);
29 static void ev_job_thumbnail_class_init (EvJobThumbnailClass *class);
30 static void ev_job_load_init            (EvJobLoad           *job);
31 static void ev_job_load_class_init      (EvJobLoadClass      *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 (EvJobPrint, ev_job_print, EV_TYPE_JOB)
55
56 static void ev_job_init (EvJob *job) { /* Do Nothing */ }
57
58 static void
59 ev_job_dispose (GObject *object)
60 {
61         EvJob *job;
62
63         job = EV_JOB (object);
64
65         if (job->document) {
66                 g_object_unref (job->document);
67                 job->document = NULL;
68         }
69
70         (* G_OBJECT_CLASS (ev_job_parent_class)->dispose) (object);
71 }
72
73 static void
74 ev_job_class_init (EvJobClass *class)
75 {
76         GObjectClass *oclass;
77
78         oclass = G_OBJECT_CLASS (class);
79
80         oclass->dispose = ev_job_dispose;
81
82         job_signals [FINISHED] =
83                 g_signal_new ("finished",
84                               EV_TYPE_JOB,
85                               G_SIGNAL_RUN_LAST,
86                               G_STRUCT_OFFSET (EvJobClass, finished),
87                               NULL, NULL,
88                               g_cclosure_marshal_VOID__VOID,
89                               G_TYPE_NONE, 0);
90 }
91
92
93 static void ev_job_links_init (EvJobLinks *job) { /* Do Nothing */ }
94
95 static void
96 ev_job_links_dispose (GObject *object)
97 {
98         EvJobLinks *job;
99
100         job = EV_JOB_LINKS (object);
101
102         if (job->model) {
103                 g_object_unref (job->model);
104                 job->model = NULL;
105         }
106
107         (* G_OBJECT_CLASS (ev_job_links_parent_class)->dispose) (object);
108 }
109
110 static void
111 ev_job_links_class_init (EvJobLinksClass *class)
112 {
113         GObjectClass *oclass;
114
115         oclass = G_OBJECT_CLASS (class);
116
117         oclass->dispose = ev_job_links_dispose;
118 }
119
120
121 static void ev_job_render_init (EvJobRender *job) { /* Do Nothing */ }
122
123 static void
124 ev_job_render_dispose (GObject *object)
125 {
126         EvJobRender *job;
127
128         job = EV_JOB_RENDER (object);
129
130         if (job->surface) {
131                 cairo_surface_destroy (job->surface);
132                 job->surface = NULL;
133         }
134
135         if (job->rc) {
136                 g_object_unref (job->rc);
137                 job->rc = NULL;
138         }
139
140         if (job->selection) {
141                 cairo_surface_destroy (job->selection);
142                 job->selection = NULL;
143         }
144
145         if (job->selection_region) {
146                 gdk_region_destroy (job->selection_region);
147                 job->selection_region = NULL;
148         }
149
150         (* G_OBJECT_CLASS (ev_job_render_parent_class)->dispose) (object);
151 }
152
153 static void
154 ev_job_render_class_init (EvJobRenderClass *class)
155 {
156         GObjectClass *oclass;
157
158         oclass = G_OBJECT_CLASS (class);
159
160         job_render_signals [PAGE_READY] =
161                 g_signal_new ("page-ready",
162                               EV_TYPE_JOB_RENDER,
163                               G_SIGNAL_RUN_LAST,
164                               G_STRUCT_OFFSET (EvJobRenderClass, page_ready),
165                               NULL, NULL,
166                               g_cclosure_marshal_VOID__VOID,
167                               G_TYPE_NONE, 0);
168
169         oclass->dispose = ev_job_render_dispose;
170 }
171
172 static void ev_job_thumbnail_init (EvJobThumbnail *job) { /* Do Nothing */ }
173
174 static void
175 ev_job_thumbnail_dispose (GObject *object)
176 {
177         EvJobThumbnail *job;
178
179         job = EV_JOB_THUMBNAIL (object);
180
181         if (job->thumbnail) {
182                 g_object_unref (job->thumbnail);
183                 job->thumbnail = NULL;
184         }
185
186         if (job->rc) {
187                 g_object_unref (job->rc);
188                 job->rc = NULL;
189         }
190
191         (* G_OBJECT_CLASS (ev_job_thumbnail_parent_class)->dispose) (object);
192 }
193
194 static void
195 ev_job_thumbnail_class_init (EvJobThumbnailClass *class)
196 {
197         GObjectClass *oclass;
198
199         oclass = G_OBJECT_CLASS (class);
200
201         oclass->dispose = ev_job_thumbnail_dispose;
202 }
203
204 static void ev_job_print_init (EvJobPrint *job) { /* Do Nothing */ }
205
206 static void
207 ev_job_print_dispose (GObject *object)
208 {
209         EvJobPrint *job;
210
211         job = EV_JOB_PRINT (object);
212
213         if (job->temp_file) {
214                 g_unlink (job->temp_file);
215                 g_free (job->temp_file);
216                 job->temp_file = NULL;
217         }
218
219         if (job->error) {
220                 g_error_free (job->error);
221                 job->error = NULL;
222         }
223
224         if (job->ranges) {
225                 g_free (job->ranges);
226                 job->ranges = NULL;
227                 job->n_ranges = 0;
228         }
229
230         (* G_OBJECT_CLASS (ev_job_print_parent_class)->dispose) (object);
231 }
232
233 static void
234 ev_job_print_class_init (EvJobPrintClass *class)
235 {
236         GObjectClass *oclass;
237
238         oclass = G_OBJECT_CLASS (class);
239
240         oclass->dispose = ev_job_print_dispose;
241 }
242
243 /* Public functions */
244 void
245 ev_job_finished (EvJob *job)
246 {
247         g_return_if_fail (EV_IS_JOB (job));
248
249         g_signal_emit (job, job_signals[FINISHED], 0);
250 }
251
252 EvJob *
253 ev_job_links_new (EvDocument *document)
254 {
255         EvJob *job;
256
257         job = g_object_new (EV_TYPE_JOB_LINKS, NULL);
258         job->document = g_object_ref (document);
259
260         return job;
261 }
262
263 void
264 ev_job_links_run (EvJobLinks *job)
265 {
266         g_return_if_fail (EV_IS_JOB_LINKS (job));
267
268         ev_document_doc_mutex_lock ();
269         job->model = ev_document_links_get_links_model (EV_DOCUMENT_LINKS (EV_JOB (job)->document));
270         EV_JOB (job)->finished = TRUE;
271         ev_document_doc_mutex_unlock ();
272 }
273
274
275 EvJob *
276 ev_job_render_new (EvDocument      *document,
277                    EvRenderContext *rc,
278                    gint             width,
279                    gint             height,
280                    EvRectangle     *selection_points,
281                    GdkColor        *text,
282                    GdkColor        *base,
283                    gboolean         include_forms,
284                    gboolean         include_links,
285                    gboolean         include_images,
286                    gboolean         include_text,
287                    gboolean         include_selection)
288 {
289         EvJobRender *job;
290
291         g_return_val_if_fail (EV_IS_RENDER_CONTEXT (rc), NULL);
292         if (include_selection)
293                 g_return_val_if_fail (selection_points != NULL, NULL);
294
295         job = g_object_new (EV_TYPE_JOB_RENDER, NULL);
296
297         EV_JOB (job)->document = g_object_ref (document);
298         job->rc = g_object_ref (rc);
299         job->target_width = width;
300         job->target_height = height;
301         job->text = *text;
302         job->base = *base;
303         job->include_forms = include_forms;
304         job->include_links = include_links;
305         job->include_images = include_images;
306         job->include_text = include_text;
307         job->include_selection = include_selection;
308
309         if (include_selection)
310                 job->selection_points = *selection_points;
311
312         if (EV_IS_ASYNC_RENDERER (document)) {  
313                 EV_JOB (job)->async = TRUE;
314         }
315
316         return EV_JOB (job);
317 }
318
319 static void
320 render_finished_cb (EvDocument      *document,
321                     GdkPixbuf       *pixbuf,
322                     EvJobRender     *job)
323 {
324         g_signal_handlers_disconnect_by_func (EV_JOB (job)->document,
325                                               render_finished_cb, job);
326
327         /* FIXME: ps backend should be ported to cairo */
328         job->surface = ev_document_misc_surface_from_pixbuf (pixbuf);
329         job->page_ready = TRUE;
330         g_signal_emit (job, job_render_signals[PAGE_READY], 0);
331         EV_JOB (job)->finished = TRUE;
332         ev_job_finished (EV_JOB (job));
333 }
334
335 static gboolean
336 notify_page_ready (EvJobRender *job)
337 {
338         g_signal_emit (job, job_render_signals[PAGE_READY], 0);
339
340         return FALSE;
341 }
342
343 static void
344 ev_job_render_page_ready (EvJobRender *job)
345 {
346         job->page_ready = TRUE;
347         g_idle_add ((GSourceFunc)notify_page_ready, job);
348 }
349
350 void
351 ev_job_render_run (EvJobRender *job)
352 {
353         g_return_if_fail (EV_IS_JOB_RENDER (job));
354
355         ev_document_doc_mutex_lock ();
356
357         if (EV_JOB (job)->async) {
358                 EvAsyncRenderer *renderer = EV_ASYNC_RENDERER (EV_JOB (job)->document);
359                 ev_async_renderer_render_pixbuf (renderer, job->rc->page, job->rc->scale,
360                                                  job->rc->rotation);
361                 g_signal_connect (EV_JOB (job)->document, "render_finished",
362                                   G_CALLBACK (render_finished_cb), job);
363         } else {
364                 ev_document_fc_mutex_lock ();
365                 
366                 job->surface = ev_document_render (EV_JOB (job)->document, job->rc);
367                 if (job->include_selection && EV_IS_SELECTION (EV_JOB (job)->document)) {
368                         ev_selection_render_selection (EV_SELECTION (EV_JOB (job)->document),
369                                                        job->rc,
370                                                        &(job->selection),
371                                                        &(job->selection_points),
372                                                        NULL,
373                                                        &(job->text), &(job->base));
374                         job->selection_region =
375                                 ev_selection_get_selection_region (EV_SELECTION (EV_JOB (job)->document),
376                                                                    job->rc,
377                                                                    &(job->selection_points));
378                 }
379
380                 ev_job_render_page_ready (job);
381                 
382                 ev_document_fc_mutex_unlock ();
383                 
384                 if (job->include_text && EV_IS_SELECTION (EV_JOB (job)->document))
385                         job->text_mapping =
386                                 ev_selection_get_selection_map (EV_SELECTION (EV_JOB (job)->document),
387                                                                 job->rc);
388                 if (job->include_links && EV_IS_DOCUMENT_LINKS (EV_JOB (job)->document))
389                         job->link_mapping =
390                                 ev_document_links_get_links (EV_DOCUMENT_LINKS (EV_JOB (job)->document),
391                                                              job->rc->page);
392                 if (job->include_forms && EV_IS_DOCUMENT_FORMS (EV_JOB (job)->document))
393                         job->form_field_mapping =
394                                 ev_document_forms_get_form_fields (EV_DOCUMENT_FORMS (EV_JOB(job)->document),
395                                                                    job->rc->page);
396                 if (job->include_images && EV_IS_DOCUMENT_IMAGES (EV_JOB (job)->document))
397                         job->image_mapping =
398                                 ev_document_images_get_images (EV_DOCUMENT_IMAGES (EV_JOB (job)->document),
399                                                                job->rc->page);
400                 EV_JOB (job)->finished = TRUE;
401         }
402
403         ev_document_doc_mutex_unlock ();
404 }
405
406 EvJob *
407 ev_job_thumbnail_new (EvDocument      *document,
408                       EvRenderContext *rc)
409 {
410         EvJobThumbnail *job;
411
412         job = g_object_new (EV_TYPE_JOB_THUMBNAIL, NULL);
413
414         EV_JOB (job)->document = g_object_ref (document);
415         job->rc = g_object_ref (rc);
416
417         return EV_JOB (job);
418 }
419
420 void
421 ev_job_thumbnail_run (EvJobThumbnail *job)
422 {
423         g_return_if_fail (EV_IS_JOB_THUMBNAIL (job));
424
425         ev_document_doc_mutex_lock ();
426
427         job->thumbnail =
428                 ev_document_thumbnails_get_thumbnail (EV_DOCUMENT_THUMBNAILS (EV_JOB (job)->document),
429                                                       job->rc, TRUE);
430         EV_JOB (job)->finished = TRUE;
431
432         ev_document_doc_mutex_unlock ();
433 }
434
435 static void ev_job_fonts_init (EvJobFonts *job) { /* Do Nothing */ }
436
437 static void ev_job_fonts_class_init (EvJobFontsClass *class) { /* Do Nothing */ }
438
439 EvJob *
440 ev_job_fonts_new (EvDocument *document)
441 {
442         EvJobFonts *job;
443
444         job = g_object_new (EV_TYPE_JOB_FONTS, NULL);
445
446         EV_JOB (job)->document = g_object_ref (document);
447
448         return EV_JOB (job);
449 }
450
451 void
452 ev_job_fonts_run (EvJobFonts *job)
453 {
454         EvDocumentFonts *fonts;
455
456         g_return_if_fail (EV_IS_JOB_FONTS (job));
457
458         ev_document_doc_mutex_lock ();
459         
460         fonts = EV_DOCUMENT_FONTS (EV_JOB (job)->document);
461         ev_document_fc_mutex_lock ();
462         job->scan_completed = !ev_document_fonts_scan (fonts, 20);
463         ev_document_fc_mutex_unlock ();
464         
465         EV_JOB (job)->finished = TRUE;
466
467         ev_document_doc_mutex_unlock ();
468 }
469
470 static void ev_job_load_init (EvJobLoad *job) { /* Do Nothing */ }
471
472 static void
473 ev_job_load_dispose (GObject *object)
474 {
475         EvJobLoad *job = EV_JOB_LOAD (object);
476
477         if (job->uri) {
478                 g_free (job->uri);
479                 job->uri = NULL;
480         }
481
482         if (job->error) {
483                 g_error_free (job->error);
484                 job->error = NULL;
485         }
486
487         if (job->dest) {
488                 g_object_unref (job->dest);
489                 job->dest = NULL;
490         }
491
492         (* G_OBJECT_CLASS (ev_job_load_parent_class)->dispose) (object);
493 }
494
495 static void
496 ev_job_load_class_init (EvJobLoadClass *class)
497 {
498         GObjectClass *oclass;
499
500         oclass = G_OBJECT_CLASS (class);
501
502         oclass->dispose = ev_job_load_dispose;
503 }
504
505
506 EvJob *
507 ev_job_load_new (const gchar *uri, EvLinkDest *dest, EvWindowRunMode mode)
508 {
509         EvJobLoad *job;
510
511         job = g_object_new (EV_TYPE_JOB_LOAD, NULL);
512
513         job->uri = g_strdup (uri);
514         if (dest)
515                 job->dest = g_object_ref (dest);
516
517         job->mode = mode;
518
519         return EV_JOB (job);
520 }
521
522 void
523 ev_job_load_set_uri (EvJobLoad *job, const gchar *uri)
524 {
525         if (job->uri)
526                 g_free (job->uri);
527         job->uri = g_strdup (uri);
528 }
529
530 void
531 ev_job_load_run (EvJobLoad *job)
532 {
533         g_return_if_fail (EV_IS_JOB_LOAD (job));
534         
535         if (job->error) {
536                 g_error_free (job->error);
537                 job->error = NULL;
538         }
539
540         ev_document_fc_mutex_lock ();
541         
542         /* This job may already have a document even if the job didn't complete
543            because, e.g., a password is required - if so, just reload rather than
544            creating a new instance */
545         if (EV_JOB (job)->document) {
546                 ev_document_load (EV_JOB (job)->document,
547                                   job->uri,
548                                   &job->error);
549         } else {
550                 EV_JOB(job)->document =
551                         ev_document_factory_get_document (job->uri,
552                                                           &job->error);
553         }
554
555         ev_document_fc_mutex_unlock ();
556         EV_JOB (job)->finished = TRUE;
557 }
558
559 EvJob *
560 ev_job_print_new (EvDocument    *document,
561                   const gchar   *format,
562                   gdouble        width,
563                   gdouble        height,
564                   EvPrintRange  *ranges,
565                   gint           n_ranges,
566                   EvPrintPageSet page_set,
567                   gint           pages_per_sheet,
568                   gint           copies,
569                   gdouble        collate,
570                   gdouble        reverse)
571 {
572         EvJobPrint *job;
573
574         job = g_object_new (EV_TYPE_JOB_PRINT, NULL);
575
576         EV_JOB (job)->document = g_object_ref (document);
577
578         job->format = format;
579         
580         job->temp_file = NULL;
581         job->error = NULL;
582
583         job->width = width;
584         job->height = height;
585
586         job->ranges = ranges;
587         job->n_ranges = n_ranges;
588
589         job->page_set = page_set;
590
591         job->pages_per_sheet = pages_per_sheet;
592         
593         job->copies = copies;
594         job->collate = collate;
595         job->reverse = reverse;
596         
597         return EV_JOB (job);
598 }
599
600 static gint
601 ev_print_job_get_first_page (EvJobPrint *job)
602 {
603         gint i;
604         gint first_page = G_MAXINT;
605         
606         if (job->n_ranges == 0)
607                 return 0;
608
609         for (i = 0; i < job->n_ranges; i++) {
610                 if (job->ranges[i].start < first_page)
611                         first_page = job->ranges[i].start;
612         }
613
614         return MAX (0, first_page);
615 }
616
617 static gint
618 ev_print_job_get_last_page (EvJobPrint *job)
619 {
620         gint i;
621         gint last_page = G_MININT;
622         gint max_page;
623
624         max_page = ev_document_get_n_pages (EV_JOB (job)->document) - 1;
625
626         if (job->n_ranges == 0)
627                 return max_page;
628
629         for (i = 0; i < job->n_ranges; i++) {
630                 if (job->ranges[i].end > last_page)
631                         last_page = job->ranges[i].end;
632         }
633
634         return MIN (max_page, last_page);
635 }
636
637 static gboolean
638 ev_print_job_print_page_in_range (EvJobPrint *job,
639                                   gint        page)
640 {
641         gint i;
642
643         for (i = 0; i < job->n_ranges; i++) {
644                 if (page >= job->ranges[i].start &&
645                     page <= job->ranges[i].end)
646                         return TRUE;
647         }
648
649         return FALSE;
650 }
651
652 static gboolean
653 ev_print_job_print_page_in_set (EvJobPrint *job,
654                                 gint        page)
655 {
656         switch (job->page_set) {
657                 case EV_PRINT_PAGE_SET_EVEN:
658                         return page % 2 == 0;
659                 case EV_PRINT_PAGE_SET_ODD:
660                         return page % 2 != 0;
661                 case EV_PRINT_PAGE_SET_ALL:
662                         return TRUE;
663         }
664
665         return FALSE;
666 }
667
668 static void
669 ev_job_print_do_page (EvJobPrint *job, gint page)
670 {
671         EvDocument      *document = EV_JOB (job)->document;
672         EvRenderContext *rc;
673
674         rc = ev_render_context_new (0, page, 1.0);
675         ev_file_exporter_do_page (EV_FILE_EXPORTER (document), rc);
676         g_object_unref (rc);
677 }
678
679 void
680 ev_job_print_run (EvJobPrint *job)
681 {
682         EvDocument            *document = EV_JOB (job)->document;
683         EvFileExporterContext  fc;
684         gint                   fd;
685         gint                   last_page;
686         gint                   first_page;
687         gint                   i;
688         gchar                 *filename;
689         
690         g_return_if_fail (EV_IS_JOB_PRINT (job));
691
692         if (job->temp_file)
693                 g_free (job->temp_file);
694         job->temp_file = NULL;
695         
696         if (job->error)
697                 g_error_free (job->error);
698         job->error = NULL;
699
700         filename = g_strdup_printf ("evince_print.%s.XXXXXX", job->format);
701         fd = g_file_open_tmp (filename, &job->temp_file, &job->error);
702         g_free (filename);
703         if (fd <= -1) {
704                 EV_JOB (job)->finished = TRUE;
705                 return;
706         }
707
708         first_page = ev_print_job_get_first_page (job);
709         last_page = ev_print_job_get_last_page (job);
710
711         fc.format = g_ascii_strcasecmp (job->format, "pdf") == 0 ?
712                 EV_FILE_FORMAT_PDF : EV_FILE_FORMAT_PS;
713         fc.filename = job->temp_file;
714         fc.first_page = MIN (first_page, last_page);
715         fc.last_page = MAX (first_page, last_page);
716         fc.paper_width = job->width;
717         fc.paper_height = job->height;
718         fc.duplex = FALSE;
719         fc.pages_per_sheet = job->pages_per_sheet;
720
721         ev_document_doc_mutex_lock ();
722         ev_file_exporter_begin (EV_FILE_EXPORTER (document), &fc);
723         ev_document_doc_mutex_unlock ();
724
725         for (i = 0; i < job->copies; i++) {
726                 gint page, step;
727                 
728                 step = job->reverse ? -1 : 1;
729                 page = job->reverse ? last_page : first_page;
730                 
731                 while ((job->reverse && (page >= first_page)) ||
732                        (!job->reverse && (page <= last_page))) {
733                         gint n_pages = 1;
734                         gint j;
735
736                         if (job->n_ranges > 0 &&
737                             !ev_print_job_print_page_in_range (job, page)) {
738                                 page += step;
739                                 continue;
740                         }
741
742                         if (!ev_print_job_print_page_in_set (job, page + 1)) {
743                                 page += step;
744                                 continue;
745                         }
746
747                         if (job->collate)
748                                 n_pages = job->copies;
749
750                         for (j = 0; j < n_pages; j++) {
751                                 ev_document_doc_mutex_lock ();
752                                 ev_job_print_do_page (job, page);
753                                 ev_document_doc_mutex_unlock ();
754                         }
755
756                         page += step;
757                 }
758
759                 if (job->collate)
760                         break;
761         }
762
763         ev_document_doc_mutex_lock ();
764         ev_file_exporter_end (EV_FILE_EXPORTER (document));
765         ev_document_doc_mutex_unlock ();
766
767         close (fd);
768         
769         EV_JOB (job)->finished = TRUE;
770 }