diff options
Diffstat (limited to 'backend/ps')
-rw-r--r-- | backend/ps/Makefile.am | 21 | ||||
-rw-r--r-- | backend/ps/gsdefaults.c | 67 | ||||
-rw-r--r-- | backend/ps/gsdefaults.h | 39 | ||||
-rw-r--r-- | backend/ps/gsio.c | 172 | ||||
-rw-r--r-- | backend/ps/gsio.h | 42 | ||||
-rw-r--r-- | backend/ps/gstypes.h | 48 | ||||
-rw-r--r-- | backend/ps/ps-document.c | 1339 | ||||
-rw-r--r-- | backend/ps/ps-document.h | 95 | ||||
-rw-r--r-- | backend/ps/ps.c | 1872 | ||||
-rw-r--r-- | backend/ps/ps.h | 107 |
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__ */ |