1 /* Ghostscript widget for GTK/GNOME
3 * Copyright (C) 1998 - 2005 the Free Software Foundation
5 * Authors: Jonathan Blandford, Jaka Mocnik
7 * Based on code by: Federico Mena (Quartic), Szekeres Istvan (Pista)
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
26 Ghostview interface to ghostscript
28 When the GHOSTVIEW environment variable is set, ghostscript draws on
29 an existing drawable rather than creating its own window. Ghostscript
30 can be directed to draw on either a window or a pixmap.
34 The GHOSTVIEW environment variable contains the window id of the target
35 window. The window id is an integer. Ghostscript will use the attributes
36 of the window to obtain the width, height, colormap, screen, and visual of
37 the window. The remainder of the information is gotten from the GHOSTVIEW
38 property on that window.
43 The GHOSTVIEW environment variable contains a window id and a pixmap id.
44 They are integers separated by white space. Ghostscript will use the
45 attributes of the window to obtain the colormap, screen, and visual to use.
46 The width and height will be obtained from the pixmap. The remainder of the
47 information, is gotten from the GHOSTVIEW property on the window. In this
48 case, the property is deleted when read.
50 The GHOSTVIEW environment variable
52 parameters: window-id [pixmap-id]
56 explanation of parameters:
58 window-id: tells ghostscript where to
59 - read the GHOSTVIEW property
61 If pixmap-id is not present,
62 ghostscript will draw on this window.
64 pixmap-id: If present, tells ghostscript that a pixmap will be used
65 as the final destination for drawing. The window will
66 not be touched for drawing purposes.
68 The GHOSTVIEW property
74 bpixmap orient llx lly urx ury xdpi ydpi [left bottom top right]
76 scanf format: "%d %d %d %d %d %d %f %f %d %d %d %d"
78 explanation of parameters:
80 bpixmap: pixmap id of the backing pixmap for the window. If no
81 pixmap is to be used, this parameter should be zero. This
82 parameter must be zero when drawing on a pixmap.
84 orient: orientation of the page. The number represents clockwise
85 rotation of the paper in degrees. Permitted values are
88 llx, lly, urx, ury: Bounding box of the drawable. The bounding box
89 is specified in PostScript points in default user coordinates.
91 xdpi, ydpi: Resolution of window. (This can be derived from the
92 other parameters, but not without roundoff error. These
93 values are included to avoid this error.)
95 left, bottom, top, right: (optional)
96 Margins around the window. The margins extend the imageable
97 area beyond the boundaries of the window. This is primarily
98 used for popup zoom windows. I have encountered several
99 instances of PostScript programs that position themselves
100 with respect to the imageable area. The margins are specified
101 in PostScript points. If omitted, the margins are assumed to
104 Events from ghostscript
106 If the final destination is a pixmap, the client will get a property notify
107 event when ghostscript reads the GHOSTVIEW property causing it to be deleted.
109 Ghostscript sends events to the window where it read the GHOSTVIEW property.
110 These events are of type ClientMessage. The message_type is set to
111 either PAGE or DONE. The first long data value gives the window to be used
112 to send replies to ghostscript. The second long data value gives the primary
113 drawable. If rendering to a pixmap, it is the primary drawable. If rendering
114 to a window, the backing pixmap is the primary drawable. If no backing pixmap
115 is employed, then the window is the primary drawable. This field is necessary
116 to distinguish multiple ghostscripts rendering to separate pixmaps where the
117 GHOSTVIEW property was placed on the same window.
119 The PAGE message indicates that a "page" has completed. Ghostscript will
120 wait until it receives a ClientMessage whose message_type is NEXT before
123 The DONE message indicates that ghostscript has finished processing.
132 #include <gtk/gtkobject.h>
133 #include <gdk/gdkprivate.h>
134 #include <gdk/gdkx.h>
136 #include <glib/gi18n.h>
138 # include <gdk/gdkx.h>
139 # include <X11/extensions/Xinerama.h>
140 #endif /* HAVE_XINERAMA */
141 #include <X11/Intrinsic.h>
146 #include <sys/stat.h>
147 #include <sys/types.h>
148 #include <sys/wait.h>
152 #include "ps-document.h"
153 #include "gsdefaults.h"
159 /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
160 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
161 # define O_NONBLOCK O_NDELAY
164 #define PS_DOCUMENT_WATCH_INTERVAL 1000
165 #define PS_DOCUMENT_WATCH_TIMEOUT 2
167 #define MAX_BUFSIZE 1024
169 #define PS_DOCUMENT_IS_COMPRESSED(gs) (PS_DOCUMENT(gs)->gs_filename_unc != NULL)
170 #define PS_DOCUMENT_GET_PS_FILE(gs) (PS_DOCUMENT_IS_COMPRESSED(gs) ? \
171 PS_DOCUMENT(gs)->gs_filename_unc : \
172 PS_DOCUMENT(gs)->gs_filename)
174 enum { INTERPRETER_MESSAGE, INTERPRETER_ERROR, LAST_SIGNAL };
181 /* structure to describe section of file to send to ghostscript */
186 gboolean seek_needed;
188 struct record_list *next;
191 static gboolean broken_pipe = FALSE;
199 /* Forward declarations */
200 static void ps_document_init(PSDocument * gs);
201 static void ps_document_class_init(PSDocumentClass * klass);
202 static void ps_document_emit_error_msg(PSDocument * gs, const gchar * msg);
203 static void ps_document_finalize(GObject * object);
204 static void send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close);
205 static void set_up_page(PSDocument * gs);
206 static void close_pipe(int p[2]);
207 static void interpreter_failed(PSDocument * gs);
208 static float compute_xdpi(void);
209 static float compute_ydpi(void);
210 static gboolean compute_size(PSDocument * gs);
211 static void output(gpointer data, gint source, GdkInputCondition condition);
212 static void input(gpointer data, gint source, GdkInputCondition condition);
213 static void stop_interpreter(PSDocument * gs);
214 static gint start_interpreter(PSDocument * gs);
215 gboolean computeSize(void);
216 static gboolean ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid);
217 static void ps_document_document_iface_init (EvDocumentIface *iface);
219 static GObjectClass *parent_class = NULL;
221 static PSDocumentClass *gs_class = NULL;
224 ps_document_init(PSDocument * gs)
228 gs->current_page = -2;
229 gs->disable_start = FALSE;
230 gs->interpreter_pid = -1;
236 gs->gs_scanstyle = 0;
238 gs->gs_filename_dsc = 0;
239 gs->gs_filename_unc = 0;
243 gs->structured_doc = FALSE;
244 gs->reading_from_pipe = FALSE;
245 gs->send_filename_to_gs = FALSE;
250 gs->interpreter_input = -1;
251 gs->interpreter_output = -1;
252 gs->interpreter_err = -1;
253 gs->interpreter_input_id = 0;
254 gs->interpreter_output_id = 0;
255 gs->interpreter_error_id = 0;
258 gs->input_buffer = NULL;
259 gs->input_buffer_ptr = NULL;
261 gs->buffer_bytes_left = 0;
267 gs->xdpi = compute_xdpi();
268 gs->ydpi = compute_ydpi();
272 gs->right_margin = 0;
273 gs->bottom_margin = 0;
275 /* Set user defined defaults */
276 gs->override_orientation = gtk_gs_defaults_get_override_orientation();
277 gs->fallback_orientation = gtk_gs_defaults_get_orientation();
278 gs->zoom_factor = gtk_gs_defaults_get_zoom_factor();
279 gs->default_size = gtk_gs_defaults_get_size();
280 gs->antialiased = gtk_gs_defaults_get_antialiased();
281 gs->override_size = gtk_gs_defaults_get_override_size();
282 gs->respect_eof = gtk_gs_defaults_get_respect_eof();
283 gs->zoom_mode = gtk_gs_defaults_get_zoom_mode();
285 gs->gs_status = _("No document loaded.");
289 ps_document_set_property (GObject *object,
304 ps_document_get_property (GObject *object,
309 PSDocument *ps = PS_DOCUMENT (object);
315 g_value_set_string (value, ps->doc->title);
317 g_value_set_string (value, NULL);
324 ps_document_class_init(PSDocumentClass * klass)
326 GObjectClass *object_class;
328 object_class = (GObjectClass *) klass;
329 parent_class = gtk_type_class(gtk_widget_get_type());
332 object_class->finalize = ps_document_finalize;
333 object_class->get_property = ps_document_get_property;
334 object_class->set_property = ps_document_set_property;
337 klass->gs_atom = gdk_atom_intern("GHOSTVIEW", FALSE);
338 klass->next_atom = gdk_atom_intern("NEXT", FALSE);
339 klass->page_atom = gdk_atom_intern("PAGE", FALSE);
340 klass->string_atom = gdk_atom_intern("STRING", FALSE);
342 gtk_gs_defaults_load();
344 g_object_class_override_property (object_class, PROP_TITLE, "title");
347 /* Clean all memory and temporal files */
349 ps_document_cleanup(PSDocument * gs)
351 g_return_if_fail(gs != NULL);
352 g_return_if_fail(GTK_IS_GS(gs));
354 stop_interpreter(gs);
357 fclose(gs->gs_psfile);
358 gs->gs_psfile = NULL;
360 if(gs->gs_filename) {
361 g_free(gs->gs_filename);
362 gs->gs_filename = NULL;
368 if(gs->gs_filename_dsc) {
369 unlink(gs->gs_filename_dsc);
370 g_free(gs->gs_filename_dsc);
371 gs->gs_filename_dsc = NULL;
373 if(gs->gs_filename_unc) {
374 unlink(gs->gs_filename_unc);
375 g_free(gs->gs_filename_unc);
376 gs->gs_filename_unc = NULL;
378 gs->current_page = -1;
388 ps_document_finalize(GObject * object)
392 g_return_if_fail(object != NULL);
393 g_return_if_fail(GTK_IS_GS(object));
395 gs = PS_DOCUMENT(object);
397 ps_document_cleanup(gs);
399 if(gs->input_buffer) {
400 g_free(gs->input_buffer);
401 gs->input_buffer = NULL;
404 (*G_OBJECT_CLASS(parent_class)->finalize) (object);
408 send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close)
410 struct record_list *ps_new;
412 if(gs->interpreter_input < 0) {
413 g_critical("No pipe to gs: error in send_ps().");
417 ps_new = (struct record_list *) g_malloc(sizeof(struct record_list));
418 ps_new->fp = gs->gs_psfile;
419 ps_new->begin = begin;
421 ps_new->seek_needed = TRUE;
422 ps_new->close = close;
425 if(gs->input_buffer == NULL) {
426 gs->input_buffer = g_malloc(MAX_BUFSIZE);
429 if(gs->ps_input == NULL) {
430 gs->input_buffer_ptr = gs->input_buffer;
431 gs->bytes_left = len;
432 gs->buffer_bytes_left = 0;
433 gs->ps_input = ps_new;
434 gs->interpreter_input_id =
435 gdk_input_add(gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
438 struct record_list *p = gs->ps_input;
439 while(p->next != NULL) {
447 ps_document_get_orientation(PSDocument * gs)
449 g_return_val_if_fail(gs != NULL, -1);
450 g_return_val_if_fail(GTK_IS_GS(gs), -1);
453 if(gs->structured_doc) {
454 if(gs->doc->pages[MAX(gs->current_page, 0)].orientation !=
455 GTK_GS_ORIENTATION_NONE)
456 gs->real_orientation =
457 gs->doc->pages[MAX(gs->current_page, 0)].orientation;
459 gs->real_orientation = gs->doc->default_page_orientation;
462 if(gs->real_orientation == GTK_GS_ORIENTATION_NONE)
463 gs->real_orientation = gs->doc->orientation;
466 if(gs->override_orientation ||
467 gs->real_orientation == GTK_GS_ORIENTATION_NONE)
468 return gs->fallback_orientation;
470 return gs->real_orientation;
474 set_up_page(PSDocument * gs)
478 //GdkColormap *colormap;
480 GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */
481 GdkColormap *colormap;
487 if (gs->pstarget == NULL)
490 /* Do we have to check if the actual geometry changed? */
492 stop_interpreter(gs);
494 orientation = ps_document_get_orientation(gs);
496 if(compute_size(gs)) {
499 /* clear new pixmap (set to white) */
500 fill = gdk_gc_new(gs->pstarget);
502 colormap = gdk_drawable_get_colormap(gs->pstarget);
503 gdk_color_alloc (colormap, &white);
504 gdk_gc_set_foreground(fill, &white);
506 if(gs->width > 0 && gs->height > 0) {
508 gdk_drawable_unref(gs->bpixmap);
512 gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1);
514 gdk_draw_rectangle(gs->bpixmap, fill, TRUE,
515 0, 0, gs->width, gs->height);
518 gdk_draw_rectangle(gs->pstarget, fill, TRUE,
519 0, 0, gs->width, gs->height);
528 /* gs needs floating point parameters with '.' as decimal point
529 * while some (european) locales use ',' instead, so we set the
530 * locale for this snprintf to "C".
532 savelocale = setlocale(LC_NUMERIC, "C");
535 g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
542 gs->xdpi * gs->zoom_factor,
543 gs->ydpi * gs->zoom_factor,
545 gs->bottom_margin, gs->right_margin, gs->top_margin);
548 setlocale(LC_NUMERIC, savelocale);
550 gdk_property_change(gs->pstarget,
552 gs_class->string_atom,
553 8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
567 is_interpreter_ready(PSDocument * gs)
569 return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
573 interpreter_failed(PSDocument * gs)
575 stop_interpreter(gs);
579 output(gpointer data, gint source, GdkInputCondition condition)
581 char buf[MAX_BUFSIZE + 1], *msg;
583 PSDocument *gs = PS_DOCUMENT(data);
585 if(source == gs->interpreter_output) {
586 bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
587 if(bytes == 0) { /* EOF occurred */
588 close(gs->interpreter_output);
589 gs->interpreter_output = -1;
590 gdk_input_remove(gs->interpreter_output_id);
593 else if(bytes == -1) {
595 interpreter_failed(gs);
598 if(gs->interpreter_err == -1) {
599 stop_interpreter(gs);
602 else if(source == gs->interpreter_err) {
603 bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
604 if(bytes == 0) { /* EOF occurred */
605 close(gs->interpreter_err);
606 gs->interpreter_err = -1;
607 gdk_input_remove(gs->interpreter_error_id);
610 else if(bytes == -1) {
612 interpreter_failed(gs);
615 if(gs->interpreter_output == -1) {
616 stop_interpreter(gs);
622 ps_document_emit_error_msg (gs, msg);
627 input(gpointer data, gint source, GdkInputCondition condition)
629 PSDocument *gs = PS_DOCUMENT(data);
631 void (*oldsig) (int);
632 oldsig = signal(SIGPIPE, catchPipe);
635 if(gs->buffer_bytes_left == 0) {
636 /* Get a new section if required */
637 if(gs->ps_input && gs->bytes_left == 0) {
638 struct record_list *ps_old = gs->ps_input;
639 gs->ps_input = ps_old->next;
640 if(ps_old->close && NULL != ps_old->fp)
642 g_free((char *) ps_old);
644 /* Have to seek at the beginning of each section */
645 if(gs->ps_input && gs->ps_input->seek_needed) {
646 fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
647 gs->ps_input->seek_needed = FALSE;
648 gs->bytes_left = gs->ps_input->len;
651 if(gs->bytes_left > MAX_BUFSIZE) {
652 gs->buffer_bytes_left =
653 fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
655 else if(gs->bytes_left > 0) {
656 gs->buffer_bytes_left =
657 fread(gs->input_buffer,
658 sizeof(char), gs->bytes_left, gs->ps_input->fp);
661 gs->buffer_bytes_left = 0;
663 if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
664 interpreter_failed(gs); /* Error occurred */
666 gs->input_buffer_ptr = gs->input_buffer;
667 gs->bytes_left -= gs->buffer_bytes_left;
670 if(gs->buffer_bytes_left > 0) {
671 /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
673 bytes_written = write(gs->interpreter_input,
674 gs->input_buffer_ptr, gs->buffer_bytes_left);
677 ps_document_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
679 interpreter_failed(gs);
681 else if(bytes_written == -1) {
682 if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
683 interpreter_failed(gs); /* Something bad happened */
687 gs->buffer_bytes_left -= bytes_written;
688 gs->input_buffer_ptr += bytes_written;
692 while(gs->ps_input && gs->buffer_bytes_left == 0);
694 signal(SIGPIPE, oldsig);
696 if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
697 if(gs->interpreter_input_id != 0) {
698 gdk_input_remove(gs->interpreter_input_id);
699 gs->interpreter_input_id = 0;
705 start_interpreter(PSDocument * gs)
707 int std_in[2] = { -1, -1 }; /* pipe to interp stdin */
708 int std_out[2]; /* pipe from interp stdout */
709 int std_err[2]; /* pipe from interp stderr */
712 #define NUM_GS_ARGS (NUM_ARGS - 20)
713 #define NUM_ALPHA_ARGS 10
715 char *argv[NUM_ARGS], *dir, *gv_env;
716 char **gs_args, **alpha_args = NULL;
722 stop_interpreter(gs);
724 if(gs->disable_start == TRUE)
727 /* set up the args... */
728 gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS);
729 for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++)
730 argv[argc] = gs_args[i];
732 if(gs->antialiased) {
733 if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
734 alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
736 alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(),
737 " ", NUM_ALPHA_ARGS);
738 for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++)
739 argv[argc] = alpha_args[i];
742 argv[argc++] = "-sDEVICE=x11";
743 argv[argc++] = "-dNOPAUSE";
744 argv[argc++] = "-dQUIET";
745 /* I assume we do _not_ want to change this... (: */
746 argv[argc++] = "-dSAFER";
748 /* set up the pipes */
749 if(gs->send_filename_to_gs) {
750 argv[argc++] = PS_DOCUMENT_GET_PS_FILE(gs);
752 argv[argc++] = "quit";
759 if(!gs->reading_from_pipe && !gs->send_filename_to_gs) {
760 if(pipe(std_in) == -1) {
761 g_critical("Unable to open pipe to Ghostscript.");
765 if(pipe(std_out) == -1) {
769 if(pipe(std_err) == -1) {
776 gs->interpreter_pid = fork();
777 switch (gs->interpreter_pid) {
793 if(!gs->reading_from_pipe) {
794 if(gs->send_filename_to_gs) {
796 /* just in case gs tries to read from stdin */
797 stdinfd = open("/dev/null", O_RDONLY);
810 gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld",
811 gdk_x11_drawable_get_xid(gs->pstarget),
812 gdk_x11_drawable_get_xid(gs->bpixmap));
815 /* change to directory where the input file is. This helps
816 * with postscript-files which include other files using
818 dir = g_path_get_dirname(gs->gs_filename);
822 execvp(argv[0], argv);
825 g_print("Unable to execute [%s]\n", argv[0]);
829 g_strfreev(alpha_args);
832 default: /* parent */
833 if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
836 /* use non-blocking IO for pipe to ghostscript */
837 result = fcntl(std_in[1], F_GETFL, 0);
838 fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
839 gs->interpreter_input = std_in[1];
842 gs->interpreter_input = -1;
845 gs->interpreter_output = std_out[0];
847 gs->interpreter_err = std_err[0];
848 gs->interpreter_output_id =
849 gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
850 gs->interpreter_error_id =
851 gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
858 stop_interpreter(PSDocument * gs)
860 if(gs->interpreter_pid > 0) {
862 kill(gs->interpreter_pid, SIGTERM);
863 while((wait(&status) == -1) && (errno == EINTR)) ;
864 gs->interpreter_pid = -1;
866 ps_document_cleanup(gs);
867 gs->gs_status = _("Interpreter failed.");
871 if(gs->interpreter_input >= 0) {
872 close(gs->interpreter_input);
873 gs->interpreter_input = -1;
874 if(gs->interpreter_input_id != 0) {
875 gdk_input_remove(gs->interpreter_input_id);
876 gs->interpreter_input_id = 0;
878 while(gs->ps_input) {
879 struct record_list *ps_old = gs->ps_input;
880 gs->ps_input = gs->ps_input->next;
881 if(ps_old->close && NULL != ps_old->fp)
883 g_free((char *) ps_old);
887 if(gs->interpreter_output >= 0) {
888 close(gs->interpreter_output);
889 gs->interpreter_output = -1;
890 if(gs->interpreter_output_id) {
891 gdk_input_remove(gs->interpreter_output_id);
892 gs->interpreter_output_id = 0;
896 if(gs->interpreter_err >= 0) {
897 close(gs->interpreter_err);
898 gs->interpreter_err = -1;
899 if(gs->interpreter_error_id) {
900 gdk_input_remove(gs->interpreter_error_id);
901 gs->interpreter_error_id = 0;
908 /* If file exists and is a regular file then return its length, else -1 */
910 file_length(const gchar * filename)
912 struct stat stat_rec;
914 if(filename && (stat(filename, &stat_rec) == 0)
915 && S_ISREG(stat_rec.st_mode))
916 return stat_rec.st_size;
921 /* Test if file exists, is a regular file and its length is > 0 */
923 file_readable(const char *filename)
925 return (file_length(filename) > 0);
929 * Decompress gs->gs_filename if necessary
930 * Set gs->filename_unc to the name of the uncompressed file or NULL.
931 * Error reporting via signal 'interpreter_message'
932 * Return name of input file to use or NULL on error..
935 check_filecompressed(PSDocument * gs)
939 gchar *filename, *filename_unc, *filename_err, *cmdline;
945 if((file = fopen(gs->gs_filename, "r"))
946 && (fread(buf, sizeof(gchar), 3, file) == 3)) {
947 if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
948 /* file is gzipped or compressed */
949 cmd = gtk_gs_defaults_get_ungzip_cmd();
951 else if(strncmp(buf, "BZh", 3) == 0) {
952 /* file is compressed with bzip2 */
953 cmd = gtk_gs_defaults_get_unbzip2_cmd();
960 return gs->gs_filename;
962 /* do the decompression */
963 filename = g_shell_quote(gs->gs_filename);
964 filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
965 if((fd = mkstemp(filename_unc)) < 0) {
966 g_free(filename_unc);
971 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
972 if((fd = mkstemp(filename_err)) < 0) {
973 g_free(filename_err);
974 g_free(filename_unc);
979 cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
980 filename, filename_unc, filename_err);
981 if((system(cmdline) == 0)
982 && file_readable(filename_unc)
983 && (file_length(filename_err) == 0)) {
984 /* sucessfully uncompressed file */
985 gs->gs_filename_unc = filename_unc;
989 g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
991 ps_document_emit_error_msg(gs, buf);
992 if(file_length(filename_err) > 0) {
994 if((err = fopen(filename_err, "r"))) {
995 /* print file to message window */
996 while(fgets(buf, 1024, err))
997 ps_document_emit_error_msg(gs, buf);
1001 unlink(filename_unc);
1002 g_free(filename_unc);
1003 filename_unc = NULL;
1005 unlink(filename_err);
1006 g_free(filename_err);
1009 return filename_unc;
1013 * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
1014 * pdf file if necessary.
1015 * Set gs->filename_dsc to the name of the dsc file or NULL.
1016 * Error reporting via signal 'interpreter_message'.
1019 check_pdf(PSDocument * gs)
1022 gchar buf[1024], *filename;
1025 /* use uncompressed file as input if necessary */
1026 filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
1028 if((file = fopen(filename, "r"))
1029 && (fread(buf, sizeof(char), 5, file) == 5)
1030 && (strncmp(buf, "%PDF-", 5) == 0)) {
1031 /* we found a PDF file */
1032 gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
1033 filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1034 if((fd = mkstemp(filename_dsc)) < 0) {
1038 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1039 if((fd = mkstemp(filename_err)) < 0) {
1040 g_free(filename_dsc);
1044 fname = g_shell_quote(filename);
1045 cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1047 /* this command (sometimes?) prints error messages to stdout! */
1048 cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1051 if((system(cmdline) == 0) && file_readable(filename_dsc)) {
1054 filename = gs->gs_filename_dsc = filename_dsc;
1056 if(file_length(filename_err) > 0) {
1057 gchar *err_msg = " ";
1062 if((err = fopen(filename_err, "r"))) {
1064 /* print the content of the file to a message box */
1065 while(fgets(buf, 1024, err))
1066 err_msg = g_strconcat(err_msg, buf, NULL);
1068 /* FIXME The dialog is not yet set to modal, difficult to
1069 * get the parent of the dialog box here
1072 dialog = gtk_message_dialog_new(NULL,
1074 GTK_MESSAGE_WARNING,
1076 ("There was an error while scaning the file: %s \n%s"),
1077 gs->gs_filename, err_msg);
1079 gdk_color_parse("white", &color);
1080 gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1082 g_signal_connect(G_OBJECT(dialog), "response",
1083 G_CALLBACK(gtk_widget_destroy), NULL);
1085 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1086 gtk_widget_show(dialog);
1094 g_snprintf(buf, 1024,
1095 _("Error while converting pdf file %s:\n"), filename);
1096 ps_document_emit_error_msg(gs, buf);
1098 if(file_length(filename_err) > 0) {
1100 if((err = fopen(filename_err, "r"))) {
1101 /* print file to message window */
1102 while(fgets(buf, 1024, err))
1103 ps_document_emit_error_msg(gs, buf);
1106 unlink(filename_dsc);
1107 g_free(filename_dsc);
1110 unlink(filename_err);
1111 g_free(filename_err);
1119 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1120 /* never mind this patch: a properly working X server should take care of
1121 calculating the proper values. */
1125 # ifndef HAVE_XINERAMA
1126 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1129 dpy = (Display *) GDK_DISPLAY();
1130 if(XineramaIsActive(dpy)) {
1132 XineramaScreenInfo *head_info;
1133 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1134 /* fake it with dimensions of the first head for now */
1135 return 25.4 * head_info[0].width / gdk_screen_width_mm();
1138 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1147 # ifndef HAVE_XINERAMA
1148 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1151 dpy = (Display *) GDK_DISPLAY();
1152 if(XineramaIsActive(dpy)) {
1154 XineramaScreenInfo *head_info;
1155 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1156 /* fake it with dimensions of the first head for now */
1157 return 25.4 * head_info[0].height / gdk_screen_height_mm();
1160 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1169 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1175 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1177 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1179 /* Compute new size of window, sets xdpi and ydpi if necessary.
1180 * returns True if new window size is different */
1182 compute_size(PSDocument * gs)
1184 guint new_width = 1;
1185 guint new_height = 1;
1186 gboolean change = FALSE;
1189 /* width and height can be changed, calculate window size according */
1190 /* to xpdi and ydpi */
1191 orientation = ps_document_get_orientation(gs);
1193 switch (orientation) {
1194 case GTK_GS_ORIENTATION_PORTRAIT:
1195 case GTK_GS_ORIENTATION_UPSIDEDOWN:
1196 new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1197 new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1199 case GTK_GS_ORIENTATION_LANDSCAPE:
1200 case GTK_GS_ORIENTATION_SEASCAPE:
1201 new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1202 new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1206 change = (new_width != gs->width * gs->zoom_factor)
1207 || (new_height != gs->height * gs->zoom_factor);
1208 gs->width = (gint) (new_width * gs->zoom_factor);
1209 gs->height = (gint) (new_height * gs->zoom_factor);
1215 ps_document_enable_interpreter(PSDocument * gs)
1217 g_return_val_if_fail(gs != NULL, FALSE);
1218 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1220 if(!gs->gs_filename)
1223 gs->disable_start = FALSE;
1225 return start_interpreter(gs);
1228 /* publicly accessible functions */
1231 ps_document_get_type(void)
1233 static GType gs_type = 0;
1235 GTypeInfo gs_info = {
1236 sizeof(PSDocumentClass),
1237 (GBaseInitFunc) NULL,
1238 (GBaseFinalizeFunc) NULL,
1239 (GClassInitFunc) ps_document_class_init,
1240 (GClassFinalizeFunc) NULL,
1241 NULL, /* class_data */
1243 0, /* n_preallocs */
1244 (GInstanceInitFunc) ps_document_init
1247 static const GInterfaceInfo document_info =
1249 (GInterfaceInitFunc) ps_document_document_iface_init,
1254 gs_type = g_type_register_static(G_TYPE_OBJECT,
1255 "PSDocument", &gs_info, 0);
1257 g_type_add_interface_static (gs_type,
1267 * Show error message -> send signal "interpreter_message"
1270 ps_document_emit_error_msg(PSDocument * gs, const gchar * msg)
1272 gdk_pointer_ungrab(GDK_CURRENT_TIME);
1273 if(strstr(msg, "Error:")) {
1274 gs->gs_status = _("File is not a valid PostScript document.");
1275 ps_document_cleanup(gs);
1280 document_load(PSDocument * gs, const gchar * fname)
1282 g_return_val_if_fail(gs != NULL, FALSE);
1283 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1285 /* clean up previous document */
1286 ps_document_cleanup(gs);
1293 /* prepare this document */
1295 /* default values: no dsc information available */
1296 gs->structured_doc = FALSE;
1297 gs->send_filename_to_gs = TRUE;
1298 gs->current_page = -2;
1301 /* an absolute path */
1302 gs->gs_filename = g_strdup(fname);
1305 /* path relative to our cwd: make it absolute */
1306 gchar *cwd = g_get_current_dir();
1307 gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1311 if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1312 gs->send_filename_to_gs = FALSE;
1316 * We need to make sure that the file is loadable/exists!
1317 * otherwise we want to exit without loading new stuff...
1319 gchar *filename = NULL;
1321 if(!file_readable(fname)) {
1323 g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1324 ps_document_emit_error_msg(gs, buf);
1325 gs->gs_status = _("File is not readable.");
1328 filename = check_filecompressed(gs);
1330 filename = check_pdf(gs);
1333 if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1334 ps_document_cleanup(gs);
1338 /* we grab the vital statistics!!! */
1339 gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1341 g_object_notify (G_OBJECT (gs), "title");
1343 if(gs->doc == NULL) {
1344 /* File does not seem to be a Postscript one */
1346 g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1347 ps_document_emit_error_msg(gs, buf);
1348 ps_document_cleanup(gs);
1349 gs->gs_status = _("The file is not a PostScript document.");
1353 if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1354 (gs->doc->epsf && gs->doc->numpages > 1)) {
1355 gs->structured_doc = TRUE;
1356 gs->send_filename_to_gs = FALSE;
1359 /* We have to set up the orientation of the document */
1362 /* orientation can only be portrait, and landscape or none.
1363 This is the document default. A document can have
1364 pages in landscape and some in portrait */
1365 if(gs->override_orientation) {
1366 /* If the orientation should be override...
1367 then gs->orientation has already the correct
1368 value (it was set when the widget was created */
1373 /* Otherwise, set the proper orientation for the doc */
1374 gs->real_orientation = gs->doc->orientation;
1377 ps_document_set_page_size(gs, -1, gs->current_page);
1380 gs->gs_status = _("Document loaded.");
1387 ps_document_next_page(PSDocument * gs)
1391 g_return_val_if_fail(gs != NULL, FALSE);
1392 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1394 if(gs->interpreter_pid == 0) { /* no interpreter active */
1398 if(gs->busy) { /* interpreter is busy */
1404 event.xclient.type = ClientMessage;
1405 event.xclient.display = gdk_display;
1406 event.xclient.window = gs->message_window;
1407 event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1408 event.xclient.format = 32;
1410 gdk_error_trap_push();
1411 XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1413 gdk_error_trap_pop();
1419 ps_document_get_current_page(PSDocument * gs)
1421 g_return_val_if_fail(gs != NULL, -1);
1422 g_return_val_if_fail(GTK_IS_GS(gs), -1);
1424 return gs->current_page;
1428 ps_document_get_page_count(PSDocument * gs)
1430 if(!gs->gs_filename)
1434 if(gs->structured_doc)
1435 return gs->doc->numpages;
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 if(!gs->gs_filename) {
1453 /* range checking... */
1457 if(gs->structured_doc && gs->doc) {
1458 if(page >= gs->doc->numpages)
1459 page = gs->doc->numpages - 1;
1461 if(page == gs->current_page && !gs->changed)
1464 gs->current_page = page;
1466 if(gs->doc->pages[page].orientation != NONE &&
1467 !gs->override_orientation &&
1468 gs->doc->pages[page].orientation != gs->real_orientation) {
1469 gs->real_orientation = gs->doc->pages[page].orientation;
1473 ps_document_set_page_size(gs, -1, page);
1475 gs->changed = FALSE;
1477 if(is_interpreter_ready(gs)) {
1478 ps_document_next_page(gs);
1481 ps_document_enable_interpreter(gs);
1482 send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1483 send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1486 send_ps(gs, gs->doc->pages[gs->current_page].begin,
1487 gs->doc->pages[gs->current_page].len, FALSE);
1490 /* Unstructured document */
1491 /* In the case of non structured documents,
1492 GS read the PS from the actual file (via command
1493 line. Hence, ggv only send a signal next page.
1494 If ghostview is not running it is usually because
1495 the last page of the file was displayed. In that
1496 case, ggv restarts GS again and the first page is displayed.
1498 if(page == gs->current_page && !gs->changed)
1501 ps_document_set_page_size(gs, -1, page);
1503 if(!is_interpreter_ready(gs))
1504 ps_document_enable_interpreter(gs);
1506 gs->current_page = page;
1508 ps_document_next_page(gs);
1514 * set pagesize sets the size from
1515 * if new_pagesize is -1, then it is set to either
1516 * a) the default settings of pageid, if they exist, or if pageid != -1.
1517 * b) the default setting of the document, if it exists.
1518 * c) the default setting of the widget.
1519 * otherwise, the new_pagesize is used as the pagesize
1522 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1528 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1530 g_return_val_if_fail(gs != NULL, FALSE);
1531 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1533 if(new_pagesize == -1) {
1534 if(gs->default_size > 0)
1535 new_pagesize = gs->default_size;
1536 if(!gs->override_size && gs->doc) {
1537 /* If we have a document:
1538 We use -- the page size (if specified)
1539 or the doc. size (if specified)
1540 or the page bbox (if specified)
1543 if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1544 (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1545 new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1547 else if(gs->doc->default_page_size != NULL) {
1548 new_pagesize = gs->doc->default_page_size - gs->doc->size;
1550 else if((pageid >= 0) &&
1551 (gs->doc->numpages > pageid) &&
1553 (gs->doc->pages[pageid].boundingbox[URX] >
1554 gs->doc->pages[pageid].boundingbox[LLX]) &&
1555 (gs->doc->pages[pageid].boundingbox[URY] >
1556 gs->doc->pages[pageid].boundingbox[LLY])) {
1559 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1560 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1566 /* Compute bounding box */
1567 if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) { /* epsf or bbox */
1570 (gs->doc->pages[pageid].boundingbox[URX] >
1571 gs->doc->pages[pageid].boundingbox[LLX])
1572 && (gs->doc->pages[pageid].boundingbox[URY] >
1573 gs->doc->pages[pageid].boundingbox[LLY])) {
1575 new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1576 new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1577 new_urx = gs->doc->pages[pageid].boundingbox[URX];
1578 new_ury = gs->doc->pages[pageid].boundingbox[URY];
1580 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1581 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1583 new_llx = gs->doc->boundingbox[LLX];
1584 new_lly = gs->doc->boundingbox[LLY];
1585 new_urx = gs->doc->boundingbox[URX];
1586 new_ury = gs->doc->boundingbox[URY];
1590 if(new_pagesize < 0)
1591 new_pagesize = gs->default_size;
1592 new_llx = new_lly = 0;
1593 if(gs->doc && !gs->override_size && gs->doc->size &&
1594 (new_pagesize < gs->doc->numsizes)) {
1595 new_urx = gs->doc->size[new_pagesize].width;
1596 new_ury = gs->doc->size[new_pagesize].height;
1599 new_urx = papersizes[new_pagesize].width;
1600 new_ury = papersizes[new_pagesize].height;
1604 if(new_urx <= new_llx)
1605 new_urx = papersizes[12].width;
1606 if(new_ury <= new_lly)
1607 new_ury = papersizes[12].height;
1609 /* If bounding box changed, setup for new size. */
1610 /* ps_document_disable_interpreter (gs); */
1611 if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1612 (new_urx != gs->urx) || (new_ury != gs->ury)) {
1629 ps_document_zoom_to_fit(PSDocument * gs, gboolean fit_width)
1633 guint avail_w, avail_h;
1635 g_return_val_if_fail(gs != NULL, 0.0);
1636 g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1638 avail_w = (gs->avail_w > 0) ? gs->avail_w : gs->width;
1639 avail_h = (gs->avail_h > 0) ? gs->avail_h : gs->height;
1641 new_zoom = ((gfloat) avail_w) / ((gfloat) gs->width) * gs->zoom_factor;
1643 new_y = new_zoom * ((gfloat) gs->height) / gs->zoom_factor;
1645 new_zoom = ((gfloat) avail_h) / ((gfloat) gs->height) * gs->zoom_factor;
1652 ps_document_set_zoom(PSDocument * gs, gfloat zoom)
1654 g_return_if_fail(gs != NULL);
1655 g_return_if_fail(GTK_IS_GS(gs));
1657 switch (gs->zoom_mode) {
1658 case GTK_GS_ZOOM_FIT_WIDTH:
1659 zoom = ps_document_zoom_to_fit(gs, TRUE);
1661 case GTK_GS_ZOOM_FIT_PAGE:
1662 zoom = ps_document_zoom_to_fit(gs, FALSE);
1664 case GTK_GS_ZOOM_ABSOLUTE:
1669 if(fabs(gs->zoom_factor - zoom) > 0.001) {
1670 gs->zoom_factor = zoom;
1675 ps_document_goto_page(gs, gs->current_page);
1679 ps_document_load (EvDocument *document,
1686 filename = g_filename_from_uri (uri, NULL, error);
1690 result = document_load (PS_DOCUMENT (document), filename);
1698 ps_document_save (EvDocument *document,
1702 g_warning ("ps_document_save not implemented"); /* FIXME */
1707 ps_document_get_n_pages (EvDocument *document)
1709 return ps_document_get_page_count (PS_DOCUMENT (document));
1713 ps_document_set_page (EvDocument *document,
1716 ps_document_goto_page (PS_DOCUMENT (document), page);
1720 ps_document_get_page (EvDocument *document)
1722 return ps_document_get_current_page (PS_DOCUMENT (document));
1726 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
1728 PSDocument *gs = (PSDocument *) data;
1730 if(event->type != GDK_CLIENT_EVENT)
1733 if (event->client.message_type == gs_class->page_atom) {
1735 ev_document_changed (EV_DOCUMENT (gs));
1742 ps_document_set_target (EvDocument *document,
1743 GdkDrawable *target)
1745 PSDocument *gs = PS_DOCUMENT (document);
1749 gs->pstarget = target;
1752 gdk_window_get_user_data (gs->pstarget, &data);
1753 g_return_if_fail (GTK_IS_WIDGET (data));
1755 widget = GTK_WIDGET (data);
1756 g_signal_connect (widget, "event",
1757 G_CALLBACK (ps_document_widget_event),
1761 ps_document_goto_page (gs, gs->current_page);
1765 ps_document_set_scale (EvDocument *document,
1768 ps_document_set_zoom (PS_DOCUMENT (document), scale);
1772 ps_document_set_page_offset (EvDocument *document,
1779 ps_document_get_page_size (EvDocument *document,
1783 PSDocument *gs = PS_DOCUMENT (document);
1790 *height = gs->height;
1795 ps_document_render (EvDocument *document,
1801 PSDocument *gs = PS_DOCUMENT (document);
1804 if (gs->pstarget == NULL ||
1805 gs->bpixmap == NULL) {
1809 gc = gdk_gc_new (gs->pstarget);
1811 gdk_draw_drawable (gs->pstarget, gc,
1815 clip_width, clip_height);
1817 g_object_unref (gc);
1821 ps_document_get_text (EvDocument *document, GdkRectangle *rect)
1823 g_warning ("ps_document_get_text not implemented"); /* FIXME ? */
1828 ps_document_get_link (EvDocument *document,
1836 ps_document_document_iface_init (EvDocumentIface *iface)
1838 iface->load = ps_document_load;
1839 iface->save = ps_document_save;
1840 iface->get_text = ps_document_get_text;
1841 iface->get_link = ps_document_get_link;
1842 iface->get_n_pages = ps_document_get_n_pages;
1843 iface->set_page = ps_document_set_page;
1844 iface->get_page = ps_document_get_page;
1845 iface->set_scale = ps_document_set_scale;
1846 iface->set_target = ps_document_set_target;
1847 iface->set_page_offset = ps_document_set_page_offset;
1848 iface->get_page_size = ps_document_get_page_size;
1849 iface->render = ps_document_render;