]> www.fi.muni.cz Git - evince.git/blob - backend/ps/ps-document.c
d1b456d28478e74da4d077b6b92bc6300a5e398f
[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 G_DEFINE_TYPE_WITH_CODE (PSDocument, ps_document, G_TYPE_OBJECT,
78                          {
79                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
80                                                         ps_document_document_iface_init);
81                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
82                                                         ps_document_document_thumbnails_iface_init);
83                                  G_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER,
84                                                         ps_document_file_exporter_iface_init);
85                                  G_IMPLEMENT_INTERFACE (EV_TYPE_ASYNC_RENDERER,
86                                                         ps_async_renderer_iface_init);
87                          });
88
89 /* PSDocument */
90 static void
91 ps_document_init (PSDocument *ps_document)
92 {
93 }
94
95 static void
96 ps_document_dispose (GObject *object)
97 {
98         PSDocument *ps_document = PS_DOCUMENT (object);
99
100         if (ps_document->gs) {
101                 g_object_unref (ps_document->gs);
102                 ps_document->gs = NULL;
103         }
104
105         if (ps_document->thumbs_gs) {
106                 g_object_unref (ps_document->thumbs_gs);
107                 ps_document->thumbs_gs = NULL;
108         }
109         
110         if (ps_document->filename) {
111                 g_free (ps_document->filename);
112                 ps_document->filename = NULL;
113         }
114
115         if (ps_document->doc) {
116                 psfree (ps_document->doc);
117                 ps_document->doc = NULL;
118         }
119
120         if (ps_document->thumbnail) {
121                 g_object_unref (ps_document->thumbnail);
122                 ps_document->thumbnail = NULL;
123         }
124
125         if (ps_document->thumbs_mutex) {
126                 g_mutex_free (ps_document->thumbs_mutex);
127                 ps_document->thumbs_mutex = NULL;
128         }
129         
130         if (ps_document->thumbs_cond) {
131                 g_cond_free (ps_document->thumbs_cond);
132                 ps_document->thumbs_cond = NULL;
133         }
134
135         if (ps_document->thumbs_rc) {
136                 g_object_unref (ps_document->thumbs_rc);
137                 ps_document->thumbs_rc = NULL;
138         }
139
140         G_OBJECT_CLASS (ps_document_parent_class)->dispose (object);
141 }
142
143 static void
144 ps_document_class_init (PSDocumentClass *klass)
145 {
146         GObjectClass *object_class;
147
148         object_class = G_OBJECT_CLASS (klass);
149
150         object_class->dispose = ps_document_dispose;
151 }
152
153 /* EvDocumentIface */
154 static gboolean
155 document_load (PSDocument *ps_document, const gchar *fname, GError **error)
156 {
157         FILE *fd;
158         
159         ps_document->filename = g_strdup (fname);
160
161         /*
162          * We need to make sure that the file is loadable/exists!
163          * otherwise we want to exit without loading new stuff...
164          */
165         if (!g_file_test (fname, G_FILE_TEST_IS_REGULAR)) {
166                 gchar *filename_dsp;
167
168                 filename_dsp = g_filename_display_name (fname);
169                 g_set_error (error,
170                              G_FILE_ERROR,
171                              G_FILE_ERROR_NOENT,
172                              _("Cannot open file “%s”.\n"), /* FIXME: remove \n after freeze */
173                              filename_dsp);
174                 g_free (filename_dsp);
175                 
176                 return FALSE;
177         }
178
179         if ((fd = fopen (ps_document->filename, "r")) == NULL) {
180                 gchar *filename_dsp;
181
182                 filename_dsp = g_filename_display_name (fname);
183                 g_set_error (error,
184                              G_FILE_ERROR,
185                              G_FILE_ERROR_NOENT,
186                              _("Cannot open file “%s”.\n"), /* FIXME: remove \n after freeze */
187                              filename_dsp);
188                 g_free (filename_dsp);
189                 
190                 return FALSE;
191         }
192         
193         /* we grab the vital statistics!!! */
194         ps_document->doc = psscan (fd, TRUE, ps_document->filename);
195         fclose (fd);
196         if (!ps_document->doc)
197                 return FALSE;
198
199         ps_document->structured_doc =
200                 ((!ps_document->doc->epsf && ps_document->doc->numpages > 0) ||
201                  (ps_document->doc->epsf && ps_document->doc->numpages > 1));
202
203         ps_document->gs = ps_interpreter_new (ps_document->filename,
204                                               ps_document->doc);
205         g_signal_connect (G_OBJECT (ps_document->gs), "page_rendered",
206                           G_CALLBACK (ps_interpreter_page_rendered),
207                           (gpointer) ps_document);
208         
209         return TRUE;
210 }
211
212 static gboolean
213 ps_document_load (EvDocument  *document,
214                   const char  *uri,
215                   GError     **error)
216 {
217         char *filename;
218         char *gs_path;
219         gboolean result;
220
221         filename = g_filename_from_uri (uri, NULL, error);
222         if (!filename)
223                 return FALSE;
224
225         gs_path = g_find_program_in_path ("gs");
226         if (!gs_path) {
227                 gchar *filename_dsp;
228                 
229                 filename_dsp = g_filename_display_name (filename);
230                 g_set_error (error,
231                              G_FILE_ERROR,
232                              G_FILE_ERROR_NOENT,
233                              _("Failed to load document “%s”. Ghostscript interpreter was not found in path"),
234                              filename);
235                 g_free (filename_dsp);
236                 g_free (filename);
237                 
238                 return FALSE;
239         }
240         g_free (gs_path);
241
242         result = document_load (PS_DOCUMENT (document), filename, error);
243         if (!result && !(*error)) {
244                 gchar *filename_dsp;
245                 
246                 filename_dsp = g_filename_display_name (filename);
247                 g_set_error (error,
248                              G_FILE_ERROR,
249                              G_FILE_ERROR_FAILED,
250                              _("Failed to load document “%s”"),
251                              filename_dsp);
252                 g_free (filename_dsp);
253         }
254         
255         g_free (filename);
256
257         return result;
258 }
259
260 static gboolean
261 save_document (PSDocument *document, const char *filename)
262 {
263         gboolean result = TRUE;
264         GtkGSDocSink *sink = gtk_gs_doc_sink_new ();
265         FILE *f, *src_file;
266         gchar *buf;
267
268         src_file = fopen (document->filename, "r");
269         if (src_file) {
270                 struct stat stat_rec;
271
272                 if (stat (document->filename, &stat_rec) == 0) {
273                         pscopy (src_file, sink, 0, stat_rec.st_size - 1);
274                 }
275
276                 fclose (src_file);
277         }
278         
279         buf = gtk_gs_doc_sink_get_buffer (sink);
280         if (buf == NULL) {
281                 return FALSE;
282         }
283         
284         f = fopen (filename, "w");
285         if (f) {
286                 fputs (buf, f);
287                 fclose (f);
288         } else {
289                 result = FALSE;
290         }
291
292         g_free (buf);
293         gtk_gs_doc_sink_free (sink);
294         g_free (sink);
295
296         return result;
297 }
298
299 static gboolean
300 save_page_list (PSDocument *document, int *page_list, const char *filename)
301 {
302         gboolean result = TRUE;
303         GtkGSDocSink *sink = gtk_gs_doc_sink_new ();
304         FILE *f;
305         gchar *buf;
306
307         pscopydoc (sink, document->filename, 
308                    document->doc, page_list);
309         
310         buf = gtk_gs_doc_sink_get_buffer (sink);
311         
312         f = fopen (filename, "w");
313         if (f) {
314                 fputs (buf, f);
315                 fclose (f);
316         } else {
317                 result = FALSE;
318         }
319
320         g_free (buf);
321         gtk_gs_doc_sink_free (sink);
322         g_free (sink);
323
324         return result;
325 }
326
327 static gboolean
328 ps_document_save (EvDocument  *document,
329                   const char  *uri,
330                   GError     **error)
331 {
332         PSDocument *ps = PS_DOCUMENT (document);
333         gboolean result;
334         char *filename;
335
336         filename = g_filename_from_uri (uri, NULL, error);
337         if (!filename)
338                 return FALSE;
339
340         result = save_document (ps, filename);
341
342         g_free (filename);
343
344         return result;
345 }
346
347 static int
348 ps_document_get_n_pages (EvDocument *document)
349 {
350         PSDocument *ps = PS_DOCUMENT (document);
351
352         if (!ps->filename || !ps->doc) {
353                 return -1;
354         }
355
356         return ps->structured_doc ? ps->doc->numpages : 1;
357 }
358
359 static gint
360 ps_document_get_page_rotation (PSDocument *ps_document,
361                                int         page)
362 {
363         gint rotation = GTK_GS_ORIENTATION_NONE;
364
365         g_assert (ps_document->doc != NULL);
366         
367         if (ps_document->structured_doc) {
368                 if (ps_document->doc->pages[page].orientation != GTK_GS_ORIENTATION_NONE)
369                         rotation = ps_document->doc->pages[page].orientation;
370                 else
371                         rotation = ps_document->doc->default_page_orientation;
372         }
373
374         if (rotation == GTK_GS_ORIENTATION_NONE)
375                 rotation = ps_document->doc->orientation;
376
377         if (rotation == GTK_GS_ORIENTATION_NONE)
378                 rotation = GTK_GS_ORIENTATION_PORTRAIT;
379
380         return rotation;
381 }
382
383 static void
384 ps_document_get_page_size (EvDocument *document,
385                            int         page,
386                            double     *width,
387                            double     *height)
388 {
389         PSDocument *ps_document = PS_DOCUMENT (document);
390         int urx, ury, llx, lly;
391         gdouble pwidth, pheight;
392         gdouble page_width, page_height;
393         gint rotate;
394
395         psgetpagebox (ps_document->doc, page, &urx, &ury, &llx, &lly);
396
397         pwidth = (urx - llx) + 0.5;
398         pheight = (ury - lly) + 0.5;
399
400         rotate = ps_document_get_page_rotation (ps_document, page);
401         if (rotate == 90 || rotate == 270) {
402                 page_height = pwidth;
403                 page_width = pheight;
404         } else {
405                 page_width = pwidth;
406                 page_height = pheight;
407         }
408         
409         if (width) {
410                 *width = page_width;
411         }
412
413         if (height) {
414                 *height = page_height;
415         }
416 }
417
418 static gboolean
419 ps_document_can_get_text (EvDocument *document)
420 {
421         return FALSE;
422 }
423
424 static EvDocumentInfo *
425 ps_document_get_info (EvDocument *document)
426 {
427         EvDocumentInfo *info;
428         PSDocument *ps = PS_DOCUMENT (document);
429         int urx, ury, llx, lly;
430
431         info = g_new0 (EvDocumentInfo, 1);
432         info->fields_mask = EV_DOCUMENT_INFO_TITLE |
433                             EV_DOCUMENT_INFO_FORMAT |
434                             EV_DOCUMENT_INFO_CREATOR |
435                             EV_DOCUMENT_INFO_N_PAGES |
436                             EV_DOCUMENT_INFO_PAPER_SIZE;
437
438         info->title = g_strdup (ps->doc->title);
439         info->format = ps->doc->epsf ? g_strdup (_("Encapsulated PostScript"))
440                                      : g_strdup (_("PostScript"));
441         info->creator = g_strdup (ps->doc->creator);
442         info->n_pages = ev_document_get_n_pages (document);
443         
444         psgetpagebox (PS_DOCUMENT (document)->doc, 0, &urx, &ury, &llx, &lly);
445
446         info->paper_width  = (urx - llx) / 72.0f * 25.4f;
447         info->paper_height = (ury - lly) / 72.0f * 25.4f;
448
449         return info;
450 }
451
452 static void
453 ps_document_document_iface_init (EvDocumentIface *iface)
454 {
455         iface->load = ps_document_load;
456         iface->save = ps_document_save;
457         iface->can_get_text = ps_document_can_get_text;
458         iface->get_n_pages = ps_document_get_n_pages;
459         iface->get_page_size = ps_document_get_page_size;
460         iface->get_info = ps_document_get_info;
461 }
462
463 /* EvAsyncRendererIface */
464 static void
465 ps_interpreter_page_rendered (PSInterpreter *gs,
466                               GdkPixbuf     *pixbuf,
467                               PSDocument    *ps_document)
468 {
469         g_signal_emit_by_name (ps_document, "render_finished", pixbuf);
470 }
471
472 static void
473 ps_async_renderer_render_pixbuf (EvAsyncRenderer *renderer,
474                                  gint             page,
475                                  gdouble          scale,
476                                  gint             rotation)
477 {
478         PSDocument *ps_document = PS_DOCUMENT (renderer);
479
480         g_return_if_fail (PS_IS_INTERPRETER (ps_document->gs));
481
482         rotation = (rotation + ps_document_get_page_rotation (ps_document, page)) % 360;
483
484         ps_interpreter_render_page (ps_document->gs, page, scale, rotation);
485 }
486
487 static void
488 ps_async_renderer_iface_init (EvAsyncRendererIface *iface)
489 {
490         iface->render_pixbuf = ps_async_renderer_render_pixbuf;
491 }
492
493 /* EvDocumentThumbnailsIface */
494 static void
495 ps_interpreter_thumbnail_rendered (PSInterpreter *gs,
496                                    GdkPixbuf     *pixbuf,
497                                    PSDocument    *ps_document)
498 {
499         if (ps_document->thumbnail)
500                 g_object_unref (ps_document->thumbnail);
501         ps_document->thumbnail = g_object_ref (pixbuf);
502
503         g_cond_broadcast (ps_document->thumbs_cond);
504 }
505
506 static gboolean
507 ps_document_render_thumbnail (PSDocument *ps_document)
508 {
509         ps_interpreter_render_page (ps_document->thumbs_gs,
510                                     ps_document->thumbs_rc->page,
511                                     ps_document->thumbs_rc->scale,
512                                     ps_document->thumbs_rc->rotation);
513
514         return FALSE;
515 }
516
517 static GdkPixbuf *
518 ps_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document_thumbnails,
519                                       gint                  page,
520                                       gint                  rotation,
521                                       gint                  size,
522                                       gboolean              border)
523 {
524         PSDocument *ps_document;
525         GdkPixbuf  *pixbuf = NULL;
526         gdouble     page_width, page_height;
527         gdouble     scale;
528
529         ps_document = PS_DOCUMENT (document_thumbnails);
530         
531         g_return_val_if_fail (ps_document->filename != NULL, NULL);
532         g_return_val_if_fail (ps_document->doc != NULL, NULL);
533                 
534         if (!ps_document->thumbs_gs) {
535                 ps_document->thumbs_gs = ps_interpreter_new (ps_document->filename,
536                                                              ps_document->doc);
537                 g_signal_connect (G_OBJECT (ps_document->thumbs_gs), "page_rendered",
538                                   G_CALLBACK (ps_interpreter_thumbnail_rendered),
539                                   (gpointer) ps_document);
540         }
541
542         if (!ps_document->thumbs_mutex)
543                 ps_document->thumbs_mutex = g_mutex_new ();
544         ps_document->thumbs_cond = g_cond_new ();
545
546         ps_document_get_page_size (EV_DOCUMENT (ps_document), page,
547                                    &page_width, &page_height);
548         scale = size / page_width;
549
550         rotation = (rotation + ps_document_get_page_rotation (ps_document, page)) % 360;
551         
552         if (!ps_document->thumbs_rc) {
553                 ps_document->thumbs_rc = ev_render_context_new (rotation, page, scale);
554         } else {
555                 ev_render_context_set_page (ps_document->thumbs_rc, page);
556                 ev_render_context_set_scale (ps_document->thumbs_rc, scale);
557                 ev_render_context_set_rotation (ps_document->thumbs_rc, rotation);
558         }
559
560         ev_document_doc_mutex_unlock ();
561         g_mutex_lock (ps_document->thumbs_mutex);
562         g_idle_add ((GSourceFunc)ps_document_render_thumbnail, ps_document);
563         g_cond_wait (ps_document->thumbs_cond, ps_document->thumbs_mutex);
564         g_cond_free (ps_document->thumbs_cond);
565         ps_document->thumbs_cond = NULL;
566         g_mutex_unlock (ps_document->thumbs_mutex);
567         ev_document_doc_mutex_lock ();
568         
569         pixbuf = ps_document->thumbnail;
570         ps_document->thumbnail = NULL;
571         
572         if (border) {
573                 GdkPixbuf *border_pixbuf;
574                 
575                 border_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, rotation, pixbuf);
576                 g_object_unref (pixbuf);
577                 pixbuf = border_pixbuf;
578         }
579
580         return pixbuf;
581 }
582
583 static void
584 ps_document_thumbnails_get_dimensions (EvDocumentThumbnails *document_thumbnails,
585                                        gint                  page,
586                                        gint                  size,
587                                        gint                 *width,
588                                        gint                 *height)
589 {
590         PSDocument *ps_document;
591         gdouble     page_width, page_height;
592
593         ps_document = PS_DOCUMENT (document_thumbnails);
594         
595         ps_document_get_page_size (EV_DOCUMENT (ps_document),
596                                    page,
597                                    &page_width, &page_height);
598         *width = size;
599         *height = (int) (size * page_height / page_width);
600 }
601
602 static void
603 ps_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
604 {
605         iface->get_thumbnail = ps_document_thumbnails_get_thumbnail;
606         iface->get_dimensions = ps_document_thumbnails_get_dimensions;
607 }
608
609 /* EvFileExporterIface */
610 static gboolean
611 ps_document_file_exporter_format_supported (EvFileExporter      *exporter,
612                                             EvFileExporterFormat format)
613 {
614         return (format == EV_FILE_FORMAT_PS);
615 }
616
617 static void
618 ps_document_file_exporter_begin (EvFileExporter      *exporter,
619                                  EvFileExporterFormat format,
620                                  const char          *filename,
621                                  int                  first_page,
622                                  int                  last_page,
623                                  double               width,
624                                  double               height,
625                                  gboolean             duplex)
626 {
627         PSDocument *document = PS_DOCUMENT (exporter);
628
629         if (document->structured_doc) {
630                 g_free (document->ps_export_pagelist);
631         
632                 document->ps_export_pagelist = g_new0 (int, document->doc->numpages);
633         }
634
635         document->ps_export_filename = g_strdup (filename);
636 }
637
638 static void
639 ps_document_file_exporter_do_page (EvFileExporter *exporter, EvRenderContext *rc)
640 {
641         PSDocument *document = PS_DOCUMENT (exporter);
642         
643         if (document->structured_doc) {
644                 document->ps_export_pagelist[rc->page] = 1;
645         }
646 }
647
648 static void
649 ps_document_file_exporter_end (EvFileExporter *exporter)
650 {
651         PSDocument *document = PS_DOCUMENT (exporter);
652
653         if (!document->structured_doc) {
654                 save_document (document, document->ps_export_filename);
655         } else {
656                 save_page_list (document, document->ps_export_pagelist,
657                                 document->ps_export_filename);
658                 g_free (document->ps_export_pagelist);
659                 g_free (document->ps_export_filename);  
660                 document->ps_export_pagelist = NULL;
661                 document->ps_export_filename = NULL;
662         }
663 }
664
665 static void
666 ps_document_file_exporter_iface_init (EvFileExporterIface *iface)
667 {
668         iface->format_supported = ps_document_file_exporter_format_supported;
669         iface->begin = ps_document_file_exporter_begin;
670         iface->do_page = ps_document_file_exporter_do_page;
671         iface->end = ps_document_file_exporter_end;
672 }