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