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