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);
219 static gboolean ps_document_goto_page(PSDocument * gs, gint page);
221 static GObjectClass *parent_class = NULL;
223 static PSDocumentClass *gs_class = NULL;
226 ps_document_init(PSDocument * gs)
230 gs->current_page = 0;
231 gs->disable_start = FALSE;
232 gs->interpreter_pid = -1;
238 gs->gs_scanstyle = 0;
240 gs->gs_filename_dsc = 0;
241 gs->gs_filename_unc = 0;
245 gs->structured_doc = FALSE;
246 gs->reading_from_pipe = FALSE;
247 gs->send_filename_to_gs = FALSE;
252 gs->interpreter_input = -1;
253 gs->interpreter_output = -1;
254 gs->interpreter_err = -1;
255 gs->interpreter_input_id = 0;
256 gs->interpreter_output_id = 0;
257 gs->interpreter_error_id = 0;
260 gs->input_buffer = NULL;
261 gs->input_buffer_ptr = NULL;
263 gs->buffer_bytes_left = 0;
269 gs->xdpi = compute_xdpi();
270 gs->ydpi = compute_ydpi();
274 gs->right_margin = 0;
275 gs->bottom_margin = 0;
277 gs->page_x_offset = 0;
278 gs->page_y_offset = 0;
280 /* Set user defined defaults */
281 gs->override_orientation = gtk_gs_defaults_get_override_orientation();
282 gs->fallback_orientation = gtk_gs_defaults_get_orientation();
283 gs->zoom_factor = gtk_gs_defaults_get_zoom_factor();
284 gs->default_size = gtk_gs_defaults_get_size();
285 gs->antialiased = gtk_gs_defaults_get_antialiased();
286 gs->override_size = gtk_gs_defaults_get_override_size();
287 gs->respect_eof = gtk_gs_defaults_get_respect_eof();
288 gs->zoom_mode = gtk_gs_defaults_get_zoom_mode();
290 gs->gs_status = _("No document loaded.");
294 ps_document_set_property (GObject *object,
309 ps_document_get_property (GObject *object,
314 PSDocument *ps = PS_DOCUMENT (object);
320 g_value_set_string (value, ps->doc->title);
322 g_value_set_string (value, NULL);
329 ps_document_class_init(PSDocumentClass *klass)
331 GObjectClass *object_class;
333 object_class = (GObjectClass *) klass;
334 parent_class = g_type_class_peek_parent (klass);
337 object_class->finalize = ps_document_finalize;
338 object_class->get_property = ps_document_get_property;
339 object_class->set_property = ps_document_set_property;
342 klass->gs_atom = gdk_atom_intern("GHOSTVIEW", FALSE);
343 klass->next_atom = gdk_atom_intern("NEXT", FALSE);
344 klass->page_atom = gdk_atom_intern("PAGE", FALSE);
345 klass->string_atom = gdk_atom_intern("STRING", FALSE);
347 gtk_gs_defaults_load();
349 g_object_class_override_property (object_class, PROP_TITLE, "title");
352 /* Clean all memory and temporal files */
354 ps_document_cleanup(PSDocument * gs)
356 g_return_if_fail(gs != NULL);
357 g_return_if_fail(GTK_IS_GS(gs));
359 stop_interpreter(gs);
362 fclose(gs->gs_psfile);
363 gs->gs_psfile = NULL;
365 if(gs->gs_filename) {
366 g_free(gs->gs_filename);
367 gs->gs_filename = NULL;
373 if(gs->gs_filename_dsc) {
374 unlink(gs->gs_filename_dsc);
375 g_free(gs->gs_filename_dsc);
376 gs->gs_filename_dsc = NULL;
378 if(gs->gs_filename_unc) {
379 unlink(gs->gs_filename_unc);
380 g_free(gs->gs_filename_unc);
381 gs->gs_filename_unc = NULL;
383 gs->current_page = 0;
393 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
395 PSDocument *gs = (PSDocument *) data;
397 if(event->type != GDK_CLIENT_EVENT)
400 gs->message_window = event->client.data.l[0];
402 if (event->client.message_type == gs_class->page_atom) {
403 LOG ("GS rendered the document");
407 ev_document_scale_changed (EV_DOCUMENT (gs));
410 ev_document_page_changed (EV_DOCUMENT (gs));
418 ps_document_set_target (EvDocument *document,
421 PSDocument *gs = PS_DOCUMENT (document);
426 gdk_window_get_user_data (gs->pstarget, &data);
427 g_return_if_fail (GTK_IS_WIDGET (data));
429 widget = GTK_WIDGET (data);
430 g_signal_handlers_disconnect_by_func
431 (widget, ps_document_widget_event, document);
434 gs->pstarget = target;
437 gdk_window_get_user_data (gs->pstarget, &data);
438 g_return_if_fail (GTK_IS_WIDGET (data));
440 widget = GTK_WIDGET (data);
441 g_signal_connect (widget, "event",
442 G_CALLBACK (ps_document_widget_event),
446 ps_document_goto_page (gs, gs->current_page);
450 ps_document_finalize (GObject * object)
454 g_return_if_fail (object != NULL);
455 g_return_if_fail (GTK_IS_GS (object));
459 gs = PS_DOCUMENT (object);
461 ps_document_cleanup (gs);
462 stop_interpreter (gs);
464 ps_document_set_target (EV_DOCUMENT (object), NULL);
466 if(gs->input_buffer) {
467 g_free(gs->input_buffer);
468 gs->input_buffer = NULL;
471 (*G_OBJECT_CLASS(parent_class)->finalize) (object);
475 send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close)
477 struct record_list *ps_new;
479 if(gs->interpreter_input < 0) {
480 g_critical("No pipe to gs: error in send_ps().");
484 ps_new = (struct record_list *) g_malloc(sizeof(struct record_list));
485 ps_new->fp = gs->gs_psfile;
486 ps_new->begin = begin;
488 ps_new->seek_needed = TRUE;
489 ps_new->close = close;
492 if(gs->input_buffer == NULL) {
493 gs->input_buffer = g_malloc(MAX_BUFSIZE);
496 if(gs->ps_input == NULL) {
497 gs->input_buffer_ptr = gs->input_buffer;
498 gs->bytes_left = len;
499 gs->buffer_bytes_left = 0;
500 gs->ps_input = ps_new;
501 gs->interpreter_input_id =
502 gdk_input_add(gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
505 struct record_list *p = gs->ps_input;
506 while(p->next != NULL) {
514 ps_document_get_orientation(PSDocument * gs)
516 g_return_val_if_fail(gs != NULL, -1);
517 g_return_val_if_fail(GTK_IS_GS(gs), -1);
520 if(gs->structured_doc) {
521 if(gs->doc->pages[MAX(gs->current_page, 0)].orientation !=
522 GTK_GS_ORIENTATION_NONE)
523 gs->real_orientation =
524 gs->doc->pages[MAX(gs->current_page, 0)].orientation;
526 gs->real_orientation = gs->doc->default_page_orientation;
529 if(gs->real_orientation == GTK_GS_ORIENTATION_NONE)
530 gs->real_orientation = gs->doc->orientation;
533 if(gs->override_orientation ||
534 gs->real_orientation == GTK_GS_ORIENTATION_NONE)
535 return gs->fallback_orientation;
537 return gs->real_orientation;
541 set_up_page(PSDocument * gs)
545 //GdkColormap *colormap;
547 GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */
548 GdkColormap *colormap;
549 gboolean size_changed;
551 LOG ("Setup the page");
557 size_changed = compute_size (gs);
559 if (gs->pstarget == NULL)
562 /* Do we have to check if the actual geometry changed? */
564 stop_interpreter(gs);
566 orientation = ps_document_get_orientation(gs);
568 if (size_changed || gs->bpixmap == NULL) {
571 /* clear new pixmap (set to white) */
572 fill = gdk_gc_new(gs->pstarget);
574 colormap = gdk_drawable_get_colormap(gs->pstarget);
575 gdk_color_alloc (colormap, &white);
576 gdk_gc_set_foreground(fill, &white);
578 if(gs->width > 0 && gs->height > 0) {
580 gdk_drawable_unref(gs->bpixmap);
584 LOG ("Create our internal pixmap");
585 gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1);
587 gdk_draw_rectangle(gs->bpixmap, fill, TRUE,
588 0, 0, gs->width, gs->height);
591 gdk_draw_rectangle(gs->pstarget, fill, TRUE,
592 0, 0, gs->width, gs->height);
601 /* gs needs floating point parameters with '.' as decimal point
602 * while some (european) locales use ',' instead, so we set the
603 * locale for this snprintf to "C".
605 savelocale = setlocale(LC_NUMERIC, "C");
608 g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
615 gs->xdpi * gs->zoom_factor,
616 gs->ydpi * gs->zoom_factor,
618 gs->bottom_margin, gs->right_margin, gs->top_margin);
620 LOG ("GS property %s", buf);
623 setlocale(LC_NUMERIC, savelocale);
625 gdk_property_change(gs->pstarget,
627 gs_class->string_atom,
628 8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
642 is_interpreter_ready(PSDocument * gs)
644 return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
648 interpreter_failed(PSDocument * gs)
650 stop_interpreter(gs);
654 output(gpointer data, gint source, GdkInputCondition condition)
656 char buf[MAX_BUFSIZE + 1], *msg;
658 PSDocument *gs = PS_DOCUMENT(data);
660 if(source == gs->interpreter_output) {
661 bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
662 if(bytes == 0) { /* EOF occurred */
663 close(gs->interpreter_output);
664 gs->interpreter_output = -1;
665 gdk_input_remove(gs->interpreter_output_id);
668 else if(bytes == -1) {
670 interpreter_failed(gs);
673 if(gs->interpreter_err == -1) {
674 stop_interpreter(gs);
677 else if(source == gs->interpreter_err) {
678 bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
679 if(bytes == 0) { /* EOF occurred */
680 close(gs->interpreter_err);
681 gs->interpreter_err = -1;
682 gdk_input_remove(gs->interpreter_error_id);
685 else if(bytes == -1) {
687 interpreter_failed(gs);
690 if(gs->interpreter_output == -1) {
691 stop_interpreter(gs);
697 ps_document_emit_error_msg (gs, msg);
702 input(gpointer data, gint source, GdkInputCondition condition)
704 PSDocument *gs = PS_DOCUMENT(data);
706 void (*oldsig) (int);
707 oldsig = signal(SIGPIPE, catchPipe);
710 if(gs->buffer_bytes_left == 0) {
711 /* Get a new section if required */
712 if(gs->ps_input && gs->bytes_left == 0) {
713 struct record_list *ps_old = gs->ps_input;
714 gs->ps_input = ps_old->next;
715 if(ps_old->close && NULL != ps_old->fp)
717 g_free((char *) ps_old);
719 /* Have to seek at the beginning of each section */
720 if(gs->ps_input && gs->ps_input->seek_needed) {
721 fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
722 gs->ps_input->seek_needed = FALSE;
723 gs->bytes_left = gs->ps_input->len;
726 if(gs->bytes_left > MAX_BUFSIZE) {
727 gs->buffer_bytes_left =
728 fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
730 else if(gs->bytes_left > 0) {
731 gs->buffer_bytes_left =
732 fread(gs->input_buffer,
733 sizeof(char), gs->bytes_left, gs->ps_input->fp);
736 gs->buffer_bytes_left = 0;
738 if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
739 interpreter_failed(gs); /* Error occurred */
741 gs->input_buffer_ptr = gs->input_buffer;
742 gs->bytes_left -= gs->buffer_bytes_left;
745 if(gs->buffer_bytes_left > 0) {
746 /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
748 bytes_written = write(gs->interpreter_input,
749 gs->input_buffer_ptr, gs->buffer_bytes_left);
752 ps_document_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
754 interpreter_failed(gs);
756 else if(bytes_written == -1) {
757 if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
758 interpreter_failed(gs); /* Something bad happened */
762 gs->buffer_bytes_left -= bytes_written;
763 gs->input_buffer_ptr += bytes_written;
767 while(gs->ps_input && gs->buffer_bytes_left == 0);
769 signal(SIGPIPE, oldsig);
771 if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
772 if(gs->interpreter_input_id != 0) {
773 gdk_input_remove(gs->interpreter_input_id);
774 gs->interpreter_input_id = 0;
780 start_interpreter(PSDocument * gs)
782 int std_in[2] = { -1, -1 }; /* pipe to interp stdin */
783 int std_out[2]; /* pipe from interp stdout */
784 int std_err[2]; /* pipe from interp stderr */
786 LOG ("Start the interpreter");
789 #define NUM_GS_ARGS (NUM_ARGS - 20)
790 #define NUM_ALPHA_ARGS 10
792 char *argv[NUM_ARGS], *dir, *gv_env;
793 char **gs_args, **alpha_args = NULL;
799 stop_interpreter(gs);
801 if(gs->disable_start == TRUE)
804 /* set up the args... */
805 gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS);
806 for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++)
807 argv[argc] = gs_args[i];
809 if(gs->antialiased) {
810 if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
811 alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
813 alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(),
814 " ", NUM_ALPHA_ARGS);
815 for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++)
816 argv[argc] = alpha_args[i];
819 argv[argc++] = "-sDEVICE=x11";
820 argv[argc++] = "-dNOPAUSE";
821 argv[argc++] = "-dQUIET";
822 /* I assume we do _not_ want to change this... (: */
823 argv[argc++] = "-dSAFER";
825 /* set up the pipes */
826 if(gs->send_filename_to_gs) {
827 argv[argc++] = PS_DOCUMENT_GET_PS_FILE(gs);
829 argv[argc++] = "quit";
836 if(!gs->reading_from_pipe && !gs->send_filename_to_gs) {
837 if(pipe(std_in) == -1) {
838 g_critical("Unable to open pipe to Ghostscript.");
842 if(pipe(std_out) == -1) {
846 if(pipe(std_err) == -1) {
852 gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld",
853 gdk_x11_drawable_get_xid(gs->pstarget),
854 gdk_x11_drawable_get_xid(gs->bpixmap));
856 LOG ("Launching ghostview with env %s", gv_env);
859 gs->interpreter_pid = fork();
860 switch (gs->interpreter_pid) {
876 if(!gs->reading_from_pipe) {
877 if(gs->send_filename_to_gs) {
879 /* just in case gs tries to read from stdin */
880 stdinfd = open("/dev/null", O_RDONLY);
895 /* change to directory where the input file is. This helps
896 * with postscript-files which include other files using
898 dir = g_path_get_dirname(gs->gs_filename);
902 execvp(argv[0], argv);
905 g_print("Unable to execute [%s]\n", argv[0]);
909 g_strfreev(alpha_args);
912 default: /* parent */
913 if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
916 /* use non-blocking IO for pipe to ghostscript */
917 result = fcntl(std_in[1], F_GETFL, 0);
918 fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
919 gs->interpreter_input = std_in[1];
922 gs->interpreter_input = -1;
925 gs->interpreter_output = std_out[0];
927 gs->interpreter_err = std_err[0];
928 gs->interpreter_output_id =
929 gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
930 gs->interpreter_error_id =
931 gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
938 stop_interpreter(PSDocument * gs)
940 if(gs->interpreter_pid > 0) {
942 LOG ("Stop the interpreter");
943 kill(gs->interpreter_pid, SIGTERM);
944 while((wait(&status) == -1) && (errno == EINTR)) ;
945 gs->interpreter_pid = -1;
947 ps_document_cleanup(gs);
948 gs->gs_status = _("Interpreter failed.");
952 if(gs->interpreter_input >= 0) {
953 close(gs->interpreter_input);
954 gs->interpreter_input = -1;
955 if(gs->interpreter_input_id != 0) {
956 gdk_input_remove(gs->interpreter_input_id);
957 gs->interpreter_input_id = 0;
959 while(gs->ps_input) {
960 struct record_list *ps_old = gs->ps_input;
961 gs->ps_input = gs->ps_input->next;
962 if(ps_old->close && NULL != ps_old->fp)
964 g_free((char *) ps_old);
968 if(gs->interpreter_output >= 0) {
969 close(gs->interpreter_output);
970 gs->interpreter_output = -1;
971 if(gs->interpreter_output_id) {
972 gdk_input_remove(gs->interpreter_output_id);
973 gs->interpreter_output_id = 0;
977 if(gs->interpreter_err >= 0) {
978 close(gs->interpreter_err);
979 gs->interpreter_err = -1;
980 if(gs->interpreter_error_id) {
981 gdk_input_remove(gs->interpreter_error_id);
982 gs->interpreter_error_id = 0;
989 /* If file exists and is a regular file then return its length, else -1 */
991 file_length(const gchar * filename)
993 struct stat stat_rec;
995 if(filename && (stat(filename, &stat_rec) == 0)
996 && S_ISREG(stat_rec.st_mode))
997 return stat_rec.st_size;
1002 /* Test if file exists, is a regular file and its length is > 0 */
1004 file_readable(const char *filename)
1006 return (file_length(filename) > 0);
1010 * Decompress gs->gs_filename if necessary
1011 * Set gs->filename_unc to the name of the uncompressed file or NULL.
1012 * Error reporting via signal 'interpreter_message'
1013 * Return name of input file to use or NULL on error..
1016 check_filecompressed(PSDocument * gs)
1020 gchar *filename, *filename_unc, *filename_err, *cmdline;
1026 if((file = fopen(gs->gs_filename, "r"))
1027 && (fread(buf, sizeof(gchar), 3, file) == 3)) {
1028 if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
1029 /* file is gzipped or compressed */
1030 cmd = gtk_gs_defaults_get_ungzip_cmd();
1032 else if(strncmp(buf, "BZh", 3) == 0) {
1033 /* file is compressed with bzip2 */
1034 cmd = gtk_gs_defaults_get_unbzip2_cmd();
1041 return gs->gs_filename;
1043 /* do the decompression */
1044 filename = g_shell_quote(gs->gs_filename);
1045 filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1046 if((fd = mkstemp(filename_unc)) < 0) {
1047 g_free(filename_unc);
1052 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1053 if((fd = mkstemp(filename_err)) < 0) {
1054 g_free(filename_err);
1055 g_free(filename_unc);
1060 cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
1061 filename, filename_unc, filename_err);
1062 if((system(cmdline) == 0)
1063 && file_readable(filename_unc)
1064 && (file_length(filename_err) == 0)) {
1065 /* sucessfully uncompressed file */
1066 gs->gs_filename_unc = filename_unc;
1070 g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
1072 ps_document_emit_error_msg(gs, buf);
1073 if(file_length(filename_err) > 0) {
1075 if((err = fopen(filename_err, "r"))) {
1076 /* print file to message window */
1077 while(fgets(buf, 1024, err))
1078 ps_document_emit_error_msg(gs, buf);
1082 unlink(filename_unc);
1083 g_free(filename_unc);
1084 filename_unc = NULL;
1086 unlink(filename_err);
1087 g_free(filename_err);
1090 return filename_unc;
1094 * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
1095 * pdf file if necessary.
1096 * Set gs->filename_dsc to the name of the dsc file or NULL.
1097 * Error reporting via signal 'interpreter_message'.
1100 check_pdf(PSDocument * gs)
1103 gchar buf[1024], *filename;
1106 /* use uncompressed file as input if necessary */
1107 filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
1109 if((file = fopen(filename, "r"))
1110 && (fread(buf, sizeof(char), 5, file) == 5)
1111 && (strncmp(buf, "%PDF-", 5) == 0)) {
1112 /* we found a PDF file */
1113 gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
1114 filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1115 if((fd = mkstemp(filename_dsc)) < 0) {
1119 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1120 if((fd = mkstemp(filename_err)) < 0) {
1121 g_free(filename_dsc);
1125 fname = g_shell_quote(filename);
1126 cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1128 /* this command (sometimes?) prints error messages to stdout! */
1129 cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1132 if((system(cmdline) == 0) && file_readable(filename_dsc)) {
1135 filename = gs->gs_filename_dsc = filename_dsc;
1137 if(file_length(filename_err) > 0) {
1138 gchar *err_msg = " ";
1143 if((err = fopen(filename_err, "r"))) {
1145 /* print the content of the file to a message box */
1146 while(fgets(buf, 1024, err))
1147 err_msg = g_strconcat(err_msg, buf, NULL);
1149 /* FIXME The dialog is not yet set to modal, difficult to
1150 * get the parent of the dialog box here
1153 dialog = gtk_message_dialog_new(NULL,
1155 GTK_MESSAGE_WARNING,
1157 ("There was an error while scaning the file: %s \n%s"),
1158 gs->gs_filename, err_msg);
1160 gdk_color_parse("white", &color);
1161 gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1163 g_signal_connect(G_OBJECT(dialog), "response",
1164 G_CALLBACK(gtk_widget_destroy), NULL);
1166 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1167 gtk_widget_show(dialog);
1175 g_snprintf(buf, 1024,
1176 _("Error while converting pdf file %s:\n"), filename);
1177 ps_document_emit_error_msg(gs, buf);
1179 if(file_length(filename_err) > 0) {
1181 if((err = fopen(filename_err, "r"))) {
1182 /* print file to message window */
1183 while(fgets(buf, 1024, err))
1184 ps_document_emit_error_msg(gs, buf);
1187 unlink(filename_dsc);
1188 g_free(filename_dsc);
1191 unlink(filename_err);
1192 g_free(filename_err);
1200 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1201 /* never mind this patch: a properly working X server should take care of
1202 calculating the proper values. */
1206 # ifndef HAVE_XINERAMA
1207 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1210 dpy = (Display *) GDK_DISPLAY();
1211 if(XineramaIsActive(dpy)) {
1213 XineramaScreenInfo *head_info;
1214 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1215 /* fake it with dimensions of the first head for now */
1216 return 25.4 * head_info[0].width / gdk_screen_width_mm();
1219 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1228 # ifndef HAVE_XINERAMA
1229 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1232 dpy = (Display *) GDK_DISPLAY();
1233 if(XineramaIsActive(dpy)) {
1235 XineramaScreenInfo *head_info;
1236 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1237 /* fake it with dimensions of the first head for now */
1238 return 25.4 * head_info[0].height / gdk_screen_height_mm();
1241 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1250 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1256 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1258 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1260 /* Compute new size of window, sets xdpi and ydpi if necessary.
1261 * returns True if new window size is different */
1263 compute_size(PSDocument * gs)
1265 guint new_width = 1;
1266 guint new_height = 1;
1267 gboolean change = FALSE;
1270 /* width and height can be changed, calculate window size according */
1271 /* to xpdi and ydpi */
1272 orientation = ps_document_get_orientation(gs);
1274 switch (orientation) {
1275 case GTK_GS_ORIENTATION_PORTRAIT:
1276 case GTK_GS_ORIENTATION_UPSIDEDOWN:
1277 new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1278 new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1280 case GTK_GS_ORIENTATION_LANDSCAPE:
1281 case GTK_GS_ORIENTATION_SEASCAPE:
1282 new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1283 new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1287 change = (new_width != gs->width * gs->zoom_factor)
1288 || (new_height != gs->height * gs->zoom_factor);
1289 gs->width = (gint) (new_width * gs->zoom_factor);
1290 gs->height = (gint) (new_height * gs->zoom_factor);
1296 ps_document_enable_interpreter(PSDocument * gs)
1298 g_return_val_if_fail(gs != NULL, FALSE);
1299 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1301 if(!gs->gs_filename)
1304 gs->disable_start = FALSE;
1306 return start_interpreter(gs);
1309 /* publicly accessible functions */
1312 ps_document_get_type(void)
1314 static GType gs_type = 0;
1316 GTypeInfo gs_info = {
1317 sizeof(PSDocumentClass),
1318 (GBaseInitFunc) NULL,
1319 (GBaseFinalizeFunc) NULL,
1320 (GClassInitFunc) ps_document_class_init,
1321 (GClassFinalizeFunc) NULL,
1322 NULL, /* class_data */
1324 0, /* n_preallocs */
1325 (GInstanceInitFunc) ps_document_init
1328 static const GInterfaceInfo document_info =
1330 (GInterfaceInitFunc) ps_document_document_iface_init,
1335 gs_type = g_type_register_static(G_TYPE_OBJECT,
1336 "PSDocument", &gs_info, 0);
1338 g_type_add_interface_static (gs_type,
1348 * Show error message -> send signal "interpreter_message"
1351 ps_document_emit_error_msg(PSDocument * gs, const gchar * msg)
1353 gdk_pointer_ungrab(GDK_CURRENT_TIME);
1354 if(strstr(msg, "Error:")) {
1355 gs->gs_status = _("File is not a valid PostScript document.");
1356 ps_document_cleanup(gs);
1361 document_load(PSDocument * gs, const gchar * fname)
1363 g_return_val_if_fail(gs != NULL, FALSE);
1364 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1366 LOG ("Load the document");
1368 /* clean up previous document */
1369 ps_document_cleanup(gs);
1376 /* prepare this document */
1378 /* default values: no dsc information available */
1379 gs->structured_doc = FALSE;
1380 gs->send_filename_to_gs = TRUE;
1381 gs->current_page = 0;
1384 /* an absolute path */
1385 gs->gs_filename = g_strdup(fname);
1388 /* path relative to our cwd: make it absolute */
1389 gchar *cwd = g_get_current_dir();
1390 gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1394 if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1395 gs->send_filename_to_gs = FALSE;
1399 * We need to make sure that the file is loadable/exists!
1400 * otherwise we want to exit without loading new stuff...
1402 gchar *filename = NULL;
1404 if(!file_readable(fname)) {
1406 g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1407 ps_document_emit_error_msg(gs, buf);
1408 gs->gs_status = _("File is not readable.");
1411 filename = check_filecompressed(gs);
1413 filename = check_pdf(gs);
1416 if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1417 ps_document_cleanup(gs);
1421 /* we grab the vital statistics!!! */
1422 gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1424 g_object_notify (G_OBJECT (gs), "title");
1426 if(gs->doc == NULL) {
1427 /* File does not seem to be a Postscript one */
1429 g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1430 ps_document_emit_error_msg(gs, buf);
1431 ps_document_cleanup(gs);
1432 gs->gs_status = _("The file is not a PostScript document.");
1436 if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1437 (gs->doc->epsf && gs->doc->numpages > 1)) {
1438 gs->structured_doc = TRUE;
1439 gs->send_filename_to_gs = FALSE;
1442 /* We have to set up the orientation of the document */
1445 /* orientation can only be portrait, and landscape or none.
1446 This is the document default. A document can have
1447 pages in landscape and some in portrait */
1448 if(gs->override_orientation) {
1449 /* If the orientation should be override...
1450 then gs->orientation has already the correct
1451 value (it was set when the widget was created */
1456 /* Otherwise, set the proper orientation for the doc */
1457 gs->real_orientation = gs->doc->orientation;
1460 ps_document_set_page_size(gs, -1, gs->current_page);
1463 gs->gs_status = _("Document loaded.");
1470 ps_document_next_page(PSDocument * gs)
1474 LOG ("Make ghostscript render next page");
1476 g_return_val_if_fail(gs != NULL, FALSE);
1477 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1479 if(gs->interpreter_pid == 0) { /* no interpreter active */
1483 if(gs->busy) { /* interpreter is busy */
1489 event.xclient.type = ClientMessage;
1490 event.xclient.display = gdk_display;
1491 event.xclient.window = gs->message_window;
1492 event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1493 event.xclient.format = 32;
1495 gdk_error_trap_push();
1496 XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1498 gdk_error_trap_pop();
1504 ps_document_goto_page(PSDocument * gs, gint page)
1506 g_return_val_if_fail(gs != NULL, FALSE);
1507 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1509 LOG ("Go to page %d", page);
1511 if(!gs->gs_filename) {
1515 /* range checking... */
1519 if(gs->structured_doc && gs->doc) {
1521 LOG ("It's a structured document, let's send one page to gs");
1523 if(page >= gs->doc->numpages)
1524 page = gs->doc->numpages - 1;
1526 if(page == gs->current_page && !gs->changed)
1529 gs->current_page = page;
1531 if(gs->doc->pages[page].orientation != NONE &&
1532 !gs->override_orientation &&
1533 gs->doc->pages[page].orientation != gs->real_orientation) {
1534 gs->real_orientation = gs->doc->pages[page].orientation;
1538 ps_document_set_page_size(gs, -1, page);
1540 gs->changed = FALSE;
1542 if(is_interpreter_ready(gs)) {
1543 ps_document_next_page(gs);
1546 ps_document_enable_interpreter(gs);
1547 send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1548 send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1551 send_ps(gs, gs->doc->pages[gs->current_page].begin,
1552 gs->doc->pages[gs->current_page].len, FALSE);
1555 /* Unstructured document */
1556 /* In the case of non structured documents,
1557 GS read the PS from the actual file (via command
1558 line. Hence, ggv only send a signal next page.
1559 If ghostview is not running it is usually because
1560 the last page of the file was displayed. In that
1561 case, ggv restarts GS again and the first page is displayed.
1564 LOG ("It's an unstructured document, gs will just read the file");
1566 if(page == gs->current_page && !gs->changed)
1569 ps_document_set_page_size(gs, -1, page);
1571 if(!is_interpreter_ready(gs))
1572 ps_document_enable_interpreter(gs);
1574 gs->current_page = page;
1576 ps_document_next_page(gs);
1582 * set pagesize sets the size from
1583 * if new_pagesize is -1, then it is set to either
1584 * a) the default settings of pageid, if they exist, or if pageid != -1.
1585 * b) the default setting of the document, if it exists.
1586 * c) the default setting of the widget.
1587 * otherwise, the new_pagesize is used as the pagesize
1590 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1596 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1598 LOG ("Set the page size");
1600 g_return_val_if_fail(gs != NULL, FALSE);
1601 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1603 if(new_pagesize == -1) {
1604 if(gs->default_size > 0)
1605 new_pagesize = gs->default_size;
1606 if(!gs->override_size && gs->doc) {
1607 /* If we have a document:
1608 We use -- the page size (if specified)
1609 or the doc. size (if specified)
1610 or the page bbox (if specified)
1613 if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1614 (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1615 new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1617 else if(gs->doc->default_page_size != NULL) {
1618 new_pagesize = gs->doc->default_page_size - gs->doc->size;
1620 else if((pageid >= 0) &&
1621 (gs->doc->numpages > pageid) &&
1623 (gs->doc->pages[pageid].boundingbox[URX] >
1624 gs->doc->pages[pageid].boundingbox[LLX]) &&
1625 (gs->doc->pages[pageid].boundingbox[URY] >
1626 gs->doc->pages[pageid].boundingbox[LLY])) {
1629 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1630 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1636 /* Compute bounding box */
1637 if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) { /* epsf or bbox */
1640 (gs->doc->pages[pageid].boundingbox[URX] >
1641 gs->doc->pages[pageid].boundingbox[LLX])
1642 && (gs->doc->pages[pageid].boundingbox[URY] >
1643 gs->doc->pages[pageid].boundingbox[LLY])) {
1645 new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1646 new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1647 new_urx = gs->doc->pages[pageid].boundingbox[URX];
1648 new_ury = gs->doc->pages[pageid].boundingbox[URY];
1650 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1651 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1653 new_llx = gs->doc->boundingbox[LLX];
1654 new_lly = gs->doc->boundingbox[LLY];
1655 new_urx = gs->doc->boundingbox[URX];
1656 new_ury = gs->doc->boundingbox[URY];
1660 if(new_pagesize < 0)
1661 new_pagesize = gs->default_size;
1662 new_llx = new_lly = 0;
1663 if(gs->doc && !gs->override_size && gs->doc->size &&
1664 (new_pagesize < gs->doc->numsizes)) {
1665 new_urx = gs->doc->size[new_pagesize].width;
1666 new_ury = gs->doc->size[new_pagesize].height;
1669 new_urx = papersizes[new_pagesize].width;
1670 new_ury = papersizes[new_pagesize].height;
1674 if(new_urx <= new_llx)
1675 new_urx = papersizes[12].width;
1676 if(new_ury <= new_lly)
1677 new_ury = papersizes[12].height;
1679 /* If bounding box changed, setup for new size. */
1680 /* ps_document_disable_interpreter (gs); */
1681 if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1682 (new_urx != gs->urx) || (new_ury != gs->ury)) {
1699 ps_document_set_zoom (PSDocument * gs, gfloat zoom)
1701 g_return_if_fail (gs != NULL);
1703 gs->zoom_factor = zoom;
1705 if (gs->pstarget != NULL) {
1709 ps_document_goto_page (gs, gs->current_page);
1714 ps_document_load (EvDocument *document,
1721 filename = g_filename_from_uri (uri, NULL, error);
1725 result = document_load (PS_DOCUMENT (document), filename);
1727 g_set_error (error, G_FILE_ERROR,
1728 G_FILE_ERROR_FAILED,
1729 "Failed to load document '%s'\n",
1739 ps_document_save (EvDocument *document,
1743 g_warning ("ps_document_save not implemented"); /* FIXME */
1748 ps_document_get_n_pages (EvDocument *document)
1750 PSDocument *ps = PS_DOCUMENT (document);
1752 g_return_val_if_fail (ps != NULL, -1);
1754 if (!ps->gs_filename || !ps->doc) {
1758 return ps->structured_doc ? ps->doc->numpages : 1;
1762 ps_document_set_page (EvDocument *document,
1765 ps_document_goto_page (PS_DOCUMENT (document), page - 1);
1769 ps_document_get_page (EvDocument *document)
1771 PSDocument *ps = PS_DOCUMENT (document);
1773 g_return_val_if_fail (ps != NULL, -1);
1775 return ps->current_page + 1;
1779 ps_document_set_scale (EvDocument *document,
1782 ps_document_set_zoom (PS_DOCUMENT (document), scale);
1786 ps_document_set_page_offset (EvDocument *document,
1790 PSDocument *gs = PS_DOCUMENT (document);
1792 gs->page_x_offset = x;
1793 gs->page_y_offset = y;
1797 ps_document_get_page_size (EvDocument *document,
1802 /* Post script documents never vary in size */
1804 PSDocument *gs = PS_DOCUMENT (document);
1811 *height = gs->height;
1816 ps_document_render (EvDocument *document,
1822 PSDocument *gs = PS_DOCUMENT (document);
1827 if (gs->pstarget == NULL ||
1828 gs->bpixmap == NULL) {
1832 page.x = gs->page_x_offset;
1833 page.y = gs->page_y_offset;
1834 page.width = gs->width;
1835 page.height = gs->height;
1839 draw.width = clip_width;
1840 draw.height = clip_height;
1842 gc = gdk_gc_new (gs->pstarget);
1844 gdk_draw_drawable (gs->pstarget, gc,
1846 draw.x - page.x, draw.y - page.y,
1848 draw.width, draw.height);
1850 LOG ("Copy the internal pixmap: %d %d %d %d %d %d",
1851 draw.x - page.x, draw.y - page.y,
1852 draw.x, draw.y, draw.width, draw.height);
1854 g_object_unref (gc);
1858 ps_document_get_text (EvDocument *document, GdkRectangle *rect)
1860 g_warning ("ps_document_get_text not implemented"); /* FIXME ? */
1865 ps_document_get_link (EvDocument *document,
1873 ps_document_document_iface_init (EvDocumentIface *iface)
1875 iface->load = ps_document_load;
1876 iface->save = ps_document_save;
1877 iface->get_text = ps_document_get_text;
1878 iface->get_link = ps_document_get_link;
1879 iface->get_n_pages = ps_document_get_n_pages;
1880 iface->set_page = ps_document_set_page;
1881 iface->get_page = ps_document_get_page;
1882 iface->set_scale = ps_document_set_scale;
1883 iface->set_target = ps_document_set_target;
1884 iface->set_page_offset = ps_document_set_page_offset;
1885 iface->get_page_size = ps_document_get_page_size;
1886 iface->render = ps_document_render;