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