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 = -2;
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 = gtk_type_class(gtk_widget_get_type());
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 = -1;
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;
490 LOG ("Setup the page")
496 if (gs->pstarget == NULL)
499 /* Do we have to check if the actual geometry changed? */
501 stop_interpreter(gs);
503 orientation = ps_document_get_orientation(gs);
505 if(compute_size(gs)) {
508 /* clear new pixmap (set to white) */
509 fill = gdk_gc_new(gs->pstarget);
511 colormap = gdk_drawable_get_colormap(gs->pstarget);
512 gdk_color_alloc (colormap, &white);
513 gdk_gc_set_foreground(fill, &white);
515 if(gs->width > 0 && gs->height > 0) {
517 gdk_drawable_unref(gs->bpixmap);
521 LOG ("Create our internal pixmap")
522 gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1);
524 gdk_draw_rectangle(gs->bpixmap, fill, TRUE,
525 0, 0, gs->width, gs->height);
528 gdk_draw_rectangle(gs->pstarget, fill, TRUE,
529 0, 0, gs->width, gs->height);
538 /* gs needs floating point parameters with '.' as decimal point
539 * while some (european) locales use ',' instead, so we set the
540 * locale for this snprintf to "C".
542 savelocale = setlocale(LC_NUMERIC, "C");
545 g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
552 gs->xdpi * gs->zoom_factor,
553 gs->ydpi * gs->zoom_factor,
555 gs->bottom_margin, gs->right_margin, gs->top_margin);
558 setlocale(LC_NUMERIC, savelocale);
560 gdk_property_change(gs->pstarget,
562 gs_class->string_atom,
563 8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
577 is_interpreter_ready(PSDocument * gs)
579 return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
583 interpreter_failed(PSDocument * gs)
585 stop_interpreter(gs);
589 output(gpointer data, gint source, GdkInputCondition condition)
591 char buf[MAX_BUFSIZE + 1], *msg;
593 PSDocument *gs = PS_DOCUMENT(data);
595 if(source == gs->interpreter_output) {
596 bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
597 if(bytes == 0) { /* EOF occurred */
598 close(gs->interpreter_output);
599 gs->interpreter_output = -1;
600 gdk_input_remove(gs->interpreter_output_id);
603 else if(bytes == -1) {
605 interpreter_failed(gs);
608 if(gs->interpreter_err == -1) {
609 stop_interpreter(gs);
612 else if(source == gs->interpreter_err) {
613 bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
614 if(bytes == 0) { /* EOF occurred */
615 close(gs->interpreter_err);
616 gs->interpreter_err = -1;
617 gdk_input_remove(gs->interpreter_error_id);
620 else if(bytes == -1) {
622 interpreter_failed(gs);
625 if(gs->interpreter_output == -1) {
626 stop_interpreter(gs);
632 ps_document_emit_error_msg (gs, msg);
637 input(gpointer data, gint source, GdkInputCondition condition)
639 PSDocument *gs = PS_DOCUMENT(data);
641 void (*oldsig) (int);
642 oldsig = signal(SIGPIPE, catchPipe);
645 if(gs->buffer_bytes_left == 0) {
646 /* Get a new section if required */
647 if(gs->ps_input && gs->bytes_left == 0) {
648 struct record_list *ps_old = gs->ps_input;
649 gs->ps_input = ps_old->next;
650 if(ps_old->close && NULL != ps_old->fp)
652 g_free((char *) ps_old);
654 /* Have to seek at the beginning of each section */
655 if(gs->ps_input && gs->ps_input->seek_needed) {
656 fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
657 gs->ps_input->seek_needed = FALSE;
658 gs->bytes_left = gs->ps_input->len;
661 if(gs->bytes_left > MAX_BUFSIZE) {
662 gs->buffer_bytes_left =
663 fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
665 else if(gs->bytes_left > 0) {
666 gs->buffer_bytes_left =
667 fread(gs->input_buffer,
668 sizeof(char), gs->bytes_left, gs->ps_input->fp);
671 gs->buffer_bytes_left = 0;
673 if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
674 interpreter_failed(gs); /* Error occurred */
676 gs->input_buffer_ptr = gs->input_buffer;
677 gs->bytes_left -= gs->buffer_bytes_left;
680 if(gs->buffer_bytes_left > 0) {
681 /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
683 bytes_written = write(gs->interpreter_input,
684 gs->input_buffer_ptr, gs->buffer_bytes_left);
687 ps_document_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
689 interpreter_failed(gs);
691 else if(bytes_written == -1) {
692 if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
693 interpreter_failed(gs); /* Something bad happened */
697 gs->buffer_bytes_left -= bytes_written;
698 gs->input_buffer_ptr += bytes_written;
702 while(gs->ps_input && gs->buffer_bytes_left == 0);
704 signal(SIGPIPE, oldsig);
706 if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
707 if(gs->interpreter_input_id != 0) {
708 gdk_input_remove(gs->interpreter_input_id);
709 gs->interpreter_input_id = 0;
715 start_interpreter(PSDocument * gs)
717 int std_in[2] = { -1, -1 }; /* pipe to interp stdin */
718 int std_out[2]; /* pipe from interp stdout */
719 int std_err[2]; /* pipe from interp stderr */
721 LOG ("Start the interpreter")
724 #define NUM_GS_ARGS (NUM_ARGS - 20)
725 #define NUM_ALPHA_ARGS 10
727 char *argv[NUM_ARGS], *dir, *gv_env;
728 char **gs_args, **alpha_args = NULL;
734 stop_interpreter(gs);
736 if(gs->disable_start == TRUE)
739 /* set up the args... */
740 gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS);
741 for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++)
742 argv[argc] = gs_args[i];
744 if(gs->antialiased) {
745 if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
746 alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
748 alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(),
749 " ", NUM_ALPHA_ARGS);
750 for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++)
751 argv[argc] = alpha_args[i];
754 argv[argc++] = "-sDEVICE=x11";
755 argv[argc++] = "-dNOPAUSE";
756 argv[argc++] = "-dQUIET";
757 /* I assume we do _not_ want to change this... (: */
758 argv[argc++] = "-dSAFER";
760 /* set up the pipes */
761 if(gs->send_filename_to_gs) {
762 argv[argc++] = PS_DOCUMENT_GET_PS_FILE(gs);
764 argv[argc++] = "quit";
771 if(!gs->reading_from_pipe && !gs->send_filename_to_gs) {
772 if(pipe(std_in) == -1) {
773 g_critical("Unable to open pipe to Ghostscript.");
777 if(pipe(std_out) == -1) {
781 if(pipe(std_err) == -1) {
788 gs->interpreter_pid = fork();
789 switch (gs->interpreter_pid) {
805 if(!gs->reading_from_pipe) {
806 if(gs->send_filename_to_gs) {
808 /* just in case gs tries to read from stdin */
809 stdinfd = open("/dev/null", O_RDONLY);
822 gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld",
823 gdk_x11_drawable_get_xid(gs->pstarget),
824 gdk_x11_drawable_get_xid(gs->bpixmap));
827 LOG ("Launching ghostview with env %s", gv_env)
829 /* change to directory where the input file is. This helps
830 * with postscript-files which include other files using
832 dir = g_path_get_dirname(gs->gs_filename);
836 execvp(argv[0], argv);
839 g_print("Unable to execute [%s]\n", argv[0]);
843 g_strfreev(alpha_args);
846 default: /* parent */
847 if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
850 /* use non-blocking IO for pipe to ghostscript */
851 result = fcntl(std_in[1], F_GETFL, 0);
852 fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
853 gs->interpreter_input = std_in[1];
856 gs->interpreter_input = -1;
859 gs->interpreter_output = std_out[0];
861 gs->interpreter_err = std_err[0];
862 gs->interpreter_output_id =
863 gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
864 gs->interpreter_error_id =
865 gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
872 stop_interpreter(PSDocument * gs)
874 if(gs->interpreter_pid > 0) {
876 LOG ("Stop the interpreter")
877 kill(gs->interpreter_pid, SIGTERM);
878 while((wait(&status) == -1) && (errno == EINTR)) ;
879 gs->interpreter_pid = -1;
881 ps_document_cleanup(gs);
882 gs->gs_status = _("Interpreter failed.");
886 if(gs->interpreter_input >= 0) {
887 close(gs->interpreter_input);
888 gs->interpreter_input = -1;
889 if(gs->interpreter_input_id != 0) {
890 gdk_input_remove(gs->interpreter_input_id);
891 gs->interpreter_input_id = 0;
893 while(gs->ps_input) {
894 struct record_list *ps_old = gs->ps_input;
895 gs->ps_input = gs->ps_input->next;
896 if(ps_old->close && NULL != ps_old->fp)
898 g_free((char *) ps_old);
902 if(gs->interpreter_output >= 0) {
903 close(gs->interpreter_output);
904 gs->interpreter_output = -1;
905 if(gs->interpreter_output_id) {
906 gdk_input_remove(gs->interpreter_output_id);
907 gs->interpreter_output_id = 0;
911 if(gs->interpreter_err >= 0) {
912 close(gs->interpreter_err);
913 gs->interpreter_err = -1;
914 if(gs->interpreter_error_id) {
915 gdk_input_remove(gs->interpreter_error_id);
916 gs->interpreter_error_id = 0;
923 /* If file exists and is a regular file then return its length, else -1 */
925 file_length(const gchar * filename)
927 struct stat stat_rec;
929 if(filename && (stat(filename, &stat_rec) == 0)
930 && S_ISREG(stat_rec.st_mode))
931 return stat_rec.st_size;
936 /* Test if file exists, is a regular file and its length is > 0 */
938 file_readable(const char *filename)
940 return (file_length(filename) > 0);
944 * Decompress gs->gs_filename if necessary
945 * Set gs->filename_unc to the name of the uncompressed file or NULL.
946 * Error reporting via signal 'interpreter_message'
947 * Return name of input file to use or NULL on error..
950 check_filecompressed(PSDocument * gs)
954 gchar *filename, *filename_unc, *filename_err, *cmdline;
960 if((file = fopen(gs->gs_filename, "r"))
961 && (fread(buf, sizeof(gchar), 3, file) == 3)) {
962 if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
963 /* file is gzipped or compressed */
964 cmd = gtk_gs_defaults_get_ungzip_cmd();
966 else if(strncmp(buf, "BZh", 3) == 0) {
967 /* file is compressed with bzip2 */
968 cmd = gtk_gs_defaults_get_unbzip2_cmd();
975 return gs->gs_filename;
977 /* do the decompression */
978 filename = g_shell_quote(gs->gs_filename);
979 filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
980 if((fd = mkstemp(filename_unc)) < 0) {
981 g_free(filename_unc);
986 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
987 if((fd = mkstemp(filename_err)) < 0) {
988 g_free(filename_err);
989 g_free(filename_unc);
994 cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
995 filename, filename_unc, filename_err);
996 if((system(cmdline) == 0)
997 && file_readable(filename_unc)
998 && (file_length(filename_err) == 0)) {
999 /* sucessfully uncompressed file */
1000 gs->gs_filename_unc = filename_unc;
1004 g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
1006 ps_document_emit_error_msg(gs, buf);
1007 if(file_length(filename_err) > 0) {
1009 if((err = fopen(filename_err, "r"))) {
1010 /* print file to message window */
1011 while(fgets(buf, 1024, err))
1012 ps_document_emit_error_msg(gs, buf);
1016 unlink(filename_unc);
1017 g_free(filename_unc);
1018 filename_unc = NULL;
1020 unlink(filename_err);
1021 g_free(filename_err);
1024 return filename_unc;
1028 * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
1029 * pdf file if necessary.
1030 * Set gs->filename_dsc to the name of the dsc file or NULL.
1031 * Error reporting via signal 'interpreter_message'.
1034 check_pdf(PSDocument * gs)
1037 gchar buf[1024], *filename;
1040 /* use uncompressed file as input if necessary */
1041 filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
1043 if((file = fopen(filename, "r"))
1044 && (fread(buf, sizeof(char), 5, file) == 5)
1045 && (strncmp(buf, "%PDF-", 5) == 0)) {
1046 /* we found a PDF file */
1047 gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
1048 filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1049 if((fd = mkstemp(filename_dsc)) < 0) {
1053 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1054 if((fd = mkstemp(filename_err)) < 0) {
1055 g_free(filename_dsc);
1059 fname = g_shell_quote(filename);
1060 cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1062 /* this command (sometimes?) prints error messages to stdout! */
1063 cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1066 if((system(cmdline) == 0) && file_readable(filename_dsc)) {
1069 filename = gs->gs_filename_dsc = filename_dsc;
1071 if(file_length(filename_err) > 0) {
1072 gchar *err_msg = " ";
1077 if((err = fopen(filename_err, "r"))) {
1079 /* print the content of the file to a message box */
1080 while(fgets(buf, 1024, err))
1081 err_msg = g_strconcat(err_msg, buf, NULL);
1083 /* FIXME The dialog is not yet set to modal, difficult to
1084 * get the parent of the dialog box here
1087 dialog = gtk_message_dialog_new(NULL,
1089 GTK_MESSAGE_WARNING,
1091 ("There was an error while scaning the file: %s \n%s"),
1092 gs->gs_filename, err_msg);
1094 gdk_color_parse("white", &color);
1095 gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1097 g_signal_connect(G_OBJECT(dialog), "response",
1098 G_CALLBACK(gtk_widget_destroy), NULL);
1100 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1101 gtk_widget_show(dialog);
1109 g_snprintf(buf, 1024,
1110 _("Error while converting pdf file %s:\n"), filename);
1111 ps_document_emit_error_msg(gs, buf);
1113 if(file_length(filename_err) > 0) {
1115 if((err = fopen(filename_err, "r"))) {
1116 /* print file to message window */
1117 while(fgets(buf, 1024, err))
1118 ps_document_emit_error_msg(gs, buf);
1121 unlink(filename_dsc);
1122 g_free(filename_dsc);
1125 unlink(filename_err);
1126 g_free(filename_err);
1134 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1135 /* never mind this patch: a properly working X server should take care of
1136 calculating the proper values. */
1140 # ifndef HAVE_XINERAMA
1141 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1144 dpy = (Display *) GDK_DISPLAY();
1145 if(XineramaIsActive(dpy)) {
1147 XineramaScreenInfo *head_info;
1148 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1149 /* fake it with dimensions of the first head for now */
1150 return 25.4 * head_info[0].width / gdk_screen_width_mm();
1153 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1162 # ifndef HAVE_XINERAMA
1163 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1166 dpy = (Display *) GDK_DISPLAY();
1167 if(XineramaIsActive(dpy)) {
1169 XineramaScreenInfo *head_info;
1170 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1171 /* fake it with dimensions of the first head for now */
1172 return 25.4 * head_info[0].height / gdk_screen_height_mm();
1175 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1184 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1190 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1192 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1194 /* Compute new size of window, sets xdpi and ydpi if necessary.
1195 * returns True if new window size is different */
1197 compute_size(PSDocument * gs)
1199 guint new_width = 1;
1200 guint new_height = 1;
1201 gboolean change = FALSE;
1204 /* width and height can be changed, calculate window size according */
1205 /* to xpdi and ydpi */
1206 orientation = ps_document_get_orientation(gs);
1208 switch (orientation) {
1209 case GTK_GS_ORIENTATION_PORTRAIT:
1210 case GTK_GS_ORIENTATION_UPSIDEDOWN:
1211 new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1212 new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1214 case GTK_GS_ORIENTATION_LANDSCAPE:
1215 case GTK_GS_ORIENTATION_SEASCAPE:
1216 new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1217 new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1221 change = (new_width != gs->width * gs->zoom_factor)
1222 || (new_height != gs->height * gs->zoom_factor);
1223 gs->width = (gint) (new_width * gs->zoom_factor);
1224 gs->height = (gint) (new_height * gs->zoom_factor);
1230 ps_document_enable_interpreter(PSDocument * gs)
1232 g_return_val_if_fail(gs != NULL, FALSE);
1233 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1235 if(!gs->gs_filename)
1238 gs->disable_start = FALSE;
1240 return start_interpreter(gs);
1243 /* publicly accessible functions */
1246 ps_document_get_type(void)
1248 static GType gs_type = 0;
1250 GTypeInfo gs_info = {
1251 sizeof(PSDocumentClass),
1252 (GBaseInitFunc) NULL,
1253 (GBaseFinalizeFunc) NULL,
1254 (GClassInitFunc) ps_document_class_init,
1255 (GClassFinalizeFunc) NULL,
1256 NULL, /* class_data */
1258 0, /* n_preallocs */
1259 (GInstanceInitFunc) ps_document_init
1262 static const GInterfaceInfo document_info =
1264 (GInterfaceInitFunc) ps_document_document_iface_init,
1269 gs_type = g_type_register_static(G_TYPE_OBJECT,
1270 "PSDocument", &gs_info, 0);
1272 g_type_add_interface_static (gs_type,
1282 * Show error message -> send signal "interpreter_message"
1285 ps_document_emit_error_msg(PSDocument * gs, const gchar * msg)
1287 gdk_pointer_ungrab(GDK_CURRENT_TIME);
1288 if(strstr(msg, "Error:")) {
1289 gs->gs_status = _("File is not a valid PostScript document.");
1290 ps_document_cleanup(gs);
1295 document_load(PSDocument * gs, const gchar * fname)
1297 g_return_val_if_fail(gs != NULL, FALSE);
1298 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1300 LOG ("Load the document")
1302 /* clean up previous document */
1303 ps_document_cleanup(gs);
1310 /* prepare this document */
1312 /* default values: no dsc information available */
1313 gs->structured_doc = FALSE;
1314 gs->send_filename_to_gs = TRUE;
1315 gs->current_page = -2;
1318 /* an absolute path */
1319 gs->gs_filename = g_strdup(fname);
1322 /* path relative to our cwd: make it absolute */
1323 gchar *cwd = g_get_current_dir();
1324 gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1328 if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1329 gs->send_filename_to_gs = FALSE;
1333 * We need to make sure that the file is loadable/exists!
1334 * otherwise we want to exit without loading new stuff...
1336 gchar *filename = NULL;
1338 if(!file_readable(fname)) {
1340 g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1341 ps_document_emit_error_msg(gs, buf);
1342 gs->gs_status = _("File is not readable.");
1345 filename = check_filecompressed(gs);
1347 filename = check_pdf(gs);
1350 if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1351 ps_document_cleanup(gs);
1355 /* we grab the vital statistics!!! */
1356 gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1358 g_object_notify (G_OBJECT (gs), "title");
1360 if(gs->doc == NULL) {
1361 /* File does not seem to be a Postscript one */
1363 g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1364 ps_document_emit_error_msg(gs, buf);
1365 ps_document_cleanup(gs);
1366 gs->gs_status = _("The file is not a PostScript document.");
1370 if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1371 (gs->doc->epsf && gs->doc->numpages > 1)) {
1372 gs->structured_doc = TRUE;
1373 gs->send_filename_to_gs = FALSE;
1376 /* We have to set up the orientation of the document */
1379 /* orientation can only be portrait, and landscape or none.
1380 This is the document default. A document can have
1381 pages in landscape and some in portrait */
1382 if(gs->override_orientation) {
1383 /* If the orientation should be override...
1384 then gs->orientation has already the correct
1385 value (it was set when the widget was created */
1390 /* Otherwise, set the proper orientation for the doc */
1391 gs->real_orientation = gs->doc->orientation;
1394 ps_document_set_page_size(gs, -1, gs->current_page);
1397 gs->gs_status = _("Document loaded.");
1404 ps_document_next_page(PSDocument * gs)
1408 LOG ("Make ghostscript render next page")
1410 g_return_val_if_fail(gs != NULL, FALSE);
1411 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1413 if(gs->interpreter_pid == 0) { /* no interpreter active */
1417 if(gs->busy) { /* interpreter is busy */
1423 event.xclient.type = ClientMessage;
1424 event.xclient.display = gdk_display;
1425 event.xclient.window = gs->message_window;
1426 event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1427 event.xclient.format = 32;
1429 gdk_error_trap_push();
1430 XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1432 gdk_error_trap_pop();
1438 ps_document_goto_page(PSDocument * gs, gint page)
1440 g_return_val_if_fail(gs != NULL, FALSE);
1441 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1443 LOG ("Go to page %d", page)
1445 if(!gs->gs_filename) {
1449 /* range checking... */
1453 if(gs->structured_doc && gs->doc) {
1455 LOG ("It's a structured document, let's send one page to gs")
1457 if(page >= gs->doc->numpages)
1458 page = gs->doc->numpages - 1;
1460 if(page == gs->current_page && !gs->changed)
1463 gs->current_page = page;
1465 if(gs->doc->pages[page].orientation != NONE &&
1466 !gs->override_orientation &&
1467 gs->doc->pages[page].orientation != gs->real_orientation) {
1468 gs->real_orientation = gs->doc->pages[page].orientation;
1472 ps_document_set_page_size(gs, -1, page);
1474 gs->changed = FALSE;
1476 if(is_interpreter_ready(gs)) {
1477 ps_document_next_page(gs);
1480 ps_document_enable_interpreter(gs);
1481 send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1482 send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1485 send_ps(gs, gs->doc->pages[gs->current_page].begin,
1486 gs->doc->pages[gs->current_page].len, FALSE);
1489 /* Unstructured document */
1490 /* In the case of non structured documents,
1491 GS read the PS from the actual file (via command
1492 line. Hence, ggv only send a signal next page.
1493 If ghostview is not running it is usually because
1494 the last page of the file was displayed. In that
1495 case, ggv restarts GS again and the first page is displayed.
1498 LOG ("It's an unstructured document, gs will just read the file")
1500 if(page == gs->current_page && !gs->changed)
1503 ps_document_set_page_size(gs, -1, page);
1505 if(!is_interpreter_ready(gs))
1506 ps_document_enable_interpreter(gs);
1508 gs->current_page = page;
1510 ps_document_next_page(gs);
1516 * set pagesize sets the size from
1517 * if new_pagesize is -1, then it is set to either
1518 * a) the default settings of pageid, if they exist, or if pageid != -1.
1519 * b) the default setting of the document, if it exists.
1520 * c) the default setting of the widget.
1521 * otherwise, the new_pagesize is used as the pagesize
1524 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1530 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1532 LOG ("Set the page size")
1534 g_return_val_if_fail(gs != NULL, FALSE);
1535 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1537 if(new_pagesize == -1) {
1538 if(gs->default_size > 0)
1539 new_pagesize = gs->default_size;
1540 if(!gs->override_size && gs->doc) {
1541 /* If we have a document:
1542 We use -- the page size (if specified)
1543 or the doc. size (if specified)
1544 or the page bbox (if specified)
1547 if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1548 (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1549 new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1551 else if(gs->doc->default_page_size != NULL) {
1552 new_pagesize = gs->doc->default_page_size - gs->doc->size;
1554 else if((pageid >= 0) &&
1555 (gs->doc->numpages > pageid) &&
1557 (gs->doc->pages[pageid].boundingbox[URX] >
1558 gs->doc->pages[pageid].boundingbox[LLX]) &&
1559 (gs->doc->pages[pageid].boundingbox[URY] >
1560 gs->doc->pages[pageid].boundingbox[LLY])) {
1563 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1564 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1570 /* Compute bounding box */
1571 if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) { /* epsf or bbox */
1574 (gs->doc->pages[pageid].boundingbox[URX] >
1575 gs->doc->pages[pageid].boundingbox[LLX])
1576 && (gs->doc->pages[pageid].boundingbox[URY] >
1577 gs->doc->pages[pageid].boundingbox[LLY])) {
1579 new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1580 new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1581 new_urx = gs->doc->pages[pageid].boundingbox[URX];
1582 new_ury = gs->doc->pages[pageid].boundingbox[URY];
1584 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1585 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1587 new_llx = gs->doc->boundingbox[LLX];
1588 new_lly = gs->doc->boundingbox[LLY];
1589 new_urx = gs->doc->boundingbox[URX];
1590 new_ury = gs->doc->boundingbox[URY];
1594 if(new_pagesize < 0)
1595 new_pagesize = gs->default_size;
1596 new_llx = new_lly = 0;
1597 if(gs->doc && !gs->override_size && gs->doc->size &&
1598 (new_pagesize < gs->doc->numsizes)) {
1599 new_urx = gs->doc->size[new_pagesize].width;
1600 new_ury = gs->doc->size[new_pagesize].height;
1603 new_urx = papersizes[new_pagesize].width;
1604 new_ury = papersizes[new_pagesize].height;
1608 if(new_urx <= new_llx)
1609 new_urx = papersizes[12].width;
1610 if(new_ury <= new_lly)
1611 new_ury = papersizes[12].height;
1613 /* If bounding box changed, setup for new size. */
1614 /* ps_document_disable_interpreter (gs); */
1615 if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1616 (new_urx != gs->urx) || (new_ury != gs->ury)) {
1633 ps_document_zoom_to_fit(PSDocument * gs, gboolean fit_width)
1637 guint avail_w, avail_h;
1639 g_return_val_if_fail(gs != NULL, 0.0);
1640 g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1642 avail_w = (gs->avail_w > 0) ? gs->avail_w : gs->width;
1643 avail_h = (gs->avail_h > 0) ? gs->avail_h : gs->height;
1645 new_zoom = ((gfloat) avail_w) / ((gfloat) gs->width) * gs->zoom_factor;
1647 new_y = new_zoom * ((gfloat) gs->height) / gs->zoom_factor;
1649 new_zoom = ((gfloat) avail_h) / ((gfloat) gs->height) * gs->zoom_factor;
1656 ps_document_set_zoom(PSDocument * gs, gfloat zoom)
1658 g_return_if_fail(gs != NULL);
1659 g_return_if_fail(GTK_IS_GS(gs));
1661 switch (gs->zoom_mode) {
1662 case GTK_GS_ZOOM_FIT_WIDTH:
1663 zoom = ps_document_zoom_to_fit(gs, TRUE);
1665 case GTK_GS_ZOOM_FIT_PAGE:
1666 zoom = ps_document_zoom_to_fit(gs, FALSE);
1668 case GTK_GS_ZOOM_ABSOLUTE:
1673 if(fabs(gs->zoom_factor - zoom) > 0.001) {
1674 gs->zoom_factor = zoom;
1679 ps_document_goto_page(gs, gs->current_page);
1683 ps_document_load (EvDocument *document,
1690 filename = g_filename_from_uri (uri, NULL, error);
1694 result = document_load (PS_DOCUMENT (document), filename);
1702 ps_document_save (EvDocument *document,
1706 g_warning ("ps_document_save not implemented"); /* FIXME */
1711 ps_document_get_n_pages (EvDocument *document)
1713 PSDocument *ps = PS_DOCUMENT (document);
1715 g_return_val_if_fail (ps != NULL, -1);
1717 if (!ps->gs_filename || !ps->doc) {
1721 return ps->structured_doc ? ps->doc->numpages : 1;
1725 ps_document_set_page (EvDocument *document,
1728 ps_document_goto_page (PS_DOCUMENT (document), page);
1732 ps_document_get_page (EvDocument *document)
1734 PSDocument *ps = PS_DOCUMENT (document);
1736 g_return_val_if_fail (ps != NULL, -1);
1738 return ps->current_page;
1742 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
1744 PSDocument *gs = (PSDocument *) data;
1746 if(event->type != GDK_CLIENT_EVENT)
1749 gs->message_window = event->client.data.l[0];
1751 if (event->client.message_type == gs_class->page_atom) {
1752 LOG ("GS rendered the document")
1754 ev_document_changed (EV_DOCUMENT (gs));
1761 ps_document_set_target (EvDocument *document,
1762 GdkDrawable *target)
1764 PSDocument *gs = PS_DOCUMENT (document);
1768 gs->pstarget = target;
1771 gdk_window_get_user_data (gs->pstarget, &data);
1772 g_return_if_fail (GTK_IS_WIDGET (data));
1774 widget = GTK_WIDGET (data);
1775 g_signal_connect (widget, "event",
1776 G_CALLBACK (ps_document_widget_event),
1780 ps_document_goto_page (gs, gs->current_page);
1784 ps_document_set_scale (EvDocument *document,
1787 ps_document_set_zoom (PS_DOCUMENT (document), scale);
1791 ps_document_set_page_offset (EvDocument *document,
1795 PSDocument *gs = PS_DOCUMENT (document);
1797 gs->page_x_offset = x;
1798 gs->page_y_offset = y;
1802 ps_document_get_page_size (EvDocument *document,
1807 /* Post script documents never vary in size */
1809 PSDocument *gs = PS_DOCUMENT (document);
1816 *height = gs->height;
1821 ps_document_render (EvDocument *document,
1827 PSDocument *gs = PS_DOCUMENT (document);
1832 if (gs->pstarget == NULL ||
1833 gs->bpixmap == NULL) {
1837 page.x = gs->page_x_offset;
1838 page.y = gs->page_y_offset;
1839 page.width = gs->width;
1840 page.height = gs->height;
1844 draw.width = clip_width;
1845 draw.height = clip_height;
1847 gc = gdk_gc_new (gs->pstarget);
1849 gdk_draw_drawable (gs->pstarget, gc,
1851 draw.x - page.x, draw.y - page.y,
1853 draw.width, draw.height);
1855 LOG ("Copy the internal pixmap: %d %d %d %d %d %d",
1856 draw.x - page.x, draw.y - page.y,
1857 draw.x, draw.y, draw.width, draw.height)
1859 g_object_unref (gc);
1863 ps_document_get_text (EvDocument *document, GdkRectangle *rect)
1865 g_warning ("ps_document_get_text not implemented"); /* FIXME ? */
1870 ps_document_get_link (EvDocument *document,
1878 ps_document_document_iface_init (EvDocumentIface *iface)
1880 iface->load = ps_document_load;
1881 iface->save = ps_document_save;
1882 iface->get_text = ps_document_get_text;
1883 iface->get_link = ps_document_get_link;
1884 iface->get_n_pages = ps_document_get_n_pages;
1885 iface->set_page = ps_document_set_page;
1886 iface->get_page = ps_document_get_page;
1887 iface->set_scale = ps_document_set_scale;
1888 iface->set_target = ps_document_set_target;
1889 iface->set_page_offset = ps_document_set_page_offset;
1890 iface->get_page_size = ps_document_get_page_size;
1891 iface->render = ps_document_render;