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