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"
153 #include "ps-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 PS_DOCUMENT_WATCH_INTERVAL 1000
167 #define PS_DOCUMENT_WATCH_TIMEOUT 2
169 #define MAX_BUFSIZE 1024
171 #define PS_DOCUMENT_IS_COMPRESSED(gs) (PS_DOCUMENT(gs)->gs_filename_unc != NULL)
172 #define PS_DOCUMENT_GET_PS_FILE(gs) (PS_DOCUMENT_IS_COMPRESSED(gs) ? \
173 PS_DOCUMENT(gs)->gs_filename_unc : \
174 PS_DOCUMENT(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 ps_document_init(PSDocument * gs);
198 static void ps_document_class_init(PSDocumentClass * klass);
199 static void ps_document_emit_error_msg(PSDocument * gs, const gchar * msg);
200 static void ps_document_finalize(GObject * object);
201 static void send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close);
202 static void set_up_page(PSDocument * gs);
203 static void close_pipe(int p[2]);
204 static void interpreter_failed(PSDocument * gs);
205 static float compute_xdpi(void);
206 static float compute_ydpi(void);
207 static gboolean compute_size(PSDocument * 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(PSDocument * gs);
211 static gint start_interpreter(PSDocument * gs);
212 gboolean computeSize(void);
213 static gboolean ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid);
214 static void ps_document_document_iface_init (EvDocumentIface *iface);
216 static GObjectClass *parent_class = NULL;
218 static PSDocumentClass *gs_class = NULL;
221 ps_document_init(PSDocument * 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 ps_document_class_init(PSDocumentClass * klass)
288 GObjectClass *object_class;
290 object_class = (GObjectClass *) klass;
291 parent_class = gtk_type_class(gtk_widget_get_type());
294 object_class->finalize = ps_document_finalize;
297 klass->gs_atom = gdk_atom_intern("GHOSTVIEW", FALSE);
298 klass->next_atom = gdk_atom_intern("NEXT", FALSE);
299 klass->page_atom = gdk_atom_intern("PAGE", FALSE);
300 klass->string_atom = gdk_atom_intern("STRING", FALSE);
302 gtk_gs_defaults_load();
305 /* Clean all memory and temporal files */
307 ps_document_cleanup(PSDocument * gs)
309 g_return_if_fail(gs != NULL);
310 g_return_if_fail(GTK_IS_GS(gs));
312 stop_interpreter(gs);
315 fclose(gs->gs_psfile);
316 gs->gs_psfile = NULL;
318 if(gs->gs_filename) {
319 g_free(gs->gs_filename);
320 gs->gs_filename = NULL;
326 if(gs->gs_filename_dsc) {
327 unlink(gs->gs_filename_dsc);
328 g_free(gs->gs_filename_dsc);
329 gs->gs_filename_dsc = NULL;
331 if(gs->gs_filename_unc) {
332 unlink(gs->gs_filename_unc);
333 g_free(gs->gs_filename_unc);
334 gs->gs_filename_unc = NULL;
336 gs->current_page = -1;
346 ps_document_finalize(GObject * object)
350 g_return_if_fail(object != NULL);
351 g_return_if_fail(GTK_IS_GS(object));
353 gs = PS_DOCUMENT(object);
355 ps_document_cleanup(gs);
357 if(gs->input_buffer) {
358 g_free(gs->input_buffer);
359 gs->input_buffer = NULL;
362 (*G_OBJECT_CLASS(parent_class)->finalize) (object);
366 send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close)
368 struct record_list *ps_new;
370 if(gs->interpreter_input < 0) {
371 g_critical("No pipe to gs: error in send_ps().");
375 ps_new = (struct record_list *) g_malloc(sizeof(struct record_list));
376 ps_new->fp = gs->gs_psfile;
377 ps_new->begin = begin;
379 ps_new->seek_needed = TRUE;
380 ps_new->close = close;
383 if(gs->input_buffer == NULL) {
384 gs->input_buffer = g_malloc(MAX_BUFSIZE);
387 if(gs->ps_input == NULL) {
388 gs->input_buffer_ptr = gs->input_buffer;
389 gs->bytes_left = len;
390 gs->buffer_bytes_left = 0;
391 gs->ps_input = ps_new;
392 gs->interpreter_input_id =
393 gdk_input_add(gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
396 struct record_list *p = gs->ps_input;
397 while(p->next != NULL) {
405 ps_document_get_orientation(PSDocument * gs)
407 g_return_val_if_fail(gs != NULL, -1);
408 g_return_val_if_fail(GTK_IS_GS(gs), -1);
411 if(gs->structured_doc) {
412 if(gs->doc->pages[MAX(gs->current_page, 0)].orientation !=
413 GTK_GS_ORIENTATION_NONE)
414 gs->real_orientation =
415 gs->doc->pages[MAX(gs->current_page, 0)].orientation;
417 gs->real_orientation = gs->doc->default_page_orientation;
420 if(gs->real_orientation == GTK_GS_ORIENTATION_NONE)
421 gs->real_orientation = gs->doc->orientation;
424 if(gs->override_orientation ||
425 gs->real_orientation == GTK_GS_ORIENTATION_NONE)
426 return gs->fallback_orientation;
428 return gs->real_orientation;
432 set_up_page(PSDocument * gs)
436 //GdkColormap *colormap;
438 GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */
439 GdkColormap *colormap;
445 if (gs->pstarget == NULL)
448 /* Do we have to check if the actual geometry changed? */
450 stop_interpreter(gs);
452 orientation = ps_document_get_orientation(gs);
454 if(compute_size(gs)) {
457 /* clear new pixmap (set to white) */
458 fill = gdk_gc_new(gs->pstarget);
460 colormap = gdk_drawable_get_colormap(gs->pstarget);
461 gdk_color_alloc (colormap, &white);
462 gdk_gc_set_foreground(fill, &white);
464 if(gs->width > 0 && gs->height > 0) {
466 gdk_drawable_unref(gs->bpixmap);
470 gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1);
472 gdk_draw_rectangle(gs->bpixmap, fill, TRUE,
473 0, 0, gs->width, gs->height);
476 gdk_draw_rectangle(gs->pstarget, fill, TRUE,
477 0, 0, gs->width, gs->height);
486 /* gs needs floating point parameters with '.' as decimal point
487 * while some (european) locales use ',' instead, so we set the
488 * locale for this snprintf to "C".
490 savelocale = setlocale(LC_NUMERIC, "C");
493 g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
500 gs->xdpi * gs->zoom_factor,
501 gs->ydpi * gs->zoom_factor,
503 gs->bottom_margin, gs->right_margin, gs->top_margin);
506 setlocale(LC_NUMERIC, savelocale);
508 gdk_property_change(gs->pstarget,
510 gs_class->string_atom,
511 8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
525 is_interpreter_ready(PSDocument * gs)
527 return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
531 interpreter_failed(PSDocument * gs)
533 stop_interpreter(gs);
537 output(gpointer data, gint source, GdkInputCondition condition)
539 char buf[MAX_BUFSIZE + 1], *msg;
541 PSDocument *gs = PS_DOCUMENT(data);
543 if(source == gs->interpreter_output) {
544 bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
545 if(bytes == 0) { /* EOF occurred */
546 close(gs->interpreter_output);
547 gs->interpreter_output = -1;
548 gdk_input_remove(gs->interpreter_output_id);
551 else if(bytes == -1) {
553 interpreter_failed(gs);
556 if(gs->interpreter_err == -1) {
557 stop_interpreter(gs);
560 else if(source == gs->interpreter_err) {
561 bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
562 if(bytes == 0) { /* EOF occurred */
563 close(gs->interpreter_err);
564 gs->interpreter_err = -1;
565 gdk_input_remove(gs->interpreter_error_id);
568 else if(bytes == -1) {
570 interpreter_failed(gs);
573 if(gs->interpreter_output == -1) {
574 stop_interpreter(gs);
580 ps_document_emit_error_msg (gs, msg);
585 input(gpointer data, gint source, GdkInputCondition condition)
587 PSDocument *gs = PS_DOCUMENT(data);
589 void (*oldsig) (int);
590 oldsig = signal(SIGPIPE, catchPipe);
593 if(gs->buffer_bytes_left == 0) {
594 /* Get a new section if required */
595 if(gs->ps_input && gs->bytes_left == 0) {
596 struct record_list *ps_old = gs->ps_input;
597 gs->ps_input = ps_old->next;
598 if(ps_old->close && NULL != ps_old->fp)
600 g_free((char *) ps_old);
602 /* Have to seek at the beginning of each section */
603 if(gs->ps_input && gs->ps_input->seek_needed) {
604 fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
605 gs->ps_input->seek_needed = FALSE;
606 gs->bytes_left = gs->ps_input->len;
609 if(gs->bytes_left > MAX_BUFSIZE) {
610 gs->buffer_bytes_left =
611 fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
613 else if(gs->bytes_left > 0) {
614 gs->buffer_bytes_left =
615 fread(gs->input_buffer,
616 sizeof(char), gs->bytes_left, gs->ps_input->fp);
619 gs->buffer_bytes_left = 0;
621 if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
622 interpreter_failed(gs); /* Error occurred */
624 gs->input_buffer_ptr = gs->input_buffer;
625 gs->bytes_left -= gs->buffer_bytes_left;
628 if(gs->buffer_bytes_left > 0) {
629 /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
631 bytes_written = write(gs->interpreter_input,
632 gs->input_buffer_ptr, gs->buffer_bytes_left);
635 ps_document_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
637 interpreter_failed(gs);
639 else if(bytes_written == -1) {
640 if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
641 interpreter_failed(gs); /* Something bad happened */
645 gs->buffer_bytes_left -= bytes_written;
646 gs->input_buffer_ptr += bytes_written;
650 while(gs->ps_input && gs->buffer_bytes_left == 0);
652 signal(SIGPIPE, oldsig);
654 if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
655 if(gs->interpreter_input_id != 0) {
656 gdk_input_remove(gs->interpreter_input_id);
657 gs->interpreter_input_id = 0;
663 start_interpreter(PSDocument * gs)
665 int std_in[2] = { -1, -1 }; /* pipe to interp stdin */
666 int std_out[2]; /* pipe from interp stdout */
667 int std_err[2]; /* pipe from interp stderr */
670 #define NUM_GS_ARGS (NUM_ARGS - 20)
671 #define NUM_ALPHA_ARGS 10
673 char *argv[NUM_ARGS], *dir, *gv_env;
674 char **gs_args, **alpha_args = NULL;
680 stop_interpreter(gs);
682 if(gs->disable_start == TRUE)
685 /* set up the args... */
686 gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS);
687 for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++)
688 argv[argc] = gs_args[i];
690 if(gs->antialiased) {
691 if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
692 alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
694 alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(),
695 " ", NUM_ALPHA_ARGS);
696 for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++)
697 argv[argc] = alpha_args[i];
700 argv[argc++] = "-sDEVICE=x11";
701 argv[argc++] = "-dNOPAUSE";
702 argv[argc++] = "-dQUIET";
703 /* I assume we do _not_ want to change this... (: */
704 argv[argc++] = "-dSAFER";
706 /* set up the pipes */
707 if(gs->send_filename_to_gs) {
708 argv[argc++] = PS_DOCUMENT_GET_PS_FILE(gs);
710 argv[argc++] = "quit";
717 if(!gs->reading_from_pipe && !gs->send_filename_to_gs) {
718 if(pipe(std_in) == -1) {
719 g_critical("Unable to open pipe to Ghostscript.");
723 if(pipe(std_out) == -1) {
727 if(pipe(std_err) == -1) {
734 gs->interpreter_pid = fork();
735 switch (gs->interpreter_pid) {
751 if(!gs->reading_from_pipe) {
752 if(gs->send_filename_to_gs) {
754 /* just in case gs tries to read from stdin */
755 stdinfd = open("/dev/null", O_RDONLY);
768 gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld",
769 gdk_x11_drawable_get_xid(gs->pstarget),
770 gdk_x11_drawable_get_xid(gs->bpixmap));
773 /* change to directory where the input file is. This helps
774 * with postscript-files which include other files using
776 dir = g_path_get_dirname(gs->gs_filename);
780 execvp(argv[0], argv);
783 g_print("Unable to execute [%s]\n", argv[0]);
787 g_strfreev(alpha_args);
790 default: /* parent */
791 if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
794 /* use non-blocking IO for pipe to ghostscript */
795 result = fcntl(std_in[1], F_GETFL, 0);
796 fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
797 gs->interpreter_input = std_in[1];
800 gs->interpreter_input = -1;
803 gs->interpreter_output = std_out[0];
805 gs->interpreter_err = std_err[0];
806 gs->interpreter_output_id =
807 gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
808 gs->interpreter_error_id =
809 gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
816 stop_interpreter(PSDocument * gs)
818 if(gs->interpreter_pid > 0) {
820 kill(gs->interpreter_pid, SIGTERM);
821 while((wait(&status) == -1) && (errno == EINTR)) ;
822 gs->interpreter_pid = -1;
824 ps_document_cleanup(gs);
825 gs->gs_status = _("Interpreter failed.");
829 if(gs->interpreter_input >= 0) {
830 close(gs->interpreter_input);
831 gs->interpreter_input = -1;
832 if(gs->interpreter_input_id != 0) {
833 gdk_input_remove(gs->interpreter_input_id);
834 gs->interpreter_input_id = 0;
836 while(gs->ps_input) {
837 struct record_list *ps_old = gs->ps_input;
838 gs->ps_input = gs->ps_input->next;
839 if(ps_old->close && NULL != ps_old->fp)
841 g_free((char *) ps_old);
845 if(gs->interpreter_output >= 0) {
846 close(gs->interpreter_output);
847 gs->interpreter_output = -1;
848 if(gs->interpreter_output_id) {
849 gdk_input_remove(gs->interpreter_output_id);
850 gs->interpreter_output_id = 0;
854 if(gs->interpreter_err >= 0) {
855 close(gs->interpreter_err);
856 gs->interpreter_err = -1;
857 if(gs->interpreter_error_id) {
858 gdk_input_remove(gs->interpreter_error_id);
859 gs->interpreter_error_id = 0;
866 /* If file exists and is a regular file then return its length, else -1 */
868 file_length(const gchar * filename)
870 struct stat stat_rec;
872 if(filename && (stat(filename, &stat_rec) == 0)
873 && S_ISREG(stat_rec.st_mode))
874 return stat_rec.st_size;
879 /* Test if file exists, is a regular file and its length is > 0 */
881 file_readable(const char *filename)
883 return (file_length(filename) > 0);
887 * Decompress gs->gs_filename if necessary
888 * Set gs->filename_unc to the name of the uncompressed file or NULL.
889 * Error reporting via signal 'interpreter_message'
890 * Return name of input file to use or NULL on error..
893 check_filecompressed(PSDocument * gs)
897 gchar *filename, *filename_unc, *filename_err, *cmdline;
903 if((file = fopen(gs->gs_filename, "r"))
904 && (fread(buf, sizeof(gchar), 3, file) == 3)) {
905 if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
906 /* file is gzipped or compressed */
907 cmd = gtk_gs_defaults_get_ungzip_cmd();
909 else if(strncmp(buf, "BZh", 3) == 0) {
910 /* file is compressed with bzip2 */
911 cmd = gtk_gs_defaults_get_unbzip2_cmd();
918 return gs->gs_filename;
920 /* do the decompression */
921 filename = g_shell_quote(gs->gs_filename);
922 filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
923 if((fd = mkstemp(filename_unc)) < 0) {
924 g_free(filename_unc);
929 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
930 if((fd = mkstemp(filename_err)) < 0) {
931 g_free(filename_err);
932 g_free(filename_unc);
937 cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
938 filename, filename_unc, filename_err);
939 if((system(cmdline) == 0)
940 && file_readable(filename_unc)
941 && (file_length(filename_err) == 0)) {
942 /* sucessfully uncompressed file */
943 gs->gs_filename_unc = filename_unc;
947 g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
949 ps_document_emit_error_msg(gs, buf);
950 if(file_length(filename_err) > 0) {
952 if((err = fopen(filename_err, "r"))) {
953 /* print file to message window */
954 while(fgets(buf, 1024, err))
955 ps_document_emit_error_msg(gs, buf);
959 unlink(filename_unc);
960 g_free(filename_unc);
963 unlink(filename_err);
964 g_free(filename_err);
971 * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
972 * pdf file if necessary.
973 * Set gs->filename_dsc to the name of the dsc file or NULL.
974 * Error reporting via signal 'interpreter_message'.
977 check_pdf(PSDocument * gs)
980 gchar buf[1024], *filename;
983 /* use uncompressed file as input if necessary */
984 filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
986 if((file = fopen(filename, "r"))
987 && (fread(buf, sizeof(char), 5, file) == 5)
988 && (strncmp(buf, "%PDF-", 5) == 0)) {
989 /* we found a PDF file */
990 gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
991 filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
992 if((fd = mkstemp(filename_dsc)) < 0) {
996 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
997 if((fd = mkstemp(filename_err)) < 0) {
998 g_free(filename_dsc);
1002 fname = g_shell_quote(filename);
1003 cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1005 /* this command (sometimes?) prints error messages to stdout! */
1006 cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1009 if((system(cmdline) == 0) && file_readable(filename_dsc)) {
1012 filename = gs->gs_filename_dsc = filename_dsc;
1014 if(file_length(filename_err) > 0) {
1015 gchar *err_msg = " ";
1020 if((err = fopen(filename_err, "r"))) {
1022 /* print the content of the file to a message box */
1023 while(fgets(buf, 1024, err))
1024 err_msg = g_strconcat(err_msg, buf, NULL);
1026 /* FIXME The dialog is not yet set to modal, difficult to
1027 * get the parent of the dialog box here
1030 dialog = gtk_message_dialog_new(NULL,
1032 GTK_MESSAGE_WARNING,
1034 ("There was an error while scaning the file: %s \n%s"),
1035 gs->gs_filename, err_msg);
1037 gdk_color_parse("white", &color);
1038 gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1040 g_signal_connect(G_OBJECT(dialog), "response",
1041 G_CALLBACK(gtk_widget_destroy), NULL);
1043 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1044 gtk_widget_show(dialog);
1052 g_snprintf(buf, 1024,
1053 _("Error while converting pdf file %s:\n"), filename);
1054 ps_document_emit_error_msg(gs, buf);
1056 if(file_length(filename_err) > 0) {
1058 if((err = fopen(filename_err, "r"))) {
1059 /* print file to message window */
1060 while(fgets(buf, 1024, err))
1061 ps_document_emit_error_msg(gs, buf);
1064 unlink(filename_dsc);
1065 g_free(filename_dsc);
1068 unlink(filename_err);
1069 g_free(filename_err);
1077 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1078 /* never mind this patch: a properly working X server should take care of
1079 calculating the proper values. */
1083 # ifndef HAVE_XINERAMA
1084 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1087 dpy = (Display *) GDK_DISPLAY();
1088 if(XineramaIsActive(dpy)) {
1090 XineramaScreenInfo *head_info;
1091 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1092 /* fake it with dimensions of the first head for now */
1093 return 25.4 * head_info[0].width / gdk_screen_width_mm();
1096 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1105 # ifndef HAVE_XINERAMA
1106 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1109 dpy = (Display *) GDK_DISPLAY();
1110 if(XineramaIsActive(dpy)) {
1112 XineramaScreenInfo *head_info;
1113 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1114 /* fake it with dimensions of the first head for now */
1115 return 25.4 * head_info[0].height / gdk_screen_height_mm();
1118 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1127 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1133 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1135 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1137 /* Compute new size of window, sets xdpi and ydpi if necessary.
1138 * returns True if new window size is different */
1140 compute_size(PSDocument * gs)
1142 guint new_width = 1;
1143 guint new_height = 1;
1144 gboolean change = FALSE;
1147 /* width and height can be changed, calculate window size according */
1148 /* to xpdi and ydpi */
1149 orientation = ps_document_get_orientation(gs);
1151 switch (orientation) {
1152 case GTK_GS_ORIENTATION_PORTRAIT:
1153 case GTK_GS_ORIENTATION_UPSIDEDOWN:
1154 new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1155 new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1157 case GTK_GS_ORIENTATION_LANDSCAPE:
1158 case GTK_GS_ORIENTATION_SEASCAPE:
1159 new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1160 new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1164 change = (new_width != gs->width * gs->zoom_factor)
1165 || (new_height != gs->height * gs->zoom_factor);
1166 gs->width = (gint) (new_width * gs->zoom_factor);
1167 gs->height = (gint) (new_height * gs->zoom_factor);
1173 ps_document_enable_interpreter(PSDocument * gs)
1175 g_return_val_if_fail(gs != NULL, FALSE);
1176 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1178 if(!gs->gs_filename)
1181 gs->disable_start = FALSE;
1183 return start_interpreter(gs);
1186 /* publicly accessible functions */
1189 ps_document_get_type(void)
1191 static GType gs_type = 0;
1193 GTypeInfo gs_info = {
1194 sizeof(PSDocumentClass),
1195 (GBaseInitFunc) NULL,
1196 (GBaseFinalizeFunc) NULL,
1197 (GClassInitFunc) ps_document_class_init,
1198 (GClassFinalizeFunc) NULL,
1199 NULL, /* class_data */
1201 0, /* n_preallocs */
1202 (GInstanceInitFunc) ps_document_init
1205 static const GInterfaceInfo document_info =
1207 (GInterfaceInitFunc) ps_document_document_iface_init,
1212 gs_type = g_type_register_static(G_TYPE_OBJECT,
1213 "PSDocument", &gs_info, 0);
1215 g_type_add_interface_static (gs_type,
1225 * Show error message -> send signal "interpreter_message"
1228 ps_document_emit_error_msg(PSDocument * gs, const gchar * msg)
1230 gdk_pointer_ungrab(GDK_CURRENT_TIME);
1231 if(strstr(msg, "Error:")) {
1232 gs->gs_status = _("File is not a valid PostScript document.");
1233 ps_document_cleanup(gs);
1238 document_load(PSDocument * gs, const gchar * fname)
1240 g_return_val_if_fail(gs != NULL, FALSE);
1241 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1243 /* clean up previous document */
1244 ps_document_cleanup(gs);
1251 /* prepare this document */
1253 /* default values: no dsc information available */
1254 gs->structured_doc = FALSE;
1255 gs->send_filename_to_gs = TRUE;
1256 gs->current_page = -2;
1259 /* an absolute path */
1260 gs->gs_filename = g_strdup(fname);
1263 /* path relative to our cwd: make it absolute */
1264 gchar *cwd = g_get_current_dir();
1265 gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1269 if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1270 gs->send_filename_to_gs = FALSE;
1274 * We need to make sure that the file is loadable/exists!
1275 * otherwise we want to exit without loading new stuff...
1277 gchar *filename = NULL;
1279 if(!file_readable(fname)) {
1281 g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1282 ps_document_emit_error_msg(gs, buf);
1283 gs->gs_status = _("File is not readable.");
1286 filename = check_filecompressed(gs);
1288 filename = check_pdf(gs);
1291 if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1292 ps_document_cleanup(gs);
1296 /* we grab the vital statistics!!! */
1297 gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1299 if(gs->doc == NULL) {
1300 /* File does not seem to be a Postscript one */
1302 g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1303 ps_document_emit_error_msg(gs, buf);
1304 ps_document_cleanup(gs);
1305 gs->gs_status = _("The file is not a PostScript document.");
1309 if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1310 (gs->doc->epsf && gs->doc->numpages > 1)) {
1311 gs->structured_doc = TRUE;
1312 gs->send_filename_to_gs = FALSE;
1315 /* We have to set up the orientation of the document */
1318 /* orientation can only be portrait, and landscape or none.
1319 This is the document default. A document can have
1320 pages in landscape and some in portrait */
1321 if(gs->override_orientation) {
1322 /* If the orientation should be override...
1323 then gs->orientation has already the correct
1324 value (it was set when the widget was created */
1329 /* Otherwise, set the proper orientation for the doc */
1330 gs->real_orientation = gs->doc->orientation;
1333 ps_document_set_page_size(gs, -1, gs->current_page);
1336 gs->gs_status = _("Document loaded.");
1343 ps_document_next_page(PSDocument * gs)
1347 g_return_val_if_fail(gs != NULL, FALSE);
1348 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1350 if(gs->interpreter_pid == 0) { /* no interpreter active */
1354 if(gs->busy) { /* interpreter is busy */
1360 event.xclient.type = ClientMessage;
1361 event.xclient.display = gdk_display;
1362 event.xclient.window = gs->message_window;
1363 event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1364 event.xclient.format = 32;
1366 gdk_error_trap_push();
1367 XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1369 gdk_error_trap_pop();
1375 ps_document_get_current_page(PSDocument * gs)
1377 g_return_val_if_fail(gs != NULL, -1);
1378 g_return_val_if_fail(GTK_IS_GS(gs), -1);
1380 return gs->current_page;
1384 ps_document_get_page_count(PSDocument * gs)
1386 if(!gs->gs_filename)
1390 if(gs->structured_doc)
1391 return gs->doc->numpages;
1400 ps_document_goto_page(PSDocument * gs, gint page)
1402 g_return_val_if_fail(gs != NULL, FALSE);
1403 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1405 if(!gs->gs_filename) {
1409 /* range checking... */
1413 if(gs->structured_doc && gs->doc) {
1414 if(page >= gs->doc->numpages)
1415 page = gs->doc->numpages - 1;
1417 if(page == gs->current_page && !gs->changed)
1420 gs->current_page = page;
1422 if(gs->doc->pages[page].orientation != NONE &&
1423 !gs->override_orientation &&
1424 gs->doc->pages[page].orientation != gs->real_orientation) {
1425 gs->real_orientation = gs->doc->pages[page].orientation;
1429 ps_document_set_page_size(gs, -1, page);
1431 gs->changed = FALSE;
1433 if(is_interpreter_ready(gs)) {
1434 ps_document_next_page(gs);
1437 ps_document_enable_interpreter(gs);
1438 send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1439 send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1442 send_ps(gs, gs->doc->pages[gs->current_page].begin,
1443 gs->doc->pages[gs->current_page].len, FALSE);
1446 /* Unstructured document */
1447 /* In the case of non structured documents,
1448 GS read the PS from the actual file (via command
1449 line. Hence, ggv only send a signal next page.
1450 If ghostview is not running it is usually because
1451 the last page of the file was displayed. In that
1452 case, ggv restarts GS again and the first page is displayed.
1454 if(page == gs->current_page && !gs->changed)
1457 if(!is_interpreter_ready(gs))
1458 ps_document_enable_interpreter(gs);
1460 gs->current_page = page;
1462 ps_document_next_page(gs);
1468 * set pagesize sets the size from
1469 * if new_pagesize is -1, then it is set to either
1470 * a) the default settings of pageid, if they exist, or if pageid != -1.
1471 * b) the default setting of the document, if it exists.
1472 * c) the default setting of the widget.
1473 * otherwise, the new_pagesize is used as the pagesize
1476 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1482 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1484 g_return_val_if_fail(gs != NULL, FALSE);
1485 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1487 if(new_pagesize == -1) {
1488 if(gs->default_size > 0)
1489 new_pagesize = gs->default_size;
1490 if(!gs->override_size && gs->doc) {
1491 /* If we have a document:
1492 We use -- the page size (if specified)
1493 or the doc. size (if specified)
1494 or the page bbox (if specified)
1497 if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1498 (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1499 new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1501 else if(gs->doc->default_page_size != NULL) {
1502 new_pagesize = gs->doc->default_page_size - gs->doc->size;
1504 else if((pageid >= 0) &&
1505 (gs->doc->numpages > pageid) &&
1507 (gs->doc->pages[pageid].boundingbox[URX] >
1508 gs->doc->pages[pageid].boundingbox[LLX]) &&
1509 (gs->doc->pages[pageid].boundingbox[URY] >
1510 gs->doc->pages[pageid].boundingbox[LLY])) {
1513 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1514 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1520 /* Compute bounding box */
1521 if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) { /* epsf or bbox */
1524 (gs->doc->pages[pageid].boundingbox[URX] >
1525 gs->doc->pages[pageid].boundingbox[LLX])
1526 && (gs->doc->pages[pageid].boundingbox[URY] >
1527 gs->doc->pages[pageid].boundingbox[LLY])) {
1529 new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1530 new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1531 new_urx = gs->doc->pages[pageid].boundingbox[URX];
1532 new_ury = gs->doc->pages[pageid].boundingbox[URY];
1534 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1535 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1537 new_llx = gs->doc->boundingbox[LLX];
1538 new_lly = gs->doc->boundingbox[LLY];
1539 new_urx = gs->doc->boundingbox[URX];
1540 new_ury = gs->doc->boundingbox[URY];
1544 if(new_pagesize < 0)
1545 new_pagesize = gs->default_size;
1546 new_llx = new_lly = 0;
1547 if(gs->doc && !gs->override_size && gs->doc->size &&
1548 (new_pagesize < gs->doc->numsizes)) {
1549 new_urx = gs->doc->size[new_pagesize].width;
1550 new_ury = gs->doc->size[new_pagesize].height;
1553 new_urx = papersizes[new_pagesize].width;
1554 new_ury = papersizes[new_pagesize].height;
1558 if(new_urx <= new_llx)
1559 new_urx = papersizes[12].width;
1560 if(new_ury <= new_lly)
1561 new_ury = papersizes[12].height;
1563 /* If bounding box changed, setup for new size. */
1564 /* ps_document_disable_interpreter (gs); */
1565 if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1566 (new_urx != gs->urx) || (new_ury != gs->ury)) {
1583 ps_document_zoom_to_fit(PSDocument * gs, gboolean fit_width)
1587 guint avail_w, avail_h;
1589 g_return_val_if_fail(gs != NULL, 0.0);
1590 g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1592 avail_w = (gs->avail_w > 0) ? gs->avail_w : gs->width;
1593 avail_h = (gs->avail_h > 0) ? gs->avail_h : gs->height;
1595 new_zoom = ((gfloat) avail_w) / ((gfloat) gs->width) * gs->zoom_factor;
1597 new_y = new_zoom * ((gfloat) gs->height) / gs->zoom_factor;
1599 new_zoom = ((gfloat) avail_h) / ((gfloat) gs->height) * gs->zoom_factor;
1606 ps_document_set_zoom(PSDocument * gs, gfloat zoom)
1608 g_return_if_fail(gs != NULL);
1609 g_return_if_fail(GTK_IS_GS(gs));
1611 switch (gs->zoom_mode) {
1612 case GTK_GS_ZOOM_FIT_WIDTH:
1613 zoom = ps_document_zoom_to_fit(gs, TRUE);
1615 case GTK_GS_ZOOM_FIT_PAGE:
1616 zoom = ps_document_zoom_to_fit(gs, FALSE);
1618 case GTK_GS_ZOOM_ABSOLUTE:
1623 if(fabs(gs->zoom_factor - zoom) > 0.001) {
1624 gs->zoom_factor = zoom;
1629 ps_document_goto_page(gs, gs->current_page);
1633 ps_document_load (EvDocument *document,
1640 filename = g_filename_from_uri (uri, NULL, error);
1644 result = document_load (PS_DOCUMENT (document), filename);
1652 ps_document_get_n_pages (EvDocument *document)
1654 return ps_document_get_page_count (PS_DOCUMENT (document));
1658 ps_document_set_page (EvDocument *document,
1661 ps_document_goto_page (PS_DOCUMENT (document), page);
1665 ps_document_get_page (EvDocument *document)
1667 return ps_document_get_current_page (PS_DOCUMENT (document));
1671 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
1673 PSDocument *gs = (PSDocument *) data;
1675 if(event->type != GDK_CLIENT_EVENT)
1678 if (event->client.message_type == gs_class->page_atom) {
1680 ev_document_changed (EV_DOCUMENT (gs));
1687 ps_document_set_target (EvDocument *document,
1688 GdkDrawable *target)
1690 PSDocument *gs = PS_DOCUMENT (document);
1694 gs->pstarget = target;
1697 gdk_window_get_user_data (gs->pstarget, &data);
1698 g_return_if_fail (GTK_IS_WIDGET (data));
1700 widget = GTK_WIDGET (data);
1701 g_signal_connect (widget, "event",
1702 G_CALLBACK (ps_document_widget_event),
1706 ps_document_goto_page (gs, gs->current_page);
1710 ps_document_set_scale (EvDocument *document,
1713 ps_document_set_zoom (PS_DOCUMENT (document), scale);
1717 ps_document_set_page_offset (EvDocument *document,
1724 ps_document_get_page_size (EvDocument *document,
1728 PSDocument *gs = PS_DOCUMENT (document);
1735 *height = gs->height;
1740 ps_document_render (EvDocument *document,
1746 PSDocument *gs = PS_DOCUMENT (document);
1749 if (gs->pstarget == NULL ||
1750 gs->bpixmap == NULL) {
1754 gc = gdk_gc_new (gs->pstarget);
1756 gdk_draw_drawable (gs->pstarget, gc,
1760 clip_width, clip_height);
1762 g_object_unref (gc);
1766 ps_document_document_iface_init (EvDocumentIface *iface)
1768 iface->load = ps_document_load;
1769 iface->get_n_pages = ps_document_get_n_pages;
1770 iface->set_page = ps_document_set_page;
1771 iface->get_page = ps_document_get_page;
1772 iface->set_scale = ps_document_set_scale;
1773 iface->set_target = ps_document_set_target;
1774 iface->set_page_offset = ps_document_set_page_offset;
1775 iface->get_page_size = ps_document_get_page_size;
1776 iface->render = ps_document_render;