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) {
787 gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld",
788 gdk_x11_drawable_get_xid(gs->pstarget),
789 gdk_x11_drawable_get_xid(gs->bpixmap));
791 LOG ("Launching ghostview with env %s", gv_env)
794 gs->interpreter_pid = fork();
795 switch (gs->interpreter_pid) {
811 if(!gs->reading_from_pipe) {
812 if(gs->send_filename_to_gs) {
814 /* just in case gs tries to read from stdin */
815 stdinfd = open("/dev/null", O_RDONLY);
830 /* change to directory where the input file is. This helps
831 * with postscript-files which include other files using
833 dir = g_path_get_dirname(gs->gs_filename);
837 execvp(argv[0], argv);
840 g_print("Unable to execute [%s]\n", argv[0]);
844 g_strfreev(alpha_args);
847 default: /* parent */
848 if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
851 /* use non-blocking IO for pipe to ghostscript */
852 result = fcntl(std_in[1], F_GETFL, 0);
853 fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
854 gs->interpreter_input = std_in[1];
857 gs->interpreter_input = -1;
860 gs->interpreter_output = std_out[0];
862 gs->interpreter_err = std_err[0];
863 gs->interpreter_output_id =
864 gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
865 gs->interpreter_error_id =
866 gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
873 stop_interpreter(PSDocument * gs)
875 if(gs->interpreter_pid > 0) {
877 LOG ("Stop the interpreter")
878 kill(gs->interpreter_pid, SIGTERM);
879 while((wait(&status) == -1) && (errno == EINTR)) ;
880 gs->interpreter_pid = -1;
882 ps_document_cleanup(gs);
883 gs->gs_status = _("Interpreter failed.");
887 if(gs->interpreter_input >= 0) {
888 close(gs->interpreter_input);
889 gs->interpreter_input = -1;
890 if(gs->interpreter_input_id != 0) {
891 gdk_input_remove(gs->interpreter_input_id);
892 gs->interpreter_input_id = 0;
894 while(gs->ps_input) {
895 struct record_list *ps_old = gs->ps_input;
896 gs->ps_input = gs->ps_input->next;
897 if(ps_old->close && NULL != ps_old->fp)
899 g_free((char *) ps_old);
903 if(gs->interpreter_output >= 0) {
904 close(gs->interpreter_output);
905 gs->interpreter_output = -1;
906 if(gs->interpreter_output_id) {
907 gdk_input_remove(gs->interpreter_output_id);
908 gs->interpreter_output_id = 0;
912 if(gs->interpreter_err >= 0) {
913 close(gs->interpreter_err);
914 gs->interpreter_err = -1;
915 if(gs->interpreter_error_id) {
916 gdk_input_remove(gs->interpreter_error_id);
917 gs->interpreter_error_id = 0;
924 /* If file exists and is a regular file then return its length, else -1 */
926 file_length(const gchar * filename)
928 struct stat stat_rec;
930 if(filename && (stat(filename, &stat_rec) == 0)
931 && S_ISREG(stat_rec.st_mode))
932 return stat_rec.st_size;
937 /* Test if file exists, is a regular file and its length is > 0 */
939 file_readable(const char *filename)
941 return (file_length(filename) > 0);
945 * Decompress gs->gs_filename if necessary
946 * Set gs->filename_unc to the name of the uncompressed file or NULL.
947 * Error reporting via signal 'interpreter_message'
948 * Return name of input file to use or NULL on error..
951 check_filecompressed(PSDocument * gs)
955 gchar *filename, *filename_unc, *filename_err, *cmdline;
961 if((file = fopen(gs->gs_filename, "r"))
962 && (fread(buf, sizeof(gchar), 3, file) == 3)) {
963 if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
964 /* file is gzipped or compressed */
965 cmd = gtk_gs_defaults_get_ungzip_cmd();
967 else if(strncmp(buf, "BZh", 3) == 0) {
968 /* file is compressed with bzip2 */
969 cmd = gtk_gs_defaults_get_unbzip2_cmd();
976 return gs->gs_filename;
978 /* do the decompression */
979 filename = g_shell_quote(gs->gs_filename);
980 filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
981 if((fd = mkstemp(filename_unc)) < 0) {
982 g_free(filename_unc);
987 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
988 if((fd = mkstemp(filename_err)) < 0) {
989 g_free(filename_err);
990 g_free(filename_unc);
995 cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
996 filename, filename_unc, filename_err);
997 if((system(cmdline) == 0)
998 && file_readable(filename_unc)
999 && (file_length(filename_err) == 0)) {
1000 /* sucessfully uncompressed file */
1001 gs->gs_filename_unc = filename_unc;
1005 g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
1007 ps_document_emit_error_msg(gs, buf);
1008 if(file_length(filename_err) > 0) {
1010 if((err = fopen(filename_err, "r"))) {
1011 /* print file to message window */
1012 while(fgets(buf, 1024, err))
1013 ps_document_emit_error_msg(gs, buf);
1017 unlink(filename_unc);
1018 g_free(filename_unc);
1019 filename_unc = NULL;
1021 unlink(filename_err);
1022 g_free(filename_err);
1025 return filename_unc;
1029 * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
1030 * pdf file if necessary.
1031 * Set gs->filename_dsc to the name of the dsc file or NULL.
1032 * Error reporting via signal 'interpreter_message'.
1035 check_pdf(PSDocument * gs)
1038 gchar buf[1024], *filename;
1041 /* use uncompressed file as input if necessary */
1042 filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
1044 if((file = fopen(filename, "r"))
1045 && (fread(buf, sizeof(char), 5, file) == 5)
1046 && (strncmp(buf, "%PDF-", 5) == 0)) {
1047 /* we found a PDF file */
1048 gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
1049 filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1050 if((fd = mkstemp(filename_dsc)) < 0) {
1054 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1055 if((fd = mkstemp(filename_err)) < 0) {
1056 g_free(filename_dsc);
1060 fname = g_shell_quote(filename);
1061 cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1063 /* this command (sometimes?) prints error messages to stdout! */
1064 cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1067 if((system(cmdline) == 0) && file_readable(filename_dsc)) {
1070 filename = gs->gs_filename_dsc = filename_dsc;
1072 if(file_length(filename_err) > 0) {
1073 gchar *err_msg = " ";
1078 if((err = fopen(filename_err, "r"))) {
1080 /* print the content of the file to a message box */
1081 while(fgets(buf, 1024, err))
1082 err_msg = g_strconcat(err_msg, buf, NULL);
1084 /* FIXME The dialog is not yet set to modal, difficult to
1085 * get the parent of the dialog box here
1088 dialog = gtk_message_dialog_new(NULL,
1090 GTK_MESSAGE_WARNING,
1092 ("There was an error while scaning the file: %s \n%s"),
1093 gs->gs_filename, err_msg);
1095 gdk_color_parse("white", &color);
1096 gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1098 g_signal_connect(G_OBJECT(dialog), "response",
1099 G_CALLBACK(gtk_widget_destroy), NULL);
1101 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1102 gtk_widget_show(dialog);
1110 g_snprintf(buf, 1024,
1111 _("Error while converting pdf file %s:\n"), filename);
1112 ps_document_emit_error_msg(gs, buf);
1114 if(file_length(filename_err) > 0) {
1116 if((err = fopen(filename_err, "r"))) {
1117 /* print file to message window */
1118 while(fgets(buf, 1024, err))
1119 ps_document_emit_error_msg(gs, buf);
1122 unlink(filename_dsc);
1123 g_free(filename_dsc);
1126 unlink(filename_err);
1127 g_free(filename_err);
1135 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1136 /* never mind this patch: a properly working X server should take care of
1137 calculating the proper values. */
1141 # ifndef HAVE_XINERAMA
1142 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1145 dpy = (Display *) GDK_DISPLAY();
1146 if(XineramaIsActive(dpy)) {
1148 XineramaScreenInfo *head_info;
1149 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1150 /* fake it with dimensions of the first head for now */
1151 return 25.4 * head_info[0].width / gdk_screen_width_mm();
1154 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1163 # ifndef HAVE_XINERAMA
1164 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1167 dpy = (Display *) GDK_DISPLAY();
1168 if(XineramaIsActive(dpy)) {
1170 XineramaScreenInfo *head_info;
1171 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1172 /* fake it with dimensions of the first head for now */
1173 return 25.4 * head_info[0].height / gdk_screen_height_mm();
1176 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1185 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1191 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1193 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1195 /* Compute new size of window, sets xdpi and ydpi if necessary.
1196 * returns True if new window size is different */
1198 compute_size(PSDocument * gs)
1200 guint new_width = 1;
1201 guint new_height = 1;
1202 gboolean change = FALSE;
1205 /* width and height can be changed, calculate window size according */
1206 /* to xpdi and ydpi */
1207 orientation = ps_document_get_orientation(gs);
1209 switch (orientation) {
1210 case GTK_GS_ORIENTATION_PORTRAIT:
1211 case GTK_GS_ORIENTATION_UPSIDEDOWN:
1212 new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1213 new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1215 case GTK_GS_ORIENTATION_LANDSCAPE:
1216 case GTK_GS_ORIENTATION_SEASCAPE:
1217 new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1218 new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1222 change = (new_width != gs->width * gs->zoom_factor)
1223 || (new_height != gs->height * gs->zoom_factor);
1224 gs->width = (gint) (new_width * gs->zoom_factor);
1225 gs->height = (gint) (new_height * gs->zoom_factor);
1231 ps_document_enable_interpreter(PSDocument * gs)
1233 g_return_val_if_fail(gs != NULL, FALSE);
1234 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1236 if(!gs->gs_filename)
1239 gs->disable_start = FALSE;
1241 return start_interpreter(gs);
1244 /* publicly accessible functions */
1247 ps_document_get_type(void)
1249 static GType gs_type = 0;
1251 GTypeInfo gs_info = {
1252 sizeof(PSDocumentClass),
1253 (GBaseInitFunc) NULL,
1254 (GBaseFinalizeFunc) NULL,
1255 (GClassInitFunc) ps_document_class_init,
1256 (GClassFinalizeFunc) NULL,
1257 NULL, /* class_data */
1259 0, /* n_preallocs */
1260 (GInstanceInitFunc) ps_document_init
1263 static const GInterfaceInfo document_info =
1265 (GInterfaceInitFunc) ps_document_document_iface_init,
1270 gs_type = g_type_register_static(G_TYPE_OBJECT,
1271 "PSDocument", &gs_info, 0);
1273 g_type_add_interface_static (gs_type,
1283 * Show error message -> send signal "interpreter_message"
1286 ps_document_emit_error_msg(PSDocument * gs, const gchar * msg)
1288 gdk_pointer_ungrab(GDK_CURRENT_TIME);
1289 if(strstr(msg, "Error:")) {
1290 gs->gs_status = _("File is not a valid PostScript document.");
1291 ps_document_cleanup(gs);
1296 document_load(PSDocument * gs, const gchar * fname)
1298 g_return_val_if_fail(gs != NULL, FALSE);
1299 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1301 LOG ("Load the document")
1303 /* clean up previous document */
1304 ps_document_cleanup(gs);
1311 /* prepare this document */
1313 /* default values: no dsc information available */
1314 gs->structured_doc = FALSE;
1315 gs->send_filename_to_gs = TRUE;
1316 gs->current_page = -2;
1319 /* an absolute path */
1320 gs->gs_filename = g_strdup(fname);
1323 /* path relative to our cwd: make it absolute */
1324 gchar *cwd = g_get_current_dir();
1325 gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1329 if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1330 gs->send_filename_to_gs = FALSE;
1334 * We need to make sure that the file is loadable/exists!
1335 * otherwise we want to exit without loading new stuff...
1337 gchar *filename = NULL;
1339 if(!file_readable(fname)) {
1341 g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1342 ps_document_emit_error_msg(gs, buf);
1343 gs->gs_status = _("File is not readable.");
1346 filename = check_filecompressed(gs);
1348 filename = check_pdf(gs);
1351 if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1352 ps_document_cleanup(gs);
1356 /* we grab the vital statistics!!! */
1357 gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1359 g_object_notify (G_OBJECT (gs), "title");
1361 if(gs->doc == NULL) {
1362 /* File does not seem to be a Postscript one */
1364 g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1365 ps_document_emit_error_msg(gs, buf);
1366 ps_document_cleanup(gs);
1367 gs->gs_status = _("The file is not a PostScript document.");
1371 if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1372 (gs->doc->epsf && gs->doc->numpages > 1)) {
1373 gs->structured_doc = TRUE;
1374 gs->send_filename_to_gs = FALSE;
1377 /* We have to set up the orientation of the document */
1380 /* orientation can only be portrait, and landscape or none.
1381 This is the document default. A document can have
1382 pages in landscape and some in portrait */
1383 if(gs->override_orientation) {
1384 /* If the orientation should be override...
1385 then gs->orientation has already the correct
1386 value (it was set when the widget was created */
1391 /* Otherwise, set the proper orientation for the doc */
1392 gs->real_orientation = gs->doc->orientation;
1395 ps_document_set_page_size(gs, -1, gs->current_page);
1398 gs->gs_status = _("Document loaded.");
1405 ps_document_next_page(PSDocument * gs)
1409 LOG ("Make ghostscript render next page")
1411 g_return_val_if_fail(gs != NULL, FALSE);
1412 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1414 if(gs->interpreter_pid == 0) { /* no interpreter active */
1418 if(gs->busy) { /* interpreter is busy */
1424 event.xclient.type = ClientMessage;
1425 event.xclient.display = gdk_display;
1426 event.xclient.window = gs->message_window;
1427 event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1428 event.xclient.format = 32;
1430 gdk_error_trap_push();
1431 XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1433 gdk_error_trap_pop();
1439 ps_document_goto_page(PSDocument * gs, gint page)
1441 g_return_val_if_fail(gs != NULL, FALSE);
1442 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1444 LOG ("Go to page %d", page)
1446 if(!gs->gs_filename) {
1450 /* range checking... */
1454 if(gs->structured_doc && gs->doc) {
1456 LOG ("It's a structured document, let's send one page to gs")
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.
1499 LOG ("It's an unstructured document, gs will just read the file")
1501 if(page == gs->current_page && !gs->changed)
1504 ps_document_set_page_size(gs, -1, page);
1506 if(!is_interpreter_ready(gs))
1507 ps_document_enable_interpreter(gs);
1509 gs->current_page = page;
1511 ps_document_next_page(gs);
1517 * set pagesize sets the size from
1518 * if new_pagesize is -1, then it is set to either
1519 * a) the default settings of pageid, if they exist, or if pageid != -1.
1520 * b) the default setting of the document, if it exists.
1521 * c) the default setting of the widget.
1522 * otherwise, the new_pagesize is used as the pagesize
1525 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1531 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1533 LOG ("Set the page size")
1535 g_return_val_if_fail(gs != NULL, FALSE);
1536 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1538 if(new_pagesize == -1) {
1539 if(gs->default_size > 0)
1540 new_pagesize = gs->default_size;
1541 if(!gs->override_size && gs->doc) {
1542 /* If we have a document:
1543 We use -- the page size (if specified)
1544 or the doc. size (if specified)
1545 or the page bbox (if specified)
1548 if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1549 (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1550 new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1552 else if(gs->doc->default_page_size != NULL) {
1553 new_pagesize = gs->doc->default_page_size - gs->doc->size;
1555 else if((pageid >= 0) &&
1556 (gs->doc->numpages > pageid) &&
1558 (gs->doc->pages[pageid].boundingbox[URX] >
1559 gs->doc->pages[pageid].boundingbox[LLX]) &&
1560 (gs->doc->pages[pageid].boundingbox[URY] >
1561 gs->doc->pages[pageid].boundingbox[LLY])) {
1564 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1565 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1571 /* Compute bounding box */
1572 if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) { /* epsf or bbox */
1575 (gs->doc->pages[pageid].boundingbox[URX] >
1576 gs->doc->pages[pageid].boundingbox[LLX])
1577 && (gs->doc->pages[pageid].boundingbox[URY] >
1578 gs->doc->pages[pageid].boundingbox[LLY])) {
1580 new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1581 new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1582 new_urx = gs->doc->pages[pageid].boundingbox[URX];
1583 new_ury = gs->doc->pages[pageid].boundingbox[URY];
1585 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1586 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1588 new_llx = gs->doc->boundingbox[LLX];
1589 new_lly = gs->doc->boundingbox[LLY];
1590 new_urx = gs->doc->boundingbox[URX];
1591 new_ury = gs->doc->boundingbox[URY];
1595 if(new_pagesize < 0)
1596 new_pagesize = gs->default_size;
1597 new_llx = new_lly = 0;
1598 if(gs->doc && !gs->override_size && gs->doc->size &&
1599 (new_pagesize < gs->doc->numsizes)) {
1600 new_urx = gs->doc->size[new_pagesize].width;
1601 new_ury = gs->doc->size[new_pagesize].height;
1604 new_urx = papersizes[new_pagesize].width;
1605 new_ury = papersizes[new_pagesize].height;
1609 if(new_urx <= new_llx)
1610 new_urx = papersizes[12].width;
1611 if(new_ury <= new_lly)
1612 new_ury = papersizes[12].height;
1614 /* If bounding box changed, setup for new size. */
1615 /* ps_document_disable_interpreter (gs); */
1616 if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1617 (new_urx != gs->urx) || (new_ury != gs->ury)) {
1634 ps_document_zoom_to_fit(PSDocument * gs, gboolean fit_width)
1638 guint avail_w, avail_h;
1640 g_return_val_if_fail(gs != NULL, 0.0);
1641 g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1643 avail_w = (gs->avail_w > 0) ? gs->avail_w : gs->width;
1644 avail_h = (gs->avail_h > 0) ? gs->avail_h : gs->height;
1646 new_zoom = ((gfloat) avail_w) / ((gfloat) gs->width) * gs->zoom_factor;
1648 new_y = new_zoom * ((gfloat) gs->height) / gs->zoom_factor;
1650 new_zoom = ((gfloat) avail_h) / ((gfloat) gs->height) * gs->zoom_factor;
1657 ps_document_set_zoom(PSDocument * gs, gfloat zoom)
1659 g_return_if_fail(gs != NULL);
1660 g_return_if_fail(GTK_IS_GS(gs));
1662 switch (gs->zoom_mode) {
1663 case GTK_GS_ZOOM_FIT_WIDTH:
1664 zoom = ps_document_zoom_to_fit(gs, TRUE);
1666 case GTK_GS_ZOOM_FIT_PAGE:
1667 zoom = ps_document_zoom_to_fit(gs, FALSE);
1669 case GTK_GS_ZOOM_ABSOLUTE:
1674 if(fabs(gs->zoom_factor - zoom) > 0.001) {
1675 gs->zoom_factor = zoom;
1680 ps_document_goto_page(gs, gs->current_page);
1684 ps_document_load (EvDocument *document,
1691 filename = g_filename_from_uri (uri, NULL, error);
1695 result = document_load (PS_DOCUMENT (document), filename);
1703 ps_document_save (EvDocument *document,
1707 g_warning ("ps_document_save not implemented"); /* FIXME */
1712 ps_document_get_n_pages (EvDocument *document)
1714 PSDocument *ps = PS_DOCUMENT (document);
1716 g_return_val_if_fail (ps != NULL, -1);
1718 if (!ps->gs_filename || !ps->doc) {
1722 return ps->structured_doc ? ps->doc->numpages : 1;
1726 ps_document_set_page (EvDocument *document,
1729 ps_document_goto_page (PS_DOCUMENT (document), page);
1733 ps_document_get_page (EvDocument *document)
1735 PSDocument *ps = PS_DOCUMENT (document);
1737 g_return_val_if_fail (ps != NULL, -1);
1739 return ps->current_page;
1743 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
1745 PSDocument *gs = (PSDocument *) data;
1747 if(event->type != GDK_CLIENT_EVENT)
1750 gs->message_window = event->client.data.l[0];
1752 if (event->client.message_type == gs_class->page_atom) {
1753 LOG ("GS rendered the document")
1755 ev_document_changed (EV_DOCUMENT (gs));
1762 ps_document_set_target (EvDocument *document,
1763 GdkDrawable *target)
1765 PSDocument *gs = PS_DOCUMENT (document);
1769 gs->pstarget = target;
1772 gdk_window_get_user_data (gs->pstarget, &data);
1773 g_return_if_fail (GTK_IS_WIDGET (data));
1775 widget = GTK_WIDGET (data);
1776 g_signal_connect (widget, "event",
1777 G_CALLBACK (ps_document_widget_event),
1781 ps_document_goto_page (gs, gs->current_page);
1785 ps_document_set_scale (EvDocument *document,
1788 ps_document_set_zoom (PS_DOCUMENT (document), scale);
1792 ps_document_set_page_offset (EvDocument *document,
1796 PSDocument *gs = PS_DOCUMENT (document);
1798 gs->page_x_offset = x;
1799 gs->page_y_offset = y;
1803 ps_document_get_page_size (EvDocument *document,
1808 /* Post script documents never vary in size */
1810 PSDocument *gs = PS_DOCUMENT (document);
1817 *height = gs->height;
1822 ps_document_render (EvDocument *document,
1828 PSDocument *gs = PS_DOCUMENT (document);
1833 if (gs->pstarget == NULL ||
1834 gs->bpixmap == NULL) {
1838 page.x = gs->page_x_offset;
1839 page.y = gs->page_y_offset;
1840 page.width = gs->width;
1841 page.height = gs->height;
1845 draw.width = clip_width;
1846 draw.height = clip_height;
1848 gc = gdk_gc_new (gs->pstarget);
1850 gdk_draw_drawable (gs->pstarget, gc,
1852 draw.x - page.x, draw.y - page.y,
1854 draw.width, draw.height);
1856 LOG ("Copy the internal pixmap: %d %d %d %d %d %d",
1857 draw.x - page.x, draw.y - page.y,
1858 draw.x, draw.y, draw.width, draw.height)
1860 g_object_unref (gc);
1864 ps_document_get_text (EvDocument *document, GdkRectangle *rect)
1866 g_warning ("ps_document_get_text not implemented"); /* FIXME ? */
1871 ps_document_get_link (EvDocument *document,
1879 ps_document_document_iface_init (EvDocumentIface *iface)
1881 iface->load = ps_document_load;
1882 iface->save = ps_document_save;
1883 iface->get_text = ps_document_get_text;
1884 iface->get_link = ps_document_get_link;
1885 iface->get_n_pages = ps_document_get_n_pages;
1886 iface->set_page = ps_document_set_page;
1887 iface->get_page = ps_document_get_page;
1888 iface->set_scale = ps_document_set_scale;
1889 iface->set_target = ps_document_set_target;
1890 iface->set_page_offset = ps_document_set_page_offset;
1891 iface->get_page_size = ps_document_get_page_size;
1892 iface->render = ps_document_render;