1 /* Ghostscript widget for GTK/GNOME
3 * Copyright (C) 1998 - 2005 the Free Software Foundation
5 * Authors: Jonathan Blandford, Jaka Mocnik
7 * Based on code by: Federico Mena (Quartic), Szekeres Istvan (Pista)
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
26 Ghostview interface to ghostscript
28 When the GHOSTVIEW environment variable is set, ghostscript draws on
29 an existing drawable rather than creating its own window. Ghostscript
30 can be directed to draw on either a window or a pixmap.
34 The GHOSTVIEW environment variable contains the window id of the target
35 window. The window id is an integer. Ghostscript will use the attributes
36 of the window to obtain the width, height, colormap, screen, and visual of
37 the window. The remainder of the information is gotten from the GHOSTVIEW
38 property on that window.
43 The GHOSTVIEW environment variable contains a window id and a pixmap id.
44 They are integers separated by white space. Ghostscript will use the
45 attributes of the window to obtain the colormap, screen, and visual to use.
46 The width and height will be obtained from the pixmap. The remainder of the
47 information, is gotten from the GHOSTVIEW property on the window. In this
48 case, the property is deleted when read.
50 The GHOSTVIEW environment variable
52 parameters: window-id [pixmap-id]
56 explanation of parameters:
58 window-id: tells ghostscript where to
59 - read the GHOSTVIEW property
61 If pixmap-id is not present,
62 ghostscript will draw on this window.
64 pixmap-id: If present, tells ghostscript that a pixmap will be used
65 as the final destination for drawing. The window will
66 not be touched for drawing purposes.
68 The GHOSTVIEW property
74 bpixmap orient llx lly urx ury xdpi ydpi [left bottom top right]
76 scanf format: "%d %d %d %d %d %d %f %f %d %d %d %d"
78 explanation of parameters:
80 bpixmap: pixmap id of the backing pixmap for the window. If no
81 pixmap is to be used, this parameter should be zero. This
82 parameter must be zero when drawing on a pixmap.
84 orient: orientation of the page. The number represents clockwise
85 rotation of the paper in degrees. Permitted values are
88 llx, lly, urx, ury: Bounding box of the drawable. The bounding box
89 is specified in PostScript points in default user coordinates.
91 xdpi, ydpi: Resolution of window. (This can be derived from the
92 other parameters, but not without roundoff error. These
93 values are included to avoid this error.)
95 left, bottom, top, right: (optional)
96 Margins around the window. The margins extend the imageable
97 area beyond the boundaries of the window. This is primarily
98 used for popup zoom windows. I have encountered several
99 instances of PostScript programs that position themselves
100 with respect to the imageable area. The margins are specified
101 in PostScript points. If omitted, the margins are assumed to
104 Events from ghostscript
106 If the final destination is a pixmap, the client will get a property notify
107 event when ghostscript reads the GHOSTVIEW property causing it to be deleted.
109 Ghostscript sends events to the window where it read the GHOSTVIEW property.
110 These events are of type ClientMessage. The message_type is set to
111 either PAGE or DONE. The first long data value gives the window to be used
112 to send replies to ghostscript. The second long data value gives the primary
113 drawable. If rendering to a pixmap, it is the primary drawable. If rendering
114 to a window, the backing pixmap is the primary drawable. If no backing pixmap
115 is employed, then the window is the primary drawable. This field is necessary
116 to distinguish multiple ghostscripts rendering to separate pixmaps where the
117 GHOSTVIEW property was placed on the same window.
119 The PAGE message indicates that a "page" has completed. Ghostscript will
120 wait until it receives a ClientMessage whose message_type is NEXT before
123 The DONE message indicates that ghostscript has finished processing.
132 #include <gtk/gtkobject.h>
133 #include <gdk/gdkprivate.h>
134 #include <gdk/gdkx.h>
136 #include <glib/gi18n.h>
138 # include <gdk/gdkx.h>
139 # include <X11/extensions/Xinerama.h>
140 #endif /* HAVE_XINERAMA */
141 #include <X11/Intrinsic.h>
146 #include <sys/stat.h>
147 #include <sys/types.h>
148 #include <sys/wait.h>
152 #include "ps-document.h"
153 #include "ev-debug.h"
154 #include "gsdefaults.h"
160 /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
161 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
162 # define O_NONBLOCK O_NDELAY
165 #define PS_DOCUMENT_WATCH_INTERVAL 1000
166 #define PS_DOCUMENT_WATCH_TIMEOUT 2
168 #define MAX_BUFSIZE 1024
170 #define PS_DOCUMENT_IS_COMPRESSED(gs) (PS_DOCUMENT(gs)->gs_filename_unc != NULL)
171 #define PS_DOCUMENT_GET_PS_FILE(gs) (PS_DOCUMENT_IS_COMPRESSED(gs) ? \
172 PS_DOCUMENT(gs)->gs_filename_unc : \
173 PS_DOCUMENT(gs)->gs_filename)
175 enum { INTERPRETER_MESSAGE, INTERPRETER_ERROR, LAST_SIGNAL };
182 /* structure to describe section of file to send to ghostscript */
187 gboolean seek_needed;
189 struct record_list *next;
192 static gboolean broken_pipe = FALSE;
200 /* Forward declarations */
201 static void ps_document_init(PSDocument * gs);
202 static void ps_document_class_init(PSDocumentClass * klass);
203 static void ps_document_emit_error_msg(PSDocument * gs, const gchar * msg);
204 static void ps_document_finalize(GObject * object);
205 static void send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close);
206 static void set_up_page(PSDocument * gs);
207 static void close_pipe(int p[2]);
208 static void interpreter_failed(PSDocument * gs);
209 static float compute_xdpi(void);
210 static float compute_ydpi(void);
211 static gboolean compute_size(PSDocument * gs);
212 static void output(gpointer data, gint source, GdkInputCondition condition);
213 static void input(gpointer data, gint source, GdkInputCondition condition);
214 static void stop_interpreter(PSDocument * gs);
215 static gint start_interpreter(PSDocument * gs);
216 gboolean computeSize(void);
217 static gboolean ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid);
218 static void ps_document_document_iface_init (EvDocumentIface *iface);
220 static GObjectClass *parent_class = NULL;
222 static PSDocumentClass *gs_class = NULL;
225 ps_document_init(PSDocument * gs)
229 gs->current_page = 0;
230 gs->disable_start = FALSE;
231 gs->interpreter_pid = -1;
237 gs->gs_scanstyle = 0;
239 gs->gs_filename_dsc = 0;
240 gs->gs_filename_unc = 0;
244 gs->structured_doc = FALSE;
245 gs->reading_from_pipe = FALSE;
246 gs->send_filename_to_gs = FALSE;
251 gs->interpreter_input = -1;
252 gs->interpreter_output = -1;
253 gs->interpreter_err = -1;
254 gs->interpreter_input_id = 0;
255 gs->interpreter_output_id = 0;
256 gs->interpreter_error_id = 0;
259 gs->input_buffer = NULL;
260 gs->input_buffer_ptr = NULL;
262 gs->buffer_bytes_left = 0;
268 gs->xdpi = compute_xdpi();
269 gs->ydpi = compute_ydpi();
273 gs->right_margin = 0;
274 gs->bottom_margin = 0;
276 gs->page_x_offset = 0;
277 gs->page_y_offset = 0;
279 /* Set user defined defaults */
280 gs->override_orientation = gtk_gs_defaults_get_override_orientation();
281 gs->fallback_orientation = gtk_gs_defaults_get_orientation();
282 gs->zoom_factor = gtk_gs_defaults_get_zoom_factor();
283 gs->default_size = gtk_gs_defaults_get_size();
284 gs->antialiased = gtk_gs_defaults_get_antialiased();
285 gs->override_size = gtk_gs_defaults_get_override_size();
286 gs->respect_eof = gtk_gs_defaults_get_respect_eof();
287 gs->zoom_mode = gtk_gs_defaults_get_zoom_mode();
289 gs->gs_status = _("No document loaded.");
293 ps_document_set_property (GObject *object,
308 ps_document_get_property (GObject *object,
313 PSDocument *ps = PS_DOCUMENT (object);
319 g_value_set_string (value, ps->doc->title);
321 g_value_set_string (value, NULL);
328 ps_document_class_init(PSDocumentClass *klass)
330 GObjectClass *object_class;
332 object_class = (GObjectClass *) klass;
333 parent_class = g_type_class_peek_parent (klass);
336 object_class->finalize = ps_document_finalize;
337 object_class->get_property = ps_document_get_property;
338 object_class->set_property = ps_document_set_property;
341 klass->gs_atom = gdk_atom_intern("GHOSTVIEW", FALSE);
342 klass->next_atom = gdk_atom_intern("NEXT", FALSE);
343 klass->page_atom = gdk_atom_intern("PAGE", FALSE);
344 klass->string_atom = gdk_atom_intern("STRING", FALSE);
346 gtk_gs_defaults_load();
348 g_object_class_override_property (object_class, PROP_TITLE, "title");
351 /* Clean all memory and temporal files */
353 ps_document_cleanup(PSDocument * gs)
355 g_return_if_fail(gs != NULL);
356 g_return_if_fail(GTK_IS_GS(gs));
358 stop_interpreter(gs);
361 fclose(gs->gs_psfile);
362 gs->gs_psfile = NULL;
364 if(gs->gs_filename) {
365 g_free(gs->gs_filename);
366 gs->gs_filename = NULL;
372 if(gs->gs_filename_dsc) {
373 unlink(gs->gs_filename_dsc);
374 g_free(gs->gs_filename_dsc);
375 gs->gs_filename_dsc = NULL;
377 if(gs->gs_filename_unc) {
378 unlink(gs->gs_filename_unc);
379 g_free(gs->gs_filename_unc);
380 gs->gs_filename_unc = NULL;
382 gs->current_page = 0;
392 ps_document_finalize (GObject * object)
396 g_return_if_fail (object != NULL);
397 g_return_if_fail (GTK_IS_GS (object));
401 gs = PS_DOCUMENT (object);
403 ps_document_cleanup (gs);
404 stop_interpreter (gs);
406 if(gs->input_buffer) {
407 g_free(gs->input_buffer);
408 gs->input_buffer = NULL;
411 (*G_OBJECT_CLASS(parent_class)->finalize) (object);
415 send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close)
417 struct record_list *ps_new;
419 if(gs->interpreter_input < 0) {
420 g_critical("No pipe to gs: error in send_ps().");
424 ps_new = (struct record_list *) g_malloc(sizeof(struct record_list));
425 ps_new->fp = gs->gs_psfile;
426 ps_new->begin = begin;
428 ps_new->seek_needed = TRUE;
429 ps_new->close = close;
432 if(gs->input_buffer == NULL) {
433 gs->input_buffer = g_malloc(MAX_BUFSIZE);
436 if(gs->ps_input == NULL) {
437 gs->input_buffer_ptr = gs->input_buffer;
438 gs->bytes_left = len;
439 gs->buffer_bytes_left = 0;
440 gs->ps_input = ps_new;
441 gs->interpreter_input_id =
442 gdk_input_add(gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
445 struct record_list *p = gs->ps_input;
446 while(p->next != NULL) {
454 ps_document_get_orientation(PSDocument * gs)
456 g_return_val_if_fail(gs != NULL, -1);
457 g_return_val_if_fail(GTK_IS_GS(gs), -1);
460 if(gs->structured_doc) {
461 if(gs->doc->pages[MAX(gs->current_page, 0)].orientation !=
462 GTK_GS_ORIENTATION_NONE)
463 gs->real_orientation =
464 gs->doc->pages[MAX(gs->current_page, 0)].orientation;
466 gs->real_orientation = gs->doc->default_page_orientation;
469 if(gs->real_orientation == GTK_GS_ORIENTATION_NONE)
470 gs->real_orientation = gs->doc->orientation;
473 if(gs->override_orientation ||
474 gs->real_orientation == GTK_GS_ORIENTATION_NONE)
475 return gs->fallback_orientation;
477 return gs->real_orientation;
481 set_up_page(PSDocument * gs)
485 //GdkColormap *colormap;
487 GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */
488 GdkColormap *colormap;
489 gboolean size_changed;
491 LOG ("Setup the page");
497 size_changed = compute_size (gs);
499 if (gs->pstarget == NULL)
502 /* Do we have to check if the actual geometry changed? */
504 stop_interpreter(gs);
506 orientation = ps_document_get_orientation(gs);
508 if (size_changed || gs->bpixmap == NULL) {
511 /* clear new pixmap (set to white) */
512 fill = gdk_gc_new(gs->pstarget);
514 colormap = gdk_drawable_get_colormap(gs->pstarget);
515 gdk_color_alloc (colormap, &white);
516 gdk_gc_set_foreground(fill, &white);
518 if(gs->width > 0 && gs->height > 0) {
520 gdk_drawable_unref(gs->bpixmap);
524 LOG ("Create our internal pixmap");
525 gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1);
527 gdk_draw_rectangle(gs->bpixmap, fill, TRUE,
528 0, 0, gs->width, gs->height);
531 gdk_draw_rectangle(gs->pstarget, fill, TRUE,
532 0, 0, gs->width, gs->height);
541 /* gs needs floating point parameters with '.' as decimal point
542 * while some (european) locales use ',' instead, so we set the
543 * locale for this snprintf to "C".
545 savelocale = setlocale(LC_NUMERIC, "C");
548 g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
555 gs->xdpi * gs->zoom_factor,
556 gs->ydpi * gs->zoom_factor,
558 gs->bottom_margin, gs->right_margin, gs->top_margin);
560 LOG ("GS property %s", buf);
563 setlocale(LC_NUMERIC, savelocale);
565 gdk_property_change(gs->pstarget,
567 gs_class->string_atom,
568 8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
582 is_interpreter_ready(PSDocument * gs)
584 return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
588 interpreter_failed(PSDocument * gs)
590 stop_interpreter(gs);
594 output(gpointer data, gint source, GdkInputCondition condition)
596 char buf[MAX_BUFSIZE + 1], *msg;
598 PSDocument *gs = PS_DOCUMENT(data);
600 if(source == gs->interpreter_output) {
601 bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
602 if(bytes == 0) { /* EOF occurred */
603 close(gs->interpreter_output);
604 gs->interpreter_output = -1;
605 gdk_input_remove(gs->interpreter_output_id);
608 else if(bytes == -1) {
610 interpreter_failed(gs);
613 if(gs->interpreter_err == -1) {
614 stop_interpreter(gs);
617 else if(source == gs->interpreter_err) {
618 bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
619 if(bytes == 0) { /* EOF occurred */
620 close(gs->interpreter_err);
621 gs->interpreter_err = -1;
622 gdk_input_remove(gs->interpreter_error_id);
625 else if(bytes == -1) {
627 interpreter_failed(gs);
630 if(gs->interpreter_output == -1) {
631 stop_interpreter(gs);
637 ps_document_emit_error_msg (gs, msg);
642 input(gpointer data, gint source, GdkInputCondition condition)
644 PSDocument *gs = PS_DOCUMENT(data);
646 void (*oldsig) (int);
647 oldsig = signal(SIGPIPE, catchPipe);
650 if(gs->buffer_bytes_left == 0) {
651 /* Get a new section if required */
652 if(gs->ps_input && gs->bytes_left == 0) {
653 struct record_list *ps_old = gs->ps_input;
654 gs->ps_input = ps_old->next;
655 if(ps_old->close && NULL != ps_old->fp)
657 g_free((char *) ps_old);
659 /* Have to seek at the beginning of each section */
660 if(gs->ps_input && gs->ps_input->seek_needed) {
661 fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
662 gs->ps_input->seek_needed = FALSE;
663 gs->bytes_left = gs->ps_input->len;
666 if(gs->bytes_left > MAX_BUFSIZE) {
667 gs->buffer_bytes_left =
668 fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
670 else if(gs->bytes_left > 0) {
671 gs->buffer_bytes_left =
672 fread(gs->input_buffer,
673 sizeof(char), gs->bytes_left, gs->ps_input->fp);
676 gs->buffer_bytes_left = 0;
678 if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
679 interpreter_failed(gs); /* Error occurred */
681 gs->input_buffer_ptr = gs->input_buffer;
682 gs->bytes_left -= gs->buffer_bytes_left;
685 if(gs->buffer_bytes_left > 0) {
686 /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
688 bytes_written = write(gs->interpreter_input,
689 gs->input_buffer_ptr, gs->buffer_bytes_left);
692 ps_document_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
694 interpreter_failed(gs);
696 else if(bytes_written == -1) {
697 if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
698 interpreter_failed(gs); /* Something bad happened */
702 gs->buffer_bytes_left -= bytes_written;
703 gs->input_buffer_ptr += bytes_written;
707 while(gs->ps_input && gs->buffer_bytes_left == 0);
709 signal(SIGPIPE, oldsig);
711 if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
712 if(gs->interpreter_input_id != 0) {
713 gdk_input_remove(gs->interpreter_input_id);
714 gs->interpreter_input_id = 0;
720 start_interpreter(PSDocument * gs)
722 int std_in[2] = { -1, -1 }; /* pipe to interp stdin */
723 int std_out[2]; /* pipe from interp stdout */
724 int std_err[2]; /* pipe from interp stderr */
726 LOG ("Start the interpreter");
729 #define NUM_GS_ARGS (NUM_ARGS - 20)
730 #define NUM_ALPHA_ARGS 10
732 char *argv[NUM_ARGS], *dir, *gv_env;
733 char **gs_args, **alpha_args = NULL;
739 stop_interpreter(gs);
741 if(gs->disable_start == TRUE)
744 /* set up the args... */
745 gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS);
746 for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++)
747 argv[argc] = gs_args[i];
749 if(gs->antialiased) {
750 if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
751 alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
753 alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(),
754 " ", NUM_ALPHA_ARGS);
755 for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++)
756 argv[argc] = alpha_args[i];
759 argv[argc++] = "-sDEVICE=x11";
760 argv[argc++] = "-dNOPAUSE";
761 argv[argc++] = "-dQUIET";
762 /* I assume we do _not_ want to change this... (: */
763 argv[argc++] = "-dSAFER";
765 /* set up the pipes */
766 if(gs->send_filename_to_gs) {
767 argv[argc++] = PS_DOCUMENT_GET_PS_FILE(gs);
769 argv[argc++] = "quit";
776 if(!gs->reading_from_pipe && !gs->send_filename_to_gs) {
777 if(pipe(std_in) == -1) {
778 g_critical("Unable to open pipe to Ghostscript.");
782 if(pipe(std_out) == -1) {
786 if(pipe(std_err) == -1) {
792 gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld",
793 gdk_x11_drawable_get_xid(gs->pstarget),
794 gdk_x11_drawable_get_xid(gs->bpixmap));
796 LOG ("Launching ghostview with env %s", gv_env);
799 gs->interpreter_pid = fork();
800 switch (gs->interpreter_pid) {
816 if(!gs->reading_from_pipe) {
817 if(gs->send_filename_to_gs) {
819 /* just in case gs tries to read from stdin */
820 stdinfd = open("/dev/null", O_RDONLY);
835 /* change to directory where the input file is. This helps
836 * with postscript-files which include other files using
838 dir = g_path_get_dirname(gs->gs_filename);
842 execvp(argv[0], argv);
845 g_print("Unable to execute [%s]\n", argv[0]);
849 g_strfreev(alpha_args);
852 default: /* parent */
853 if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
856 /* use non-blocking IO for pipe to ghostscript */
857 result = fcntl(std_in[1], F_GETFL, 0);
858 fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
859 gs->interpreter_input = std_in[1];
862 gs->interpreter_input = -1;
865 gs->interpreter_output = std_out[0];
867 gs->interpreter_err = std_err[0];
868 gs->interpreter_output_id =
869 gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
870 gs->interpreter_error_id =
871 gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
878 stop_interpreter(PSDocument * gs)
880 if(gs->interpreter_pid > 0) {
882 LOG ("Stop the interpreter");
883 kill(gs->interpreter_pid, SIGTERM);
884 while((wait(&status) == -1) && (errno == EINTR)) ;
885 gs->interpreter_pid = -1;
887 ps_document_cleanup(gs);
888 gs->gs_status = _("Interpreter failed.");
892 if(gs->interpreter_input >= 0) {
893 close(gs->interpreter_input);
894 gs->interpreter_input = -1;
895 if(gs->interpreter_input_id != 0) {
896 gdk_input_remove(gs->interpreter_input_id);
897 gs->interpreter_input_id = 0;
899 while(gs->ps_input) {
900 struct record_list *ps_old = gs->ps_input;
901 gs->ps_input = gs->ps_input->next;
902 if(ps_old->close && NULL != ps_old->fp)
904 g_free((char *) ps_old);
908 if(gs->interpreter_output >= 0) {
909 close(gs->interpreter_output);
910 gs->interpreter_output = -1;
911 if(gs->interpreter_output_id) {
912 gdk_input_remove(gs->interpreter_output_id);
913 gs->interpreter_output_id = 0;
917 if(gs->interpreter_err >= 0) {
918 close(gs->interpreter_err);
919 gs->interpreter_err = -1;
920 if(gs->interpreter_error_id) {
921 gdk_input_remove(gs->interpreter_error_id);
922 gs->interpreter_error_id = 0;
929 /* If file exists and is a regular file then return its length, else -1 */
931 file_length(const gchar * filename)
933 struct stat stat_rec;
935 if(filename && (stat(filename, &stat_rec) == 0)
936 && S_ISREG(stat_rec.st_mode))
937 return stat_rec.st_size;
942 /* Test if file exists, is a regular file and its length is > 0 */
944 file_readable(const char *filename)
946 return (file_length(filename) > 0);
950 * Decompress gs->gs_filename if necessary
951 * Set gs->filename_unc to the name of the uncompressed file or NULL.
952 * Error reporting via signal 'interpreter_message'
953 * Return name of input file to use or NULL on error..
956 check_filecompressed(PSDocument * gs)
960 gchar *filename, *filename_unc, *filename_err, *cmdline;
966 if((file = fopen(gs->gs_filename, "r"))
967 && (fread(buf, sizeof(gchar), 3, file) == 3)) {
968 if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
969 /* file is gzipped or compressed */
970 cmd = gtk_gs_defaults_get_ungzip_cmd();
972 else if(strncmp(buf, "BZh", 3) == 0) {
973 /* file is compressed with bzip2 */
974 cmd = gtk_gs_defaults_get_unbzip2_cmd();
981 return gs->gs_filename;
983 /* do the decompression */
984 filename = g_shell_quote(gs->gs_filename);
985 filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
986 if((fd = mkstemp(filename_unc)) < 0) {
987 g_free(filename_unc);
992 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
993 if((fd = mkstemp(filename_err)) < 0) {
994 g_free(filename_err);
995 g_free(filename_unc);
1000 cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
1001 filename, filename_unc, filename_err);
1002 if((system(cmdline) == 0)
1003 && file_readable(filename_unc)
1004 && (file_length(filename_err) == 0)) {
1005 /* sucessfully uncompressed file */
1006 gs->gs_filename_unc = filename_unc;
1010 g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
1012 ps_document_emit_error_msg(gs, buf);
1013 if(file_length(filename_err) > 0) {
1015 if((err = fopen(filename_err, "r"))) {
1016 /* print file to message window */
1017 while(fgets(buf, 1024, err))
1018 ps_document_emit_error_msg(gs, buf);
1022 unlink(filename_unc);
1023 g_free(filename_unc);
1024 filename_unc = NULL;
1026 unlink(filename_err);
1027 g_free(filename_err);
1030 return filename_unc;
1034 * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
1035 * pdf file if necessary.
1036 * Set gs->filename_dsc to the name of the dsc file or NULL.
1037 * Error reporting via signal 'interpreter_message'.
1040 check_pdf(PSDocument * gs)
1043 gchar buf[1024], *filename;
1046 /* use uncompressed file as input if necessary */
1047 filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
1049 if((file = fopen(filename, "r"))
1050 && (fread(buf, sizeof(char), 5, file) == 5)
1051 && (strncmp(buf, "%PDF-", 5) == 0)) {
1052 /* we found a PDF file */
1053 gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
1054 filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1055 if((fd = mkstemp(filename_dsc)) < 0) {
1059 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1060 if((fd = mkstemp(filename_err)) < 0) {
1061 g_free(filename_dsc);
1065 fname = g_shell_quote(filename);
1066 cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1068 /* this command (sometimes?) prints error messages to stdout! */
1069 cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1072 if((system(cmdline) == 0) && file_readable(filename_dsc)) {
1075 filename = gs->gs_filename_dsc = filename_dsc;
1077 if(file_length(filename_err) > 0) {
1078 gchar *err_msg = " ";
1083 if((err = fopen(filename_err, "r"))) {
1085 /* print the content of the file to a message box */
1086 while(fgets(buf, 1024, err))
1087 err_msg = g_strconcat(err_msg, buf, NULL);
1089 /* FIXME The dialog is not yet set to modal, difficult to
1090 * get the parent of the dialog box here
1093 dialog = gtk_message_dialog_new(NULL,
1095 GTK_MESSAGE_WARNING,
1097 ("There was an error while scaning the file: %s \n%s"),
1098 gs->gs_filename, err_msg);
1100 gdk_color_parse("white", &color);
1101 gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1103 g_signal_connect(G_OBJECT(dialog), "response",
1104 G_CALLBACK(gtk_widget_destroy), NULL);
1106 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1107 gtk_widget_show(dialog);
1115 g_snprintf(buf, 1024,
1116 _("Error while converting pdf file %s:\n"), filename);
1117 ps_document_emit_error_msg(gs, buf);
1119 if(file_length(filename_err) > 0) {
1121 if((err = fopen(filename_err, "r"))) {
1122 /* print file to message window */
1123 while(fgets(buf, 1024, err))
1124 ps_document_emit_error_msg(gs, buf);
1127 unlink(filename_dsc);
1128 g_free(filename_dsc);
1131 unlink(filename_err);
1132 g_free(filename_err);
1140 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1141 /* never mind this patch: a properly working X server should take care of
1142 calculating the proper values. */
1146 # ifndef HAVE_XINERAMA
1147 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1150 dpy = (Display *) GDK_DISPLAY();
1151 if(XineramaIsActive(dpy)) {
1153 XineramaScreenInfo *head_info;
1154 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1155 /* fake it with dimensions of the first head for now */
1156 return 25.4 * head_info[0].width / gdk_screen_width_mm();
1159 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1168 # ifndef HAVE_XINERAMA
1169 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1172 dpy = (Display *) GDK_DISPLAY();
1173 if(XineramaIsActive(dpy)) {
1175 XineramaScreenInfo *head_info;
1176 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1177 /* fake it with dimensions of the first head for now */
1178 return 25.4 * head_info[0].height / gdk_screen_height_mm();
1181 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1190 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1196 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1198 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1200 /* Compute new size of window, sets xdpi and ydpi if necessary.
1201 * returns True if new window size is different */
1203 compute_size(PSDocument * gs)
1205 guint new_width = 1;
1206 guint new_height = 1;
1207 gboolean change = FALSE;
1210 /* width and height can be changed, calculate window size according */
1211 /* to xpdi and ydpi */
1212 orientation = ps_document_get_orientation(gs);
1214 switch (orientation) {
1215 case GTK_GS_ORIENTATION_PORTRAIT:
1216 case GTK_GS_ORIENTATION_UPSIDEDOWN:
1217 new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1218 new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1220 case GTK_GS_ORIENTATION_LANDSCAPE:
1221 case GTK_GS_ORIENTATION_SEASCAPE:
1222 new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1223 new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1227 change = (new_width != gs->width * gs->zoom_factor)
1228 || (new_height != gs->height * gs->zoom_factor);
1229 gs->width = (gint) (new_width * gs->zoom_factor);
1230 gs->height = (gint) (new_height * gs->zoom_factor);
1236 ps_document_enable_interpreter(PSDocument * gs)
1238 g_return_val_if_fail(gs != NULL, FALSE);
1239 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1241 if(!gs->gs_filename)
1244 gs->disable_start = FALSE;
1246 return start_interpreter(gs);
1249 /* publicly accessible functions */
1252 ps_document_get_type(void)
1254 static GType gs_type = 0;
1256 GTypeInfo gs_info = {
1257 sizeof(PSDocumentClass),
1258 (GBaseInitFunc) NULL,
1259 (GBaseFinalizeFunc) NULL,
1260 (GClassInitFunc) ps_document_class_init,
1261 (GClassFinalizeFunc) NULL,
1262 NULL, /* class_data */
1264 0, /* n_preallocs */
1265 (GInstanceInitFunc) ps_document_init
1268 static const GInterfaceInfo document_info =
1270 (GInterfaceInitFunc) ps_document_document_iface_init,
1275 gs_type = g_type_register_static(G_TYPE_OBJECT,
1276 "PSDocument", &gs_info, 0);
1278 g_type_add_interface_static (gs_type,
1288 * Show error message -> send signal "interpreter_message"
1291 ps_document_emit_error_msg(PSDocument * gs, const gchar * msg)
1293 gdk_pointer_ungrab(GDK_CURRENT_TIME);
1294 if(strstr(msg, "Error:")) {
1295 gs->gs_status = _("File is not a valid PostScript document.");
1296 ps_document_cleanup(gs);
1301 document_load(PSDocument * gs, const gchar * fname)
1303 g_return_val_if_fail(gs != NULL, FALSE);
1304 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1306 LOG ("Load the document");
1308 /* clean up previous document */
1309 ps_document_cleanup(gs);
1316 /* prepare this document */
1318 /* default values: no dsc information available */
1319 gs->structured_doc = FALSE;
1320 gs->send_filename_to_gs = TRUE;
1321 gs->current_page = 0;
1324 /* an absolute path */
1325 gs->gs_filename = g_strdup(fname);
1328 /* path relative to our cwd: make it absolute */
1329 gchar *cwd = g_get_current_dir();
1330 gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1334 if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1335 gs->send_filename_to_gs = FALSE;
1339 * We need to make sure that the file is loadable/exists!
1340 * otherwise we want to exit without loading new stuff...
1342 gchar *filename = NULL;
1344 if(!file_readable(fname)) {
1346 g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1347 ps_document_emit_error_msg(gs, buf);
1348 gs->gs_status = _("File is not readable.");
1351 filename = check_filecompressed(gs);
1353 filename = check_pdf(gs);
1356 if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1357 ps_document_cleanup(gs);
1361 /* we grab the vital statistics!!! */
1362 gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1364 g_object_notify (G_OBJECT (gs), "title");
1366 if(gs->doc == NULL) {
1367 /* File does not seem to be a Postscript one */
1369 g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1370 ps_document_emit_error_msg(gs, buf);
1371 ps_document_cleanup(gs);
1372 gs->gs_status = _("The file is not a PostScript document.");
1376 if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1377 (gs->doc->epsf && gs->doc->numpages > 1)) {
1378 gs->structured_doc = TRUE;
1379 gs->send_filename_to_gs = FALSE;
1382 /* We have to set up the orientation of the document */
1385 /* orientation can only be portrait, and landscape or none.
1386 This is the document default. A document can have
1387 pages in landscape and some in portrait */
1388 if(gs->override_orientation) {
1389 /* If the orientation should be override...
1390 then gs->orientation has already the correct
1391 value (it was set when the widget was created */
1396 /* Otherwise, set the proper orientation for the doc */
1397 gs->real_orientation = gs->doc->orientation;
1400 ps_document_set_page_size(gs, -1, gs->current_page);
1403 gs->gs_status = _("Document loaded.");
1410 ps_document_next_page(PSDocument * gs)
1414 LOG ("Make ghostscript render next page");
1416 g_return_val_if_fail(gs != NULL, FALSE);
1417 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1419 if(gs->interpreter_pid == 0) { /* no interpreter active */
1423 if(gs->busy) { /* interpreter is busy */
1429 event.xclient.type = ClientMessage;
1430 event.xclient.display = gdk_display;
1431 event.xclient.window = gs->message_window;
1432 event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1433 event.xclient.format = 32;
1435 gdk_error_trap_push();
1436 XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1438 gdk_error_trap_pop();
1444 ps_document_goto_page(PSDocument * gs, gint page)
1446 g_return_val_if_fail(gs != NULL, FALSE);
1447 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1449 LOG ("Go to page %d", page);
1451 if(!gs->gs_filename) {
1455 /* range checking... */
1459 if(gs->structured_doc && gs->doc) {
1461 LOG ("It's a structured document, let's send one page to gs");
1463 if(page >= gs->doc->numpages)
1464 page = gs->doc->numpages - 1;
1466 if(page == gs->current_page && !gs->changed)
1469 gs->current_page = page;
1471 if(gs->doc->pages[page].orientation != NONE &&
1472 !gs->override_orientation &&
1473 gs->doc->pages[page].orientation != gs->real_orientation) {
1474 gs->real_orientation = gs->doc->pages[page].orientation;
1478 ps_document_set_page_size(gs, -1, page);
1480 gs->changed = FALSE;
1482 if(is_interpreter_ready(gs)) {
1483 ps_document_next_page(gs);
1486 ps_document_enable_interpreter(gs);
1487 send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1488 send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1491 send_ps(gs, gs->doc->pages[gs->current_page].begin,
1492 gs->doc->pages[gs->current_page].len, FALSE);
1495 /* Unstructured document */
1496 /* In the case of non structured documents,
1497 GS read the PS from the actual file (via command
1498 line. Hence, ggv only send a signal next page.
1499 If ghostview is not running it is usually because
1500 the last page of the file was displayed. In that
1501 case, ggv restarts GS again and the first page is displayed.
1504 LOG ("It's an unstructured document, gs will just read the file");
1506 if(page == gs->current_page && !gs->changed)
1509 ps_document_set_page_size(gs, -1, page);
1511 if(!is_interpreter_ready(gs))
1512 ps_document_enable_interpreter(gs);
1514 gs->current_page = page;
1516 ps_document_next_page(gs);
1522 * set pagesize sets the size from
1523 * if new_pagesize is -1, then it is set to either
1524 * a) the default settings of pageid, if they exist, or if pageid != -1.
1525 * b) the default setting of the document, if it exists.
1526 * c) the default setting of the widget.
1527 * otherwise, the new_pagesize is used as the pagesize
1530 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1536 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1538 LOG ("Set the page size");
1540 g_return_val_if_fail(gs != NULL, FALSE);
1541 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1543 if(new_pagesize == -1) {
1544 if(gs->default_size > 0)
1545 new_pagesize = gs->default_size;
1546 if(!gs->override_size && gs->doc) {
1547 /* If we have a document:
1548 We use -- the page size (if specified)
1549 or the doc. size (if specified)
1550 or the page bbox (if specified)
1553 if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1554 (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1555 new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1557 else if(gs->doc->default_page_size != NULL) {
1558 new_pagesize = gs->doc->default_page_size - gs->doc->size;
1560 else if((pageid >= 0) &&
1561 (gs->doc->numpages > pageid) &&
1563 (gs->doc->pages[pageid].boundingbox[URX] >
1564 gs->doc->pages[pageid].boundingbox[LLX]) &&
1565 (gs->doc->pages[pageid].boundingbox[URY] >
1566 gs->doc->pages[pageid].boundingbox[LLY])) {
1569 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1570 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1576 /* Compute bounding box */
1577 if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) { /* epsf or bbox */
1580 (gs->doc->pages[pageid].boundingbox[URX] >
1581 gs->doc->pages[pageid].boundingbox[LLX])
1582 && (gs->doc->pages[pageid].boundingbox[URY] >
1583 gs->doc->pages[pageid].boundingbox[LLY])) {
1585 new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1586 new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1587 new_urx = gs->doc->pages[pageid].boundingbox[URX];
1588 new_ury = gs->doc->pages[pageid].boundingbox[URY];
1590 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1591 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1593 new_llx = gs->doc->boundingbox[LLX];
1594 new_lly = gs->doc->boundingbox[LLY];
1595 new_urx = gs->doc->boundingbox[URX];
1596 new_ury = gs->doc->boundingbox[URY];
1600 if(new_pagesize < 0)
1601 new_pagesize = gs->default_size;
1602 new_llx = new_lly = 0;
1603 if(gs->doc && !gs->override_size && gs->doc->size &&
1604 (new_pagesize < gs->doc->numsizes)) {
1605 new_urx = gs->doc->size[new_pagesize].width;
1606 new_ury = gs->doc->size[new_pagesize].height;
1609 new_urx = papersizes[new_pagesize].width;
1610 new_ury = papersizes[new_pagesize].height;
1614 if(new_urx <= new_llx)
1615 new_urx = papersizes[12].width;
1616 if(new_ury <= new_lly)
1617 new_ury = papersizes[12].height;
1619 /* If bounding box changed, setup for new size. */
1620 /* ps_document_disable_interpreter (gs); */
1621 if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1622 (new_urx != gs->urx) || (new_ury != gs->ury)) {
1639 ps_document_set_zoom (PSDocument * gs, gfloat zoom)
1641 g_return_if_fail (gs != NULL);
1643 gs->zoom_factor = zoom;
1645 if (gs->pstarget != NULL) {
1649 ps_document_goto_page (gs, gs->current_page);
1654 ps_document_load (EvDocument *document,
1661 filename = g_filename_from_uri (uri, NULL, error);
1665 result = document_load (PS_DOCUMENT (document), filename);
1667 g_set_error (error, G_FILE_ERROR,
1668 G_FILE_ERROR_FAILED,
1669 "Failed to load document '%s'\n",
1679 ps_document_save (EvDocument *document,
1683 g_warning ("ps_document_save not implemented"); /* FIXME */
1688 ps_document_get_n_pages (EvDocument *document)
1690 PSDocument *ps = PS_DOCUMENT (document);
1692 g_return_val_if_fail (ps != NULL, -1);
1694 if (!ps->gs_filename || !ps->doc) {
1698 return ps->structured_doc ? ps->doc->numpages : 1;
1702 ps_document_set_page (EvDocument *document,
1705 ps_document_goto_page (PS_DOCUMENT (document), page - 1);
1709 ps_document_get_page (EvDocument *document)
1711 PSDocument *ps = PS_DOCUMENT (document);
1713 g_return_val_if_fail (ps != NULL, -1);
1715 return ps->current_page + 1;
1719 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
1721 PSDocument *gs = (PSDocument *) data;
1723 if(event->type != GDK_CLIENT_EVENT)
1726 gs->message_window = event->client.data.l[0];
1728 if (event->client.message_type == gs_class->page_atom) {
1729 LOG ("GS rendered the document");
1733 ev_document_scale_changed (EV_DOCUMENT (gs));
1734 gs->scaling = FALSE;
1736 ev_document_page_changed (EV_DOCUMENT (gs));
1744 ps_document_set_target (EvDocument *document,
1745 GdkDrawable *target)
1747 PSDocument *gs = PS_DOCUMENT (document);
1751 gs->pstarget = target;
1754 gdk_window_get_user_data (gs->pstarget, &data);
1755 g_return_if_fail (GTK_IS_WIDGET (data));
1757 widget = GTK_WIDGET (data);
1758 g_signal_connect (widget, "event",
1759 G_CALLBACK (ps_document_widget_event),
1763 ps_document_goto_page (gs, gs->current_page);
1767 ps_document_set_scale (EvDocument *document,
1770 ps_document_set_zoom (PS_DOCUMENT (document), scale);
1774 ps_document_set_page_offset (EvDocument *document,
1778 PSDocument *gs = PS_DOCUMENT (document);
1780 gs->page_x_offset = x;
1781 gs->page_y_offset = y;
1785 ps_document_get_page_size (EvDocument *document,
1790 /* Post script documents never vary in size */
1792 PSDocument *gs = PS_DOCUMENT (document);
1799 *height = gs->height;
1804 ps_document_render (EvDocument *document,
1810 PSDocument *gs = PS_DOCUMENT (document);
1815 if (gs->pstarget == NULL ||
1816 gs->bpixmap == NULL) {
1820 page.x = gs->page_x_offset;
1821 page.y = gs->page_y_offset;
1822 page.width = gs->width;
1823 page.height = gs->height;
1827 draw.width = clip_width;
1828 draw.height = clip_height;
1830 gc = gdk_gc_new (gs->pstarget);
1832 gdk_draw_drawable (gs->pstarget, gc,
1834 draw.x - page.x, draw.y - page.y,
1836 draw.width, draw.height);
1838 LOG ("Copy the internal pixmap: %d %d %d %d %d %d",
1839 draw.x - page.x, draw.y - page.y,
1840 draw.x, draw.y, draw.width, draw.height);
1842 g_object_unref (gc);
1846 ps_document_get_text (EvDocument *document, GdkRectangle *rect)
1848 g_warning ("ps_document_get_text not implemented"); /* FIXME ? */
1853 ps_document_get_link (EvDocument *document,
1861 ps_document_document_iface_init (EvDocumentIface *iface)
1863 iface->load = ps_document_load;
1864 iface->save = ps_document_save;
1865 iface->get_text = ps_document_get_text;
1866 iface->get_link = ps_document_get_link;
1867 iface->get_n_pages = ps_document_get_n_pages;
1868 iface->set_page = ps_document_set_page;
1869 iface->get_page = ps_document_get_page;
1870 iface->set_scale = ps_document_set_scale;
1871 iface->set_target = ps_document_set_target;
1872 iface->set_page_offset = ps_document_set_page_offset;
1873 iface->get_page_size = ps_document_get_page_size;
1874 iface->render = ps_document_render;