]> www.fi.muni.cz Git - evince.git/blob - backend/ps/ps-document.c
Plugin system for backends. Fixes bug #351348.
[evince.git] / backend / ps / ps-document.c
1 /* Ghostscript widget for GTK/GNOME
2  *
3  * Copyright (C) 1998 - 2005 the Free Software Foundation
4  *
5  * Authors: Jonathan Blandford, Jaka Mocnik, Carlos Garcia Campos
6  *
7  * Based on code by: Federico Mena (Quartic), Szekeres Istvan (Pista)
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include "config.h"
26
27 #include <glib/gstdio.h>
28 #include <glib/gi18n.h>
29 #include <gdk/gdkpixbuf.h>
30 #include <string.h>
31 #include <stdlib.h>
32
33 #include "ps-document.h"
34 #include "ps-interpreter.h"
35 #include "ps.h"
36 #include "gstypes.h"
37 #include "gsdefaults.h"
38 #include "ev-file-exporter.h"
39 #include "ev-async-renderer.h"
40 #include "ev-document-thumbnails.h"
41 #include "ev-document-misc.h"
42
43 struct _PSDocument {
44         GObject object;
45
46         gchar *filename;
47         struct document *doc;
48         gboolean structured_doc;
49
50         PSInterpreter *gs;
51
52         /* Document Thumbnails */
53         PSInterpreter   *thumbs_gs;
54         GdkPixbuf       *thumbnail;
55         EvRenderContext *thumbs_rc;
56         GMutex          *thumbs_mutex;
57         GCond           *thumbs_cond;
58         
59         /* File exporter */
60         gint  *ps_export_pagelist;
61         gchar *ps_export_filename;
62 };
63
64 struct _PSDocumentClass {
65         GObjectClass parent_class;
66 }; 
67
68 static void     ps_document_document_iface_init            (EvDocumentIface           *iface);
69 static void     ps_document_file_exporter_iface_init       (EvFileExporterIface       *iface);
70 static void     ps_async_renderer_iface_init               (EvAsyncRendererIface      *iface);
71 static void     ps_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
72
73 static void     ps_interpreter_page_rendered               (PSInterpreter             *gs,
74                                                             GdkPixbuf                 *pixbuf,
75                                                             PSDocument                *ps_document);
76
77 EV_BACKEND_REGISTER_WITH_CODE (PSDocument, ps_document,
78                          {
79                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
80                                                         ps_document_document_thumbnails_iface_init);
81                                  G_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER,
82                                                         ps_document_file_exporter_iface_init);
83                                  G_IMPLEMENT_INTERFACE (EV_TYPE_ASYNC_RENDERER,
84                                                         ps_async_renderer_iface_init);
85                          });
86
87 /* PSDocument */
88 static void
89 ps_document_init (PSDocument *ps_document)
90 {
91 }
92
93 static void
94 ps_document_dispose (GObject *object)
95 {
96         PSDocument *ps_document = PS_DOCUMENT (object);
97
98         if (ps_document->gs) {
99                 g_object_unref (ps_document->gs);
100                 ps_document->gs = NULL;
101         }
102
103         if (ps_document->thumbs_gs) {
104                 g_object_unref (ps_document->thumbs_gs);
105                 ps_document->thumbs_gs = NULL;
106         }
107         
108         if (ps_document->filename) {
109                 g_free (ps_document->filename);
110                 ps_document->filename = NULL;
111         }
112
113         if (ps_document->doc) {
114                 psfree (ps_document->doc);
115                 ps_document->doc = NULL;
116         }
117
118         if (ps_document->thumbnail) {
119                 g_object_unref (ps_document->thumbnail);
120                 ps_document->thumbnail = NULL;
121         }
122
123         if (ps_document->thumbs_mutex) {
124                 g_mutex_free (ps_document->thumbs_mutex);
125                 ps_document->thumbs_mutex = NULL;
126         }
127         
128         if (ps_document->thumbs_cond) {
129                 g_cond_free (ps_document->thumbs_cond);
130                 ps_document->thumbs_cond = NULL;
131         }
132
133         if (ps_document->thumbs_rc) {
134                 g_object_unref (ps_document->thumbs_rc);
135                 ps_document->thumbs_rc = NULL;
136         }
137
138         G_OBJECT_CLASS (ps_document_parent_class)->dispose (object);
139 }
140
141 static void
142 ps_document_class_init (PSDocumentClass *klass)
143 {
144         GObjectClass *object_class;
145
146         object_class = G_OBJECT_CLASS (klass);
147
148         object_class->dispose = ps_document_dispose;
149 }
150
151 /* EvDocumentIface */
152 static gboolean
153 document_load (PSDocument *ps_document, const gchar *fname, GError **error)
154 {
155         FILE *fd;
156         
157         ps_document->filename = g_strdup (fname);
158
159         /*
160          * We need to make sure that the file is loadable/exists!
161          * otherwise we want to exit without loading new stuff...
162          */
163         if (!g_file_test (fname, G_FILE_TEST_IS_REGULAR)) {
164                 gchar *filename_dsp;
165
166                 filename_dsp = g_filename_display_name (fname);
167                 g_set_error (error,
168                              G_FILE_ERROR,
169                              G_FILE_ERROR_NOENT,
170                              _("Cannot open file ā€œ%sā€."),
171                              filename_dsp);
172                 g_free (filename_dsp);
173                 
174                 return FALSE;
175         }
176
177         if ((fd = fopen (ps_document->filename, "r")) == NULL) {
178                 gchar *filename_dsp;
179
180                 filename_dsp = g_filename_display_name (fname);
181                 g_set_error (error,
182                              G_FILE_ERROR,
183                              G_FILE_ERROR_NOENT,
184                              _("Cannot open file ā€œ%sā€."),
185                              filename_dsp);
186                 g_free (filename_dsp);
187                 
188                 return FALSE;
189         }
190         
191         /* we grab the vital statistics!!! */
192         ps_document->doc = psscan (fd, TRUE, ps_document->filename);
193         fclose (fd);
194         if (!ps_document->doc)
195                 return FALSE;
196
197         ps_document->structured_doc =
198                 ((!ps_document->doc->epsf && ps_document->doc->numpages > 0) ||
199                  (ps_document->doc->epsf && ps_document->doc->numpages > 1));
200
201         ps_document->gs = ps_interpreter_new (ps_document->filename,
202                                               ps_document->doc);
203         g_signal_connect (G_OBJECT (ps_document->gs), "page_rendered",
204                           G_CALLBACK (ps_interpreter_page_rendered),
205                           (gpointer) ps_document);
206         
207         return TRUE;
208 }
209
210 static gboolean
211 ps_document_load (EvDocument  *document,
212                   const char  *uri,
213                   GError     **error)
214 {
215         char *filename;
216         char *gs_path;
217         gboolean result;
218
219         filename = g_filename_from_uri (uri, NULL, error);
220         if (!filename)
221                 return FALSE;
222
223         gs_path = g_find_program_in_path ("gs");
224         if (!gs_path) {
225                 gchar *filename_dsp;
226                 
227                 filename_dsp = g_filename_display_name (filename);
228                 g_set_error (error,
229                              G_FILE_ERROR,
230                              G_FILE_ERROR_NOENT,
231                              _("Failed to load document ā€œ%sā€. Ghostscript interpreter was not found in path"),
232                              filename);
233                 g_free (filename_dsp);
234                 g_free (filename);
235                 
236                 return FALSE;
237         }
238         g_free (gs_path);
239
240         result = document_load (PS_DOCUMENT (document), filename, error);
241         if (!result && !(*error)) {
242                 gchar *filename_dsp;
243                 
244                 filename_dsp = g_filename_display_name (filename);
245                 g_set_error (error,
246                              G_FILE_ERROR,
247                              G_FILE_ERROR_FAILED,
248                              _("Failed to load document ā€œ%sā€"),
249                              filename_dsp);
250                 g_free (filename_dsp);
251         }
252         
253         g_free (filename);
254
255         return result;
256 }
257
258 static gboolean
259 save_document (PSDocument *document, const char *filename)
260 {
261         gboolean result = TRUE;
262         GtkGSDocSink *sink = gtk_gs_doc_sink_new ();
263         FILE *f, *src_file;
264         gchar *buf;
265
266         src_file = fopen (document->filename, "r");
267         if (src_file) {
268                 struct stat stat_rec;
269
270                 if (stat (document->filename, &stat_rec) == 0) {
271                         pscopy (src_file, sink, 0, stat_rec.st_size - 1);
272                 }
273
274                 fclose (src_file);
275         }
276         
277         buf = gtk_gs_doc_sink_get_buffer (sink);
278         if (buf == NULL) {
279                 return FALSE;
280         }
281         
282         f = fopen (filename, "w");
283         if (f) {
284                 fputs (buf, f);
285                 fclose (f);
286         } else {
287                 result = FALSE;
288         }
289
290         g_free (buf);
291         gtk_gs_doc_sink_free (sink);
292         g_free (sink);
293
294         return result;
295 }
296
297 static gboolean
298 save_page_list (PSDocument *document, int *page_list, const char *filename)
299 {
300         gboolean result = TRUE;
301         GtkGSDocSink *sink = gtk_gs_doc_sink_new ();
302         FILE *f;
303         gchar *buf;
304
305         pscopydoc (sink, document->filename, 
306                    document->doc, page_list);
307         
308         buf = gtk_gs_doc_sink_get_buffer (sink);
309         
310         f = fopen (filename, "w");
311         if (f) {
312                 fputs (buf, f);
313                 fclose (f);
314         } else {
315                 result = FALSE;
316         }
317
318         g_free (buf);
319         gtk_gs_doc_sink_free (sink);
320         g_free (sink);
321
322         return result;
323 }
324
325 static gboolean
326 ps_document_save (EvDocument  *document,
327                   const char  *uri,
328                   GError     **error)
329 {
330         PSDocument *ps = PS_DOCUMENT (document);
331         gboolean result;
332         char *filename;
333
334         filename = g_filename_from_uri (uri, NULL, error);
335         if (!filename)
336                 return FALSE;
337
338         result = save_document (ps, filename);
339
340         g_free (filename);
341
342         return result;
343 }
344
345 static int
346 ps_document_get_n_pages (EvDocument *document)
347 {
348         PSDocument *ps = PS_DOCUMENT (document);
349
350         if (!ps->filename || !ps->doc) {
351                 return -1;
352         }
353
354         return ps->structured_doc ? ps->doc->numpages : 1;
355 }
356
357 static gint
358 ps_document_get_page_rotation (PSDocument *ps_document,
359                                int         page)
360 {
361         gint rotation = GTK_GS_ORIENTATION_NONE;
362
363         g_assert (ps_document->doc != NULL);
364         
365         if (ps_document->structured_doc) {
366                 if (ps_document->doc->pages[page].orientation != GTK_GS_ORIENTATION_NONE)
367                         rotation = ps_document->doc->pages[page].orientation;
368                 else
369                         rotation = ps_document->doc->default_page_orientation;
370         }
371
372         if (rotation == GTK_GS_ORIENTATION_NONE)
373                 rotation = ps_document->doc->orientation;
374
375         if (rotation == GTK_GS_ORIENTATION_NONE)
376                 rotation = GTK_GS_ORIENTATION_PORTRAIT;
377
378         return rotation;
379 }
380
381 static void
382 ps_document_get_page_size (EvDocument *document,
383                            int         page,
384                            double     *width,
385                            double     *height)
386 {
387         PSDocument *ps_document = PS_DOCUMENT (document);
388         int urx, ury, llx, lly;
389         gdouble pwidth, pheight;
390         gdouble page_width, page_height;
391         gint rotate;
392
393         psgetpagebox (ps_document->doc, page, &urx, &ury, &llx, &lly);
394
395         pwidth = (urx - llx) + 0.5;
396         pheight = (ury - lly) + 0.5;
397
398         rotate = ps_document_get_page_rotation (ps_document, page);
399         if (rotate == 90 || rotate == 270) {
400                 page_height = pwidth;
401                 page_width = pheight;
402         } else {
403                 page_width = pwidth;
404                 page_height = pheight;
405         }
406         
407         if (width) {
408                 *width = page_width;
409         }
410
411         if (height) {
412                 *height = page_height;
413         }
414 }
415
416 static EvDocumentInfo *
417 ps_document_get_info (EvDocument *document)
418 {
419         EvDocumentInfo *info;
420         PSDocument *ps = PS_DOCUMENT (document);
421         int urx, ury, llx, lly;
422
423         info = g_new0 (EvDocumentInfo, 1);
424         info->fields_mask = EV_DOCUMENT_INFO_TITLE |
425                             EV_DOCUMENT_INFO_FORMAT |
426                             EV_DOCUMENT_INFO_CREATOR |
427                             EV_DOCUMENT_INFO_N_PAGES |
428                             EV_DOCUMENT_INFO_PAPER_SIZE;
429
430         info->title = g_strdup (ps->doc->title);
431         info->format = ps->doc->epsf ? g_strdup (_("Encapsulated PostScript"))
432                                      : g_strdup (_("PostScript"));
433         info->creator = g_strdup (ps->doc->creator);
434         info->n_pages = ev_document_get_n_pages (document);
435         
436         psgetpagebox (PS_DOCUMENT (document)->doc, 0, &urx, &ury, &llx, &lly);
437
438         info->paper_width  = (urx - llx) / 72.0f * 25.4f;
439         info->paper_height = (ury - lly) / 72.0f * 25.4f;
440
441         return info;
442 }
443
444 static void
445 ps_document_document_iface_init (EvDocumentIface *iface)
446 {
447         iface->load = ps_document_load;
448         iface->save = ps_document_save;
449         iface->get_n_pages = ps_document_get_n_pages;
450         iface->get_page_size = ps_document_get_page_size;
451         iface->get_info = ps_document_get_info;
452 }
453
454 /* EvAsyncRendererIface */
455 static void
456 ps_interpreter_page_rendered (PSInterpreter *gs,
457                               GdkPixbuf     *pixbuf,
458                               PSDocument    *ps_document)
459 {
460         g_signal_emit_by_name (ps_document, "render_finished", pixbuf);
461 }
462
463 static void
464 ps_async_renderer_render_pixbuf (EvAsyncRenderer *renderer,
465                                  gint             page,
466                                  gdouble          scale,
467                                  gint             rotation)
468 {
469         PSDocument *ps_document = PS_DOCUMENT (renderer);
470
471         g_return_if_fail (PS_IS_INTERPRETER (ps_document->gs));
472
473         rotation = (rotation + ps_document_get_page_rotation (ps_document, page)) % 360;
474
475         ps_interpreter_render_page (ps_document->gs, page, scale, rotation);
476 }
477
478 static void
479 ps_async_renderer_iface_init (EvAsyncRendererIface *iface)
480 {
481         iface->render_pixbuf = ps_async_renderer_render_pixbuf;
482 }
483
484 /* EvDocumentThumbnailsIface */
485 static void
486 ps_interpreter_thumbnail_rendered (PSInterpreter *gs,
487                                    GdkPixbuf     *pixbuf,
488                                    PSDocument    *ps_document)
489 {
490         if (ps_document->thumbnail)
491                 g_object_unref (ps_document->thumbnail);
492         ps_document->thumbnail = g_object_ref (pixbuf);
493
494         g_cond_broadcast (ps_document->thumbs_cond);
495 }
496
497 static gboolean
498 ps_document_render_thumbnail (PSDocument *ps_document)
499 {
500         ps_interpreter_render_page (ps_document->thumbs_gs,
501                                     ps_document->thumbs_rc->page,
502                                     ps_document->thumbs_rc->scale,
503                                     ps_document->thumbs_rc->rotation);
504
505         return FALSE;
506 }
507
508 static GdkPixbuf *
509 ps_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails,
510                                       EvRenderContext      *rc, 
511                                       gboolean              border)
512 {
513         PSDocument *ps_document;
514         GdkPixbuf  *pixbuf = NULL;
515
516         ps_document = PS_DOCUMENT (document_thumbnails);
517         
518         g_return_val_if_fail (ps_document->filename != NULL, NULL);
519         g_return_val_if_fail (ps_document->doc != NULL, NULL);
520                 
521         if (!ps_document->thumbs_gs) {
522                 ps_document->thumbs_gs = ps_interpreter_new (ps_document->filename,
523                                                              ps_document->doc);
524                 g_signal_connect (G_OBJECT (ps_document->thumbs_gs), "page_rendered",
525                                   G_CALLBACK (ps_interpreter_thumbnail_rendered),
526                                   (gpointer) ps_document);
527         }
528
529         if (!ps_document->thumbs_mutex)
530                 ps_document->thumbs_mutex = g_mutex_new ();
531         ps_document->thumbs_cond = g_cond_new ();
532
533         if (ps_document->thumbs_rc)
534                 g_object_unref (ps_document->thumbs_rc);
535         ps_document->thumbs_rc = g_object_ref (rc);
536
537         ev_document_doc_mutex_unlock ();
538         g_mutex_lock (ps_document->thumbs_mutex);
539         g_idle_add ((GSourceFunc)ps_document_render_thumbnail, ps_document);
540         g_cond_wait (ps_document->thumbs_cond, ps_document->thumbs_mutex);
541         g_cond_free (ps_document->thumbs_cond);
542         ps_document->thumbs_cond = NULL;
543         g_mutex_unlock (ps_document->thumbs_mutex);
544         ev_document_doc_mutex_lock ();
545         
546         pixbuf = ps_document->thumbnail;
547         ps_document->thumbnail = NULL;
548         
549         if (border) {
550                 GdkPixbuf *border_pixbuf;
551                 
552                 border_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, pixbuf);
553                 g_object_unref (pixbuf);
554                 pixbuf = border_pixbuf;
555         }
556
557         return pixbuf;
558 }
559
560 static void
561 ps_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails,
562                                        EvRenderContext      *rc, 
563                                        gint                 *width,
564                                        gint                 *height)
565 {
566         PSDocument *ps_document;
567         gdouble     page_width, page_height;
568
569         ps_document = PS_DOCUMENT (document_thumbnails);
570         
571         ps_document_get_page_size (EV_DOCUMENT (ps_document),
572                                    rc->page,
573                                    &page_width, &page_height);
574         
575         if (rc->rotation == 90 || rc->rotation == 270) {
576                 *width = (gint) (page_height * rc->scale);
577                 *height = (gint) (page_width * rc->scale);
578         } else {
579                 *width = (gint) (page_width * rc->scale);
580                 *height = (gint) (page_height * rc->scale);
581         }
582 }
583
584 static void
585 ps_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
586 {
587         iface->get_thumbnail = ps_document_thumbnails_get_thumbnail;
588         iface->get_dimensions = ps_document_thumbnails_get_dimensions;
589 }
590
591 /* EvFileExporterIface */
592 static void
593 ps_document_file_exporter_begin (EvFileExporter        *exporter,
594                                  EvFileExporterContext *fc)
595 {
596         PSDocument *document = PS_DOCUMENT (exporter);
597
598         if (document->structured_doc) {
599                 g_free (document->ps_export_pagelist);
600         
601                 document->ps_export_pagelist = g_new0 (int, document->doc->numpages);
602         }
603
604         document->ps_export_filename = g_strdup (fc->filename);
605 }
606
607 static void
608 ps_document_file_exporter_do_page (EvFileExporter  *exporter,
609                                    EvRenderContext *rc)
610 {
611         PSDocument *document = PS_DOCUMENT (exporter);
612         
613         if (document->structured_doc) {
614                 document->ps_export_pagelist[rc->page] = 1;
615         }
616 }
617
618 static void
619 ps_document_file_exporter_end (EvFileExporter *exporter)
620 {
621         PSDocument *document = PS_DOCUMENT (exporter);
622
623         if (!document->structured_doc) {
624                 save_document (document, document->ps_export_filename);
625         } else {
626                 save_page_list (document, document->ps_export_pagelist,
627                                 document->ps_export_filename);
628                 g_free (document->ps_export_pagelist);
629                 g_free (document->ps_export_filename);  
630                 document->ps_export_pagelist = NULL;
631                 document->ps_export_filename = NULL;
632         }
633 }
634
635 static EvFileExporterCapabilities
636 ps_document_file_exporter_get_capabilities (EvFileExporter *exporter)
637 {
638         return  EV_FILE_EXPORTER_CAN_PAGE_SET |
639                 EV_FILE_EXPORTER_CAN_GENERATE_PS;
640 }
641
642 static void
643 ps_document_file_exporter_iface_init (EvFileExporterIface *iface)
644 {
645         iface->begin = ps_document_file_exporter_begin;
646         iface->do_page = ps_document_file_exporter_do_page;
647         iface->end = ps_document_file_exporter_end;
648         iface->get_capabilities = ps_document_file_exporter_get_capabilities;
649 }