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