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