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 "gsdefaults.h"
159 /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
160 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
161 # define O_NONBLOCK O_NDELAY
164 #define PS_DOCUMENT_WATCH_INTERVAL 1000
165 #define PS_DOCUMENT_WATCH_TIMEOUT 2
167 #define MAX_BUFSIZE 1024
169 #define PS_DOCUMENT_IS_COMPRESSED(gs) (PS_DOCUMENT(gs)->gs_filename_unc != NULL)
170 #define PS_DOCUMENT_GET_PS_FILE(gs) (PS_DOCUMENT_IS_COMPRESSED(gs) ? \
171 PS_DOCUMENT(gs)->gs_filename_unc : \
172 PS_DOCUMENT(gs)->gs_filename)
174 enum { INTERPRETER_MESSAGE, INTERPRETER_ERROR, LAST_SIGNAL };
181 /* structure to describe section of file to send to ghostscript */
186 gboolean seek_needed;
188 struct record_list *next;
191 static gboolean broken_pipe = FALSE;
199 /* Forward declarations */
200 static void ps_document_init(PSDocument * gs);
201 static void ps_document_class_init(PSDocumentClass * klass);
202 static void ps_document_emit_error_msg(PSDocument * gs, const gchar * msg);
203 static void ps_document_finalize(GObject * object);
204 static void send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close);
205 static void set_up_page(PSDocument * gs);
206 static void close_pipe(int p[2]);
207 static void interpreter_failed(PSDocument * gs);
208 static float compute_xdpi(void);
209 static float compute_ydpi(void);
210 static gboolean compute_size(PSDocument * gs);
211 static void output(gpointer data, gint source, GdkInputCondition condition);
212 static void input(gpointer data, gint source, GdkInputCondition condition);
213 static void stop_interpreter(PSDocument * gs);
214 static gint start_interpreter(PSDocument * gs);
215 gboolean computeSize(void);
216 static gboolean ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid);
217 static void ps_document_document_iface_init (EvDocumentIface *iface);
219 static GObjectClass *parent_class = NULL;
221 static PSDocumentClass *gs_class = NULL;
224 ps_document_init(PSDocument * gs)
228 gs->current_page = -2;
229 gs->disable_start = FALSE;
230 gs->interpreter_pid = -1;
236 gs->gs_scanstyle = 0;
238 gs->gs_filename_dsc = 0;
239 gs->gs_filename_unc = 0;
243 gs->structured_doc = FALSE;
244 gs->reading_from_pipe = FALSE;
245 gs->send_filename_to_gs = FALSE;
250 gs->interpreter_input = -1;
251 gs->interpreter_output = -1;
252 gs->interpreter_err = -1;
253 gs->interpreter_input_id = 0;
254 gs->interpreter_output_id = 0;
255 gs->interpreter_error_id = 0;
258 gs->input_buffer = NULL;
259 gs->input_buffer_ptr = NULL;
261 gs->buffer_bytes_left = 0;
267 gs->xdpi = compute_xdpi();
268 gs->ydpi = compute_ydpi();
272 gs->right_margin = 0;
273 gs->bottom_margin = 0;
275 gs->page_x_offset = 0;
276 gs->page_y_offset = 0;
278 /* Set user defined defaults */
279 gs->override_orientation = gtk_gs_defaults_get_override_orientation();
280 gs->fallback_orientation = gtk_gs_defaults_get_orientation();
281 gs->zoom_factor = gtk_gs_defaults_get_zoom_factor();
282 gs->default_size = gtk_gs_defaults_get_size();
283 gs->antialiased = gtk_gs_defaults_get_antialiased();
284 gs->override_size = gtk_gs_defaults_get_override_size();
285 gs->respect_eof = gtk_gs_defaults_get_respect_eof();
286 gs->zoom_mode = gtk_gs_defaults_get_zoom_mode();
288 gs->gs_status = _("No document loaded.");
292 ps_document_set_property (GObject *object,
307 ps_document_get_property (GObject *object,
312 PSDocument *ps = PS_DOCUMENT (object);
318 g_value_set_string (value, ps->doc->title);
320 g_value_set_string (value, NULL);
327 ps_document_class_init(PSDocumentClass * klass)
329 GObjectClass *object_class;
331 object_class = (GObjectClass *) klass;
332 parent_class = gtk_type_class(gtk_widget_get_type());
335 object_class->finalize = ps_document_finalize;
336 object_class->get_property = ps_document_get_property;
337 object_class->set_property = ps_document_set_property;
340 klass->gs_atom = gdk_atom_intern("GHOSTVIEW", FALSE);
341 klass->next_atom = gdk_atom_intern("NEXT", FALSE);
342 klass->page_atom = gdk_atom_intern("PAGE", FALSE);
343 klass->string_atom = gdk_atom_intern("STRING", FALSE);
345 gtk_gs_defaults_load();
347 g_object_class_override_property (object_class, PROP_TITLE, "title");
350 /* Clean all memory and temporal files */
352 ps_document_cleanup(PSDocument * gs)
354 g_return_if_fail(gs != NULL);
355 g_return_if_fail(GTK_IS_GS(gs));
357 stop_interpreter(gs);
360 fclose(gs->gs_psfile);
361 gs->gs_psfile = NULL;
363 if(gs->gs_filename) {
364 g_free(gs->gs_filename);
365 gs->gs_filename = NULL;
371 if(gs->gs_filename_dsc) {
372 unlink(gs->gs_filename_dsc);
373 g_free(gs->gs_filename_dsc);
374 gs->gs_filename_dsc = NULL;
376 if(gs->gs_filename_unc) {
377 unlink(gs->gs_filename_unc);
378 g_free(gs->gs_filename_unc);
379 gs->gs_filename_unc = NULL;
381 gs->current_page = -1;
391 ps_document_finalize(GObject * object)
395 g_return_if_fail(object != NULL);
396 g_return_if_fail(GTK_IS_GS(object));
398 gs = PS_DOCUMENT(object);
400 ps_document_cleanup(gs);
402 if(gs->input_buffer) {
403 g_free(gs->input_buffer);
404 gs->input_buffer = NULL;
407 (*G_OBJECT_CLASS(parent_class)->finalize) (object);
411 send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close)
413 struct record_list *ps_new;
415 if(gs->interpreter_input < 0) {
416 g_critical("No pipe to gs: error in send_ps().");
420 ps_new = (struct record_list *) g_malloc(sizeof(struct record_list));
421 ps_new->fp = gs->gs_psfile;
422 ps_new->begin = begin;
424 ps_new->seek_needed = TRUE;
425 ps_new->close = close;
428 if(gs->input_buffer == NULL) {
429 gs->input_buffer = g_malloc(MAX_BUFSIZE);
432 if(gs->ps_input == NULL) {
433 gs->input_buffer_ptr = gs->input_buffer;
434 gs->bytes_left = len;
435 gs->buffer_bytes_left = 0;
436 gs->ps_input = ps_new;
437 gs->interpreter_input_id =
438 gdk_input_add(gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
441 struct record_list *p = gs->ps_input;
442 while(p->next != NULL) {
450 ps_document_get_orientation(PSDocument * gs)
452 g_return_val_if_fail(gs != NULL, -1);
453 g_return_val_if_fail(GTK_IS_GS(gs), -1);
456 if(gs->structured_doc) {
457 if(gs->doc->pages[MAX(gs->current_page, 0)].orientation !=
458 GTK_GS_ORIENTATION_NONE)
459 gs->real_orientation =
460 gs->doc->pages[MAX(gs->current_page, 0)].orientation;
462 gs->real_orientation = gs->doc->default_page_orientation;
465 if(gs->real_orientation == GTK_GS_ORIENTATION_NONE)
466 gs->real_orientation = gs->doc->orientation;
469 if(gs->override_orientation ||
470 gs->real_orientation == GTK_GS_ORIENTATION_NONE)
471 return gs->fallback_orientation;
473 return gs->real_orientation;
477 set_up_page(PSDocument * gs)
481 //GdkColormap *colormap;
483 GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */
484 GdkColormap *colormap;
490 if (gs->pstarget == NULL)
493 /* Do we have to check if the actual geometry changed? */
495 stop_interpreter(gs);
497 orientation = ps_document_get_orientation(gs);
499 if(compute_size(gs)) {
502 /* clear new pixmap (set to white) */
503 fill = gdk_gc_new(gs->pstarget);
505 colormap = gdk_drawable_get_colormap(gs->pstarget);
506 gdk_color_alloc (colormap, &white);
507 gdk_gc_set_foreground(fill, &white);
509 if(gs->width > 0 && gs->height > 0) {
511 gdk_drawable_unref(gs->bpixmap);
515 gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1);
517 gdk_draw_rectangle(gs->bpixmap, fill, TRUE,
518 0, 0, gs->width, gs->height);
521 gdk_draw_rectangle(gs->pstarget, fill, TRUE,
522 0, 0, gs->width, gs->height);
531 /* gs needs floating point parameters with '.' as decimal point
532 * while some (european) locales use ',' instead, so we set the
533 * locale for this snprintf to "C".
535 savelocale = setlocale(LC_NUMERIC, "C");
538 g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
545 gs->xdpi * gs->zoom_factor,
546 gs->ydpi * gs->zoom_factor,
548 gs->bottom_margin, gs->right_margin, gs->top_margin);
551 setlocale(LC_NUMERIC, savelocale);
553 gdk_property_change(gs->pstarget,
555 gs_class->string_atom,
556 8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
570 is_interpreter_ready(PSDocument * gs)
572 return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
576 interpreter_failed(PSDocument * gs)
578 stop_interpreter(gs);
582 output(gpointer data, gint source, GdkInputCondition condition)
584 char buf[MAX_BUFSIZE + 1], *msg;
586 PSDocument *gs = PS_DOCUMENT(data);
588 if(source == gs->interpreter_output) {
589 bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
590 if(bytes == 0) { /* EOF occurred */
591 close(gs->interpreter_output);
592 gs->interpreter_output = -1;
593 gdk_input_remove(gs->interpreter_output_id);
596 else if(bytes == -1) {
598 interpreter_failed(gs);
601 if(gs->interpreter_err == -1) {
602 stop_interpreter(gs);
605 else if(source == gs->interpreter_err) {
606 bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
607 if(bytes == 0) { /* EOF occurred */
608 close(gs->interpreter_err);
609 gs->interpreter_err = -1;
610 gdk_input_remove(gs->interpreter_error_id);
613 else if(bytes == -1) {
615 interpreter_failed(gs);
618 if(gs->interpreter_output == -1) {
619 stop_interpreter(gs);
625 ps_document_emit_error_msg (gs, msg);
630 input(gpointer data, gint source, GdkInputCondition condition)
632 PSDocument *gs = PS_DOCUMENT(data);
634 void (*oldsig) (int);
635 oldsig = signal(SIGPIPE, catchPipe);
638 if(gs->buffer_bytes_left == 0) {
639 /* Get a new section if required */
640 if(gs->ps_input && gs->bytes_left == 0) {
641 struct record_list *ps_old = gs->ps_input;
642 gs->ps_input = ps_old->next;
643 if(ps_old->close && NULL != ps_old->fp)
645 g_free((char *) ps_old);
647 /* Have to seek at the beginning of each section */
648 if(gs->ps_input && gs->ps_input->seek_needed) {
649 fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
650 gs->ps_input->seek_needed = FALSE;
651 gs->bytes_left = gs->ps_input->len;
654 if(gs->bytes_left > MAX_BUFSIZE) {
655 gs->buffer_bytes_left =
656 fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
658 else if(gs->bytes_left > 0) {
659 gs->buffer_bytes_left =
660 fread(gs->input_buffer,
661 sizeof(char), gs->bytes_left, gs->ps_input->fp);
664 gs->buffer_bytes_left = 0;
666 if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
667 interpreter_failed(gs); /* Error occurred */
669 gs->input_buffer_ptr = gs->input_buffer;
670 gs->bytes_left -= gs->buffer_bytes_left;
673 if(gs->buffer_bytes_left > 0) {
674 /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
676 bytes_written = write(gs->interpreter_input,
677 gs->input_buffer_ptr, gs->buffer_bytes_left);
680 ps_document_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
682 interpreter_failed(gs);
684 else if(bytes_written == -1) {
685 if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
686 interpreter_failed(gs); /* Something bad happened */
690 gs->buffer_bytes_left -= bytes_written;
691 gs->input_buffer_ptr += bytes_written;
695 while(gs->ps_input && gs->buffer_bytes_left == 0);
697 signal(SIGPIPE, oldsig);
699 if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
700 if(gs->interpreter_input_id != 0) {
701 gdk_input_remove(gs->interpreter_input_id);
702 gs->interpreter_input_id = 0;
708 start_interpreter(PSDocument * gs)
710 int std_in[2] = { -1, -1 }; /* pipe to interp stdin */
711 int std_out[2]; /* pipe from interp stdout */
712 int std_err[2]; /* pipe from interp stderr */
715 #define NUM_GS_ARGS (NUM_ARGS - 20)
716 #define NUM_ALPHA_ARGS 10
718 char *argv[NUM_ARGS], *dir, *gv_env;
719 char **gs_args, **alpha_args = NULL;
725 stop_interpreter(gs);
727 if(gs->disable_start == TRUE)
730 /* set up the args... */
731 gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS);
732 for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++)
733 argv[argc] = gs_args[i];
735 if(gs->antialiased) {
736 if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
737 alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
739 alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(),
740 " ", NUM_ALPHA_ARGS);
741 for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++)
742 argv[argc] = alpha_args[i];
745 argv[argc++] = "-sDEVICE=x11";
746 argv[argc++] = "-dNOPAUSE";
747 argv[argc++] = "-dQUIET";
748 /* I assume we do _not_ want to change this... (: */
749 argv[argc++] = "-dSAFER";
751 /* set up the pipes */
752 if(gs->send_filename_to_gs) {
753 argv[argc++] = PS_DOCUMENT_GET_PS_FILE(gs);
755 argv[argc++] = "quit";
762 if(!gs->reading_from_pipe && !gs->send_filename_to_gs) {
763 if(pipe(std_in) == -1) {
764 g_critical("Unable to open pipe to Ghostscript.");
768 if(pipe(std_out) == -1) {
772 if(pipe(std_err) == -1) {
779 gs->interpreter_pid = fork();
780 switch (gs->interpreter_pid) {
796 if(!gs->reading_from_pipe) {
797 if(gs->send_filename_to_gs) {
799 /* just in case gs tries to read from stdin */
800 stdinfd = open("/dev/null", O_RDONLY);
813 gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld",
814 gdk_x11_drawable_get_xid(gs->pstarget),
815 gdk_x11_drawable_get_xid(gs->bpixmap));
818 /* change to directory where the input file is. This helps
819 * with postscript-files which include other files using
821 dir = g_path_get_dirname(gs->gs_filename);
825 execvp(argv[0], argv);
828 g_print("Unable to execute [%s]\n", argv[0]);
832 g_strfreev(alpha_args);
835 default: /* parent */
836 if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
839 /* use non-blocking IO for pipe to ghostscript */
840 result = fcntl(std_in[1], F_GETFL, 0);
841 fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
842 gs->interpreter_input = std_in[1];
845 gs->interpreter_input = -1;
848 gs->interpreter_output = std_out[0];
850 gs->interpreter_err = std_err[0];
851 gs->interpreter_output_id =
852 gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
853 gs->interpreter_error_id =
854 gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
861 stop_interpreter(PSDocument * gs)
863 if(gs->interpreter_pid > 0) {
865 kill(gs->interpreter_pid, SIGTERM);
866 while((wait(&status) == -1) && (errno == EINTR)) ;
867 gs->interpreter_pid = -1;
869 ps_document_cleanup(gs);
870 gs->gs_status = _("Interpreter failed.");
874 if(gs->interpreter_input >= 0) {
875 close(gs->interpreter_input);
876 gs->interpreter_input = -1;
877 if(gs->interpreter_input_id != 0) {
878 gdk_input_remove(gs->interpreter_input_id);
879 gs->interpreter_input_id = 0;
881 while(gs->ps_input) {
882 struct record_list *ps_old = gs->ps_input;
883 gs->ps_input = gs->ps_input->next;
884 if(ps_old->close && NULL != ps_old->fp)
886 g_free((char *) ps_old);
890 if(gs->interpreter_output >= 0) {
891 close(gs->interpreter_output);
892 gs->interpreter_output = -1;
893 if(gs->interpreter_output_id) {
894 gdk_input_remove(gs->interpreter_output_id);
895 gs->interpreter_output_id = 0;
899 if(gs->interpreter_err >= 0) {
900 close(gs->interpreter_err);
901 gs->interpreter_err = -1;
902 if(gs->interpreter_error_id) {
903 gdk_input_remove(gs->interpreter_error_id);
904 gs->interpreter_error_id = 0;
911 /* If file exists and is a regular file then return its length, else -1 */
913 file_length(const gchar * filename)
915 struct stat stat_rec;
917 if(filename && (stat(filename, &stat_rec) == 0)
918 && S_ISREG(stat_rec.st_mode))
919 return stat_rec.st_size;
924 /* Test if file exists, is a regular file and its length is > 0 */
926 file_readable(const char *filename)
928 return (file_length(filename) > 0);
932 * Decompress gs->gs_filename if necessary
933 * Set gs->filename_unc to the name of the uncompressed file or NULL.
934 * Error reporting via signal 'interpreter_message'
935 * Return name of input file to use or NULL on error..
938 check_filecompressed(PSDocument * gs)
942 gchar *filename, *filename_unc, *filename_err, *cmdline;
948 if((file = fopen(gs->gs_filename, "r"))
949 && (fread(buf, sizeof(gchar), 3, file) == 3)) {
950 if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
951 /* file is gzipped or compressed */
952 cmd = gtk_gs_defaults_get_ungzip_cmd();
954 else if(strncmp(buf, "BZh", 3) == 0) {
955 /* file is compressed with bzip2 */
956 cmd = gtk_gs_defaults_get_unbzip2_cmd();
963 return gs->gs_filename;
965 /* do the decompression */
966 filename = g_shell_quote(gs->gs_filename);
967 filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
968 if((fd = mkstemp(filename_unc)) < 0) {
969 g_free(filename_unc);
974 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
975 if((fd = mkstemp(filename_err)) < 0) {
976 g_free(filename_err);
977 g_free(filename_unc);
982 cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
983 filename, filename_unc, filename_err);
984 if((system(cmdline) == 0)
985 && file_readable(filename_unc)
986 && (file_length(filename_err) == 0)) {
987 /* sucessfully uncompressed file */
988 gs->gs_filename_unc = filename_unc;
992 g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
994 ps_document_emit_error_msg(gs, buf);
995 if(file_length(filename_err) > 0) {
997 if((err = fopen(filename_err, "r"))) {
998 /* print file to message window */
999 while(fgets(buf, 1024, err))
1000 ps_document_emit_error_msg(gs, buf);
1004 unlink(filename_unc);
1005 g_free(filename_unc);
1006 filename_unc = NULL;
1008 unlink(filename_err);
1009 g_free(filename_err);
1012 return filename_unc;
1016 * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
1017 * pdf file if necessary.
1018 * Set gs->filename_dsc to the name of the dsc file or NULL.
1019 * Error reporting via signal 'interpreter_message'.
1022 check_pdf(PSDocument * gs)
1025 gchar buf[1024], *filename;
1028 /* use uncompressed file as input if necessary */
1029 filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
1031 if((file = fopen(filename, "r"))
1032 && (fread(buf, sizeof(char), 5, file) == 5)
1033 && (strncmp(buf, "%PDF-", 5) == 0)) {
1034 /* we found a PDF file */
1035 gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
1036 filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1037 if((fd = mkstemp(filename_dsc)) < 0) {
1041 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1042 if((fd = mkstemp(filename_err)) < 0) {
1043 g_free(filename_dsc);
1047 fname = g_shell_quote(filename);
1048 cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1050 /* this command (sometimes?) prints error messages to stdout! */
1051 cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1054 if((system(cmdline) == 0) && file_readable(filename_dsc)) {
1057 filename = gs->gs_filename_dsc = filename_dsc;
1059 if(file_length(filename_err) > 0) {
1060 gchar *err_msg = " ";
1065 if((err = fopen(filename_err, "r"))) {
1067 /* print the content of the file to a message box */
1068 while(fgets(buf, 1024, err))
1069 err_msg = g_strconcat(err_msg, buf, NULL);
1071 /* FIXME The dialog is not yet set to modal, difficult to
1072 * get the parent of the dialog box here
1075 dialog = gtk_message_dialog_new(NULL,
1077 GTK_MESSAGE_WARNING,
1079 ("There was an error while scaning the file: %s \n%s"),
1080 gs->gs_filename, err_msg);
1082 gdk_color_parse("white", &color);
1083 gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1085 g_signal_connect(G_OBJECT(dialog), "response",
1086 G_CALLBACK(gtk_widget_destroy), NULL);
1088 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1089 gtk_widget_show(dialog);
1097 g_snprintf(buf, 1024,
1098 _("Error while converting pdf file %s:\n"), filename);
1099 ps_document_emit_error_msg(gs, buf);
1101 if(file_length(filename_err) > 0) {
1103 if((err = fopen(filename_err, "r"))) {
1104 /* print file to message window */
1105 while(fgets(buf, 1024, err))
1106 ps_document_emit_error_msg(gs, buf);
1109 unlink(filename_dsc);
1110 g_free(filename_dsc);
1113 unlink(filename_err);
1114 g_free(filename_err);
1122 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1123 /* never mind this patch: a properly working X server should take care of
1124 calculating the proper values. */
1128 # ifndef HAVE_XINERAMA
1129 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1132 dpy = (Display *) GDK_DISPLAY();
1133 if(XineramaIsActive(dpy)) {
1135 XineramaScreenInfo *head_info;
1136 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1137 /* fake it with dimensions of the first head for now */
1138 return 25.4 * head_info[0].width / gdk_screen_width_mm();
1141 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1150 # ifndef HAVE_XINERAMA
1151 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1154 dpy = (Display *) GDK_DISPLAY();
1155 if(XineramaIsActive(dpy)) {
1157 XineramaScreenInfo *head_info;
1158 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1159 /* fake it with dimensions of the first head for now */
1160 return 25.4 * head_info[0].height / gdk_screen_height_mm();
1163 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1172 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1178 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1180 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1182 /* Compute new size of window, sets xdpi and ydpi if necessary.
1183 * returns True if new window size is different */
1185 compute_size(PSDocument * gs)
1187 guint new_width = 1;
1188 guint new_height = 1;
1189 gboolean change = FALSE;
1192 /* width and height can be changed, calculate window size according */
1193 /* to xpdi and ydpi */
1194 orientation = ps_document_get_orientation(gs);
1196 switch (orientation) {
1197 case GTK_GS_ORIENTATION_PORTRAIT:
1198 case GTK_GS_ORIENTATION_UPSIDEDOWN:
1199 new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1200 new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1202 case GTK_GS_ORIENTATION_LANDSCAPE:
1203 case GTK_GS_ORIENTATION_SEASCAPE:
1204 new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1205 new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1209 change = (new_width != gs->width * gs->zoom_factor)
1210 || (new_height != gs->height * gs->zoom_factor);
1211 gs->width = (gint) (new_width * gs->zoom_factor);
1212 gs->height = (gint) (new_height * gs->zoom_factor);
1218 ps_document_enable_interpreter(PSDocument * gs)
1220 g_return_val_if_fail(gs != NULL, FALSE);
1221 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1223 if(!gs->gs_filename)
1226 gs->disable_start = FALSE;
1228 return start_interpreter(gs);
1231 /* publicly accessible functions */
1234 ps_document_get_type(void)
1236 static GType gs_type = 0;
1238 GTypeInfo gs_info = {
1239 sizeof(PSDocumentClass),
1240 (GBaseInitFunc) NULL,
1241 (GBaseFinalizeFunc) NULL,
1242 (GClassInitFunc) ps_document_class_init,
1243 (GClassFinalizeFunc) NULL,
1244 NULL, /* class_data */
1246 0, /* n_preallocs */
1247 (GInstanceInitFunc) ps_document_init
1250 static const GInterfaceInfo document_info =
1252 (GInterfaceInitFunc) ps_document_document_iface_init,
1257 gs_type = g_type_register_static(G_TYPE_OBJECT,
1258 "PSDocument", &gs_info, 0);
1260 g_type_add_interface_static (gs_type,
1270 * Show error message -> send signal "interpreter_message"
1273 ps_document_emit_error_msg(PSDocument * gs, const gchar * msg)
1275 gdk_pointer_ungrab(GDK_CURRENT_TIME);
1276 if(strstr(msg, "Error:")) {
1277 gs->gs_status = _("File is not a valid PostScript document.");
1278 ps_document_cleanup(gs);
1283 document_load(PSDocument * gs, const gchar * fname)
1285 g_return_val_if_fail(gs != NULL, FALSE);
1286 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1288 /* clean up previous document */
1289 ps_document_cleanup(gs);
1296 /* prepare this document */
1298 /* default values: no dsc information available */
1299 gs->structured_doc = FALSE;
1300 gs->send_filename_to_gs = TRUE;
1301 gs->current_page = -2;
1304 /* an absolute path */
1305 gs->gs_filename = g_strdup(fname);
1308 /* path relative to our cwd: make it absolute */
1309 gchar *cwd = g_get_current_dir();
1310 gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1314 if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1315 gs->send_filename_to_gs = FALSE;
1319 * We need to make sure that the file is loadable/exists!
1320 * otherwise we want to exit without loading new stuff...
1322 gchar *filename = NULL;
1324 if(!file_readable(fname)) {
1326 g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1327 ps_document_emit_error_msg(gs, buf);
1328 gs->gs_status = _("File is not readable.");
1331 filename = check_filecompressed(gs);
1333 filename = check_pdf(gs);
1336 if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1337 ps_document_cleanup(gs);
1341 /* we grab the vital statistics!!! */
1342 gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1344 g_object_notify (G_OBJECT (gs), "title");
1346 if(gs->doc == NULL) {
1347 /* File does not seem to be a Postscript one */
1349 g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1350 ps_document_emit_error_msg(gs, buf);
1351 ps_document_cleanup(gs);
1352 gs->gs_status = _("The file is not a PostScript document.");
1356 if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1357 (gs->doc->epsf && gs->doc->numpages > 1)) {
1358 gs->structured_doc = TRUE;
1359 gs->send_filename_to_gs = FALSE;
1362 /* We have to set up the orientation of the document */
1365 /* orientation can only be portrait, and landscape or none.
1366 This is the document default. A document can have
1367 pages in landscape and some in portrait */
1368 if(gs->override_orientation) {
1369 /* If the orientation should be override...
1370 then gs->orientation has already the correct
1371 value (it was set when the widget was created */
1376 /* Otherwise, set the proper orientation for the doc */
1377 gs->real_orientation = gs->doc->orientation;
1380 ps_document_set_page_size(gs, -1, gs->current_page);
1383 gs->gs_status = _("Document loaded.");
1390 ps_document_next_page(PSDocument * gs)
1394 g_return_val_if_fail(gs != NULL, FALSE);
1395 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1397 if(gs->interpreter_pid == 0) { /* no interpreter active */
1401 if(gs->busy) { /* interpreter is busy */
1407 event.xclient.type = ClientMessage;
1408 event.xclient.display = gdk_display;
1409 event.xclient.window = gs->message_window;
1410 event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1411 event.xclient.format = 32;
1413 gdk_error_trap_push();
1414 XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1416 gdk_error_trap_pop();
1422 ps_document_get_current_page(PSDocument * gs)
1424 g_return_val_if_fail(gs != NULL, -1);
1425 g_return_val_if_fail(GTK_IS_GS(gs), -1);
1427 return gs->current_page;
1431 ps_document_get_page_count(PSDocument * gs)
1433 if(!gs->gs_filename)
1437 if(gs->structured_doc)
1438 return gs->doc->numpages;
1447 ps_document_goto_page(PSDocument * gs, gint page)
1449 g_return_val_if_fail(gs != NULL, FALSE);
1450 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1452 if(!gs->gs_filename) {
1456 /* range checking... */
1460 if(gs->structured_doc && gs->doc) {
1461 if(page >= gs->doc->numpages)
1462 page = gs->doc->numpages - 1;
1464 if(page == gs->current_page && !gs->changed)
1467 gs->current_page = page;
1469 if(gs->doc->pages[page].orientation != NONE &&
1470 !gs->override_orientation &&
1471 gs->doc->pages[page].orientation != gs->real_orientation) {
1472 gs->real_orientation = gs->doc->pages[page].orientation;
1476 ps_document_set_page_size(gs, -1, page);
1478 gs->changed = FALSE;
1480 if(is_interpreter_ready(gs)) {
1481 ps_document_next_page(gs);
1484 ps_document_enable_interpreter(gs);
1485 send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1486 send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1489 send_ps(gs, gs->doc->pages[gs->current_page].begin,
1490 gs->doc->pages[gs->current_page].len, FALSE);
1493 /* Unstructured document */
1494 /* In the case of non structured documents,
1495 GS read the PS from the actual file (via command
1496 line. Hence, ggv only send a signal next page.
1497 If ghostview is not running it is usually because
1498 the last page of the file was displayed. In that
1499 case, ggv restarts GS again and the first page is displayed.
1501 if(page == gs->current_page && !gs->changed)
1504 ps_document_set_page_size(gs, -1, page);
1506 if(!is_interpreter_ready(gs))
1507 ps_document_enable_interpreter(gs);
1509 gs->current_page = page;
1511 ps_document_next_page(gs);
1517 * set pagesize sets the size from
1518 * if new_pagesize is -1, then it is set to either
1519 * a) the default settings of pageid, if they exist, or if pageid != -1.
1520 * b) the default setting of the document, if it exists.
1521 * c) the default setting of the widget.
1522 * otherwise, the new_pagesize is used as the pagesize
1525 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1531 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1533 g_return_val_if_fail(gs != NULL, FALSE);
1534 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1536 if(new_pagesize == -1) {
1537 if(gs->default_size > 0)
1538 new_pagesize = gs->default_size;
1539 if(!gs->override_size && gs->doc) {
1540 /* If we have a document:
1541 We use -- the page size (if specified)
1542 or the doc. size (if specified)
1543 or the page bbox (if specified)
1546 if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1547 (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1548 new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1550 else if(gs->doc->default_page_size != NULL) {
1551 new_pagesize = gs->doc->default_page_size - gs->doc->size;
1553 else if((pageid >= 0) &&
1554 (gs->doc->numpages > pageid) &&
1556 (gs->doc->pages[pageid].boundingbox[URX] >
1557 gs->doc->pages[pageid].boundingbox[LLX]) &&
1558 (gs->doc->pages[pageid].boundingbox[URY] >
1559 gs->doc->pages[pageid].boundingbox[LLY])) {
1562 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1563 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1569 /* Compute bounding box */
1570 if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) { /* epsf or bbox */
1573 (gs->doc->pages[pageid].boundingbox[URX] >
1574 gs->doc->pages[pageid].boundingbox[LLX])
1575 && (gs->doc->pages[pageid].boundingbox[URY] >
1576 gs->doc->pages[pageid].boundingbox[LLY])) {
1578 new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1579 new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1580 new_urx = gs->doc->pages[pageid].boundingbox[URX];
1581 new_ury = gs->doc->pages[pageid].boundingbox[URY];
1583 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1584 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1586 new_llx = gs->doc->boundingbox[LLX];
1587 new_lly = gs->doc->boundingbox[LLY];
1588 new_urx = gs->doc->boundingbox[URX];
1589 new_ury = gs->doc->boundingbox[URY];
1593 if(new_pagesize < 0)
1594 new_pagesize = gs->default_size;
1595 new_llx = new_lly = 0;
1596 if(gs->doc && !gs->override_size && gs->doc->size &&
1597 (new_pagesize < gs->doc->numsizes)) {
1598 new_urx = gs->doc->size[new_pagesize].width;
1599 new_ury = gs->doc->size[new_pagesize].height;
1602 new_urx = papersizes[new_pagesize].width;
1603 new_ury = papersizes[new_pagesize].height;
1607 if(new_urx <= new_llx)
1608 new_urx = papersizes[12].width;
1609 if(new_ury <= new_lly)
1610 new_ury = papersizes[12].height;
1612 /* If bounding box changed, setup for new size. */
1613 /* ps_document_disable_interpreter (gs); */
1614 if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1615 (new_urx != gs->urx) || (new_ury != gs->ury)) {
1632 ps_document_zoom_to_fit(PSDocument * gs, gboolean fit_width)
1636 guint avail_w, avail_h;
1638 g_return_val_if_fail(gs != NULL, 0.0);
1639 g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1641 avail_w = (gs->avail_w > 0) ? gs->avail_w : gs->width;
1642 avail_h = (gs->avail_h > 0) ? gs->avail_h : gs->height;
1644 new_zoom = ((gfloat) avail_w) / ((gfloat) gs->width) * gs->zoom_factor;
1646 new_y = new_zoom * ((gfloat) gs->height) / gs->zoom_factor;
1648 new_zoom = ((gfloat) avail_h) / ((gfloat) gs->height) * gs->zoom_factor;
1655 ps_document_set_zoom(PSDocument * gs, gfloat zoom)
1657 g_return_if_fail(gs != NULL);
1658 g_return_if_fail(GTK_IS_GS(gs));
1660 switch (gs->zoom_mode) {
1661 case GTK_GS_ZOOM_FIT_WIDTH:
1662 zoom = ps_document_zoom_to_fit(gs, TRUE);
1664 case GTK_GS_ZOOM_FIT_PAGE:
1665 zoom = ps_document_zoom_to_fit(gs, FALSE);
1667 case GTK_GS_ZOOM_ABSOLUTE:
1672 if(fabs(gs->zoom_factor - zoom) > 0.001) {
1673 gs->zoom_factor = zoom;
1678 ps_document_goto_page(gs, gs->current_page);
1682 ps_document_load (EvDocument *document,
1689 filename = g_filename_from_uri (uri, NULL, error);
1693 result = document_load (PS_DOCUMENT (document), filename);
1701 ps_document_save (EvDocument *document,
1705 g_warning ("ps_document_save not implemented"); /* FIXME */
1710 ps_document_get_n_pages (EvDocument *document)
1712 return ps_document_get_page_count (PS_DOCUMENT (document));
1716 ps_document_set_page (EvDocument *document,
1719 ps_document_goto_page (PS_DOCUMENT (document), page);
1723 ps_document_get_page (EvDocument *document)
1725 return ps_document_get_current_page (PS_DOCUMENT (document));
1729 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
1731 PSDocument *gs = (PSDocument *) data;
1733 if(event->type != GDK_CLIENT_EVENT)
1736 if (event->client.message_type == gs_class->page_atom) {
1738 ev_document_changed (EV_DOCUMENT (gs));
1745 ps_document_set_target (EvDocument *document,
1746 GdkDrawable *target)
1748 PSDocument *gs = PS_DOCUMENT (document);
1752 gs->pstarget = target;
1755 gdk_window_get_user_data (gs->pstarget, &data);
1756 g_return_if_fail (GTK_IS_WIDGET (data));
1758 widget = GTK_WIDGET (data);
1759 g_signal_connect (widget, "event",
1760 G_CALLBACK (ps_document_widget_event),
1764 ps_document_goto_page (gs, gs->current_page);
1768 ps_document_set_scale (EvDocument *document,
1771 ps_document_set_zoom (PS_DOCUMENT (document), scale);
1775 ps_document_set_page_offset (EvDocument *document,
1779 PSDocument *gs = PS_DOCUMENT (document);
1781 gs->page_x_offset = x;
1782 gs->page_y_offset = y;
1786 ps_document_get_page_size (EvDocument *document,
1791 /* Post script documents never vary in size */
1793 PSDocument *gs = PS_DOCUMENT (document);
1800 *height = gs->height;
1805 ps_document_render (EvDocument *document,
1811 PSDocument *gs = PS_DOCUMENT (document);
1816 if (gs->pstarget == NULL ||
1817 gs->bpixmap == NULL) {
1821 page.x = gs->page_x_offset;
1822 page.y = gs->page_y_offset;
1823 page.width = gs->width;
1824 page.height = gs->height;
1828 draw.width = clip_width;
1829 draw.height = clip_height;
1831 gc = gdk_gc_new (gs->pstarget);
1833 gdk_draw_drawable (gs->pstarget, gc,
1835 draw.x - page.x, draw.y - page.y,
1837 draw.width, draw.height);
1839 g_object_unref (gc);
1843 ps_document_get_text (EvDocument *document, GdkRectangle *rect)
1845 g_warning ("ps_document_get_text not implemented"); /* FIXME ? */
1850 ps_document_get_link (EvDocument *document,
1858 ps_document_document_iface_init (EvDocumentIface *iface)
1860 iface->load = ps_document_load;
1861 iface->save = ps_document_save;
1862 iface->get_text = ps_document_get_text;
1863 iface->get_link = ps_document_get_link;
1864 iface->get_n_pages = ps_document_get_n_pages;
1865 iface->set_page = ps_document_set_page;
1866 iface->get_page = ps_document_get_page;
1867 iface->set_scale = ps_document_set_scale;
1868 iface->set_target = ps_document_set_target;
1869 iface->set_page_offset = ps_document_set_page_offset;
1870 iface->get_page_size = ps_document_get_page_size;
1871 iface->render = ps_document_render;