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 "ev-document.h"
155 #include "gsdefaults.h"
161 /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
162 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
163 # define O_NONBLOCK O_NDELAY
166 #define GTK_GS_WATCH_INTERVAL 1000
167 #define GTK_GS_WATCH_TIMEOUT 2
169 #define MAX_BUFSIZE 1024
171 #define GTK_GS_IS_COMPRESSED(gs) (GTK_GS(gs)->gs_filename_unc != NULL)
172 #define GTK_GS_GET_PS_FILE(gs) (GTK_GS_IS_COMPRESSED(gs) ? \
173 GTK_GS(gs)->gs_filename_unc : \
174 GTK_GS(gs)->gs_filename)
176 enum { INTERPRETER_MESSAGE, INTERPRETER_ERROR, LAST_SIGNAL };
178 /* structure to describe section of file to send to ghostscript */
183 gboolean seek_needed;
185 struct record_list *next;
188 static gboolean broken_pipe = FALSE;
196 /* Forward declarations */
197 static void gtk_gs_init(GtkGS * gs);
198 static void gtk_gs_class_init(GtkGSClass * klass);
199 static void gtk_gs_emit_error_msg(GtkGS * gs, const gchar * msg);
200 static void gtk_gs_finalize(GObject * object);
201 static void send_ps(GtkGS * gs, long begin, unsigned int len, gboolean close);
202 static void set_up_page(GtkGS * gs);
203 static void close_pipe(int p[2]);
204 static void interpreter_failed(GtkGS * gs);
205 static float compute_xdpi(void);
206 static float compute_ydpi(void);
207 static gboolean compute_size(GtkGS * gs);
208 static void output(gpointer data, gint source, GdkInputCondition condition);
209 static void input(gpointer data, gint source, GdkInputCondition condition);
210 static void stop_interpreter(GtkGS * gs);
211 static gint start_interpreter(GtkGS * gs);
212 gboolean computeSize(void);
213 static gboolean gtk_gs_set_page_size(GtkGS * gs, gint new_pagesize, gint pageid);
214 static void ps_document_document_iface_init (EvDocumentIface *iface);
216 static GObjectClass *parent_class = NULL;
218 static GtkGSClass *gs_class = NULL;
221 gtk_gs_init(GtkGS * gs)
225 gs->current_page = -2;
226 gs->disable_start = FALSE;
227 gs->interpreter_pid = -1;
233 gs->gs_scanstyle = 0;
235 gs->gs_filename_dsc = 0;
236 gs->gs_filename_unc = 0;
240 gs->structured_doc = FALSE;
241 gs->reading_from_pipe = FALSE;
242 gs->send_filename_to_gs = FALSE;
247 gs->interpreter_input = -1;
248 gs->interpreter_output = -1;
249 gs->interpreter_err = -1;
250 gs->interpreter_input_id = 0;
251 gs->interpreter_output_id = 0;
252 gs->interpreter_error_id = 0;
255 gs->input_buffer = NULL;
256 gs->input_buffer_ptr = NULL;
258 gs->buffer_bytes_left = 0;
264 gs->xdpi = compute_xdpi();
265 gs->ydpi = compute_ydpi();
269 gs->right_margin = 0;
270 gs->bottom_margin = 0;
272 /* Set user defined defaults */
273 gs->override_orientation = gtk_gs_defaults_get_override_orientation();
274 gs->fallback_orientation = gtk_gs_defaults_get_orientation();
275 gs->zoom_factor = gtk_gs_defaults_get_zoom_factor();
276 gs->default_size = gtk_gs_defaults_get_size();
277 gs->antialiased = gtk_gs_defaults_get_antialiased();
278 gs->override_size = gtk_gs_defaults_get_override_size();
279 gs->respect_eof = gtk_gs_defaults_get_respect_eof();
280 gs->zoom_mode = gtk_gs_defaults_get_zoom_mode();
282 gs->gs_status = _("No document loaded.");
286 gtk_gs_class_init(GtkGSClass * klass)
288 GObjectClass *object_class;
290 object_class = (GObjectClass *) klass;
291 parent_class = gtk_type_class(gtk_widget_get_type());
294 object_class->finalize = gtk_gs_finalize;
297 klass->gs_atom = gdk_atom_intern("GHOSTVIEW", FALSE);
298 klass->gs_colors_atom = gdk_atom_intern("GHOSTVIEW_COLORS", FALSE);
299 klass->next_atom = gdk_atom_intern("NEXT", FALSE);
300 klass->page_atom = gdk_atom_intern("PAGE", FALSE);
301 klass->done_atom = gdk_atom_intern("DONE", FALSE);
302 klass->string_atom = gdk_atom_intern("STRING", FALSE);
304 gtk_gs_defaults_load();
307 /* Clean all memory and temporal files */
309 gtk_gs_cleanup(GtkGS * gs)
311 g_return_if_fail(gs != NULL);
312 g_return_if_fail(GTK_IS_GS(gs));
314 stop_interpreter(gs);
317 fclose(gs->gs_psfile);
318 gs->gs_psfile = NULL;
320 if(gs->gs_filename) {
321 g_free(gs->gs_filename);
322 gs->gs_filename = NULL;
328 if(gs->gs_filename_dsc) {
329 unlink(gs->gs_filename_dsc);
330 g_free(gs->gs_filename_dsc);
331 gs->gs_filename_dsc = NULL;
333 if(gs->gs_filename_unc) {
334 unlink(gs->gs_filename_unc);
335 g_free(gs->gs_filename_unc);
336 gs->gs_filename_unc = NULL;
338 gs->current_page = -1;
348 gtk_gs_finalize(GObject * object)
352 g_return_if_fail(object != NULL);
353 g_return_if_fail(GTK_IS_GS(object));
359 if(gs->input_buffer) {
360 g_free(gs->input_buffer);
361 gs->input_buffer = NULL;
364 (*G_OBJECT_CLASS(parent_class)->finalize) (object);
368 send_ps(GtkGS * gs, long begin, unsigned int len, gboolean close)
370 struct record_list *ps_new;
372 if(gs->interpreter_input < 0) {
373 g_critical("No pipe to gs: error in send_ps().");
377 ps_new = (struct record_list *) g_malloc(sizeof(struct record_list));
378 ps_new->fp = gs->gs_psfile;
379 ps_new->begin = begin;
381 ps_new->seek_needed = TRUE;
382 ps_new->close = close;
385 if(gs->input_buffer == NULL) {
386 gs->input_buffer = g_malloc(MAX_BUFSIZE);
389 if(gs->ps_input == NULL) {
390 gs->input_buffer_ptr = gs->input_buffer;
391 gs->bytes_left = len;
392 gs->buffer_bytes_left = 0;
393 gs->ps_input = ps_new;
394 gs->interpreter_input_id =
395 gdk_input_add(gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
398 struct record_list *p = gs->ps_input;
399 while(p->next != NULL) {
407 gtk_gs_get_orientation(GtkGS * gs)
409 g_return_val_if_fail(gs != NULL, -1);
410 g_return_val_if_fail(GTK_IS_GS(gs), -1);
413 if(gs->structured_doc) {
414 if(gs->doc->pages[MAX(gs->current_page, 0)].orientation !=
415 GTK_GS_ORIENTATION_NONE)
416 gs->real_orientation =
417 gs->doc->pages[MAX(gs->current_page, 0)].orientation;
419 gs->real_orientation = gs->doc->default_page_orientation;
422 if(gs->real_orientation == GTK_GS_ORIENTATION_NONE)
423 gs->real_orientation = gs->doc->orientation;
426 if(gs->override_orientation ||
427 gs->real_orientation == GTK_GS_ORIENTATION_NONE)
428 return gs->fallback_orientation;
430 return gs->real_orientation;
434 set_up_page(GtkGS * gs)
438 //GdkColormap *colormap;
440 GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */
441 GdkColormap *colormap;
447 if (gs->pstarget == NULL)
450 /* Do we have to check if the actual geometry changed? */
452 stop_interpreter(gs);
454 orientation = gtk_gs_get_orientation(gs);
456 if(compute_size(gs)) {
459 /* clear new pixmap (set to white) */
460 fill = gdk_gc_new(gs->pstarget);
462 colormap = gdk_drawable_get_colormap(gs->pstarget);
463 gdk_color_alloc (colormap, &white);
464 gdk_gc_set_foreground(fill, &white);
466 if(gs->width > 0 && gs->height > 0) {
468 gdk_drawable_unref(gs->bpixmap);
472 gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1);
474 gdk_draw_rectangle(gs->bpixmap, fill, TRUE,
475 0, 0, gs->width, gs->height);
478 gdk_draw_rectangle(gs->pstarget, fill, TRUE,
479 0, 0, gs->width, gs->height);
488 /* gs needs floating point parameters with '.' as decimal point
489 * while some (european) locales use ',' instead, so we set the
490 * locale for this snprintf to "C".
492 savelocale = setlocale(LC_NUMERIC, "C");
495 g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
502 gs->xdpi * gs->zoom_factor,
503 gs->ydpi * gs->zoom_factor,
505 gs->bottom_margin, gs->right_margin, gs->top_margin);
508 setlocale(LC_NUMERIC, savelocale);
510 gdk_property_change(gs->pstarget,
512 gs_class->string_atom,
513 8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
527 is_interpreter_ready(GtkGS * gs)
529 return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
533 interpreter_failed(GtkGS * gs)
535 stop_interpreter(gs);
539 output(gpointer data, gint source, GdkInputCondition condition)
541 char buf[MAX_BUFSIZE + 1], *msg;
543 GtkGS *gs = GTK_GS(data);
545 if(source == gs->interpreter_output) {
546 bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
547 if(bytes == 0) { /* EOF occurred */
548 close(gs->interpreter_output);
549 gs->interpreter_output = -1;
550 gdk_input_remove(gs->interpreter_output_id);
553 else if(bytes == -1) {
555 interpreter_failed(gs);
558 if(gs->interpreter_err == -1) {
559 stop_interpreter(gs);
562 else if(source == gs->interpreter_err) {
563 bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
564 if(bytes == 0) { /* EOF occurred */
565 close(gs->interpreter_err);
566 gs->interpreter_err = -1;
567 gdk_input_remove(gs->interpreter_error_id);
570 else if(bytes == -1) {
572 interpreter_failed(gs);
575 if(gs->interpreter_output == -1) {
576 stop_interpreter(gs);
582 gtk_gs_emit_error_msg (gs, msg);
587 input(gpointer data, gint source, GdkInputCondition condition)
589 GtkGS *gs = GTK_GS(data);
591 void (*oldsig) (int);
592 oldsig = signal(SIGPIPE, catchPipe);
595 if(gs->buffer_bytes_left == 0) {
596 /* Get a new section if required */
597 if(gs->ps_input && gs->bytes_left == 0) {
598 struct record_list *ps_old = gs->ps_input;
599 gs->ps_input = ps_old->next;
600 if(ps_old->close && NULL != ps_old->fp)
602 g_free((char *) ps_old);
604 /* Have to seek at the beginning of each section */
605 if(gs->ps_input && gs->ps_input->seek_needed) {
606 fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
607 gs->ps_input->seek_needed = FALSE;
608 gs->bytes_left = gs->ps_input->len;
611 if(gs->bytes_left > MAX_BUFSIZE) {
612 gs->buffer_bytes_left =
613 fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
615 else if(gs->bytes_left > 0) {
616 gs->buffer_bytes_left =
617 fread(gs->input_buffer,
618 sizeof(char), gs->bytes_left, gs->ps_input->fp);
621 gs->buffer_bytes_left = 0;
623 if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
624 interpreter_failed(gs); /* Error occurred */
626 gs->input_buffer_ptr = gs->input_buffer;
627 gs->bytes_left -= gs->buffer_bytes_left;
630 if(gs->buffer_bytes_left > 0) {
631 /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
633 bytes_written = write(gs->interpreter_input,
634 gs->input_buffer_ptr, gs->buffer_bytes_left);
637 gtk_gs_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
639 interpreter_failed(gs);
641 else if(bytes_written == -1) {
642 if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
643 interpreter_failed(gs); /* Something bad happened */
647 gs->buffer_bytes_left -= bytes_written;
648 gs->input_buffer_ptr += bytes_written;
652 while(gs->ps_input && gs->buffer_bytes_left == 0);
654 signal(SIGPIPE, oldsig);
656 if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
657 if(gs->interpreter_input_id != 0) {
658 gdk_input_remove(gs->interpreter_input_id);
659 gs->interpreter_input_id = 0;
665 start_interpreter(GtkGS * gs)
667 int std_in[2] = { -1, -1 }; /* pipe to interp stdin */
668 int std_out[2]; /* pipe from interp stdout */
669 int std_err[2]; /* pipe from interp stderr */
672 #define NUM_GS_ARGS (NUM_ARGS - 20)
673 #define NUM_ALPHA_ARGS 10
675 char *argv[NUM_ARGS], *dir, *gv_env;
676 char **gs_args, **alpha_args = NULL;
682 stop_interpreter(gs);
684 if(gs->disable_start == TRUE)
687 /* set up the args... */
688 gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS);
689 for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++)
690 argv[argc] = gs_args[i];
692 if(gs->antialiased) {
693 if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
694 alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
696 alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(),
697 " ", NUM_ALPHA_ARGS);
698 for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++)
699 argv[argc] = alpha_args[i];
702 argv[argc++] = "-sDEVICE=x11";
703 argv[argc++] = "-dNOPAUSE";
704 argv[argc++] = "-dQUIET";
705 /* I assume we do _not_ want to change this... (: */
706 argv[argc++] = "-dSAFER";
708 /* set up the pipes */
709 if(gs->send_filename_to_gs) {
710 argv[argc++] = GTK_GS_GET_PS_FILE(gs);
712 argv[argc++] = "quit";
719 if(!gs->reading_from_pipe && !gs->send_filename_to_gs) {
720 if(pipe(std_in) == -1) {
721 g_critical("Unable to open pipe to Ghostscript.");
725 if(pipe(std_out) == -1) {
729 if(pipe(std_err) == -1) {
736 gs->interpreter_pid = fork();
737 switch (gs->interpreter_pid) {
753 if(!gs->reading_from_pipe) {
754 if(gs->send_filename_to_gs) {
756 /* just in case gs tries to read from stdin */
757 stdinfd = open("/dev/null", O_RDONLY);
770 gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld",
771 gdk_x11_drawable_get_xid(gs->pstarget),
772 gdk_x11_drawable_get_xid(gs->bpixmap));
775 /* change to directory where the input file is. This helps
776 * with postscript-files which include other files using
778 dir = g_path_get_dirname(gs->gs_filename);
782 execvp(argv[0], argv);
785 g_print("Unable to execute [%s]\n", argv[0]);
789 g_strfreev(alpha_args);
792 default: /* parent */
793 if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
796 /* use non-blocking IO for pipe to ghostscript */
797 result = fcntl(std_in[1], F_GETFL, 0);
798 fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
799 gs->interpreter_input = std_in[1];
802 gs->interpreter_input = -1;
805 gs->interpreter_output = std_out[0];
807 gs->interpreter_err = std_err[0];
808 gs->interpreter_output_id =
809 gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
810 gs->interpreter_error_id =
811 gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
818 stop_interpreter(GtkGS * gs)
820 if(gs->interpreter_pid > 0) {
822 kill(gs->interpreter_pid, SIGTERM);
823 while((wait(&status) == -1) && (errno == EINTR)) ;
824 gs->interpreter_pid = -1;
827 gs->gs_status = _("Interpreter failed.");
831 if(gs->interpreter_input >= 0) {
832 close(gs->interpreter_input);
833 gs->interpreter_input = -1;
834 if(gs->interpreter_input_id != 0) {
835 gdk_input_remove(gs->interpreter_input_id);
836 gs->interpreter_input_id = 0;
838 while(gs->ps_input) {
839 struct record_list *ps_old = gs->ps_input;
840 gs->ps_input = gs->ps_input->next;
841 if(ps_old->close && NULL != ps_old->fp)
843 g_free((char *) ps_old);
847 if(gs->interpreter_output >= 0) {
848 close(gs->interpreter_output);
849 gs->interpreter_output = -1;
850 if(gs->interpreter_output_id) {
851 gdk_input_remove(gs->interpreter_output_id);
852 gs->interpreter_output_id = 0;
856 if(gs->interpreter_err >= 0) {
857 close(gs->interpreter_err);
858 gs->interpreter_err = -1;
859 if(gs->interpreter_error_id) {
860 gdk_input_remove(gs->interpreter_error_id);
861 gs->interpreter_error_id = 0;
868 /* If file exists and is a regular file then return its length, else -1 */
870 file_length(const gchar * filename)
872 struct stat stat_rec;
874 if(filename && (stat(filename, &stat_rec) == 0)
875 && S_ISREG(stat_rec.st_mode))
876 return stat_rec.st_size;
881 /* Test if file exists, is a regular file and its length is > 0 */
883 file_readable(const char *filename)
885 return (file_length(filename) > 0);
889 * Decompress gs->gs_filename if necessary
890 * Set gs->filename_unc to the name of the uncompressed file or NULL.
891 * Error reporting via signal 'interpreter_message'
892 * Return name of input file to use or NULL on error..
895 check_filecompressed(GtkGS * gs)
899 gchar *filename, *filename_unc, *filename_err, *cmdline;
905 if((file = fopen(gs->gs_filename, "r"))
906 && (fread(buf, sizeof(gchar), 3, file) == 3)) {
907 if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
908 /* file is gzipped or compressed */
909 cmd = gtk_gs_defaults_get_ungzip_cmd();
911 else if(strncmp(buf, "BZh", 3) == 0) {
912 /* file is compressed with bzip2 */
913 cmd = gtk_gs_defaults_get_unbzip2_cmd();
920 return gs->gs_filename;
922 /* do the decompression */
923 filename = g_shell_quote(gs->gs_filename);
924 filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
925 if((fd = mkstemp(filename_unc)) < 0) {
926 g_free(filename_unc);
931 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
932 if((fd = mkstemp(filename_err)) < 0) {
933 g_free(filename_err);
934 g_free(filename_unc);
939 cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
940 filename, filename_unc, filename_err);
941 if((system(cmdline) == 0)
942 && file_readable(filename_unc)
943 && (file_length(filename_err) == 0)) {
944 /* sucessfully uncompressed file */
945 gs->gs_filename_unc = filename_unc;
949 g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
951 gtk_gs_emit_error_msg(gs, buf);
952 if(file_length(filename_err) > 0) {
954 if((err = fopen(filename_err, "r"))) {
955 /* print file to message window */
956 while(fgets(buf, 1024, err))
957 gtk_gs_emit_error_msg(gs, buf);
961 unlink(filename_unc);
962 g_free(filename_unc);
965 unlink(filename_err);
966 g_free(filename_err);
973 * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
974 * pdf file if necessary.
975 * Set gs->filename_dsc to the name of the dsc file or NULL.
976 * Error reporting via signal 'interpreter_message'.
979 check_pdf(GtkGS * gs)
982 gchar buf[1024], *filename;
985 /* use uncompressed file as input if necessary */
986 filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
988 if((file = fopen(filename, "r"))
989 && (fread(buf, sizeof(char), 5, file) == 5)
990 && (strncmp(buf, "%PDF-", 5) == 0)) {
991 /* we found a PDF file */
992 gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
993 filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
994 if((fd = mkstemp(filename_dsc)) < 0) {
998 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
999 if((fd = mkstemp(filename_err)) < 0) {
1000 g_free(filename_dsc);
1004 fname = g_shell_quote(filename);
1005 cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1007 /* this command (sometimes?) prints error messages to stdout! */
1008 cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1011 if((system(cmdline) == 0) && file_readable(filename_dsc)) {
1014 filename = gs->gs_filename_dsc = filename_dsc;
1016 if(file_length(filename_err) > 0) {
1017 gchar *err_msg = " ";
1022 if((err = fopen(filename_err, "r"))) {
1024 /* print the content of the file to a message box */
1025 while(fgets(buf, 1024, err))
1026 err_msg = g_strconcat(err_msg, buf, NULL);
1028 /* FIXME The dialog is not yet set to modal, difficult to
1029 * get the parent of the dialog box here
1032 dialog = gtk_message_dialog_new(NULL,
1034 GTK_MESSAGE_WARNING,
1036 ("There was an error while scaning the file: %s \n%s"),
1037 gs->gs_filename, err_msg);
1039 gdk_color_parse("white", &color);
1040 gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1042 g_signal_connect(G_OBJECT(dialog), "response",
1043 G_CALLBACK(gtk_widget_destroy), NULL);
1045 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1046 gtk_widget_show(dialog);
1054 g_snprintf(buf, 1024,
1055 _("Error while converting pdf file %s:\n"), filename);
1056 gtk_gs_emit_error_msg(gs, buf);
1058 if(file_length(filename_err) > 0) {
1060 if((err = fopen(filename_err, "r"))) {
1061 /* print file to message window */
1062 while(fgets(buf, 1024, err))
1063 gtk_gs_emit_error_msg(gs, buf);
1066 unlink(filename_dsc);
1067 g_free(filename_dsc);
1070 unlink(filename_err);
1071 g_free(filename_err);
1079 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1080 /* never mind this patch: a properly working X server should take care of
1081 calculating the proper values. */
1085 # ifndef HAVE_XINERAMA
1086 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1089 dpy = (Display *) GDK_DISPLAY();
1090 if(XineramaIsActive(dpy)) {
1092 XineramaScreenInfo *head_info;
1093 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1094 /* fake it with dimensions of the first head for now */
1095 return 25.4 * head_info[0].width / gdk_screen_width_mm();
1098 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1107 # ifndef HAVE_XINERAMA
1108 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1111 dpy = (Display *) GDK_DISPLAY();
1112 if(XineramaIsActive(dpy)) {
1114 XineramaScreenInfo *head_info;
1115 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1116 /* fake it with dimensions of the first head for now */
1117 return 25.4 * head_info[0].height / gdk_screen_height_mm();
1120 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1129 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1135 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1137 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1139 /* Compute new size of window, sets xdpi and ydpi if necessary.
1140 * returns True if new window size is different */
1142 compute_size(GtkGS * gs)
1144 guint new_width = 1;
1145 guint new_height = 1;
1146 gboolean change = FALSE;
1149 /* width and height can be changed, calculate window size according */
1150 /* to xpdi and ydpi */
1151 orientation = gtk_gs_get_orientation(gs);
1153 switch (orientation) {
1154 case GTK_GS_ORIENTATION_PORTRAIT:
1155 case GTK_GS_ORIENTATION_UPSIDEDOWN:
1156 new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1157 new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1159 case GTK_GS_ORIENTATION_LANDSCAPE:
1160 case GTK_GS_ORIENTATION_SEASCAPE:
1161 new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1162 new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1166 change = (new_width != gs->width * gs->zoom_factor)
1167 || (new_height != gs->height * gs->zoom_factor);
1168 gs->width = (gint) (new_width * gs->zoom_factor);
1169 gs->height = (gint) (new_height * gs->zoom_factor);
1175 gtk_gs_enable_interpreter(GtkGS * gs)
1177 g_return_val_if_fail(gs != NULL, FALSE);
1178 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1180 if(!gs->gs_filename)
1183 gs->disable_start = FALSE;
1185 return start_interpreter(gs);
1188 /* publicly accessible functions */
1191 gtk_gs_get_type(void)
1193 static GType gs_type = 0;
1195 GTypeInfo gs_info = {
1197 (GBaseInitFunc) NULL,
1198 (GBaseFinalizeFunc) NULL,
1199 (GClassInitFunc) gtk_gs_class_init,
1200 (GClassFinalizeFunc) NULL,
1201 NULL, /* class_data */
1203 0, /* n_preallocs */
1204 (GInstanceInitFunc) gtk_gs_init
1207 static const GInterfaceInfo document_info =
1209 (GInterfaceInitFunc) ps_document_document_iface_init,
1214 gs_type = g_type_register_static(G_TYPE_OBJECT,
1215 "GtkGS", &gs_info, 0);
1217 g_type_add_interface_static (gs_type,
1227 * Show error message -> send signal "interpreter_message"
1230 gtk_gs_emit_error_msg(GtkGS * gs, const gchar * msg)
1232 gdk_pointer_ungrab(GDK_CURRENT_TIME);
1233 if(strstr(msg, "Error:")) {
1234 gs->gs_status = _("File is not a valid PostScript document.");
1240 gtk_gs_load(GtkGS * gs, const gchar * fname)
1242 g_return_val_if_fail(gs != NULL, FALSE);
1243 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1245 /* clean up previous document */
1253 /* prepare this document */
1255 /* default values: no dsc information available */
1256 gs->structured_doc = FALSE;
1257 gs->send_filename_to_gs = TRUE;
1258 gs->current_page = -2;
1261 /* an absolute path */
1262 gs->gs_filename = g_strdup(fname);
1265 /* path relative to our cwd: make it absolute */
1266 gchar *cwd = g_get_current_dir();
1267 gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1271 if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1272 gs->send_filename_to_gs = FALSE;
1276 * We need to make sure that the file is loadable/exists!
1277 * otherwise we want to exit without loading new stuff...
1279 gchar *filename = NULL;
1281 if(!file_readable(fname)) {
1283 g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1284 gtk_gs_emit_error_msg(gs, buf);
1285 gs->gs_status = _("File is not readable.");
1288 filename = check_filecompressed(gs);
1290 filename = check_pdf(gs);
1293 if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1298 /* we grab the vital statistics!!! */
1299 gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1301 if(gs->doc == NULL) {
1302 /* File does not seem to be a Postscript one */
1304 g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1305 gtk_gs_emit_error_msg(gs, buf);
1307 gs->gs_status = _("The file is not a PostScript document.");
1311 if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1312 (gs->doc->epsf && gs->doc->numpages > 1)) {
1313 gs->structured_doc = TRUE;
1314 gs->send_filename_to_gs = FALSE;
1317 /* We have to set up the orientation of the document */
1320 /* orientation can only be portrait, and landscape or none.
1321 This is the document default. A document can have
1322 pages in landscape and some in portrait */
1323 if(gs->override_orientation) {
1324 /* If the orientation should be override...
1325 then gs->orientation has already the correct
1326 value (it was set when the widget was created */
1331 /* Otherwise, set the proper orientation for the doc */
1332 gs->real_orientation = gs->doc->orientation;
1335 gtk_gs_set_page_size(gs, -1, gs->current_page);
1338 gs->gs_status = _("Document loaded.");
1345 gtk_gs_next_page(GtkGS * gs)
1349 g_return_val_if_fail(gs != NULL, FALSE);
1350 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1352 if(gs->interpreter_pid == 0) { /* no interpreter active */
1356 if(gs->busy) { /* interpreter is busy */
1362 event.xclient.type = ClientMessage;
1363 event.xclient.display = gdk_display;
1364 event.xclient.window = gs->message_window;
1365 event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1366 event.xclient.format = 32;
1368 gdk_error_trap_push();
1369 XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1371 gdk_error_trap_pop();
1377 gtk_gs_get_current_page(GtkGS * gs)
1379 g_return_val_if_fail(gs != NULL, -1);
1380 g_return_val_if_fail(GTK_IS_GS(gs), -1);
1382 return gs->current_page;
1386 gtk_gs_get_page_count(GtkGS * gs)
1388 if(!gs->gs_filename)
1392 if(gs->structured_doc)
1393 return gs->doc->numpages;
1402 gtk_gs_goto_page(GtkGS * gs, gint page)
1404 g_return_val_if_fail(gs != NULL, FALSE);
1405 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1407 if(!gs->gs_filename) {
1411 /* range checking... */
1415 if(gs->structured_doc && gs->doc) {
1416 if(page >= gs->doc->numpages)
1417 page = gs->doc->numpages - 1;
1419 if(page == gs->current_page && !gs->changed)
1422 gs->current_page = page;
1424 if(gs->doc->pages[page].orientation != NONE &&
1425 !gs->override_orientation &&
1426 gs->doc->pages[page].orientation != gs->real_orientation) {
1427 gs->real_orientation = gs->doc->pages[page].orientation;
1431 gtk_gs_set_page_size(gs, -1, page);
1433 gs->changed = FALSE;
1435 if(is_interpreter_ready(gs)) {
1436 gtk_gs_next_page(gs);
1439 gtk_gs_enable_interpreter(gs);
1440 send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1441 send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1444 send_ps(gs, gs->doc->pages[gs->current_page].begin,
1445 gs->doc->pages[gs->current_page].len, FALSE);
1448 /* Unstructured document */
1449 /* In the case of non structured documents,
1450 GS read the PS from the actual file (via command
1451 line. Hence, ggv only send a signal next page.
1452 If ghostview is not running it is usually because
1453 the last page of the file was displayed. In that
1454 case, ggv restarts GS again and the first page is displayed.
1456 if(page == gs->current_page && !gs->changed)
1459 if(!is_interpreter_ready(gs))
1460 gtk_gs_enable_interpreter(gs);
1462 gs->current_page = page;
1464 gtk_gs_next_page(gs);
1470 * set pagesize sets the size from
1471 * if new_pagesize is -1, then it is set to either
1472 * a) the default settings of pageid, if they exist, or if pageid != -1.
1473 * b) the default setting of the document, if it exists.
1474 * c) the default setting of the widget.
1475 * otherwise, the new_pagesize is used as the pagesize
1478 gtk_gs_set_page_size(GtkGS * gs, gint new_pagesize, gint pageid)
1484 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1486 g_return_val_if_fail(gs != NULL, FALSE);
1487 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1489 if(new_pagesize == -1) {
1490 if(gs->default_size > 0)
1491 new_pagesize = gs->default_size;
1492 if(!gs->override_size && gs->doc) {
1493 /* If we have a document:
1494 We use -- the page size (if specified)
1495 or the doc. size (if specified)
1496 or the page bbox (if specified)
1499 if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1500 (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1501 new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1503 else if(gs->doc->default_page_size != NULL) {
1504 new_pagesize = gs->doc->default_page_size - gs->doc->size;
1506 else if((pageid >= 0) &&
1507 (gs->doc->numpages > pageid) &&
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])) {
1515 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1516 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1522 /* Compute bounding box */
1523 if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) { /* epsf or bbox */
1526 (gs->doc->pages[pageid].boundingbox[URX] >
1527 gs->doc->pages[pageid].boundingbox[LLX])
1528 && (gs->doc->pages[pageid].boundingbox[URY] >
1529 gs->doc->pages[pageid].boundingbox[LLY])) {
1531 new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1532 new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1533 new_urx = gs->doc->pages[pageid].boundingbox[URX];
1534 new_ury = gs->doc->pages[pageid].boundingbox[URY];
1536 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1537 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1539 new_llx = gs->doc->boundingbox[LLX];
1540 new_lly = gs->doc->boundingbox[LLY];
1541 new_urx = gs->doc->boundingbox[URX];
1542 new_ury = gs->doc->boundingbox[URY];
1546 if(new_pagesize < 0)
1547 new_pagesize = gs->default_size;
1548 new_llx = new_lly = 0;
1549 if(gs->doc && !gs->override_size && gs->doc->size &&
1550 (new_pagesize < gs->doc->numsizes)) {
1551 new_urx = gs->doc->size[new_pagesize].width;
1552 new_ury = gs->doc->size[new_pagesize].height;
1555 new_urx = papersizes[new_pagesize].width;
1556 new_ury = papersizes[new_pagesize].height;
1560 if(new_urx <= new_llx)
1561 new_urx = papersizes[12].width;
1562 if(new_ury <= new_lly)
1563 new_ury = papersizes[12].height;
1565 /* If bounding box changed, setup for new size. */
1566 /* gtk_gs_disable_interpreter (gs); */
1567 if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1568 (new_urx != gs->urx) || (new_ury != gs->ury)) {
1585 gtk_gs_zoom_to_fit(GtkGS * gs, gboolean fit_width)
1589 guint avail_w, avail_h;
1591 g_return_val_if_fail(gs != NULL, 0.0);
1592 g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1594 avail_w = (gs->avail_w > 0) ? gs->avail_w : gs->width;
1595 avail_h = (gs->avail_h > 0) ? gs->avail_h : gs->height;
1597 new_zoom = ((gfloat) avail_w) / ((gfloat) gs->width) * gs->zoom_factor;
1599 new_y = new_zoom * ((gfloat) gs->height) / gs->zoom_factor;
1601 new_zoom = ((gfloat) avail_h) / ((gfloat) gs->height) * gs->zoom_factor;
1608 gtk_gs_set_zoom(GtkGS * gs, gfloat zoom)
1610 g_return_if_fail(gs != NULL);
1611 g_return_if_fail(GTK_IS_GS(gs));
1613 switch (gs->zoom_mode) {
1614 case GTK_GS_ZOOM_FIT_WIDTH:
1615 zoom = gtk_gs_zoom_to_fit(gs, TRUE);
1617 case GTK_GS_ZOOM_FIT_PAGE:
1618 zoom = gtk_gs_zoom_to_fit(gs, FALSE);
1620 case GTK_GS_ZOOM_ABSOLUTE:
1625 if(fabs(gs->zoom_factor - zoom) > 0.001) {
1626 gs->zoom_factor = zoom;
1631 gtk_gs_goto_page(gs, gs->current_page);
1635 ps_document_load (EvDocument *document,
1642 filename = g_filename_from_uri (uri, NULL, error);
1646 result = gtk_gs_load (GTK_GS (document), filename);
1654 ps_document_get_n_pages (EvDocument *document)
1656 return gtk_gs_get_page_count (GTK_GS (document));
1660 ps_document_set_page (EvDocument *document,
1663 gtk_gs_goto_page (GTK_GS (document), page);
1667 ps_document_get_page (EvDocument *document)
1669 return gtk_gs_get_current_page (GTK_GS (document));
1673 gtk_gs_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
1675 GtkGS *gs = (GtkGS *) data;
1677 if(event->type != GDK_CLIENT_EVENT)
1680 if (event->client.message_type == gs_class->page_atom) {
1682 ev_document_changed (EV_DOCUMENT (gs));
1689 ps_document_set_target (EvDocument *document,
1690 GdkDrawable *target)
1692 GtkGS *gs = GTK_GS (document);
1696 gs->pstarget = target;
1699 gdk_window_get_user_data (gs->pstarget, &data);
1700 g_return_if_fail (GTK_IS_WIDGET (data));
1702 widget = GTK_WIDGET (data);
1703 g_signal_connect (widget, "event",
1704 G_CALLBACK (gtk_gs_widget_event),
1708 gtk_gs_goto_page (gs, gs->current_page);
1712 ps_document_set_scale (EvDocument *document,
1715 gtk_gs_set_zoom (GTK_GS (document), scale);
1719 ps_document_set_page_offset (EvDocument *document,
1726 ps_document_get_page_size (EvDocument *document,
1730 GtkGS *gs = GTK_GS (document);
1737 *height = gs->height;
1742 ps_document_render (EvDocument *document,
1748 GtkGS *gs = GTK_GS (document);
1751 if (gs->pstarget == NULL ||
1752 gs->bpixmap == NULL) {
1756 gc = gdk_gc_new (gs->pstarget);
1758 gdk_draw_drawable (gs->pstarget, gc,
1762 clip_width, clip_height);
1764 g_object_unref (gc);
1768 ps_document_document_iface_init (EvDocumentIface *iface)
1770 iface->load = ps_document_load;
1771 iface->get_n_pages = ps_document_get_n_pages;
1772 iface->set_page = ps_document_set_page;
1773 iface->get_page = ps_document_get_page;
1774 iface->set_scale = ps_document_set_scale;
1775 iface->set_target = ps_document_set_target;
1776 iface->set_page_offset = ps_document_set_page_offset;
1777 iface->get_page_size = ps_document_get_page_size;
1778 iface->render = ps_document_render;