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