]> www.fi.muni.cz Git - evince.git/blob - ps/ps-document.c
Added Walloon file
[evince.git] / 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
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 #include <string.h>
27 #include <stdlib.h>
28 #include <signal.h>
29 #include <gtk/gtk.h>
30 #include <gtk/gtkobject.h>
31 #include <gdk/gdkprivate.h>
32 #include <gdk/gdkx.h>
33 #include <gdk/gdk.h>
34 #include <glib/gi18n.h>
35 #include <X11/Intrinsic.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <sys/wait.h>
43 #include <stdio.h>
44 #include <math.h>
45
46 #include "ps-document.h"
47 #include "ev-debug.h"
48 #include "gsdefaults.h"
49
50 #ifdef HAVE_LOCALE_H
51 #   include <locale.h>
52 #endif
53
54 /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
55 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
56 #   define O_NONBLOCK O_NDELAY
57 #endif
58
59 #define MAX_BUFSIZE 1024
60
61 #define PS_DOCUMENT_IS_COMPRESSED(gs)       (PS_DOCUMENT(gs)->gs_filename_unc != NULL)
62 #define PS_DOCUMENT_GET_PS_FILE(gs)         (PS_DOCUMENT_IS_COMPRESSED(gs) ? \
63                                         PS_DOCUMENT(gs)->gs_filename_unc : \
64                                         PS_DOCUMENT(gs)->gs_filename)
65
66 GCond* pixbuf_cond = NULL;
67 GMutex* pixbuf_mutex = NULL;
68 GdkPixbuf *current_pixbuf = NULL;
69
70 enum {
71         PROP_0,
72         PROP_TITLE
73 };
74
75 /* structure to describe section of file to send to ghostscript */
76 struct record_list {
77   FILE *fp;
78   long begin;
79   guint len;
80   gboolean seek_needed;
81   gboolean close;
82   struct record_list *next;
83 };
84
85 static gboolean broken_pipe = FALSE;
86
87 /* Forward declarations */
88 static void ps_document_init(PSDocument * gs);
89 static void ps_document_class_init(PSDocumentClass * klass);
90 static void ps_document_finalize(GObject * object);
91 static void send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close);
92 static void close_pipe(int p[2]);
93 static void output(gpointer data, gint source, GdkInputCondition condition);
94 static void input(gpointer data, gint source, GdkInputCondition condition);
95 static void stop_interpreter(PSDocument * gs);
96 static gint start_interpreter(PSDocument * gs);
97 static void ps_document_document_iface_init (EvDocumentIface *iface);
98 static gboolean ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data);
99
100 static GObjectClass *parent_class = NULL;
101
102 static PSDocumentClass *gs_class = NULL;
103
104 static void
105 ps_document_init (PSDocument *gs)
106 {
107         gs->bpixmap = NULL;
108
109         gs->current_page = 0;
110         gs->interpreter_pid = -1;
111
112         gs->width = -1;
113         gs->height = -1;
114         gs->busy = FALSE;
115         gs->gs_filename = 0;
116         gs->gs_filename_unc = 0;
117
118         broken_pipe = FALSE;
119
120         gs->structured_doc = FALSE;
121         gs->reading_from_pipe = FALSE;
122         gs->send_filename_to_gs = FALSE;
123
124         gs->doc = NULL;
125         gs->loaded = FALSE;
126
127         gs->interpreter_input = -1;
128         gs->interpreter_output = -1;
129         gs->interpreter_err = -1;
130         gs->interpreter_input_id = 0;
131         gs->interpreter_output_id = 0;
132         gs->interpreter_error_id = 0;
133
134         gs->ps_input = NULL;
135         gs->input_buffer = NULL;
136         gs->input_buffer_ptr = NULL;
137         gs->bytes_left = 0;
138         gs->buffer_bytes_left = 0;
139
140         gs->zoom_factor = 1.0;
141
142         gs->gs_status = _("No document loaded.");
143
144         pixbuf_cond = g_cond_new ();
145         pixbuf_mutex = g_mutex_new ();
146 }
147
148 static void
149 ps_document_set_property (GObject *object,
150                           guint prop_id,
151                           const GValue *value,
152                           GParamSpec *pspec)
153 {
154         switch (prop_id)
155
156         {
157                 case PROP_TITLE:
158                         /* read only */
159                         break;
160         }
161 }
162
163 static void
164 ps_document_get_property (GObject *object,
165                           guint prop_id,
166                           GValue *value,
167                           GParamSpec *pspec)
168 {
169         PSDocument *ps = PS_DOCUMENT (object);
170
171         switch (prop_id)
172         {
173                 case PROP_TITLE:
174                         if (ps->doc) {
175                                 g_value_set_string (value, ps->doc->title);
176                         } else {
177                                 g_value_set_string (value, NULL);
178                         }
179                         break;
180         }
181 }
182
183 static void
184 ps_document_class_init(PSDocumentClass *klass)
185 {
186         GObjectClass *object_class;
187
188         object_class = (GObjectClass *) klass;
189         parent_class = g_type_class_peek_parent (klass);
190         gs_class = klass;
191
192         object_class->finalize = ps_document_finalize;
193         object_class->get_property = ps_document_get_property;
194         object_class->set_property = ps_document_set_property;
195
196         klass->gs_atom = gdk_atom_intern ("GHOSTVIEW", FALSE);
197         klass->next_atom = gdk_atom_intern ("NEXT", FALSE);
198         klass->page_atom = gdk_atom_intern ("PAGE", FALSE);
199         klass->string_atom = gdk_atom_intern ("STRING", FALSE);
200
201         g_object_class_override_property (object_class, PROP_TITLE, "title");
202 }
203
204 static void
205 push_pixbuf (PSDocument *gs)
206 {
207         GdkColormap *cmap;
208         GdkPixbuf *pixbuf;
209
210         cmap = gdk_window_get_colormap (gs->pstarget);
211         
212         pixbuf =  gdk_pixbuf_get_from_drawable (NULL, gs->bpixmap, cmap,
213                                                 0, 0, 0, 0,
214                                                 gs->width, gs->height);
215         g_mutex_lock (pixbuf_mutex);
216         current_pixbuf = pixbuf;
217         g_cond_signal (pixbuf_cond);
218         g_mutex_unlock (pixbuf_mutex);
219
220 }
221
222 static void
223 interpreter_failed (PSDocument *gs, char *msg)
224 {
225         LOG ("Interpreter failed %s", msg);
226
227         push_pixbuf (gs);
228
229         stop_interpreter (gs);
230 }
231
232 static void
233 ps_document_cleanup (PSDocument *gs)
234 {
235         g_return_if_fail (gs != NULL);
236         g_return_if_fail (PS_IS_DOCUMENT (gs));
237
238         LOG ("Cleanup\n");
239
240         stop_interpreter (gs);
241
242         if (gs->gs_psfile) {
243                 fclose (gs->gs_psfile);
244                 gs->gs_psfile = NULL;
245         }
246
247         if (gs->gs_filename) {
248                 g_free (gs->gs_filename);
249                 gs->gs_filename = NULL;
250         }
251
252         if (gs->doc) {
253                 psfree (gs->doc);
254                 gs->doc = NULL;
255         }
256
257         if (gs->gs_filename_unc) {
258                 unlink(gs->gs_filename_unc);
259                 g_free(gs->gs_filename_unc);
260                 gs->gs_filename_unc = NULL;
261         }
262
263         gs->current_page = 0;
264         gs->loaded = FALSE;
265 }
266
267 static gboolean
268 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
269 {
270         PSDocument *gs = (PSDocument *) data;
271
272         if(event->type != GDK_CLIENT_EVENT)
273                 return FALSE;
274
275         gs->message_window = event->client.data.l[0];
276
277         if (event->client.message_type == gs_class->page_atom) {
278                 LOG ("GS rendered the document");
279                 gs->busy = FALSE;
280
281                 push_pixbuf (gs);
282         }
283
284         return TRUE;
285 }
286
287 static void
288 ps_document_finalize (GObject * object)
289 {
290         PSDocument *gs;
291
292         g_return_if_fail (object != NULL);
293         g_return_if_fail (PS_IS_DOCUMENT (object));
294
295         LOG ("Finalize");
296
297         gs = PS_DOCUMENT (object);
298
299         ps_document_cleanup (gs);
300         stop_interpreter (gs);
301
302         if(gs->input_buffer) {
303                 g_free(gs->input_buffer);
304                 gs->input_buffer = NULL;
305         }
306
307         (*G_OBJECT_CLASS (parent_class)->finalize) (object);
308 }
309
310 static void
311 send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close)
312 {
313   struct record_list *ps_new;
314
315   if(gs->interpreter_input < 0) {
316     g_critical("No pipe to gs: error in send_ps().");
317     return;
318   }
319
320   ps_new = (struct record_list *) g_malloc(sizeof(struct record_list));
321   ps_new->fp = gs->gs_psfile;
322   ps_new->begin = begin;
323   ps_new->len = len;
324   ps_new->seek_needed = TRUE;
325   ps_new->close = close;
326   ps_new->next = NULL;
327
328   if(gs->input_buffer == NULL) {
329     gs->input_buffer = g_malloc(MAX_BUFSIZE);
330   }
331
332   if(gs->ps_input == NULL) {
333     gs->input_buffer_ptr = gs->input_buffer;
334     gs->bytes_left = len;
335     gs->buffer_bytes_left = 0;
336     gs->ps_input = ps_new;
337     gs->interpreter_input_id =
338       gdk_input_add(gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
339   }
340   else {
341     struct record_list *p = gs->ps_input;
342     while(p->next != NULL) {
343       p = p->next;
344     }
345     p->next = ps_new;
346   }
347 }
348
349 static float
350 get_xdpi (PSDocument *gs)
351 {
352         return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
353 }
354
355 static float
356 get_ydpi (PSDocument *gs)
357 {
358         return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
359 }
360
361 static void
362 setup_pixmap (PSDocument *gs)
363 {
364         GdkGC *fill;
365         GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF };   /* pixel, r, g, b */
366         GdkColormap *colormap;
367
368         LOG ("Create our internal pixmap");
369
370         if(gs->bpixmap) {
371                 gdk_drawable_unref(gs->bpixmap);
372         }
373
374         fill = gdk_gc_new (gs->pstarget);
375         colormap = gdk_drawable_get_colormap (gs->pstarget);
376         gdk_color_alloc (colormap, &white);
377         gdk_gc_set_foreground (fill, &white);
378         gs->bpixmap = gdk_pixmap_new (gs->pstarget, gs->width, gs->height, -1);
379         gdk_draw_rectangle (gs->bpixmap, fill, TRUE,
380                             0, 0, gs->width, gs->height);
381 }
382
383 static void
384 setup_page (PSDocument *gs)
385 {
386         char buf[1024];
387 #ifdef HAVE_LOCALE_H
388         char *savelocale;
389 #endif
390
391         LOG ("Setup the page");
392
393 #ifdef HAVE_LOCALE_H
394         /* gs needs floating point parameters with '.' as decimal point
395          * while some (european) locales use ',' instead, so we set the 
396          * locale for this snprintf to "C".
397          */
398         savelocale = setlocale (LC_NUMERIC, "C");
399 #endif
400
401         g_snprintf (buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
402                     0L, gs->orientation * 90, gs->llx, gs->lly, gs->urx, gs->ury,
403                     get_xdpi (gs) * gs->zoom_factor,
404                     get_ydpi (gs) * gs->zoom_factor,
405                     0, 0, 0, 0);
406         LOG ("GS property %s", buf);
407
408 #ifdef HAVE_LOCALE_H
409         setlocale(LC_NUMERIC, savelocale);
410 #endif
411         gdk_property_change (gs->pstarget, gs_class->gs_atom, gs_class->string_atom,
412                              8, GDK_PROP_MODE_REPLACE, (guchar *)buf, strlen(buf));
413         gdk_flush ();
414 }
415
416 static void
417 close_pipe (int p[2])
418 {
419         if (p[0] != -1) {
420                 close (p[0]);
421         }
422         if (p[1] != -1) {
423                 close (p[1]);
424         }
425 }
426
427 static gboolean
428 is_interpreter_ready (PSDocument *gs)
429 {
430         return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
431 }
432
433 static void
434 output(gpointer data, gint source, GdkInputCondition condition)
435 {
436   char buf[MAX_BUFSIZE + 1], *msg;
437   guint bytes = 0;
438   PSDocument *gs = PS_DOCUMENT(data);
439
440   if(source == gs->interpreter_output) {
441     bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
442     if(bytes == 0) {            /* EOF occurred */
443       close(gs->interpreter_output);
444       gs->interpreter_output = -1;
445       gdk_input_remove(gs->interpreter_output_id);
446       return;
447     }
448     else if(bytes == -1) {
449       /* trouble... */
450       interpreter_failed(gs, NULL);
451       return;
452     }
453     if(gs->interpreter_err == -1) {
454       interpreter_failed(gs, NULL);
455     }
456   }
457   else if(source == gs->interpreter_err) {
458     bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
459     if(bytes == 0) {            /* EOF occurred */
460       close(gs->interpreter_err);
461       gs->interpreter_err = -1;
462       gdk_input_remove(gs->interpreter_error_id);
463       return;
464     }
465     else if(bytes == -1) {
466       /* trouble... */
467       interpreter_failed(gs, NULL);
468       return;
469     }
470     if(gs->interpreter_output == -1) {
471       interpreter_failed(gs, NULL);
472     }
473   }
474   if(bytes > 0) {
475     buf[bytes] = '\0';
476     msg = g_strdup(buf);
477     interpreter_failed (gs, msg);   
478   }
479 }
480
481 static void
482 catchPipe(int i)
483 {
484   broken_pipe = True;
485 }
486
487 static void
488 input(gpointer data, gint source, GdkInputCondition condition)
489 {
490   PSDocument *gs = PS_DOCUMENT(data);
491   int bytes_written;
492   void (*oldsig) (int);
493   oldsig = signal(SIGPIPE, catchPipe);
494
495   LOG ("Input");
496
497   do {
498     if(gs->buffer_bytes_left == 0) {
499       /* Get a new section if required */
500       if(gs->ps_input && gs->bytes_left == 0) {
501         struct record_list *ps_old = gs->ps_input;
502         gs->ps_input = ps_old->next;
503         if(ps_old->close && NULL != ps_old->fp)
504           fclose(ps_old->fp);
505         g_free((char *) ps_old);
506       }
507       /* Have to seek at the beginning of each section */
508       if(gs->ps_input && gs->ps_input->seek_needed) {
509         fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
510         gs->ps_input->seek_needed = FALSE;
511         gs->bytes_left = gs->ps_input->len;
512       }
513
514       if(gs->bytes_left > MAX_BUFSIZE) {
515         gs->buffer_bytes_left =
516           fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
517       }
518       else if(gs->bytes_left > 0) {
519         gs->buffer_bytes_left =
520           fread(gs->input_buffer,
521                 sizeof(char), gs->bytes_left, gs->ps_input->fp);
522       }
523       else {
524         gs->buffer_bytes_left = 0;
525       }
526       if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
527         interpreter_failed (gs, NULL); /* Error occurred */
528       }
529       gs->input_buffer_ptr = gs->input_buffer;
530       gs->bytes_left -= gs->buffer_bytes_left;
531     }
532
533     if(gs->buffer_bytes_left > 0) {
534       bytes_written = write(gs->interpreter_input,
535                             gs->input_buffer_ptr, gs->buffer_bytes_left);
536
537       if(broken_pipe) {
538         interpreter_failed (gs, g_strdup(_("Broken pipe.")));
539         broken_pipe = FALSE;
540         interpreter_failed (gs, NULL);
541       }
542       else if(bytes_written == -1) {
543         if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
544           interpreter_failed (gs, NULL);   /* Something bad happened */
545         }
546       }
547       else {
548         gs->buffer_bytes_left -= bytes_written;
549         gs->input_buffer_ptr += bytes_written;
550       }
551     }
552   }
553   while(gs->ps_input && gs->buffer_bytes_left == 0);
554
555   signal(SIGPIPE, oldsig);
556
557   if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
558     if(gs->interpreter_input_id != 0) {
559       gdk_input_remove(gs->interpreter_input_id);
560       gs->interpreter_input_id = 0;
561     }
562   }
563 }
564
565 static int
566 start_interpreter (PSDocument *gs)
567 {
568         int std_in[2] = { -1, -1 };   /* pipe to interp stdin */
569         int std_out[2];               /* pipe from interp stdout */
570         int std_err[2];               /* pipe from interp stderr */
571
572 #define NUM_ARGS    100
573 #define NUM_GS_ARGS (NUM_ARGS - 20)
574 #define NUM_ALPHA_ARGS 10
575
576         char *argv[NUM_ARGS], *dir, *gv_env;
577         char **gs_args, **alpha_args = NULL;
578         int argc = 0, i;
579
580         LOG ("Start the interpreter");
581
582         if(!gs->gs_filename)
583                 return 0;
584
585         stop_interpreter(gs);
586
587         /* set up the args... */
588         gs_args = g_strsplit (gtk_gs_defaults_get_interpreter_cmd (), " ", NUM_GS_ARGS);
589         for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++) {
590                 argv[argc] = gs_args[i];
591         }
592
593         alpha_args = g_strsplit (ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
594
595         argv[argc++] = "-dNOPAUSE";
596         argv[argc++] = "-dQUIET";
597         argv[argc++] = "-dSAFER";
598
599         /* set up the pipes */
600         if (gs->send_filename_to_gs) {
601                 argv[argc++] = PS_DOCUMENT_GET_PS_FILE (gs);
602                 argv[argc++] = "-c";
603                 argv[argc++] = "quit";
604         } else {
605                 argv[argc++] = "-";
606         }
607
608         argv[argc++] = NULL;
609
610         if (!gs->reading_from_pipe && !gs->send_filename_to_gs) {
611                 if (pipe (std_in) == -1) {
612                         g_critical ("Unable to open pipe to Ghostscript.");
613                         return -1;
614                 }
615         }
616
617         if (pipe (std_out) == -1) {
618                 close_pipe (std_in);
619                 return -1;
620         }
621
622         if (pipe(std_err) == -1) {
623                 close_pipe (std_in);
624                 close_pipe (std_out);
625                 return -1;
626         }
627
628         gv_env = g_strdup_printf ("GHOSTVIEW=%ld %ld",
629                                   gdk_x11_drawable_get_xid (gs->pstarget),
630                                   gdk_x11_drawable_get_xid (gs->bpixmap));
631         LOG ("Launching ghostview with env %s", gv_env);
632
633         gs->busy = TRUE;
634         gs->interpreter_pid = fork ();
635         switch (gs->interpreter_pid) {
636                 case -1:                     /* error */
637                         close_pipe (std_in);
638                         close_pipe (std_out);
639                         close_pipe (std_err);
640                         return -2;
641                         break;
642                 case 0:                      /* child */
643                         close (std_out[0]);
644                         dup2 (std_out[1], 1);
645                         close (std_out[1]);
646
647                         close (std_err[0]);
648                         dup2 (std_err[1], 2);
649                         close (std_err[1]);
650
651                         if (!gs->reading_from_pipe) {
652                                 if (gs->send_filename_to_gs) {
653                                         int stdinfd;
654                                         /* just in case gs tries to read from stdin */
655                                         stdinfd = open("/dev/null", O_RDONLY);
656                                         if (stdinfd != 0) {
657                                                 dup2(stdinfd, 0);
658                                                 close(stdinfd);
659                                         }
660                                 } else {
661                                         close (std_in[1]);
662                                         dup2 (std_in[0], 0);
663                                         close (std_in[0]);
664                                 }
665                         }
666
667                         putenv(gv_env);
668
669                         /* change to directory where the input file is. This helps
670                          * with postscript-files which include other files using
671                          * a relative path */
672                         dir = g_path_get_dirname (gs->gs_filename);
673                         chdir (dir);
674                         g_free (dir);
675
676                         execvp (argv[0], argv);
677
678                         /* Notify error */
679                         g_critical ("Unable to execute [%s]\n", argv[0]);
680                         g_strfreev (gs_args);
681                         g_free (gv_env);
682                         g_strfreev (alpha_args);
683                         _exit (1);
684                         break;
685                 default:                     /* parent */
686                         if (!gs->send_filename_to_gs && !gs->reading_from_pipe) {
687                                 int result;
688                                 close (std_in[0]);
689                                 /* use non-blocking IO for pipe to ghostscript */
690                                 result = fcntl (std_in[1], F_GETFL, 0);
691                                 fcntl (std_in[1], F_SETFL, result | O_NONBLOCK);
692                                 gs->interpreter_input = std_in[1];
693                         } else {
694                                 gs->interpreter_input = -1;
695                         }
696                         close (std_out[1]);
697
698                         gs->interpreter_output = std_out[0];
699                         close (std_err[1]);
700                         gs->interpreter_err = std_err[0];
701                         gs->interpreter_output_id =
702                                 gdk_input_add (std_out[0], GDK_INPUT_READ, output, gs);
703                         gs->interpreter_error_id =
704                                 gdk_input_add (std_err[0], GDK_INPUT_READ, output, gs);
705                         break;
706         }
707
708         return TRUE;
709 }
710
711 static void
712 stop_interpreter(PSDocument * gs)
713 {
714   if(gs->interpreter_pid > 0) {
715     int status = 0;
716     LOG ("Stop the interpreter");
717     kill(gs->interpreter_pid, SIGTERM);
718     while((wait(&status) == -1) && (errno == EINTR)) ;
719     gs->interpreter_pid = -1;
720     if(status == 1) {
721       ps_document_cleanup(gs);
722       gs->gs_status = _("Interpreter failed.");
723     }
724   }
725
726   if(gs->interpreter_input >= 0) {
727     close(gs->interpreter_input);
728     gs->interpreter_input = -1;
729     if(gs->interpreter_input_id != 0) {
730       gdk_input_remove(gs->interpreter_input_id);
731       gs->interpreter_input_id = 0;
732     }
733     while(gs->ps_input) {
734       struct record_list *ps_old = gs->ps_input;
735       gs->ps_input = gs->ps_input->next;
736       if(ps_old->close && NULL != ps_old->fp)
737         fclose(ps_old->fp);
738       g_free((char *) ps_old);
739     }
740   }
741
742   if(gs->interpreter_output >= 0) {
743     close(gs->interpreter_output);
744     gs->interpreter_output = -1;
745     if(gs->interpreter_output_id) {
746       gdk_input_remove(gs->interpreter_output_id);
747       gs->interpreter_output_id = 0;
748     }
749   }
750
751   if(gs->interpreter_err >= 0) {
752     close(gs->interpreter_err);
753     gs->interpreter_err = -1;
754     if(gs->interpreter_error_id) {
755       gdk_input_remove(gs->interpreter_error_id);
756       gs->interpreter_error_id = 0;
757     }
758   }
759
760   gs->busy = FALSE;
761 }
762
763 /* If file exists and is a regular file then return its length, else -1 */
764 static gint
765 file_length(const gchar * filename)
766 {
767   struct stat stat_rec;
768
769   if(filename && (stat(filename, &stat_rec) == 0)
770      && S_ISREG(stat_rec.st_mode))
771     return stat_rec.st_size;
772   else
773     return -1;
774 }
775
776 /* Test if file exists, is a regular file and its length is > 0 */
777 static gboolean
778 file_readable(const char *filename)
779 {
780   return (file_length(filename) > 0);
781 }
782
783 /*
784  * Decompress gs->gs_filename if necessary
785  * Set gs->filename_unc to the name of the uncompressed file or NULL.
786  * Error reporting via signal 'interpreter_message'
787  * Return name of input file to use or NULL on error..
788  */
789 static gchar *
790 check_filecompressed(PSDocument * gs)
791 {
792   FILE *file;
793   gchar buf[1024];
794   gchar *filename, *filename_unc, *filename_err, *cmdline;
795   const gchar *cmd;
796   int fd;
797
798   cmd = NULL;
799
800   if((file = fopen(gs->gs_filename, "r"))
801      && (fread(buf, sizeof(gchar), 3, file) == 3)) {
802     if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
803       /* file is gzipped or compressed */
804       cmd = gtk_gs_defaults_get_ungzip_cmd();
805     }
806     else if(strncmp(buf, "BZh", 3) == 0) {
807       /* file is compressed with bzip2 */
808       cmd = gtk_gs_defaults_get_unbzip2_cmd();
809     }
810   }
811   if(NULL != file)
812     fclose(file);
813
814   if(!cmd)
815     return gs->gs_filename;
816
817   /* do the decompression */
818   filename = g_shell_quote(gs->gs_filename);
819   filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
820   if((fd = mkstemp(filename_unc)) < 0) {
821     g_free(filename_unc);
822     g_free(filename);
823     return NULL;
824   }
825   close(fd);
826   filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
827   if((fd = mkstemp(filename_err)) < 0) {
828     g_free(filename_err);
829     g_free(filename_unc);
830     g_free(filename);
831     return NULL;
832   }
833   close(fd);
834   cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
835                             filename, filename_unc, filename_err);
836   if((system(cmdline) == 0)
837      && file_readable(filename_unc)
838      && (file_length(filename_err) == 0)) {
839     /* sucessfully uncompressed file */
840     gs->gs_filename_unc = filename_unc;
841   }
842   else {
843     /* report error */
844     g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
845                gs->gs_filename);
846     interpreter_failed (gs, buf);
847     unlink(filename_unc);
848     g_free(filename_unc);
849     filename_unc = NULL;
850   }
851   unlink(filename_err);
852   g_free(filename_err);
853   g_free(cmdline);
854   g_free(filename);
855   return filename_unc;
856 }
857
858 static void
859 compute_dimensions (PSDocument *gs, int page)
860 {
861         GtkGSPaperSize *paper_sizes = gtk_gs_defaults_get_paper_sizes ();
862         int urx, ury, llx, lly;
863         int width, height;
864         int orientation;
865
866         g_return_if_fail (PS_IS_DOCUMENT (gs));
867         g_return_if_fail (gs->doc != NULL);
868         g_return_if_fail (page >= 0);
869         g_return_if_fail (gs->doc->numpages > page);
870
871         orientation = GTK_GS_ORIENTATION_NONE;
872         if (gs->structured_doc) {
873                 orientation = gs->doc->pages[gs->current_page].orientation;
874         }
875         if (orientation == GTK_GS_ORIENTATION_NONE) {
876                 orientation = GTK_GS_ORIENTATION_PORTRAIT;
877         }
878
879         if (gs->doc->pages && gs->doc->pages[page].size) {
880                 int page_size;
881
882                 page_size = gs->doc->pages[page].size - gs->doc->size;
883                 llx = lly = 0;
884                 urx = gs->doc->size[page_size].width;
885                 ury = gs->doc->size[page_size].height;
886         } else if (gs->doc->pages &&
887                    (gs->doc->pages[page].boundingbox[URX] >
888                     gs->doc->pages[page].boundingbox[LLX]) &&
889                    (gs->doc->pages[page].boundingbox[URY] >
890                     gs->doc->pages[page].boundingbox[LLY])) {
891                 llx = gs->doc->pages[page].boundingbox[LLX];
892                 lly = gs->doc->pages[page].boundingbox[LLY];
893                 urx = gs->doc->pages[page].boundingbox[URX];
894                 ury = gs->doc->pages[page].boundingbox[URY];
895         } else if ((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
896                    (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
897                 llx = gs->doc->boundingbox[LLX];
898                 lly = gs->doc->boundingbox[LLY];
899                 urx = gs->doc->boundingbox[URX];
900                 ury = gs->doc->boundingbox[URY];
901         } else {
902                 /* Fallback to A4 */
903                 llx = lly = 0;
904                 urx = paper_sizes[12].width;
905                 ury = paper_sizes[12].height;
906         }
907
908         switch (orientation) {
909                 case GTK_GS_ORIENTATION_PORTRAIT:
910                 case GTK_GS_ORIENTATION_UPSIDEDOWN:
911                         width = (urx - llx) / 72.0 * get_xdpi (gs) + 0.5;
912                         height = (ury - lly) / 72.0 * get_ydpi (gs) + 0.5;
913                         break;
914                 case GTK_GS_ORIENTATION_LANDSCAPE:
915                 case GTK_GS_ORIENTATION_SEASCAPE:
916                         width = (ury - lly) / 72.0 * get_xdpi (gs) + 0.5;
917                         height = (urx - llx) / 72.0 * get_ydpi (gs) + 0.5;
918                         break;
919                 default:
920                         width = height = 0;
921                         g_assert_not_reached ();
922                         break;
923         }
924
925         width = width * gs->zoom_factor;
926         height = height * gs->zoom_factor;
927
928         if (llx != gs->llx || lly != gs->lly ||
929             urx != gs->urx || ury != gs->ury ||
930             gs->width != width || gs->height != height ||
931             orientation != gs->orientation) {
932                 gs->llx = llx;
933                 gs->lly = lly;
934                 gs->urx = urx;
935                 gs->ury = ury;
936                 gs->width = width;
937                 gs->height = height;
938                 gs->orientation = orientation;
939                 gs->changed = TRUE;
940         }
941 }
942
943 static gint
944 ps_document_enable_interpreter(PSDocument * gs)
945 {
946   g_return_val_if_fail(gs != NULL, FALSE);
947   g_return_val_if_fail(PS_IS_DOCUMENT(gs), FALSE);
948
949   if(!gs->gs_filename)
950     return 0;
951
952   return start_interpreter(gs);
953 }
954
955 /* publicly accessible functions */
956
957 GType
958 ps_document_get_type(void)
959 {
960   static GType gs_type = 0;
961   if(!gs_type) {
962     GTypeInfo gs_info = {
963       sizeof(PSDocumentClass),
964       (GBaseInitFunc) NULL,
965       (GBaseFinalizeFunc) NULL,
966       (GClassInitFunc) ps_document_class_init,
967       (GClassFinalizeFunc) NULL,
968       NULL,                     /* class_data */
969       sizeof(PSDocument),
970       0,                        /* n_preallocs */
971       (GInstanceInitFunc) ps_document_init
972     };
973
974     static const GInterfaceInfo document_info =
975     {
976         (GInterfaceInitFunc) ps_document_document_iface_init,
977         NULL,
978         NULL
979     };
980
981     gs_type = g_type_register_static(G_TYPE_OBJECT,
982                                      "PSDocument", &gs_info, 0);
983
984     g_type_add_interface_static (gs_type,
985                                  EV_TYPE_DOCUMENT,
986                                  &document_info);
987   }
988   return gs_type;
989
990
991 }
992
993 static gboolean
994 document_load(PSDocument * gs, const gchar * fname)
995 {
996   g_return_val_if_fail(gs != NULL, FALSE);
997   g_return_val_if_fail(PS_IS_DOCUMENT(gs), FALSE);
998
999   LOG ("Load the document");
1000
1001   /* clean up previous document */
1002   ps_document_cleanup(gs);
1003
1004   if(fname == NULL) {
1005     gs->gs_status = "";
1006     return FALSE;
1007   }
1008
1009   /* prepare this document */
1010   gs->structured_doc = FALSE;
1011   gs->send_filename_to_gs = TRUE;
1012   gs->current_page = 0;
1013   gs->loaded = FALSE;
1014   if(*fname == '/') {
1015     /* an absolute path */
1016     gs->gs_filename = g_strdup(fname);
1017   }
1018   else {
1019     /* path relative to our cwd: make it absolute */
1020     gchar *cwd = g_get_current_dir();
1021     gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1022     g_free(cwd);
1023   }
1024
1025   if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1026     gs->send_filename_to_gs = FALSE;
1027   }
1028   else {
1029     /*
1030      * We need to make sure that the file is loadable/exists!
1031      * otherwise we want to exit without loading new stuff...
1032      */
1033     gchar *filename = NULL;
1034
1035     if(!file_readable(fname)) {
1036       gchar buf[1024];
1037       g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1038       interpreter_failed (gs, buf);
1039       gs->gs_status = _("File is not readable.");
1040     }
1041     else {
1042       filename = check_filecompressed(gs);
1043     }
1044
1045     if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1046       interpreter_failed (gs, NULL);
1047       ps_document_cleanup(gs);
1048       return FALSE;
1049     }
1050
1051     /* we grab the vital statistics!!! */
1052     gs->doc = psscan(gs->gs_psfile, TRUE, filename);
1053
1054     g_object_notify (G_OBJECT (gs), "title");
1055
1056     if(gs->doc == NULL) {
1057       /* File does not seem to be a Postscript one */
1058       gchar buf[1024];
1059       g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1060       interpreter_failed (gs, buf);
1061       ps_document_cleanup(gs);
1062       gs->gs_status = _("The file is not a PostScript document.");
1063       return FALSE;
1064     }
1065
1066     if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1067        (gs->doc->epsf && gs->doc->numpages > 1)) {
1068       gs->structured_doc = TRUE;
1069       gs->send_filename_to_gs = FALSE;
1070     }
1071   }
1072   gs->loaded = TRUE;
1073   compute_dimensions (gs, gs->current_page);
1074
1075   gs->gs_status = _("Document loaded.");
1076
1077   return gs->loaded;
1078 }
1079
1080
1081 static gboolean
1082 ps_document_next_page (PSDocument *gs)
1083 {
1084         XEvent event;
1085
1086         LOG ("Make ghostscript render next page");
1087
1088         g_return_val_if_fail (PS_IS_DOCUMENT(gs), FALSE);
1089         g_return_val_if_fail (gs->interpreter_pid != 0, FALSE);
1090         g_return_val_if_fail (gs->busy != TRUE, FALSE);
1091
1092         gs->busy = TRUE;
1093
1094         event.xclient.type = ClientMessage;
1095         event.xclient.display = gdk_display;
1096         event.xclient.window = gs->message_window;
1097         event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1098         event.xclient.format = 32;
1099
1100         gdk_error_trap_push ();
1101         XSendEvent (gdk_display, gs->message_window, FALSE, 0, &event);
1102         gdk_flush ();
1103         gdk_error_trap_pop ();
1104
1105         return TRUE;
1106 }
1107
1108 static gboolean
1109 render_page (PSDocument *gs)
1110 {
1111         g_return_val_if_fail(gs != NULL, FALSE);
1112         g_return_val_if_fail(PS_IS_DOCUMENT(gs), FALSE);
1113
1114         if(!gs->gs_filename) {
1115                 return FALSE;
1116         }
1117
1118         if (gs->structured_doc && gs->doc) {
1119                 LOG ("It's a structured document, let's send one page to gs");
1120
1121                 if (is_interpreter_ready (gs)) {
1122                         ps_document_next_page (gs);
1123                 } else {
1124                         ps_document_enable_interpreter (gs);
1125                         send_ps (gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1126                         send_ps (gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1127                 }
1128
1129                 send_ps (gs, gs->doc->pages[gs->current_page].begin,
1130                          gs->doc->pages[gs->current_page].len, FALSE);
1131         } else {
1132                 /* Unstructured document
1133                  *
1134                  * In the case of non structured documents,
1135                  * GS read the PS from the  actual file (via command
1136                  * line. Hence, ggv only send a signal next page.
1137                  * If ghostview is not running it is usually because
1138                  * the last page of the file was displayed. In that
1139                  * case, ggv restarts GS again and the first page is displayed.
1140                  */
1141
1142                 LOG ("It's an unstructured document, gs will just read the file");
1143
1144                 if (!is_interpreter_ready (gs)) {
1145                         ps_document_enable_interpreter(gs);
1146                 }
1147                 ps_document_next_page(gs);
1148         }
1149
1150         return TRUE;
1151 }
1152
1153 static gboolean
1154 ps_document_load (EvDocument  *document,
1155                   const char  *uri,
1156                   GError     **error)
1157 {
1158         gboolean result;
1159         char *filename;
1160
1161         filename = g_filename_from_uri (uri, NULL, error);
1162         if (!filename)
1163                 return FALSE;
1164
1165         result = document_load (PS_DOCUMENT (document), filename);
1166         if (!result) {
1167                 g_set_error (error, G_FILE_ERROR,
1168                              G_FILE_ERROR_FAILED,
1169                              "Failed to load document '%s'\n",
1170                              uri);
1171         }
1172
1173         g_free (filename);
1174
1175         return result;
1176 }
1177
1178 static gboolean
1179 ps_document_save (EvDocument  *document,
1180                   const char  *uri,
1181                   GError     **error)
1182 {
1183         g_warning ("ps_document_save not implemented"); /* FIXME */
1184         return TRUE;
1185 }
1186
1187 static int
1188 ps_document_get_n_pages (EvDocument  *document)
1189 {
1190         PSDocument *ps = PS_DOCUMENT (document);
1191
1192         g_return_val_if_fail (ps != NULL, -1);
1193
1194         if (!ps->gs_filename || !ps->doc) {
1195                 return -1;
1196         }
1197
1198         return ps->structured_doc ? ps->doc->numpages : 1;
1199 }
1200
1201 static void
1202 ps_document_set_page (EvDocument  *document,
1203                        int          page)
1204 {
1205         PSDocument *gs = PS_DOCUMENT (document);
1206
1207         LOG ("Set document page %d\n", page);
1208
1209         gs->current_page = page;
1210         compute_dimensions (gs, page);
1211 }
1212
1213 static int
1214 ps_document_get_page (EvDocument  *document)
1215 {
1216         PSDocument *ps = PS_DOCUMENT (document);
1217
1218         g_return_val_if_fail (ps != NULL, -1);
1219
1220         return ps->current_page;
1221 }
1222
1223 static void
1224 ps_document_set_scale (EvDocument  *document,
1225                         double       scale)
1226 {
1227         PSDocument *gs = PS_DOCUMENT (document);
1228
1229         gs->zoom_factor = scale;
1230         compute_dimensions (gs, gs->current_page);
1231 }
1232
1233 static void
1234 ps_document_get_page_size (EvDocument   *document,
1235                            int           page,
1236                            int          *width,
1237                            int          *height)
1238 {
1239         /* Post script documents never vary in size */
1240
1241         PSDocument *gs = PS_DOCUMENT (document);
1242
1243         if (width) {
1244                 *width = gs->width;
1245         }
1246
1247         if (height) {
1248                 *height = gs->height;
1249         }
1250 }
1251
1252 static char *
1253 ps_document_get_text (EvDocument *document, GdkRectangle *rect)
1254 {
1255         g_warning ("ps_document_get_text not implemented"); /* FIXME ? */
1256         return NULL;
1257 }
1258
1259 static EvLink *
1260 ps_document_get_link (EvDocument *document,
1261                       int         x,
1262                       int         y)
1263 {
1264         return NULL;
1265 }
1266
1267 static gboolean
1268 render_pixbuf_idle (EvDocument *document)
1269 {
1270         PSDocument *gs = PS_DOCUMENT (document);
1271
1272         if (gs->pstarget == NULL) {
1273                 GtkWidget *widget;
1274
1275                 widget = gtk_window_new (GTK_WINDOW_POPUP);
1276                 gtk_widget_realize (widget);
1277                 gs->pstarget = widget->window;
1278
1279                 g_assert (gs->pstarget != NULL);
1280
1281                 g_signal_connect (widget, "event",
1282                                   G_CALLBACK (ps_document_widget_event),
1283                                   gs);
1284         }
1285
1286         if (gs->changed) {
1287                 stop_interpreter (gs);
1288                 setup_pixmap (gs);
1289                 setup_page (gs);
1290                 gs->changed = FALSE;
1291         }
1292
1293         render_page (PS_DOCUMENT (document));
1294
1295         return FALSE;
1296 }
1297
1298 static GdkPixbuf *
1299 ps_document_render_pixbuf (EvDocument *document)
1300 {
1301         GdkPixbuf *pixbuf;
1302
1303         g_idle_add ((GSourceFunc)render_pixbuf_idle, document);
1304
1305         g_mutex_lock (pixbuf_mutex);
1306         while (!current_pixbuf)
1307                 g_cond_wait (pixbuf_cond, pixbuf_mutex);
1308         pixbuf = current_pixbuf;
1309         current_pixbuf = NULL;
1310         g_mutex_unlock (pixbuf_mutex);
1311
1312         LOG ("Pixbuf rendered %p\n", pixbuf);
1313
1314         return pixbuf;
1315 }
1316
1317 static void
1318 ps_document_document_iface_init (EvDocumentIface *iface)
1319 {
1320         iface->load = ps_document_load;
1321         iface->save = ps_document_save;
1322         iface->get_text = ps_document_get_text;
1323         iface->get_link = ps_document_get_link;
1324         iface->get_n_pages = ps_document_get_n_pages;
1325         iface->set_page = ps_document_set_page;
1326         iface->get_page = ps_document_get_page;
1327         iface->set_scale = ps_document_set_scale;
1328         iface->get_page_size = ps_document_get_page_size;
1329         iface->render_pixbuf = ps_document_render_pixbuf;
1330 }