]> www.fi.muni.cz Git - evince.git/blob - ps/gtkgs.c
a548942c5bde7142c8c2c71c4a0e1273934c5a6c
[evince.git] / ps / gtkgs.c
1 /* Ghostscript widget for GTK/GNOME
2  * 
3  * Copyright (C) 1998 - 2005 the Free Software Foundation
4  * 
5  * Authors: Jonathan Blandford, Jaka Mocnik
6  * 
7  * Based on code by: Federico Mena (Quartic), Szekeres Istvan (Pista)
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24  
25 /*
26 Ghostview interface to ghostscript
27
28 When the GHOSTVIEW environment variable is set, ghostscript draws on
29 an existing drawable rather than creating its own window.  Ghostscript
30 can be directed to draw on either a window or a pixmap.
31
32 Drawing on a Window
33
34 The GHOSTVIEW environment variable contains the window id of the target
35 window.  The window id is an integer.  Ghostscript will use the attributes
36 of the window to obtain the width, height, colormap, screen, and visual of
37 the window. The remainder of the information is gotten from the GHOSTVIEW
38 property on that window.
39
40
41 Drawing on a Pixmap
42
43 The GHOSTVIEW environment variable contains a window id and a pixmap id.
44 They are integers separated by white space.  Ghostscript will use the
45 attributes of the window to obtain the colormap, screen, and visual to use.
46 The width and height will be obtained from the pixmap. The remainder of the
47 information, is gotten from the GHOSTVIEW property on the window.  In this
48 case, the property is deleted when read.
49
50 The GHOSTVIEW environment variable
51
52 parameters:     window-id [pixmap-id]
53
54 scanf format:   "%d %d"
55
56 explanation of parameters:
57
58         window-id: tells ghostscript where to
59                     - read the GHOSTVIEW property
60                     - send events
61                     If pixmap-id is not present,
62                     ghostscript will draw on this window.
63
64         pixmap-id: If present, tells ghostscript that a pixmap will be used
65                     as the final destination for drawing.  The window will
66                     not be touched for drawing purposes.
67
68 The GHOSTVIEW property
69
70 type:   STRING
71
72 parameters:
73
74     bpixmap orient llx lly urx ury xdpi ydpi [left bottom top right]
75
76 scanf format: "%d %d %d %d %d %d %f %f %d %d %d %d"
77
78 explanation of parameters:
79
80         bpixmap: pixmap id of the backing pixmap for the window.  If no
81                 pixmap is to be used, this parameter should be zero.  This
82                 parameter must be zero when drawing on a pixmap.
83
84         orient: orientation of the page.  The number represents clockwise
85                 rotation of the paper in degrees.  Permitted values are
86                 0, 90, 180, 270.
87
88         llx, lly, urx, ury: Bounding box of the drawable.  The bounding box
89                 is specified in PostScript points in default user coordinates.
90
91         xdpi, ydpi: Resolution of window.  (This can be derived from the
92                 other parameters, but not without roundoff error.  These
93                 values are included to avoid this error.)
94
95         left, bottom, top, right: (optional)
96                 Margins around the window.  The margins extend the imageable
97                 area beyond the boundaries of the window.  This is primarily
98                 used for popup zoom windows.  I have encountered several
99                 instances of PostScript programs that position themselves
100                 with respect to the imageable area.  The margins are specified
101                 in PostScript points.  If omitted, the margins are assumed to
102                 be 0.
103
104 Events from ghostscript
105
106 If the final destination is a pixmap, the client will get a property notify
107 event when ghostscript reads the GHOSTVIEW property causing it to be deleted.
108
109 Ghostscript sends events to the window where it read the GHOSTVIEW property.
110 These events are of type ClientMessage.  The message_type is set to
111 either PAGE or DONE.  The first long data value gives the window to be used
112 to send replies to ghostscript.  The second long data value gives the primary
113 drawable.  If rendering to a pixmap, it is the primary drawable.  If rendering
114 to a window, the backing pixmap is the primary drawable.  If no backing pixmap
115 is employed, then the window is the primary drawable.  This field is necessary
116 to distinguish multiple ghostscripts rendering to separate pixmaps where the
117 GHOSTVIEW property was placed on the same window.
118
119 The PAGE message indicates that a "page" has completed.  Ghostscript will
120 wait until it receives a ClientMessage whose message_type is NEXT before
121 continuing.
122
123 The DONE message indicates that ghostscript has finished processing.
124
125 */
126
127 #include "config.h"
128 #include <string.h>
129 #include <stdlib.h>
130 #include <signal.h>
131 #include <gtk/gtk.h>
132 #include <gtk/gtkobject.h>
133 #include <gdk/gdkprivate.h>
134 #include <gdk/gdkx.h>
135 #include <gdk/gdk.h>
136 #ifdef  HAVE_XINERAMA
137 #   include <gdk/gdkx.h>
138 #   include <X11/extensions/Xinerama.h>
139 #endif /* HAVE_XINERAMA */
140 #include <X11/Intrinsic.h>
141 #include <unistd.h>
142 #include <fcntl.h>
143 #include <stdlib.h>
144 #include <errno.h>
145 #include <sys/stat.h>
146 #include <sys/types.h>
147 #include <sys/wait.h>
148 #include <stdio.h>
149 #include <math.h>
150
151 #include "ev-document.h"
152 #include "gtkgs.h"
153 #include "ggvutils.h"
154 #include "ps.h"
155 #include "gsdefaults.h"
156
157 #ifdef HAVE_LOCALE_H
158 #   include <locale.h>
159 #endif
160
161 /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
162 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
163 #   define O_NONBLOCK O_NDELAY
164 #endif
165
166 #define GTK_GS_WATCH_INTERVAL 1000
167 #define GTK_GS_WATCH_TIMEOUT  2
168
169 #define MAX_BUFSIZE 1024
170
171 enum { INTERPRETER_MESSAGE, INTERPRETER_ERROR, LAST_SIGNAL };
172
173 static gboolean broken_pipe = FALSE;
174
175 static void
176 catchPipe(int i)
177 {
178   broken_pipe = True;
179 }
180
181 /* Forward declarations */
182 static void gtk_gs_init(GtkGS * gs);
183 static void gtk_gs_class_init(GtkGSClass * klass);
184 static void gtk_gs_value_adjustment_changed(GtkAdjustment * adjustment,
185                                             gpointer data);
186 static void gtk_gs_interpreter_message(GtkGS * gs, gchar * msg,
187                                        gpointer user_data);
188 static void gtk_gs_emit_error_msg(GtkGS * gs, const gchar * msg);
189 static void gtk_gs_set_adjustments(GtkGS * gs, GtkAdjustment * hadj,
190                                    GtkAdjustment * vadj);
191 static void gtk_gs_finalize(GObject * object);
192 static void send_ps(GtkGS * gs, long begin, unsigned int len, gboolean close);
193 static void set_up_page(GtkGS * gs);
194 static void close_pipe(int p[2]);
195 static void interpreter_failed(GtkGS * gs);
196 static float compute_xdpi(void);
197 static float compute_ydpi(void);
198 static gboolean compute_size(GtkGS * gs);
199 static void output(gpointer data, gint source, GdkInputCondition condition);
200 static void input(gpointer data, gint source, GdkInputCondition condition);
201 static void stop_interpreter(GtkGS * gs);
202 static gint start_interpreter(GtkGS * gs);
203 gboolean computeSize(void);
204 static void ps_document_document_iface_init (EvDocumentIface *iface);
205
206 static GObjectClass *parent_class = NULL;
207
208 static GtkGSClass *gs_class = NULL;
209
210 static gint gtk_gs_signals[LAST_SIGNAL] = { 0 };
211
212 /* Static, private functions */
213
214 static void
215 ggv_marshaller_VOID__POINTER(GClosure * closure,
216                              GValue * return_value,
217                              guint n_param_values,
218                              const GValue * param_values,
219                              gpointer invocation_hint, gpointer marshal_data)
220 {
221   typedef void (*GMarshalFunc_VOID__POINTER) (gpointer data1,
222                                               gpointer arg_1, gpointer data2);
223   register GMarshalFunc_VOID__POINTER callback;
224   register GCClosure *cc = (GCClosure *) closure;
225   register gpointer data1, data2;
226
227   g_return_if_fail(n_param_values == 2);
228
229   if(G_CCLOSURE_SWAP_DATA(closure)) {
230     data1 = closure->data;
231     data2 = g_value_peek_pointer(param_values + 0);
232   }
233   else {
234     data1 = g_value_peek_pointer(param_values + 0);
235     data2 = closure->data;
236   }
237   callback =
238     (GMarshalFunc_VOID__POINTER) (marshal_data ? marshal_data : cc->callback);
239
240   callback(data1, g_value_get_pointer(param_values + 1), data2);
241 }
242
243 static void
244 ggv_marshaller_VOID__INT(GClosure * closure,
245                          GValue * return_value,
246                          guint n_param_values,
247                          const GValue * param_values,
248                          gpointer invocation_hint, gpointer marshal_data)
249 {
250   typedef void (*GMarshalFunc_VOID__INT) (gpointer data1,
251                                           gint arg_1, gpointer data2);
252   register GMarshalFunc_VOID__INT callback;
253   register GCClosure *cc = (GCClosure *) closure;
254   register gpointer data1, data2;
255
256   g_return_if_fail(n_param_values == 2);
257
258   if(G_CCLOSURE_SWAP_DATA(closure)) {
259     data1 = closure->data;
260     data2 = g_value_peek_pointer(param_values + 0);
261   }
262   else {
263     data1 = g_value_peek_pointer(param_values + 0);
264     data2 = closure->data;
265   }
266   callback =
267     (GMarshalFunc_VOID__INT) (marshal_data ? marshal_data : cc->callback);
268
269   callback(data1, g_value_get_int(param_values + 1), data2);
270 }
271
272 static void
273 gtk_gs_init(GtkGS * gs)
274 {
275   gs->bpixmap = NULL;
276   gs->use_bpixmap = TRUE;
277
278   gs->current_page = -2;
279   gs->disable_start = FALSE;
280   gs->interpreter_pid = -1;
281
282   gs->width = -1;
283   gs->height = -1;
284   gs->busy = FALSE;
285   gs->changed = FALSE;
286   gs->gs_scanstyle = 0;
287   gs->gs_filename = 0;
288   gs->gs_filename_dsc = 0;
289   gs->gs_filename_unc = 0;
290
291   broken_pipe = FALSE;
292
293   gs->structured_doc = FALSE;
294   gs->reading_from_pipe = FALSE;
295   gs->send_filename_to_gs = FALSE;
296
297   gs->doc = NULL;
298   gs->loaded = FALSE;
299
300   gs->interpreter_input = -1;
301   gs->interpreter_output = -1;
302   gs->interpreter_err = -1;
303   gs->interpreter_input_id = 0;
304   gs->interpreter_output_id = 0;
305   gs->interpreter_error_id = 0;
306
307   gs->ps_input = NULL;
308   gs->input_buffer = NULL;
309   gs->input_buffer_ptr = NULL;
310   gs->bytes_left = 0;
311   gs->buffer_bytes_left = 0;
312
313   gs->llx = 0;
314   gs->lly = 0;
315   gs->urx = 0;
316   gs->ury = 0;
317   gs->xdpi = compute_xdpi();
318   gs->ydpi = compute_ydpi();
319
320   gs->left_margin = 0;
321   gs->top_margin = 0;
322   gs->right_margin = 0;
323   gs->bottom_margin = 0;
324
325   /* Set user defined defaults */
326   gs->override_orientation = gtk_gs_defaults_get_override_orientation();
327   gs->fallback_orientation = gtk_gs_defaults_get_orientation();
328   gs->zoom_factor = gtk_gs_defaults_get_zoom_factor();
329   gs->default_size = gtk_gs_defaults_get_size();
330   gs->antialiased = gtk_gs_defaults_get_antialiased();
331   gs->override_size = gtk_gs_defaults_get_override_size();
332   gs->respect_eof = gtk_gs_defaults_get_respect_eof();
333   gs->show_scroll_rect = gtk_gs_defaults_get_show_scroll_rect();
334   gs->scroll_step = gtk_gs_defaults_get_scroll_step();
335   gs->zoom_mode = gtk_gs_defaults_get_zoom_mode();
336
337   gs->scroll_start_x = gs->scroll_start_y = -1;
338
339   gs->gs_status = _("No document loaded.");
340 }
341
342 static void
343 gtk_gs_class_init(GtkGSClass * klass)
344 {
345   GObjectClass *object_class;
346
347   object_class = (GObjectClass *) klass;
348   parent_class = gtk_type_class(gtk_widget_get_type());
349   gs_class = klass;
350
351   gtk_gs_signals[INTERPRETER_MESSAGE] = g_signal_new("interpreter_message",
352                                                      G_TYPE_FROM_CLASS
353                                                      (object_class),
354                                                      G_SIGNAL_RUN_LAST,
355                                                      G_STRUCT_OFFSET
356                                                      (GtkGSClass,
357                                                       interpreter_message),
358                                                      NULL, NULL,
359                                                      ggv_marshaller_VOID__POINTER,
360                                                      G_TYPE_NONE, 1,
361                                                      G_TYPE_POINTER);
362   gtk_gs_signals[INTERPRETER_ERROR] =
363     g_signal_new("interpreter_error", G_TYPE_FROM_CLASS(object_class),
364                  G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GtkGSClass,
365                                                     interpreter_error),
366                  NULL, NULL, ggv_marshaller_VOID__INT, G_TYPE_NONE, 1,
367                  G_TYPE_INT);
368
369   object_class->finalize = gtk_gs_finalize;
370
371   /* Create atoms */
372   klass->gs_atom = gdk_atom_intern("GHOSTVIEW", FALSE);
373   klass->gs_colors_atom = gdk_atom_intern("GHOSTVIEW_COLORS", FALSE);
374   klass->next_atom = gdk_atom_intern("NEXT", FALSE);
375   klass->page_atom = gdk_atom_intern("PAGE", FALSE);
376   klass->done_atom = gdk_atom_intern("DONE", FALSE);
377   klass->string_atom = gdk_atom_intern("STRING", FALSE);
378
379   /* a default handler for "interpreter_message" signal */
380   klass->interpreter_message = gtk_gs_interpreter_message;
381   /* supply a scrollable interface */
382   klass->set_scroll_adjustments = gtk_gs_set_adjustments;
383
384   gtk_gs_defaults_load();
385 }
386
387 /* Clean all memory and temporal files */
388 static void
389 gtk_gs_cleanup(GtkGS * gs)
390 {
391   g_return_if_fail(gs != NULL);
392   g_return_if_fail(GTK_IS_GS(gs));
393
394   stop_interpreter(gs);
395
396   if(gs->gs_psfile) {
397     fclose(gs->gs_psfile);
398     gs->gs_psfile = NULL;
399   }
400   if(gs->gs_filename) {
401     g_free(gs->gs_filename);
402     gs->gs_filename = NULL;
403   }
404   if(gs->doc) {
405     psfree(gs->doc);
406     gs->doc = NULL;
407   }
408   if(gs->gs_filename_dsc) {
409     unlink(gs->gs_filename_dsc);
410     g_free(gs->gs_filename_dsc);
411     gs->gs_filename_dsc = NULL;
412   }
413   if(gs->gs_filename_unc) {
414     unlink(gs->gs_filename_unc);
415     g_free(gs->gs_filename_unc);
416     gs->gs_filename_unc = NULL;
417   }
418   if(gs->pstarget && gdk_window_is_visible(gs->pstarget))
419     gdk_window_hide(gs->pstarget);
420   gs->current_page = -1;
421   gs->loaded = FALSE;
422   gs->llx = 0;
423   gs->lly = 0;
424   gs->urx = 0;
425   gs->ury = 0;
426   set_up_page(gs);
427 }
428
429 /* free message as it was allocated in output() */
430 static void
431 gtk_gs_interpreter_message(GtkGS * gs, gchar * msg, gpointer user_data)
432 {
433   gdk_pointer_ungrab(GDK_CURRENT_TIME);
434   if(strstr(msg, "Error:")) {
435     gs->gs_status = _("File is not a valid PostScript document.");
436     gtk_gs_cleanup(gs);
437     g_signal_emit_by_name(G_OBJECT(gs), "interpreter_error", 1, NULL);
438   }
439   g_free(msg);
440 }
441
442 static void
443 gtk_gs_finalize(GObject * object)
444 {
445   GtkGS *gs;
446
447   g_return_if_fail(object != NULL);
448   g_return_if_fail(GTK_IS_GS(object));
449
450   gs = GTK_GS(object);
451
452   gtk_gs_cleanup(gs);
453
454   if(gs->input_buffer) {
455     g_free(gs->input_buffer);
456     gs->input_buffer = NULL;
457   }
458   if(gs->hadj) {
459     g_signal_handlers_disconnect_matched(G_OBJECT(gs->hadj),
460                                          G_SIGNAL_MATCH_DATA,
461                                          0, 0, NULL, NULL, gs);
462     gtk_object_unref(GTK_OBJECT(gs->hadj));
463     gs->hadj = NULL;
464   }
465   if(gs->vadj) {
466     g_signal_handlers_disconnect_matched(G_OBJECT(gs->vadj),
467                                          G_SIGNAL_MATCH_DATA,
468                                          0, 0, NULL, NULL, gs);
469     gtk_object_unref(GTK_OBJECT(gs->vadj));
470     gs->vadj = NULL;
471   }
472
473   (*G_OBJECT_CLASS(parent_class)->finalize) (object);
474 }
475
476 static void
477 gtk_gs_value_adjustment_changed(GtkAdjustment * adjustment, gpointer data)
478 {
479 #if 0
480   GtkGS *gs;
481   gint x, y, width, height, depth;
482   gint newx, newy;
483
484   g_return_if_fail(adjustment != NULL);
485   g_return_if_fail(data != NULL);
486   gs = GTK_GS(data);
487   if(gs->bpixmap == NULL)
488     return;
489
490 #if 0
491   g_print("Adjustment %c: val = %f, page = %f, upper = %f, lower = %f\n",
492           (adjustment == gs->hadj) ? 'H' : 'V',
493           adjustment->value, adjustment->page_size,
494           adjustment->upper, adjustment->lower);
495 #endif
496
497   gdk_window_get_geometry(gs->pstarget, &x, &y, &width, &height, &depth);
498   if(gs->width <= gs->widget.allocation.width)
499     newx = (gs->widget.allocation.width - gs->width) / 2;
500   else
501     newx = -gs->hadj->value * gs->width;
502   if(gs->height <= gs->widget.allocation.height)
503     newy = (gs->widget.allocation.height - gs->height) / 2;
504   else
505     newy = -gs->vadj->value * gs->height;
506
507   gdk_window_move(gs->pstarget, newx, newy);
508 #endif
509 }
510
511 void
512 gtk_gs_set_center(GtkGS * gs, gfloat hval, gfloat vval)
513 {
514   if(hval <= gs->hadj->upper - gs->hadj->page_size / 2 &&
515      hval >= gs->hadj->lower + gs->hadj->page_size / 2)
516     gtk_adjustment_set_value(gs->hadj, hval);
517   if(vval <= gs->vadj->upper - gs->vadj->page_size / 2 &&
518      vval >= gs->vadj->lower + gs->vadj->page_size / 2)
519     gtk_adjustment_set_value(gs->vadj, vval);
520 }
521
522 static void
523 send_ps(GtkGS * gs, long begin, unsigned int len, gboolean close)
524 {
525   struct record_list *ps_new;
526
527   if(gs->interpreter_input < 0) {
528     g_critical("No pipe to gs: error in send_ps().");
529     return;
530   }
531
532   ps_new = (struct record_list *) g_malloc(sizeof(struct record_list));
533   ps_new->fp = gs->gs_psfile;
534   ps_new->begin = begin;
535   ps_new->len = len;
536   ps_new->seek_needed = TRUE;
537   ps_new->close = close;
538   ps_new->next = NULL;
539
540   if(gs->input_buffer == NULL) {
541     gs->input_buffer = g_malloc(MAX_BUFSIZE);
542   }
543
544   if(gs->ps_input == NULL) {
545     gs->input_buffer_ptr = gs->input_buffer;
546     gs->bytes_left = len;
547     gs->buffer_bytes_left = 0;
548     gs->ps_input = ps_new;
549     gs->interpreter_input_id =
550       gdk_input_add(gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
551   }
552   else {
553     struct record_list *p = gs->ps_input;
554     while(p->next != NULL) {
555       p = p->next;
556     }
557     p->next = ps_new;
558   }
559 }
560
561 static void
562 set_up_page(GtkGS * gs)
563      /* 
564       * This is used to prepare the widget internally for
565       * a new document. It sets gs->pstarget to the
566       * correct size and position, and updates the 
567       * adjustments appropriately.
568       *
569       * It is not meant to be used every time a specific page
570       * is selected.
571       *
572       * NOTE: It expects the widget is realized.
573       */
574 {
575   guint orientation;
576   char buf[1024];
577   GdkPixmap *pprivate;
578   GdkColormap *colormap;
579   GdkGC *fill;
580   GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF };   /* pixel, r, g, b */
581
582 #ifdef HAVE_LOCALE_H
583   char *savelocale;
584 #endif
585
586   if(!GTK_WIDGET_REALIZED(gs))
587     return;
588
589   /* Do we have to check if the actual geometry changed? */
590
591   stop_interpreter(gs);
592
593   orientation = gtk_gs_get_orientation(gs);
594
595   if(compute_size(gs)) {
596     gdk_flush();
597
598     /* clear new pixmap (set to white) */
599     fill = gdk_gc_new(gs->pstarget);
600     if(fill) {
601       colormap = gtk_widget_get_colormap(GTK_WIDGET(gs));
602       gdk_color_alloc(colormap, &white);
603       gdk_gc_set_foreground(fill, &white);
604
605       if(gs->use_bpixmap && gs->width > 0 && gs->height > 0) {
606         if(gs->bpixmap) {
607           gdk_drawable_unref(gs->bpixmap);
608           gs->bpixmap = NULL;
609         }
610
611         gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1);
612
613         gdk_draw_rectangle(gs->bpixmap, fill, TRUE,
614                            0, 0, gs->width, gs->height);
615
616         gdk_window_set_back_pixmap(gs->pstarget, gs->bpixmap, FALSE);
617       }
618       else {
619         gdk_draw_rectangle(gs->pstarget, fill, TRUE,
620                            0, 0, gs->width, gs->height);
621       }
622       gdk_gc_unref(fill);
623
624       gdk_window_resize(gs->pstarget, gs->width, gs->height);
625
626       gdk_flush();
627     }
628   }
629
630 #ifdef HAVE_LOCALE_H
631   /* gs needs floating point parameters with '.' as decimal point
632    * while some (european) locales use ',' instead, so we set the 
633    * locale for this snprintf to "C".
634    */
635   savelocale = setlocale(LC_NUMERIC, "C");
636 #endif
637   pprivate = (GdkPixmap *) gs->bpixmap;
638
639   g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
640              pprivate ? gdk_x11_drawable_get_xid(pprivate) : 0L,
641              orientation * 90,
642              gs->llx,
643              gs->lly,
644              gs->urx,
645              gs->ury,
646              gs->xdpi * gs->zoom_factor,
647              gs->ydpi * gs->zoom_factor,
648              gs->left_margin,
649              gs->bottom_margin, gs->right_margin, gs->top_margin);
650
651 #ifdef HAVE_LOCALE_H
652   setlocale(LC_NUMERIC, savelocale);
653 #endif
654   gdk_property_change(gs->pstarget,
655                       gs_class->gs_atom,
656                       gs_class->string_atom,
657                       8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
658   gdk_flush();
659 }
660
661 static void
662 close_pipe(int p[2])
663 {
664   if(p[0] != -1)
665     close(p[0]);
666   if(p[1] != -1)
667     close(p[1]);
668 }
669
670 static gboolean
671 is_interpreter_ready(GtkGS * gs)
672 {
673   return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
674 }
675
676 static void
677 interpreter_failed(GtkGS * gs)
678 {
679   stop_interpreter(gs);
680 }
681
682 static void
683 output(gpointer data, gint source, GdkInputCondition condition)
684 {
685   char buf[MAX_BUFSIZE + 1], *msg;
686   guint bytes = 0;
687   GtkGS *gs = GTK_GS(data);
688
689   if(source == gs->interpreter_output) {
690     bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
691     if(bytes == 0) {            /* EOF occurred */
692       close(gs->interpreter_output);
693       gs->interpreter_output = -1;
694       gdk_input_remove(gs->interpreter_output_id);
695       return;
696     }
697     else if(bytes == -1) {
698       /* trouble... */
699       interpreter_failed(gs);
700       return;
701     }
702     if(gs->interpreter_err == -1) {
703       stop_interpreter(gs);
704     }
705   }
706   else if(source == gs->interpreter_err) {
707     bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
708     if(bytes == 0) {            /* EOF occurred */
709       close(gs->interpreter_err);
710       gs->interpreter_err = -1;
711       gdk_input_remove(gs->interpreter_error_id);
712       return;
713     }
714     else if(bytes == -1) {
715       /* trouble... */
716       interpreter_failed(gs);
717       return;
718     }
719     if(gs->interpreter_output == -1) {
720       stop_interpreter(gs);
721     }
722   }
723   if(bytes > 0) {
724     buf[bytes] = '\0';
725     msg = g_strdup(buf);
726     gtk_signal_emit(GTK_OBJECT(gs), gtk_gs_signals[INTERPRETER_MESSAGE], msg);
727   }
728 }
729
730 static void
731 input(gpointer data, gint source, GdkInputCondition condition)
732 {
733   GtkGS *gs = GTK_GS(data);
734   int bytes_written;
735   void (*oldsig) (int);
736   oldsig = signal(SIGPIPE, catchPipe);
737
738   do {
739     if(gs->buffer_bytes_left == 0) {
740       /* Get a new section if required */
741       if(gs->ps_input && gs->bytes_left == 0) {
742         struct record_list *ps_old = gs->ps_input;
743         gs->ps_input = ps_old->next;
744         if(ps_old->close && NULL != ps_old->fp)
745           fclose(ps_old->fp);
746         g_free((char *) ps_old);
747       }
748       /* Have to seek at the beginning of each section */
749       if(gs->ps_input && gs->ps_input->seek_needed) {
750         fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
751         gs->ps_input->seek_needed = FALSE;
752         gs->bytes_left = gs->ps_input->len;
753       }
754
755       if(gs->bytes_left > MAX_BUFSIZE) {
756         gs->buffer_bytes_left =
757           fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
758       }
759       else if(gs->bytes_left > 0) {
760         gs->buffer_bytes_left =
761           fread(gs->input_buffer,
762                 sizeof(char), gs->bytes_left, gs->ps_input->fp);
763       }
764       else {
765         gs->buffer_bytes_left = 0;
766       }
767       if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
768         interpreter_failed(gs); /* Error occurred */
769       }
770       gs->input_buffer_ptr = gs->input_buffer;
771       gs->bytes_left -= gs->buffer_bytes_left;
772     }
773
774     if(gs->buffer_bytes_left > 0) {
775       /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
776
777       bytes_written = write(gs->interpreter_input,
778                             gs->input_buffer_ptr, gs->buffer_bytes_left);
779
780       if(broken_pipe) {
781         gtk_gs_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
782         broken_pipe = FALSE;
783         interpreter_failed(gs);
784       }
785       else if(bytes_written == -1) {
786         if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
787           interpreter_failed(gs);   /* Something bad happened */
788         }
789       }
790       else {
791         gs->buffer_bytes_left -= bytes_written;
792         gs->input_buffer_ptr += bytes_written;
793       }
794     }
795   }
796   while(gs->ps_input && gs->buffer_bytes_left == 0);
797
798   signal(SIGPIPE, oldsig);
799
800   if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
801     if(gs->interpreter_input_id != 0) {
802       gdk_input_remove(gs->interpreter_input_id);
803       gs->interpreter_input_id = 0;
804     }
805   }
806 }
807
808 static int
809 start_interpreter(GtkGS * gs)
810 {
811   int std_in[2] = { -1, -1 };   /* pipe to interp stdin */
812   int std_out[2];               /* pipe from interp stdout */
813   int std_err[2];               /* pipe from interp stderr */
814
815 #define NUM_ARGS    100
816 #define NUM_GS_ARGS (NUM_ARGS - 20)
817 #define NUM_ALPHA_ARGS 10
818
819   char *argv[NUM_ARGS], *dir, *gv_env;
820   char **gs_args, **alpha_args = NULL;
821   int argc = 0, i;
822
823   if(!gs->gs_filename)
824     return 0;
825
826   stop_interpreter(gs);
827
828   if(gs->disable_start == TRUE)
829     return 0;
830
831   /* set up the args... */
832   gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS);
833   for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++)
834     argv[argc] = gs_args[i];
835
836   if(gs->antialiased) {
837     if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
838       alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
839     else
840       alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(),
841                               " ", NUM_ALPHA_ARGS);
842     for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++)
843       argv[argc] = alpha_args[i];
844   }
845   else
846     argv[argc++] = "-sDEVICE=x11";
847   argv[argc++] = "-dNOPAUSE";
848   argv[argc++] = "-dQUIET";
849   /* I assume we do _not_ want to change this... (: */
850   argv[argc++] = "-dSAFER";
851
852   /* set up the pipes */
853   if(gs->send_filename_to_gs) {
854     argv[argc++] = GTK_GS_GET_PS_FILE(gs);
855     argv[argc++] = "-c";
856     argv[argc++] = "quit";
857   }
858   else
859     argv[argc++] = "-";
860
861   argv[argc++] = NULL;
862
863   if(!gs->reading_from_pipe && !gs->send_filename_to_gs) {
864     if(pipe(std_in) == -1) {
865       g_critical("Unable to open pipe to Ghostscript.");
866       return -1;
867     }
868   }
869   if(pipe(std_out) == -1) {
870     close_pipe(std_in);
871     return -1;
872   }
873   if(pipe(std_err) == -1) {
874     close_pipe(std_in);
875     close_pipe(std_out);
876     return -1;
877   }
878
879   gs->busy = TRUE;
880   gs->interpreter_pid = fork();
881   switch (gs->interpreter_pid) {
882   case -1:                     /* error */
883     close_pipe(std_in);
884     close_pipe(std_out);
885     close_pipe(std_err);
886     return -2;
887     break;
888   case 0:                      /* child */
889     close(std_out[0]);
890     dup2(std_out[1], 1);
891     close(std_out[1]);
892
893     close(std_err[0]);
894     dup2(std_err[1], 2);
895     close(std_err[1]);
896
897     if(!gs->reading_from_pipe) {
898       if(gs->send_filename_to_gs) {
899         int stdinfd;
900         /* just in case gs tries to read from stdin */
901         stdinfd = open("/dev/null", O_RDONLY);
902         if(stdinfd != 0) {
903           dup2(stdinfd, 0);
904           close(stdinfd);
905         }
906       }
907       else {
908         close(std_in[1]);
909         dup2(std_in[0], 0);
910         close(std_in[0]);
911       }
912     }
913
914     gv_env = g_strdup_printf("GHOSTVIEW=%ld",
915                              gdk_x11_drawable_get_xid(gs->pstarget));
916     putenv(gv_env);
917
918     /* change to directory where the input file is. This helps
919      * with postscript-files which include other files using
920      * a relative path */
921     dir = g_path_get_dirname(gs->gs_filename);
922     chdir(dir);
923     g_free(dir);
924
925     execvp(argv[0], argv);
926
927     /* Notify error */
928     g_print("Unable to execute [%s]\n", argv[0]);
929     g_strfreev(gs_args);
930     g_free(gv_env);
931     if(alpha_args)
932       g_strfreev(alpha_args);
933     _exit(1);
934     break;
935   default:                     /* parent */
936     if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
937       int result;
938       close(std_in[0]);
939       /* use non-blocking IO for pipe to ghostscript */
940       result = fcntl(std_in[1], F_GETFL, 0);
941       fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
942       gs->interpreter_input = std_in[1];
943     }
944     else {
945       gs->interpreter_input = -1;
946     }
947     close(std_out[1]);
948     gs->interpreter_output = std_out[0];
949     close(std_err[1]);
950     gs->interpreter_err = std_err[0];
951     gs->interpreter_output_id =
952       gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
953     gs->interpreter_error_id =
954       gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
955     break;
956   }
957   return TRUE;
958 }
959
960 static void
961 stop_interpreter(GtkGS * gs)
962 {
963   if(gs->interpreter_pid > 0) {
964     int status = 0;
965     kill(gs->interpreter_pid, SIGTERM);
966     while((wait(&status) == -1) && (errno == EINTR)) ;
967     gs->interpreter_pid = -1;
968     if(status == 1) {
969       gtk_gs_cleanup(gs);
970       gs->gs_status = _("Interpreter failed.");
971       g_signal_emit_by_name(G_OBJECT(gs), "interpreter_error", status);
972     }
973   }
974
975   if(gs->interpreter_input >= 0) {
976     close(gs->interpreter_input);
977     gs->interpreter_input = -1;
978     if(gs->interpreter_input_id != 0) {
979       gdk_input_remove(gs->interpreter_input_id);
980       gs->interpreter_input_id = 0;
981     }
982     while(gs->ps_input) {
983       struct record_list *ps_old = gs->ps_input;
984       gs->ps_input = gs->ps_input->next;
985       if(ps_old->close && NULL != ps_old->fp)
986         fclose(ps_old->fp);
987       g_free((char *) ps_old);
988     }
989   }
990
991   if(gs->interpreter_output >= 0) {
992     close(gs->interpreter_output);
993     gs->interpreter_output = -1;
994     if(gs->interpreter_output_id) {
995       gdk_input_remove(gs->interpreter_output_id);
996       gs->interpreter_output_id = 0;
997     }
998   }
999
1000   if(gs->interpreter_err >= 0) {
1001     close(gs->interpreter_err);
1002     gs->interpreter_err = -1;
1003     if(gs->interpreter_error_id) {
1004       gdk_input_remove(gs->interpreter_error_id);
1005       gs->interpreter_error_id = 0;
1006     }
1007   }
1008
1009   gs->busy = FALSE;
1010 }
1011
1012
1013 /*
1014  * Decompress gs->gs_filename if necessary
1015  * Set gs->filename_unc to the name of the uncompressed file or NULL.
1016  * Error reporting via signal 'interpreter_message'
1017  * Return name of input file to use or NULL on error..
1018  */
1019 static gchar *
1020 check_filecompressed(GtkGS * gs)
1021 {
1022   FILE *file;
1023   gchar buf[1024];
1024   gchar *filename, *filename_unc, *filename_err, *cmdline;
1025   const gchar *cmd;
1026   int fd;
1027
1028   cmd = NULL;
1029
1030   if((file = fopen(gs->gs_filename, "r"))
1031      && (fread(buf, sizeof(gchar), 3, file) == 3)) {
1032     if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
1033       /* file is gzipped or compressed */
1034       cmd = gtk_gs_defaults_get_ungzip_cmd();
1035     }
1036     else if(strncmp(buf, "BZh", 3) == 0) {
1037       /* file is compressed with bzip2 */
1038       cmd = gtk_gs_defaults_get_unbzip2_cmd();
1039     }
1040   }
1041   if(NULL != file)
1042     fclose(file);
1043
1044   if(!cmd)
1045     return gs->gs_filename;
1046
1047   /* do the decompression */
1048   filename = ggv_quote_filename(gs->gs_filename);
1049   filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1050   if((fd = mkstemp(filename_unc)) < 0) {
1051     g_free(filename_unc);
1052     g_free(filename);
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_err);
1059     g_free(filename_unc);
1060     g_free(filename);
1061     return NULL;
1062   }
1063   close(fd);
1064   cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
1065                             filename, filename_unc, filename_err);
1066   if((system(cmdline) == 0)
1067      && ggv_file_readable(filename_unc)
1068      && (ggv_file_length(filename_err) == 0)) {
1069     /* sucessfully uncompressed file */
1070     gs->gs_filename_unc = filename_unc;
1071   }
1072   else {
1073     /* report error */
1074     g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
1075                gs->gs_filename);
1076     gtk_gs_emit_error_msg(gs, buf);
1077     if(ggv_file_length(filename_err) > 0) {
1078       FILE *err;
1079       if((err = fopen(filename_err, "r"))) {
1080         /* print file to message window */
1081         while(fgets(buf, 1024, err))
1082           gtk_gs_emit_error_msg(gs, buf);
1083         fclose(err);
1084       }
1085     }
1086     unlink(filename_unc);
1087     g_free(filename_unc);
1088     filename_unc = NULL;
1089   }
1090   unlink(filename_err);
1091   g_free(filename_err);
1092   g_free(cmdline);
1093   g_free(filename);
1094   return filename_unc;
1095 }
1096
1097 /*
1098  * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
1099  * pdf file if necessary.
1100  * Set gs->filename_dsc to the name of the dsc file or NULL.
1101  * Error reporting via signal 'interpreter_message'.
1102  */
1103 static gchar *
1104 check_pdf(GtkGS * gs)
1105 {
1106   FILE *file;
1107   gchar buf[1024], *filename;
1108   int fd;
1109
1110   /* use uncompressed file as input if necessary */
1111   filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
1112
1113   if((file = fopen(filename, "r"))
1114      && (fread(buf, sizeof(char), 5, file) == 5)
1115      && (strncmp(buf, "%PDF-", 5) == 0)) {
1116     /* we found a PDF file */
1117     gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
1118     filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1119     if((fd = mkstemp(filename_dsc)) < 0) {
1120       return NULL;
1121     }
1122     close(fd);
1123     filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1124     if((fd = mkstemp(filename_err)) < 0) {
1125       g_free(filename_dsc);
1126       return NULL;
1127     }
1128     close(fd);
1129     fname = ggv_quote_filename(filename);
1130     cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1131     g_free(fname);
1132     /* this command (sometimes?) prints error messages to stdout! */
1133     cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1134     g_free(cmd);
1135
1136     if((system(cmdline) == 0) && ggv_file_readable(filename_dsc)) {
1137
1138       /* success */
1139       filename = gs->gs_filename_dsc = filename_dsc;
1140
1141       if(ggv_file_length(filename_err) > 0) {
1142         gchar *err_msg = " ";
1143         GtkWidget *dialog;
1144         FILE *err;
1145         GdkColor color;
1146
1147         if((err = fopen(filename_err, "r"))) {
1148
1149           /* print the content of the file to a message box */
1150           while(fgets(buf, 1024, err))
1151             err_msg = g_strconcat(err_msg, buf, NULL);
1152
1153           /* FIXME The dialog is not yet set to modal, difficult to 
1154            * get the parent of the dialog box here 
1155            */
1156
1157           dialog = gtk_message_dialog_new(NULL,
1158                                           GTK_DIALOG_MODAL,
1159                                           GTK_MESSAGE_WARNING,
1160                                           GTK_BUTTONS_OK,
1161                                           ("There was an error while scaning the file: %s \n%s"),
1162                                           gs->gs_filename, err_msg);
1163
1164           gdk_color_parse("white", &color);
1165           gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1166
1167           g_signal_connect(G_OBJECT(dialog), "response",
1168                            G_CALLBACK(gtk_widget_destroy), NULL);
1169
1170           gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1171           gtk_widget_show(dialog);
1172           g_free(err_msg);
1173         }
1174       }
1175
1176     }
1177     else {
1178       /* report error */
1179       g_snprintf(buf, 1024,
1180                  _("Error while converting pdf file %s:\n"), filename);
1181       gtk_gs_emit_error_msg(gs, buf);
1182
1183       if(ggv_file_length(filename_err) > 0) {
1184         FILE *err;
1185         if((err = fopen(filename_err, "r"))) {
1186           /* print file to message window */
1187           while(fgets(buf, 1024, err))
1188             gtk_gs_emit_error_msg(gs, buf);
1189         }
1190       }
1191       unlink(filename_dsc);
1192       g_free(filename_dsc);
1193       filename = NULL;
1194     }
1195     unlink(filename_err);
1196     g_free(filename_err);
1197     g_free(cmdline);
1198   }
1199   if(NULL != file)
1200     fclose(file);
1201   return filename;
1202 }
1203
1204 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1205 /* never mind this patch: a properly working X server should take care of
1206    calculating the proper values. */
1207 static float
1208 compute_xdpi(void)
1209 {
1210 #   ifndef HAVE_XINERAMA
1211   return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1212 #   else
1213   Display *dpy;
1214   dpy = (Display *) GDK_DISPLAY();
1215   if(XineramaIsActive(dpy)) {
1216     int num_heads;
1217     XineramaScreenInfo *head_info;
1218     head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1219     /* fake it with dimensions of the first head for now */
1220     return 25.4 * head_info[0].width / gdk_screen_width_mm();
1221   }
1222   else {
1223     return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1224   }
1225 #   endif
1226   /* HAVE_XINERAMA */
1227 }
1228
1229 static float
1230 compute_ydpi(void)
1231 {
1232 #   ifndef HAVE_XINERAMA
1233   return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1234 #   else
1235   Display *dpy;
1236   dpy = (Display *) GDK_DISPLAY();
1237   if(XineramaIsActive(dpy)) {
1238     int num_heads;
1239     XineramaScreenInfo *head_info;
1240     head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1241     /* fake it with dimensions of the first head for now */
1242     return 25.4 * head_info[0].height / gdk_screen_height_mm();
1243   }
1244   else {
1245     return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1246   }
1247 #   endif
1248   /* HAVE_XINERAMA */
1249 }
1250 #else
1251 static float
1252 compute_xdpi(void)
1253 {
1254   return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1255 }
1256
1257 static float
1258 compute_ydpi(void)
1259 {
1260   return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1261 }
1262 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1263
1264 /* Compute new size of window, sets xdpi and ydpi if necessary.
1265  * returns True if new window size is different */
1266 static gboolean
1267 compute_size(GtkGS * gs)
1268 {
1269   guint new_width = 1;
1270   guint new_height = 1;
1271   gboolean change = FALSE;
1272   gint orientation;
1273
1274   /* width and height can be changed, calculate window size according */
1275   /* to xpdi and ydpi */
1276   orientation = gtk_gs_get_orientation(gs);
1277
1278   switch (orientation) {
1279   case GTK_GS_ORIENTATION_PORTRAIT:
1280   case GTK_GS_ORIENTATION_UPSIDEDOWN:
1281     new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1282     new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1283     break;
1284   case GTK_GS_ORIENTATION_LANDSCAPE:
1285   case GTK_GS_ORIENTATION_SEASCAPE:
1286     new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1287     new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1288     break;
1289   }
1290
1291   change = (new_width != gs->width * gs->zoom_factor)
1292     || (new_height != gs->height * gs->zoom_factor);
1293   gs->width = (gint) (new_width * gs->zoom_factor);
1294   gs->height = (gint) (new_height * gs->zoom_factor);
1295   if(GTK_WIDGET_REALIZED(gs)) {
1296     if(!gs->loaded) {
1297       if(gdk_window_is_visible(gs->pstarget))
1298         gdk_window_hide(gs->pstarget);
1299     }
1300     else {
1301       if(!gdk_window_is_visible(gs->pstarget) && gs->width > 0
1302          && gs->height > 0)
1303         gdk_window_show(gs->pstarget);
1304     }
1305     //gtk_gs_munge_adjustments(gs);
1306   }
1307
1308   return (change);
1309 }
1310
1311 gint
1312 gtk_gs_enable_interpreter(GtkGS * gs)
1313 {
1314   g_return_val_if_fail(gs != NULL, FALSE);
1315   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1316
1317   if(!gs->gs_filename)
1318     return 0;
1319
1320   gs->disable_start = FALSE;
1321   if(GTK_WIDGET_REALIZED(gs)) {
1322     return start_interpreter(gs);
1323   }
1324   else {
1325     return 0;
1326   }
1327 }
1328
1329 /* publicly accessible functions */
1330
1331 GType
1332 gtk_gs_get_type(void)
1333 {
1334   static GType gs_type = 0;
1335   if(!gs_type) {
1336     GTypeInfo gs_info = {
1337       sizeof(GtkGSClass),
1338       (GBaseInitFunc) NULL,
1339       (GBaseFinalizeFunc) NULL,
1340       (GClassInitFunc) gtk_gs_class_init,
1341       (GClassFinalizeFunc) NULL,
1342       NULL,                     /* class_data */
1343       sizeof(GtkGS),
1344       0,                        /* n_preallocs */
1345       (GInstanceInitFunc) gtk_gs_init
1346     };
1347
1348     static const GInterfaceInfo document_info =
1349     {
1350         (GInterfaceInitFunc) ps_document_document_iface_init,
1351         NULL,
1352         NULL
1353     };
1354
1355     gs_type = g_type_register_static(gtk_widget_get_type(),
1356                                      "GtkGS", &gs_info, 0);
1357
1358     g_type_add_interface_static (gs_type,
1359                                  EV_TYPE_DOCUMENT,
1360                                  &document_info);
1361   }
1362   return gs_type;
1363
1364
1365 }
1366
1367 GObject *
1368 gtk_gs_new(GtkAdjustment * hadj, GtkAdjustment * vadj)
1369 {
1370   GObject *gs;
1371
1372   if(NULL == hadj)
1373     hadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 1.0, 0.01, 0.1, 0.09));
1374   if(NULL == vadj)
1375     vadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 1.0, 0.01, 0.1, 0.09));
1376
1377   gs = g_object_new(GTK_GS_TYPE, NULL);
1378
1379   //gtk_gs_set_adjustments(gs, hadj, vadj);
1380
1381   return gs;
1382 }
1383
1384
1385 GObject *
1386 gtk_gs_new_from_file(GtkAdjustment * hadj, GtkAdjustment * vadj, char *fname)
1387 {
1388   GObject *gs = gtk_gs_new(hadj, vadj);
1389   gtk_gs_load(GTK_GS(gs), fname);
1390   return gs;
1391 }
1392
1393 void
1394 gtk_gs_reload(GtkGS * gs)
1395 {
1396   gchar *fname;
1397   gfloat hval = gs->hadj->value;
1398   gfloat vval = gs->vadj->value;
1399   gint page;
1400
1401   if(!gs->gs_filename)
1402     return;
1403
1404   page = gtk_gs_get_current_page(gs);
1405   fname = g_strdup(gs->gs_filename);
1406   gtk_gs_load(gs, fname);
1407   gtk_gs_goto_page(gs, page);
1408   gtk_adjustment_set_value(gs->hadj, hval);
1409   gtk_adjustment_set_value(gs->vadj, vval);
1410   g_free(fname);
1411 }
1412
1413
1414 /*
1415  * Show error message -> send signal "interpreter_message"
1416  */
1417 static void
1418 gtk_gs_emit_error_msg(GtkGS * gs, const gchar * msg)
1419 {
1420   gtk_signal_emit(GTK_OBJECT(gs),
1421                   gtk_gs_signals[INTERPRETER_MESSAGE], g_strdup(msg));
1422 }
1423
1424
1425 void
1426 gtk_gs_center_page(GtkGS * gs)
1427 {
1428 #if 0
1429   g_return_if_fail(gs != NULL);
1430   g_return_if_fail(GTK_IS_GS(gs));
1431
1432   gdk_window_move(gs->pstarget,
1433                   (gs->widget.allocation.width - gs->width) / 2,
1434                   (gs->widget.allocation.height - gs->height) / 2);
1435   gs->hadj->page_size = ((gfloat) gs->widget.allocation.width) / gs->width;
1436   gs->hadj->page_size = MIN(gs->hadj->page_size, 1.0);
1437   gs->vadj->page_size = ((gfloat) gs->widget.allocation.height) / gs->height;
1438   gs->vadj->page_size = MIN(gs->vadj->page_size, 1.0);
1439   gs->hadj->value = 0.5 - gs->hadj->page_size / 2;
1440   gs->vadj->value = 0.5 - gs->vadj->page_size / 2;
1441   gtk_adjustment_changed(gs->hadj);
1442   gtk_adjustment_changed(gs->vadj);
1443 #endif
1444 }
1445
1446 void
1447 gtk_gs_scroll(GtkGS * gs, gint x_delta, gint y_delta)
1448 {
1449   gfloat hval, vval;
1450
1451   g_return_if_fail(gs != NULL);
1452   g_return_if_fail(GTK_IS_GS(gs));
1453
1454   hval = gs->hadj->value + ((gfloat) x_delta) / gs->width;
1455   vval = gs->vadj->value + ((gfloat) y_delta) / gs->height;
1456   if(hval <= gs->hadj->upper - gs->hadj->page_size && hval >= gs->hadj->lower)
1457     gtk_adjustment_set_value(gs->hadj, hval);
1458   if(vval <= gs->vadj->upper - gs->vadj->page_size && vval >= gs->vadj->lower)
1459     gtk_adjustment_set_value(gs->vadj, vval);
1460 }
1461
1462 void
1463 gtk_gs_disable_interpreter(GtkGS * gs)
1464 {
1465   g_return_if_fail(gs != NULL);
1466   g_return_if_fail(GTK_IS_GS(gs));
1467
1468   gs->disable_start = TRUE;
1469   if(GTK_WIDGET_REALIZED(GTK_WIDGET(gs)))
1470     stop_interpreter(gs);
1471 }
1472
1473 gboolean
1474 gtk_gs_load(GtkGS * gs, const gchar * fname)
1475 {
1476   g_return_val_if_fail(gs != NULL, FALSE);
1477   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1478
1479   /* clean up previous document */
1480   gtk_gs_cleanup(gs);
1481
1482   if(fname == NULL) {
1483     if(gs->pstarget != NULL && gdk_window_is_visible(gs->pstarget))
1484       gdk_window_hide(gs->pstarget);
1485     gs->gs_status = "";
1486     return FALSE;
1487   }
1488
1489   /* prepare this document */
1490
1491   /* default values: no dsc information available  */
1492   gs->structured_doc = FALSE;
1493   gs->send_filename_to_gs = TRUE;
1494   gs->current_page = -2;
1495   gs->loaded = FALSE;
1496   if(*fname == '/') {
1497     /* an absolute path */
1498     gs->gs_filename = g_strdup(fname);
1499   }
1500   else {
1501     /* path relative to our cwd: make it absolute */
1502     gchar *cwd = g_get_current_dir();
1503     gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1504     g_free(cwd);
1505   }
1506
1507   if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1508     gs->send_filename_to_gs = FALSE;
1509   }
1510   else {
1511     /*
1512      * We need to make sure that the file is loadable/exists!
1513      * otherwise we want to exit without loading new stuff...
1514      */
1515     gchar *filename = NULL;
1516
1517     if(!ggv_file_readable(fname)) {
1518       gchar buf[1024];
1519       g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1520       gtk_gs_emit_error_msg(gs, buf);
1521       gs->gs_status = _("File is not readable.");
1522     }
1523     else {
1524       filename = check_filecompressed(gs);
1525       if(filename)
1526         filename = check_pdf(gs);
1527     }
1528
1529     if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1530       gtk_gs_cleanup(gs);
1531       return FALSE;
1532     }
1533
1534     /* we grab the vital statistics!!! */
1535     gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1536
1537     if(gs->doc == NULL) {
1538       /* File does not seem to be a Postscript one */
1539       gchar buf[1024];
1540       g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1541       gtk_gs_emit_error_msg(gs, buf);
1542       gtk_gs_cleanup(gs);
1543       gs->gs_status = _("The file is not a PostScript document.");
1544       return FALSE;
1545     }
1546
1547     if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1548        (gs->doc->epsf && gs->doc->numpages > 1)) {
1549       gs->structured_doc = TRUE;
1550       gs->send_filename_to_gs = FALSE;
1551     }
1552
1553     /* We have to set up the orientation of the document */
1554
1555
1556     /* orientation can only be portrait, and landscape or none.
1557        This is the document default. A document can have
1558        pages in landscape and some in portrait */
1559     if(gs->override_orientation) {
1560       /* If the orientation should be override... 
1561          then gs->orientation has already the correct
1562          value (it was set when the widget was created */
1563       /* So do nothing */
1564
1565     }
1566     else {
1567       /* Otherwise, set the proper orientation for the doc */
1568       gs->real_orientation = gs->doc->orientation;
1569     }
1570   }
1571   gtk_gs_set_page_size(gs, -1, gs->current_page);
1572   gs->loaded = TRUE;
1573
1574   gs->gs_status = _("Document loaded.");
1575
1576   return gs->loaded;
1577 }
1578
1579
1580 gboolean
1581 gtk_gs_next_page(GtkGS * gs)
1582 {
1583   XEvent event;
1584
1585   g_return_val_if_fail(gs != NULL, FALSE);
1586   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1587
1588   if(gs->interpreter_pid == 0) {    /* no interpreter active */
1589     return FALSE;
1590   }
1591
1592   if(gs->busy) {                /* interpreter is busy */
1593     return FALSE;
1594   }
1595
1596   gs->busy = TRUE;
1597
1598   event.xclient.type = ClientMessage;
1599   event.xclient.display = gdk_display;
1600   event.xclient.window = gs->message_window;
1601   event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1602   event.xclient.format = 32;
1603
1604   gdk_error_trap_push();
1605   XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1606   gdk_flush();
1607   gdk_error_trap_pop();
1608
1609   return TRUE;
1610 }
1611
1612 gint
1613 gtk_gs_get_current_page(GtkGS * gs)
1614 {
1615   g_return_val_if_fail(gs != NULL, -1);
1616   g_return_val_if_fail(GTK_IS_GS(gs), -1);
1617
1618   return gs->current_page;
1619 }
1620
1621 gint
1622 gtk_gs_get_page_count(GtkGS * gs)
1623 {
1624   if(!gs->gs_filename)
1625     return 0;
1626
1627   if(gs->doc) {
1628     if(gs->structured_doc)
1629       return gs->doc->numpages;
1630     else
1631       return G_MAXINT;
1632   }
1633   else
1634     return 0;
1635 }
1636
1637 gboolean
1638 gtk_gs_goto_page(GtkGS * gs, gint page)
1639 {
1640   g_return_val_if_fail(gs != NULL, FALSE);
1641   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1642
1643   if(!gs->gs_filename) {
1644     return FALSE;
1645   }
1646
1647   /* range checking... */
1648   if(page < 0)
1649     page = 0;
1650
1651   if(gs->structured_doc && gs->doc) {
1652     if(page >= gs->doc->numpages)
1653       page = gs->doc->numpages - 1;
1654
1655     if(page == gs->current_page && !gs->changed)
1656       return TRUE;
1657
1658     gs->current_page = page;
1659
1660     if(!GTK_WIDGET_REALIZED(gs))
1661       return FALSE;
1662
1663     if(gs->doc->pages[page].orientation != NONE &&
1664        !gs->override_orientation &&
1665        gs->doc->pages[page].orientation != gs->real_orientation) {
1666       gs->real_orientation = gs->doc->pages[page].orientation;
1667       gs->changed = TRUE;
1668     }
1669
1670     gtk_gs_set_page_size(gs, -1, page);
1671
1672     gs->changed = FALSE;
1673
1674     if(is_interpreter_ready(gs)) {
1675       gtk_gs_next_page(gs);
1676     }
1677     else {
1678       gtk_gs_enable_interpreter(gs);
1679       send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1680       send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1681     }
1682
1683     send_ps(gs, gs->doc->pages[gs->current_page].begin,
1684             gs->doc->pages[gs->current_page].len, FALSE);
1685   }
1686   else {
1687     /* Unstructured document */
1688     /* In the case of non structured documents,
1689        GS read the PS from the  actual file (via command
1690        line. Hence, ggv only send a signal next page.
1691        If ghostview is not running it is usually because
1692        the last page of the file was displayed. In that
1693        case, ggv restarts GS again and the first page is displayed.
1694      */
1695     if(page == gs->current_page && !gs->changed)
1696       return TRUE;
1697
1698     if(!GTK_WIDGET_REALIZED(gs))
1699       return FALSE;
1700
1701     if(!is_interpreter_ready(gs))
1702       gtk_gs_enable_interpreter(gs);
1703
1704     gs->current_page = page;
1705
1706     gtk_gs_next_page(gs);
1707   }
1708   return TRUE;
1709 }
1710
1711 /*
1712  * set pagesize sets the size from
1713  * if new_pagesize is -1, then it is set to either
1714  *  a) the default settings of pageid, if they exist, or if pageid != -1.
1715  *  b) the default setting of the document, if it exists.
1716  *  c) the default setting of the widget.
1717  * otherwise, the new_pagesize is used as the pagesize
1718  */
1719 gboolean
1720 gtk_gs_set_page_size(GtkGS * gs, gint new_pagesize, gint pageid)
1721 {
1722   gint new_llx = 0;
1723   gint new_lly = 0;
1724   gint new_urx = 0;
1725   gint new_ury = 0;
1726   GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1727
1728   g_return_val_if_fail(gs != NULL, FALSE);
1729   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1730
1731   if(new_pagesize == -1) {
1732     if(gs->default_size > 0)
1733       new_pagesize = gs->default_size;
1734     if(!gs->override_size && gs->doc) {
1735       /* If we have a document:
1736          We use -- the page size (if specified)
1737          or the doc. size (if specified)
1738          or the page bbox (if specified)
1739          or the bounding box
1740        */
1741       if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1742          (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1743         new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1744       }
1745       else if(gs->doc->default_page_size != NULL) {
1746         new_pagesize = gs->doc->default_page_size - gs->doc->size;
1747       }
1748       else if((pageid >= 0) &&
1749               (gs->doc->numpages > pageid) &&
1750               (gs->doc->pages) &&
1751               (gs->doc->pages[pageid].boundingbox[URX] >
1752                gs->doc->pages[pageid].boundingbox[LLX]) &&
1753               (gs->doc->pages[pageid].boundingbox[URY] >
1754                gs->doc->pages[pageid].boundingbox[LLY])) {
1755         new_pagesize = -1;
1756       }
1757       else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1758               (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1759         new_pagesize = -1;
1760       }
1761     }
1762   }
1763
1764   /* Compute bounding box */
1765   if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) {    /* epsf or bbox */
1766     if((pageid >= 0) &&
1767        (gs->doc->pages) &&
1768        (gs->doc->pages[pageid].boundingbox[URX] >
1769         gs->doc->pages[pageid].boundingbox[LLX])
1770        && (gs->doc->pages[pageid].boundingbox[URY] >
1771            gs->doc->pages[pageid].boundingbox[LLY])) {
1772       /* use page bbox */
1773       new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1774       new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1775       new_urx = gs->doc->pages[pageid].boundingbox[URX];
1776       new_ury = gs->doc->pages[pageid].boundingbox[URY];
1777     }
1778     else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1779             (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1780       /* use doc bbox */
1781       new_llx = gs->doc->boundingbox[LLX];
1782       new_lly = gs->doc->boundingbox[LLY];
1783       new_urx = gs->doc->boundingbox[URX];
1784       new_ury = gs->doc->boundingbox[URY];
1785     }
1786   }
1787   else {
1788     if(new_pagesize < 0)
1789       new_pagesize = gs->default_size;
1790     new_llx = new_lly = 0;
1791     if(gs->doc && !gs->override_size && gs->doc->size &&
1792        (new_pagesize < gs->doc->numsizes)) {
1793       new_urx = gs->doc->size[new_pagesize].width;
1794       new_ury = gs->doc->size[new_pagesize].height;
1795     }
1796     else {
1797       new_urx = papersizes[new_pagesize].width;
1798       new_ury = papersizes[new_pagesize].height;
1799     }
1800   }
1801
1802   if(new_urx <= new_llx)
1803     new_urx = papersizes[12].width;
1804   if(new_ury <= new_lly)
1805     new_ury = papersizes[12].height;
1806
1807   /* If bounding box changed, setup for new size. */
1808   /* gtk_gs_disable_interpreter (gs); */
1809   if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1810      (new_urx != gs->urx) || (new_ury != gs->ury)) {
1811     gs->llx = new_llx;
1812     gs->lly = new_lly;
1813     gs->urx = new_urx;
1814     gs->ury = new_ury;
1815     gs->changed = TRUE;
1816   }
1817
1818   if(gs->changed) {
1819     if(GTK_WIDGET_REALIZED(gs)) {
1820       set_up_page(gs);
1821       //gtk_widget_queue_resize(&(gs->widget));
1822     }
1823     return TRUE;
1824   }
1825
1826   return FALSE;
1827 }
1828
1829 void
1830 gtk_gs_set_override_orientation(GtkGS * gs, gboolean bNewOverride)
1831 {
1832   gint iOldOrientation;
1833
1834   g_return_if_fail(gs != NULL);
1835   g_return_if_fail(GTK_IS_GS(gs));
1836
1837   iOldOrientation = gtk_gs_get_orientation(gs);
1838
1839   gs->override_orientation = bNewOverride;
1840
1841   /* If the current orientation is different from the 
1842      new orientation  then redisplay */
1843   if(iOldOrientation != gtk_gs_get_orientation(gs)) {
1844     gs->changed = TRUE;
1845     if(GTK_WIDGET_REALIZED(gs))
1846       set_up_page(gs);
1847   }
1848   //gtk_widget_queue_resize(&(gs->widget));
1849 }
1850
1851 gboolean
1852 gtk_gs_get_override_orientation(GtkGS * gs)
1853 {
1854   g_return_val_if_fail(gs != NULL, FALSE);
1855   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1856
1857   return gs->override_orientation;
1858 }
1859
1860 void
1861 gtk_gs_set_override_size(GtkGS * gs, gboolean f)
1862 {
1863   g_return_if_fail(gs != NULL);
1864   g_return_if_fail(GTK_IS_GS(gs));
1865
1866   if(f != gs->override_size) {
1867     gs->override_size = f;
1868     gs->changed = TRUE;
1869     gtk_gs_set_page_size(gs, -1, gs->current_page);
1870     if(GTK_WIDGET_REALIZED(gs))
1871       set_up_page(gs);
1872   }
1873   //gtk_widget_queue_resize(&(gs->widget));
1874 }
1875
1876 gboolean
1877 gtk_gs_get_override_size(GtkGS * gs)
1878 {
1879   g_return_val_if_fail(gs != NULL, FALSE);
1880   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1881
1882   return gs->override_size;
1883 }
1884
1885 void
1886 gtk_gs_set_zoom(GtkGS * gs, gfloat zoom)
1887 {
1888   g_return_if_fail(gs != NULL);
1889   g_return_if_fail(GTK_IS_GS(gs));
1890
1891   switch (gs->zoom_mode) {
1892   case GTK_GS_ZOOM_FIT_WIDTH:
1893     zoom = gtk_gs_zoom_to_fit(gs, TRUE);
1894     break;
1895   case GTK_GS_ZOOM_FIT_PAGE:
1896     zoom = gtk_gs_zoom_to_fit(gs, FALSE);
1897     break;
1898   case GTK_GS_ZOOM_ABSOLUTE:
1899   default:
1900     break;
1901   }
1902   if(zoom < ggv_zoom_levels[0])
1903     zoom = ggv_zoom_levels[0];
1904   else if(zoom > ggv_zoom_levels[ggv_max_zoom_levels])
1905     zoom = ggv_zoom_levels[ggv_max_zoom_levels];
1906   if(fabs(gs->zoom_factor - zoom) > 0.001) {
1907     gs->zoom_factor = zoom;
1908     if(GTK_WIDGET_REALIZED(gs))
1909       set_up_page(gs);
1910     gs->changed = TRUE;
1911     //gtk_widget_queue_resize(&(gs->widget));
1912   }
1913 }
1914
1915 gfloat
1916 gtk_gs_get_zoom(GtkGS * gs)
1917 {
1918   g_return_val_if_fail(gs != NULL, 0.0);
1919   g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1920
1921   return gs->zoom_factor;
1922 }
1923
1924 gfloat
1925 gtk_gs_zoom_to_fit(GtkGS * gs, gboolean fit_width)
1926 {
1927   gint new_y;
1928   gfloat new_zoom;
1929   guint avail_w, avail_h;
1930
1931   g_return_val_if_fail(gs != NULL, 0.0);
1932   g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1933
1934   avail_w = (gs->avail_w > 0) ? gs->avail_w : gs->width;
1935   avail_h = (gs->avail_h > 0) ? gs->avail_h : gs->height;
1936
1937   new_zoom = ((gfloat) avail_w) / ((gfloat) gs->width) * gs->zoom_factor;
1938   if(!fit_width) {
1939     new_y = new_zoom * ((gfloat) gs->height) / gs->zoom_factor;
1940     if(new_y > avail_h)
1941       new_zoom = ((gfloat) avail_h) / ((gfloat) gs->height) * gs->zoom_factor;
1942   }
1943
1944   return new_zoom;
1945 }
1946
1947 gboolean
1948 gtk_gs_set_default_orientation(GtkGS * gs, gint orientation)
1949 {
1950   gint iOldOrientation;
1951
1952   g_return_val_if_fail(gs != NULL, FALSE);
1953   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1954   g_return_val_if_fail((orientation == GTK_GS_ORIENTATION_PORTRAIT) ||
1955                        (orientation == GTK_GS_ORIENTATION_LANDSCAPE) ||
1956                        (orientation == GTK_GS_ORIENTATION_UPSIDEDOWN) ||
1957                        (orientation == GTK_GS_ORIENTATION_SEASCAPE), FALSE);
1958
1959   iOldOrientation = gtk_gs_get_orientation(gs);
1960   gs->fallback_orientation = orientation;
1961
1962   /* We are setting the fallback orientation */
1963   if(iOldOrientation != gtk_gs_get_orientation(gs)) {
1964     gs->changed = TRUE;
1965     if(GTK_WIDGET_REALIZED(gs))
1966       set_up_page(gs);
1967     //gtk_widget_queue_resize(&(gs->widget));
1968     return TRUE;
1969   }
1970
1971   return FALSE;
1972 }
1973
1974 gint
1975 gtk_gs_get_default_orientation(GtkGS * gs)
1976 {
1977   g_return_val_if_fail(gs != NULL, -1);
1978   g_return_val_if_fail(GTK_IS_GS(gs), -1);
1979
1980   return gs->fallback_orientation;
1981 }
1982
1983 gint
1984 gtk_gs_get_orientation(GtkGS * gs)
1985 {
1986   g_return_val_if_fail(gs != NULL, -1);
1987   g_return_val_if_fail(GTK_IS_GS(gs), -1);
1988
1989   if(gs->doc) {
1990     if(gs->structured_doc) {
1991       if(gs->doc->pages[MAX(gs->current_page, 0)].orientation !=
1992          GTK_GS_ORIENTATION_NONE)
1993         gs->real_orientation =
1994           gs->doc->pages[MAX(gs->current_page, 0)].orientation;
1995       else
1996         gs->real_orientation = gs->doc->default_page_orientation;
1997     }
1998
1999     if(gs->real_orientation == GTK_GS_ORIENTATION_NONE)
2000       gs->real_orientation = gs->doc->orientation;
2001   }
2002
2003   if(gs->override_orientation ||
2004      gs->real_orientation == GTK_GS_ORIENTATION_NONE)
2005     return gs->fallback_orientation;
2006   else
2007     return gs->real_orientation;
2008 }
2009
2010 void
2011 gtk_gs_set_default_size(GtkGS * gs, gint size)
2012 {
2013   g_return_if_fail(gs != NULL);
2014   g_return_if_fail(GTK_IS_GS(gs));
2015
2016   gs->default_size = size;
2017   gtk_gs_set_page_size(gs, -1, gs->current_page);
2018 }
2019
2020 gint
2021 gtk_gs_get_default_size(GtkGS * gs)
2022 {
2023   g_return_val_if_fail(gs != NULL, -1);
2024   g_return_val_if_fail(GTK_IS_GS(gs), -1);
2025
2026   return gs->default_size;
2027 }
2028
2029 void
2030 gtk_gs_set_respect_eof(GtkGS * gs, gboolean f)
2031 {
2032   g_return_if_fail(gs != NULL);
2033   g_return_if_fail(GTK_IS_GS(gs));
2034
2035   if(gs->respect_eof == f)
2036     return;
2037
2038   gs->respect_eof = f;
2039   gtk_gs_set_page_size(gs, -1, gs->current_page);
2040 }
2041
2042 gint
2043 gtk_gs_get_respect_eof(GtkGS * gs)
2044 {
2045   g_return_val_if_fail(gs != NULL, -1);
2046   g_return_val_if_fail(GTK_IS_GS(gs), -1);
2047
2048   return gs->respect_eof;
2049 }
2050
2051 void
2052 gtk_gs_set_antialiasing(GtkGS * gs, gboolean f)
2053 {
2054   g_return_if_fail(gs != NULL);
2055   g_return_if_fail(GTK_IS_GS(gs));
2056
2057   if(gs->antialiased == f)
2058     return;
2059
2060   gs->antialiased = f;
2061   gs->changed = TRUE;
2062   if(GTK_WIDGET_REALIZED(gs))
2063     start_interpreter(gs);
2064   gtk_gs_goto_page(gs, gs->current_page);
2065 }
2066
2067 gint
2068 gtk_gs_get_antialiasing(GtkGS * gs)
2069 {
2070   g_return_val_if_fail(gs != NULL, -1);
2071   g_return_val_if_fail(GTK_IS_GS(gs), -1);
2072
2073   return gs->antialiased;
2074 }
2075
2076 const gchar *
2077 gtk_gs_get_document_title(GtkGS * gs)
2078 {
2079   g_return_val_if_fail(gs != NULL, NULL);
2080   g_return_val_if_fail(GTK_IS_GS(gs), NULL);
2081
2082   if(gs->doc && gs->doc->title)
2083     return gs->doc->title;
2084
2085   return NULL;
2086 }
2087
2088 guint
2089 gtk_gs_get_document_numpages(GtkGS * widget)
2090 {
2091   g_return_val_if_fail(widget != NULL, 0);
2092   g_return_val_if_fail(GTK_IS_GS(widget), 0);
2093
2094   if(widget->doc)
2095     return widget->doc->numpages;
2096
2097   return 0;
2098 }
2099
2100 const gchar *
2101 gtk_gs_get_document_page_label(GtkGS * widget, int page)
2102 {
2103   g_return_val_if_fail(widget != NULL, NULL);
2104   g_return_val_if_fail(GTK_IS_GS(widget), NULL);
2105
2106   if(widget->doc && widget->doc->pages && (widget->doc->numpages >= page))
2107     return widget->doc->pages[page - 1].label;
2108
2109   return NULL;
2110 }
2111
2112 gint
2113 gtk_gs_get_size_index(const gchar * string, GtkGSPaperSize * size)
2114 {
2115   guint idx = 0;
2116
2117   while(size[idx].name != NULL) {
2118     if(strcmp(size[idx].name, string) == 0)
2119       return idx;
2120     idx++;
2121   }
2122
2123   return -1;
2124 }
2125
2126 void
2127 gtk_gs_start_scroll(GtkGS * gs)
2128 {
2129   gint x, y, w, h;
2130
2131   if(!GTK_WIDGET_REALIZED(gs) || !gs->show_scroll_rect)
2132     return;
2133
2134   gdk_window_get_geometry(gs->pstarget, &x, &y, &w, &h, NULL);
2135   gs->scroll_start_x = MAX(-x, 0);
2136   gs->scroll_start_y = MAX(-y, 0);
2137   //gs->scroll_width = MIN(gs->widget.allocation.width - 1, w - 1);
2138   //gs->scroll_height = MIN(gs->widget.allocation.height - 1, h - 1);
2139
2140   if(gs->bpixmap) {
2141     GdkRectangle rect;
2142     rect.x = gs->scroll_start_x;
2143     rect.y = gs->scroll_start_y;
2144     rect.width = gs->scroll_width + 1;
2145     rect.height = gs->scroll_height + 1;
2146     gdk_draw_rectangle(gs->bpixmap, gs->psgc, FALSE,
2147                        gs->scroll_start_x, gs->scroll_start_y,
2148                        gs->scroll_width, gs->scroll_height);
2149     rect.width = 1;
2150     gdk_window_invalidate_rect(gs->pstarget, &rect, TRUE);
2151     rect.x = gs->scroll_start_x + gs->scroll_width;
2152     gdk_window_invalidate_rect(gs->pstarget, &rect, TRUE);
2153     rect.x = gs->scroll_start_x + 1;
2154     rect.width = gs->scroll_start_x + gs->scroll_width - 1;
2155     rect.height = 1;
2156     gdk_window_invalidate_rect(gs->pstarget, &rect, TRUE);
2157     rect.y = gs->scroll_start_y + gs->scroll_height;
2158     gdk_window_invalidate_rect(gs->pstarget, &rect, TRUE);
2159   }
2160 }
2161
2162 void
2163 gtk_gs_end_scroll(GtkGS * gs)
2164 {
2165   if(!GTK_WIDGET_REALIZED(gs) || !gs->show_scroll_rect)
2166     return;
2167
2168   if(gs->scroll_start_x == -1 || gs->scroll_start_y == -1)
2169     return;
2170
2171   if(gs->bpixmap) {
2172     GdkRectangle rect;
2173     rect.x = gs->scroll_start_x;
2174     rect.y = gs->scroll_start_y;
2175     rect.width = gs->scroll_width + 1;
2176     rect.height = gs->scroll_height + 1;
2177     gdk_draw_rectangle(gs->bpixmap, gs->psgc, FALSE,
2178                        gs->scroll_start_x, gs->scroll_start_y,
2179                        gs->scroll_width, gs->scroll_height);
2180     rect.width = 1;
2181     gdk_window_invalidate_rect(gs->pstarget, &rect, TRUE);
2182     rect.x = gs->scroll_start_x + gs->scroll_width;
2183     gdk_window_invalidate_rect(gs->pstarget, &rect, TRUE);
2184     rect.x = gs->scroll_start_x + 1;
2185     rect.width = gs->scroll_start_x + gs->scroll_width - 1;
2186     rect.height = 1;
2187     gdk_window_invalidate_rect(gs->pstarget, &rect, TRUE);
2188     rect.y = gs->scroll_start_y + gs->scroll_height;
2189     gdk_window_invalidate_rect(gs->pstarget, &rect, TRUE);
2190   }
2191   gs->scroll_start_x = -1;
2192   gs->scroll_start_y = -1;
2193 }
2194
2195 void
2196 gtk_gs_set_show_scroll_rect(GtkGS * gs, gboolean f)
2197 {
2198   gs->show_scroll_rect = f;
2199 }
2200
2201 gboolean
2202 gtk_gs_get_show_scroll_rect(GtkGS * gs)
2203 {
2204   return gs->show_scroll_rect;
2205 }
2206
2207 gboolean
2208 gtk_gs_scroll_to_edge(GtkGS * gs, GtkPositionType vertical,
2209                       GtkPositionType horizontal)
2210 {
2211   g_return_val_if_fail(gs != NULL, FALSE);
2212   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
2213
2214   switch (vertical) {
2215   case GTK_POS_TOP:
2216     gs->vadj->value = gs->vadj->lower;
2217     gtk_adjustment_value_changed(gs->vadj);
2218     break;
2219   case GTK_POS_BOTTOM:
2220     gs->vadj->value = gs->vadj->upper - gs->vadj->page_size;
2221     gtk_adjustment_value_changed(gs->vadj);
2222     break;
2223   default:
2224     g_assert(0);                /* Illegal parameter error */
2225   }
2226
2227
2228   switch (horizontal) {
2229   case GTK_POS_TOP:
2230     gs->hadj->value = gs->hadj->lower;
2231     gtk_adjustment_value_changed(gs->hadj);
2232     break;
2233   case GTK_POS_BOTTOM:
2234     gs->hadj->value = gs->hadj->upper - gs->hadj->page_size;
2235     gtk_adjustment_value_changed(gs->hadj);
2236     break;
2237   default:
2238     g_assert(0);                /* Illegal parameter error */
2239   }
2240
2241   return TRUE;
2242 }
2243
2244 gboolean
2245 gtk_gs_scroll_step(GtkGS * gs, GtkScrollType direction, gboolean dowrap)
2246 {
2247   GtkAdjustment *MainAdj;       /* We will move this adjustment */
2248   GtkAdjustment *SecoAdj;       /* And this _only_ if we can't move MainAdj (ie. we're edge)
2249                                    and there is wrapping */
2250
2251   gboolean MoveHorizontal = TRUE;   /* Positive if we move horizontal */
2252   gboolean DirectionFlag = TRUE;    /* Positive if we move towards upper */
2253   g_return_val_if_fail(gs != NULL, FALSE);
2254   g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
2255
2256 #define EPSILON 0.00005
2257
2258 #define CHECK_THERE_IS_NO_LOWER_SPACE(adj) \
2259         ((adj)->value - (EPSILON) <= (adj)->lower)
2260 #define CHECK_THERE_IS_NO_UPPER_SPACE(adj) \
2261         ((adj)->value + (EPSILON) >= (adj)->upper - (adj)->page_size)
2262
2263 #define CHECK_THERE_IS_NO_SPACE_FOR_STEP(adj,dir) \
2264         (dir?CHECK_THERE_IS_NO_UPPER_SPACE(adj):CHECK_THERE_IS_NO_LOWER_SPACE(adj))
2265
2266   /* To make code more readable, we make a macro */
2267 #define ADVANCE_TOWARDS_LOWER(adj) \
2268         (adj->value -= gs->scroll_step * (adj->page_size))
2269 #define ADVANCE_TOWARDS_UPPER(adj) \
2270         (adj->value += gs->scroll_step * (adj->page_size))
2271
2272 #define ADVANCE_STEP(adj,dir) \
2273         (dir?ADVANCE_TOWARDS_UPPER(adj):ADVANCE_TOWARDS_LOWER(adj))
2274
2275 #define MOVE_TO_LOWER_EDGE(adj) \
2276         (adj->value = adj->lower)
2277 #define MOVE_TO_UPPER_EDGE(adj) \
2278         (adj->value = adj->upper - adj->page_size)
2279
2280   /* if upper is 1 goto upper, otherwise to lower */
2281 #define MOVE_TO_EDGE(adj,upper) (upper?MOVE_TO_UPPER_EDGE(adj):MOVE_TO_LOWER_EDGE(adj))
2282
2283   /* These variables make our life easier */
2284   switch (direction) {
2285   case GTK_SCROLL_STEP_RIGHT:
2286     MoveHorizontal = TRUE;
2287     DirectionFlag = TRUE;
2288     break;
2289   case GTK_SCROLL_STEP_LEFT:
2290     MoveHorizontal = TRUE;
2291     DirectionFlag = FALSE;
2292     break;
2293   case GTK_SCROLL_STEP_DOWN:
2294     MoveHorizontal = FALSE;
2295     DirectionFlag = TRUE;
2296     break;
2297   case GTK_SCROLL_STEP_UP:
2298     MoveHorizontal = FALSE;
2299     DirectionFlag = FALSE;
2300     break;
2301   default:
2302     g_warning("Illegal scroll step direction.");
2303   }
2304
2305   if(MoveHorizontal) {
2306     MainAdj = gs->hadj;
2307     SecoAdj = gs->vadj;
2308   }
2309   else {
2310     MainAdj = gs->vadj;
2311     SecoAdj = gs->hadj;
2312   }
2313
2314   if(CHECK_THERE_IS_NO_SPACE_FOR_STEP(MainAdj, DirectionFlag)) {
2315     if(!dowrap)
2316       return FALSE;
2317     /* Move in the oposite axis */
2318     if(CHECK_THERE_IS_NO_SPACE_FOR_STEP(SecoAdj, DirectionFlag)) {
2319       /* there is no place to move, we need a new page */
2320       return FALSE;
2321     }
2322     ADVANCE_STEP(SecoAdj, DirectionFlag);
2323
2324     if(CHECK_THERE_IS_NO_SPACE_FOR_STEP(SecoAdj, DirectionFlag)) {
2325       /* We move it too far, lets move it to the edge */
2326       MOVE_TO_EDGE(SecoAdj, DirectionFlag);
2327     }
2328     /* now move to edge (other axis) in oposite direction */
2329     MOVE_TO_EDGE(MainAdj, !DirectionFlag);
2330     gtk_adjustment_value_changed(SecoAdj);
2331     return TRUE;
2332   }
2333
2334   /* Now we know we can move in the direction sought */
2335   ADVANCE_STEP(MainAdj, DirectionFlag);
2336
2337   if(CHECK_THERE_IS_NO_SPACE_FOR_STEP(MainAdj, DirectionFlag)) {
2338     /* We move it too far, lets move it to the edge */
2339     MOVE_TO_EDGE(MainAdj, DirectionFlag);
2340   }
2341   gtk_adjustment_value_changed(MainAdj);
2342
2343   return TRUE;
2344 }
2345
2346 gchar *
2347 gtk_gs_get_postscript(GtkGS * gs, gint * pages)
2348 {
2349   GtkGSDocSink *sink;
2350   gchar *doc;
2351   gboolean free_pages = FALSE;
2352
2353   if(pages == NULL) {
2354     if(!gs->structured_doc) {
2355       FILE *f;
2356       struct stat sb;
2357
2358       if(stat(GTK_GS_GET_PS_FILE(gs), &sb))
2359         return NULL;
2360       doc = g_new(gchar, sb.st_size);
2361       if(!doc)
2362         return NULL;
2363       f = fopen(GTK_GS_GET_PS_FILE(gs), "r");
2364       if(NULL != f && fread(doc, sb.st_size, 1, f) != 1) {
2365         g_free(doc);
2366         doc = NULL;
2367       }
2368       if(NULL != f)
2369         fclose(f);
2370       return doc;
2371     }
2372     else {
2373       int i, n = gtk_gs_get_page_count(gs);
2374       pages = g_new0(gint, n);
2375       for(i = 0; i < n; i++)
2376         pages[i] = TRUE;
2377       free_pages = TRUE;
2378     }
2379   }
2380
2381   sink = gtk_gs_doc_sink_new();
2382
2383   if(GTK_GS_IS_PDF(gs)) {
2384     gchar *tmpn = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
2385     gchar *cmd, *fname;
2386     int tmpfd;
2387
2388     if((tmpfd = mkstemp(tmpn)) < 0) {
2389       g_free(tmpn);
2390       return NULL;
2391     }
2392     close(tmpfd);
2393     fname = ggv_quote_filename(gs->gs_filename_unc ?
2394                                gs->gs_filename_unc : gs->gs_filename);
2395     cmd = g_strdup_printf(gtk_gs_defaults_get_convert_pdf_cmd(), tmpn, fname);
2396     g_free(fname);
2397     if((system(cmd) == 0) && ggv_file_readable(tmpn)) {
2398       GObject *tmp_gs;
2399       tmp_gs = gtk_gs_new_from_file(NULL, NULL, tmpn);
2400       if(NULL != tmp_gs) {
2401         if(GTK_GS(tmp_gs)->loaded)
2402           pscopydoc(sink, tmpn, GTK_GS(tmp_gs)->doc, pages);
2403         g_object_unref(tmp_gs);
2404       }
2405     }
2406     g_free(cmd);
2407     g_free(tmpn);
2408   }
2409   else {
2410     /* Use uncompressed file if necessary */
2411     pscopydoc(sink, GTK_GS_GET_PS_FILE(gs), gs->doc, pages);
2412   }
2413   if(free_pages)
2414     g_free(pages);
2415   doc = gtk_gs_doc_sink_get_buffer(sink);
2416   gtk_gs_doc_sink_free(sink);
2417   return doc;
2418 }
2419
2420 void
2421 gtk_gs_set_adjustments(GtkGS * gs, GtkAdjustment * hadj, GtkAdjustment * vadj)
2422 {
2423   g_return_if_fail(gs != NULL);
2424   g_return_if_fail(GTK_IS_GS(gs));
2425   if(hadj)
2426     g_return_if_fail(GTK_IS_ADJUSTMENT(hadj));
2427   else
2428     hadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 1.0, 0.0, 0.0, 1.0));
2429   if(vadj)
2430     g_return_if_fail(GTK_IS_ADJUSTMENT(vadj));
2431   else
2432     vadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 1.0, 0.0, 0.0, 1.0));
2433
2434   if(gs->hadj && (gs->hadj != hadj)) {
2435     g_signal_handlers_disconnect_matched(G_OBJECT(gs->hadj),
2436                                          G_SIGNAL_MATCH_DATA,
2437                                          0, 0, NULL, NULL, gs);
2438     gtk_object_unref(GTK_OBJECT(gs->hadj));
2439   }
2440   if(gs->vadj && (gs->vadj != vadj)) {
2441     g_signal_handlers_disconnect_matched(G_OBJECT(gs->vadj),
2442                                          G_SIGNAL_MATCH_DATA,
2443                                          0, 0, NULL, NULL, gs);
2444     gtk_object_unref(GTK_OBJECT(gs->vadj));
2445   }
2446   if(gs->hadj != hadj) {
2447     hadj->lower = 0.0;
2448     hadj->upper = 1.0;
2449     hadj->value = 0.0;
2450     hadj->page_size = 1.0;
2451     hadj->page_increment = 1.0;
2452     gs->hadj = hadj;
2453     gtk_object_ref(GTK_OBJECT(gs->hadj));
2454     gtk_object_sink(GTK_OBJECT(gs->hadj));
2455
2456     g_signal_connect(G_OBJECT(hadj), "value_changed",
2457                      G_CALLBACK(gtk_gs_value_adjustment_changed),
2458                      (gpointer) gs);
2459   }
2460   if(gs->vadj != vadj) {
2461     vadj->lower = 0.0;
2462     vadj->upper = 1.0;
2463     vadj->value = 0.0;
2464     vadj->page_size = 1.0;
2465     vadj->page_increment = 1.0;
2466     gs->vadj = vadj;
2467     gtk_object_ref(GTK_OBJECT(gs->vadj));
2468     gtk_object_sink(GTK_OBJECT(gs->vadj));
2469
2470     g_signal_connect(G_OBJECT(vadj), "value_changed",
2471                      G_CALLBACK(gtk_gs_value_adjustment_changed),
2472                      (gpointer) gs);
2473   }
2474   //if(GTK_WIDGET_REALIZED(gs))
2475     //gtk_gs_munge_adjustments(gs);
2476 }
2477
2478
2479 void
2480 gtk_gs_set_scroll_step(GtkGS * gs, gfloat scroll_step)
2481 {
2482   gs->scroll_step = scroll_step;
2483 }
2484
2485 gfloat
2486 gtk_gs_get_scroll_step(GtkGS * gs)
2487 {
2488   return gs->scroll_step;
2489 }
2490
2491 void
2492 gtk_gs_set_zoom_mode(GtkGS * gs, GtkGSZoomMode zoom_mode)
2493 {
2494   if(zoom_mode != gs->zoom_mode) {
2495     gs->zoom_mode = zoom_mode;
2496     gtk_gs_set_zoom(gs, 1.0);
2497   }
2498 }
2499
2500 GtkGSZoomMode
2501 gtk_gs_get_zoom_mode(GtkGS * gs)
2502 {
2503   return gs->zoom_mode;
2504 }
2505
2506 void
2507 gtk_gs_set_available_size(GtkGS * gs, guint avail_w, guint avail_h)
2508 {
2509   gs->avail_w = avail_w;
2510   gs->avail_h = avail_h;
2511   if(gs->zoom_mode != GTK_GS_ZOOM_ABSOLUTE) {
2512     gtk_gs_set_zoom(gs, 0.0);
2513   }
2514 }
2515
2516 static gboolean
2517 ps_document_load (EvDocument  *document,
2518                    const char  *uri,
2519                    GError     **error)
2520 {
2521         return gtk_gs_load (GTK_GS (document), uri);
2522 }
2523
2524 static int
2525 ps_document_get_n_pages (EvDocument  *document)
2526 {
2527         return gtk_gs_get_page_count (GTK_GS (document));
2528 }
2529
2530 static void
2531 ps_document_set_page (EvDocument  *document,
2532                        int          page)
2533 {
2534         gtk_gs_goto_page (GTK_GS (document), page);
2535 }
2536
2537 static int
2538 ps_document_get_page (EvDocument  *document)
2539 {
2540         return gtk_gs_get_current_page (GTK_GS (document));
2541 }
2542
2543 static void
2544 ps_document_set_target (EvDocument  *document,
2545                          GdkDrawable *target)
2546 {
2547         GTK_GS (document)->pstarget = target;
2548 }
2549
2550 static void
2551 ps_document_set_scale (EvDocument  *document,
2552                         double       scale)
2553 {
2554         gtk_gs_set_zoom (GTK_GS (document), scale);
2555 }
2556
2557 static void
2558 ps_document_set_page_offset (EvDocument  *document,
2559                               int          x,
2560                               int          y)
2561 {
2562 }
2563
2564 static void
2565 ps_document_get_page_size (EvDocument   *document,
2566                             int          *width,
2567                             int          *height)
2568 {
2569 }
2570
2571 static void
2572 ps_document_render (EvDocument  *document,
2573                      int          clip_x,
2574                      int          clip_y,
2575                      int          clip_width,
2576                      int          clip_height)
2577 {
2578         GtkGS *gs = GTK_GS (document);
2579
2580         start_interpreter(gs);
2581         gtk_gs_goto_page(gs, gs->current_page);
2582 }
2583
2584 static void
2585 ps_document_begin_find (EvDocument   *document,
2586                          const char   *search_string,
2587                          gboolean      case_sensitive)
2588 {
2589 }
2590
2591 static void
2592 ps_document_end_find (EvDocument   *document)
2593 {
2594 }
2595
2596 static void
2597 ps_document_document_iface_init (EvDocumentIface *iface)
2598 {
2599         iface->load = ps_document_load;
2600         iface->get_n_pages = ps_document_get_n_pages;
2601         iface->set_page = ps_document_set_page;
2602         iface->get_page = ps_document_get_page;
2603         iface->set_scale = ps_document_set_scale;
2604         iface->set_target = ps_document_set_target;
2605         iface->set_page_offset = ps_document_set_page_offset;
2606         iface->get_page_size = ps_document_get_page_size;
2607         iface->render = ps_document_render;
2608         iface->begin_find = ps_document_begin_find;
2609         iface->end_find = ps_document_end_find;
2610 }