1 /* Ghostscript widget for GTK/GNOME
3 * Copyright (C) 1998 - 2005 the Free Software Foundation
5 * Authors: Jonathan Blandford, Jaka Mocnik
7 * Based on code by: Federico Mena (Quartic), Szekeres Istvan (Pista)
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.
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.
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.
26 Ghostview interface to ghostscript
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.
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.
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.
50 The GHOSTVIEW environment variable
52 parameters: window-id [pixmap-id]
56 explanation of parameters:
58 window-id: tells ghostscript where to
59 - read the GHOSTVIEW property
61 If pixmap-id is not present,
62 ghostscript will draw on this window.
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.
68 The GHOSTVIEW property
74 bpixmap orient llx lly urx ury xdpi ydpi [left bottom top right]
76 scanf format: "%d %d %d %d %d %d %f %f %d %d %d %d"
78 explanation of parameters:
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.
84 orient: orientation of the page. The number represents clockwise
85 rotation of the paper in degrees. Permitted values are
88 llx, lly, urx, ury: Bounding box of the drawable. The bounding box
89 is specified in PostScript points in default user coordinates.
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.)
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
104 Events from ghostscript
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.
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.
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
123 The DONE message indicates that ghostscript has finished processing.
132 #include <gtk/gtkobject.h>
133 #include <gdk/gdkprivate.h>
134 #include <gdk/gdkx.h>
136 #include <glib/gi18n.h>
138 # include <gdk/gdkx.h>
139 # include <X11/extensions/Xinerama.h>
140 #endif /* HAVE_XINERAMA */
141 #include <X11/Intrinsic.h>
146 #include <sys/stat.h>
147 #include <sys/types.h>
148 #include <sys/wait.h>
152 #include "ps-document.h"
153 #include "ev-debug.h"
154 #include "gsdefaults.h"
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
165 #define PS_DOCUMENT_WATCH_INTERVAL 1000
166 #define PS_DOCUMENT_WATCH_TIMEOUT 2
168 #define MAX_BUFSIZE 1024
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)
175 enum { INTERPRETER_MESSAGE, INTERPRETER_ERROR, LAST_SIGNAL };
182 /* structure to describe section of file to send to ghostscript */
187 gboolean seek_needed;
189 struct record_list *next;
192 static gboolean broken_pipe = FALSE;
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);
221 static GObjectClass *parent_class = NULL;
223 static PSDocumentClass *gs_class = NULL;
226 ps_document_init(PSDocument * gs)
230 gs->current_page = 0;
231 gs->disable_start = FALSE;
232 gs->interpreter_pid = -1;
238 gs->gs_scanstyle = 0;
240 gs->gs_filename_dsc = 0;
241 gs->gs_filename_unc = 0;
245 gs->structured_doc = FALSE;
246 gs->reading_from_pipe = FALSE;
247 gs->send_filename_to_gs = FALSE;
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;
260 gs->input_buffer = NULL;
261 gs->input_buffer_ptr = NULL;
263 gs->buffer_bytes_left = 0;
269 gs->xdpi = compute_xdpi();
270 gs->ydpi = compute_ydpi();
274 gs->right_margin = 0;
275 gs->bottom_margin = 0;
277 gs->page_x_offset = 0;
278 gs->page_y_offset = 0;
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;
287 gs->gs_status = _("No document loaded.");
291 ps_document_set_property (GObject *object,
306 ps_document_get_property (GObject *object,
311 PSDocument *ps = PS_DOCUMENT (object);
317 g_value_set_string (value, ps->doc->title);
319 g_value_set_string (value, NULL);
326 ps_document_class_init(PSDocumentClass *klass)
328 GObjectClass *object_class;
330 object_class = (GObjectClass *) klass;
331 parent_class = g_type_class_peek_parent (klass);
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;
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);
344 g_object_class_override_property (object_class, PROP_TITLE, "title");
347 /* Clean all memory and temporal files */
349 ps_document_cleanup(PSDocument * gs)
351 g_return_if_fail(gs != NULL);
352 g_return_if_fail(GTK_IS_GS(gs));
354 stop_interpreter(gs);
357 fclose(gs->gs_psfile);
358 gs->gs_psfile = NULL;
360 if(gs->gs_filename) {
361 g_free(gs->gs_filename);
362 gs->gs_filename = NULL;
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;
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;
378 gs->current_page = 0;
388 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
390 PSDocument *gs = (PSDocument *) data;
392 if(event->type != GDK_CLIENT_EVENT)
395 gs->message_window = event->client.data.l[0];
397 if (event->client.message_type == gs_class->page_atom) {
398 LOG ("GS rendered the document");
402 ev_document_scale_changed (EV_DOCUMENT (gs));
405 ev_document_page_changed (EV_DOCUMENT (gs));
413 ps_document_set_target (EvDocument *document,
416 PSDocument *gs = PS_DOCUMENT (document);
421 gdk_window_get_user_data (gs->pstarget, &data);
422 g_return_if_fail (GTK_IS_WIDGET (data));
424 widget = GTK_WIDGET (data);
425 g_signal_handlers_disconnect_by_func
426 (widget, ps_document_widget_event, document);
429 gs->pstarget = target;
432 gdk_window_get_user_data (gs->pstarget, &data);
433 g_return_if_fail (GTK_IS_WIDGET (data));
435 widget = GTK_WIDGET (data);
436 g_signal_connect (widget, "event",
437 G_CALLBACK (ps_document_widget_event),
441 ps_document_goto_page (gs, gs->current_page);
445 ps_document_finalize (GObject * object)
449 g_return_if_fail (object != NULL);
450 g_return_if_fail (GTK_IS_GS (object));
454 gs = PS_DOCUMENT (object);
456 ps_document_cleanup (gs);
457 stop_interpreter (gs);
459 ps_document_set_target (EV_DOCUMENT (object), NULL);
461 if(gs->input_buffer) {
462 g_free(gs->input_buffer);
463 gs->input_buffer = NULL;
466 (*G_OBJECT_CLASS(parent_class)->finalize) (object);
470 send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close)
472 struct record_list *ps_new;
474 if(gs->interpreter_input < 0) {
475 g_critical("No pipe to gs: error in send_ps().");
479 ps_new = (struct record_list *) g_malloc(sizeof(struct record_list));
480 ps_new->fp = gs->gs_psfile;
481 ps_new->begin = begin;
483 ps_new->seek_needed = TRUE;
484 ps_new->close = close;
487 if(gs->input_buffer == NULL) {
488 gs->input_buffer = g_malloc(MAX_BUFSIZE);
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);
500 struct record_list *p = gs->ps_input;
501 while(p->next != NULL) {
509 ps_document_get_orientation(PSDocument * gs)
511 g_return_val_if_fail(gs != NULL, -1);
512 g_return_val_if_fail(GTK_IS_GS(gs), -1);
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;
521 gs->real_orientation = gs->doc->default_page_orientation;
524 if(gs->real_orientation == GTK_GS_ORIENTATION_NONE)
525 gs->real_orientation = gs->doc->orientation;
528 if(gs->real_orientation == GTK_GS_ORIENTATION_NONE)
529 return gs->fallback_orientation;
531 return gs->real_orientation;
535 set_up_page(PSDocument * gs)
539 //GdkColormap *colormap;
541 GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */
542 GdkColormap *colormap;
543 gboolean size_changed;
549 LOG ("Setup the page");
551 size_changed = compute_size (gs);
553 if (gs->pstarget == NULL)
556 /* Do we have to check if the actual geometry changed? */
558 stop_interpreter(gs);
560 orientation = ps_document_get_orientation(gs);
562 if (size_changed || gs->bpixmap == NULL) {
565 /* clear new pixmap (set to white) */
566 fill = gdk_gc_new(gs->pstarget);
568 colormap = gdk_drawable_get_colormap(gs->pstarget);
569 gdk_color_alloc (colormap, &white);
570 gdk_gc_set_foreground(fill, &white);
572 if(gs->width > 0 && gs->height > 0) {
574 gdk_drawable_unref(gs->bpixmap);
578 LOG ("Create our internal pixmap");
579 gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1);
581 gdk_draw_rectangle(gs->bpixmap, fill, TRUE,
582 0, 0, gs->width, gs->height);
585 gdk_draw_rectangle(gs->pstarget, fill, TRUE,
586 0, 0, gs->width, gs->height);
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".
599 savelocale = setlocale(LC_NUMERIC, "C");
602 g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
609 gs->xdpi * gs->zoom_factor,
610 gs->ydpi * gs->zoom_factor,
612 gs->bottom_margin, gs->right_margin, gs->top_margin);
614 LOG ("GS property %s", buf);
617 setlocale(LC_NUMERIC, savelocale);
619 gdk_property_change(gs->pstarget,
621 gs_class->string_atom,
622 8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
636 is_interpreter_ready(PSDocument * gs)
638 return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
642 interpreter_failed(PSDocument * gs)
644 stop_interpreter(gs);
648 output(gpointer data, gint source, GdkInputCondition condition)
650 char buf[MAX_BUFSIZE + 1], *msg;
652 PSDocument *gs = PS_DOCUMENT(data);
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);
662 else if(bytes == -1) {
664 interpreter_failed(gs);
667 if(gs->interpreter_err == -1) {
668 stop_interpreter(gs);
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);
679 else if(bytes == -1) {
681 interpreter_failed(gs);
684 if(gs->interpreter_output == -1) {
685 stop_interpreter(gs);
691 ps_document_emit_error_msg (gs, msg);
696 input(gpointer data, gint source, GdkInputCondition condition)
698 PSDocument *gs = PS_DOCUMENT(data);
700 void (*oldsig) (int);
701 oldsig = signal(SIGPIPE, catchPipe);
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)
711 g_free((char *) ps_old);
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;
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);
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);
730 gs->buffer_bytes_left = 0;
732 if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
733 interpreter_failed(gs); /* Error occurred */
735 gs->input_buffer_ptr = gs->input_buffer;
736 gs->bytes_left -= gs->buffer_bytes_left;
739 if(gs->buffer_bytes_left > 0) {
740 /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
742 bytes_written = write(gs->interpreter_input,
743 gs->input_buffer_ptr, gs->buffer_bytes_left);
746 ps_document_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
748 interpreter_failed(gs);
750 else if(bytes_written == -1) {
751 if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
752 interpreter_failed(gs); /* Something bad happened */
756 gs->buffer_bytes_left -= bytes_written;
757 gs->input_buffer_ptr += bytes_written;
761 while(gs->ps_input && gs->buffer_bytes_left == 0);
763 signal(SIGPIPE, oldsig);
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;
774 start_interpreter(PSDocument * gs)
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 */
781 #define NUM_GS_ARGS (NUM_ARGS - 20)
782 #define NUM_ALPHA_ARGS 10
784 char *argv[NUM_ARGS], *dir, *gv_env;
785 char **gs_args, **alpha_args = NULL;
788 LOG ("Start the interpreter");
793 stop_interpreter(gs);
795 if(gs->disable_start == TRUE)
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];
803 if(gs->antialiased) {
804 if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
805 alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
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];
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";
819 /* set up the pipes */
820 if(gs->send_filename_to_gs) {
821 argv[argc++] = PS_DOCUMENT_GET_PS_FILE(gs);
823 argv[argc++] = "quit";
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.");
836 if(pipe(std_out) == -1) {
840 if(pipe(std_err) == -1) {
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));
850 LOG ("Launching ghostview with env %s", gv_env);
853 gs->interpreter_pid = fork();
854 switch (gs->interpreter_pid) {
870 if(!gs->reading_from_pipe) {
871 if(gs->send_filename_to_gs) {
873 /* just in case gs tries to read from stdin */
874 stdinfd = open("/dev/null", O_RDONLY);
889 /* change to directory where the input file is. This helps
890 * with postscript-files which include other files using
892 dir = g_path_get_dirname(gs->gs_filename);
896 execvp(argv[0], argv);
899 g_print("Unable to execute [%s]\n", argv[0]);
903 g_strfreev(alpha_args);
906 default: /* parent */
907 if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
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];
916 gs->interpreter_input = -1;
919 gs->interpreter_output = std_out[0];
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);
932 stop_interpreter(PSDocument * gs)
934 if(gs->interpreter_pid > 0) {
936 LOG ("Stop the interpreter");
937 kill(gs->interpreter_pid, SIGTERM);
938 while((wait(&status) == -1) && (errno == EINTR)) ;
939 gs->interpreter_pid = -1;
941 ps_document_cleanup(gs);
942 gs->gs_status = _("Interpreter failed.");
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;
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)
958 g_free((char *) ps_old);
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;
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;
983 /* If file exists and is a regular file then return its length, else -1 */
985 file_length(const gchar * filename)
987 struct stat stat_rec;
989 if(filename && (stat(filename, &stat_rec) == 0)
990 && S_ISREG(stat_rec.st_mode))
991 return stat_rec.st_size;
996 /* Test if file exists, is a regular file and its length is > 0 */
998 file_readable(const char *filename)
1000 return (file_length(filename) > 0);
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..
1010 check_filecompressed(PSDocument * gs)
1014 gchar *filename, *filename_unc, *filename_err, *cmdline;
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();
1026 else if(strncmp(buf, "BZh", 3) == 0) {
1027 /* file is compressed with bzip2 */
1028 cmd = gtk_gs_defaults_get_unbzip2_cmd();
1035 return gs->gs_filename;
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);
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);
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;
1064 g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
1066 ps_document_emit_error_msg(gs, buf);
1067 if(file_length(filename_err) > 0) {
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);
1076 unlink(filename_unc);
1077 g_free(filename_unc);
1078 filename_unc = NULL;
1080 unlink(filename_err);
1081 g_free(filename_err);
1084 return filename_unc;
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. */
1093 # ifndef HAVE_XINERAMA
1094 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1097 dpy = (Display *) GDK_DISPLAY();
1098 if(XineramaIsActive(dpy)) {
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();
1106 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1115 # ifndef HAVE_XINERAMA
1116 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1119 dpy = (Display *) GDK_DISPLAY();
1120 if(XineramaIsActive(dpy)) {
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();
1128 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1137 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1143 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1145 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1147 /* Compute new size of window, sets xdpi and ydpi if necessary.
1148 * returns True if new window size is different */
1150 compute_size(PSDocument * gs)
1152 guint new_width = 1;
1153 guint new_height = 1;
1154 gboolean change = FALSE;
1157 /* width and height can be changed, calculate window size according */
1158 /* to xpdi and ydpi */
1159 orientation = ps_document_get_orientation(gs);
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;
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;
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);
1183 ps_document_enable_interpreter(PSDocument * gs)
1185 g_return_val_if_fail(gs != NULL, FALSE);
1186 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1188 if(!gs->gs_filename)
1191 gs->disable_start = FALSE;
1193 return start_interpreter(gs);
1196 /* publicly accessible functions */
1199 ps_document_get_type(void)
1201 static GType gs_type = 0;
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 */
1211 0, /* n_preallocs */
1212 (GInstanceInitFunc) ps_document_init
1215 static const GInterfaceInfo document_info =
1217 (GInterfaceInitFunc) ps_document_document_iface_init,
1222 gs_type = g_type_register_static(G_TYPE_OBJECT,
1223 "PSDocument", &gs_info, 0);
1225 g_type_add_interface_static (gs_type,
1235 * Show error message -> send signal "interpreter_message"
1238 ps_document_emit_error_msg(PSDocument * gs, const gchar * msg)
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);
1248 document_load(PSDocument * gs, const gchar * fname)
1250 g_return_val_if_fail(gs != NULL, FALSE);
1251 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1253 LOG ("Load the document");
1255 /* clean up previous document */
1256 ps_document_cleanup(gs);
1263 /* prepare this document */
1265 /* default values: no dsc information available */
1266 gs->structured_doc = FALSE;
1267 gs->send_filename_to_gs = TRUE;
1268 gs->current_page = 0;
1271 /* an absolute path */
1272 gs->gs_filename = g_strdup(fname);
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);
1281 if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1282 gs->send_filename_to_gs = FALSE;
1286 * We need to make sure that the file is loadable/exists!
1287 * otherwise we want to exit without loading new stuff...
1289 gchar *filename = NULL;
1291 if(!file_readable(fname)) {
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.");
1298 filename = check_filecompressed(gs);
1301 if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1302 ps_document_cleanup(gs);
1306 /* we grab the vital statistics!!! */
1307 gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1309 g_object_notify (G_OBJECT (gs), "title");
1311 if(gs->doc == NULL) {
1312 /* File does not seem to be a Postscript one */
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.");
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;
1327 /* We have to set up the orientation of the document */
1328 gs->real_orientation = gs->doc->orientation;
1330 ps_document_set_page_size(gs, -1, gs->current_page);
1333 gs->gs_status = _("Document loaded.");
1340 ps_document_next_page(PSDocument * gs)
1344 LOG ("Make ghostscript render next page");
1346 g_return_val_if_fail(gs != NULL, FALSE);
1347 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1349 if(gs->interpreter_pid == 0) { /* no interpreter active */
1353 if(gs->busy) { /* interpreter is busy */
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;
1365 gdk_error_trap_push();
1366 XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1368 gdk_error_trap_pop();
1374 ps_document_goto_page(PSDocument * gs, gint page)
1376 g_return_val_if_fail(gs != NULL, FALSE);
1377 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1379 LOG ("Go to page %d", page);
1381 if(!gs->gs_filename) {
1385 /* range checking... */
1389 if(gs->structured_doc && gs->doc) {
1391 LOG ("It's a structured document, let's send one page to gs");
1393 if(page >= gs->doc->numpages)
1394 page = gs->doc->numpages - 1;
1396 if(page == gs->current_page && !gs->changed)
1399 gs->current_page = page;
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;
1407 ps_document_set_page_size(gs, -1, page);
1409 gs->changed = FALSE;
1411 if(is_interpreter_ready(gs)) {
1412 ps_document_next_page(gs);
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);
1420 send_ps(gs, gs->doc->pages[gs->current_page].begin,
1421 gs->doc->pages[gs->current_page].len, FALSE);
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.
1433 LOG ("It's an unstructured document, gs will just read the file");
1435 if(page == gs->current_page && !gs->changed)
1438 ps_document_set_page_size(gs, -1, page);
1440 if(!is_interpreter_ready(gs))
1441 ps_document_enable_interpreter(gs);
1443 gs->current_page = page;
1445 ps_document_next_page(gs);
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
1459 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1465 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1467 LOG ("Set the page size");
1469 g_return_val_if_fail(gs != NULL, FALSE);
1470 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1472 if(new_pagesize == -1) {
1473 if(gs->default_size > 0)
1474 new_pagesize = gs->default_size;
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)
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;
1486 else if(gs->doc->default_page_size != NULL) {
1487 new_pagesize = gs->doc->default_page_size - gs->doc->size;
1489 else if((pageid >= 0) &&
1490 (gs->doc->numpages > pageid) &&
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])) {
1498 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1499 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1505 /* Compute bounding box */
1506 if(gs->doc && (gs->doc->epsf || new_pagesize == -1)) { /* epsf or bbox */
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])) {
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];
1519 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1520 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
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];
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;
1538 new_urx = papersizes[new_pagesize].width;
1539 new_ury = papersizes[new_pagesize].height;
1543 if(new_urx <= new_llx)
1544 new_urx = papersizes[12].width;
1545 if(new_ury <= new_lly)
1546 new_ury = papersizes[12].height;
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)) {
1568 ps_document_set_zoom (PSDocument * gs, gfloat zoom)
1570 g_return_if_fail (gs != NULL);
1572 gs->zoom_factor = zoom;
1574 if (gs->pstarget != NULL) {
1578 ps_document_goto_page (gs, gs->current_page);
1583 ps_document_load (EvDocument *document,
1590 filename = g_filename_from_uri (uri, NULL, error);
1594 result = document_load (PS_DOCUMENT (document), filename);
1596 g_set_error (error, G_FILE_ERROR,
1597 G_FILE_ERROR_FAILED,
1598 "Failed to load document '%s'\n",
1608 ps_document_save (EvDocument *document,
1612 g_warning ("ps_document_save not implemented"); /* FIXME */
1617 ps_document_get_n_pages (EvDocument *document)
1619 PSDocument *ps = PS_DOCUMENT (document);
1621 g_return_val_if_fail (ps != NULL, -1);
1623 if (!ps->gs_filename || !ps->doc) {
1627 return ps->structured_doc ? ps->doc->numpages : 1;
1631 ps_document_set_page (EvDocument *document,
1634 ps_document_goto_page (PS_DOCUMENT (document), page - 1);
1638 ps_document_get_page (EvDocument *document)
1640 PSDocument *ps = PS_DOCUMENT (document);
1642 g_return_val_if_fail (ps != NULL, -1);
1644 return ps->current_page + 1;
1648 ps_document_set_scale (EvDocument *document,
1651 ps_document_set_zoom (PS_DOCUMENT (document), scale);
1655 ps_document_set_page_offset (EvDocument *document,
1659 PSDocument *gs = PS_DOCUMENT (document);
1661 gs->page_x_offset = x;
1662 gs->page_y_offset = y;
1666 ps_document_get_page_size (EvDocument *document,
1671 /* Post script documents never vary in size */
1673 PSDocument *gs = PS_DOCUMENT (document);
1680 *height = gs->height;
1685 ps_document_render (EvDocument *document,
1691 PSDocument *gs = PS_DOCUMENT (document);
1696 if (gs->pstarget == NULL ||
1697 gs->bpixmap == NULL) {
1701 page.x = gs->page_x_offset;
1702 page.y = gs->page_y_offset;
1703 page.width = gs->width;
1704 page.height = gs->height;
1708 draw.width = clip_width;
1709 draw.height = clip_height;
1711 gc = gdk_gc_new (gs->pstarget);
1713 gdk_draw_drawable (gs->pstarget, gc,
1715 draw.x - page.x, draw.y - page.y,
1717 draw.width, draw.height);
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);
1723 g_object_unref (gc);
1727 ps_document_get_text (EvDocument *document, GdkRectangle *rect)
1729 g_warning ("ps_document_get_text not implemented"); /* FIXME ? */
1734 ps_document_get_link (EvDocument *document,
1742 ps_document_document_iface_init (EvDocumentIface *iface)
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;