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