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