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));
399 gs = PS_DOCUMENT(object);
401 ps_document_cleanup(gs);
403 if(gs->input_buffer) {
404 g_free(gs->input_buffer);
405 gs->input_buffer = NULL;
408 (*G_OBJECT_CLASS(parent_class)->finalize) (object);
412 send_ps(PSDocument * gs, long begin, unsigned int len, gboolean close)
414 struct record_list *ps_new;
416 if(gs->interpreter_input < 0) {
417 g_critical("No pipe to gs: error in send_ps().");
421 ps_new = (struct record_list *) g_malloc(sizeof(struct record_list));
422 ps_new->fp = gs->gs_psfile;
423 ps_new->begin = begin;
425 ps_new->seek_needed = TRUE;
426 ps_new->close = close;
429 if(gs->input_buffer == NULL) {
430 gs->input_buffer = g_malloc(MAX_BUFSIZE);
433 if(gs->ps_input == NULL) {
434 gs->input_buffer_ptr = gs->input_buffer;
435 gs->bytes_left = len;
436 gs->buffer_bytes_left = 0;
437 gs->ps_input = ps_new;
438 gs->interpreter_input_id =
439 gdk_input_add(gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
442 struct record_list *p = gs->ps_input;
443 while(p->next != NULL) {
451 ps_document_get_orientation(PSDocument * gs)
453 g_return_val_if_fail(gs != NULL, -1);
454 g_return_val_if_fail(GTK_IS_GS(gs), -1);
457 if(gs->structured_doc) {
458 if(gs->doc->pages[MAX(gs->current_page, 0)].orientation !=
459 GTK_GS_ORIENTATION_NONE)
460 gs->real_orientation =
461 gs->doc->pages[MAX(gs->current_page, 0)].orientation;
463 gs->real_orientation = gs->doc->default_page_orientation;
466 if(gs->real_orientation == GTK_GS_ORIENTATION_NONE)
467 gs->real_orientation = gs->doc->orientation;
470 if(gs->override_orientation ||
471 gs->real_orientation == GTK_GS_ORIENTATION_NONE)
472 return gs->fallback_orientation;
474 return gs->real_orientation;
478 set_up_page(PSDocument * gs)
482 //GdkColormap *colormap;
484 GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */
485 GdkColormap *colormap;
487 LOG ("Setup the page")
493 if (gs->pstarget == NULL)
496 /* Do we have to check if the actual geometry changed? */
498 stop_interpreter(gs);
500 orientation = ps_document_get_orientation(gs);
502 if(compute_size(gs)) {
505 /* clear new pixmap (set to white) */
506 fill = gdk_gc_new(gs->pstarget);
508 colormap = gdk_drawable_get_colormap(gs->pstarget);
509 gdk_color_alloc (colormap, &white);
510 gdk_gc_set_foreground(fill, &white);
512 if(gs->width > 0 && gs->height > 0) {
514 gdk_drawable_unref(gs->bpixmap);
518 LOG ("Create our internal pixmap")
519 gs->bpixmap = gdk_pixmap_new(gs->pstarget, gs->width, gs->height, -1);
521 gdk_draw_rectangle(gs->bpixmap, fill, TRUE,
522 0, 0, gs->width, gs->height);
525 gdk_draw_rectangle(gs->pstarget, fill, TRUE,
526 0, 0, gs->width, gs->height);
535 /* gs needs floating point parameters with '.' as decimal point
536 * while some (european) locales use ',' instead, so we set the
537 * locale for this snprintf to "C".
539 savelocale = setlocale(LC_NUMERIC, "C");
542 g_snprintf(buf, 1024, "%ld %d %d %d %d %d %f %f %d %d %d %d",
549 gs->xdpi * gs->zoom_factor,
550 gs->ydpi * gs->zoom_factor,
552 gs->bottom_margin, gs->right_margin, gs->top_margin);
555 setlocale(LC_NUMERIC, savelocale);
557 gdk_property_change(gs->pstarget,
559 gs_class->string_atom,
560 8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
574 is_interpreter_ready(PSDocument * gs)
576 return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
580 interpreter_failed(PSDocument * gs)
582 stop_interpreter(gs);
586 output(gpointer data, gint source, GdkInputCondition condition)
588 char buf[MAX_BUFSIZE + 1], *msg;
590 PSDocument *gs = PS_DOCUMENT(data);
592 if(source == gs->interpreter_output) {
593 bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
594 if(bytes == 0) { /* EOF occurred */
595 close(gs->interpreter_output);
596 gs->interpreter_output = -1;
597 gdk_input_remove(gs->interpreter_output_id);
600 else if(bytes == -1) {
602 interpreter_failed(gs);
605 if(gs->interpreter_err == -1) {
606 stop_interpreter(gs);
609 else if(source == gs->interpreter_err) {
610 bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
611 if(bytes == 0) { /* EOF occurred */
612 close(gs->interpreter_err);
613 gs->interpreter_err = -1;
614 gdk_input_remove(gs->interpreter_error_id);
617 else if(bytes == -1) {
619 interpreter_failed(gs);
622 if(gs->interpreter_output == -1) {
623 stop_interpreter(gs);
629 ps_document_emit_error_msg (gs, msg);
634 input(gpointer data, gint source, GdkInputCondition condition)
636 PSDocument *gs = PS_DOCUMENT(data);
638 void (*oldsig) (int);
639 oldsig = signal(SIGPIPE, catchPipe);
642 if(gs->buffer_bytes_left == 0) {
643 /* Get a new section if required */
644 if(gs->ps_input && gs->bytes_left == 0) {
645 struct record_list *ps_old = gs->ps_input;
646 gs->ps_input = ps_old->next;
647 if(ps_old->close && NULL != ps_old->fp)
649 g_free((char *) ps_old);
651 /* Have to seek at the beginning of each section */
652 if(gs->ps_input && gs->ps_input->seek_needed) {
653 fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
654 gs->ps_input->seek_needed = FALSE;
655 gs->bytes_left = gs->ps_input->len;
658 if(gs->bytes_left > MAX_BUFSIZE) {
659 gs->buffer_bytes_left =
660 fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
662 else if(gs->bytes_left > 0) {
663 gs->buffer_bytes_left =
664 fread(gs->input_buffer,
665 sizeof(char), gs->bytes_left, gs->ps_input->fp);
668 gs->buffer_bytes_left = 0;
670 if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
671 interpreter_failed(gs); /* Error occurred */
673 gs->input_buffer_ptr = gs->input_buffer;
674 gs->bytes_left -= gs->buffer_bytes_left;
677 if(gs->buffer_bytes_left > 0) {
678 /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
680 bytes_written = write(gs->interpreter_input,
681 gs->input_buffer_ptr, gs->buffer_bytes_left);
684 ps_document_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
686 interpreter_failed(gs);
688 else if(bytes_written == -1) {
689 if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
690 interpreter_failed(gs); /* Something bad happened */
694 gs->buffer_bytes_left -= bytes_written;
695 gs->input_buffer_ptr += bytes_written;
699 while(gs->ps_input && gs->buffer_bytes_left == 0);
701 signal(SIGPIPE, oldsig);
703 if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
704 if(gs->interpreter_input_id != 0) {
705 gdk_input_remove(gs->interpreter_input_id);
706 gs->interpreter_input_id = 0;
712 start_interpreter(PSDocument * gs)
714 int std_in[2] = { -1, -1 }; /* pipe to interp stdin */
715 int std_out[2]; /* pipe from interp stdout */
716 int std_err[2]; /* pipe from interp stderr */
718 LOG ("Start the interpreter")
721 #define NUM_GS_ARGS (NUM_ARGS - 20)
722 #define NUM_ALPHA_ARGS 10
724 char *argv[NUM_ARGS], *dir, *gv_env;
725 char **gs_args, **alpha_args = NULL;
731 stop_interpreter(gs);
733 if(gs->disable_start == TRUE)
736 /* set up the args... */
737 gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS);
738 for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++)
739 argv[argc] = gs_args[i];
741 if(gs->antialiased) {
742 if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
743 alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
745 alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(),
746 " ", NUM_ALPHA_ARGS);
747 for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++)
748 argv[argc] = alpha_args[i];
751 argv[argc++] = "-sDEVICE=x11";
752 argv[argc++] = "-dNOPAUSE";
753 argv[argc++] = "-dQUIET";
754 /* I assume we do _not_ want to change this... (: */
755 argv[argc++] = "-dSAFER";
757 /* set up the pipes */
758 if(gs->send_filename_to_gs) {
759 argv[argc++] = PS_DOCUMENT_GET_PS_FILE(gs);
761 argv[argc++] = "quit";
768 if(!gs->reading_from_pipe && !gs->send_filename_to_gs) {
769 if(pipe(std_in) == -1) {
770 g_critical("Unable to open pipe to Ghostscript.");
774 if(pipe(std_out) == -1) {
778 if(pipe(std_err) == -1) {
785 gs->interpreter_pid = fork();
786 switch (gs->interpreter_pid) {
802 if(!gs->reading_from_pipe) {
803 if(gs->send_filename_to_gs) {
805 /* just in case gs tries to read from stdin */
806 stdinfd = open("/dev/null", O_RDONLY);
819 gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld",
820 gdk_x11_drawable_get_xid(gs->pstarget),
821 gdk_x11_drawable_get_xid(gs->bpixmap));
824 LOG ("Launching ghostview with env %s", gv_env)
826 /* change to directory where the input file is. This helps
827 * with postscript-files which include other files using
829 dir = g_path_get_dirname(gs->gs_filename);
833 execvp(argv[0], argv);
836 g_print("Unable to execute [%s]\n", argv[0]);
840 g_strfreev(alpha_args);
843 default: /* parent */
844 if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
847 /* use non-blocking IO for pipe to ghostscript */
848 result = fcntl(std_in[1], F_GETFL, 0);
849 fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
850 gs->interpreter_input = std_in[1];
853 gs->interpreter_input = -1;
856 gs->interpreter_output = std_out[0];
858 gs->interpreter_err = std_err[0];
859 gs->interpreter_output_id =
860 gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
861 gs->interpreter_error_id =
862 gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
869 stop_interpreter(PSDocument * gs)
871 if(gs->interpreter_pid > 0) {
873 LOG ("Stop the interpreter")
874 kill(gs->interpreter_pid, SIGTERM);
875 while((wait(&status) == -1) && (errno == EINTR)) ;
876 gs->interpreter_pid = -1;
878 ps_document_cleanup(gs);
879 gs->gs_status = _("Interpreter failed.");
883 if(gs->interpreter_input >= 0) {
884 close(gs->interpreter_input);
885 gs->interpreter_input = -1;
886 if(gs->interpreter_input_id != 0) {
887 gdk_input_remove(gs->interpreter_input_id);
888 gs->interpreter_input_id = 0;
890 while(gs->ps_input) {
891 struct record_list *ps_old = gs->ps_input;
892 gs->ps_input = gs->ps_input->next;
893 if(ps_old->close && NULL != ps_old->fp)
895 g_free((char *) ps_old);
899 if(gs->interpreter_output >= 0) {
900 close(gs->interpreter_output);
901 gs->interpreter_output = -1;
902 if(gs->interpreter_output_id) {
903 gdk_input_remove(gs->interpreter_output_id);
904 gs->interpreter_output_id = 0;
908 if(gs->interpreter_err >= 0) {
909 close(gs->interpreter_err);
910 gs->interpreter_err = -1;
911 if(gs->interpreter_error_id) {
912 gdk_input_remove(gs->interpreter_error_id);
913 gs->interpreter_error_id = 0;
920 /* If file exists and is a regular file then return its length, else -1 */
922 file_length(const gchar * filename)
924 struct stat stat_rec;
926 if(filename && (stat(filename, &stat_rec) == 0)
927 && S_ISREG(stat_rec.st_mode))
928 return stat_rec.st_size;
933 /* Test if file exists, is a regular file and its length is > 0 */
935 file_readable(const char *filename)
937 return (file_length(filename) > 0);
941 * Decompress gs->gs_filename if necessary
942 * Set gs->filename_unc to the name of the uncompressed file or NULL.
943 * Error reporting via signal 'interpreter_message'
944 * Return name of input file to use or NULL on error..
947 check_filecompressed(PSDocument * gs)
951 gchar *filename, *filename_unc, *filename_err, *cmdline;
957 if((file = fopen(gs->gs_filename, "r"))
958 && (fread(buf, sizeof(gchar), 3, file) == 3)) {
959 if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
960 /* file is gzipped or compressed */
961 cmd = gtk_gs_defaults_get_ungzip_cmd();
963 else if(strncmp(buf, "BZh", 3) == 0) {
964 /* file is compressed with bzip2 */
965 cmd = gtk_gs_defaults_get_unbzip2_cmd();
972 return gs->gs_filename;
974 /* do the decompression */
975 filename = g_shell_quote(gs->gs_filename);
976 filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
977 if((fd = mkstemp(filename_unc)) < 0) {
978 g_free(filename_unc);
983 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
984 if((fd = mkstemp(filename_err)) < 0) {
985 g_free(filename_err);
986 g_free(filename_unc);
991 cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
992 filename, filename_unc, filename_err);
993 if((system(cmdline) == 0)
994 && file_readable(filename_unc)
995 && (file_length(filename_err) == 0)) {
996 /* sucessfully uncompressed file */
997 gs->gs_filename_unc = filename_unc;
1001 g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
1003 ps_document_emit_error_msg(gs, buf);
1004 if(file_length(filename_err) > 0) {
1006 if((err = fopen(filename_err, "r"))) {
1007 /* print file to message window */
1008 while(fgets(buf, 1024, err))
1009 ps_document_emit_error_msg(gs, buf);
1013 unlink(filename_unc);
1014 g_free(filename_unc);
1015 filename_unc = NULL;
1017 unlink(filename_err);
1018 g_free(filename_err);
1021 return filename_unc;
1025 * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
1026 * pdf file if necessary.
1027 * Set gs->filename_dsc to the name of the dsc file or NULL.
1028 * Error reporting via signal 'interpreter_message'.
1031 check_pdf(PSDocument * gs)
1034 gchar buf[1024], *filename;
1037 /* use uncompressed file as input if necessary */
1038 filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
1040 if((file = fopen(filename, "r"))
1041 && (fread(buf, sizeof(char), 5, file) == 5)
1042 && (strncmp(buf, "%PDF-", 5) == 0)) {
1043 /* we found a PDF file */
1044 gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
1045 filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1046 if((fd = mkstemp(filename_dsc)) < 0) {
1050 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1051 if((fd = mkstemp(filename_err)) < 0) {
1052 g_free(filename_dsc);
1056 fname = g_shell_quote(filename);
1057 cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1059 /* this command (sometimes?) prints error messages to stdout! */
1060 cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1063 if((system(cmdline) == 0) && file_readable(filename_dsc)) {
1066 filename = gs->gs_filename_dsc = filename_dsc;
1068 if(file_length(filename_err) > 0) {
1069 gchar *err_msg = " ";
1074 if((err = fopen(filename_err, "r"))) {
1076 /* print the content of the file to a message box */
1077 while(fgets(buf, 1024, err))
1078 err_msg = g_strconcat(err_msg, buf, NULL);
1080 /* FIXME The dialog is not yet set to modal, difficult to
1081 * get the parent of the dialog box here
1084 dialog = gtk_message_dialog_new(NULL,
1086 GTK_MESSAGE_WARNING,
1088 ("There was an error while scaning the file: %s \n%s"),
1089 gs->gs_filename, err_msg);
1091 gdk_color_parse("white", &color);
1092 gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1094 g_signal_connect(G_OBJECT(dialog), "response",
1095 G_CALLBACK(gtk_widget_destroy), NULL);
1097 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1098 gtk_widget_show(dialog);
1106 g_snprintf(buf, 1024,
1107 _("Error while converting pdf file %s:\n"), filename);
1108 ps_document_emit_error_msg(gs, buf);
1110 if(file_length(filename_err) > 0) {
1112 if((err = fopen(filename_err, "r"))) {
1113 /* print file to message window */
1114 while(fgets(buf, 1024, err))
1115 ps_document_emit_error_msg(gs, buf);
1118 unlink(filename_dsc);
1119 g_free(filename_dsc);
1122 unlink(filename_err);
1123 g_free(filename_err);
1131 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1132 /* never mind this patch: a properly working X server should take care of
1133 calculating the proper values. */
1137 # ifndef HAVE_XINERAMA
1138 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1141 dpy = (Display *) GDK_DISPLAY();
1142 if(XineramaIsActive(dpy)) {
1144 XineramaScreenInfo *head_info;
1145 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1146 /* fake it with dimensions of the first head for now */
1147 return 25.4 * head_info[0].width / gdk_screen_width_mm();
1150 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1159 # ifndef HAVE_XINERAMA
1160 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1163 dpy = (Display *) GDK_DISPLAY();
1164 if(XineramaIsActive(dpy)) {
1166 XineramaScreenInfo *head_info;
1167 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1168 /* fake it with dimensions of the first head for now */
1169 return 25.4 * head_info[0].height / gdk_screen_height_mm();
1172 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1181 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1187 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1189 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1191 /* Compute new size of window, sets xdpi and ydpi if necessary.
1192 * returns True if new window size is different */
1194 compute_size(PSDocument * gs)
1196 guint new_width = 1;
1197 guint new_height = 1;
1198 gboolean change = FALSE;
1201 /* width and height can be changed, calculate window size according */
1202 /* to xpdi and ydpi */
1203 orientation = ps_document_get_orientation(gs);
1205 switch (orientation) {
1206 case GTK_GS_ORIENTATION_PORTRAIT:
1207 case GTK_GS_ORIENTATION_UPSIDEDOWN:
1208 new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1209 new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1211 case GTK_GS_ORIENTATION_LANDSCAPE:
1212 case GTK_GS_ORIENTATION_SEASCAPE:
1213 new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1214 new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1218 change = (new_width != gs->width * gs->zoom_factor)
1219 || (new_height != gs->height * gs->zoom_factor);
1220 gs->width = (gint) (new_width * gs->zoom_factor);
1221 gs->height = (gint) (new_height * gs->zoom_factor);
1227 ps_document_enable_interpreter(PSDocument * gs)
1229 g_return_val_if_fail(gs != NULL, FALSE);
1230 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1232 if(!gs->gs_filename)
1235 gs->disable_start = FALSE;
1237 return start_interpreter(gs);
1240 /* publicly accessible functions */
1243 ps_document_get_type(void)
1245 static GType gs_type = 0;
1247 GTypeInfo gs_info = {
1248 sizeof(PSDocumentClass),
1249 (GBaseInitFunc) NULL,
1250 (GBaseFinalizeFunc) NULL,
1251 (GClassInitFunc) ps_document_class_init,
1252 (GClassFinalizeFunc) NULL,
1253 NULL, /* class_data */
1255 0, /* n_preallocs */
1256 (GInstanceInitFunc) ps_document_init
1259 static const GInterfaceInfo document_info =
1261 (GInterfaceInitFunc) ps_document_document_iface_init,
1266 gs_type = g_type_register_static(G_TYPE_OBJECT,
1267 "PSDocument", &gs_info, 0);
1269 g_type_add_interface_static (gs_type,
1279 * Show error message -> send signal "interpreter_message"
1282 ps_document_emit_error_msg(PSDocument * gs, const gchar * msg)
1284 gdk_pointer_ungrab(GDK_CURRENT_TIME);
1285 if(strstr(msg, "Error:")) {
1286 gs->gs_status = _("File is not a valid PostScript document.");
1287 ps_document_cleanup(gs);
1292 document_load(PSDocument * gs, const gchar * fname)
1294 g_return_val_if_fail(gs != NULL, FALSE);
1295 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1297 LOG ("Load the document")
1299 /* clean up previous document */
1300 ps_document_cleanup(gs);
1307 /* prepare this document */
1309 /* default values: no dsc information available */
1310 gs->structured_doc = FALSE;
1311 gs->send_filename_to_gs = TRUE;
1312 gs->current_page = -2;
1315 /* an absolute path */
1316 gs->gs_filename = g_strdup(fname);
1319 /* path relative to our cwd: make it absolute */
1320 gchar *cwd = g_get_current_dir();
1321 gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1325 if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1326 gs->send_filename_to_gs = FALSE;
1330 * We need to make sure that the file is loadable/exists!
1331 * otherwise we want to exit without loading new stuff...
1333 gchar *filename = NULL;
1335 if(!file_readable(fname)) {
1337 g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1338 ps_document_emit_error_msg(gs, buf);
1339 gs->gs_status = _("File is not readable.");
1342 filename = check_filecompressed(gs);
1344 filename = check_pdf(gs);
1347 if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1348 ps_document_cleanup(gs);
1352 /* we grab the vital statistics!!! */
1353 gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1355 g_object_notify (G_OBJECT (gs), "title");
1357 if(gs->doc == NULL) {
1358 /* File does not seem to be a Postscript one */
1360 g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1361 ps_document_emit_error_msg(gs, buf);
1362 ps_document_cleanup(gs);
1363 gs->gs_status = _("The file is not a PostScript document.");
1367 if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1368 (gs->doc->epsf && gs->doc->numpages > 1)) {
1369 gs->structured_doc = TRUE;
1370 gs->send_filename_to_gs = FALSE;
1373 /* We have to set up the orientation of the document */
1376 /* orientation can only be portrait, and landscape or none.
1377 This is the document default. A document can have
1378 pages in landscape and some in portrait */
1379 if(gs->override_orientation) {
1380 /* If the orientation should be override...
1381 then gs->orientation has already the correct
1382 value (it was set when the widget was created */
1387 /* Otherwise, set the proper orientation for the doc */
1388 gs->real_orientation = gs->doc->orientation;
1391 ps_document_set_page_size(gs, -1, gs->current_page);
1394 gs->gs_status = _("Document loaded.");
1401 ps_document_next_page(PSDocument * gs)
1405 LOG ("Make ghostscript render next page")
1407 g_return_val_if_fail(gs != NULL, FALSE);
1408 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1410 if(gs->interpreter_pid == 0) { /* no interpreter active */
1414 if(gs->busy) { /* interpreter is busy */
1420 event.xclient.type = ClientMessage;
1421 event.xclient.display = gdk_display;
1422 event.xclient.window = gs->message_window;
1423 event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1424 event.xclient.format = 32;
1426 gdk_error_trap_push();
1427 XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1429 gdk_error_trap_pop();
1435 ps_document_get_current_page(PSDocument * gs)
1437 g_return_val_if_fail(gs != NULL, -1);
1438 g_return_val_if_fail(GTK_IS_GS(gs), -1);
1440 return gs->current_page;
1444 ps_document_get_page_count(PSDocument * gs)
1446 if(!gs->gs_filename)
1450 if(gs->structured_doc)
1451 return gs->doc->numpages;
1460 ps_document_goto_page(PSDocument * gs, gint page)
1462 g_return_val_if_fail(gs != NULL, FALSE);
1463 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1465 LOG ("Go to page %d", page)
1467 if(!gs->gs_filename) {
1471 /* range checking... */
1475 if(gs->structured_doc && gs->doc) {
1477 LOG ("It's a structured document, let's send one page to gs")
1479 if(page >= gs->doc->numpages)
1480 page = gs->doc->numpages - 1;
1482 if(page == gs->current_page && !gs->changed)
1485 gs->current_page = page;
1487 if(gs->doc->pages[page].orientation != NONE &&
1488 !gs->override_orientation &&
1489 gs->doc->pages[page].orientation != gs->real_orientation) {
1490 gs->real_orientation = gs->doc->pages[page].orientation;
1494 ps_document_set_page_size(gs, -1, page);
1496 gs->changed = FALSE;
1498 if(is_interpreter_ready(gs)) {
1499 ps_document_next_page(gs);
1502 ps_document_enable_interpreter(gs);
1503 send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1504 send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1507 send_ps(gs, gs->doc->pages[gs->current_page].begin,
1508 gs->doc->pages[gs->current_page].len, FALSE);
1511 /* Unstructured document */
1512 /* In the case of non structured documents,
1513 GS read the PS from the actual file (via command
1514 line. Hence, ggv only send a signal next page.
1515 If ghostview is not running it is usually because
1516 the last page of the file was displayed. In that
1517 case, ggv restarts GS again and the first page is displayed.
1520 LOG ("It's an unstructured document, gs will just read the file")
1522 if(page == gs->current_page && !gs->changed)
1525 ps_document_set_page_size(gs, -1, page);
1527 if(!is_interpreter_ready(gs))
1528 ps_document_enable_interpreter(gs);
1530 gs->current_page = page;
1532 ps_document_next_page(gs);
1538 * set pagesize sets the size from
1539 * if new_pagesize is -1, then it is set to either
1540 * a) the default settings of pageid, if they exist, or if pageid != -1.
1541 * b) the default setting of the document, if it exists.
1542 * c) the default setting of the widget.
1543 * otherwise, the new_pagesize is used as the pagesize
1546 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1552 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1554 LOG ("Set the page size")
1556 g_return_val_if_fail(gs != NULL, FALSE);
1557 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1559 if(new_pagesize == -1) {
1560 if(gs->default_size > 0)
1561 new_pagesize = gs->default_size;
1562 if(!gs->override_size && gs->doc) {
1563 /* If we have a document:
1564 We use -- the page size (if specified)
1565 or the doc. size (if specified)
1566 or the page bbox (if specified)
1569 if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1570 (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1571 new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1573 else if(gs->doc->default_page_size != NULL) {
1574 new_pagesize = gs->doc->default_page_size - gs->doc->size;
1576 else if((pageid >= 0) &&
1577 (gs->doc->numpages > pageid) &&
1579 (gs->doc->pages[pageid].boundingbox[URX] >
1580 gs->doc->pages[pageid].boundingbox[LLX]) &&
1581 (gs->doc->pages[pageid].boundingbox[URY] >
1582 gs->doc->pages[pageid].boundingbox[LLY])) {
1585 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1586 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1592 /* Compute bounding box */
1593 if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) { /* epsf or bbox */
1596 (gs->doc->pages[pageid].boundingbox[URX] >
1597 gs->doc->pages[pageid].boundingbox[LLX])
1598 && (gs->doc->pages[pageid].boundingbox[URY] >
1599 gs->doc->pages[pageid].boundingbox[LLY])) {
1601 new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1602 new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1603 new_urx = gs->doc->pages[pageid].boundingbox[URX];
1604 new_ury = gs->doc->pages[pageid].boundingbox[URY];
1606 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1607 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1609 new_llx = gs->doc->boundingbox[LLX];
1610 new_lly = gs->doc->boundingbox[LLY];
1611 new_urx = gs->doc->boundingbox[URX];
1612 new_ury = gs->doc->boundingbox[URY];
1616 if(new_pagesize < 0)
1617 new_pagesize = gs->default_size;
1618 new_llx = new_lly = 0;
1619 if(gs->doc && !gs->override_size && gs->doc->size &&
1620 (new_pagesize < gs->doc->numsizes)) {
1621 new_urx = gs->doc->size[new_pagesize].width;
1622 new_ury = gs->doc->size[new_pagesize].height;
1625 new_urx = papersizes[new_pagesize].width;
1626 new_ury = papersizes[new_pagesize].height;
1630 if(new_urx <= new_llx)
1631 new_urx = papersizes[12].width;
1632 if(new_ury <= new_lly)
1633 new_ury = papersizes[12].height;
1635 /* If bounding box changed, setup for new size. */
1636 /* ps_document_disable_interpreter (gs); */
1637 if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1638 (new_urx != gs->urx) || (new_ury != gs->ury)) {
1655 ps_document_zoom_to_fit(PSDocument * gs, gboolean fit_width)
1659 guint avail_w, avail_h;
1661 g_return_val_if_fail(gs != NULL, 0.0);
1662 g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1664 avail_w = (gs->avail_w > 0) ? gs->avail_w : gs->width;
1665 avail_h = (gs->avail_h > 0) ? gs->avail_h : gs->height;
1667 new_zoom = ((gfloat) avail_w) / ((gfloat) gs->width) * gs->zoom_factor;
1669 new_y = new_zoom * ((gfloat) gs->height) / gs->zoom_factor;
1671 new_zoom = ((gfloat) avail_h) / ((gfloat) gs->height) * gs->zoom_factor;
1678 ps_document_set_zoom(PSDocument * gs, gfloat zoom)
1680 g_return_if_fail(gs != NULL);
1681 g_return_if_fail(GTK_IS_GS(gs));
1683 switch (gs->zoom_mode) {
1684 case GTK_GS_ZOOM_FIT_WIDTH:
1685 zoom = ps_document_zoom_to_fit(gs, TRUE);
1687 case GTK_GS_ZOOM_FIT_PAGE:
1688 zoom = ps_document_zoom_to_fit(gs, FALSE);
1690 case GTK_GS_ZOOM_ABSOLUTE:
1695 if(fabs(gs->zoom_factor - zoom) > 0.001) {
1696 gs->zoom_factor = zoom;
1701 ps_document_goto_page(gs, gs->current_page);
1705 ps_document_load (EvDocument *document,
1712 filename = g_filename_from_uri (uri, NULL, error);
1716 result = document_load (PS_DOCUMENT (document), filename);
1724 ps_document_save (EvDocument *document,
1728 g_warning ("ps_document_save not implemented"); /* FIXME */
1733 ps_document_get_n_pages (EvDocument *document)
1735 return ps_document_get_page_count (PS_DOCUMENT (document));
1739 ps_document_set_page (EvDocument *document,
1742 ps_document_goto_page (PS_DOCUMENT (document), page);
1746 ps_document_get_page (EvDocument *document)
1748 return ps_document_get_current_page (PS_DOCUMENT (document));
1752 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
1754 PSDocument *gs = (PSDocument *) data;
1756 if(event->type != GDK_CLIENT_EVENT)
1759 gs->message_window = event->client.data.l[0];
1761 if (event->client.message_type == gs_class->page_atom) {
1762 LOG ("GS rendered the document")
1764 ev_document_changed (EV_DOCUMENT (gs));
1771 ps_document_set_target (EvDocument *document,
1772 GdkDrawable *target)
1774 PSDocument *gs = PS_DOCUMENT (document);
1778 gs->pstarget = target;
1781 gdk_window_get_user_data (gs->pstarget, &data);
1782 g_return_if_fail (GTK_IS_WIDGET (data));
1784 widget = GTK_WIDGET (data);
1785 g_signal_connect (widget, "event",
1786 G_CALLBACK (ps_document_widget_event),
1790 ps_document_goto_page (gs, gs->current_page);
1794 ps_document_set_scale (EvDocument *document,
1797 ps_document_set_zoom (PS_DOCUMENT (document), scale);
1801 ps_document_set_page_offset (EvDocument *document,
1805 PSDocument *gs = PS_DOCUMENT (document);
1807 gs->page_x_offset = x;
1808 gs->page_y_offset = y;
1812 ps_document_get_page_size (EvDocument *document,
1817 /* Post script documents never vary in size */
1819 PSDocument *gs = PS_DOCUMENT (document);
1826 *height = gs->height;
1831 ps_document_render (EvDocument *document,
1837 PSDocument *gs = PS_DOCUMENT (document);
1842 if (gs->pstarget == NULL ||
1843 gs->bpixmap == NULL) {
1847 page.x = gs->page_x_offset;
1848 page.y = gs->page_y_offset;
1849 page.width = gs->width;
1850 page.height = gs->height;
1854 draw.width = clip_width;
1855 draw.height = clip_height;
1857 gc = gdk_gc_new (gs->pstarget);
1859 gdk_draw_drawable (gs->pstarget, gc,
1861 draw.x - page.x, draw.y - page.y,
1863 draw.width, draw.height);
1865 LOG ("Copy the internal pixmap: %d %d %d %d %d %d",
1866 draw.x - page.x, draw.y - page.y,
1867 draw.x, draw.y, draw.width, draw.height)
1869 g_object_unref (gc);
1873 ps_document_get_text (EvDocument *document, GdkRectangle *rect)
1875 g_warning ("ps_document_get_text not implemented"); /* FIXME ? */
1880 ps_document_get_link (EvDocument *document,
1888 ps_document_document_iface_init (EvDocumentIface *iface)
1890 iface->load = ps_document_load;
1891 iface->save = ps_document_save;
1892 iface->get_text = ps_document_get_text;
1893 iface->get_link = ps_document_get_link;
1894 iface->get_n_pages = ps_document_get_n_pages;
1895 iface->set_page = ps_document_set_page;
1896 iface->get_page = ps_document_get_page;
1897 iface->set_scale = ps_document_set_scale;
1898 iface->set_target = ps_document_set_target;
1899 iface->set_page_offset = ps_document_set_page_offset;
1900 iface->get_page_size = ps_document_get_page_size;
1901 iface->render = ps_document_render;