Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/backend/ps
diff options
context:
space:
mode:
Diffstat (limited to 'backend/ps')
-rw-r--r--backend/ps/Makefile.am21
-rw-r--r--backend/ps/gsdefaults.c67
-rw-r--r--backend/ps/gsdefaults.h39
-rw-r--r--backend/ps/gsio.c172
-rw-r--r--backend/ps/gsio.h42
-rw-r--r--backend/ps/gstypes.h48
-rw-r--r--backend/ps/ps-document.c1339
-rw-r--r--backend/ps/ps-document.h95
-rw-r--r--backend/ps/ps.c1872
-rw-r--r--backend/ps/ps.h107
10 files changed, 3802 insertions, 0 deletions
diff --git a/backend/ps/Makefile.am b/backend/ps/Makefile.am
new file mode 100644
index 0000000..499eee3
--- /dev/null
+++ b/backend/ps/Makefile.am
@@ -0,0 +1,21 @@
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/libdocument \
+ $(BACKEND_CFLAGS) \
+ $(WARN_CFLAGS)
+
+# $(DISABLE_DEPRECATED)
+
+noinst_LTLIBRARIES = libpsdocument.la
+
+libpsdocument_la_SOURCES = \
+ gsio.c \
+ gsio.h \
+ gstypes.h \
+ ps.c \
+ ps.h \
+ ps-document.c \
+ ps-document.h \
+ gsdefaults.c \
+ gsdefaults.h
+
diff --git a/backend/ps/gsdefaults.c b/backend/ps/gsdefaults.c
new file mode 100644
index 0000000..2263fca
--- /dev/null
+++ b/backend/ps/gsdefaults.c
@@ -0,0 +1,67 @@
+/*
+ * gsdefaults.c: default settings for the GtkGS widget
+ *
+ * Copyright 2002 - 2005 the Free Software Foundation
+ *
+ * Author: Jaka Mocnik <jaka@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+
+#include "gsdefaults.h"
+
+GtkGSPaperSize gtk_gs_paper_sizes[] = {
+ {N_("BBox"), 0, 0},
+ {N_("Letter"), 612, 792,},
+ {N_("Tabloid"), 792, 1224,},
+ {N_("Ledger"), 1224, 792,},
+ {N_("Legal"), 612, 1008,},
+ {N_("Statement"), 396, 612,},
+ {N_("Executive"), 540, 720,},
+ {N_("A0"), 2380, 3368,},
+ {N_("A1"), 1684, 2380,},
+ {N_("A2"), 1190, 1684,},
+ {N_("A3"), 842, 1190,},
+ {N_("A4"), 595, 842,},
+ {N_("A5"), 420, 595,},
+ {N_("B4"), 729, 1032,},
+ {N_("B5"), 516, 729,},
+ {N_("Folio"), 612, 936,},
+ {N_("Quarto"), 610, 780,},
+ {N_("10x14"), 720, 1008,},
+ {NULL, 0, 0}
+};
+
+GtkGSPaperSize *
+gtk_gs_defaults_get_paper_sizes()
+{
+ return gtk_gs_paper_sizes;
+}
+
+const gchar *
+gtk_gs_defaults_get_ungzip_cmd (void)
+{
+ return "gzip -cd";
+}
+
+const gchar *
+gtk_gs_defaults_get_unbzip2_cmd (void)
+{
+ return "bzip2 -cd";
+}
diff --git a/backend/ps/gsdefaults.h b/backend/ps/gsdefaults.h
new file mode 100644
index 0000000..2f50f6f
--- /dev/null
+++ b/backend/ps/gsdefaults.h
@@ -0,0 +1,39 @@
+/*
+ * gsdefaults.h: default settings of a GtkGS widget.
+ *
+ * Copyright 2002 - 2005 the Free Software Foundation
+ *
+ * Author: Jaka Mocnik <jaka@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef __GS_DEFAULTS_H__
+#define __GS_DEFAULTS_H__
+
+#include <glib.h>
+
+#include "gstypes.h"
+
+G_BEGIN_DECLS
+
+/* defaults accessors */
+
+GtkGSPaperSize *gtk_gs_defaults_get_paper_sizes(void);
+const gchar *gtk_gs_defaults_get_ungzip_cmd(void);
+const gchar *gtk_gs_defaults_get_unbzip2_cmd(void);
+
+G_END_DECLS
+
+#endif /* __GS_DEFAULTS_H__ */
diff --git a/backend/ps/gsio.c b/backend/ps/gsio.c
new file mode 100644
index 0000000..15c7f38
--- /dev/null
+++ b/backend/ps/gsio.c
@@ -0,0 +1,172 @@
+/*
+ * gsio.c: an IO abstraction
+ *
+ * Copyright 2002 - 2005 the Free Software Foundation
+ *
+ * Author: Jaka Mocnik <jaka@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+
+#include <gsio.h>
+
+#define CHUNK_SIZE 32768
+
+typedef struct _GtkGSDocChunk GtkGSDocChunk;
+struct _GtkGSDocChunk {
+ gchar *buf, *ptr;
+ guint len, max_len;
+};
+
+struct _GtkGSDocSink {
+ GSList *chunks;
+ GtkGSDocChunk *tail;
+};
+
+static GtkGSDocChunk *
+gtk_gs_doc_chunk_new(guint size)
+{
+ GtkGSDocChunk *c;
+
+ c = g_new0(GtkGSDocChunk, 1);
+ if((c->buf = g_malloc(sizeof(gchar) * size)) == NULL) {
+ g_free(c);
+ return NULL;
+ }
+ c->ptr = c->buf;
+ *c->ptr = '\0';
+ c->max_len = size;
+ c->len = 0;
+ return c;
+}
+
+static void
+gtk_gs_doc_chunk_free(GtkGSDocChunk * c)
+{
+ if(c->buf)
+ g_free(c->buf);
+ g_free(c);
+}
+
+GtkGSDocSink *
+gtk_gs_doc_sink_new()
+{
+ GtkGSDocSink *sink;
+
+ sink = g_new0(GtkGSDocSink, 1);
+ return sink;
+}
+
+void
+gtk_gs_doc_sink_free(GtkGSDocSink * sink)
+{
+ GSList *node;
+
+ node = sink->chunks;
+ while(node) {
+ gtk_gs_doc_chunk_free((GtkGSDocChunk *) node->data);
+ node = node->next;
+ }
+ g_slist_free(sink->chunks);
+}
+
+void
+gtk_gs_doc_sink_write(GtkGSDocSink * sink, const gchar * buf, int len)
+{
+ gint real_len;
+
+ if(sink->tail == NULL) {
+ sink->tail = gtk_gs_doc_chunk_new(CHUNK_SIZE);
+ sink->chunks = g_slist_append(sink->chunks, sink->tail);
+ }
+
+ real_len = MIN(sink->tail->max_len - sink->tail->len, len);
+ if(real_len > 0) {
+ strncpy(sink->tail->ptr, buf, real_len);
+ sink->tail->ptr += real_len;
+ sink->tail->len += real_len;
+ }
+ len -= real_len;
+ if(len > 0) {
+ sink->tail = NULL;
+ gtk_gs_doc_sink_write(sink, buf + real_len, len);
+ }
+}
+
+void
+gtk_gs_doc_sink_printf_v(GtkGSDocSink * sink, const gchar * fmt, va_list ap)
+{
+ gint max_len, len;
+
+ if(sink->tail == NULL) {
+ sink->tail = gtk_gs_doc_chunk_new(CHUNK_SIZE);
+ sink->chunks = g_slist_append(sink->chunks, sink->tail);
+ }
+
+ max_len = sink->tail->max_len - sink->tail->len;
+ if(max_len > 0) {
+ len = g_vsnprintf(sink->tail->ptr, max_len, fmt, ap);
+ if(len >= max_len - 1) {
+ /* force printf in the next chunk later on */
+ max_len = 0;
+ sink->tail = NULL;
+ }
+ else {
+ sink->tail->ptr += len;
+ sink->tail->len += len;
+ }
+ }
+ if(max_len <= 0) {
+ gtk_gs_doc_sink_printf(sink, fmt, ap);
+ }
+}
+
+void
+gtk_gs_doc_sink_printf(GtkGSDocSink * sink, const gchar * fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ gtk_gs_doc_sink_printf_v(sink, fmt, ap);
+ va_end(ap);
+}
+
+gchar *
+gtk_gs_doc_sink_get_buffer(GtkGSDocSink * sink)
+{
+ guint total;
+ GSList *node;
+
+ for(total = 0, node = sink->chunks; node; node = node->next) {
+ total += ((GtkGSDocChunk *) node->data)->len;
+ }
+ if(total) {
+ gchar *buf = g_malloc(sizeof(gchar) * (total + 1)), *ptr;
+ if(!buf)
+ return NULL;
+ for(ptr = buf, node = sink->chunks; node; node = node->next) {
+ memcpy(ptr,
+ ((GtkGSDocChunk *) node->data)->buf,
+ ((GtkGSDocChunk *) node->data)->len);
+ ptr += ((GtkGSDocChunk *) node->data)->len;
+ }
+ buf[total] = '\0';
+ return buf;
+ }
+ else
+ return NULL;
+}
diff --git a/backend/ps/gsio.h b/backend/ps/gsio.h
new file mode 100644
index 0000000..3f83cb0
--- /dev/null
+++ b/backend/ps/gsio.h
@@ -0,0 +1,42 @@
+/*
+ * gsio.h: an IO abstraction
+ *
+ * Copyright 2002 - 2005 The Free Software Foundation
+ *
+ * Author: jaKa Mocnik <jaka@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GS_IO_H__
+#define __GS_IO_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GtkGSDocSink GtkGSDocSink;
+
+GtkGSDocSink *gtk_gs_doc_sink_new(void);
+void gtk_gs_doc_sink_free(GtkGSDocSink * sink);
+void gtk_gs_doc_sink_write(GtkGSDocSink * sink, const gchar * buf, int len);
+void gtk_gs_doc_sink_printf_v(GtkGSDocSink * sink, const gchar * fmt,
+ va_list ap);
+void gtk_gs_doc_sink_printf(GtkGSDocSink * sink, const gchar * fmt, ...);
+gchar *gtk_gs_doc_sink_get_buffer(GtkGSDocSink * sink);
+
+G_END_DECLS
+
+#endif /* __GS_IO_H__ */
diff --git a/backend/ps/gstypes.h b/backend/ps/gstypes.h
new file mode 100644
index 0000000..f1f5cb2
--- /dev/null
+++ b/backend/ps/gstypes.h
@@ -0,0 +1,48 @@
+/*
+ * Ghostscript widget for GTK/GNOME
+ *
+ * Copyright 1998 - 2005 The Free Software Foundation
+ *
+ * Authors: Jaka Mocnik, Federico Mena (Quartic), Szekeres Istvan (Pista)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GSTYPES_H__
+#define __GSTYPES_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GtkGSPaperSize GtkGSPaperSize;
+
+typedef enum {
+ GTK_GS_ORIENTATION_NONE = -1,
+ GTK_GS_ORIENTATION_PORTRAIT = 0,
+ GTK_GS_ORIENTATION_SEASCAPE = 3,
+ GTK_GS_ORIENTATION_UPSIDEDOWN = 2,
+ GTK_GS_ORIENTATION_LANDSCAPE = 1
+} GtkGSOrientation;
+
+struct _GtkGSPaperSize {
+ gchar *name;
+ gint width, height;
+};
+
+G_END_DECLS
+
+#endif /* __GSTYPES_H__ */
diff --git a/backend/ps/ps-document.c b/backend/ps/ps-document.c
new file mode 100644
index 0000000..ee5d449
--- /dev/null
+++ b/backend/ps/ps-document.c
@@ -0,0 +1,1339 @@
+/* Ghostscript widget for GTK/GNOME
+ *
+ * Copyright (C) 1998 - 2005 the Free Software Foundation
+ *
+ * Authors: Jonathan Blandford, Jaka Mocnik
+ *
+ * Based on code by: Federico Mena (Quartic), Szekeres Istvan (Pista)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkobject.h>
+#include <gdk/gdkprivate.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n.h>
+#include <X11/Intrinsic.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "ps-document.h"
+#include "gsdefaults.h"
+#include "ev-file-exporter.h"
+#include "ev-async-renderer.h"
+
+#define MAX_BUFSIZE 1024
+
+#define PS_DOCUMENT_IS_COMPRESSED(gs) (PS_DOCUMENT(gs)->gs_filename_unc != NULL)
+#define PS_DOCUMENT_GET_PS_FILE(gs) (PS_DOCUMENT_IS_COMPRESSED(gs) ? \
+ PS_DOCUMENT(gs)->gs_filename_unc : \
+ PS_DOCUMENT(gs)->gs_filename)
+
+/* structure to describe section of file to send to ghostscript */
+struct record_list
+{
+ FILE *fp;
+ long begin;
+ guint len;
+ gboolean seek_needed;
+ gboolean close;
+ struct record_list *next;
+};
+
+static gboolean broken_pipe = FALSE;
+
+/* Forward declarations */
+static void ps_document_init (PSDocument *gs);
+static void ps_document_class_init (PSDocumentClass *klass);
+static void send_ps (PSDocument *gs,
+ long begin,
+ unsigned int len,
+ gboolean close);
+static void output (gpointer data,
+ gint source,
+ GdkInputCondition condition);
+static void input (gpointer data,
+ gint source,
+ GdkInputCondition condition);
+static void stop_interpreter (PSDocument *gs);
+static gint start_interpreter (PSDocument *gs);
+static void ps_document_document_iface_init (EvDocumentIface *iface);
+static void ps_document_file_exporter_iface_init (EvFileExporterIface *iface);
+static void ps_async_renderer_iface_init (EvAsyncRendererIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (PSDocument, ps_document, G_TYPE_OBJECT,
+ {
+ G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
+ ps_document_document_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_FILE_EXPORTER,
+ ps_document_file_exporter_iface_init);
+ G_IMPLEMENT_INTERFACE (EV_TYPE_ASYNC_RENDERER,
+ ps_async_renderer_iface_init);
+ });
+
+static GObjectClass *parent_class = NULL;
+static PSDocumentClass *gs_class = NULL;
+
+static void
+ps_document_init (PSDocument *gs)
+{
+ gs->bpixmap = NULL;
+
+ gs->interpreter_pid = -1;
+
+ gs->busy = FALSE;
+ gs->gs_filename = 0;
+ gs->gs_filename_unc = 0;
+
+ broken_pipe = FALSE;
+
+ gs->structured_doc = FALSE;
+ gs->reading_from_pipe = FALSE;
+ gs->send_filename_to_gs = FALSE;
+
+ gs->doc = NULL;
+
+ gs->interpreter_input = -1;
+ gs->interpreter_output = -1;
+ gs->interpreter_err = -1;
+ gs->interpreter_input_id = 0;
+ gs->interpreter_output_id = 0;
+ gs->interpreter_error_id = 0;
+
+ gs->ps_input = NULL;
+ gs->input_buffer = NULL;
+ gs->input_buffer_ptr = NULL;
+ gs->bytes_left = 0;
+ gs->buffer_bytes_left = 0;
+
+ gs->gs_status = _("No document loaded.");
+
+ gs->ps_export_pagelist = NULL;
+ gs->ps_export_filename = NULL;
+}
+
+static void
+ps_document_dispose (GObject *object)
+{
+ PSDocument *gs = PS_DOCUMENT (object);
+
+ g_return_if_fail (gs != NULL);
+
+ if (gs->gs_psfile) {
+ fclose (gs->gs_psfile);
+ gs->gs_psfile = NULL;
+ }
+
+ if (gs->gs_filename) {
+ g_free (gs->gs_filename);
+ gs->gs_filename = NULL;
+ }
+
+ if (gs->doc) {
+ psfree (gs->doc);
+ gs->doc = NULL;
+ }
+
+ if (gs->gs_filename_unc) {
+ unlink(gs->gs_filename_unc);
+ g_free(gs->gs_filename_unc);
+ gs->gs_filename_unc = NULL;
+ }
+
+ if (gs->bpixmap) {
+ gdk_drawable_unref (gs->bpixmap);
+ }
+
+ if(gs->input_buffer) {
+ g_free(gs->input_buffer);
+ gs->input_buffer = NULL;
+ }
+
+ if (gs->target_window) {
+ gtk_widget_destroy (gs->target_window);
+ gs->target_window = NULL;
+ gs->pstarget = NULL;
+ }
+
+ stop_interpreter (gs);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+ps_document_class_init(PSDocumentClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) klass;
+ parent_class = g_type_class_peek_parent (klass);
+ gs_class = klass;
+
+ object_class->dispose = ps_document_dispose;
+
+ klass->gs_atom = gdk_atom_intern ("GHOSTVIEW", FALSE);
+ klass->next_atom = gdk_atom_intern ("NEXT", FALSE);
+ klass->page_atom = gdk_atom_intern ("PAGE", FALSE);
+ klass->string_atom = gdk_atom_intern ("STRING", FALSE);
+}
+
+static void
+push_pixbuf (PSDocument *gs)
+{
+ GdkColormap *cmap;
+ GdkPixbuf *pixbuf;
+ int width, height;
+
+ if (gs->pstarget == NULL)
+ return;
+
+ cmap = gdk_window_get_colormap (gs->pstarget);
+ gdk_drawable_get_size (gs->bpixmap, &width, &height);
+ pixbuf = gdk_pixbuf_get_from_drawable (NULL, gs->bpixmap, cmap,
+ 0, 0, 0, 0,
+ width, height);
+ g_signal_emit_by_name (gs, "render_finished", pixbuf);
+ g_object_unref (pixbuf);
+}
+
+static void
+interpreter_failed (PSDocument *gs, char *msg)
+{
+ push_pixbuf (gs);
+
+ stop_interpreter (gs);
+}
+
+static gboolean
+ps_document_widget_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ PSDocument *gs = (PSDocument *) data;
+
+ if(event->type != GDK_CLIENT_EVENT)
+ return FALSE;
+
+ gs->message_window = event->client.data.l[0];
+
+ if (event->client.message_type == gs_class->page_atom) {
+ gs->busy = FALSE;
+
+ push_pixbuf (gs);
+ }
+
+ return TRUE;
+}
+
+static void
+send_ps (PSDocument *gs, long begin, unsigned int len, gboolean close)
+{
+ struct record_list *ps_new;
+
+ if (gs->interpreter_input < 0) {
+ g_critical("No pipe to gs: error in send_ps().");
+ return;
+ }
+
+ ps_new = g_new0 (struct record_list, 1);
+ ps_new->fp = gs->gs_psfile;
+ ps_new->begin = begin;
+ ps_new->len = len;
+ ps_new->seek_needed = TRUE;
+ ps_new->close = close;
+ ps_new->next = NULL;
+
+ if (gs->input_buffer == NULL) {
+ gs->input_buffer = g_malloc(MAX_BUFSIZE);
+ }
+
+ if (gs->ps_input == NULL) {
+ gs->input_buffer_ptr = gs->input_buffer;
+ gs->bytes_left = len;
+ gs->buffer_bytes_left = 0;
+ gs->ps_input = ps_new;
+ gs->interpreter_input_id = gdk_input_add
+ (gs->interpreter_input, GDK_INPUT_WRITE, input, gs);
+ } else {
+ struct record_list *p = gs->ps_input;
+ while (p->next != NULL) {
+ p = p->next;
+ }
+ p->next = ps_new;
+ }
+}
+
+static void
+setup_pixmap (PSDocument *gs, int page, double scale, int rotation)
+{
+ GdkGC *fill;
+ GdkColor white = { 0, 0xFFFF, 0xFFFF, 0xFFFF }; /* pixel, r, g, b */
+ GdkColormap *colormap;
+ double width, height;
+ int pixmap_width, pixmap_height;
+
+ if (gs->pstarget == NULL)
+ return;
+
+ ev_document_get_page_size (EV_DOCUMENT (gs), page, &width, &height);
+
+ if (rotation == 90 || rotation == 270) {
+ pixmap_height = width * scale + 0.5;
+ pixmap_width = height * scale + 0.5;
+ } else {
+ pixmap_width = width * scale + 0.5;
+ pixmap_height = height * scale + 0.5;
+ }
+
+ if(gs->bpixmap) {
+ int w, h;
+
+ gdk_drawable_get_size (gs->bpixmap, &w, &h);
+
+ if (pixmap_width != w || h != pixmap_height) {
+ gdk_drawable_unref (gs->bpixmap);
+ gs->bpixmap = NULL;
+ stop_interpreter (gs);
+ }
+ }
+
+ if (!gs->bpixmap) {
+
+ fill = gdk_gc_new (gs->pstarget);
+ colormap = gdk_drawable_get_colormap (gs->pstarget);
+ gdk_color_alloc (colormap, &white);
+ gdk_gc_set_foreground (fill, &white);
+ gs->bpixmap = gdk_pixmap_new (gs->pstarget, pixmap_width,
+ pixmap_height, -1);
+ gdk_draw_rectangle (gs->bpixmap, fill, TRUE,
+ 0, 0, pixmap_width, pixmap_height);
+ }
+}
+
+#define DEFAULT_PAGE_SIZE 1
+
+static void
+get_page_box (PSDocument *gs, int page, int *urx, int *ury, int *llx, int *lly)
+{
+ gint new_llx = 0;
+ gint new_lly = 0;
+ gint new_urx = 0;
+ gint new_ury = 0;
+ GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes ();
+ int new_pagesize = -1;
+
+ g_return_if_fail (PS_IS_DOCUMENT (gs));
+
+ if (new_pagesize == -1) {
+ new_pagesize = DEFAULT_PAGE_SIZE;
+ if (gs->doc) {
+ /* If we have a document:
+ * We use -- the page size (if specified)
+ * or the doc. size (if specified)
+ * or the page bbox (if specified)
+ * or the bounding box
+ */
+ if ((page >= 0) && (gs->doc->numpages > page) &&
+ (gs->doc->pages) && (gs->doc->pages[page].size)) {
+ new_pagesize = gs->doc->pages[page].size - gs->doc->size;
+ } else if (gs->doc->default_page_size != NULL) {
+ new_pagesize = gs->doc->default_page_size - gs->doc->size;
+ } else if ((page >= 0) &&
+ (gs->doc->numpages > page) &&
+ (gs->doc->pages) &&
+ (gs->doc->pages[page].boundingbox[URX] >
+ gs->doc->pages[page].boundingbox[LLX]) &&
+ (gs->doc->pages[page].boundingbox[URY] >
+ gs->doc->pages[page].boundingbox[LLY])) {
+ new_pagesize = -1;
+ } else if ((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
+ (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
+ new_pagesize = -1;
+ }
+ }
+ }
+
+ /* Compute bounding box */
+ if (gs->doc && (gs->doc->epsf || new_pagesize == -1)) { /* epsf or bbox */
+ if ((page >= 0) &&
+ (gs->doc->pages) &&
+ (gs->doc->pages[page].boundingbox[URX] >
+ gs->doc->pages[page].boundingbox[LLX]) &&
+ (gs->doc->pages[page].boundingbox[URY] >
+ gs->doc->pages[page].boundingbox[LLY])) {
+ /* use page bbox */
+ new_llx = gs->doc->pages[page].boundingbox[LLX];
+ new_lly = gs->doc->pages[page].boundingbox[LLY];
+ new_urx = gs->doc->pages[page].boundingbox[URX];
+ new_ury = gs->doc->pages[page].boundingbox[URY];
+ } else if ((gs->doc->boundingbox[URX] > gs->doc->boundingbox[LLX]) &&
+ (gs->doc->boundingbox[URY] > gs->doc->boundingbox[LLY])) {
+ /* use doc bbox */
+ new_llx = gs->doc->boundingbox[LLX];
+ new_lly = gs->doc->boundingbox[LLY];
+ new_urx = gs->doc->boundingbox[URX];
+ new_ury = gs->doc->boundingbox[URY];
+ }
+ } else {
+ if (new_pagesize < 0)
+ new_pagesize = DEFAULT_PAGE_SIZE;
+ new_llx = new_lly = 0;
+ if (gs->doc && gs->doc->size &&
+ (new_pagesize < gs->doc->numsizes)) {
+ new_urx = gs->doc->size[new_pagesize].width;
+ new_ury = gs->doc->size[new_pagesize].height;
+ } else {
+ new_urx = papersizes[new_pagesize].width;
+ new_ury = papersizes[new_pagesize].height;
+ }
+ }
+
+ if (new_urx <= new_llx)
+ new_urx = papersizes[12].width;
+ if (new_ury <= new_lly)
+ new_ury = papersizes[12].height;
+
+ *urx = new_urx;
+ *ury = new_ury;
+ *llx = new_llx;
+ *lly = new_lly;
+}
+
+static void
+setup_page (PSDocument *gs, int page, double scale, int rotation)
+{
+ gchar *buf;
+ char scaled_dpi[G_ASCII_DTOSTR_BUF_SIZE];
+ int urx, ury, llx, lly;
+
+ get_page_box (gs, page, &urx, &ury, &llx, &lly);
+ g_ascii_dtostr (scaled_dpi, G_ASCII_DTOSTR_BUF_SIZE, 72.0 * scale);
+
+ buf = g_strdup_printf ("%ld %d %d %d %d %d %s %s %d %d %d %d",
+ 0L, rotation, llx, lly, urx, ury,
+ scaled_dpi, scaled_dpi,
+ 0, 0, 0, 0);
+
+ gdk_property_change (gs->pstarget, gs_class->gs_atom, gs_class->string_atom,
+ 8, GDK_PROP_MODE_REPLACE, (guchar *)buf, strlen(buf));
+ g_free (buf);
+
+ gdk_flush ();
+}
+
+static void
+close_pipe (int p[2])
+{
+ if (p[0] != -1) {
+ close (p[0]);
+ }
+ if (p[1] != -1) {
+ close (p[1]);
+ }
+}
+
+static gboolean
+is_interpreter_ready (PSDocument *gs)
+{
+ return (gs->interpreter_pid != -1 && !gs->busy && gs->ps_input == NULL);
+}
+
+static void
+output (gpointer data, gint source, GdkInputCondition condition)
+{
+ char buf[MAX_BUFSIZE + 1];
+ guint bytes = 0;
+ PSDocument *gs = PS_DOCUMENT(data);
+
+ if (source == gs->interpreter_output) {
+ bytes = read(gs->interpreter_output, buf, MAX_BUFSIZE);
+ if (bytes == 0) { /* EOF occurred */
+ close (gs->interpreter_output);
+ gs->interpreter_output = -1;
+ gdk_input_remove (gs->interpreter_output_id);
+ return;
+ } else if (bytes == -1) {
+ /* trouble... */
+ interpreter_failed (gs, NULL);
+ return;
+ }
+ if (gs->interpreter_err == -1) {
+ interpreter_failed (gs, NULL);
+ }
+ } else if (source == gs->interpreter_err) {
+ bytes = read (gs->interpreter_err, buf, MAX_BUFSIZE);
+ if (bytes == 0) { /* EOF occurred */
+ close (gs->interpreter_err);
+ gs->interpreter_err = -1;
+ gdk_input_remove (gs->interpreter_error_id);
+ return;
+ } else if (bytes == -1) {
+ /* trouble... */
+ interpreter_failed (gs, NULL);
+ return;
+ }
+ if (gs->interpreter_output == -1) {
+ interpreter_failed(gs, NULL);
+ }
+ }
+
+ if (bytes > 0) {
+ buf[bytes] = '\0';
+ printf ("%s", buf);
+ }
+}
+
+static void
+catchPipe (int i)
+{
+ broken_pipe = True;
+}
+
+static void
+input(gpointer data, gint source, GdkInputCondition condition)
+{
+ PSDocument *gs = PS_DOCUMENT(data);
+ int bytes_written;
+ void (*oldsig) (int);
+ oldsig = signal(SIGPIPE, catchPipe);
+
+ do {
+ if (gs->buffer_bytes_left == 0) {
+ /* Get a new section if required */
+ if (gs->ps_input && gs->bytes_left == 0) {
+ struct record_list *ps_old = gs->ps_input;
+ gs->ps_input = ps_old->next;
+ if (ps_old->close && NULL != ps_old->fp)
+ fclose (ps_old->fp);
+ g_free (ps_old);
+ }
+
+ /* Have to seek at the beginning of each section */
+ if (gs->ps_input && gs->ps_input->seek_needed) {
+ fseek (gs->ps_input->fp, gs->ps_input->begin, SEEK_SET);
+ gs->ps_input->seek_needed = FALSE;
+ gs->bytes_left = gs->ps_input->len;
+ }
+
+ if (gs->bytes_left > MAX_BUFSIZE) {
+ gs->buffer_bytes_left = fread (gs->input_buffer, sizeof(char),
+ MAX_BUFSIZE, gs->ps_input->fp);
+ } else if (gs->bytes_left > 0) {
+ gs->buffer_bytes_left = fread (gs->input_buffer, sizeof(char),
+ gs->bytes_left, gs->ps_input->fp);
+ } else {
+ gs->buffer_bytes_left = 0;
+ }
+ if (gs->bytes_left > 0 && gs->buffer_bytes_left == 0) {
+ interpreter_failed (gs, NULL); /* Error occurred */
+ }
+ gs->input_buffer_ptr = gs->input_buffer;
+ gs->bytes_left -= gs->buffer_bytes_left;
+ }
+
+ if (gs->buffer_bytes_left > 0) {
+ bytes_written = write (gs->interpreter_input,
+ gs->input_buffer_ptr, gs->buffer_bytes_left);
+
+ if (broken_pipe) {
+ interpreter_failed (gs, g_strdup(_("Broken pipe.")));
+ broken_pipe = FALSE;
+ interpreter_failed (gs, NULL);
+ } else if (bytes_written == -1) {
+ if ((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
+ interpreter_failed (gs, NULL); /* Something bad happened */
+ }
+ } else {
+ gs->buffer_bytes_left -= bytes_written;
+ gs->input_buffer_ptr += bytes_written;
+ }
+ }
+ } while (gs->ps_input && gs->buffer_bytes_left == 0);
+
+ signal (SIGPIPE, oldsig);
+
+ if (gs->ps_input == NULL && gs->buffer_bytes_left == 0) {
+ if (gs->interpreter_input_id != 0) {
+ gdk_input_remove (gs->interpreter_input_id);
+ gs->interpreter_input_id = 0;
+ }
+ }
+}
+
+static int
+start_interpreter (PSDocument *gs)
+{
+ int std_in[2] = { -1, -1 }; /* pipe to interp stdin */
+ int std_out[2]; /* pipe from interp stdout */
+ int std_err[2]; /* pipe from interp stderr */
+
+#define NUM_ARGS 100
+#define NUM_GS_ARGS (NUM_ARGS - 20)
+#define NUM_ALPHA_ARGS 10
+
+ char *argv[NUM_ARGS], *dir, *gv_env, *gs_path;
+ char **gs_args, **alpha_args = NULL;
+ char **gv_env_vars = NULL;
+ int argc = 0, i;
+
+ if(!gs->gs_filename)
+ return 0;
+
+ stop_interpreter(gs);
+
+ /* set up the args... */
+ gs_path = g_find_program_in_path ("gs");
+ gs_args = g_strsplit (gs_path, " ", NUM_GS_ARGS);
+ g_free (gs_path);
+ for(i = 0; i < NUM_GS_ARGS && gs_args[i]; i++, argc++) {
+ argv[argc] = gs_args[i];
+ }
+
+ alpha_args = g_strsplit (ALPHA_PARAMS, " ", NUM_ALPHA_ARGS);
+ for(i = 0; i < NUM_ALPHA_ARGS && alpha_args[i]; i++, argc++) {
+ argv[argc] = alpha_args[i];
+ }
+
+ argv[argc++] = "-dNOPAUSE";
+ argv[argc++] = "-dQUIET";
+ argv[argc++] = "-dSAFER";
+
+ /* set up the pipes */
+ if (gs->send_filename_to_gs) {
+ argv[argc++] = PS_DOCUMENT_GET_PS_FILE (gs);
+ argv[argc++] = "-c";
+ argv[argc++] = "quit";
+ } else {
+ argv[argc++] = "-";
+ }
+
+ argv[argc++] = NULL;
+
+ if (!gs->reading_from_pipe && !gs->send_filename_to_gs) {
+ if (pipe (std_in) == -1) {
+ g_critical ("Unable to open pipe to Ghostscript.");
+ return -1;
+ }
+ }
+
+ if (pipe (std_out) == -1) {
+ close_pipe (std_in);
+ return -1;
+ }
+
+ if (pipe(std_err) == -1) {
+ close_pipe (std_in);
+ close_pipe (std_out);
+ return -1;
+ }
+
+ gv_env = g_strdup_printf ("GHOSTVIEW=%ld %ld;DISPLAY=%s",
+ gdk_x11_drawable_get_xid (gs->pstarget),
+ gdk_x11_drawable_get_xid (gs->bpixmap),
+ gdk_display_get_name (gdk_drawable_get_display (gs->pstarget)));
+
+ gs->interpreter_pid = fork ();
+ switch (gs->interpreter_pid) {
+ case -1: /* error */
+ close_pipe (std_in);
+ close_pipe (std_out);
+ close_pipe (std_err);
+ return -2;
+ break;
+ case 0: /* child */
+ close (std_out[0]);
+ dup2 (std_out[1], 1);
+ close (std_out[1]);
+
+ close (std_err[0]);
+ dup2 (std_err[1], 2);
+ close (std_err[1]);
+
+ if (!gs->reading_from_pipe) {
+ if (gs->send_filename_to_gs) {
+ int stdinfd;
+ /* just in case gs tries to read from stdin */
+ stdinfd = open("/dev/null", O_RDONLY);
+ if (stdinfd != 0) {
+ dup2(stdinfd, 0);
+ close(stdinfd);
+ }
+ } else {
+ close (std_in[1]);
+ dup2 (std_in[0], 0);
+ close (std_in[0]);
+ }
+ }
+
+ gv_env_vars = g_strsplit (gv_env, ";", -1);
+ g_free (gv_env);
+ for (i = 0; gv_env_vars[i]; i++) {
+ putenv (gv_env_vars[i]);
+ }
+
+ /* change to directory where the input file is. This helps
+ * with postscript-files which include other files using
+ * a relative path */
+ dir = g_path_get_dirname (gs->gs_filename);
+ chdir (dir);
+ g_free (dir);
+
+ execvp (argv[0], argv);
+
+ /* Notify error */
+ g_critical ("Unable to execute [%s]\n", argv[0]);
+ g_strfreev (gs_args);
+ g_strfreev (alpha_args);
+ g_strfreev (gv_env_vars);
+ _exit (1);
+ break;
+ default: /* parent */
+ if (!gs->send_filename_to_gs && !gs->reading_from_pipe) {
+ int result;
+ close (std_in[0]);
+ /* use non-blocking IO for pipe to ghostscript */
+ result = fcntl (std_in[1], F_GETFL, 0);
+ fcntl (std_in[1], F_SETFL, result | O_NONBLOCK);
+ gs->interpreter_input = std_in[1];
+ } else {
+ gs->interpreter_input = -1;
+ }
+ close (std_out[1]);
+
+ gs->interpreter_output = std_out[0];
+ close (std_err[1]);
+ gs->interpreter_err = std_err[0];
+ gs->interpreter_output_id =
+ gdk_input_add (std_out[0], GDK_INPUT_READ, output, gs);
+ gs->interpreter_error_id =
+ gdk_input_add (std_err[0], GDK_INPUT_READ, output, gs);
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+stop_interpreter(PSDocument * gs)
+{
+ if (gs->interpreter_pid > 0) {
+ int status = 0;
+ kill (gs->interpreter_pid, SIGTERM);
+ while ((wait(&status) == -1) && (errno == EINTR));
+ gs->interpreter_pid = -1;
+ if (status == 1) {
+ gs->gs_status = _("Interpreter failed.");
+ }
+ }
+
+ if (gs->interpreter_input >= 0) {
+ close (gs->interpreter_input);
+ gs->interpreter_input = -1;
+ if (gs->interpreter_input_id != 0) {
+ gdk_input_remove(gs->interpreter_input_id);
+ gs->interpreter_input_id = 0;
+ }
+ while (gs->ps_input) {
+ struct record_list *ps_old = gs->ps_input;
+ gs->ps_input = gs->ps_input->next;
+ if (ps_old->close && NULL != ps_old->fp)
+ fclose (ps_old->fp);
+ g_free (ps_old);
+ }
+ }
+
+ if (gs->interpreter_output >= 0) {
+ close (gs->interpreter_output);
+ gs->interpreter_output = -1;
+ if (gs->interpreter_output_id) {
+ gdk_input_remove (gs->interpreter_output_id);
+ gs->interpreter_output_id = 0;
+ }
+ }
+
+ if (gs->interpreter_err >= 0) {
+ close (gs->interpreter_err);
+ gs->interpreter_err = -1;
+ if (gs->interpreter_error_id) {
+ gdk_input_remove (gs->interpreter_error_id);
+ gs->interpreter_error_id = 0;
+ }
+ }
+
+ gs->busy = FALSE;
+}
+
+/* If file exists and is a regular file then return its length, else -1 */
+static gint
+file_length (const gchar * filename)
+{
+ struct stat stat_rec;
+
+ if (filename && (stat (filename, &stat_rec) == 0) && S_ISREG (stat_rec.st_mode))
+ return stat_rec.st_size;
+ else
+ return -1;
+}
+
+/* Test if file exists, is a regular file and its length is > 0 */
+static gboolean
+file_readable(const char *filename)
+{
+ return (file_length (filename) > 0);
+}
+
+/*
+ * Decompress gs->gs_filename if necessary
+ * Set gs->filename_unc to the name of the uncompressed file or NULL.
+ * Error reporting via signal 'interpreter_message'
+ * Return name of input file to use or NULL on error..
+ */
+static gchar *
+check_filecompressed (PSDocument * gs)
+{
+ FILE *file;
+ gchar buf[1024];
+ gchar *filename, *filename_unc, *filename_err, *cmdline;
+ const gchar *cmd;
+ int fd;
+
+ cmd = NULL;
+
+ if ((file = fopen(gs->gs_filename, "r")) &&
+ (fread (buf, sizeof(gchar), 3, file) == 3)) {
+ if ((buf[0] == '\037') && ((buf[1] == '\235') || (buf[1] == '\213'))) {
+ /* file is gzipped or compressed */
+ cmd = gtk_gs_defaults_get_ungzip_cmd ();
+ } else if (strncmp (buf, "BZh", 3) == 0) {
+ /* file is compressed with bzip2 */
+ cmd = gtk_gs_defaults_get_unbzip2_cmd ();
+ }
+ }
+
+ if (NULL != file)
+ fclose(file);
+
+ if (!cmd)
+ return gs->gs_filename;
+
+ /* do the decompression */
+ filename = g_shell_quote (gs->gs_filename);
+ filename_unc = g_strconcat (g_get_tmp_dir (), "/evinceXXXXXX", NULL);
+ if ((fd = mkstemp (filename_unc)) < 0) {
+ g_free (filename_unc);
+ g_free (filename);
+ return NULL;
+ }
+ close (fd);
+
+ filename_err = g_strconcat (g_get_tmp_dir (), "/evinceXXXXXX", NULL);
+ if ((fd = mkstemp(filename_err)) < 0) {
+ g_free (filename_err);
+ g_free (filename_unc);
+ g_free (filename);
+ return NULL;
+ }
+ close (fd);
+
+ cmdline = g_strdup_printf ("%s %s >%s 2>%s", cmd,
+ filename, filename_unc, filename_err);
+ if (system (cmdline) == 0 &&
+ file_readable (filename_unc) &&
+ file_length (filename_err) == 0) {
+ /* sucessfully uncompressed file */
+ gs->gs_filename_unc = filename_unc;
+ } else {
+ gchar *filename_dsp;
+ gchar *msg;
+
+ /* report error */
+ filename_dsp = g_filename_display_name (gs->gs_filename);
+ msg = g_strdup_printf (_("Error while decompressing file “%s”:\n"), filename_dsp);
+ g_free (filename_dsp);
+
+ interpreter_failed (gs, msg);
+ g_free (msg);
+ unlink (filename_unc);
+ g_free (filename_unc);
+ filename_unc = NULL;
+ }
+
+ unlink (filename_err);
+ g_free (filename_err);
+ g_free (cmdline);
+ g_free (filename);
+
+ return filename_unc;
+}
+
+static gint
+ps_document_enable_interpreter(PSDocument *gs)
+{
+ g_return_val_if_fail (PS_IS_DOCUMENT (gs), FALSE);
+
+ if (!gs->gs_filename)
+ return 0;
+
+ return start_interpreter (gs);
+}
+
+static gboolean
+document_load (PSDocument *gs, const gchar *fname)
+{
+ g_return_val_if_fail (PS_IS_DOCUMENT(gs), FALSE);
+
+ if (fname == NULL) {
+ gs->gs_status = "";
+ return FALSE;
+ }
+
+ /* prepare this document */
+ gs->structured_doc = FALSE;
+ gs->send_filename_to_gs = TRUE;
+ gs->gs_filename = g_strdup (fname);
+
+ if ((gs->reading_from_pipe = (strcmp (fname, "-") == 0))) {
+ gs->send_filename_to_gs = FALSE;
+ } else {
+ /*
+ * We need to make sure that the file is loadable/exists!
+ * otherwise we want to exit without loading new stuff...
+ */
+ gchar *filename = NULL;
+
+ if (!file_readable(fname)) {
+ gchar *filename_dsp;
+ gchar *msg;
+
+ filename_dsp = g_filename_display_name (fname);
+ msg = g_strdup_printf (_("Cannot open file “%s”.\n"), filename_dsp);
+ g_free (filename_dsp);
+
+ interpreter_failed (gs, msg);
+ g_free (msg);
+ gs->gs_status = _("File is not readable.");
+ } else {
+ filename = check_filecompressed(gs);
+ }
+
+ if (!filename || (gs->gs_psfile = fopen(filename, "r")) == NULL) {
+ interpreter_failed (gs, NULL);
+ return FALSE;
+ }
+
+ /* we grab the vital statistics!!! */
+ gs->doc = psscan(gs->gs_psfile, TRUE, filename);
+
+ if ((!gs->doc->epsf && gs->doc->numpages > 0) ||
+ (gs->doc->epsf && gs->doc->numpages > 1)) {
+ gs->structured_doc = TRUE;
+ gs->send_filename_to_gs = FALSE;
+ }
+ }
+
+ gs->gs_status = _("Document loaded.");
+
+ return TRUE;
+}
+
+static gboolean
+ps_document_next_page (PSDocument *gs)
+{
+ XEvent event;
+ GdkScreen *screen;
+ GdkDisplay *display;
+ Display *dpy;
+
+ g_return_val_if_fail (PS_IS_DOCUMENT(gs), FALSE);
+ g_return_val_if_fail (gs->interpreter_pid != 0, FALSE);
+ g_return_val_if_fail (gs->busy != TRUE, FALSE);
+
+ gs->busy = TRUE;
+
+ screen = gtk_window_get_screen (GTK_WINDOW (gs->target_window));
+ display = gdk_screen_get_display (screen);
+ dpy = gdk_x11_display_get_xdisplay (display);
+
+ event.xclient.type = ClientMessage;
+ event.xclient.display = dpy;
+ event.xclient.window = gs->message_window;
+ event.xclient.message_type =
+ gdk_x11_atom_to_xatom_for_display (display,
+ gs_class->next_atom);
+ event.xclient.format = 32;
+
+ gdk_error_trap_push ();
+ XSendEvent (dpy, gs->message_window, FALSE, 0, &event);
+ gdk_flush ();
+ gdk_error_trap_pop ();
+
+ return TRUE;
+}
+
+static gboolean
+render_page (PSDocument *gs, int page)
+{
+ g_return_val_if_fail(gs != NULL, FALSE);
+ g_return_val_if_fail(PS_IS_DOCUMENT(gs), FALSE);
+
+ if(!gs->gs_filename) {
+ return FALSE;
+ }
+
+ if (gs->structured_doc && gs->doc) {
+
+ if (is_interpreter_ready (gs)) {
+ ps_document_next_page (gs);
+ } else {
+ ps_document_enable_interpreter (gs);
+ send_ps (gs, gs->doc->beginprolog, gs->doc->lenprolog, FALSE);
+ send_ps (gs, gs->doc->beginsetup, gs->doc->lensetup, FALSE);
+ }
+
+ send_ps (gs, gs->doc->pages[page].begin,
+ gs->doc->pages[page].len, FALSE);
+ } else {
+ /* Unstructured document
+ *
+ * In the case of non structured documents,
+ * GS read the PS from the actual file (via command
+ * line. Hence, ggv only send a signal next page.
+ * If ghostview is not running it is usually because
+ * the last page of the file was displayed. In that
+ * case, ggv restarts GS again and the first page is displayed.
+ */
+
+ if (!is_interpreter_ready (gs)) {
+ ps_document_enable_interpreter(gs);
+ }
+ ps_document_next_page(gs);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ps_document_load (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ char *filename;
+ char *gs_path;
+ gboolean result;
+
+ filename = g_filename_from_uri (uri, NULL, error);
+ if (!filename)
+ return FALSE;
+
+ gs_path = g_find_program_in_path ("gs");
+ if (!gs_path) {
+ gchar *filename_dsp;
+ filename_dsp = g_filename_display_name (filename);
+ g_set_error(error,
+ G_FILE_ERROR,
+ G_FILE_ERROR_NOENT,
+ _("Failed to load document “%s”. Ghostscript interpreter was not found in path"),
+ filename);
+ g_free (filename_dsp);
+ result = FALSE;
+ } else {
+ result = document_load (PS_DOCUMENT (document), filename);
+ if (!result) {
+ gchar *filename_dsp;
+ filename_dsp = g_filename_display_name (filename);
+
+ g_set_error (error, G_FILE_ERROR,
+ G_FILE_ERROR_FAILED,
+ _("Failed to load document “%s”"),
+ filename_dsp);
+ g_free (filename_dsp);
+ }
+ g_free (gs_path);
+ }
+ g_free (filename);
+
+ return result;
+}
+
+static gboolean
+save_document (PSDocument *document, const char *filename)
+{
+ gboolean result = TRUE;
+ GtkGSDocSink *sink = gtk_gs_doc_sink_new ();
+ FILE *f, *src_file;
+ gchar *buf;
+
+ src_file = fopen (PS_DOCUMENT_GET_PS_FILE(document), "r");
+ if (src_file) {
+ struct stat stat_rec;
+
+ if (stat (PS_DOCUMENT_GET_PS_FILE(document), &stat_rec) == 0) {
+ pscopy (src_file, sink, 0, stat_rec.st_size - 1);
+ }
+
+ fclose (src_file);
+ }
+
+ buf = gtk_gs_doc_sink_get_buffer (sink);
+ if (buf == NULL) {
+ return FALSE;
+ }
+
+ f = fopen (filename, "w");
+ if (f) {
+ fputs (buf, f);
+ fclose (f);
+ } else {
+ result = FALSE;
+ }
+
+ g_free (buf);
+ gtk_gs_doc_sink_free (sink);
+ g_free (sink);
+
+ return result;
+}
+
+static gboolean
+save_page_list (PSDocument *document, int *page_list, const char *filename)
+{
+ gboolean result = TRUE;
+ GtkGSDocSink *sink = gtk_gs_doc_sink_new ();
+ FILE *f;
+ gchar *buf;
+
+ pscopydoc (sink, PS_DOCUMENT_GET_PS_FILE(document),
+ document->doc, page_list);
+
+ buf = gtk_gs_doc_sink_get_buffer (sink);
+
+ f = fopen (filename, "w");
+ if (f) {
+ fputs (buf, f);
+ fclose (f);
+ } else {
+ result = FALSE;
+ }
+
+ g_free (buf);
+ gtk_gs_doc_sink_free (sink);
+ g_free (sink);
+
+ return result;
+}
+
+static gboolean
+ps_document_save (EvDocument *document,
+ const char *uri,
+ GError **error)
+{
+ PSDocument *ps = PS_DOCUMENT (document);
+ gboolean result;
+ char *filename;
+
+ filename = g_filename_from_uri (uri, NULL, error);
+ if (!filename)
+ return FALSE;
+
+ result = save_document (ps, filename);
+
+ g_free (filename);
+
+ return result;
+}
+
+static int
+ps_document_get_n_pages (EvDocument *document)
+{
+ PSDocument *ps = PS_DOCUMENT (document);
+
+ g_return_val_if_fail (ps != NULL, -1);
+
+ if (!ps->gs_filename || !ps->doc) {
+ return -1;
+ }
+
+ return ps->structured_doc ? ps->doc->numpages : 1;
+}
+
+static void
+ps_document_get_page_size (EvDocument *document,
+ int page,
+ double *width,
+ double *height)
+{
+ PSDocument *gs = PS_DOCUMENT (document);
+ int urx, ury, llx, lly;
+
+ get_page_box (gs, page, &urx, &ury, &llx, &lly);
+
+ if (width) {
+ *width = (urx - llx) + 0.5;
+ }
+
+ if (height) {
+ *height = (ury - lly) + 0.5;
+ }
+}
+
+static gboolean
+ps_document_can_get_text (EvDocument *document)
+{
+ return FALSE;
+}
+
+static void
+ps_async_renderer_render_pixbuf (EvAsyncRenderer *renderer, int page, double scale, int rotation)
+{
+ PSDocument *gs = PS_DOCUMENT (renderer);
+
+ if (gs->pstarget == NULL) {
+ gs->target_window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_widget_realize (gs->target_window);
+ gs->pstarget = gs->target_window->window;
+
+ g_assert (gs->pstarget != NULL);
+
+ g_signal_connect (gs->target_window, "event",
+ G_CALLBACK (ps_document_widget_event),
+ gs);
+ }
+
+ setup_pixmap (gs, page, scale, rotation);
+ setup_page (gs, page, scale, rotation);
+
+ render_page (gs, page);
+}
+
+static EvDocumentInfo *
+ps_document_get_info (EvDocument *document)
+{
+ EvDocumentInfo *info;
+ PSDocument *ps = PS_DOCUMENT (document);
+ int urx, ury, llx, lly;
+
+ info = g_new0 (EvDocumentInfo, 1);
+ info->fields_mask = EV_DOCUMENT_INFO_TITLE |
+ EV_DOCUMENT_INFO_FORMAT |
+ EV_DOCUMENT_INFO_CREATOR |
+ EV_DOCUMENT_INFO_N_PAGES |
+ EV_DOCUMENT_INFO_PAPER_SIZE;
+
+ info->title = g_strdup (ps->doc->title);
+ info->format = ps->doc->epsf ? g_strdup (_("Encapsulated PostScript"))
+ : g_strdup (_("PostScript"));
+ info->creator = g_strdup (ps->doc->creator);
+ info->n_pages = ev_document_get_n_pages (document);
+
+ get_page_box (PS_DOCUMENT (document), 0, &urx, &ury, &llx, &lly);
+
+ info->paper_width = (urx - llx) / 72.0f * 25.4f;
+ info->paper_height = (ury - lly) / 72.0f * 25.4f;
+
+ return info;
+}
+
+static void
+ps_document_document_iface_init (EvDocumentIface *iface)
+{
+ iface->load = ps_document_load;
+ iface->save = ps_document_save;
+ iface->can_get_text = ps_document_can_get_text;
+ iface->get_n_pages = ps_document_get_n_pages;
+ iface->get_page_size = ps_document_get_page_size;
+ iface->get_info = ps_document_get_info;
+}
+
+static void
+ps_async_renderer_iface_init (EvAsyncRendererIface *iface)
+{
+ iface->render_pixbuf = ps_async_renderer_render_pixbuf;
+}
+
+static gboolean
+ps_document_file_exporter_format_supported (EvFileExporter *exporter,
+ EvFileExporterFormat format)
+{
+ return (format == EV_FILE_FORMAT_PS);
+}
+
+static void
+ps_document_file_exporter_begin (EvFileExporter *exporter,
+ EvFileExporterFormat format,
+ const char *filename,
+ int first_page,
+ int last_page,
+ double width,
+ double height,
+ gboolean duplex)
+{
+ PSDocument *document = PS_DOCUMENT (exporter);
+
+ if (document->structured_doc) {
+ g_free (document->ps_export_pagelist);
+
+ document->ps_export_pagelist = g_new0 (int, document->doc->numpages);
+ }
+
+ document->ps_export_filename = g_strdup (filename);
+}
+
+static void
+ps_document_file_exporter_do_page (EvFileExporter *exporter, EvRenderContext *rc)
+{
+ PSDocument *document = PS_DOCUMENT (exporter);
+
+ if (document->structured_doc) {
+ document->ps_export_pagelist[rc->page] = 1;
+ }
+}
+
+static void
+ps_document_file_exporter_end (EvFileExporter *exporter)
+{
+ PSDocument *document = PS_DOCUMENT (exporter);
+
+ if (!document->structured_doc) {
+ save_document (document, document->ps_export_filename);
+ } else {
+ save_page_list (document, document->ps_export_pagelist,
+ document->ps_export_filename);
+ g_free (document->ps_export_pagelist);
+ g_free (document->ps_export_filename);
+ document->ps_export_pagelist = NULL;
+ document->ps_export_filename = NULL;
+ }
+}
+
+static void
+ps_document_file_exporter_iface_init (EvFileExporterIface *iface)
+{
+ iface->format_supported = ps_document_file_exporter_format_supported;
+ iface->begin = ps_document_file_exporter_begin;
+ iface->do_page = ps_document_file_exporter_do_page;
+ iface->end = ps_document_file_exporter_end;
+}
diff --git a/backend/ps/ps-document.h b/backend/ps/ps-document.h
new file mode 100644
index 0000000..c8d19db
--- /dev/null
+++ b/backend/ps/ps-document.h
@@ -0,0 +1,95 @@
+/*
+ * Ghostscript widget for GTK/GNOME
+ *
+ * Copyright 1998 - 2005 The Free Software Foundation
+ *
+ * Authors: Jaka Mocnik, Federico Mena (Quartic), Szekeres Istvan (Pista)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PS_DOCUMENT_H__
+#define __PS_DOCUMENT_H__
+
+#include <sys/types.h>
+#include <gtk/gtkwidget.h>
+
+#include "ev-document.h"
+#include "ps.h"
+#include "gstypes.h"
+
+G_BEGIN_DECLS
+
+#define PS_TYPE_DOCUMENT (ps_document_get_type())
+#define PS_DOCUMENT(obj) GTK_CHECK_CAST (obj, ps_document_get_type (), PSDocument)
+#define PS_DOCUMENT_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, ps_document_get_type (), PSDocumentClass)
+#define PS_IS_DOCUMENT(obj) GTK_CHECK_TYPE (obj, ps_document_get_type())
+
+typedef struct _PSDocument PSDocument;
+typedef struct _PSDocumentClass PSDocumentClass;
+
+struct _PSDocument {
+ GObject object;
+
+ GtkWidget *target_window;
+ GdkWindow *pstarget;
+ GdkPixmap *bpixmap;
+ long message_window; /* Used by ghostview to receive messages from app */
+
+ pid_t interpreter_pid; /* PID of interpreter, -1 if none */
+ int interpreter_input; /* stdin of interpreter */
+ int interpreter_output; /* stdout of interpreter */
+ int interpreter_err; /* stderr of interpreter */
+ guint interpreter_input_id;
+ guint interpreter_output_id;
+ guint interpreter_error_id;
+
+ gboolean busy; /* Is gs busy drawing? */
+ gboolean structured_doc;
+
+ struct record_list *ps_input;
+ gchar *input_buffer_ptr;
+ guint bytes_left;
+ guint buffer_bytes_left;
+
+ FILE *gs_psfile; /* the currently loaded FILE */
+ gchar *gs_filename; /* the currently loaded filename */
+ gchar *gs_filename_unc; /* Uncompressed file */
+ gchar *input_buffer;
+ gboolean send_filename_to_gs; /* True if gs should read from file directly */
+ gboolean reading_from_pipe; /* True if ggv is reading input from pipe */
+ struct document *doc;
+
+ int *ps_export_pagelist;
+ char *ps_export_filename;
+
+ const gchar *gs_status; /* PSDocument status */
+};
+
+struct _PSDocumentClass {
+ GObjectClass parent_class;
+
+ GdkAtom gs_atom;
+ GdkAtom next_atom;
+ GdkAtom page_atom;
+ GdkAtom string_atom;
+};
+
+GType ps_document_get_type(void);
+
+G_END_DECLS
+
+#endif /* __PS_DOCUMENT_H__ */
diff --git a/backend/ps/ps.c b/backend/ps/ps.c
new file mode 100644
index 0000000..b4b54e0
--- /dev/null
+++ b/backend/ps/ps.c
@@ -0,0 +1,1872 @@
+/*
+ * ps.c -- Postscript scanning and copying routines.
+ * Copyright (C) 1992, 1998 Timothy O. Theisen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Tim Theisen Systems Programmer
+ * Internet: tim@cs.wisc.edu Department of Computer Sciences
+ * UUCP: uwvax!tim University of Wisconsin-Madison
+ * Phone: (608)262-0438 1210 West Dayton Street
+ * FAX: (608)262-9777 Madison, WI 53706
+ */
+
+/* 18/3/98 Jake Hamby patch */
+
+/*
+ * 98/03/17: Jake Hamby (jehamby@lightside.com):
+ * Added support for compressed/gzipped Postscript and PDF files.
+ * Compressed files are gunzipped to a temporary file, and PDF files are
+ * scanned by calling Ghostscript to generate a fake DSC file.
+ * This is based on code from GV 3.5.8, which is available at:
+ * http://wwwthep.physik.uni-mainz.de/~plass/gv/
+ */
+
+/* GV by Johannes Plass
+ * Department of Physics
+ * Johannes Gutenberg University
+ * Mainz, Germany
+ *
+ * <plass@thep.physik.uni-mainz.de>
+ */
+
+/* end of patch */
+
+#include <stdlib.h>
+#include <stdio.h>
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+#ifndef BUFSIZ
+# define BUFSIZ 1024
+#endif
+#include <ctype.h>
+#include <X11/Xos.h> /* #includes the appropriate <string.h> */
+#include "gstypes.h"
+#include "gsdefaults.h"
+#include "ps.h"
+#include "gsio.h"
+
+#include <glib.h>
+
+/* length calculates string length at compile time */
+/* can only be used with character constants */
+#define length(a) (sizeof(a)-1)
+#define iscomment(a, b) (strncmp(a, b, length(b)) == 0)
+#define DSCcomment(a) (a[0] == '%' && a[1] == '%')
+
+ /* list of standard paper sizes from Adobe's PPD. */
+
+/*--------------------------------------------------*/
+/* Declarations for ps_io_*() routines. */
+
+typedef struct FileDataStruct_ *FileData;
+
+typedef struct FileDataStruct_ {
+ FILE *file; /* file */
+ int file_desc; /* file descriptor corresponding to file */
+ int filepos; /* file position corresponding to the start of the line */
+ char *buf; /* buffer */
+ int buf_size; /* size of buffer */
+ int buf_end; /* last char in buffer given as offset to buf */
+ int line_begin; /* start of the line given as offset to buf */
+ int line_end; /* end of the line given as offset to buf */
+ int line_len; /* length of line, i.e. (line_end-line_begin) */
+ char line_termchar; /* char exchanged for a '\0' at end of line */
+ int status; /* 0 = okay, 1 = failed */
+} FileDataStruct;
+
+static FileData ps_io_init (FILE *file);
+static void ps_io_exit (FileData data);
+static char *ps_io_fgetchars (FileData data, int offset);
+
+static char *skipped_line = "% ps_io_fgetchars: skipped line";
+static char *empty_string = "";
+
+static char *readline (FileData fd, char **lineP, long *positionP, unsigned int *line_lenP);
+static char *gettextline(char *line);
+static char *get_next_text(char *line, char **next_char);
+static int blank(char *line);
+
+static struct page *
+pages_new(struct page *pages, int current, int maxpages)
+{
+ struct page *oldpages = pages;
+ if(!oldpages)
+ pages = g_new0(struct page, maxpages);
+ else
+ pages = g_renew(struct page, oldpages, maxpages);
+ for(; current < maxpages; current++) {
+ memset(&(pages[current]), 0x00, sizeof(struct page));
+ pages[current].orientation = GTK_GS_ORIENTATION_NONE;
+ }
+ return pages;
+}
+
+/*
+ * psscan -- scan the PostScript file for document structuring comments.
+ *
+ * This scanner is designed to retrieve the information necessary for
+ * the ghostview previewer. It will scan files that conform to any
+ * version (1.0, 2.0, 2.1, or 3.0) of the document structuring conventions.
+ * It does not really care which version of comments the file contains.
+ * (The comments are largely upward compatible.) It will scan a number
+ * of non-conforming documents. (You could have part of the document
+ * conform to V2.0 and the rest conform to V3.0. It would be similar
+ * to the DC-2 1/2+, it would look funny but it can still fly.)
+ *
+ * This routine returns a pointer to the document structure.
+ * The structure contains the information relevant to previewing.
+ * These include EPSF flag (to tell if the file is a encapsulated figure),
+ * Page Size (for the Page Size), Bounding Box (to minimize backing
+ * pixmap size or determine window size for encapsulated PostScript),
+ * Orientation of Paper (for default transformation matrix), and
+ * Page Order. The Title, Creator, and CreationDate are also retrieved to
+ * help identify the document.
+ *
+ * The following comments are examined:
+ *
+ * Header section:
+ * Must start with %!PS-Adobe-. Version numbers ignored.
+ * Also allowed to be just %!PS, many files seem to have that.
+ *
+ * %!PS-Adobe-* [EPSF-*]
+ * %%BoundingBox: <int> <int> <int> <int>|(atend)
+ * %%Creator: <textline>
+ * %%CreationDate: <textline>
+ * %%Orientation: Portrait|Landscape|(atend)
+ * %%Pages: <uint> [<int>]|(atend)
+ * %%PageOrder: Ascend|Descend|Special|(atend)
+ * %%Title: <textline>
+ * %%DocumentMedia: <text> <real> <real> <real> <text> <text>
+ * %%DocumentPaperSizes: <text>
+ * %%EndComments
+ *
+ * Note: Either the 3.0 or 2.0 syntax for %%Pages is accepted.
+ * Also either the 2.0 %%DocumentPaperSizes or the 3.0
+ * %%DocumentMedia comments are accepted as well.
+ *
+ * The header section ends either explicitly with %%EndComments or
+ * implicitly with any line that does not begin with %X where X is
+ * a not whitespace character.
+ *
+ * If the file is encapsulated PostScript the optional Preview section
+ * is next:
+ *
+ * %%BeginPreview
+ * %%EndPreview
+ *
+ * This section explicitly begins and ends with the above comments.
+ *
+ * Next the Defaults section for version 3 page defaults:
+ *
+ * %%BeginDefaults
+ * %%PageBoundingBox: <int> <int> <int> <int>
+ * %%PageOrientation: Portrait|Landscape
+ * %%PageMedia: <text>
+ * %%EndDefaults
+ *
+ * This section explicitly begins and ends with the above comments.
+ *
+ * The prolog section either explicitly starts with %%BeginProlog or
+ * implicitly with any nonblank line.
+ *
+ * %%BeginProlog
+ * %%EndProlog
+ *
+ * The Prolog should end with %%EndProlog, however the proglog implicitly
+ * ends when %%BeginSetup, %%Page, %%Trailer or %%EOF are encountered.
+ *
+ * The Setup section is where the version 2 page defaults are found.
+ * This section either explicitly begins with %%BeginSetup or implicitly
+ * with any nonblank line after the Prolog.
+ *
+ * %%BeginSetup
+ * %%PageBoundingBox: <int> <int> <int> <int>
+ * %%PageOrientation: Portrait|Landscape
+ * %%PaperSize: <text>
+ * %%EndSetup
+ *
+ * The Setup should end with %%EndSetup, however the setup implicitly
+ * ends when %%Page, %%Trailer or %%EOF are encountered.
+ *
+ * Next each page starts explicitly with %%Page and ends implicitly with
+ * %%Page or %%Trailer or %%EOF. The following comments are recognized:
+ *
+ * %%Page: <text> <uint>
+ * %%PageBoundingBox: <int> <int> <int> <int>|(atend)
+ * %%PageOrientation: Portrait|Landscape
+ * %%PageMedia: <text>
+ * %%PaperSize: <text>
+ *
+ * The tralier section start explicitly with %%Trailer and end with %%EOF.
+ * The following comment are examined with the proper (atend) notation
+ * was used in the header:
+ *
+ * %%Trailer
+ * %%BoundingBox: <int> <int> <int> <int>|(atend)
+ * %%Orientation: Portrait|Landscape|(atend)
+ * %%Pages: <uint> [<int>]|(atend)
+ * %%PageOrder: Ascend|Descend|Special|(atend)
+ * %%EOF
+ *
+ *
+ * + A DC-3 received severe damage to one of its wings. The wing was a total
+ * loss. There was no replacement readily available, so the mechanic
+ * installed a wing from a DC-2.
+ */
+
+#include <glib.h>
+
+struct document *
+psscan(FILE * file, int respect_eof, const gchar * fname)
+{
+ struct document *doc;
+ int bb_set = NONE;
+ int pages_set = NONE;
+ int page_order_set = NONE;
+ int orientation_set = NONE;
+ int page_bb_set = NONE;
+ int page_size_set = NONE;
+ int preread; /* flag which tells the readline isn't needed */
+ int i;
+ unsigned int maxpages = 0;
+ unsigned int nextpage = 1; /* Next expected page */
+ unsigned int thispage;
+ int ignore = 0; /* whether to ignore page ordinals */
+ char *label;
+ char *line;
+ char text[PSLINELENGTH]; /* Temporary storage for text */
+ long position; /* Position of the current line */
+ long beginsection; /* Position of the beginning of the section */
+ unsigned int line_len; /* Length of the current line */
+ unsigned int section_len; /* Place to accumulate the section length */
+ char *next_char; /* 1st char after text returned by get_next_text() */
+ char *cp;
+ GtkGSPaperSize *dmp;
+ GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
+ FileData fd;
+
+ if(!file)
+ return NULL;
+
+ rewind(file);
+
+ fd = ps_io_init(file);
+ if (!readline(fd, &line, &position, &line_len)) {
+ fprintf(stderr, "Warning: empty file.\n");
+ ps_io_exit(fd);
+ return(NULL);
+ }
+
+ /* HP printer job language data follows. Some printer drivers add pjl
+ * commands to switch a pjl printer to postscript mode. If no PS header
+ * follows, this seems to be a real pjl file. */
+ if(iscomment(line, "\033%-12345X@PJL")) {
+ /* read until first DSC comment */
+ while(readline(fd, &line, &position, &line_len)
+ && (line[0] != '%')) ;
+ if(line[0] != '%') {
+ g_print("psscan error: input files seems to be a PJL file.\n");
+ return (NULL);
+ }
+ }
+
+ /* Header comments */
+
+ /* Header should start with "%!PS-Adobe-", but some programms omit
+ * parts of this or add a ^D at the beginning. */
+ if(iscomment(line, "%!PS") || iscomment(line, "\004%!PS")) {
+ doc = g_new0(struct document, 1);
+ doc->default_page_orientation = GTK_GS_ORIENTATION_NONE;
+ doc->orientation = GTK_GS_ORIENTATION_NONE;
+
+ /* ignore possible leading ^D */
+ if(*line == '\004') {
+ position++;
+ line_len--;
+ }
+
+/* Jake Hamby patch 18/3/98 */
+
+ text[0] = '\0';
+ sscanf(line, "%*s %256s", text);
+ /*doc->epsf = iscomment(text, "EPSF-"); */
+ doc->epsf = iscomment(text, "EPSF"); /* Hamby - This line changed */
+ doc->beginheader = position;
+ section_len = line_len;
+ }
+ else {
+ /* There are postscript documents that do not have
+ %PS at the beginning, usually unstructured. We should GS decide
+ For instance, the tech reports at this university:
+
+ http://svrc.it.uq.edu.au/Bibliography/svrc-tr.html?94-45
+
+ add ugly PostScript before the actual document.
+
+ GS and gv is
+ able to display them correctly as unstructured PS.
+
+ In a way, this makes sense, a program PostScript does not need
+ the !PS at the beginning.
+ */
+ doc = g_new0(struct document, 1);
+ doc->default_page_orientation = GTK_GS_ORIENTATION_NONE;
+ doc->orientation = GTK_GS_ORIENTATION_NONE;
+ return (doc);
+ }
+
+ preread = 0;
+ while(preread || readline(fd, &line, &position, &line_len)) {
+ if(!preread)
+ section_len += line_len;
+ preread = 0;
+ if(line[0] != '%' ||
+ iscomment(line + 1, "%EndComments") ||
+ line[1] == ' ' || line[1] == '\t' || line[1] == '\n' ||
+ !isprint(line[1])) {
+ break;
+ }
+ else if(line[1] != '%') {
+ /* Do nothing */
+ }
+ else if(doc->title == NULL && iscomment(line + 2, "Title:")) {
+ doc->title = gettextline(line + length("%%Title:"));
+ }
+ else if(doc->date == NULL && iscomment(line + 2, "CreationDate:")) {
+ doc->date = gettextline(line + length("%%CreationDate:"));
+ }
+ else if(doc->creator == NULL && iscomment(line + 2, "Creator:")) {
+ doc->creator = gettextline(line + length("%%Creator:"));
+ }
+ else if(bb_set == NONE && iscomment(line + 2, "BoundingBox:")) {
+ sscanf(line + length("%%BoundingBox:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ bb_set = ATEND;
+ }
+ else {
+ if(sscanf(line + length("%%BoundingBox:"), "%d %d %d %d",
+ &(doc->boundingbox[LLX]),
+ &(doc->boundingbox[LLY]),
+ &(doc->boundingbox[URX]), &(doc->boundingbox[URY])) == 4)
+ bb_set = 1;
+ else {
+ float fllx, flly, furx, fury;
+ if(sscanf(line + length("%%BoundingBox:"), "%f %f %f %f",
+ &fllx, &flly, &furx, &fury) == 4) {
+ bb_set = 1;
+ doc->boundingbox[LLX] = fllx;
+ doc->boundingbox[LLY] = flly;
+ doc->boundingbox[URX] = furx;
+ doc->boundingbox[URY] = fury;
+ if(fllx < doc->boundingbox[LLX])
+ doc->boundingbox[LLX]--;
+ if(flly < doc->boundingbox[LLY])
+ doc->boundingbox[LLY]--;
+ if(furx > doc->boundingbox[URX])
+ doc->boundingbox[URX]++;
+ if(fury > doc->boundingbox[URY])
+ doc->boundingbox[URY]++;
+ }
+ }
+ }
+ }
+ else if(orientation_set == NONE && iscomment(line + 2, "Orientation:")) {
+ sscanf(line + length("%%Orientation:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ orientation_set = ATEND;
+ }
+ else if(strcmp(text, "Portrait") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_PORTRAIT;
+ orientation_set = 1;
+ }
+ else if(strcmp(text, "Landscape") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_LANDSCAPE;
+ orientation_set = 1;
+ }
+ else if(strcmp(text, "Seascape") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_SEASCAPE;
+ orientation_set = 1;
+ }
+ }
+ else if(page_order_set == NONE && iscomment(line + 2, "PageOrder:")) {
+ sscanf(line + length("%%PageOrder:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ page_order_set = ATEND;
+ }
+ else if(strcmp(text, "Ascend") == 0) {
+ doc->pageorder = ASCEND;
+ page_order_set = 1;
+ }
+ else if(strcmp(text, "Descend") == 0) {
+ doc->pageorder = DESCEND;
+ page_order_set = 1;
+ }
+ else if(strcmp(text, "Special") == 0) {
+ doc->pageorder = SPECIAL;
+ page_order_set = 1;
+ }
+ }
+ else if(pages_set == NONE && iscomment(line + 2, "Pages:")) {
+ sscanf(line + length("%%Pages:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ pages_set = ATEND;
+ }
+ else {
+ switch (sscanf(line + length("%%Pages:"), "%d %d", &maxpages, &i)) {
+ case 2:
+ if(page_order_set == NONE) {
+ if(i == -1) {
+ doc->pageorder = DESCEND;
+ page_order_set = 1;
+ }
+ else if(i == 0) {
+ doc->pageorder = SPECIAL;
+ page_order_set = 1;
+ }
+ else if(i == 1) {
+ doc->pageorder = ASCEND;
+ page_order_set = 1;
+ }
+ }
+ case 1:
+ if(maxpages > 0)
+ doc->pages = pages_new(NULL, 0, maxpages);
+ }
+ }
+ }
+ else if(doc->numsizes == NONE && iscomment(line + 2, "DocumentMedia:")) {
+ float w, h;
+ doc->size = g_new0(GtkGSPaperSize, 1);
+ doc->size[0].name =
+ get_next_text(line + length("%%DocumentMedia:"), &next_char);
+ if(doc->size[0].name != NULL) {
+ if(sscanf(next_char, "%f %f", &w, &h) == 2) {
+ doc->size[0].width = w + 0.5;
+ doc->size[0].height = h + 0.5;
+ }
+ if(doc->size[0].width != 0 && doc->size[0].height != 0)
+ doc->numsizes = 1;
+ else
+ g_free(doc->size[0].name);
+ }
+ preread = 1;
+ while(readline(fd, &line, &position, &line_len) &&
+ DSCcomment(line) && iscomment(line + 2, "+")) {
+ section_len += line_len;
+ doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
+ doc->size[doc->numsizes].name =
+ get_next_text(line + length("%%+"), &next_char);
+ if(doc->size[doc->numsizes].name != NULL) {
+ if(sscanf(next_char, "%f %f", &w, &h) == 2) {
+ doc->size[doc->numsizes].width = w + 0.5;
+ doc->size[doc->numsizes].height = h + 0.5;
+ }
+ if(doc->size[doc->numsizes].width != 0 &&
+ doc->size[doc->numsizes].height != 0)
+ doc->numsizes++;
+ else
+ g_free(doc->size[doc->numsizes].name);
+ }
+ }
+ section_len += line_len;
+ if(doc->numsizes != 0)
+ doc->default_page_size = doc->size;
+ }
+ else if(doc->numsizes == NONE && iscomment(line + 2, "DocumentPaperSizes:")) {
+
+ doc->size = g_new0(GtkGSPaperSize, 1);
+ doc->size[0].name =
+ get_next_text(line + length("%%DocumentPaperSizes:"), &next_char);
+ if(doc->size[0].name != NULL) {
+ doc->size[0].width = 0;
+ doc->size[0].height = 0;
+ for(dmp = papersizes; dmp->name != NULL; dmp++) {
+ /* Note: Paper size comment uses down cased paper size
+ * name. Case insensitive compares are only used for
+ * PaperSize comments.
+ */
+ if(strcasecmp(doc->size[0].name, dmp->name) == 0) {
+ g_free(doc->size[0].name);
+ doc->size[0].name = g_strdup(dmp->name);
+ doc->size[0].width = dmp->width;
+ doc->size[0].height = dmp->height;
+ break;
+ }
+ }
+ if(doc->size[0].width != 0 && doc->size[0].height != 0)
+ doc->numsizes = 1;
+ else
+ g_free(doc->size[0].name);
+ }
+ while((cp = get_next_text(next_char, &next_char))) {
+ doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
+ doc->size[doc->numsizes].name = cp;
+ doc->size[doc->numsizes].width = 0;
+ doc->size[doc->numsizes].height = 0;
+ for(dmp = papersizes; dmp->name != NULL; dmp++) {
+ /* Note: Paper size comment uses down cased paper size
+ * name. Case insensitive compares are only used for
+ * PaperSize comments.
+ */
+ if(strcasecmp(doc->size[doc->numsizes].name, dmp->name) == 0) {
+ g_free(doc->size[doc->numsizes].name);
+ doc->size[doc->numsizes].name = g_strdup(dmp->name);
+ doc->size[doc->numsizes].name = dmp->name;
+ doc->size[doc->numsizes].width = dmp->width;
+ doc->size[doc->numsizes].height = dmp->height;
+ break;
+ }
+ }
+ if(doc->size[doc->numsizes].width != 0 &&
+ doc->size[doc->numsizes].height != 0)
+ doc->numsizes++;
+ else
+ g_free(doc->size[doc->numsizes].name);
+ }
+ preread = 1;
+ while(readline(fd, &line, &position, &line_len) &&
+ DSCcomment(line) && iscomment(line + 2, "+")) {
+ section_len += line_len;
+ next_char = line + length("%%+");
+ while((cp = get_next_text(next_char, &next_char))) {
+ doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
+ doc->size[doc->numsizes].name = cp;
+ doc->size[doc->numsizes].width = 0;
+ doc->size[doc->numsizes].height = 0;
+ for(dmp = papersizes; dmp->name != NULL; dmp++) {
+ /* Note: Paper size comment uses down cased paper size
+ * name. Case insensitive compares are only used for
+ * PaperSize comments.
+ */
+ if(strcasecmp(doc->size[doc->numsizes].name, dmp->name) == 0) {
+ doc->size[doc->numsizes].width = dmp->width;
+ doc->size[doc->numsizes].height = dmp->height;
+ break;
+ }
+ }
+ if(doc->size[doc->numsizes].width != 0 &&
+ doc->size[doc->numsizes].height != 0)
+ doc->numsizes++;
+ else
+ g_free(doc->size[doc->numsizes].name);
+ }
+ }
+ section_len += line_len;
+ if(doc->numsizes != 0)
+ doc->default_page_size = doc->size;
+ }
+ }
+
+ if(DSCcomment(line) && iscomment(line + 2, "EndComments")) {
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ }
+ doc->endheader = position;
+ doc->lenheader = section_len - line_len;
+
+ /* Optional Preview comments for encapsulated PostScript files */
+
+ beginsection = position;
+ section_len = line_len;
+ while(blank(line) && readline(fd, &line, &position, &line_len)) {
+ section_len += line_len;
+ }
+
+ if(doc->epsf && DSCcomment(line) && iscomment(line + 2, "BeginPreview")) {
+ doc->beginpreview = beginsection;
+ beginsection = 0;
+ while(readline(fd, &line, &position, &line_len) &&
+ !(DSCcomment(line) && iscomment(line + 2, "EndPreview"))) {
+ section_len += line_len;
+ }
+ section_len += line_len;
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ doc->endpreview = position;
+ doc->lenpreview = section_len - line_len;
+ }
+
+ /* Page Defaults for Version 3.0 files */
+
+ if(beginsection == 0) {
+ beginsection = position;
+ section_len = line_len;
+ }
+ while(blank(line) && readline(fd, &line, &position, &line_len)) {
+ section_len += line_len;
+ }
+
+ if(DSCcomment(line) && iscomment(line + 2, "BeginDefaults")) {
+ doc->begindefaults = beginsection;
+ beginsection = 0;
+ while(readline(fd, &line, &position, &line_len) &&
+ !(DSCcomment(line) && iscomment(line + 2, "EndDefaults"))) {
+ section_len += line_len;
+ if(!DSCcomment(line)) {
+ /* Do nothing */
+ }
+ else if(doc->default_page_orientation == GTK_GS_ORIENTATION_NONE &&
+ iscomment(line + 2, "PageOrientation:")) {
+ sscanf(line + length("%%PageOrientation:"), "%256s", text);
+ if(strcmp(text, "Portrait") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_PORTRAIT;
+ }
+ else if(strcmp(text, "Landscape") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_LANDSCAPE;
+ }
+ else if(strcmp(text, "Seascape") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_SEASCAPE;
+ }
+ }
+ else if(page_size_set == NONE && iscomment(line + 2, "PageMedia:")) {
+ cp = get_next_text(line + length("%%PageMedia:"), NULL);
+ for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
+ if(strcmp(cp, dmp->name) == 0) {
+ doc->default_page_size = dmp;
+ page_size_set = 1;
+ break;
+ }
+ }
+ g_free(cp);
+ }
+ else if(page_bb_set == NONE && iscomment(line + 2, "PageBoundingBox:")) {
+ if(sscanf(line + length("%%PageBoundingBox:"), "%d %d %d %d",
+ &(doc->default_page_boundingbox[LLX]),
+ &(doc->default_page_boundingbox[LLY]),
+ &(doc->default_page_boundingbox[URX]),
+ &(doc->default_page_boundingbox[URY])) == 4)
+ page_bb_set = 1;
+ else {
+ float fllx, flly, furx, fury;
+ if(sscanf
+ (line + length("%%PageBoundingBox:"), "%f %f %f %f",
+ &fllx, &flly, &furx, &fury) == 4) {
+ page_bb_set = 1;
+ doc->default_page_boundingbox[LLX] = fllx;
+ doc->default_page_boundingbox[LLY] = flly;
+ doc->default_page_boundingbox[URX] = furx;
+ doc->default_page_boundingbox[URY] = fury;
+ if(fllx < doc->default_page_boundingbox[LLX])
+ doc->default_page_boundingbox[LLX]--;
+ if(flly < doc->default_page_boundingbox[LLY])
+ doc->default_page_boundingbox[LLY]--;
+ if(furx > doc->default_page_boundingbox[URX])
+ doc->default_page_boundingbox[URX]++;
+ if(fury > doc->default_page_boundingbox[URY])
+ doc->default_page_boundingbox[URY]++;
+ }
+ }
+ }
+ }
+ section_len += line_len;
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ doc->enddefaults = position;
+ doc->lendefaults = section_len - line_len;
+ }
+
+ /* Document Prolog */
+
+ if(beginsection == 0) {
+ beginsection = position;
+ section_len = line_len;
+ }
+ while(blank(line) && readline(fd, &line, &position, &line_len)) {
+ section_len += line_len;
+ }
+
+ if(!(DSCcomment(line) &&
+ (iscomment(line + 2, "BeginSetup") ||
+ iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") || iscomment(line + 2, "EOF")))) {
+ doc->beginprolog = beginsection;
+ beginsection = 0;
+ preread = 1;
+
+ while((preread ||
+ readline(fd, &line, &position, &line_len)) &&
+ !(DSCcomment(line) &&
+ (iscomment(line + 2, "EndProlog") ||
+ iscomment(line + 2, "BeginSetup") ||
+ iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") || iscomment(line + 2, "EOF")))) {
+ if(!preread)
+ section_len += line_len;
+ preread = 0;
+ }
+ section_len += line_len;
+ if(DSCcomment(line) && iscomment(line + 2, "EndProlog")) {
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ }
+ doc->endprolog = position;
+ doc->lenprolog = section_len - line_len;
+ }
+
+ /* Document Setup, Page Defaults found here for Version 2 files */
+
+ if(beginsection == 0) {
+ beginsection = position;
+ section_len = line_len;
+ }
+ while(blank(line) && readline(fd, &line, &position, &line_len)) {
+ section_len += line_len;
+ }
+
+ if(!(DSCcomment(line) &&
+ (iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") ||
+ (respect_eof && iscomment(line + 2, "EOF"))))) {
+ doc->beginsetup = beginsection;
+ beginsection = 0;
+ preread = 1;
+ while((preread ||
+ readline(fd, &line, &position, &line_len)) &&
+ !(DSCcomment(line) &&
+ (iscomment(line + 2, "EndSetup") ||
+ iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") ||
+ (respect_eof && iscomment(line + 2, "EOF"))))) {
+ if(!preread)
+ section_len += line_len;
+ preread = 0;
+ if(!DSCcomment(line)) {
+ /* Do nothing */
+ }
+ else if(doc->default_page_orientation == GTK_GS_ORIENTATION_NONE &&
+ iscomment(line + 2, "PageOrientation:")) {
+ sscanf(line + length("%%PageOrientation:"), "%256s", text);
+ if(strcmp(text, "Portrait") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_PORTRAIT;
+ }
+ else if(strcmp(text, "Landscape") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_LANDSCAPE;
+ }
+ else if(strcmp(text, "Seascape") == 0) {
+ doc->default_page_orientation = GTK_GS_ORIENTATION_SEASCAPE;
+ }
+ }
+ else if(page_size_set == NONE && iscomment(line + 2, "PaperSize:")) {
+ cp = get_next_text(line + length("%%PaperSize:"), NULL);
+ for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
+ /* Note: Paper size comment uses down cased paper size
+ * name. Case insensitive compares are only used for
+ * PaperSize comments.
+ */
+ if(strcasecmp(cp, dmp->name) == 0) {
+ doc->default_page_size = dmp;
+ page_size_set = 1;
+ break;
+ }
+ }
+ g_free(cp);
+ }
+ else if(page_bb_set == NONE && iscomment(line + 2, "PageBoundingBox:")) {
+ if(sscanf(line + length("%%PageBoundingBox:"), "%d %d %d %d",
+ &(doc->default_page_boundingbox[LLX]),
+ &(doc->default_page_boundingbox[LLY]),
+ &(doc->default_page_boundingbox[URX]),
+ &(doc->default_page_boundingbox[URY])) == 4)
+ page_bb_set = 1;
+ else {
+ float fllx, flly, furx, fury;
+ if(sscanf
+ (line + length("%%PageBoundingBox:"), "%f %f %f %f",
+ &fllx, &flly, &furx, &fury) == 4) {
+ page_bb_set = 1;
+ doc->default_page_boundingbox[LLX] = fllx;
+ doc->default_page_boundingbox[LLY] = flly;
+ doc->default_page_boundingbox[URX] = furx;
+ doc->default_page_boundingbox[URY] = fury;
+ if(fllx < doc->default_page_boundingbox[LLX])
+ doc->default_page_boundingbox[LLX]--;
+ if(flly < doc->default_page_boundingbox[LLY])
+ doc->default_page_boundingbox[LLY]--;
+ if(furx > doc->default_page_boundingbox[URX])
+ doc->default_page_boundingbox[URX]++;
+ if(fury > doc->default_page_boundingbox[URY])
+ doc->default_page_boundingbox[URY]++;
+ }
+ }
+ }
+ }
+ section_len += line_len;
+ if(DSCcomment(line) && iscomment(line + 2, "EndSetup")) {
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ }
+ doc->endsetup = position;
+ doc->lensetup = section_len - line_len;
+ }
+
+ /* HACK: Mozilla 1.8 Workaround.
+
+ It seems that Mozilla 1.8 generates important postscript code
+ after the '%%EndProlog' and before the first page comment '%%Page: x y'.
+ See comment below also.
+ */
+
+ if(doc->beginprolog && !doc->beginsetup) {
+ doc->lenprolog += section_len - line_len;
+ doc->endprolog = position;
+ }
+
+ /* HACK: Windows NT Workaround
+
+ Mark Pfeifer (pfeiferm%ppddev@comet.cmis.abbott.com) noticed
+ about problems when viewing Windows NT 3.51 generated postscript
+ files with gv. He found that the relevant postscript files
+ show important postscript code after the '%%EndSetup' and before
+ the first page comment '%%Page: x y'.
+ */
+ if(doc->beginsetup) {
+ while(!(DSCcomment(line) &&
+ (iscomment(line + 2, "EndSetup") ||
+ (iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") ||
+ (respect_eof && iscomment(line + 2, "EOF"))))) &&
+ (readline(fd, &line, &position, &line_len))) {
+ section_len += line_len;
+ doc->lensetup = section_len - line_len;
+ doc->endsetup = position;
+ }
+ }
+
+ /* Individual Pages */
+
+ if(beginsection == 0) {
+ beginsection = position;
+ section_len = line_len;
+ }
+ while(blank(line) && readline(fd, &line, &position, &line_len)) {
+ section_len += line_len;
+ }
+
+
+newpage:
+ while(DSCcomment(line) && iscomment(line + 2, "Page:")) {
+ if(maxpages == 0) {
+ maxpages = 1;
+ doc->pages = pages_new(NULL, 0, maxpages);
+ }
+ label = get_next_text(line + length("%%Page:"), &next_char);
+ if(sscanf(next_char, "%d", &thispage) != 1)
+ thispage = 0;
+ if(nextpage == 1) {
+ ignore = thispage != 1;
+ }
+ if(!ignore && thispage != nextpage) {
+ g_free(label);
+ doc->numpages--;
+ goto continuepage;
+ }
+ nextpage++;
+ if(doc->numpages == maxpages) {
+ maxpages++;
+ doc->pages = pages_new(doc->pages, maxpages - 1, maxpages);
+ }
+ page_bb_set = NONE;
+ doc->pages[doc->numpages].label = label;
+ if(beginsection) {
+ doc->pages[doc->numpages].begin = beginsection;
+ beginsection = 0;
+ }
+ else {
+ doc->pages[doc->numpages].begin = position;
+ section_len = line_len;
+ }
+ continuepage:
+ while(readline(fd, &line, &position, &line_len) &&
+ !(DSCcomment(line) &&
+ (iscomment(line + 2, "Page:") ||
+ iscomment(line + 2, "Trailer") ||
+ (respect_eof && iscomment(line + 2, "EOF"))))) {
+ section_len += line_len;
+ if(!DSCcomment(line)) {
+ /* Do nothing */
+ }
+ else if(doc->pages[doc->numpages].orientation == NONE &&
+ iscomment(line + 2, "PageOrientation:")) {
+ sscanf(line + length("%%PageOrientation:"), "%256s", text);
+ if(strcmp(text, "Portrait") == 0) {
+ doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_PORTRAIT;
+ }
+ else if(strcmp(text, "Landscape") == 0) {
+ doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_LANDSCAPE;
+ }
+ else if(strcmp(text, "Seascape") == 0) {
+ doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_SEASCAPE;
+ }
+ }
+ else if(doc->pages[doc->numpages].size == NULL &&
+ iscomment(line + 2, "PageMedia:")) {
+ cp = get_next_text(line + length("%%PageMedia:"), NULL);
+ for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
+ if(strcmp(cp, dmp->name) == 0) {
+ doc->pages[doc->numpages].size = dmp;
+ break;
+ }
+ }
+ g_free(cp);
+ }
+ else if(doc->pages[doc->numpages].size == NULL &&
+ iscomment(line + 2, "PaperSize:")) {
+ cp = get_next_text(line + length("%%PaperSize:"), NULL);
+ for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
+ /* Note: Paper size comment uses down cased paper size
+ * name. Case insensitive compares are only used for
+ * PaperSize comments.
+ */
+ if(strcasecmp(cp, dmp->name) == 0) {
+ doc->pages[doc->numpages].size = dmp;
+ break;
+ }
+ }
+ g_free(cp);
+ }
+ else if((page_bb_set == NONE || page_bb_set == ATEND) &&
+ iscomment(line + 2, "PageBoundingBox:")) {
+ sscanf(line + length("%%PageBoundingBox:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ page_bb_set = ATEND;
+ }
+ else {
+ if(sscanf
+ (line + length("%%PageBoundingBox:"), "%d %d %d %d",
+ &(doc->pages[doc->numpages].boundingbox[LLX]),
+ &(doc->pages[doc->numpages].boundingbox[LLY]),
+ &(doc->pages[doc->numpages].boundingbox[URX]),
+ &(doc->pages[doc->numpages].boundingbox[URY])) == 4) {
+ if(page_bb_set == NONE)
+ page_bb_set = 1;
+ }
+ else {
+ float fllx, flly, furx, fury;
+ if(sscanf(line + length("%%PageBoundingBox:"),
+ "%f %f %f %f", &fllx, &flly, &furx, &fury) == 4) {
+ if(page_bb_set == NONE)
+ page_bb_set = 1;
+ doc->pages[doc->numpages].boundingbox[LLX] = fllx;
+ doc->pages[doc->numpages].boundingbox[LLY] = flly;
+ doc->pages[doc->numpages].boundingbox[URX] = furx;
+ doc->pages[doc->numpages].boundingbox[URY] = fury;
+ if(fllx < doc->pages[doc->numpages].boundingbox[LLX])
+ doc->pages[doc->numpages].boundingbox[LLX]--;
+ if(flly < doc->pages[doc->numpages].boundingbox[LLY])
+ doc->pages[doc->numpages].boundingbox[LLY]--;
+ if(furx > doc->pages[doc->numpages].boundingbox[URX])
+ doc->pages[doc->numpages].boundingbox[URX]++;
+ if(fury > doc->pages[doc->numpages].boundingbox[URY])
+ doc->pages[doc->numpages].boundingbox[URY]++;
+ }
+ }
+ }
+ }
+ }
+ section_len += line_len;
+ doc->pages[doc->numpages].end = position;
+ doc->pages[doc->numpages].len = section_len - line_len;
+ doc->numpages++;
+ }
+
+ /* Document Trailer */
+
+ if(beginsection) {
+ doc->begintrailer = beginsection;
+ beginsection = 0;
+ }
+ else {
+ doc->begintrailer = position;
+ section_len = line_len;
+ }
+
+ preread = 1;
+ while((preread ||
+ readline(fd, &line, &position, &line_len)) &&
+ !(respect_eof && DSCcomment(line) && iscomment(line + 2, "EOF"))) {
+ if(!preread)
+ section_len += line_len;
+ preread = 0;
+ if(!DSCcomment(line)) {
+ /* Do nothing */
+ }
+ else if(iscomment(line + 2, "Page:")) {
+ g_free(get_next_text(line + length("%%Page:"), &next_char));
+ if(sscanf(next_char, "%d", &thispage) != 1)
+ thispage = 0;
+ if(!ignore && thispage == nextpage) {
+ if(doc->numpages > 0) {
+ doc->pages[doc->numpages - 1].end = position;
+ doc->pages[doc->numpages - 1].len += section_len - line_len;
+ }
+ else {
+ if(doc->endsetup) {
+ doc->endsetup = position;
+ doc->endsetup += section_len - line_len;
+ }
+ else if(doc->endprolog) {
+ doc->endprolog = position;
+ doc->endprolog += section_len - line_len;
+ }
+ }
+ goto newpage;
+ }
+ }
+ else if(!respect_eof && iscomment(line + 2, "Trailer")) {
+ /* What we thought was the start of the trailer was really */
+ /* the trailer of an EPS on the page. */
+ /* Set the end of the page to this trailer and keep scanning. */
+ if(doc->numpages > 0) {
+ doc->pages[doc->numpages - 1].end = position;
+ doc->pages[doc->numpages - 1].len += section_len - line_len;
+ }
+ doc->begintrailer = position;
+ section_len = line_len;
+ }
+ else if(bb_set == ATEND && iscomment(line + 2, "BoundingBox:")) {
+ if(sscanf(line + length("%%BoundingBox:"), "%d %d %d %d",
+ &(doc->boundingbox[LLX]),
+ &(doc->boundingbox[LLY]),
+ &(doc->boundingbox[URX]), &(doc->boundingbox[URY])) != 4) {
+ float fllx, flly, furx, fury;
+ if(sscanf(line + length("%%BoundingBox:"), "%f %f %f %f",
+ &fllx, &flly, &furx, &fury) == 4) {
+ doc->boundingbox[LLX] = fllx;
+ doc->boundingbox[LLY] = flly;
+ doc->boundingbox[URX] = furx;
+ doc->boundingbox[URY] = fury;
+ if(fllx < doc->boundingbox[LLX])
+ doc->boundingbox[LLX]--;
+ if(flly < doc->boundingbox[LLY])
+ doc->boundingbox[LLY]--;
+ if(furx > doc->boundingbox[URX])
+ doc->boundingbox[URX]++;
+ if(fury > doc->boundingbox[URY])
+ doc->boundingbox[URY]++;
+ }
+ }
+ }
+ else if(orientation_set == ATEND && iscomment(line + 2, "Orientation:")) {
+ sscanf(line + length("%%Orientation:"), "%256s", text);
+ if(strcmp(text, "Portrait") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_PORTRAIT;
+ }
+ else if(strcmp(text, "Landscape") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_LANDSCAPE;
+ }
+ else if(strcmp(text, "Seascape") == 0) {
+ doc->orientation = GTK_GS_ORIENTATION_SEASCAPE;
+ }
+ }
+ else if(page_order_set == ATEND && iscomment(line + 2, "PageOrder:")) {
+ sscanf(line + length("%%PageOrder:"), "%256s", text);
+ if(strcmp(text, "Ascend") == 0) {
+ doc->pageorder = ASCEND;
+ }
+ else if(strcmp(text, "Descend") == 0) {
+ doc->pageorder = DESCEND;
+ }
+ else if(strcmp(text, "Special") == 0) {
+ doc->pageorder = SPECIAL;
+ }
+ }
+ else if(pages_set == ATEND && iscomment(line + 2, "Pages:")) {
+ if(sscanf(line + length("%%Pages:"), "%*u %d", &i) == 1) {
+ if(page_order_set == NONE) {
+ if(i == -1)
+ doc->pageorder = DESCEND;
+ else if(i == 0)
+ doc->pageorder = SPECIAL;
+ else if(i == 1)
+ doc->pageorder = ASCEND;
+ }
+ }
+ }
+ }
+ section_len += line_len;
+ if(DSCcomment(line) && iscomment(line + 2, "EOF")) {
+ readline(fd, &line, &position, &line_len);
+ section_len += line_len;
+ }
+ doc->endtrailer = position;
+ doc->lentrailer = section_len - line_len;
+
+#if 0
+ section_len = line_len;
+ preread = 1;
+ while(preread || readline(line, sizeof line, file, &position, &line_len)) {
+ if(!preread)
+ section_len += line_len;
+ preread = 0;
+ if(DSCcomment(line) && iscomment(line + 2, "Page:")) {
+ g_free(get_next_text(line + length("%%Page:"), &next_char));
+ if(sscanf(next_char, "%d", &thispage) != 1)
+ thispage = 0;
+ if(!ignore && thispage == nextpage) {
+ if(doc->numpages > 0) {
+ doc->pages[doc->numpages - 1].end = position;
+ doc->pages[doc->numpages - 1].len += doc->lentrailer +
+ section_len - line_len;
+ }
+ else {
+ if(doc->endsetup) {
+ doc->endsetup = position;
+ doc->endsetup += doc->lentrailer + section_len - line_len;
+ }
+ else if(doc->endprolog) {
+ doc->endprolog = position;
+ doc->endprolog += doc->lentrailer + section_len - line_len;
+ }
+ }
+ goto newpage;
+ }
+ }
+ }
+#endif
+ return doc;
+}
+
+/*
+ * psfree -- free dynamic storage associated with document structure.
+ */
+
+void
+psfree(doc)
+ struct document *doc;
+{
+ int i;
+
+ if(doc) {
+ /*
+ printf("This document exists\n");
+ */
+ for(i = 0; i < doc->numpages; i++) {
+ if(doc->pages[i].label)
+ g_free(doc->pages[i].label);
+ }
+ for(i = 0; i < doc->numsizes; i++) {
+ if(doc->size[i].name)
+ g_free(doc->size[i].name);
+ }
+ if(doc->title)
+ g_free(doc->title);
+ if(doc->date)
+ g_free(doc->date);
+ if(doc->creator)
+ g_free(doc->creator);
+ if(doc->pages)
+ g_free(doc->pages);
+ if(doc->size)
+ g_free(doc->size);
+ g_free(doc);
+ }
+}
+
+/*
+ * gettextine -- skip over white space and return the rest of the line.
+ * If the text begins with '(' return the text string
+ * using get_next_text().
+ */
+
+static char *
+gettextline(char *line)
+{
+ char *cp;
+
+ while(*line && (*line == ' ' || *line == '\t'))
+ line++;
+ if(*line == '(') {
+ return get_next_text(line, NULL);
+ }
+ else {
+ if(strlen(line) == 0)
+ return NULL;
+
+ cp = g_strdup(line);
+
+ /* Remove end of line */
+ if(cp[strlen(line) - 2] == '\r' && cp[strlen(line) - 1] == '\n')
+ /* Handle DOS \r\n */
+ cp[strlen(line) - 2] = '\0';
+ else if(cp[strlen(line) - 1] == '\n' || cp[strlen(line) - 1] == '\r')
+ /* Handle mac and unix */
+ cp[strlen(line) - 1] = '\0';
+
+ return cp;
+ }
+}
+
+/*
+ * get_next_text -- return the next text string on the line.
+ * return NULL if nothing is present.
+ */
+
+static char *
+get_next_text(line, next_char)
+ char *line;
+ char **next_char;
+{
+ char text[PSLINELENGTH]; /* Temporary storage for text */
+ char *cp;
+ int quoted = 0;
+
+ while(*line && (*line == ' ' || *line == '\t'))
+ line++;
+ cp = text;
+ if(*line == '(') {
+ int level = 0;
+ quoted = 1;
+ line++;
+ while(*line && !(*line == ')' && level == 0)
+ && (cp - text) < PSLINELENGTH - 1) {
+ if(*line == '\\') {
+ if(*(line + 1) == 'n') {
+ *cp++ = '\n';
+ line += 2;
+ }
+ else if(*(line + 1) == 'r') {
+ *cp++ = '\r';
+ line += 2;
+ }
+ else if(*(line + 1) == 't') {
+ *cp++ = '\t';
+ line += 2;
+ }
+ else if(*(line + 1) == 'b') {
+ *cp++ = '\b';
+ line += 2;
+ }
+ else if(*(line + 1) == 'f') {
+ *cp++ = '\f';
+ line += 2;
+ }
+ else if(*(line + 1) == '\\') {
+ *cp++ = '\\';
+ line += 2;
+ }
+ else if(*(line + 1) == '(') {
+ *cp++ = '(';
+ line += 2;
+ }
+ else if(*(line + 1) == ')') {
+ *cp++ = ')';
+ line += 2;
+ }
+ else if(*(line + 1) >= '0' && *(line + 1) <= '9') {
+ if(*(line + 2) >= '0' && *(line + 2) <= '9') {
+ if(*(line + 3) >= '0' && *(line + 3) <= '9') {
+ *cp++ =
+ ((*(line + 1) - '0') * 8 + *(line + 2) -
+ '0') * 8 + *(line + 3) - '0';
+ line += 4;
+ }
+ else {
+ *cp++ = (*(line + 1) - '0') * 8 + *(line + 2) - '0';
+ line += 3;
+ }
+ }
+ else {
+ *cp++ = *(line + 1) - '0';
+ line += 2;
+ }
+ }
+ else {
+ line++;
+ *cp++ = *line++;
+ }
+ }
+ else if(*line == '(') {
+ level++;
+ *cp++ = *line++;
+ }
+ else if(*line == ')') {
+ level--;
+ *cp++ = *line++;
+ }
+ else {
+ *cp++ = *line++;
+ }
+ }
+ }
+ else {
+ while(*line && !(*line == ' ' || *line == '\t' || *line == '\n')
+ && (cp - text) < PSLINELENGTH - 1)
+ *cp++ = *line++;
+ }
+ *cp = '\0';
+ if(next_char)
+ *next_char = line;
+ if(!quoted && strlen(text) == 0)
+ return NULL;
+ return g_strdup(text);
+}
+
+/*
+ * pscopy -- copy lines of Postscript from a section of one file
+ * to another file.
+ * Automatically switch to binary copying whenever
+ * %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
+ * comments are encountered.
+ */
+
+void
+pscopy(from, to, begin, end)
+ FILE *from;
+ GtkGSDocSink *to;
+ long begin; /* set negative to avoid initial seek */
+ long end;
+{
+ char line[PSLINELENGTH]; /* 255 characters + 1 newline + 1 NULL */
+ char text[PSLINELENGTH]; /* Temporary storage for text */
+ unsigned int num;
+ int i;
+ char buf[BUFSIZ];
+
+ if(begin >= 0)
+ fseek(from, begin, SEEK_SET);
+ while(ftell(from) < end) {
+ fgets(line, sizeof line, from);
+ gtk_gs_doc_sink_write(to, line, strlen(line));
+
+ if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
+ /* Do nothing */
+ }
+ else if(iscomment(line + 7, "Data:")) {
+ text[0] = '\0';
+ if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
+ if(strcmp(text, "Lines") == 0) {
+ for(i = 0; i < num; i++) {
+ fgets(line, sizeof(line), from);
+ gtk_gs_doc_sink_write(to, line, strlen(line));
+ }
+ }
+ else {
+ while(num > BUFSIZ) {
+ fread(buf, sizeof(char), BUFSIZ, from);
+ gtk_gs_doc_sink_write(to, buf, BUFSIZ);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof(char), num, from);
+ gtk_gs_doc_sink_write(to, buf, num);
+ }
+ }
+ }
+ else if(iscomment(line + 7, "Binary:")) {
+ if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
+ while(num > BUFSIZ) {
+ fread(buf, sizeof(char), BUFSIZ, from);
+ gtk_gs_doc_sink_write(to, buf, BUFSIZ);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof(char), num, from);
+ gtk_gs_doc_sink_write(to, buf, num);
+ }
+ }
+ }
+}
+
+/*
+ * pscopyuntil -- copy lines of Postscript from a section of one file
+ * to another file until a particular comment is reached.
+ * Automatically switch to binary copying whenever
+ * %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
+ * comments are encountered.
+ */
+
+char *
+pscopyuntil(FILE * from, GtkGSDocSink * to, long begin, long end,
+ const char *comment)
+{
+ char line[PSLINELENGTH]; /* 255 characters + 1 newline + 1 NULL */
+ char text[PSLINELENGTH]; /* Temporary storage for text */
+ unsigned int num;
+ int comment_length;
+ int i;
+ char buf[BUFSIZ];
+
+ if(comment != NULL)
+ comment_length = strlen(comment);
+ else
+ comment_length = 0;
+ if(begin >= 0)
+ fseek(from, begin, SEEK_SET);
+
+ while(ftell(from) < end && !feof(from)) {
+ fgets(line, sizeof line, from);
+
+ /* iscomment cannot be used here,
+ * because comment_length is not known at compile time. */
+ if(comment != NULL && strncmp(line, comment, comment_length) == 0) {
+ return g_strdup(line);
+ }
+ gtk_gs_doc_sink_write(to, line, strlen(line));
+ if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
+ /* Do nothing */
+ }
+ else if(iscomment(line + 7, "Data:")) {
+ text[0] = '\0';
+ if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
+ if(strcmp(text, "Lines") == 0) {
+ for(i = 0; i < num; i++) {
+ fgets(line, sizeof line, from);
+ gtk_gs_doc_sink_write(to, line, strlen(line));
+ }
+ }
+ else {
+ while(num > BUFSIZ) {
+ fread(buf, sizeof(char), BUFSIZ, from);
+ gtk_gs_doc_sink_write(to, buf, BUFSIZ);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof(char), num, from);
+ gtk_gs_doc_sink_write(to, buf, num);
+ }
+ }
+ }
+ else if(iscomment(line + 7, "Binary:")) {
+ if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
+ while(num > BUFSIZ) {
+ fread(buf, sizeof(char), BUFSIZ, from);
+ gtk_gs_doc_sink_write(to, buf, BUFSIZ);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof(char), num, from);
+ gtk_gs_doc_sink_write(to, buf, num);
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * blank -- determine whether the line contains nothing but whitespace.
+ */
+
+static int
+blank(char *line)
+{
+ char *cp = line;
+
+ while(*cp == ' ' || *cp == '\t')
+ cp++;
+ return *cp == '\n' || (*cp == '%' && (line[0] != '%' || line[1] != '%'));
+}
+
+/*##########################################################*/
+/* pscopydoc */
+/* Copy the headers, marked pages, and trailer to fp */
+/*##########################################################*/
+
+void
+pscopydoc(GtkGSDocSink * dest,
+ char *src_filename, struct document *d, gint * pagelist)
+{
+ FILE *src_file;
+ char text[PSLINELENGTH];
+ char *comment;
+ gboolean pages_written = FALSE;
+ gboolean pages_atend = FALSE;
+ int pages;
+ int page = 1;
+ int i, j;
+ int here;
+
+ src_file = fopen(src_filename, "r");
+ i = 0;
+ pages = 0;
+ for(i = 0; i < d->numpages; i++) {
+ if(pagelist[i])
+ pages++;
+ }
+
+ here = d->beginheader;
+
+ while((comment = pscopyuntil(src_file, dest, here, d->endheader, "%%Pages:"))) {
+ here = ftell(src_file);
+ if(pages_written || pages_atend) {
+ g_free(comment);
+ continue;
+ }
+ sscanf(comment + length("%%Pages:"), "%256s", text);
+ if(strcmp(text, "(atend)") == 0) {
+ gtk_gs_doc_sink_write(dest, comment, strlen(comment));
+ pages_atend = TRUE;
+ }
+ else {
+ switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
+ case 1:
+ gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
+ break;
+ default:
+ gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
+ break;
+ }
+ pages_written = TRUE;
+ }
+ g_free(comment);
+ }
+ pscopyuntil(src_file, dest, d->beginpreview, d->endpreview, NULL);
+ pscopyuntil(src_file, dest, d->begindefaults, d->enddefaults, NULL);
+ pscopyuntil(src_file, dest, d->beginprolog, d->endprolog, NULL);
+ pscopyuntil(src_file, dest, d->beginsetup, d->endsetup, NULL);
+
+ for(i = 0; i < d->numpages; i++) {
+ if(d->pageorder == DESCEND)
+ j = (d->numpages - 1) - i;
+ else
+ j = i;
+ j = i;
+ if(pagelist[j]) {
+ comment = pscopyuntil(src_file, dest,
+ d->pages[i].begin, d->pages[i].end, "%%Page:");
+ gtk_gs_doc_sink_printf(dest, "%%%%Page: %s %d\n",
+ d->pages[i].label, page++);
+ g_free(comment);
+ pscopyuntil(src_file, dest, -1, d->pages[i].end, NULL);
+ }
+ }
+
+ here = d->begintrailer;
+ while((comment = pscopyuntil(src_file, dest, here, d->endtrailer,
+ "%%Pages:"))) {
+ here = ftell(src_file);
+ if(pages_written) {
+ g_free(comment);
+ continue;
+ }
+ switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
+ case 1:
+ gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
+ break;
+ default:
+ gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
+ break;
+ }
+ pages_written = TRUE;
+ g_free(comment);
+ }
+
+ fclose(src_file);
+}
+
+/*----------------------------------------------------------*/
+/* ps_io_init */
+/*----------------------------------------------------------*/
+
+#define FD_FILE (fd->file)
+#define FD_FILE_DESC (fd->file_desc)
+#define FD_FILEPOS (fd->filepos)
+#define FD_LINE_BEGIN (fd->line_begin)
+#define FD_LINE_END (fd->line_end)
+#define FD_LINE_LEN (fd->line_len)
+#define FD_LINE_TERMCHAR (fd->line_termchar)
+#define FD_BUF (fd->buf)
+#define FD_BUF_END (fd->buf_end)
+#define FD_BUF_SIZE (fd->buf_size)
+#define FD_STATUS (fd->status)
+
+#define FD_STATUS_OKAY 0
+#define FD_STATUS_BUFTOOLARGE 1
+#define FD_STATUS_NOMORECHARS 2
+
+#define LINE_CHUNK_SIZE 4096
+#define MAX_PS_IO_FGETCHARS_BUF_SIZE 57344
+#define BREAK_PS_IO_FGETCHARS_BUF_SIZE 49152
+
+static FileData ps_io_init(file)
+ FILE *file;
+{
+ FileData fd;
+ size_t size = sizeof(FileDataStruct);
+
+ fd = (FileData) g_malloc(size);
+ memset((void*) fd ,0,(size_t)size);
+
+ rewind(file);
+ FD_FILE = file;
+ FD_FILE_DESC = fileno(file);
+ FD_FILEPOS = ftell(file);
+ FD_BUF_SIZE = (2*LINE_CHUNK_SIZE)+1;
+ FD_BUF = g_malloc(FD_BUF_SIZE);
+ FD_BUF[0] = '\0';
+ return(fd);
+}
+
+/*----------------------------------------------------------*/
+/* ps_io_exit */
+/*----------------------------------------------------------*/
+
+static void
+ps_io_exit(fd)
+ FileData fd;
+{
+ g_free(FD_BUF);
+ g_free(fd);
+}
+
+/*----------------------------------------------------------*/
+/* ps_io_fseek */
+/*----------------------------------------------------------*/
+
+/*static int
+ps_io_fseek(fd,offset)
+ FileData fd;
+ int offset;
+{
+ int status;
+ status=fseek(FD_FILE,(long)offset,SEEK_SET);
+ FD_BUF_END = FD_LINE_BEGIN = FD_LINE_END = FD_LINE_LEN = 0;
+ FD_FILEPOS = offset;
+ FD_STATUS = FD_STATUS_OKAY;
+ return(status);
+}*/
+
+/*----------------------------------------------------------*/
+/* ps_io_ftell */
+/*----------------------------------------------------------*/
+
+/*static int
+ps_io_ftell(fd)
+ FileData fd;
+{
+ return(FD_FILEPOS);
+}*/
+
+/*----------------------------------------------------------*/
+/* ps_io_fgetchars */
+/*----------------------------------------------------------*/
+
+#ifdef USE_MEMMOVE_CODE
+static void ps_memmove (d, s, l)
+ char *d;
+ const char *s;
+ unsigned l;
+{
+ if (s < d) for (s += l, d += l; l; --l) *--d = *--s;
+ else if (s != d) for (; l; --l) *d++ = *s++;
+}
+#else
+# define ps_memmove memmove
+#endif
+
+static char * ps_io_fgetchars(fd,num)
+ FileData fd;
+ int num;
+{
+ char *eol=NULL,*tmp;
+ size_t size_of_char = sizeof(char);
+
+ if (FD_STATUS != FD_STATUS_OKAY) {
+ return(NULL);
+ }
+
+ FD_BUF[FD_LINE_END] = FD_LINE_TERMCHAR; /* restoring char previously exchanged against '\0' */
+ FD_LINE_BEGIN = FD_LINE_END;
+
+ do {
+ if (num<0) { /* reading whole line */
+ if (FD_BUF_END-FD_LINE_END) {
+ /* strpbrk is faster but fails on lines with embedded NULLs
+ eol = strpbrk(FD_BUF+FD_LINE_END,"\n\r");
+ */
+ tmp = FD_BUF + FD_BUF_END;
+ eol = FD_BUF + FD_LINE_END;
+ while (eol < tmp && *eol != '\n' && *eol != '\r') eol++;
+ if (eol >= tmp) eol = NULL;
+ if (eol) {
+ if (*eol=='\r' && *(eol+1)=='\n') eol += 2;
+ else eol++;
+ break;
+ }
+ }
+ } else { /* reading specified num of chars */
+ if (FD_BUF_END >= FD_LINE_BEGIN+num) {
+ eol = FD_BUF+FD_LINE_BEGIN+num;
+ break;
+ }
+ }
+
+ if (FD_BUF_END - FD_LINE_BEGIN > BREAK_PS_IO_FGETCHARS_BUF_SIZE) {
+ eol = FD_BUF + FD_BUF_END - 1;
+ break;
+ }
+
+ while (FD_BUF_SIZE < FD_BUF_END+LINE_CHUNK_SIZE+1) {
+ if (FD_BUF_SIZE > MAX_PS_IO_FGETCHARS_BUF_SIZE) {
+ /* we should never get here, since the line is broken
+ artificially after BREAK_PS_IO_FGETCHARS_BUF_SIZE bytes. */
+ fprintf(stderr, "gv: ps_io_fgetchars: Fatal Error: buffer became too large.\n");
+ exit(-1);
+ }
+ if (FD_LINE_BEGIN) {
+ ps_memmove((void*)FD_BUF,(void*)(FD_BUF+FD_LINE_BEGIN),
+ ((size_t)(FD_BUF_END-FD_LINE_BEGIN+1))*size_of_char);
+ FD_BUF_END -= FD_LINE_BEGIN;
+ FD_LINE_BEGIN = 0;
+ } else {
+ FD_BUF_SIZE = FD_BUF_SIZE+LINE_CHUNK_SIZE+1;
+ FD_BUF = g_realloc(FD_BUF,FD_BUF_SIZE);
+ }
+ }
+
+ FD_LINE_END = FD_BUF_END;
+#ifdef VMS
+ /* different existing VMS file formats require that we use read here ###jp###,10/12/96 */
+ if (num<0) FD_BUF_END += read(FD_FILE_DESC,FD_BUF+FD_BUF_END,LINE_CHUNK_SIZE);
+ else FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
+#else
+ /* read() seems to fail sometimes (? ? ?) so we always use fread ###jp###,07/31/96*/
+ FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
+#endif
+
+ FD_BUF[FD_BUF_END] = '\0';
+ if (FD_BUF_END-FD_LINE_END == 0) {
+ FD_STATUS = FD_STATUS_NOMORECHARS;
+ return(NULL);
+ }
+ }
+ while (1);
+
+ FD_LINE_END = eol - FD_BUF;
+ FD_LINE_LEN = FD_LINE_END - FD_LINE_BEGIN;
+ FD_LINE_TERMCHAR = FD_BUF[FD_LINE_END];
+ FD_BUF[FD_LINE_END] = '\0';
+#ifdef USE_FTELL_FOR_FILEPOS
+ if (FD_LINE_END==FD_BUF_END) {
+ /*
+ For VMS we cannot assume that the record is FD_LINE_LEN bytes long
+ on the disk. For stream_lf and stream_cr that is true, but not for
+ other formats, since VAXC/DECC converts the formatting into a single \n.
+ eg. variable format files have a 2-byte length and padding to an even
+ number of characters. So, we use ftell for each record.
+ This still will not work if we need to fseek to a \n or \r inside a
+ variable record (ftell always returns the start of the record in this
+ case).
+ (Tim Adye, adye@v2.rl.ac.uk)
+ */
+ FD_FILEPOS = ftell(FD_FILE);
+ } else
+#endif /* USE_FTELL_FOR_FILEPOS */
+ FD_FILEPOS += FD_LINE_LEN;
+
+ return(FD_BUF+FD_LINE_BEGIN);
+}
+
+/*----------------------------------------------------------*/
+/*
+ readline()
+ Read the next line in the postscript file.
+ Automatically skip over data (as indicated by
+ %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
+ comments.)
+ Also, skip over included documents (as indicated by
+ %%BeginDocument/%%EndDocument comments.)
+*/
+/*----------------------------------------------------------*/
+
+static char *readline (fd, lineP, positionP, line_lenP)
+ FileData fd;
+ char **lineP;
+ long *positionP;
+ unsigned int *line_lenP;
+{
+ unsigned int nbytes=0;
+ int skipped=0;
+ char *line;
+
+ if (positionP) *positionP = FD_FILEPOS;
+ line = ps_io_fgetchars(fd,-1);
+ if (!line) {
+ *line_lenP = 0;
+ *lineP = empty_string;
+ return(NULL);
+ }
+
+ *line_lenP = FD_LINE_LEN;
+
+#define IS_COMMENT(comment) \
+ (DSCcomment(line) && iscomment(line+2,(comment)))
+#define IS_BEGIN(comment) \
+ (iscomment(line+7,(comment)))
+#define SKIP_WHILE(cond) \
+ while (readline(fd, &line, NULL, &nbytes) && (cond)) *line_lenP += nbytes;\
+ skipped=1;
+#define SKIP_UNTIL_1(comment) { \
+ SKIP_WHILE((!IS_COMMENT(comment))) \
+ }
+#define SKIP_UNTIL_2(comment1,comment2) { \
+ SKIP_WHILE((!IS_COMMENT(comment1) && !IS_COMMENT(comment2)))\
+ }
+
+ if (!IS_COMMENT("Begin")) {} /* Do nothing */
+ else if IS_BEGIN("Document:") SKIP_UNTIL_1("EndDocument")
+ else if IS_BEGIN("Feature:") SKIP_UNTIL_1("EndFeature")
+#ifdef USE_ACROREAD_WORKAROUND
+ else if IS_BEGIN("File") SKIP_UNTIL_2("EndFile","EOF")
+#else
+ else if IS_BEGIN("File") SKIP_UNTIL_1("EndFile")
+#endif
+ else if IS_BEGIN("Font") SKIP_UNTIL_1("EndFont")
+ else if IS_BEGIN("ProcSet") SKIP_UNTIL_1("EndProcSet")
+ else if IS_BEGIN("Resource") SKIP_UNTIL_1("EndResource")
+ else if IS_BEGIN("Data:") {
+ int num;
+ char text[101];
+ if (FD_LINE_LEN > 100) FD_BUF[100] = '\0';
+ text[0] = '\0';
+ if (sscanf(line+length("%%BeginData:"), "%d %*s %s", &num, text) >= 1) {
+ if (strcmp(text, "Lines") == 0) {
+ while (num) {
+ line = ps_io_fgetchars(fd,-1);
+ if (line) *line_lenP += FD_LINE_LEN;
+ num--;
+ }
+ } else {
+ int read_chunk_size = LINE_CHUNK_SIZE;
+ while (num>0) {
+ if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
+ line = ps_io_fgetchars(fd,read_chunk_size);
+ if (line) *line_lenP += FD_LINE_LEN;
+ num -= read_chunk_size;
+ }
+ }
+ }
+ SKIP_UNTIL_1("EndData")
+ }
+ else if IS_BEGIN("Binary:") {
+ int num;
+ if (sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
+ int read_chunk_size = LINE_CHUNK_SIZE;
+ while (num>0) {
+ if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
+ line = ps_io_fgetchars(fd,read_chunk_size);
+ if (line) *line_lenP += FD_LINE_LEN;
+ num -= read_chunk_size;
+ }
+ SKIP_UNTIL_1("EndBinary")
+ }
+ }
+
+ if (skipped) {
+ *line_lenP += nbytes;
+ *lineP = skipped_line;
+ } else {
+ *lineP = FD_BUF+FD_LINE_BEGIN;
+ }
+
+ return(FD_BUF+FD_LINE_BEGIN);
+}
diff --git a/backend/ps/ps.h b/backend/ps/ps.h
new file mode 100644
index 0000000..5e27618
--- /dev/null
+++ b/backend/ps/ps.h
@@ -0,0 +1,107 @@
+/*
+ * ps.h -- Include file for PostScript routines.
+ * Copyright (C) 1992 Timothy O. Theisen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Tim Theisen Systems Programmer
+ * Internet: tim@cs.wisc.edu Department of Computer Sciences
+ * UUCP: uwvax!tim University of Wisconsin-Madison
+ * Phone: (608)262-0438 1210 West Dayton Street
+ * FAX: (608)262-9777 Madison, WI 53706
+ */
+#ifndef __GGV_PS_H__
+#define __GGV_PS_H__
+
+#include <stdio.h>
+
+#include <gsio.h>
+#include <gstypes.h>
+
+G_BEGIN_DECLS
+
+/* Constants used to index into the bounding box array. */
+#define LLX 0
+#define LLY 1
+#define URX 2
+#define URY 3
+
+/* Constants used to store keywords that are scanned. */
+/* NONE is not a keyword, it tells when a field was not set */
+
+enum { ATEND = -1, NONE = 0, ASCEND, DESCEND, SPECIAL };
+
+#define PSLINELENGTH 257 /* 255 characters + 1 newline + 1 NULL */
+
+struct document {
+ int epsf; /* Encapsulated PostScript flag. */
+ char *title; /* Title of document. */
+ char *date; /* Creation date. */
+ char *creator; /* Program that created the file */
+ int pageorder; /* ASCEND, DESCEND, SPECIAL */
+ long beginheader, endheader; /* offsets into file */
+ unsigned int lenheader;
+ long beginpreview, endpreview;
+ unsigned int lenpreview;
+ long begindefaults, enddefaults;
+ unsigned int lendefaults;
+ long beginprolog, endprolog;
+ unsigned int lenprolog;
+ long beginsetup, endsetup;
+ unsigned int lensetup;
+ long begintrailer, endtrailer;
+ unsigned int lentrailer;
+ int boundingbox[4];
+ int default_page_boundingbox[4];
+ int orientation; /* GTK_GS_ORIENTATION_PORTRAIT, GTK_GS_ORIENTATION_LANDSCAPE */
+ int default_page_orientation; /* GTK_GS_ORIENTATION_PORTRAIT, GTK_GS_ORIENTATION_LANDSCAPE */
+ unsigned int numsizes;
+ GtkGSPaperSize *size;
+ GtkGSPaperSize *default_page_size;
+ unsigned int numpages;
+ struct page *pages;
+};
+
+struct page {
+ char *label;
+ int boundingbox[4];
+ GtkGSPaperSize *size;
+ int orientation; /* GTK_GS_ORIENTATION_PORTRAIT, GTK_GS_ORIENTATION_LANDSCAPE */
+ long begin, end; /* offsets into file */
+ unsigned int len;
+};
+
+/* scans a PostScript file and return a pointer to the document
+ structure. Returns NULL if file does not Conform to commenting
+ conventions . */
+struct document *psscan(FILE * fileP, int respect_eof, const gchar * fname);
+
+/* free data structure malloc'ed by psscan */
+void psfree(struct document *);
+
+/* Copy a portion of the PostScript file */
+void pscopy(FILE * from, GtkGSDocSink * to, long begin, long end);
+
+/* Copy a portion of the PostScript file upto a comment */
+char *pscopyuntil(FILE * from, GtkGSDocSink * to, long begin, long end,
+ const char *comment);
+
+/* Copy the headers, marked pages, and trailer to fp */
+void pscopydoc(GtkGSDocSink * dest_file, char *src_filename,
+ struct document *d, int *pagelist);
+
+G_END_DECLS
+
+#endif /* __GGV_PS_H__ */