]> www.fi.muni.cz Git - evince.git/blob - ps/ps-document.c
928e38c0da43d42eb87471eff131542839089e4b
[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 /*
26 Ghostview interface to ghostscript
27
28 When the GHOSTVIEW environment variable is set, ghostscript draws on
29 an existing drawable rather than creating its own window.  Ghostscript
30 can be directed to draw on either a window or a pixmap.
31
32 Drawing on a Window
33
34 The GHOSTVIEW environment variable contains the window id of the target
35 window.  The window id is an integer.  Ghostscript will use the attributes
36 of the window to obtain the width, height, colormap, screen, and visual of
37 the window. The remainder of the information is gotten from the GHOSTVIEW
38 property on that window.
39
40
41 Drawing on a Pixmap
42
43 The GHOSTVIEW environment variable contains a window id and a pixmap id.
44 They are integers separated by white space.  Ghostscript will use the
45 attributes of the window to obtain the colormap, screen, and visual to use.
46 The width and height will be obtained from the pixmap. The remainder of the
47 information, is gotten from the GHOSTVIEW property on the window.  In this
48 case, the property is deleted when read.
49
50 The GHOSTVIEW environment variable
51
52 parameters:     window-id [pixmap-id]
53
54 scanf format:   "%d %d"
55
56 explanation of parameters:
57
58         window-id: tells ghostscript where to
59                     - read the GHOSTVIEW property
60                     - send events
61                     If pixmap-id is not present,
62                     ghostscript will draw on this window.
63
64         pixmap-id: If present, tells ghostscript that a pixmap will be used
65                     as the final destination for drawing.  The window will
66                     not be touched for drawing purposes.
67
68 The GHOSTVIEW property
69
70 type:   STRING
71
72 parameters:
73
74     bpixmap orient llx lly urx ury xdpi ydpi [left bottom top right]
75
76 scanf format: "%d %d %d %d %d %d %f %f %d %d %d %d"
77
78 explanation of parameters:
79
80         bpixmap: pixmap id of the backing pixmap for the window.  If no
81                 pixmap is to be used, this parameter should be zero.  This
82                 parameter must be zero when drawing on a pixmap.
83
84         orient: orientation of the page.  The number represents clockwise
85                 rotation of the paper in degrees.  Permitted values are
86                 0, 90, 180, 270.
87
88         llx, lly, urx, ury: Bounding box of the drawable.  The bounding box
89                 is specified in PostScript points in default user coordinates.
90
91         xdpi, ydpi: Resolution of window.  (This can be derived from the
92                 other parameters, but not without roundoff error.  These
93                 values are included to avoid this error.)
94
95         left, bottom, top, right: (optional)
96                 Margins around the window.  The margins extend the imageable
97                 area beyond the boundaries of the window.  This is primarily
98                 used for popup zoom windows.  I have encountered several
99                 instances of PostScript programs that position themselves
100                 with respect to the imageable area.  The margins are specified
101                 in PostScript points.  If omitted, the margins are assumed to
102                 be 0.
103
104 Events from ghostscript
105
106 If the final destination is a pixmap, the client will get a property notify
107 event when ghostscript reads the GHOSTVIEW property causing it to be deleted.
108
109 Ghostscript sends events to the window where it read the GHOSTVIEW property.
110 These events are of type ClientMessage.  The message_type is set to
111 either PAGE or DONE.  The first long data value gives the window to be used
112 to send replies to ghostscript.  The second long data value gives the primary
113 drawable.  If rendering to a pixmap, it is the primary drawable.  If rendering
114 to a window, the backing pixmap is the primary drawable.  If no backing pixmap
115 is employed, then the window is the primary drawable.  This field is necessary
116 to distinguish multiple ghostscripts rendering to separate pixmaps where the
117 GHOSTVIEW property was placed on the same window.
118
119 The PAGE message indicates that a "page" has completed.  Ghostscript will
120 wait until it receives a ClientMessage whose message_type is NEXT before
121 continuing.
122
123 The DONE message indicates that ghostscript has finished processing.
124
125 */
126
127 #include "config.h"
128 #include <string.h>
129 #include <stdlib.h>
130 #include <signal.h>
131 #include <gtk/gtk.h>
132 #include <gtk/gtkobject.h>
133 #include <gdk/gdkprivate.h>
134 #include <gdk/gdkx.h>
135 #include <gdk/gdk.h>
136 #include <glib/gi18n.h>
137 #ifdef  HAVE_XINERAMA
138 #   include <gdk/gdkx.h>
139 #   include <X11/extensions/Xinerama.h>
140 #endif /* HAVE_XINERAMA */
141 #include <X11/Intrinsic.h>
142 #include <unistd.h>
143 #include <fcntl.h>
144 #include <stdlib.h>
145 #include <errno.h>
146 #include <sys/stat.h>
147 #include <sys/types.h>
148 #include <sys/wait.h>
149 #include <stdio.h>
150 #include <math.h>
151
152 #include "ps-document.h"
153 #include "ev-debug.h"
154 #include "gsdefaults.h"
155
156 #ifdef HAVE_LOCALE_H
157 #   include <locale.h>
158 #endif
159
160 /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
161 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
162 #   define O_NONBLOCK O_NDELAY
163 #endif
164
165 #define PS_DOCUMENT_WATCH_INTERVAL 1000
166 #define PS_DOCUMENT_WATCH_TIMEOUT  2
167
168 #define MAX_BUFSIZE 1024
169
170 #define PS_DOCUMENT_IS_COMPRESSED(gs)       (PS_DOCUMENT(gs)->gs_filename_unc != NULL)
171 #define PS_DOCUMENT_GET_PS_FILE(gs)         (PS_DOCUMENT_IS_COMPRESSED(gs) ? \
172                                         PS_DOCUMENT(gs)->gs_filename_unc : \
173                                         PS_DOCUMENT(gs)->gs_filename)
174
175 enum { INTERPRETER_MESSAGE, INTERPRETER_ERROR, LAST_SIGNAL };
176
177 enum {
178         PROP_0,
179         PROP_TITLE
180 };
181
182 /* structure to describe section of file to send to ghostscript */
183 struct record_list {
184   FILE *fp;
185   long begin;
186   guint len;
187   gboolean seek_needed;
188   gboolean close;
189   struct record_list *next;
190 };
191
192 static gboolean broken_pipe = FALSE;
193
194 static void
195 catchPipe(int i)
196 {
197   broken_pipe = True;
198 }
199
200 /* Forward declarations */
201 static void ps_document_init(PSDocument * gs);
202 static void ps_document_class_init(PSDocumentClass * klass);
203 static void ps_document_emit_error_msg(PSDocument * gs, const gchar * msg);
204 static void ps_document_finalize(GObject * object);
205 static void send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close);
206 static void set_up_page(PSDocument * gs);
207 static void close_pipe(int p[2]);
208 static void interpreter_failed(PSDocument * gs);
209 static float compute_xdpi(void);
210 static float compute_ydpi(void);
211 static gboolean compute_size(PSDocument * gs);
212 static void output(gpointer data, gint source, GdkInputCondition condition);
213 static void input(gpointer data, gint source, GdkInputCondition condition);
214 static void stop_interpreter(PSDocument * gs);
215 static gint start_interpreter(PSDocument * gs);
216 gboolean computeSize(void);
217 static gboolean ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid);
218 static void ps_document_document_iface_init (EvDocumentIface *iface);
219
220 static GObjectClass *parent_class = NULL;
221
222 static PSDocumentClass *gs_class = NULL;
223
224 static void
225 ps_document_init(PSDocument * gs)
226 {
227   gs->bpixmap = NULL;
228
229   gs->current_page = -2;
230   gs->disable_start = FALSE;
231   gs->interpreter_pid = -1;
232
233   gs->width = -1;
234   gs->height = -1;
235   gs->busy = FALSE;
236   gs->changed = FALSE;
237   gs->gs_scanstyle = 0;
238   gs->gs_filename = 0;
239   gs->gs_filename_dsc = 0;
240   gs->gs_filename_unc = 0;
241
242   broken_pipe = FALSE;
243
244   gs->structured_doc = FALSE;
245   gs->reading_from_pipe = FALSE;
246   gs->send_filename_to_gs = FALSE;
247
248   gs->doc = NULL;
249   gs->loaded = FALSE;
250
251   gs->interpreter_input = -1;
252   gs->interpreter_output = -1;
253   gs->interpreter_err = -1;
254   gs->interpreter_input_id = 0;
255   gs->interpreter_output_id = 0;
256   gs->interpreter_error_id = 0;
257
258   gs->ps_input = NULL;
259   gs->input_buffer = NULL;
260   gs->input_buffer_ptr = NULL;
261   gs->bytes_left = 0;
262   gs->buffer_bytes_left = 0;
263
264   gs->llx = 0;
265   gs->lly = 0;
266   gs->urx = 0;
267   gs->ury = 0;
268   gs->xdpi = compute_xdpi();
269   gs->ydpi = compute_ydpi();
270
271   gs->left_margin = 0;
272   gs->top_margin = 0;
273   gs->right_margin = 0;
274   gs->bottom_margin = 0;
275
276   gs->page_x_offset = 0;
277   gs->page_y_offset = 0;
278
279   /* Set user defined defaults */
280   gs->override_orientation = gtk_gs_defaults_get_override_orientation();
281   gs->fallback_orientation = gtk_gs_defaults_get_orientation();
282   gs->zoom_factor = gtk_gs_defaults_get_zoom_factor();
283   gs->default_size = gtk_gs_defaults_get_size();
284   gs->antialiased = gtk_gs_defaults_get_antialiased();
285   gs->override_size = gtk_gs_defaults_get_override_size();
286   gs->respect_eof = gtk_gs_defaults_get_respect_eof();
287   gs->zoom_mode = gtk_gs_defaults_get_zoom_mode();
288
289   gs->gs_status = _("No document loaded.");
290 }
291
292 static void
293 ps_document_set_property (GObject *object,
294                           guint prop_id,
295                           const GValue *value,
296                           GParamSpec *pspec)
297 {
298         switch (prop_id)
299
300         {
301                 case PROP_TITLE:
302                         /* read only */
303                         break;
304         }
305 }
306
307 static void
308 ps_document_get_property (GObject *object,
309                           guint prop_id,
310                           GValue *value,
311                           GParamSpec *pspec)
312 {
313         PSDocument *ps = PS_DOCUMENT (object);
314
315         switch (prop_id)
316         {
317                 case PROP_TITLE:
318                         if (ps->doc) {
319                                 g_value_set_string (value, ps->doc->title);
320                         } else {
321                                 g_value_set_string (value, NULL);
322                         }
323                         break;
324         }
325 }
326
327 static void
328 ps_document_class_init(PSDocumentClass * klass)
329 {
330   GObjectClass *object_class;
331
332   object_class = (GObjectClass *) klass;
333   parent_class = gtk_type_class(gtk_widget_get_type());
334   gs_class = klass;
335
336   object_class->finalize = ps_document_finalize;
337   object_class->get_property = ps_document_get_property;
338   object_class->set_property = ps_document_set_property;
339
340   /* Create atoms */
341   klass->gs_atom = gdk_atom_intern("GHOSTVIEW", FALSE);
342   klass->next_atom = gdk_atom_intern("NEXT", FALSE);
343   klass->page_atom = gdk_atom_intern("PAGE", FALSE);
344   klass->string_atom = gdk_atom_intern("STRING", FALSE);
345
346   gtk_gs_defaults_load();
347
348   g_object_class_override_property (object_class, PROP_TITLE, "title");
349 }
350
351 /* Clean all memory and temporal files */
352 static void
353 ps_document_cleanup(PSDocument * gs)
354 {
355   g_return_if_fail(gs != NULL);
356   g_return_if_fail(GTK_IS_GS(gs));
357
358   stop_interpreter(gs);
359
360   if(gs->gs_psfile) {
361     fclose(gs->gs_psfile);
362     gs->gs_psfile = NULL;
363   }
364   if(gs->gs_filename) {
365     g_free(gs->gs_filename);
366     gs->gs_filename = NULL;
367   }
368   if(gs->doc) {
369     psfree(gs->doc);
370     gs->doc = NULL;
371   }
372   if(gs->gs_filename_dsc) {
373     unlink(gs->gs_filename_dsc);
374     g_free(gs->gs_filename_dsc);
375     gs->gs_filename_dsc = NULL;
376   }
377   if(gs->gs_filename_unc) {
378     unlink(gs->gs_filename_unc);
379     g_free(gs->gs_filename_unc);
380     gs->gs_filename_unc = NULL;
381   }
382   gs->current_page = -1;
383   gs->loaded = FALSE;
384   gs->llx = 0;
385   gs->lly = 0;
386   gs->urx = 0;
387   gs->ury = 0;
388   set_up_page(gs);
389 }
390
391 static void
392 ps_document_finalize (GObject * object)
393 {
394         PSDocument *gs;
395
396         g_return_if_fail (object != NULL);
397         g_return_if_fail (GTK_IS_GS (object));
398
399         LOG ("Finalize")
400
401         gs = PS_DOCUMENT (object);
402
403         ps_document_cleanup (gs);
404         stop_interpreter (gs);
405
406         if(gs->input_buffer) {
407                 g_free(gs->input_buffer);
408                 gs->input_buffer = NULL;
409         }
410
411         (*G_OBJECT_CLASS(parent_class)->finalize) (object);
412 }
413
414 static void
415 send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close)
416 {
417   struct record_list *ps_new;
418
419   if(gs->interpreter_input < 0) {
420     g_critical("No pipe to gs: error in send_ps().");
421     return;
422   }
423
424   ps_new = (struct record_list *) g_malloc(sizeof(struct record_list));
425   ps_new->fp = gs->gs_psfile;
426   ps_new->begin = begin;
427   ps_new->len = len;
428   ps_new->seek_needed = TRUE;
429   ps_new->close = close;
430   ps_new->next = NULL;
431
432   if(gs->input_buffer == NULL) {
433     gs->input_buffer = g_malloc(MAX_BUFSIZE);
434   }
435
436   if(gs->ps_input == NULL) {
437     gs->input_buffer_ptr = gs->input_buffer;
438     gs->bytes_left = len;
439     gs->buffer_bytes_left = 0;
440     gs->ps_input = ps_new;
441     gs->interpreter_input_id =
442       gdk_input_add(gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
443   }
444   else {
445     struct record_list *p = gs->ps_input;
446     while(p->next != NULL) {
447       p = p->next;
448     }
449     p->next = ps_new;
450   }
451 }
452
453 static gint
454 ps_document_get_orientation(PSDocument * gs)
455 {
456   g_return_val_if_fail(gs != NULL, -1);
457   g_return_val_if_fail(GTK_IS_GS(gs), -1);
458
459   if(gs->doc) {
460     if(gs->structured_doc) {
461       if(gs->doc->pages[MAX(gs->current_page, 0)].orientation !=
462          GTK_GS_ORIENTATION_NONE)
463         gs->real_orientation =
464           gs->doc->pages[MAX(gs->current_page, 0)].orientation;
465       else
466         gs->real_orientation = gs->doc->default_page_orientation;
467     }
468
469     if(gs->real_orientation == GTK_GS_ORIENTATION_NONE)
470       gs->real_orientation = gs->doc->orientation;
471   }
472
473   if(gs->override_orientation ||
474      gs->real_orientation == GTK_GS_ORIENTATION_NONE)
475     return gs->fallback_orientation;
476   else
477     return gs->real_orientation;
478 }
479
480 static void
481 set_up_page(PSDocument * gs)
482 {
483   guint orientation;
484   char buf[1024];
485   //GdkColormap *colormap;
486   GdkGC *fill;
487   GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF };   /* pixel, r, g, b */
488   GdkColormap *colormap;
489
490   LOG ("Setup the page")
491
492 #ifdef HAVE_LOCALE_H
493   char *savelocale;
494 #endif
495
496   if (gs->pstarget == NULL)
497     return;
498
499   /* Do we have to check if the actual geometry changed? */
500
501   stop_interpreter(gs);
502
503   orientation = ps_document_get_orientation(gs);
504
505   if(compute_size(gs)) {
506     gdk_flush();
507
508     /* clear new pixmap (set to white) */
509     fill = gdk_gc_new(gs->pstarget);
510     if(fill) {
511       colormap = gdk_drawable_get_colormap(gs->pstarget);
512       gdk_color_alloc (colormap, &white);
513       gdk_gc_set_foreground(fill, &white);
514
515       if(gs->width > 0 && gs->height > 0) {
516         if(gs->bpixmap) {
517           gdk_drawable_unref(gs->bpixmap);
518           gs->bpixmap = NULL;
519         }
520
521         LOG ("Create our internal pixmap")
522         gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1);
523
524         gdk_draw_rectangle(gs->bpixmap, fill, TRUE,
525                            0, 0, gs->width, gs->height);
526       }
527       else {
528         gdk_draw_rectangle(gs->pstarget, fill, TRUE,
529                            0, 0, gs->width, gs->height);
530       }
531       gdk_gc_unref(fill);
532
533       gdk_flush();
534     }
535   }
536
537 #ifdef HAVE_LOCALE_H
538   /* gs needs floating point parameters with '.' as decimal point
539    * while some (european) locales use ',' instead, so we set the 
540    * locale for this snprintf to "C".
541    */
542   savelocale = setlocale(LC_NUMERIC, "C");
543 #endif
544
545   g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
546              0L,
547              orientation * 90,
548              gs->llx,
549              gs->lly,
550              gs->urx,
551              gs->ury,
552              gs->xdpi * gs->zoom_factor,
553              gs->ydpi * gs->zoom_factor,
554              gs->left_margin,
555              gs->bottom_margin, gs->right_margin, gs->top_margin);
556
557 #ifdef HAVE_LOCALE_H
558   setlocale(LC_NUMERIC, savelocale);
559 #endif
560   gdk_property_change(gs->pstarget,
561                       gs_class->gs_atom,
562                       gs_class->string_atom,
563                       8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
564   gdk_flush();
565 }
566
567 static void
568 close_pipe(int p[2])
569 {
570   if(p[0] != -1)
571     close(p[0]);
572   if(p[1] != -1)
573     close(p[1]);
574 }
575
576 static gboolean
577 is_interpreter_ready(PSDocument * gs)
578 {
579   return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
580 }
581
582 static void
583 interpreter_failed(PSDocument * gs)
584 {
585   stop_interpreter(gs);
586 }
587
588 static void
589 output(gpointer data, gint source, GdkInputCondition condition)
590 {
591   char buf[MAX_BUFSIZE + 1], *msg;
592   guint bytes = 0;
593   PSDocument *gs = PS_DOCUMENT(data);
594
595   if(source == gs->interpreter_output) {
596     bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
597     if(bytes == 0) {            /* EOF occurred */
598       close(gs->interpreter_output);
599       gs->interpreter_output = -1;
600       gdk_input_remove(gs->interpreter_output_id);
601       return;
602     }
603     else if(bytes == -1) {
604       /* trouble... */
605       interpreter_failed(gs);
606       return;
607     }
608     if(gs->interpreter_err == -1) {
609       stop_interpreter(gs);
610     }
611   }
612   else if(source == gs->interpreter_err) {
613     bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
614     if(bytes == 0) {            /* EOF occurred */
615       close(gs->interpreter_err);
616       gs->interpreter_err = -1;
617       gdk_input_remove(gs->interpreter_error_id);
618       return;
619     }
620     else if(bytes == -1) {
621       /* trouble... */
622       interpreter_failed(gs);
623       return;
624     }
625     if(gs->interpreter_output == -1) {
626       stop_interpreter(gs);
627     }
628   }
629   if(bytes > 0) {
630     buf[bytes] = '\0';
631     msg = g_strdup(buf);
632     ps_document_emit_error_msg (gs, msg);   
633   }
634 }
635
636 static void
637 input(gpointer data, gint source, GdkInputCondition condition)
638 {
639   PSDocument *gs = PS_DOCUMENT(data);
640   int bytes_written;
641   void (*oldsig) (int);
642   oldsig = signal(SIGPIPE, catchPipe);
643
644   do {
645     if(gs->buffer_bytes_left == 0) {
646       /* Get a new section if required */
647       if(gs->ps_input && gs->bytes_left == 0) {
648         struct record_list *ps_old = gs->ps_input;
649         gs->ps_input = ps_old->next;
650         if(ps_old->close && NULL != ps_old->fp)
651           fclose(ps_old->fp);
652         g_free((char *) ps_old);
653       }
654       /* Have to seek at the beginning of each section */
655       if(gs->ps_input && gs->ps_input->seek_needed) {
656         fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
657         gs->ps_input->seek_needed = FALSE;
658         gs->bytes_left = gs->ps_input->len;
659       }
660
661       if(gs->bytes_left > MAX_BUFSIZE) {
662         gs->buffer_bytes_left =
663           fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
664       }
665       else if(gs->bytes_left > 0) {
666         gs->buffer_bytes_left =
667           fread(gs->input_buffer,
668                 sizeof(char), gs->bytes_left, gs->ps_input->fp);
669       }
670       else {
671         gs->buffer_bytes_left = 0;
672       }
673       if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
674         interpreter_failed(gs); /* Error occurred */
675       }
676       gs->input_buffer_ptr = gs->input_buffer;
677       gs->bytes_left -= gs->buffer_bytes_left;
678     }
679
680     if(gs->buffer_bytes_left > 0) {
681       /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
682
683       bytes_written = write(gs->interpreter_input,
684                             gs->input_buffer_ptr, gs->buffer_bytes_left);
685
686       if(broken_pipe) {
687         ps_document_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
688         broken_pipe = FALSE;
689         interpreter_failed(gs);
690       }
691       else if(bytes_written == -1) {
692         if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
693           interpreter_failed(gs);   /* Something bad happened */
694         }
695       }
696       else {
697         gs->buffer_bytes_left -= bytes_written;
698         gs->input_buffer_ptr += bytes_written;
699       }
700     }
701   }
702   while(gs->ps_input && gs->buffer_bytes_left == 0);
703
704   signal(SIGPIPE, oldsig);
705
706   if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
707     if(gs->interpreter_input_id != 0) {
708       gdk_input_remove(gs->interpreter_input_id);
709       gs->interpreter_input_id = 0;
710     }
711   }
712 }
713
714 static int
715 start_interpreter(PSDocument * gs)
716 {
717   int std_in[2] = { -1, -1 };   /* pipe to interp stdin */
718   int std_out[2];               /* pipe from interp stdout */
719   int std_err[2];               /* pipe from interp stderr */
720
721   LOG ("Start the interpreter")
722
723 #define NUM_ARGS    100
724 #define NUM_GS_ARGS (NUM_ARGS - 20)
725 #define NUM_ALPHA_ARGS 10
726
727   char *argv[NUM_ARGS], *dir, *gv_env;
728   char **gs_args, **alpha_args = NULL;
729   int argc = 0, i;
730
731   if(!gs->gs_filename)
732     return 0;
733
734   stop_interpreter(gs);
735
736   if(gs->disable_start == TRUE)
737     return 0;
738
739   /* set up the args... */
740   gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS);
741   for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++)
742     argv[argc] = gs_args[i];
743
744   if(gs->antialiased) {
745     if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
746       alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
747     else
748       alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(),
749                               " ", NUM_ALPHA_ARGS);
750     for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++)
751       argv[argc] = alpha_args[i];
752   }
753   else
754     argv[argc++] = "-sDEVICE=x11";
755   argv[argc++] = "-dNOPAUSE";
756   argv[argc++] = "-dQUIET";
757   /* I assume we do _not_ want to change this... (: */
758   argv[argc++] = "-dSAFER";
759
760   /* set up the pipes */
761   if(gs->send_filename_to_gs) {
762     argv[argc++] = PS_DOCUMENT_GET_PS_FILE(gs);
763     argv[argc++] = "-c";
764     argv[argc++] = "quit";
765   }
766   else
767     argv[argc++] = "-";
768
769   argv[argc++] = NULL;
770
771   if(!gs->reading_from_pipe && !gs->send_filename_to_gs) {
772     if(pipe(std_in) == -1) {
773       g_critical("Unable to open pipe to Ghostscript.");
774       return -1;
775     }
776   }
777   if(pipe(std_out) == -1) {
778     close_pipe(std_in);
779     return -1;
780   }
781   if(pipe(std_err) == -1) {
782     close_pipe(std_in);
783     close_pipe(std_out);
784     return -1;
785   }
786
787   gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld",
788                            gdk_x11_drawable_get_xid(gs->pstarget),
789                            gdk_x11_drawable_get_xid(gs->bpixmap));
790
791   LOG ("Launching ghostview with env %s", gv_env)
792
793   gs->busy = TRUE;
794   gs->interpreter_pid = fork();
795   switch (gs->interpreter_pid) {
796   case -1:                     /* error */
797     close_pipe(std_in);
798     close_pipe(std_out);
799     close_pipe(std_err);
800     return -2;
801     break;
802   case 0:                      /* child */
803     close(std_out[0]);
804     dup2(std_out[1], 1);
805     close(std_out[1]);
806
807     close(std_err[0]);
808     dup2(std_err[1], 2);
809     close(std_err[1]);
810
811     if(!gs->reading_from_pipe) {
812       if(gs->send_filename_to_gs) {
813         int stdinfd;
814         /* just in case gs tries to read from stdin */
815         stdinfd = open("/dev/null", O_RDONLY);
816         if(stdinfd != 0) {
817           dup2(stdinfd, 0);
818           close(stdinfd);
819         }
820       }
821       else {
822         close(std_in[1]);
823         dup2(std_in[0], 0);
824         close(std_in[0]);
825       }
826     }
827
828     putenv(gv_env);
829
830     /* change to directory where the input file is. This helps
831      * with postscript-files which include other files using
832      * a relative path */
833     dir = g_path_get_dirname(gs->gs_filename);
834     chdir(dir);
835     g_free(dir);
836
837     execvp(argv[0], argv);
838
839     /* Notify error */
840     g_print("Unable to execute [%s]\n", argv[0]);
841     g_strfreev(gs_args);
842     g_free(gv_env);
843     if(alpha_args)
844       g_strfreev(alpha_args);
845     _exit(1);
846     break;
847   default:                     /* parent */
848     if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
849       int result;
850       close(std_in[0]);
851       /* use non-blocking IO for pipe to ghostscript */
852       result = fcntl(std_in[1], F_GETFL, 0);
853       fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
854       gs->interpreter_input = std_in[1];
855     }
856     else {
857       gs->interpreter_input = -1;
858     }
859     close(std_out[1]);
860     gs->interpreter_output = std_out[0];
861     close(std_err[1]);
862     gs->interpreter_err = std_err[0];
863     gs->interpreter_output_id =
864       gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
865     gs->interpreter_error_id =
866       gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
867     break;
868   }
869   return TRUE;
870 }
871
872 static void
873 stop_interpreter(PSDocument * gs)
874 {
875   if(gs->interpreter_pid > 0) {
876     int status = 0;
877     LOG ("Stop the interpreter")
878     kill(gs->interpreter_pid, SIGTERM);
879     while((wait(&status) == -1) && (errno == EINTR)) ;
880     gs->interpreter_pid = -1;
881     if(status == 1) {
882       ps_document_cleanup(gs);
883       gs->gs_status = _("Interpreter failed.");
884     }
885   }
886
887   if(gs->interpreter_input >= 0) {
888     close(gs->interpreter_input);
889     gs->interpreter_input = -1;
890     if(gs->interpreter_input_id != 0) {
891       gdk_input_remove(gs->interpreter_input_id);
892       gs->interpreter_input_id = 0;
893     }
894     while(gs->ps_input) {
895       struct record_list *ps_old = gs->ps_input;
896       gs->ps_input = gs->ps_input->next;
897       if(ps_old->close && NULL != ps_old->fp)
898         fclose(ps_old->fp);
899       g_free((char *) ps_old);
900     }
901   }
902
903   if(gs->interpreter_output >= 0) {
904     close(gs->interpreter_output);
905     gs->interpreter_output = -1;
906     if(gs->interpreter_output_id) {
907       gdk_input_remove(gs->interpreter_output_id);
908       gs->interpreter_output_id = 0;
909     }
910   }
911
912   if(gs->interpreter_err >= 0) {
913     close(gs->interpreter_err);
914     gs->interpreter_err = -1;
915     if(gs->interpreter_error_id) {
916       gdk_input_remove(gs->interpreter_error_id);
917       gs->interpreter_error_id = 0;
918     }
919   }
920
921   gs->busy = FALSE;
922 }
923
924 /* If file exists and is a regular file then return its length, else -1 */
925 static gint
926 file_length(const gchar * filename)
927 {
928   struct stat stat_rec;
929
930   if(filename && (stat(filename, &stat_rec) == 0)
931      && S_ISREG(stat_rec.st_mode))
932     return stat_rec.st_size;
933   else
934     return -1;
935 }
936
937 /* Test if file exists, is a regular file and its length is > 0 */
938 static gboolean
939 file_readable(const char *filename)
940 {
941   return (file_length(filename) > 0);
942 }
943
944 /*
945  * Decompress gs->gs_filename if necessary
946  * Set gs->filename_unc to the name of the uncompressed file or NULL.
947  * Error reporting via signal 'interpreter_message'
948  * Return name of input file to use or NULL on error..
949  */
950 static gchar *
951 check_filecompressed(PSDocument * gs)
952 {
953   FILE *file;
954   gchar buf[1024];
955   gchar *filename, *filename_unc, *filename_err, *cmdline;
956   const gchar *cmd;
957   int fd;
958
959   cmd = NULL;
960
961   if((file = fopen(gs->gs_filename, "r"))
962      && (fread(buf, sizeof(gchar), 3, file) == 3)) {
963     if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
964       /* file is gzipped or compressed */
965       cmd = gtk_gs_defaults_get_ungzip_cmd();
966     }
967     else if(strncmp(buf, "BZh", 3) == 0) {
968       /* file is compressed with bzip2 */
969       cmd = gtk_gs_defaults_get_unbzip2_cmd();
970     }
971   }
972   if(NULL != file)
973     fclose(file);
974
975   if(!cmd)
976     return gs->gs_filename;
977
978   /* do the decompression */
979   filename = g_shell_quote(gs->gs_filename);
980   filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
981   if((fd = mkstemp(filename_unc)) < 0) {
982     g_free(filename_unc);
983     g_free(filename);
984     return NULL;
985   }
986   close(fd);
987   filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
988   if((fd = mkstemp(filename_err)) < 0) {
989     g_free(filename_err);
990     g_free(filename_unc);
991     g_free(filename);
992     return NULL;
993   }
994   close(fd);
995   cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
996                             filename, filename_unc, filename_err);
997   if((system(cmdline) == 0)
998      && file_readable(filename_unc)
999      && (file_length(filename_err) == 0)) {
1000     /* sucessfully uncompressed file */
1001     gs->gs_filename_unc = filename_unc;
1002   }
1003   else {
1004     /* report error */
1005     g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
1006                gs->gs_filename);
1007     ps_document_emit_error_msg(gs, buf);
1008     if(file_length(filename_err) > 0) {
1009       FILE *err;
1010       if((err = fopen(filename_err, "r"))) {
1011         /* print file to message window */
1012         while(fgets(buf, 1024, err))
1013           ps_document_emit_error_msg(gs, buf);
1014         fclose(err);
1015       }
1016     }
1017     unlink(filename_unc);
1018     g_free(filename_unc);
1019     filename_unc = NULL;
1020   }
1021   unlink(filename_err);
1022   g_free(filename_err);
1023   g_free(cmdline);
1024   g_free(filename);
1025   return filename_unc;
1026 }
1027
1028 /*
1029  * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
1030  * pdf file if necessary.
1031  * Set gs->filename_dsc to the name of the dsc file or NULL.
1032  * Error reporting via signal 'interpreter_message'.
1033  */
1034 static gchar *
1035 check_pdf(PSDocument * gs)
1036 {
1037   FILE *file;
1038   gchar buf[1024], *filename;
1039   int fd;
1040
1041   /* use uncompressed file as input if necessary */
1042   filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
1043
1044   if((file = fopen(filename, "r"))
1045      && (fread(buf, sizeof(char), 5, file) == 5)
1046      && (strncmp(buf, "%PDF-", 5) == 0)) {
1047     /* we found a PDF file */
1048     gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
1049     filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1050     if((fd = mkstemp(filename_dsc)) < 0) {
1051       return NULL;
1052     }
1053     close(fd);
1054     filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1055     if((fd = mkstemp(filename_err)) < 0) {
1056       g_free(filename_dsc);
1057       return NULL;
1058     }
1059     close(fd);
1060     fname = g_shell_quote(filename);
1061     cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1062     g_free(fname);
1063     /* this command (sometimes?) prints error messages to stdout! */
1064     cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1065     g_free(cmd);
1066
1067     if((system(cmdline) == 0) && file_readable(filename_dsc)) {
1068
1069       /* success */
1070       filename = gs->gs_filename_dsc = filename_dsc;
1071
1072       if(file_length(filename_err) > 0) {
1073         gchar *err_msg = " ";
1074         GtkWidget *dialog;
1075         FILE *err;
1076         GdkColor color;
1077
1078         if((err = fopen(filename_err, "r"))) {
1079
1080           /* print the content of the file to a message box */
1081           while(fgets(buf, 1024, err))
1082             err_msg = g_strconcat(err_msg, buf, NULL);
1083
1084           /* FIXME The dialog is not yet set to modal, difficult to 
1085            * get the parent of the dialog box here 
1086            */
1087
1088           dialog = gtk_message_dialog_new(NULL,
1089                                           GTK_DIALOG_MODAL,
1090                                           GTK_MESSAGE_WARNING,
1091                                           GTK_BUTTONS_OK,
1092                                           ("There was an error while scaning the file: %s \n%s"),
1093                                           gs->gs_filename, err_msg);
1094
1095           gdk_color_parse("white", &color);
1096           gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1097
1098           g_signal_connect(G_OBJECT(dialog), "response",
1099                            G_CALLBACK(gtk_widget_destroy), NULL);
1100
1101           gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1102           gtk_widget_show(dialog);
1103           g_free(err_msg);
1104         }
1105       }
1106
1107     }
1108     else {
1109       /* report error */
1110       g_snprintf(buf, 1024,
1111                  _("Error while converting pdf file %s:\n"), filename);
1112       ps_document_emit_error_msg(gs, buf);
1113
1114       if(file_length(filename_err) > 0) {
1115         FILE *err;
1116         if((err = fopen(filename_err, "r"))) {
1117           /* print file to message window */
1118           while(fgets(buf, 1024, err))
1119             ps_document_emit_error_msg(gs, buf);
1120         }
1121       }
1122       unlink(filename_dsc);
1123       g_free(filename_dsc);
1124       filename = NULL;
1125     }
1126     unlink(filename_err);
1127     g_free(filename_err);
1128     g_free(cmdline);
1129   }
1130   if(NULL != file)
1131     fclose(file);
1132   return filename;
1133 }
1134
1135 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1136 /* never mind this patch: a properly working X server should take care of
1137    calculating the proper values. */
1138 static float
1139 compute_xdpi(void)
1140 {
1141 #   ifndef HAVE_XINERAMA
1142   return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1143 #   else
1144   Display *dpy;
1145   dpy = (Display *) GDK_DISPLAY();
1146   if(XineramaIsActive(dpy)) {
1147     int num_heads;
1148     XineramaScreenInfo *head_info;
1149     head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1150     /* fake it with dimensions of the first head for now */
1151     return 25.4 * head_info[0].width / gdk_screen_width_mm();
1152   }
1153   else {
1154     return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1155   }
1156 #   endif
1157   /* HAVE_XINERAMA */
1158 }
1159
1160 static float
1161 compute_ydpi(void)
1162 {
1163 #   ifndef HAVE_XINERAMA
1164   return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1165 #   else
1166   Display *dpy;
1167   dpy = (Display *) GDK_DISPLAY();
1168   if(XineramaIsActive(dpy)) {
1169     int num_heads;
1170     XineramaScreenInfo *head_info;
1171     head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1172     /* fake it with dimensions of the first head for now */
1173     return 25.4 * head_info[0].height / gdk_screen_height_mm();
1174   }
1175   else {
1176     return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1177   }
1178 #   endif
1179   /* HAVE_XINERAMA */
1180 }
1181 #else
1182 static float
1183 compute_xdpi(void)
1184 {
1185   return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1186 }
1187
1188 static float
1189 compute_ydpi(void)
1190 {
1191   return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1192 }
1193 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1194
1195 /* Compute new size of window, sets xdpi and ydpi if necessary.
1196  * returns True if new window size is different */
1197 static gboolean
1198 compute_size(PSDocument * gs)
1199 {
1200   guint new_width = 1;
1201   guint new_height = 1;
1202   gboolean change = FALSE;
1203   gint orientation;
1204
1205   /* width and height can be changed, calculate window size according */
1206   /* to xpdi and ydpi */
1207   orientation = ps_document_get_orientation(gs);
1208
1209   switch (orientation) {
1210   case GTK_GS_ORIENTATION_PORTRAIT:
1211   case GTK_GS_ORIENTATION_UPSIDEDOWN:
1212     new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1213     new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1214     break;
1215   case GTK_GS_ORIENTATION_LANDSCAPE:
1216   case GTK_GS_ORIENTATION_SEASCAPE:
1217     new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1218     new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1219     break;
1220   }
1221
1222   change = (new_width != gs->width * gs->zoom_factor)
1223     || (new_height != gs->height * gs->zoom_factor);
1224   gs->width = (gint) (new_width * gs->zoom_factor);
1225   gs->height = (gint) (new_height * gs->zoom_factor);
1226
1227   return (change);
1228 }
1229
1230 static gint
1231 ps_document_enable_interpreter(PSDocument * gs)
1232 {
1233   g_return_val_if_fail(gs != NULL, FALSE);
1234   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1235
1236   if(!gs->gs_filename)
1237     return 0;
1238
1239   gs->disable_start = FALSE;
1240   
1241   return start_interpreter(gs);
1242 }
1243
1244 /* publicly accessible functions */
1245
1246 GType
1247 ps_document_get_type(void)
1248 {
1249   static GType gs_type = 0;
1250   if(!gs_type) {
1251     GTypeInfo gs_info = {
1252       sizeof(PSDocumentClass),
1253       (GBaseInitFunc) NULL,
1254       (GBaseFinalizeFunc) NULL,
1255       (GClassInitFunc) ps_document_class_init,
1256       (GClassFinalizeFunc) NULL,
1257       NULL,                     /* class_data */
1258       sizeof(PSDocument),
1259       0,                        /* n_preallocs */
1260       (GInstanceInitFunc) ps_document_init
1261     };
1262
1263     static const GInterfaceInfo document_info =
1264     {
1265         (GInterfaceInitFunc) ps_document_document_iface_init,
1266         NULL,
1267         NULL
1268     };
1269
1270     gs_type = g_type_register_static(G_TYPE_OBJECT,
1271                                      "PSDocument", &gs_info, 0);
1272
1273     g_type_add_interface_static (gs_type,
1274                                  EV_TYPE_DOCUMENT,
1275                                  &document_info);
1276   }
1277   return gs_type;
1278
1279
1280 }
1281
1282 /*
1283  * Show error message -> send signal "interpreter_message"
1284  */
1285 static void
1286 ps_document_emit_error_msg(PSDocument * gs, const gchar * msg)
1287 {
1288   gdk_pointer_ungrab(GDK_CURRENT_TIME);
1289   if(strstr(msg, "Error:")) {
1290     gs->gs_status = _("File is not a valid PostScript document.");
1291     ps_document_cleanup(gs);
1292   }
1293 }
1294
1295 static gboolean
1296 document_load(PSDocument * gs, const gchar * fname)
1297 {
1298   g_return_val_if_fail(gs != NULL, FALSE);
1299   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1300
1301   LOG ("Load the document")
1302
1303   /* clean up previous document */
1304   ps_document_cleanup(gs);
1305
1306   if(fname == NULL) {
1307     gs->gs_status = "";
1308     return FALSE;
1309   }
1310
1311   /* prepare this document */
1312
1313   /* default values: no dsc information available  */
1314   gs->structured_doc = FALSE;
1315   gs->send_filename_to_gs = TRUE;
1316   gs->current_page = -2;
1317   gs->loaded = FALSE;
1318   if(*fname == '/') {
1319     /* an absolute path */
1320     gs->gs_filename = g_strdup(fname);
1321   }
1322   else {
1323     /* path relative to our cwd: make it absolute */
1324     gchar *cwd = g_get_current_dir();
1325     gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1326     g_free(cwd);
1327   }
1328
1329   if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1330     gs->send_filename_to_gs = FALSE;
1331   }
1332   else {
1333     /*
1334      * We need to make sure that the file is loadable/exists!
1335      * otherwise we want to exit without loading new stuff...
1336      */
1337     gchar *filename = NULL;
1338
1339     if(!file_readable(fname)) {
1340       gchar buf[1024];
1341       g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1342       ps_document_emit_error_msg(gs, buf);
1343       gs->gs_status = _("File is not readable.");
1344     }
1345     else {
1346       filename = check_filecompressed(gs);
1347       if(filename)
1348         filename = check_pdf(gs);
1349     }
1350
1351     if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1352       ps_document_cleanup(gs);
1353       return FALSE;
1354     }
1355
1356     /* we grab the vital statistics!!! */
1357     gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1358
1359     g_object_notify (G_OBJECT (gs), "title");
1360
1361     if(gs->doc == NULL) {
1362       /* File does not seem to be a Postscript one */
1363       gchar buf[1024];
1364       g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1365       ps_document_emit_error_msg(gs, buf);
1366       ps_document_cleanup(gs);
1367       gs->gs_status = _("The file is not a PostScript document.");
1368       return FALSE;
1369     }
1370
1371     if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1372        (gs->doc->epsf && gs->doc->numpages > 1)) {
1373       gs->structured_doc = TRUE;
1374       gs->send_filename_to_gs = FALSE;
1375     }
1376
1377     /* We have to set up the orientation of the document */
1378
1379
1380     /* orientation can only be portrait, and landscape or none.
1381        This is the document default. A document can have
1382        pages in landscape and some in portrait */
1383     if(gs->override_orientation) {
1384       /* If the orientation should be override... 
1385          then gs->orientation has already the correct
1386          value (it was set when the widget was created */
1387       /* So do nothing */
1388
1389     }
1390     else {
1391       /* Otherwise, set the proper orientation for the doc */
1392       gs->real_orientation = gs->doc->orientation;
1393     }
1394   }
1395   ps_document_set_page_size(gs, -1, gs->current_page);
1396   gs->loaded = TRUE;
1397
1398   gs->gs_status = _("Document loaded.");
1399
1400   return gs->loaded;
1401 }
1402
1403
1404 static gboolean
1405 ps_document_next_page(PSDocument * gs)
1406 {
1407   XEvent event;
1408
1409   LOG ("Make ghostscript render next page")
1410
1411   g_return_val_if_fail(gs != NULL, FALSE);
1412   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1413
1414   if(gs->interpreter_pid == 0) {    /* no interpreter active */
1415     return FALSE;
1416   }
1417
1418   if(gs->busy) {                /* interpreter is busy */
1419     return FALSE;
1420   }
1421
1422   gs->busy = TRUE;
1423
1424   event.xclient.type = ClientMessage;
1425   event.xclient.display = gdk_display;
1426   event.xclient.window = gs->message_window;
1427   event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1428   event.xclient.format = 32;
1429
1430   gdk_error_trap_push();
1431   XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1432   gdk_flush();
1433   gdk_error_trap_pop();
1434
1435   return TRUE;
1436 }
1437
1438 static gboolean
1439 ps_document_goto_page(PSDocument * gs, gint page)
1440 {
1441   g_return_val_if_fail(gs != NULL, FALSE);
1442   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1443
1444   LOG ("Go to page %d", page)
1445
1446   if(!gs->gs_filename) {
1447     return FALSE;
1448   }
1449
1450   /* range checking... */
1451   if(page < 0)
1452     page = 0;
1453
1454   if(gs->structured_doc && gs->doc) {
1455
1456     LOG ("It's a structured document, let's send one page to gs")
1457
1458     if(page >= gs->doc->numpages)
1459       page = gs->doc->numpages - 1;
1460
1461     if(page == gs->current_page && !gs->changed)
1462       return TRUE;
1463
1464     gs->current_page = page;
1465
1466     if(gs->doc->pages[page].orientation != NONE &&
1467        !gs->override_orientation &&
1468        gs->doc->pages[page].orientation != gs->real_orientation) {
1469       gs->real_orientation = gs->doc->pages[page].orientation;
1470       gs->changed = TRUE;
1471     }
1472
1473     ps_document_set_page_size(gs, -1, page);
1474
1475     gs->changed = FALSE;
1476
1477     if(is_interpreter_ready(gs)) {
1478       ps_document_next_page(gs);
1479     }
1480     else {
1481       ps_document_enable_interpreter(gs);
1482       send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1483       send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1484     }
1485
1486     send_ps(gs, gs->doc->pages[gs->current_page].begin,
1487             gs->doc->pages[gs->current_page].len, FALSE);
1488   }
1489   else {
1490     /* Unstructured document */
1491     /* In the case of non structured documents,
1492        GS read the PS from the  actual file (via command
1493        line. Hence, ggv only send a signal next page.
1494        If ghostview is not running it is usually because
1495        the last page of the file was displayed. In that
1496        case, ggv restarts GS again and the first page is displayed.
1497      */
1498
1499     LOG ("It's an unstructured document, gs will just read the file")
1500
1501     if(page == gs->current_page && !gs->changed)
1502       return TRUE;
1503
1504     ps_document_set_page_size(gs, -1, page);
1505
1506     if(!is_interpreter_ready(gs))
1507       ps_document_enable_interpreter(gs);
1508
1509     gs->current_page = page;
1510
1511     ps_document_next_page(gs);
1512   }
1513   return TRUE;
1514 }
1515
1516 /*
1517  * set pagesize sets the size from
1518  * if new_pagesize is -1, then it is set to either
1519  *  a) the default settings of pageid, if they exist, or if pageid != -1.
1520  *  b) the default setting of the document, if it exists.
1521  *  c) the default setting of the widget.
1522  * otherwise, the new_pagesize is used as the pagesize
1523  */
1524 static gboolean
1525 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1526 {
1527   gint new_llx = 0;
1528   gint new_lly = 0;
1529   gint new_urx = 0;
1530   gint new_ury = 0;
1531   GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1532
1533   LOG ("Set the page size")
1534
1535   g_return_val_if_fail(gs != NULL, FALSE);
1536   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1537
1538   if(new_pagesize == -1) {
1539     if(gs->default_size > 0)
1540       new_pagesize = gs->default_size;
1541     if(!gs->override_size && gs->doc) {
1542       /* If we have a document:
1543          We use -- the page size (if specified)
1544          or the doc. size (if specified)
1545          or the page bbox (if specified)
1546          or the bounding box
1547        */
1548       if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1549          (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1550         new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1551       }
1552       else if(gs->doc->default_page_size != NULL) {
1553         new_pagesize = gs->doc->default_page_size - gs->doc->size;
1554       }
1555       else if((pageid >= 0) &&
1556               (gs->doc->numpages > pageid) &&
1557               (gs->doc->pages) &&
1558               (gs->doc->pages[pageid].boundingbox[URX] >
1559                gs->doc->pages[pageid].boundingbox[LLX]) &&
1560               (gs->doc->pages[pageid].boundingbox[URY] >
1561                gs->doc->pages[pageid].boundingbox[LLY])) {
1562         new_pagesize = -1;
1563       }
1564       else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1565               (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1566         new_pagesize = -1;
1567       }
1568     }
1569   }
1570
1571   /* Compute bounding box */
1572   if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) {    /* epsf or bbox */
1573     if((pageid >= 0) &&
1574        (gs->doc->pages) &&
1575        (gs->doc->pages[pageid].boundingbox[URX] >
1576         gs->doc->pages[pageid].boundingbox[LLX])
1577        && (gs->doc->pages[pageid].boundingbox[URY] >
1578            gs->doc->pages[pageid].boundingbox[LLY])) {
1579       /* use page bbox */
1580       new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1581       new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1582       new_urx = gs->doc->pages[pageid].boundingbox[URX];
1583       new_ury = gs->doc->pages[pageid].boundingbox[URY];
1584     }
1585     else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1586             (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1587       /* use doc bbox */
1588       new_llx = gs->doc->boundingbox[LLX];
1589       new_lly = gs->doc->boundingbox[LLY];
1590       new_urx = gs->doc->boundingbox[URX];
1591       new_ury = gs->doc->boundingbox[URY];
1592     }
1593   }
1594   else {
1595     if(new_pagesize < 0)
1596       new_pagesize = gs->default_size;
1597     new_llx = new_lly = 0;
1598     if(gs->doc && !gs->override_size && gs->doc->size &&
1599        (new_pagesize < gs->doc->numsizes)) {
1600       new_urx = gs->doc->size[new_pagesize].width;
1601       new_ury = gs->doc->size[new_pagesize].height;
1602     }
1603     else {
1604       new_urx = papersizes[new_pagesize].width;
1605       new_ury = papersizes[new_pagesize].height;
1606     }
1607   }
1608
1609   if(new_urx <= new_llx)
1610     new_urx = papersizes[12].width;
1611   if(new_ury <= new_lly)
1612     new_ury = papersizes[12].height;
1613
1614   /* If bounding box changed, setup for new size. */
1615   /* ps_document_disable_interpreter (gs); */
1616   if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1617      (new_urx != gs->urx) || (new_ury != gs->ury)) {
1618     gs->llx = new_llx;
1619     gs->lly = new_lly;
1620     gs->urx = new_urx;
1621     gs->ury = new_ury;
1622     gs->changed = TRUE;
1623   }
1624
1625   if(gs->changed) {
1626     set_up_page(gs);
1627     return TRUE;
1628   }
1629
1630   return FALSE;
1631 }
1632
1633 static gfloat
1634 ps_document_zoom_to_fit(PSDocument * gs, gboolean fit_width)
1635 {
1636   gint new_y;
1637   gfloat new_zoom;
1638   guint avail_w, avail_h;
1639
1640   g_return_val_if_fail(gs != NULL, 0.0);
1641   g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1642
1643   avail_w = (gs->avail_w > 0) ? gs->avail_w : gs->width;
1644   avail_h = (gs->avail_h > 0) ? gs->avail_h : gs->height;
1645
1646   new_zoom = ((gfloat) avail_w) / ((gfloat) gs->width) * gs->zoom_factor;
1647   if(!fit_width) {
1648     new_y = new_zoom * ((gfloat) gs->height) / gs->zoom_factor;
1649     if(new_y > avail_h)
1650       new_zoom = ((gfloat) avail_h) / ((gfloat) gs->height) * gs->zoom_factor;
1651   }
1652
1653   return new_zoom;
1654 }
1655
1656 static void
1657 ps_document_set_zoom(PSDocument * gs, gfloat zoom)
1658 {
1659   g_return_if_fail(gs != NULL);
1660   g_return_if_fail(GTK_IS_GS(gs));
1661
1662   switch (gs->zoom_mode) {
1663   case GTK_GS_ZOOM_FIT_WIDTH:
1664     zoom = ps_document_zoom_to_fit(gs, TRUE);
1665     break;
1666   case GTK_GS_ZOOM_FIT_PAGE:
1667     zoom = ps_document_zoom_to_fit(gs, FALSE);
1668     break;
1669   case GTK_GS_ZOOM_ABSOLUTE:
1670   default:
1671     break;
1672   }
1673
1674   if(fabs(gs->zoom_factor - zoom) > 0.001) {
1675     gs->zoom_factor = zoom;
1676     set_up_page(gs);
1677     gs->changed = TRUE;
1678   }
1679
1680   ps_document_goto_page(gs, gs->current_page);
1681 }
1682
1683 static gboolean
1684 ps_document_load (EvDocument  *document,
1685                   const char  *uri,
1686                   GError     **error)
1687 {
1688         gboolean result;
1689         char *filename;
1690
1691         filename = g_filename_from_uri (uri, NULL, error);
1692         if (!filename)
1693                 return FALSE;
1694
1695         result = document_load (PS_DOCUMENT (document), filename);
1696
1697         g_free (filename);
1698
1699         return result;
1700 }
1701
1702 static gboolean
1703 ps_document_save (EvDocument  *document,
1704                   const char  *uri,
1705                   GError     **error)
1706 {
1707         g_warning ("ps_document_save not implemented"); /* FIXME */
1708         return TRUE;
1709 }
1710
1711 static int
1712 ps_document_get_n_pages (EvDocument  *document)
1713 {
1714         PSDocument *ps = PS_DOCUMENT (document);
1715
1716         g_return_val_if_fail (ps != NULL, -1);
1717
1718         if (!ps->gs_filename || !ps->doc) {
1719                 return -1;
1720         }
1721
1722         return ps->structured_doc ? ps->doc->numpages : 1;
1723 }
1724
1725 static void
1726 ps_document_set_page (EvDocument  *document,
1727                        int          page)
1728 {
1729         ps_document_goto_page (PS_DOCUMENT (document), page);
1730 }
1731
1732 static int
1733 ps_document_get_page (EvDocument  *document)
1734 {
1735         PSDocument *ps = PS_DOCUMENT (document);
1736
1737         g_return_val_if_fail (ps != NULL, -1);
1738
1739         return ps->current_page;
1740 }
1741
1742 static gboolean
1743 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
1744 {
1745         PSDocument *gs = (PSDocument *) data;
1746
1747         if(event->type != GDK_CLIENT_EVENT)
1748                 return FALSE;
1749
1750         gs->message_window = event->client.data.l[0];
1751
1752         if (event->client.message_type == gs_class->page_atom) {
1753                 LOG ("GS rendered the document")
1754                 gs->busy = FALSE;
1755                 ev_document_changed (EV_DOCUMENT (gs));
1756         }
1757
1758         return TRUE;
1759 }
1760
1761 static void
1762 ps_document_set_target (EvDocument  *document,
1763                         GdkDrawable *target)
1764 {
1765         PSDocument *gs = PS_DOCUMENT (document);
1766         GtkWidget *widget;
1767         gpointer data;
1768
1769         gs->pstarget = target;
1770
1771         if (gs->pstarget) {
1772                 gdk_window_get_user_data (gs->pstarget, &data);
1773                 g_return_if_fail (GTK_IS_WIDGET (data));
1774
1775                 widget = GTK_WIDGET (data);
1776                 g_signal_connect (widget, "event",
1777                                   G_CALLBACK (ps_document_widget_event),
1778                                   document);
1779         }
1780
1781         ps_document_goto_page (gs, gs->current_page);
1782 }
1783
1784 static void
1785 ps_document_set_scale (EvDocument  *document,
1786                         double       scale)
1787 {
1788         ps_document_set_zoom (PS_DOCUMENT (document), scale);
1789 }
1790
1791 static void
1792 ps_document_set_page_offset (EvDocument  *document,
1793                               int          x,
1794                               int          y)
1795 {
1796         PSDocument *gs = PS_DOCUMENT (document);
1797
1798         gs->page_x_offset = x;
1799         gs->page_y_offset = y;
1800 }
1801
1802 static void
1803 ps_document_get_page_size (EvDocument   *document,
1804                            int           page,
1805                            int          *width,
1806                            int          *height)
1807 {
1808         /* Post script documents never vary in size */
1809
1810         PSDocument *gs = PS_DOCUMENT (document);
1811
1812         if (width) {
1813                 *width = gs->width;
1814         }
1815
1816         if (height) {
1817                 *height = gs->height;
1818         }
1819 }
1820
1821 static void
1822 ps_document_render (EvDocument  *document,
1823                     int          clip_x,
1824                     int          clip_y,
1825                     int          clip_width,
1826                     int          clip_height)
1827 {
1828         PSDocument *gs = PS_DOCUMENT (document);
1829         GdkRectangle page;
1830         GdkRectangle draw;
1831         GdkGC *gc;
1832
1833         if (gs->pstarget == NULL ||
1834             gs->bpixmap == NULL) {
1835                 return;
1836         }
1837
1838         page.x = gs->page_x_offset;
1839         page.y = gs->page_y_offset;
1840         page.width = gs->width;
1841         page.height = gs->height;
1842
1843         draw.x = clip_x;
1844         draw.y = clip_y;
1845         draw.width = clip_width;
1846         draw.height = clip_height;
1847
1848         gc = gdk_gc_new (gs->pstarget);
1849
1850         gdk_draw_drawable (gs->pstarget, gc,
1851                            gs->bpixmap,
1852                            draw.x - page.x, draw.y - page.y,
1853                            draw.x, draw.y,
1854                            draw.width, draw.height);
1855
1856         LOG ("Copy the internal pixmap: %d %d %d %d %d %d",
1857              draw.x - page.x, draw.y - page.y,
1858              draw.x, draw.y, draw.width, draw.height)
1859
1860         g_object_unref (gc);
1861 }
1862
1863 static char *
1864 ps_document_get_text (EvDocument *document, GdkRectangle *rect)
1865 {
1866         g_warning ("ps_document_get_text not implemented"); /* FIXME ? */
1867         return NULL;
1868 }
1869
1870 static EvLink *
1871 ps_document_get_link (EvDocument *document,
1872                       int         x,
1873                       int         y)
1874 {
1875         return NULL;
1876 }
1877
1878 static void
1879 ps_document_document_iface_init (EvDocumentIface *iface)
1880 {
1881         iface->load = ps_document_load;
1882         iface->save = ps_document_save;
1883         iface->get_text = ps_document_get_text;
1884         iface->get_link = ps_document_get_link;
1885         iface->get_n_pages = ps_document_get_n_pages;
1886         iface->set_page = ps_document_set_page;
1887         iface->get_page = ps_document_get_page;
1888         iface->set_scale = ps_document_set_scale;
1889         iface->set_target = ps_document_set_target;
1890         iface->set_page_offset = ps_document_set_page_offset;
1891         iface->get_page_size = ps_document_get_page_size;
1892         iface->render = ps_document_render;
1893 }