]> www.fi.muni.cz Git - evince.git/blob - ps/ps-document.c
View status message support, use it to show the links. Needs work...
[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     ps_document_set_page_size(gs, -1, page);
1502
1503     if(!is_interpreter_ready(gs))
1504       ps_document_enable_interpreter(gs);
1505
1506     gs->current_page = page;
1507
1508     ps_document_next_page(gs);
1509   }
1510   return TRUE;
1511 }
1512
1513 /*
1514  * set pagesize sets the size from
1515  * if new_pagesize is -1, then it is set to either
1516  *  a) the default settings of pageid, if they exist, or if pageid != -1.
1517  *  b) the default setting of the document, if it exists.
1518  *  c) the default setting of the widget.
1519  * otherwise, the new_pagesize is used as the pagesize
1520  */
1521 static gboolean
1522 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1523 {
1524   gint new_llx = 0;
1525   gint new_lly = 0;
1526   gint new_urx = 0;
1527   gint new_ury = 0;
1528   GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1529
1530   g_return_val_if_fail(gs != NULL, FALSE);
1531   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1532
1533   if(new_pagesize == -1) {
1534     if(gs->default_size > 0)
1535       new_pagesize = gs->default_size;
1536     if(!gs->override_size && gs->doc) {
1537       /* If we have a document:
1538          We use -- the page size (if specified)
1539          or the doc. size (if specified)
1540          or the page bbox (if specified)
1541          or the bounding box
1542        */
1543       if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1544          (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1545         new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1546       }
1547       else if(gs->doc->default_page_size != NULL) {
1548         new_pagesize = gs->doc->default_page_size - gs->doc->size;
1549       }
1550       else if((pageid >= 0) &&
1551               (gs->doc->numpages > pageid) &&
1552               (gs->doc->pages) &&
1553               (gs->doc->pages[pageid].boundingbox[URX] >
1554                gs->doc->pages[pageid].boundingbox[LLX]) &&
1555               (gs->doc->pages[pageid].boundingbox[URY] >
1556                gs->doc->pages[pageid].boundingbox[LLY])) {
1557         new_pagesize = -1;
1558       }
1559       else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1560               (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1561         new_pagesize = -1;
1562       }
1563     }
1564   }
1565
1566   /* Compute bounding box */
1567   if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) {    /* epsf or bbox */
1568     if((pageid >= 0) &&
1569        (gs->doc->pages) &&
1570        (gs->doc->pages[pageid].boundingbox[URX] >
1571         gs->doc->pages[pageid].boundingbox[LLX])
1572        && (gs->doc->pages[pageid].boundingbox[URY] >
1573            gs->doc->pages[pageid].boundingbox[LLY])) {
1574       /* use page bbox */
1575       new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1576       new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1577       new_urx = gs->doc->pages[pageid].boundingbox[URX];
1578       new_ury = gs->doc->pages[pageid].boundingbox[URY];
1579     }
1580     else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1581             (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1582       /* use doc bbox */
1583       new_llx = gs->doc->boundingbox[LLX];
1584       new_lly = gs->doc->boundingbox[LLY];
1585       new_urx = gs->doc->boundingbox[URX];
1586       new_ury = gs->doc->boundingbox[URY];
1587     }
1588   }
1589   else {
1590     if(new_pagesize < 0)
1591       new_pagesize = gs->default_size;
1592     new_llx = new_lly = 0;
1593     if(gs->doc && !gs->override_size && gs->doc->size &&
1594        (new_pagesize < gs->doc->numsizes)) {
1595       new_urx = gs->doc->size[new_pagesize].width;
1596       new_ury = gs->doc->size[new_pagesize].height;
1597     }
1598     else {
1599       new_urx = papersizes[new_pagesize].width;
1600       new_ury = papersizes[new_pagesize].height;
1601     }
1602   }
1603
1604   if(new_urx <= new_llx)
1605     new_urx = papersizes[12].width;
1606   if(new_ury <= new_lly)
1607     new_ury = papersizes[12].height;
1608
1609   /* If bounding box changed, setup for new size. */
1610   /* ps_document_disable_interpreter (gs); */
1611   if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1612      (new_urx != gs->urx) || (new_ury != gs->ury)) {
1613     gs->llx = new_llx;
1614     gs->lly = new_lly;
1615     gs->urx = new_urx;
1616     gs->ury = new_ury;
1617     gs->changed = TRUE;
1618   }
1619
1620   if(gs->changed) {
1621     set_up_page(gs);
1622     return TRUE;
1623   }
1624
1625   return FALSE;
1626 }
1627
1628 static gfloat
1629 ps_document_zoom_to_fit(PSDocument * gs, gboolean fit_width)
1630 {
1631   gint new_y;
1632   gfloat new_zoom;
1633   guint avail_w, avail_h;
1634
1635   g_return_val_if_fail(gs != NULL, 0.0);
1636   g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1637
1638   avail_w = (gs->avail_w > 0) ? gs->avail_w : gs->width;
1639   avail_h = (gs->avail_h > 0) ? gs->avail_h : gs->height;
1640
1641   new_zoom = ((gfloat) avail_w) / ((gfloat) gs->width) * gs->zoom_factor;
1642   if(!fit_width) {
1643     new_y = new_zoom * ((gfloat) gs->height) / gs->zoom_factor;
1644     if(new_y > avail_h)
1645       new_zoom = ((gfloat) avail_h) / ((gfloat) gs->height) * gs->zoom_factor;
1646   }
1647
1648   return new_zoom;
1649 }
1650
1651 static void
1652 ps_document_set_zoom(PSDocument * gs, gfloat zoom)
1653 {
1654   g_return_if_fail(gs != NULL);
1655   g_return_if_fail(GTK_IS_GS(gs));
1656
1657   switch (gs->zoom_mode) {
1658   case GTK_GS_ZOOM_FIT_WIDTH:
1659     zoom = ps_document_zoom_to_fit(gs, TRUE);
1660     break;
1661   case GTK_GS_ZOOM_FIT_PAGE:
1662     zoom = ps_document_zoom_to_fit(gs, FALSE);
1663     break;
1664   case GTK_GS_ZOOM_ABSOLUTE:
1665   default:
1666     break;
1667   }
1668
1669   if(fabs(gs->zoom_factor - zoom) > 0.001) {
1670     gs->zoom_factor = zoom;
1671     set_up_page(gs);
1672     gs->changed = TRUE;
1673   }
1674
1675   ps_document_goto_page(gs, gs->current_page);
1676 }
1677
1678 static gboolean
1679 ps_document_load (EvDocument  *document,
1680                   const char  *uri,
1681                   GError     **error)
1682 {
1683         gboolean result;
1684         char *filename;
1685
1686         filename = g_filename_from_uri (uri, NULL, error);
1687         if (!filename)
1688                 return FALSE;
1689
1690         result = document_load (PS_DOCUMENT (document), filename);
1691
1692         g_free (filename);
1693
1694         return result;
1695 }
1696
1697 static int
1698 ps_document_get_n_pages (EvDocument  *document)
1699 {
1700         return ps_document_get_page_count (PS_DOCUMENT (document));
1701 }
1702
1703 static void
1704 ps_document_set_page (EvDocument  *document,
1705                        int          page)
1706 {
1707         ps_document_goto_page (PS_DOCUMENT (document), page);
1708 }
1709
1710 static int
1711 ps_document_get_page (EvDocument  *document)
1712 {
1713         return ps_document_get_current_page (PS_DOCUMENT (document));
1714 }
1715
1716 static gboolean
1717 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
1718 {
1719         PSDocument *gs = (PSDocument *) data;
1720
1721         if(event->type != GDK_CLIENT_EVENT)
1722                 return FALSE;
1723
1724         if (event->client.message_type == gs_class->page_atom) {
1725                 gs->busy = FALSE;
1726                 ev_document_changed (EV_DOCUMENT (gs));
1727         }
1728
1729         return TRUE;
1730 }
1731
1732 static void
1733 ps_document_set_target (EvDocument  *document,
1734                         GdkDrawable *target)
1735 {
1736         PSDocument *gs = PS_DOCUMENT (document);
1737         GtkWidget *widget;
1738         gpointer data;
1739
1740         gs->pstarget = target;
1741
1742         if (gs->pstarget) {
1743                 gdk_window_get_user_data (gs->pstarget, &data);
1744                 g_return_if_fail (GTK_IS_WIDGET (data));
1745
1746                 widget = GTK_WIDGET (data);
1747                 g_signal_connect (widget, "event",
1748                                   G_CALLBACK (ps_document_widget_event),
1749                                   document);
1750         }
1751
1752         ps_document_goto_page (gs, gs->current_page);
1753 }
1754
1755 static void
1756 ps_document_set_scale (EvDocument  *document,
1757                         double       scale)
1758 {
1759         ps_document_set_zoom (PS_DOCUMENT (document), scale);
1760 }
1761
1762 static void
1763 ps_document_set_page_offset (EvDocument  *document,
1764                               int          x,
1765                               int          y)
1766 {
1767 }
1768
1769 static void
1770 ps_document_get_page_size (EvDocument   *document,
1771                             int          *width,
1772                             int          *height)
1773 {
1774         PSDocument *gs = PS_DOCUMENT (document);
1775
1776         if (width) {
1777                 *width = gs->width;
1778         }
1779
1780         if (height) {
1781                 *height = gs->height;
1782         }
1783 }
1784
1785 static void
1786 ps_document_render (EvDocument  *document,
1787                     int          clip_x,
1788                     int          clip_y,
1789                     int          clip_width,
1790                     int          clip_height)
1791 {
1792         PSDocument *gs = PS_DOCUMENT (document);
1793         GdkGC *gc;
1794
1795         if (gs->pstarget == NULL ||
1796             gs->bpixmap == NULL) {
1797                 return;
1798         }
1799
1800         gc = gdk_gc_new (gs->pstarget);
1801
1802         gdk_draw_drawable (gs->pstarget, gc,
1803                            gs->bpixmap,
1804                            clip_x, clip_y,
1805                            clip_x, clip_y,
1806                            clip_width, clip_height);
1807
1808         g_object_unref (gc);
1809 }
1810
1811 static EvLink *
1812 ps_document_get_link (EvDocument *document,
1813                       int         x,
1814                       int         y)
1815 {
1816         return NULL;
1817 }
1818
1819 static void
1820 ps_document_document_iface_init (EvDocumentIface *iface)
1821 {
1822         iface->load = ps_document_load;
1823         iface->get_link = ps_document_get_link;
1824         iface->get_n_pages = ps_document_get_n_pages;
1825         iface->set_page = ps_document_set_page;
1826         iface->get_page = ps_document_get_page;
1827         iface->set_scale = ps_document_set_scale;
1828         iface->set_target = ps_document_set_target;
1829         iface->set_page_offset = ps_document_set_page_offset;
1830         iface->get_page_size = ps_document_get_page_size;
1831         iface->render = ps_document_render;
1832 }