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);
557 LOG ("GS property %s", buf)
560 setlocale(LC_NUMERIC, savelocale);
562 gdk_property_change(gs->pstarget,
564 gs_class->string_atom,
565 8, GDK_PROP_MODE_REPLACE, buf, strlen(buf));
579 is_interpreter_ready(PSDocument * gs)
581 return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
585 interpreter_failed(PSDocument * gs)
587 stop_interpreter(gs);
591 output(gpointer data, gint source, GdkInputCondition condition)
593 char buf[MAX_BUFSIZE + 1], *msg;
595 PSDocument *gs = PS_DOCUMENT(data);
597 if(source == gs->interpreter_output) {
598 bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
599 if(bytes == 0) { /* EOF occurred */
600 close(gs->interpreter_output);
601 gs->interpreter_output = -1;
602 gdk_input_remove(gs->interpreter_output_id);
605 else if(bytes == -1) {
607 interpreter_failed(gs);
610 if(gs->interpreter_err == -1) {
611 stop_interpreter(gs);
614 else if(source == gs->interpreter_err) {
615 bytes = read(gs->interpreter_err, buf, MAX_BUFSIZE);
616 if(bytes == 0) { /* EOF occurred */
617 close(gs->interpreter_err);
618 gs->interpreter_err = -1;
619 gdk_input_remove(gs->interpreter_error_id);
622 else if(bytes == -1) {
624 interpreter_failed(gs);
627 if(gs->interpreter_output == -1) {
628 stop_interpreter(gs);
634 ps_document_emit_error_msg (gs, msg);
639 input(gpointer data, gint source, GdkInputCondition condition)
641 PSDocument *gs = PS_DOCUMENT(data);
643 void (*oldsig) (int);
644 oldsig = signal(SIGPIPE, catchPipe);
647 if(gs->buffer_bytes_left == 0) {
648 /* Get a new section if required */
649 if(gs->ps_input && gs->bytes_left == 0) {
650 struct record_list *ps_old = gs->ps_input;
651 gs->ps_input = ps_old->next;
652 if(ps_old->close && NULL != ps_old->fp)
654 g_free((char *) ps_old);
656 /* Have to seek at the beginning of each section */
657 if(gs->ps_input && gs->ps_input->seek_needed) {
658 fseek(gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
659 gs->ps_input->seek_needed = FALSE;
660 gs->bytes_left = gs->ps_input->len;
663 if(gs->bytes_left > MAX_BUFSIZE) {
664 gs->buffer_bytes_left =
665 fread(gs->input_buffer, sizeof(char), MAX_BUFSIZE, gs->ps_input->fp);
667 else if(gs->bytes_left > 0) {
668 gs->buffer_bytes_left =
669 fread(gs->input_buffer,
670 sizeof(char), gs->bytes_left, gs->ps_input->fp);
673 gs->buffer_bytes_left = 0;
675 if(gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
676 interpreter_failed(gs); /* Error occurred */
678 gs->input_buffer_ptr = gs->input_buffer;
679 gs->bytes_left -= gs->buffer_bytes_left;
682 if(gs->buffer_bytes_left > 0) {
683 /* g_print (" writing: %s\n",gs->input_buffer_ptr); */
685 bytes_written = write(gs->interpreter_input,
686 gs->input_buffer_ptr, gs->buffer_bytes_left);
689 ps_document_emit_error_msg(gs, g_strdup(_("Broken pipe.")));
691 interpreter_failed(gs);
693 else if(bytes_written == -1) {
694 if((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
695 interpreter_failed(gs); /* Something bad happened */
699 gs->buffer_bytes_left -= bytes_written;
700 gs->input_buffer_ptr += bytes_written;
704 while(gs->ps_input && gs->buffer_bytes_left == 0);
706 signal(SIGPIPE, oldsig);
708 if(gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
709 if(gs->interpreter_input_id != 0) {
710 gdk_input_remove(gs->interpreter_input_id);
711 gs->interpreter_input_id = 0;
717 start_interpreter(PSDocument * gs)
719 int std_in[2] = { -1, -1 }; /* pipe to interp stdin */
720 int std_out[2]; /* pipe from interp stdout */
721 int std_err[2]; /* pipe from interp stderr */
723 LOG ("Start the interpreter")
726 #define NUM_GS_ARGS (NUM_ARGS - 20)
727 #define NUM_ALPHA_ARGS 10
729 char *argv[NUM_ARGS], *dir, *gv_env;
730 char **gs_args, **alpha_args = NULL;
736 stop_interpreter(gs);
738 if(gs->disable_start == TRUE)
741 /* set up the args... */
742 gs_args = g_strsplit(gtk_gs_defaults_get_interpreter_cmd(), " ", NUM_GS_ARGS);
743 for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++)
744 argv[argc] = gs_args[i];
746 if(gs->antialiased) {
747 if(strlen(gtk_gs_defaults_get_alpha_parameters()) == 0)
748 alpha_args = g_strsplit(ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
750 alpha_args = g_strsplit(gtk_gs_defaults_get_alpha_parameters(),
751 " ", NUM_ALPHA_ARGS);
752 for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++)
753 argv[argc] = alpha_args[i];
756 argv[argc++] = "-sDEVICE=x11";
757 argv[argc++] = "-dNOPAUSE";
758 argv[argc++] = "-dQUIET";
759 /* I assume we do _not_ want to change this... (: */
760 argv[argc++] = "-dSAFER";
762 /* set up the pipes */
763 if(gs->send_filename_to_gs) {
764 argv[argc++] = PS_DOCUMENT_GET_PS_FILE(gs);
766 argv[argc++] = "quit";
773 if(!gs->reading_from_pipe && !gs->send_filename_to_gs) {
774 if(pipe(std_in) == -1) {
775 g_critical("Unable to open pipe to Ghostscript.");
779 if(pipe(std_out) == -1) {
783 if(pipe(std_err) == -1) {
789 gv_env = g_strdup_printf("GHOSTVIEW=%ld %ld",
790 gdk_x11_drawable_get_xid(gs->pstarget),
791 gdk_x11_drawable_get_xid(gs->bpixmap));
793 LOG ("Launching ghostview with env %s", gv_env)
796 gs->interpreter_pid = fork();
797 switch (gs->interpreter_pid) {
813 if(!gs->reading_from_pipe) {
814 if(gs->send_filename_to_gs) {
816 /* just in case gs tries to read from stdin */
817 stdinfd = open("/dev/null", O_RDONLY);
832 /* change to directory where the input file is. This helps
833 * with postscript-files which include other files using
835 dir = g_path_get_dirname(gs->gs_filename);
839 execvp(argv[0], argv);
842 g_print("Unable to execute [%s]\n", argv[0]);
846 g_strfreev(alpha_args);
849 default: /* parent */
850 if(!gs->send_filename_to_gs && !gs->reading_from_pipe) {
853 /* use non-blocking IO for pipe to ghostscript */
854 result = fcntl(std_in[1], F_GETFL, 0);
855 fcntl(std_in[1], F_SETFL, result | O_NONBLOCK);
856 gs->interpreter_input = std_in[1];
859 gs->interpreter_input = -1;
862 gs->interpreter_output = std_out[0];
864 gs->interpreter_err = std_err[0];
865 gs->interpreter_output_id =
866 gdk_input_add(std_out[0], GDK_INPUT_READ, output, gs);
867 gs->interpreter_error_id =
868 gdk_input_add(std_err[0], GDK_INPUT_READ, output, gs);
875 stop_interpreter(PSDocument * gs)
877 if(gs->interpreter_pid > 0) {
879 LOG ("Stop the interpreter")
880 kill(gs->interpreter_pid, SIGTERM);
881 while((wait(&status) == -1) && (errno == EINTR)) ;
882 gs->interpreter_pid = -1;
884 ps_document_cleanup(gs);
885 gs->gs_status = _("Interpreter failed.");
889 if(gs->interpreter_input >= 0) {
890 close(gs->interpreter_input);
891 gs->interpreter_input = -1;
892 if(gs->interpreter_input_id != 0) {
893 gdk_input_remove(gs->interpreter_input_id);
894 gs->interpreter_input_id = 0;
896 while(gs->ps_input) {
897 struct record_list *ps_old = gs->ps_input;
898 gs->ps_input = gs->ps_input->next;
899 if(ps_old->close && NULL != ps_old->fp)
901 g_free((char *) ps_old);
905 if(gs->interpreter_output >= 0) {
906 close(gs->interpreter_output);
907 gs->interpreter_output = -1;
908 if(gs->interpreter_output_id) {
909 gdk_input_remove(gs->interpreter_output_id);
910 gs->interpreter_output_id = 0;
914 if(gs->interpreter_err >= 0) {
915 close(gs->interpreter_err);
916 gs->interpreter_err = -1;
917 if(gs->interpreter_error_id) {
918 gdk_input_remove(gs->interpreter_error_id);
919 gs->interpreter_error_id = 0;
926 /* If file exists and is a regular file then return its length, else -1 */
928 file_length(const gchar * filename)
930 struct stat stat_rec;
932 if(filename && (stat(filename, &stat_rec) == 0)
933 && S_ISREG(stat_rec.st_mode))
934 return stat_rec.st_size;
939 /* Test if file exists, is a regular file and its length is > 0 */
941 file_readable(const char *filename)
943 return (file_length(filename) > 0);
947 * Decompress gs->gs_filename if necessary
948 * Set gs->filename_unc to the name of the uncompressed file or NULL.
949 * Error reporting via signal 'interpreter_message'
950 * Return name of input file to use or NULL on error..
953 check_filecompressed(PSDocument * gs)
957 gchar *filename, *filename_unc, *filename_err, *cmdline;
963 if((file = fopen(gs->gs_filename, "r"))
964 && (fread(buf, sizeof(gchar), 3, file) == 3)) {
965 if((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
966 /* file is gzipped or compressed */
967 cmd = gtk_gs_defaults_get_ungzip_cmd();
969 else if(strncmp(buf, "BZh", 3) == 0) {
970 /* file is compressed with bzip2 */
971 cmd = gtk_gs_defaults_get_unbzip2_cmd();
978 return gs->gs_filename;
980 /* do the decompression */
981 filename = g_shell_quote(gs->gs_filename);
982 filename_unc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
983 if((fd = mkstemp(filename_unc)) < 0) {
984 g_free(filename_unc);
989 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
990 if((fd = mkstemp(filename_err)) < 0) {
991 g_free(filename_err);
992 g_free(filename_unc);
997 cmdline = g_strdup_printf("%s %s >%s 2>%s", cmd,
998 filename, filename_unc, filename_err);
999 if((system(cmdline) == 0)
1000 && file_readable(filename_unc)
1001 && (file_length(filename_err) == 0)) {
1002 /* sucessfully uncompressed file */
1003 gs->gs_filename_unc = filename_unc;
1007 g_snprintf(buf, 1024, _("Error while decompressing file %s:\n"),
1009 ps_document_emit_error_msg(gs, buf);
1010 if(file_length(filename_err) > 0) {
1012 if((err = fopen(filename_err, "r"))) {
1013 /* print file to message window */
1014 while(fgets(buf, 1024, err))
1015 ps_document_emit_error_msg(gs, buf);
1019 unlink(filename_unc);
1020 g_free(filename_unc);
1021 filename_unc = NULL;
1023 unlink(filename_err);
1024 g_free(filename_err);
1027 return filename_unc;
1031 * Check if gs->gs_filename or gs->gs_filename_unc is a pdf file and scan
1032 * pdf file if necessary.
1033 * Set gs->filename_dsc to the name of the dsc file or NULL.
1034 * Error reporting via signal 'interpreter_message'.
1037 check_pdf(PSDocument * gs)
1040 gchar buf[1024], *filename;
1043 /* use uncompressed file as input if necessary */
1044 filename = (gs->gs_filename_unc ? gs->gs_filename_unc : gs->gs_filename);
1046 if((file = fopen(filename, "r"))
1047 && (fread(buf, sizeof(char), 5, file) == 5)
1048 && (strncmp(buf, "%PDF-", 5) == 0)) {
1049 /* we found a PDF file */
1050 gchar *fname, *filename_dsc, *filename_err, *cmd, *cmdline;
1051 filename_dsc = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1052 if((fd = mkstemp(filename_dsc)) < 0) {
1056 filename_err = g_strconcat(g_get_tmp_dir(), "/ggvXXXXXX", NULL);
1057 if((fd = mkstemp(filename_err)) < 0) {
1058 g_free(filename_dsc);
1062 fname = g_shell_quote(filename);
1063 cmd = g_strdup_printf(gtk_gs_defaults_get_dsc_cmd(), filename_dsc, fname);
1065 /* this command (sometimes?) prints error messages to stdout! */
1066 cmdline = g_strdup_printf("%s >%s 2>&1", cmd, filename_err);
1069 if((system(cmdline) == 0) && file_readable(filename_dsc)) {
1072 filename = gs->gs_filename_dsc = filename_dsc;
1074 if(file_length(filename_err) > 0) {
1075 gchar *err_msg = " ";
1080 if((err = fopen(filename_err, "r"))) {
1082 /* print the content of the file to a message box */
1083 while(fgets(buf, 1024, err))
1084 err_msg = g_strconcat(err_msg, buf, NULL);
1086 /* FIXME The dialog is not yet set to modal, difficult to
1087 * get the parent of the dialog box here
1090 dialog = gtk_message_dialog_new(NULL,
1092 GTK_MESSAGE_WARNING,
1094 ("There was an error while scaning the file: %s \n%s"),
1095 gs->gs_filename, err_msg);
1097 gdk_color_parse("white", &color);
1098 gtk_widget_modify_bg(GTK_WIDGET(dialog), GTK_STATE_NORMAL, &color);
1100 g_signal_connect(G_OBJECT(dialog), "response",
1101 G_CALLBACK(gtk_widget_destroy), NULL);
1103 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1104 gtk_widget_show(dialog);
1112 g_snprintf(buf, 1024,
1113 _("Error while converting pdf file %s:\n"), filename);
1114 ps_document_emit_error_msg(gs, buf);
1116 if(file_length(filename_err) > 0) {
1118 if((err = fopen(filename_err, "r"))) {
1119 /* print file to message window */
1120 while(fgets(buf, 1024, err))
1121 ps_document_emit_error_msg(gs, buf);
1124 unlink(filename_dsc);
1125 g_free(filename_dsc);
1128 unlink(filename_err);
1129 g_free(filename_err);
1137 #ifdef BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED
1138 /* never mind this patch: a properly working X server should take care of
1139 calculating the proper values. */
1143 # ifndef HAVE_XINERAMA
1144 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1147 dpy = (Display *) GDK_DISPLAY();
1148 if(XineramaIsActive(dpy)) {
1150 XineramaScreenInfo *head_info;
1151 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1152 /* fake it with dimensions of the first head for now */
1153 return 25.4 * head_info[0].width / gdk_screen_width_mm();
1156 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1165 # ifndef HAVE_XINERAMA
1166 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1169 dpy = (Display *) GDK_DISPLAY();
1170 if(XineramaIsActive(dpy)) {
1172 XineramaScreenInfo *head_info;
1173 head_info = (XineramaScreenInfo *) XineramaQueryScreens(dpy, &num_heads);
1174 /* fake it with dimensions of the first head for now */
1175 return 25.4 * head_info[0].height / gdk_screen_height_mm();
1178 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1187 return 25.4 * gdk_screen_width() / gdk_screen_width_mm();
1193 return 25.4 * gdk_screen_height() / gdk_screen_height_mm();
1195 #endif /* BROKEN_XINERAMA_PATCH_THAT_SHOULD_NOT_BE_USED */
1197 /* Compute new size of window, sets xdpi and ydpi if necessary.
1198 * returns True if new window size is different */
1200 compute_size(PSDocument * gs)
1202 guint new_width = 1;
1203 guint new_height = 1;
1204 gboolean change = FALSE;
1207 /* width and height can be changed, calculate window size according */
1208 /* to xpdi and ydpi */
1209 orientation = ps_document_get_orientation(gs);
1211 switch (orientation) {
1212 case GTK_GS_ORIENTATION_PORTRAIT:
1213 case GTK_GS_ORIENTATION_UPSIDEDOWN:
1214 new_width = (gs->urx - gs->llx) / 72.0 * gs->xdpi + 0.5;
1215 new_height = (gs->ury - gs->lly) / 72.0 * gs->ydpi + 0.5;
1217 case GTK_GS_ORIENTATION_LANDSCAPE:
1218 case GTK_GS_ORIENTATION_SEASCAPE:
1219 new_width = (gs->ury - gs->lly) / 72.0 * gs->xdpi + 0.5;
1220 new_height = (gs->urx - gs->llx) / 72.0 * gs->ydpi + 0.5;
1224 change = (new_width != gs->width * gs->zoom_factor)
1225 || (new_height != gs->height * gs->zoom_factor);
1226 gs->width = (gint) (new_width * gs->zoom_factor);
1227 gs->height = (gint) (new_height * gs->zoom_factor);
1233 ps_document_enable_interpreter(PSDocument * gs)
1235 g_return_val_if_fail(gs != NULL, FALSE);
1236 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1238 if(!gs->gs_filename)
1241 gs->disable_start = FALSE;
1243 return start_interpreter(gs);
1246 /* publicly accessible functions */
1249 ps_document_get_type(void)
1251 static GType gs_type = 0;
1253 GTypeInfo gs_info = {
1254 sizeof(PSDocumentClass),
1255 (GBaseInitFunc) NULL,
1256 (GBaseFinalizeFunc) NULL,
1257 (GClassInitFunc) ps_document_class_init,
1258 (GClassFinalizeFunc) NULL,
1259 NULL, /* class_data */
1261 0, /* n_preallocs */
1262 (GInstanceInitFunc) ps_document_init
1265 static const GInterfaceInfo document_info =
1267 (GInterfaceInitFunc) ps_document_document_iface_init,
1272 gs_type = g_type_register_static(G_TYPE_OBJECT,
1273 "PSDocument", &gs_info, 0);
1275 g_type_add_interface_static (gs_type,
1285 * Show error message -> send signal "interpreter_message"
1288 ps_document_emit_error_msg(PSDocument * gs, const gchar * msg)
1290 gdk_pointer_ungrab(GDK_CURRENT_TIME);
1291 if(strstr(msg, "Error:")) {
1292 gs->gs_status = _("File is not a valid PostScript document.");
1293 ps_document_cleanup(gs);
1298 document_load(PSDocument * gs, const gchar * fname)
1300 g_return_val_if_fail(gs != NULL, FALSE);
1301 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1303 LOG ("Load the document")
1305 /* clean up previous document */
1306 ps_document_cleanup(gs);
1313 /* prepare this document */
1315 /* default values: no dsc information available */
1316 gs->structured_doc = FALSE;
1317 gs->send_filename_to_gs = TRUE;
1318 gs->current_page = -2;
1321 /* an absolute path */
1322 gs->gs_filename = g_strdup(fname);
1325 /* path relative to our cwd: make it absolute */
1326 gchar *cwd = g_get_current_dir();
1327 gs->gs_filename = g_strconcat(cwd, "/", fname, NULL);
1331 if((gs->reading_from_pipe = (strcmp(fname, "-") == 0))) {
1332 gs->send_filename_to_gs = FALSE;
1336 * We need to make sure that the file is loadable/exists!
1337 * otherwise we want to exit without loading new stuff...
1339 gchar *filename = NULL;
1341 if(!file_readable(fname)) {
1343 g_snprintf(buf, 1024, _("Cannot open file %s.\n"), fname);
1344 ps_document_emit_error_msg(gs, buf);
1345 gs->gs_status = _("File is not readable.");
1348 filename = check_filecompressed(gs);
1350 filename = check_pdf(gs);
1353 if(!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
1354 ps_document_cleanup(gs);
1358 /* we grab the vital statistics!!! */
1359 gs->doc = psscan(gs->gs_psfile, gs->respect_eof, filename);
1361 g_object_notify (G_OBJECT (gs), "title");
1363 if(gs->doc == NULL) {
1364 /* File does not seem to be a Postscript one */
1366 g_snprintf(buf, 1024, _("Error while scanning file %s\n"), fname);
1367 ps_document_emit_error_msg(gs, buf);
1368 ps_document_cleanup(gs);
1369 gs->gs_status = _("The file is not a PostScript document.");
1373 if((!gs->doc->epsf && gs->doc->numpages > 0) ||
1374 (gs->doc->epsf && gs->doc->numpages > 1)) {
1375 gs->structured_doc = TRUE;
1376 gs->send_filename_to_gs = FALSE;
1379 /* We have to set up the orientation of the document */
1382 /* orientation can only be portrait, and landscape or none.
1383 This is the document default. A document can have
1384 pages in landscape and some in portrait */
1385 if(gs->override_orientation) {
1386 /* If the orientation should be override...
1387 then gs->orientation has already the correct
1388 value (it was set when the widget was created */
1393 /* Otherwise, set the proper orientation for the doc */
1394 gs->real_orientation = gs->doc->orientation;
1397 ps_document_set_page_size(gs, -1, gs->current_page);
1400 gs->gs_status = _("Document loaded.");
1407 ps_document_next_page(PSDocument * gs)
1411 LOG ("Make ghostscript render next page")
1413 g_return_val_if_fail(gs != NULL, FALSE);
1414 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1416 if(gs->interpreter_pid == 0) { /* no interpreter active */
1420 if(gs->busy) { /* interpreter is busy */
1426 event.xclient.type = ClientMessage;
1427 event.xclient.display = gdk_display;
1428 event.xclient.window = gs->message_window;
1429 event.xclient.message_type = gdk_x11_atom_to_xatom(gs_class->next_atom);
1430 event.xclient.format = 32;
1432 gdk_error_trap_push();
1433 XSendEvent(gdk_display, gs->message_window, FALSE, 0, &event);
1435 gdk_error_trap_pop();
1441 ps_document_goto_page(PSDocument * gs, gint page)
1443 g_return_val_if_fail(gs != NULL, FALSE);
1444 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1446 LOG ("Go to page %d", page)
1448 if(!gs->gs_filename) {
1452 /* range checking... */
1456 if(gs->structured_doc && gs->doc) {
1458 LOG ("It's a structured document, let's send one page to gs")
1460 if(page >= gs->doc->numpages)
1461 page = gs->doc->numpages - 1;
1463 if(page == gs->current_page && !gs->changed)
1466 gs->current_page = page;
1468 if(gs->doc->pages[page].orientation != NONE &&
1469 !gs->override_orientation &&
1470 gs->doc->pages[page].orientation != gs->real_orientation) {
1471 gs->real_orientation = gs->doc->pages[page].orientation;
1475 ps_document_set_page_size(gs, -1, page);
1477 gs->changed = FALSE;
1479 if(is_interpreter_ready(gs)) {
1480 ps_document_next_page(gs);
1483 ps_document_enable_interpreter(gs);
1484 send_ps(gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
1485 send_ps(gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
1488 send_ps(gs, gs->doc->pages[gs->current_page].begin,
1489 gs->doc->pages[gs->current_page].len, FALSE);
1492 /* Unstructured document */
1493 /* In the case of non structured documents,
1494 GS read the PS from the actual file (via command
1495 line. Hence, ggv only send a signal next page.
1496 If ghostview is not running it is usually because
1497 the last page of the file was displayed. In that
1498 case, ggv restarts GS again and the first page is displayed.
1501 LOG ("It's an unstructured document, gs will just read the file")
1503 if(page == gs->current_page && !gs->changed)
1506 ps_document_set_page_size(gs, -1, page);
1508 if(!is_interpreter_ready(gs))
1509 ps_document_enable_interpreter(gs);
1511 gs->current_page = page;
1513 ps_document_next_page(gs);
1519 * set pagesize sets the size from
1520 * if new_pagesize is -1, then it is set to either
1521 * a) the default settings of pageid, if they exist, or if pageid != -1.
1522 * b) the default setting of the document, if it exists.
1523 * c) the default setting of the widget.
1524 * otherwise, the new_pagesize is used as the pagesize
1527 ps_document_set_page_size(PSDocument * gs, gint new_pagesize, gint pageid)
1533 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
1535 LOG ("Set the page size")
1537 g_return_val_if_fail(gs != NULL, FALSE);
1538 g_return_val_if_fail(GTK_IS_GS(gs), FALSE);
1540 if(new_pagesize == -1) {
1541 if(gs->default_size > 0)
1542 new_pagesize = gs->default_size;
1543 if(!gs->override_size && gs->doc) {
1544 /* If we have a document:
1545 We use -- the page size (if specified)
1546 or the doc. size (if specified)
1547 or the page bbox (if specified)
1550 if((pageid >= 0) && (gs->doc->numpages > pageid) &&
1551 (gs->doc->pages) && (gs->doc->pages[pageid].size)) {
1552 new_pagesize = gs->doc->pages[pageid].size - gs->doc->size;
1554 else if(gs->doc->default_page_size != NULL) {
1555 new_pagesize = gs->doc->default_page_size - gs->doc->size;
1557 else if((pageid >= 0) &&
1558 (gs->doc->numpages > pageid) &&
1560 (gs->doc->pages[pageid].boundingbox[URX] >
1561 gs->doc->pages[pageid].boundingbox[LLX]) &&
1562 (gs->doc->pages[pageid].boundingbox[URY] >
1563 gs->doc->pages[pageid].boundingbox[LLY])) {
1566 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1567 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1573 /* Compute bounding box */
1574 if(gs->doc && ((gs->doc->epsf && !gs->override_size) || new_pagesize == -1)) { /* epsf or bbox */
1577 (gs->doc->pages[pageid].boundingbox[URX] >
1578 gs->doc->pages[pageid].boundingbox[LLX])
1579 && (gs->doc->pages[pageid].boundingbox[URY] >
1580 gs->doc->pages[pageid].boundingbox[LLY])) {
1582 new_llx = gs->doc->pages[pageid].boundingbox[LLX];
1583 new_lly = gs->doc->pages[pageid].boundingbox[LLY];
1584 new_urx = gs->doc->pages[pageid].boundingbox[URX];
1585 new_ury = gs->doc->pages[pageid].boundingbox[URY];
1587 else if((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
1588 (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
1590 new_llx = gs->doc->boundingbox[LLX];
1591 new_lly = gs->doc->boundingbox[LLY];
1592 new_urx = gs->doc->boundingbox[URX];
1593 new_ury = gs->doc->boundingbox[URY];
1597 if(new_pagesize < 0)
1598 new_pagesize = gs->default_size;
1599 new_llx = new_lly = 0;
1600 if(gs->doc && !gs->override_size && gs->doc->size &&
1601 (new_pagesize < gs->doc->numsizes)) {
1602 new_urx = gs->doc->size[new_pagesize].width;
1603 new_ury = gs->doc->size[new_pagesize].height;
1606 new_urx = papersizes[new_pagesize].width;
1607 new_ury = papersizes[new_pagesize].height;
1611 if(new_urx <= new_llx)
1612 new_urx = papersizes[12].width;
1613 if(new_ury <= new_lly)
1614 new_ury = papersizes[12].height;
1616 /* If bounding box changed, setup for new size. */
1617 /* ps_document_disable_interpreter (gs); */
1618 if((new_llx != gs->llx) || (new_lly != gs->lly) ||
1619 (new_urx != gs->urx) || (new_ury != gs->ury)) {
1636 ps_document_zoom_to_fit(PSDocument * gs, gboolean fit_width)
1640 guint avail_w, avail_h;
1642 g_return_val_if_fail(gs != NULL, 0.0);
1643 g_return_val_if_fail(GTK_IS_GS(gs), 0.0);
1645 avail_w = (gs->avail_w > 0) ? gs->avail_w : gs->width;
1646 avail_h = (gs->avail_h > 0) ? gs->avail_h : gs->height;
1648 new_zoom = ((gfloat) avail_w) / ((gfloat) gs->width) * gs->zoom_factor;
1650 new_y = new_zoom * ((gfloat) gs->height) / gs->zoom_factor;
1652 new_zoom = ((gfloat) avail_h) / ((gfloat) gs->height) * gs->zoom_factor;
1659 ps_document_set_zoom(PSDocument * gs, gfloat zoom)
1661 g_return_if_fail(gs != NULL);
1662 g_return_if_fail(GTK_IS_GS(gs));
1664 switch (gs->zoom_mode) {
1665 case GTK_GS_ZOOM_FIT_WIDTH:
1666 zoom = ps_document_zoom_to_fit(gs, TRUE);
1668 case GTK_GS_ZOOM_FIT_PAGE:
1669 zoom = ps_document_zoom_to_fit(gs, FALSE);
1671 case GTK_GS_ZOOM_ABSOLUTE:
1676 if(fabs(gs->zoom_factor - zoom) > 0.001) {
1677 gs->zoom_factor = zoom;
1682 ps_document_goto_page(gs, gs->current_page);
1686 ps_document_load (EvDocument *document,
1693 filename = g_filename_from_uri (uri, NULL, error);
1697 result = document_load (PS_DOCUMENT (document), filename);
1705 ps_document_save (EvDocument *document,
1709 g_warning ("ps_document_save not implemented"); /* FIXME */
1714 ps_document_get_n_pages (EvDocument *document)
1716 PSDocument *ps = PS_DOCUMENT (document);
1718 g_return_val_if_fail (ps != NULL, -1);
1720 if (!ps->gs_filename || !ps->doc) {
1724 return ps->structured_doc ? ps->doc->numpages : 1;
1728 ps_document_set_page (EvDocument *document,
1731 ps_document_goto_page (PS_DOCUMENT (document), page);
1735 ps_document_get_page (EvDocument *document)
1737 PSDocument *ps = PS_DOCUMENT (document);
1739 g_return_val_if_fail (ps != NULL, -1);
1741 return ps->current_page;
1745 ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
1747 PSDocument *gs = (PSDocument *) data;
1749 if(event->type != GDK_CLIENT_EVENT)
1752 gs->message_window = event->client.data.l[0];
1754 if (event->client.message_type == gs_class->page_atom) {
1755 LOG ("GS rendered the document")
1757 ev_document_changed (EV_DOCUMENT (gs));
1764 ps_document_set_target (EvDocument *document,
1765 GdkDrawable *target)
1767 PSDocument *gs = PS_DOCUMENT (document);
1771 gs->pstarget = target;
1774 gdk_window_get_user_data (gs->pstarget, &data);
1775 g_return_if_fail (GTK_IS_WIDGET (data));
1777 widget = GTK_WIDGET (data);
1778 g_signal_connect (widget, "event",
1779 G_CALLBACK (ps_document_widget_event),
1783 ps_document_goto_page (gs, gs->current_page);
1787 ps_document_set_scale (EvDocument *document,
1790 ps_document_set_zoom (PS_DOCUMENT (document), scale);
1794 ps_document_set_page_offset (EvDocument *document,
1798 PSDocument *gs = PS_DOCUMENT (document);
1800 gs->page_x_offset = x;
1801 gs->page_y_offset = y;
1805 ps_document_get_page_size (EvDocument *document,
1810 /* Post script documents never vary in size */
1812 PSDocument *gs = PS_DOCUMENT (document);
1819 *height = gs->height;
1824 ps_document_render (EvDocument *document,
1830 PSDocument *gs = PS_DOCUMENT (document);
1835 if (gs->pstarget == NULL ||
1836 gs->bpixmap == NULL) {
1840 page.x = gs->page_x_offset;
1841 page.y = gs->page_y_offset;
1842 page.width = gs->width;
1843 page.height = gs->height;
1847 draw.width = clip_width;
1848 draw.height = clip_height;
1850 gc = gdk_gc_new (gs->pstarget);
1852 gdk_draw_drawable (gs->pstarget, gc,
1854 draw.x - page.x, draw.y - page.y,
1856 draw.width, draw.height);
1858 LOG ("Copy the internal pixmap: %d %d %d %d %d %d",
1859 draw.x - page.x, draw.y - page.y,
1860 draw.x, draw.y, draw.width, draw.height)
1862 g_object_unref (gc);
1866 ps_document_get_text (EvDocument *document, GdkRectangle *rect)
1868 g_warning ("ps_document_get_text not implemented"); /* FIXME ? */
1873 ps_document_get_link (EvDocument *document,
1881 ps_document_document_iface_init (EvDocumentIface *iface)
1883 iface->load = ps_document_load;
1884 iface->save = ps_document_save;
1885 iface->get_text = ps_document_get_text;
1886 iface->get_link = ps_document_get_link;
1887 iface->get_n_pages = ps_document_get_n_pages;
1888 iface->set_page = ps_document_set_page;
1889 iface->get_page = ps_document_get_page;
1890 iface->set_scale = ps_document_set_scale;
1891 iface->set_target = ps_document_set_target;
1892 iface->set_page_offset = ps_document_set_page_offset;
1893 iface->get_page_size = ps_document_get_page_size;
1894 iface->render = ps_document_render;