summaryrefslogtreecommitdiffstats
path: root/plug-ins/common/file-ps.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/common/file-ps.c')
-rw-r--r--plug-ins/common/file-ps.c3940
1 files changed, 3940 insertions, 0 deletions
diff --git a/plug-ins/common/file-ps.c b/plug-ins/common/file-ps.c
new file mode 100644
index 0000000..ee432a8
--- /dev/null
+++ b/plug-ins/common/file-ps.c
@@ -0,0 +1,3940 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * PostScript file plugin
+ * PostScript writing and GhostScript interfacing code
+ * Copyright (C) 1997-98 Peter Kirchgessner
+ * (email: peter@kirchgessner.net, WWW: http://www.kirchgessner.net)
+ *
+ * Added controls for TextAlphaBits and GraphicsAlphaBits
+ * George White <aa056@chebucto.ns.ca>
+ *
+ * Added Ascii85 encoding
+ * Austin Donnelly <austin@gimp.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 3 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, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Event history:
+ * V 0.90, PK, 28-Mar-97: Creation.
+ * V 0.91, PK, 03-Apr-97: Clip everything outside BoundingBox.
+ * 24-Apr-97: Multi page read support.
+ * V 1.00, PK, 30-Apr-97: PDF support.
+ * V 1.01, PK, 05-Oct-97: Parse rc-file.
+ * V 1.02, GW, 09-Oct-97: Antialiasing support.
+ * PK, 11-Oct-97: No progress bars when running non-interactive.
+ * New procedure file_ps_load_setargs to set
+ * load-arguments non-interactively.
+ * If GS_OPTIONS are not set, use at least "-dSAFER"
+ * V 1.03, nn, 20-Dec-97: Initialize some variables
+ * V 1.04, PK, 20-Dec-97: Add Encapsulated PostScript output and preview
+ * V 1.05, PK, 21-Sep-98: Write b/w-images (indexed) using image-operator
+ * V 1.06, PK, 22-Dec-98: Fix problem with writing color PS files.
+ * Ghostview may hang when displaying the files.
+ * V 1.07, PK, 14-Sep-99: Add resolution to image
+ * V 1.08, PK, 16-Jan-2000: Add PostScript-Level 2 by Austin Donnelly
+ * V 1.09, PK, 15-Feb-2000: Force showpage on EPS-files
+ * Add "RunLength" compression
+ * Fix problem with "Level 2" toggle
+ * V 1.10, PK, 15-Mar-2000: For load EPSF, allow negative Bounding Box Values
+ * Save PS: don't start lines of image data with %%
+ * to prevent problems with stupid PostScript
+ * analyzer programs (Stanislav Brabec)
+ * Add BeginData/EndData comments
+ * Save PS: Set default rotation to 0
+ * V 1.11, PK, 20-Aug-2000: Fix problem with BoundingBox recognition
+ * for Mac files.
+ * Fix problem with loop when reading not all
+ * images of a multi page file.
+ * PK, 31-Aug-2000: Load PS: Add checks for space in filename.
+ * V 1.12 PK, 19-Jun-2001: Fix problem with command line switch --
+ * (reported by Ferenc Wagner)
+ * V 1.13 PK, 07-Apr-2002: Fix problem with DOS binary EPS files
+ * V 1.14 PK, 14-May-2002: Workaround EPS files of Adb. Ill. 8.0
+ * V 1.15 PK, 04-Oct-2002: Be more accurate with using BoundingBox
+ * V 1.16 PK, 22-Jan-2004: Don't use popen(), use g_spawn_async_with_pipes()
+ * or g_spawn_sync().
+ * V 1.17 PK, 19-Sep-2004: Fix problem with interpretation of bounding box
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include <ghostscript/ierrors.h>
+#include <ghostscript/iapi.h>
+#include <ghostscript/gdevdsp.h>
+
+#define VERSIO 1.17
+static const gchar dversio[] = "v1.17 19-Sep-2004";
+
+#define LOAD_PS_PROC "file-ps-load"
+#define LOAD_EPS_PROC "file-eps-load"
+#define LOAD_PS_SETARGS_PROC "file-ps-load-setargs"
+#define LOAD_PS_THUMB_PROC "file-ps-load-thumb"
+#define SAVE_PS_PROC "file-ps-save"
+#define SAVE_EPS_PROC "file-eps-save"
+#define PLUG_IN_BINARY "file-ps"
+#define PLUG_IN_ROLE "gimp-file-ps"
+
+
+#define STR_LENGTH 64
+#define MIN_RESOLUTION 5
+#define MAX_RESOLUTION 8192
+
+/* Load info */
+typedef struct
+{
+ guint resolution; /* resolution (dpi) at which to run ghostscript */
+ guint width, height; /* desired size (ghostscript may ignore this) */
+ gboolean use_bbox; /* 0: use width/height, 1: try to use BoundingBox */
+ gchar pages[STR_LENGTH]; /* Pages to load (eg.: 1,3,5-7) */
+ gint pnm_type; /* 4: pbm, 5: pgm, 6: ppm, 7: automatic */
+ gint textalpha; /* antialiasing: 1,2, or 4 TextAlphaBits */
+ gint graphicsalpha; /* antialiasing: 1,2, or 4 GraphicsAlphaBits */
+} PSLoadVals;
+
+static PSLoadVals plvals =
+{
+ 100, /* 100 dpi */
+ 826, 1170, /* default width/height (A4) */
+ TRUE, /* try to use BoundingBox */
+ "1", /* pages to load */
+ 6, /* use ppm (color) */
+ 1, /* don't use text antialiasing */
+ 1 /* don't use graphics antialiasing */
+};
+
+/* Widgets for width and height of PostScript image to
+* be loaded, so that they can be updated when desired resolution is
+* changed
+*/
+static GtkWidget *ps_width_spinbutton;
+static GtkWidget *ps_height_spinbutton;
+
+/* Save info */
+typedef struct
+{
+ gdouble width, height; /* Size of image */
+ gdouble x_offset, y_offset; /* Offset to image on page */
+ gboolean unit_mm; /* Unit of measure (0: inch, 1: mm) */
+ gboolean keep_ratio; /* Keep aspect ratio */
+ gint rotate; /* Rotation (0, 90, 180, 270) */
+ gint level; /* PostScript Level */
+ gboolean eps; /* Encapsulated PostScript flag */
+ gboolean preview; /* Preview Flag */
+ gint preview_size; /* Preview size */
+} PSSaveVals;
+
+static PSSaveVals psvals =
+{
+ 287.0, 200.0, /* Image size (A4) */
+ 5.0, 5.0, /* Offset */
+ TRUE, /* Unit is mm */
+ TRUE, /* Keep edge ratio */
+ 0, /* Rotate */
+ 2, /* PostScript Level */
+ FALSE, /* Encapsulated PostScript flag */
+ FALSE, /* Preview flag */
+ 256 /* Preview size */
+};
+
+static const char hex[] = "0123456789abcdef";
+
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gboolean save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+static gboolean save_ps_header (GOutputStream *output,
+ GFile *file,
+ GError **error);
+static gboolean save_ps_setup (GOutputStream *output,
+ gint32 drawable_ID,
+ gint width,
+ gint height,
+ gint bpp,
+ GError **error);
+static gboolean save_ps_trailer (GOutputStream *output,
+ GError **error);
+
+static gboolean save_ps_preview (GOutputStream *output,
+ gint32 drawable_ID,
+ GError **error);
+
+static gboolean save_gray (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static gboolean save_bw (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static gboolean save_index (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static gboolean save_rgb (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+static gboolean print (GOutputStream *output,
+ GError **error,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (3, 4);
+
+static gint32 create_new_image (const gchar *filename,
+ guint pagenum,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ gint32 *layer_ID);
+
+static void check_load_vals (void);
+static void check_save_vals (void);
+
+static gint page_in_list (gchar *list,
+ guint pagenum);
+
+static gint get_bbox (const gchar *filename,
+ gint *x0,
+ gint *y0,
+ gint *x1,
+ gint *y1);
+
+static FILE * ps_open (const gchar *filename,
+ const PSLoadVals *loadopt,
+ gint *llx,
+ gint *lly,
+ gint *urx,
+ gint *ury,
+ gboolean *is_epsf,
+ gchar **tmp_filename);
+
+static void ps_close (FILE *ifp,
+ gchar *tmp_filename);
+
+static gboolean skip_ps (FILE *ifp);
+
+static gint32 load_ps (const gchar *filename,
+ guint pagenum,
+ FILE *ifp,
+ gint llx,
+ gint lly,
+ gint urx,
+ gint ury);
+
+static void dither_grey (const guchar *grey,
+ guchar *bw,
+ gint npix,
+ gint linecount);
+
+
+/* Dialog-handling */
+
+static gint32 count_ps_pages (const gchar *filename);
+static gboolean load_dialog (const gchar *filename);
+static void load_pages_entry_callback (GtkWidget *widget,
+ gpointer data);
+
+static gboolean resolution_change_callback (GtkAdjustment *adjustment,
+ gpointer data);
+
+typedef struct
+{
+ GtkAdjustment *adjustment[4];
+ gint level;
+} SaveDialogVals;
+
+static gboolean save_dialog (void);
+static void save_unit_toggle_update (GtkWidget *widget,
+ gpointer data);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+/* The run mode */
+static GimpRunMode l_run_mode;
+
+static void compress_packbits (int nin,
+ unsigned char *src,
+ int *nout,
+ unsigned char *dst);
+
+
+static guint32 ascii85_buf = 0;
+static gint ascii85_len = 0;
+static gint ascii85_linewidth = 0;
+
+static GimpPageSelectorTarget ps_pagemode = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
+
+static void
+ascii85_init (void)
+{
+ ascii85_len = 0;
+ ascii85_linewidth = 0;
+}
+
+static gboolean
+ascii85_flush (GOutputStream *output,
+ GError **error)
+{
+ gchar c[5];
+ gint i;
+ gboolean zero_case = (ascii85_buf == 0);
+ GString *string = g_string_new (NULL);
+
+ static gint max_linewidth = 75;
+
+ for (i = 4; i >= 0; i--)
+ {
+ c[i] = (ascii85_buf % 85) + '!';
+ ascii85_buf /= 85;
+ }
+
+ /* check for special case: "!!!!!" becomes "z", but only if not
+ * at end of data. */
+ if (zero_case && (ascii85_len == 4))
+ {
+ if (ascii85_linewidth >= max_linewidth)
+ {
+ g_string_append_c (string, '\n');
+
+ ascii85_linewidth = 0;
+ }
+
+ g_string_append_c (string, 'z');
+
+ ascii85_linewidth++;
+ }
+ else
+ {
+ for (i = 0; i < ascii85_len + 1; i++)
+ {
+ if ((ascii85_linewidth >= max_linewidth) && (c[i] != '%'))
+ {
+ g_string_append_c (string, '\n');
+
+ ascii85_linewidth = 0;
+ }
+
+ g_string_append_c (string, c[i]);
+
+ ascii85_linewidth++;
+ }
+ }
+
+ ascii85_len = 0;
+ ascii85_buf = 0;
+
+ if (string->len > 0 &&
+ ! g_output_stream_write_all (output,
+ string->str, string->len, NULL,
+ NULL, error))
+ {
+ g_string_free (string, TRUE);
+
+ return FALSE;
+ }
+
+ g_string_free (string, TRUE);
+
+ return TRUE;
+}
+
+static inline gboolean
+ascii85_out (GOutputStream *output,
+ guchar byte,
+ GError **error)
+{
+ if (ascii85_len == 4)
+ if (! ascii85_flush (output, error))
+ return FALSE;
+
+ ascii85_buf <<= 8;
+ ascii85_buf |= byte;
+ ascii85_len++;
+
+ return TRUE;
+}
+
+static gboolean
+ascii85_nout (GOutputStream *output,
+ gint n,
+ guchar *uptr,
+ GError **error)
+{
+ while (n-- > 0)
+ {
+ if (! ascii85_out (output, *uptr, error))
+ return FALSE;
+
+ uptr++;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ascii85_done (GOutputStream *output,
+ GError **error)
+{
+ if (ascii85_len)
+ {
+ /* zero any unfilled buffer portion, then flush */
+ ascii85_buf <<= (8 * (4 - ascii85_len));
+
+ if (! ascii85_flush (output, error))
+ return FALSE;
+ }
+
+ if (! print (output, error, "~>\n"))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static void
+compress_packbits (int nin,
+ unsigned char *src,
+ int *nout,
+ unsigned char *dst)
+
+{
+ unsigned char c;
+ int nrepeat, nliteral;
+ unsigned char *run_start;
+ unsigned char *start_dst = dst;
+ unsigned char *last_literal = NULL;
+
+ for (;;)
+ {
+ if (nin <= 0) break;
+
+ run_start = src;
+ c = *run_start;
+
+ /* Search repeat bytes */
+ if ((nin > 1) && (c == src[1]))
+ {
+ nrepeat = 1;
+ nin -= 2;
+ src += 2;
+ while ((nin > 0) && (c == *src))
+ {
+ nrepeat++;
+ src++;
+ nin--;
+ if (nrepeat == 127) break; /* Maximum repeat */
+ }
+
+ /* Add two-byte repeat to last literal run ? */
+ if ( (nrepeat == 1)
+ && (last_literal != NULL) && (((*last_literal)+1)+2 <= 128))
+ {
+ *last_literal += 2;
+ *(dst++) = c;
+ *(dst++) = c;
+ continue;
+ }
+
+ /* Add repeat run */
+ *(dst++) = (unsigned char)((-nrepeat) & 0xff);
+ *(dst++) = c;
+ last_literal = NULL;
+ continue;
+ }
+ /* Search literal bytes */
+ nliteral = 1;
+ nin--;
+ src++;
+
+ for (;;)
+ {
+ if (nin <= 0) break;
+
+ if ((nin >= 2) && (src[0] == src[1])) /* A two byte repeat ? */
+ break;
+
+ nliteral++;
+ nin--;
+ src++;
+ if (nliteral == 128) break; /* Maximum literal run */
+ }
+
+ /* Could be added to last literal run ? */
+ if ((last_literal != NULL) && (((*last_literal)+1)+nliteral <= 128))
+ {
+ *last_literal += nliteral;
+ }
+ else
+ {
+ last_literal = dst;
+ *(dst++) = (unsigned char)(nliteral-1);
+ }
+ while (nliteral-- > 0) *(dst++) = *(run_start++);
+ }
+ *nout = dst - start_dst;
+}
+
+
+typedef struct
+{
+ goffset eol;
+ goffset begin_data;
+} PS_DATA_POS;
+
+static PS_DATA_POS ps_data_pos = { 0, 0 };
+
+static gboolean
+ps_begin_data (GOutputStream *output,
+ GError **error)
+{
+ /* %%BeginData: 123456789012 ASCII Bytes */
+ if (! print (output, error, "%s", "%%BeginData: "))
+ return FALSE;
+
+ ps_data_pos.eol = g_seekable_tell (G_SEEKABLE (output));
+
+ if (! print (output, error, "\n"))
+ return FALSE;
+
+ ps_data_pos.begin_data = g_seekable_tell (G_SEEKABLE (output));
+
+ return TRUE;
+}
+
+static gboolean
+ps_end_data (GOutputStream *output,
+ GError **error)
+{
+ goffset end_data;
+ gchar s[64];
+
+ if ((ps_data_pos.begin_data > 0) && (ps_data_pos.eol > 0))
+ {
+ end_data = g_seekable_tell (G_SEEKABLE (output));
+
+ if (end_data > 0)
+ {
+ g_snprintf (s, sizeof (s),
+ "%"G_GOFFSET_FORMAT" ASCII Bytes", end_data - ps_data_pos.begin_data);
+
+ if (! g_seekable_seek (G_SEEKABLE (output),
+ ps_data_pos.eol - strlen (s), G_SEEK_SET,
+ NULL, error))
+ return FALSE;
+
+ if (! print (output, error, "%s", s))
+ return FALSE;
+
+ if (! g_seekable_seek (G_SEEKABLE (output),
+ end_data, G_SEEK_SET,
+ NULL, error))
+ return FALSE;
+ }
+ }
+
+ if (! print (output, error, "%s\n", "%%EndData"))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef set_load_args[] =
+ {
+ { GIMP_PDB_INT32, "resolution", "Resolution to interpret image (dpi)" },
+ { GIMP_PDB_INT32, "width", "Desired width" },
+ { GIMP_PDB_INT32, "height", "Desired height" },
+ { GIMP_PDB_INT32, "check-bbox", "0: Use width/height, 1: Use BoundingBox" },
+ { GIMP_PDB_STRING, "pages", "Pages to load (e.g.: 1,3,5-7)" },
+ { GIMP_PDB_INT32, "coloring", "4: b/w, 5: grey, 6: color image, 7: automatic" },
+ { GIMP_PDB_INT32, "text-alpha-bits", "1, 2, or 4" },
+ { GIMP_PDB_INT32, "graphic-alpha-bits", "1, 2, or 4" }
+ };
+
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
+ { GIMP_PDB_FLOAT, "width", "Width of the image in PostScript file (0: use input image size)" },
+ { GIMP_PDB_FLOAT, "height", "Height of image in PostScript file (0: use input image size)" },
+ { GIMP_PDB_FLOAT, "x-offset", "X-offset to image from lower left corner" },
+ { GIMP_PDB_FLOAT, "y-offset", "Y-offset to image from lower left corner" },
+ { GIMP_PDB_INT32, "unit", "Unit for width/height/offset. 0: inches, 1: millimeters" },
+ { GIMP_PDB_INT32, "keep-ratio", "0: use width/height, 1: keep aspect ratio" },
+ { GIMP_PDB_INT32, "rotation", "0, 90, 180, 270" },
+ { GIMP_PDB_INT32, "eps-flag", "0: PostScript, 1: Encapsulated PostScript" },
+ { GIMP_PDB_INT32, "preview", "0: no preview, >0: max. size of preview" },
+ { GIMP_PDB_INT32, "level", "1: PostScript Level 1, 2: PostScript Level 2" }
+ };
+
+ gimp_install_procedure (LOAD_PS_PROC,
+ "load PostScript documents",
+ "load PostScript documents",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ N_("PostScript document"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PS_PROC, "application/postscript");
+ gimp_register_magic_load_handler (LOAD_PS_PROC,
+ "ps",
+ "",
+ "0,string,%!,0,long,0xc5d0d3c6");
+
+ gimp_install_procedure (LOAD_EPS_PROC,
+ "load Encapsulated PostScript images",
+ "load Encapsulated PostScript images",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ N_("Encapsulated PostScript image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_EPS_PROC, "image/x-eps");
+ gimp_register_magic_load_handler (LOAD_EPS_PROC,
+ "eps",
+ "",
+ "0,string,%!,0,long,0xc5d0d3c6");
+
+ gimp_install_procedure (LOAD_PS_SETARGS_PROC,
+ "set additional parameters for procedure file-ps-load",
+ "set additional parameters for procedure file-ps-load",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (set_load_args), 0,
+ set_load_args, NULL);
+
+ gimp_install_procedure (LOAD_PS_THUMB_PROC,
+ "Loads a small preview from a PostScript or PDF document",
+ "",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ gimp_register_thumbnail_loader (LOAD_PS_PROC, LOAD_PS_THUMB_PROC);
+ gimp_register_thumbnail_loader (LOAD_EPS_PROC, LOAD_PS_THUMB_PROC);
+
+ gimp_install_procedure (SAVE_PS_PROC,
+ "export image as PostScript document",
+ "PostScript exporting handles all image types except "
+ "those with alpha channels.",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ N_("PostScript document"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PS_PROC, "application/postscript");
+ gimp_register_file_handler_uri (SAVE_PS_PROC);
+ gimp_register_save_handler (SAVE_PS_PROC, "ps", "");
+
+ gimp_install_procedure (SAVE_EPS_PROC,
+ "export image as Encapsulated PostScript image",
+ "PostScript exporting handles all image types except "
+ "those with alpha channels.",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ N_("Encapsulated PostScript image"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_EPS_PROC, "application/x-eps");
+ gimp_register_file_handler_uri (SAVE_EPS_PROC);
+ gimp_register_save_handler (SAVE_EPS_PROC, "eps", "");
+}
+
+static void
+ps_set_save_size (PSSaveVals *vals,
+ gint32 image_ID)
+{
+ gdouble xres, yres, factor, iw, ih;
+ guint width, height;
+ GimpUnit unit;
+
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+
+ if ((xres < 1e-5) || (yres < 1e-5))
+ xres = yres = 72.0;
+
+ /* Calculate size of image in inches */
+ width = gimp_image_width (image_ID);
+ height = gimp_image_height (image_ID);
+ iw = width / xres;
+ ih = height / yres;
+
+ unit = gimp_image_get_unit (image_ID);
+ factor = gimp_unit_get_factor (unit);
+
+ if (factor == 0.0254 ||
+ factor == 0.254 ||
+ factor == 2.54 ||
+ factor == 25.4)
+ {
+ vals->unit_mm = TRUE;
+ }
+
+ if (vals->unit_mm)
+ {
+ iw *= 25.4;
+ ih *= 25.4;
+ }
+
+ vals->width = iw;
+ vals->height = ih;
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID = -1;
+ gint32 drawable_ID = -1;
+ gint32 orig_image_ID = -1;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ l_run_mode = run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PS_PROC) == 0 ||
+ strcmp (name, LOAD_EPS_PROC) == 0)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (LOAD_PS_PROC, &plvals);
+
+ if (! load_dialog (param[1].data.d_string))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 3)
+ status = GIMP_PDB_CALLING_ERROR;
+ else /* Get additional interpretation arguments */
+ gimp_get_data (LOAD_PS_PROC, &plvals);
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (LOAD_PS_PROC, &plvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ check_load_vals ();
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ /* Store plvals data */
+ if (status == GIMP_PDB_SUCCESS)
+ gimp_set_data (LOAD_PS_PROC, &plvals, sizeof (PSLoadVals));
+ }
+ else if (strcmp (name, LOAD_PS_THUMB_PROC) == 0)
+ {
+ if (nparams < 2)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ gint size = param[1].data.d_int32;
+
+ /* We should look for an embedded preview but for now we
+ * just load the document at a small resolution and the
+ * first page only.
+ */
+
+ plvals.resolution = size / 4;
+ plvals.width = size;
+ plvals.height = size;
+ strncpy (plvals.pages, "1", sizeof (plvals.pages) - 1);
+
+ check_load_vals ();
+ image_ID = load_image (param[0].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else if (strcmp (name, SAVE_PS_PROC) == 0 ||
+ strcmp (name, SAVE_EPS_PROC) == 0)
+ {
+ psvals.eps = strcmp (name, SAVE_PS_PROC);
+
+ image_ID = orig_image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID,
+ psvals.eps ? "EPS" : "PostScript",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (name, &psvals);
+
+ ps_set_save_size (&psvals, orig_image_ID);
+
+ /* First acquire information with a dialog */
+ if (! save_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 15)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ psvals.width = param[5].data.d_float;
+ psvals.height = param[6].data.d_float;
+ psvals.x_offset = param[7].data.d_float;
+ psvals.y_offset = param[8].data.d_float;
+ psvals.unit_mm = (param[9].data.d_int32 != 0);
+ psvals.keep_ratio = (param[10].data.d_int32 != 0);
+ psvals.rotate = param[11].data.d_int32;
+ psvals.eps = (param[12].data.d_int32 != 0);
+ psvals.preview = (param[13].data.d_int32 != 0);
+ psvals.preview_size = param[13].data.d_int32;
+ psvals.level = param[14].data.d_int32;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (name, &psvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if ((psvals.width == 0.0) || (psvals.height == 0.0))
+ ps_set_save_size (&psvals, orig_image_ID);
+
+ check_save_vals ();
+
+ if (save_image (g_file_new_for_uri (param[3].data.d_string),
+ image_ID, drawable_ID,
+ &error))
+ {
+ /* Store psvals data */
+ gimp_set_data (name, &psvals, sizeof (PSSaveVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else if (strcmp (name, LOAD_PS_SETARGS_PROC) == 0)
+ {
+ /* Make sure all the arguments are there! */
+ if (nparams != 8)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ plvals.resolution = param[0].data.d_int32;
+ plvals.width = param[1].data.d_int32;
+ plvals.height = param[2].data.d_int32;
+ plvals.use_bbox = param[3].data.d_int32;
+ if (param[4].data.d_string != NULL)
+ strncpy (plvals.pages, param[4].data.d_string,
+ sizeof (plvals.pages));
+ else
+ plvals.pages[0] = '\0';
+ plvals.pages[sizeof (plvals.pages) - 1] = '\0';
+ plvals.pnm_type = param[5].data.d_int32;
+ plvals.textalpha = param[6].data.d_int32;
+ plvals.graphicsalpha = param[7].data.d_int32;
+ check_load_vals ();
+
+ gimp_set_data (LOAD_PS_PROC, &plvals, sizeof (PSLoadVals));
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ gint32 image_ID = 0;
+ gint32 *image_list, *nl;
+ guint page_count;
+ FILE *ifp;
+ gchar *temp;
+ gint llx, lly, urx, ury;
+ gint k, n_images, max_images, max_pagenum;
+ gboolean is_epsf;
+ GdkPixbuf *pixbuf = NULL;
+ gchar *tmp_filename = NULL;
+
+#ifdef PS_DEBUG
+ g_print ("load_image:\n resolution = %d\n", plvals.resolution);
+ g_print (" %dx%d pixels\n", plvals.width, plvals.height);
+ g_print (" BoundingBox: %d\n", plvals.use_bbox);
+ g_print (" Coloring: %d\n", plvals.pnm_type);
+ g_print (" TextAlphaBits: %d\n", plvals.textalpha);
+ g_print (" GraphicsAlphaBits: %d\n", plvals.graphicsalpha);
+#endif
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /* Try to see if PostScript file is available */
+ ifp = g_fopen (filename, "r");
+ if (ifp == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+ fclose (ifp);
+
+ ifp = ps_open (filename, &plvals, &llx, &lly, &urx, &ury, &is_epsf, &tmp_filename);
+ if (!ifp)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR,
+ _("Could not interpret PostScript file '%s'"),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+
+ image_list = g_new (gint32, 10);
+ n_images = 0;
+ max_images = 10;
+
+ max_pagenum = 9999; /* Try to get the maximum pagenumber to read */
+ if (is_epsf)
+ {
+ max_pagenum = 1;
+ /* Use pixbuf to load transparent EPS as PNGs */
+ pixbuf = gdk_pixbuf_new_from_file (tmp_filename, error);
+ if (! pixbuf)
+ return -1;
+ }
+
+ if (!page_in_list (plvals.pages, max_pagenum)) /* Is there a limit in list ? */
+ {
+ max_pagenum = -1;
+ for (temp = plvals.pages; *temp != '\0'; temp++)
+ {
+ if ((*temp < '0') || (*temp > '9'))
+ continue; /* Search next digit */
+ sscanf (temp, "%d", &k);
+ if (k > max_pagenum)
+ max_pagenum = k;
+ while ((*temp >= '0') && (*temp <= '9'))
+ temp++;
+ temp--;
+ }
+
+ if (max_pagenum < 1)
+ max_pagenum = 9999;
+ }
+
+ /* Load all images */
+ for (page_count = 1; page_count <= max_pagenum; page_count++)
+ {
+ if (page_in_list (plvals.pages, page_count))
+ {
+ image_ID = load_ps (filename, page_count, ifp, llx, lly, urx, ury);
+ if (image_ID == -1)
+ break;
+
+ gimp_image_set_resolution (image_ID,
+ (gdouble) plvals.resolution,
+ (gdouble) plvals.resolution);
+
+ if (n_images == max_images)
+ {
+ nl = (gint32 *) g_realloc (image_list,
+ (max_images+10)*sizeof (gint32));
+ if (nl == NULL) break;
+ image_list = nl;
+ max_images += 10;
+ }
+ image_list[n_images++] = image_ID;
+ }
+ else /* Skip an image */
+ {
+ image_ID = -1;
+ if (! skip_ps (ifp))
+ break;
+ }
+ }
+
+ ps_close (ifp, tmp_filename);
+
+ /* EPS are now imported using pngalpha, so they can be converted
+ * to a layer with gimp_layer_new_from_pixbuf () and exported at
+ * this part of the loading process
+ */
+ if (is_epsf)
+ {
+ gint32 layer;
+
+ image_ID = gimp_image_new (urx, ury, GIMP_RGB);
+
+ gimp_image_undo_disable (image_ID);
+
+ gimp_image_set_filename (image_ID, filename);
+ gimp_image_set_resolution (image_ID,
+ plvals.resolution,
+ plvals.resolution);
+
+ layer = gimp_layer_new_from_pixbuf (image_ID, _("Rendered EPS"), pixbuf,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID),
+ 0.0, 1.0);
+ gimp_image_insert_layer (image_ID, layer, -1, 0);
+
+ gimp_image_undo_enable (image_ID);
+
+ g_free (image_list);
+ g_object_unref (pixbuf);
+
+ return image_ID;
+ }
+
+ if (ps_pagemode == GIMP_PAGE_SELECTOR_TARGET_LAYERS)
+ {
+ for (k = 0; k < n_images; k++)
+ {
+ gchar *name;
+
+ if (k == 0)
+ {
+ image_ID = image_list[0];
+
+ name = g_strdup_printf (_("%s-pages"), filename);
+ gimp_image_set_filename (image_ID, name);
+ g_free (name);
+ }
+ else
+ {
+ gint32 current_layer;
+ gint32 tmp_ID;
+
+ tmp_ID = gimp_image_get_active_drawable (image_list[k]);
+
+ name = gimp_item_get_name (tmp_ID);
+
+ current_layer = gimp_layer_new_from_drawable (tmp_ID, image_ID);
+ gimp_item_set_name (current_layer, name);
+ gimp_image_insert_layer (image_ID, current_layer, -1, -1);
+ gimp_image_delete (image_list[k]);
+
+ g_free (name);
+ }
+ }
+
+ gimp_image_undo_enable (image_ID);
+ }
+ else
+ {
+ /* Display images in reverse order.
+ * The last will be displayed by GIMP itself
+ */
+ for (k = n_images - 1; k >= 0; k--)
+ {
+ gimp_image_undo_enable (image_list[k]);
+ gimp_image_clean_all (image_list[k]);
+
+ if (l_run_mode != GIMP_RUN_NONINTERACTIVE && k > 0)
+ gimp_display_new (image_list[k]);
+ }
+
+ image_ID = (n_images > 0) ? image_list[0] : -1;
+ }
+
+ g_free (image_list);
+
+ return image_ID;
+}
+
+
+static gboolean
+save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GOutputStream *output;
+ GCancellable *cancellable;
+ GimpImageType drawable_type;
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+
+ /* Make sure we're not exporting an image with an alpha channel */
+ if (gimp_drawable_has_alpha (drawable_ID))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("PostScript export cannot handle images with alpha channels"));
+ return FALSE;
+ }
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_GRAY_IMAGE:
+ case GIMP_RGB_IMAGE:
+ break;
+
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Cannot operate on unknown image types."));
+ return FALSE;
+ break;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (output)
+ {
+ GOutputStream *buffered;
+
+ buffered = g_buffered_output_stream_new (output);
+ g_object_unref (output);
+
+ output = buffered;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ if (! save_ps_header (output, file, error))
+ goto fail;
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ if (! save_index (output, image_ID, drawable_ID, error))
+ goto fail;
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ if (! save_gray (output, image_ID, drawable_ID, error))
+ goto fail;
+ break;
+
+ case GIMP_RGB_IMAGE:
+ if (! save_rgb (output, image_ID, drawable_ID, error))
+ goto fail;
+ break;
+
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ if (! save_ps_trailer (output, error))
+ goto fail;
+
+ if (! g_output_stream_close (output, NULL, error))
+ goto fail;
+
+ g_object_unref (output);
+
+ return TRUE;
+
+ fail:
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+
+ g_object_unref (output);
+ g_object_unref (cancellable);
+
+ return FALSE;
+}
+
+
+/* Check (and correct) the load values plvals */
+static void
+check_load_vals (void)
+{
+ if (plvals.resolution < MIN_RESOLUTION)
+ plvals.resolution = MIN_RESOLUTION;
+ else if (plvals.resolution > MAX_RESOLUTION)
+ plvals.resolution = MAX_RESOLUTION;
+
+ if (plvals.width < 2)
+ plvals.width = 2;
+ if (plvals.height < 2)
+ plvals.height = 2;
+ plvals.use_bbox = (plvals.use_bbox != 0);
+ if (plvals.pages[0] == '\0')
+ strncpy (plvals.pages, "1-99", sizeof (plvals.pages) - 1);
+ if ((plvals.pnm_type < 4) || (plvals.pnm_type > 7))
+ plvals.pnm_type = 6;
+ if ( (plvals.textalpha != 1) && (plvals.textalpha != 2)
+ && (plvals.textalpha != 4))
+ plvals.textalpha = 1;
+ if ( (plvals.graphicsalpha != 1) && (plvals.graphicsalpha != 2)
+ && (plvals.graphicsalpha != 4))
+ plvals.graphicsalpha = 1;
+}
+
+
+/* Check (and correct) the save values psvals */
+static void
+check_save_vals (void)
+{
+ int i;
+
+ i = psvals.rotate;
+ if ((i != 0) && (i != 90) && (i != 180) && (i != 270))
+ psvals.rotate = 90;
+ if (psvals.preview_size <= 0)
+ psvals.preview = FALSE;
+}
+
+
+/* Check if a page is in a given list */
+static gint
+page_in_list (gchar *list,
+ guint page_num)
+{
+ char tmplist[STR_LENGTH], *c0, *c1;
+ int state, start_num, end_num;
+#define READ_STARTNUM 0
+#define READ_ENDNUM 1
+#define CHK_LIST(a,b,c) {int low=(a),high=(b),swp; \
+ if ((low>0) && (high>0)) { \
+ if (low>high) {swp=low; low=high; high=swp;} \
+ if ((low<=(c))&&(high>=(c))) return (1); } }
+
+ if ((list == NULL) || (*list == '\0'))
+ return 1;
+
+ strncpy (tmplist, list, STR_LENGTH);
+ tmplist[STR_LENGTH-1] = '\0';
+
+ c0 = c1 = tmplist;
+ while (*c1) /* Remove all whitespace and break on unsupported characters */
+ {
+ if ((*c1 >= '0') && (*c1 <= '9'))
+ {
+ *(c0++) = *c1;
+ }
+ else if ((*c1 == '-') || (*c1 == ','))
+ { /* Try to remove double occurrences of these characters */
+ if (c0 == tmplist)
+ {
+ *(c0++) = *c1;
+ }
+ else
+ {
+ if (*(c0-1) != *c1)
+ *(c0++) = *c1;
+ }
+ }
+ else
+ break;
+
+ c1++;
+ }
+
+ if (c0 == tmplist)
+ return 1;
+
+ *c0 = '\0';
+
+ /* Now we have a comma separated list like 1-4-1,-3,1- */
+
+ start_num = end_num = -1;
+ state = READ_STARTNUM;
+ for (c0 = tmplist; *c0 != '\0'; c0++)
+ {
+ switch (state)
+ {
+ case READ_STARTNUM:
+ if (*c0 == ',')
+ {
+ if ((start_num > 0) && (start_num == (int)page_num))
+ return -1;
+ start_num = -1;
+ }
+ else if (*c0 == '-')
+ {
+ if (start_num < 0) start_num = 1;
+ state = READ_ENDNUM;
+ }
+ else /* '0' - '9' */
+ {
+ if (start_num < 0) start_num = 0;
+ start_num *= 10;
+ start_num += *c0 - '0';
+ }
+ break;
+
+ case READ_ENDNUM:
+ if (*c0 == ',')
+ {
+ if (end_num < 0) end_num = 9999;
+ CHK_LIST (start_num, end_num, (int)page_num);
+ start_num = end_num = -1;
+ state = READ_STARTNUM;
+ }
+ else if (*c0 == '-')
+ {
+ CHK_LIST (start_num, end_num, (int)page_num);
+ start_num = end_num;
+ end_num = -1;
+ }
+ else /* '0' - '9' */
+ {
+ if (end_num < 0) end_num = 0;
+ end_num *= 10;
+ end_num += *c0 - '0';
+ }
+ break;
+ }
+ }
+ if (state == READ_STARTNUM)
+ {
+ if (start_num > 0)
+ return (start_num == (int) page_num);
+ }
+ else
+ {
+ if (end_num < 0) end_num = 9999;
+ CHK_LIST (start_num, end_num, (int)page_num);
+ }
+
+ return 0;
+#undef CHK_LIST
+}
+
+
+/* A function like fgets, but treats single CR-character as line break. */
+/* As a line break the newline-character is returned. */
+static char *psfgets (char *s, int size, FILE *stream)
+
+{
+ int c;
+ char *sptr = s;
+
+ if (size <= 0)
+ return NULL;
+
+ if (size == 1)
+ {
+ *s = '\0';
+ return NULL;
+ }
+
+ c = getc (stream);
+ if (c == EOF)
+ return NULL;
+
+ for (;;)
+ {
+ /* At this point we have space in sptr for at least two characters */
+ if (c == '\n') /* Got end of line (UNIX line end) ? */
+ {
+ *(sptr++) = '\n';
+ break;
+ }
+ else if (c == '\r') /* Got a carriage return. Check next character */
+ {
+ c = getc (stream);
+ if ((c == EOF) || (c == '\n')) /* EOF or DOS line end ? */
+ {
+ *(sptr++) = '\n'; /* Return UNIX line end */
+ break;
+ }
+ else /* Single carriage return. Return UNIX line end. */
+ {
+ ungetc (c, stream); /* Save the extra character */
+ *(sptr++) = '\n';
+ break;
+ }
+ }
+ else /* no line end character */
+ {
+ *(sptr++) = (char)c;
+ size--;
+ }
+ if (size == 1)
+ break; /* Only space for the nul-character ? */
+
+ c = getc (stream);
+ if (c == EOF)
+ break;
+ }
+
+ *sptr = '\0';
+
+ return s;
+}
+
+
+/* Get the BoundingBox of a PostScript file. On success, 0 is returned. */
+/* On failure, -1 is returned. */
+static gint
+get_bbox (const gchar *filename,
+ gint *x0,
+ gint *y0,
+ gint *x1,
+ gint *y1)
+{
+ char line[1024], *src;
+ FILE *ifp;
+ int retval = -1;
+
+ ifp = g_fopen (filename, "rb");
+ if (ifp == NULL)
+ return -1;
+
+ for (;;)
+ {
+ if (psfgets (line, sizeof (line)-1, ifp) == NULL) break;
+ if ((line[0] != '%') || (line[1] != '%')) continue;
+ src = &(line[2]);
+ while ((*src == ' ') || (*src == '\t')) src++;
+ if (strncmp (src, "BoundingBox", 11) != 0) continue;
+ src += 11;
+ while ((*src == ' ') || (*src == '\t') || (*src == ':')) src++;
+ if (strncmp (src, "(atend)", 7) == 0) continue;
+ if (sscanf (src, "%d%d%d%d", x0, y0, x1, y1) == 4)
+ retval = 0;
+ break;
+ }
+ fclose (ifp);
+
+ return retval;
+}
+
+/* Open the PostScript file. On failure, NULL is returned. */
+/* The filepointer returned will give a PNM-file generated */
+/* by the PostScript-interpreter. */
+static FILE *
+ps_open (const gchar *filename,
+ const PSLoadVals *loadopt,
+ gint *llx,
+ gint *lly,
+ gint *urx,
+ gint *ury,
+ gboolean *is_epsf,
+ gchar **tmp_filename)
+{
+ const gchar *driver;
+ GPtrArray *cmdA;
+ gchar **pcmdA;
+ FILE *fd_popen = NULL;
+ FILE *eps_file;
+ gint width, height;
+ gint resolution;
+ gint x0, y0, x1, y1;
+ gint offx = 0;
+ gint offy = 0;
+ gboolean is_pdf;
+ gboolean maybe_epsf = FALSE;
+ int code;
+ void *instance = NULL;
+
+ resolution = loadopt->resolution;
+ *llx = *lly = 0;
+ width = loadopt->width;
+ height = loadopt->height;
+ *urx = width - 1;
+ *ury = height - 1;
+
+ /* Check if the file is a PDF. For PDF, we can't set geometry */
+ is_pdf = FALSE;
+
+ /* Check if it is a EPS-file */
+ *is_epsf = FALSE;
+
+ eps_file = g_fopen (filename, "rb");
+
+ if (eps_file != NULL)
+ {
+ gchar hdr[512];
+
+ fread (hdr, 1, sizeof(hdr), eps_file);
+ is_pdf = (strncmp (hdr, "%PDF", 4) == 0);
+
+ if (!is_pdf) /* Check for EPSF */
+ {
+ char *adobe, *epsf;
+ int ds = 0;
+ static unsigned char doseps[5] = { 0xc5, 0xd0, 0xd3, 0xc6, 0 };
+
+ hdr[sizeof(hdr)-1] = '\0';
+ adobe = strstr (hdr, "PS-Adobe-");
+ epsf = strstr (hdr, "EPSF-");
+
+ if ((adobe != NULL) && (epsf != NULL))
+ ds = epsf - adobe;
+
+ *is_epsf = ((ds >= 11) && (ds <= 15));
+
+ /* Illustrator uses negative values in BoundingBox without marking */
+ /* files as EPSF. Try to handle that. */
+ maybe_epsf =
+ (strstr (hdr, "%%Creator: Adobe Illustrator(R) 8.0") != 0);
+
+ /* Check DOS EPS binary file */
+ if ((!*is_epsf) && (strncmp (hdr, (char *)doseps, 4) == 0))
+ *is_epsf = 1;
+ }
+
+ fclose (eps_file);
+ }
+
+ if ((!is_pdf) && (loadopt->use_bbox)) /* Try the BoundingBox ? */
+ {
+ if (get_bbox (filename, &x0, &y0, &x1, &y1) == 0)
+ {
+ if (maybe_epsf && ((x0 < 0) || (y0 < 0)))
+ *is_epsf = 1;
+
+ if (*is_epsf) /* Handle negative BoundingBox for EPSF */
+ {
+ offx = -x0; x1 += offx; x0 += offx;
+ offy = -y0; y1 += offy; y0 += offy;
+ }
+ if ((x0 >= 0) && (y0 >= 0) && (x1 > x0) && (y1 > y0))
+ {
+ *llx = (int)((x0/72.0) * resolution + 0.0001);
+ *lly = (int)((y0/72.0) * resolution + 0.0001);
+ /* Use upper bbox values as image size */
+ width = (int)((x1/72.0) * resolution + 0.5);
+ height = (int)((y1/72.0) * resolution + 0.5);
+ /* Pixel coordinates must be one less */
+ *urx = width - 1;
+ *ury = height - 1;
+ if (*urx < *llx) *urx = *llx;
+ if (*ury < *lly) *ury = *lly;
+ }
+ }
+ }
+
+ switch (loadopt->pnm_type)
+ {
+ case 4:
+ driver = "pbmraw";
+ break;
+ case 5:
+ driver = "pgmraw";
+ break;
+ case 7:
+ driver = "pnmraw";
+ break;
+ default:
+ driver = "ppmraw";
+ break;
+ }
+
+ /* For instance, the Win32 port of ghostscript doesn't work correctly when
+ * using standard output as output file.
+ * Thus, use a real output file.
+ */
+ if (*is_epsf)
+ {
+ driver = "pngalpha";
+ *tmp_filename = gimp_temp_name ("png");
+ }
+ else
+ {
+ *tmp_filename = gimp_temp_name ("pnm");
+ }
+
+ /* Build command array */
+ cmdA = g_ptr_array_new ();
+
+ g_ptr_array_add (cmdA, g_strdup (g_get_prgname ()));
+ g_ptr_array_add (cmdA, g_strdup_printf ("-sDEVICE=%s", driver));
+ g_ptr_array_add (cmdA, g_strdup_printf ("-r%d", resolution));
+
+ if (is_pdf)
+ {
+ /* Acrobat Reader honors CropBox over MediaBox, so let's match that
+ * behavior.
+ */
+ g_ptr_array_add (cmdA, g_strdup ("-dUseCropBox"));
+ }
+ else
+ {
+ /* For PDF, we can't set geometry */
+ g_ptr_array_add (cmdA, g_strdup_printf ("-g%dx%d", width, height));
+ }
+
+ /* Antialiasing not available for PBM-device */
+ if ((loadopt->pnm_type != 4) && (loadopt->textalpha != 1))
+ g_ptr_array_add (cmdA, g_strdup_printf ("-dTextAlphaBits=%d",
+ loadopt->textalpha));
+ if ((loadopt->pnm_type != 4) && (loadopt->graphicsalpha != 1))
+ g_ptr_array_add (cmdA, g_strdup_printf ("-dGraphicsAlphaBits=%d",
+ loadopt->graphicsalpha));
+ g_ptr_array_add (cmdA, g_strdup ("-q"));
+ g_ptr_array_add (cmdA, g_strdup ("-dBATCH"));
+ g_ptr_array_add (cmdA, g_strdup ("-dNOPAUSE"));
+
+ /* If no additional options specified, use at least -dSAFER */
+ if (g_getenv ("GS_OPTIONS") == NULL)
+ g_ptr_array_add (cmdA, g_strdup ("-dSAFER"));
+
+ /* Output file name */
+ g_ptr_array_add (cmdA, g_strdup_printf ("-sOutputFile=%s", *tmp_filename));
+
+ /* Offset command for gs to get image part with negative x/y-coord. */
+ if ((offx != 0) || (offy != 0))
+ {
+ g_ptr_array_add (cmdA, g_strdup ("-c"));
+ g_ptr_array_add (cmdA, g_strdup_printf ("%d", offx));
+ g_ptr_array_add (cmdA, g_strdup_printf ("%d", offy));
+ g_ptr_array_add (cmdA, g_strdup ("translate"));
+ }
+
+ /* input file name */
+ g_ptr_array_add (cmdA, g_strdup ("-f"));
+ g_ptr_array_add (cmdA, g_strdup (filename));
+
+ if (*is_epsf)
+ {
+ g_ptr_array_add (cmdA, g_strdup ("-c"));
+ g_ptr_array_add (cmdA, g_strdup ("showpage"));
+ }
+
+ g_ptr_array_add (cmdA, g_strdup ("-c"));
+ g_ptr_array_add (cmdA, g_strdup ("quit"));
+ g_ptr_array_add (cmdA, NULL);
+
+ pcmdA = (gchar **) cmdA->pdata;
+
+#ifdef PS_DEBUG
+ {
+ gchar **p = pcmdA;
+ g_print ("Passing args (argc=%d):\n", cmdA->len - 1);
+
+ while (*p)
+ {
+ g_print ("%s\n", *p);
+ p++;
+ }
+ }
+#endif
+
+ code = gsapi_new_instance (&instance, NULL);
+ if (code == 0) {
+ code = gsapi_set_arg_encoding(instance, GS_ARG_ENCODING_UTF8);
+ code = gsapi_init_with_args (instance, cmdA->len - 1, pcmdA);
+ code = gsapi_exit (instance);
+ gsapi_delete_instance (instance);
+ }
+
+ /* Don't care about exit status of ghostscript. */
+ /* Just try to read what it wrote. */
+
+ fd_popen = g_fopen (*tmp_filename, "rb");
+
+ g_ptr_array_free (cmdA, FALSE);
+ g_strfreev (pcmdA);
+
+ return fd_popen;
+}
+
+
+/* Close the PNM-File of the PostScript interpreter */
+static void
+ps_close (FILE *ifp, gchar *tmp_filename)
+{
+ /* If a real outputfile was used, close the file and remove it. */
+ fclose (ifp);
+ g_unlink (tmp_filename);
+}
+
+
+/* Read the header of a raw PNM-file and return type (4-6) or -1 on failure */
+static gint
+read_pnmraw_type (FILE *ifp,
+ gint *width,
+ gint *height,
+ gint *maxval)
+{
+ int frst, scnd, thrd;
+ gint pnmtype;
+ gchar line[1024];
+
+ /* GhostScript may write some informational messages infront of the header. */
+ /* We are just looking at a Px\n in the input stream. */
+ frst = getc (ifp);
+ scnd = getc (ifp);
+ thrd = getc (ifp);
+ for (;;)
+ {
+ if (thrd == EOF) return -1;
+#ifdef G_OS_WIN32
+ if (thrd == '\r') thrd = getc (ifp);
+#endif
+ if ((thrd == '\n') && (frst == 'P') && (scnd >= '1') && (scnd <= '6'))
+ break;
+ frst = scnd;
+ scnd = thrd;
+ thrd = getc (ifp);
+ }
+ pnmtype = scnd - '0';
+ /* We don't use the ASCII-versions */
+ if ((pnmtype >= 1) && (pnmtype <= 3))
+ return -1;
+
+ /* Read width/height */
+ for (;;)
+ {
+ if (fgets (line, sizeof (line)-1, ifp) == NULL)
+ return -1;
+ if (line[0] != '#')
+ break;
+ }
+ if (sscanf (line, "%d%d", width, height) != 2)
+ return -1;
+
+ *maxval = 255;
+
+ if (pnmtype != 4) /* Read maxval */
+ {
+ for (;;)
+ {
+ if (fgets (line, sizeof (line)-1, ifp) == NULL)
+ return -1;
+ if (line[0] != '#')
+ break;
+ }
+ if (sscanf (line, "%d", maxval) != 1)
+ return -1;
+ }
+
+ return pnmtype;
+}
+
+
+/* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
+static gint32
+create_new_image (const gchar *filename,
+ guint pagenum,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ gint32 *layer_ID)
+{
+ gint32 image_ID;
+ GimpImageType gdtype;
+ gchar *tmp;
+
+ switch (type)
+ {
+ case GIMP_GRAY:
+ gdtype = GIMP_GRAY_IMAGE;
+ break;
+ case GIMP_INDEXED:
+ gdtype = GIMP_INDEXED_IMAGE;
+ break;
+ case GIMP_RGB:
+ default:
+ gdtype = GIMP_RGB_IMAGE;
+ }
+
+ image_ID = gimp_image_new_with_precision (width, height, type,
+ GIMP_PRECISION_U8_GAMMA);
+ gimp_image_undo_disable (image_ID);
+
+ tmp = g_strdup_printf ("%s-%d", filename, pagenum);
+ gimp_image_set_filename (image_ID, tmp);
+ g_free (tmp);
+
+ tmp = g_strdup_printf (_("Page %d"), pagenum);
+ *layer_ID = gimp_layer_new (image_ID, tmp, width, height,
+ gdtype,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ g_free (tmp);
+
+ gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
+
+ return image_ID;
+}
+
+
+/* Skip PNM image generated from PostScript file. */
+/* Return TRUE on success, FALSE otherwise. */
+static gboolean
+skip_ps (FILE *ifp)
+{
+ guchar buf[8192];
+ gsize len;
+ gint pnmtype, width, height, maxval, bpl;
+
+ pnmtype = read_pnmraw_type (ifp, &width, &height, &maxval);
+
+ if (pnmtype == 4) /* Portable bitmap */
+ bpl = (width + 7) / 8;
+ else if (pnmtype == 5)
+ bpl = width;
+ else if (pnmtype == 6)
+ bpl = width * 3;
+ else
+ return FALSE;
+
+ len = bpl * height;
+ while (len)
+ {
+ gsize bytes = fread (buf, 1, MIN (len, sizeof (buf)), ifp);
+
+ if (bytes < MIN (len, sizeof (buf)))
+ return FALSE;
+
+ len -= bytes;
+ }
+
+ return TRUE;
+}
+
+
+/* Load PNM image generated from PostScript file */
+static gint32
+load_ps (const gchar *filename,
+ guint pagenum,
+ FILE *ifp,
+ gint llx,
+ gint lly,
+ gint urx,
+ gint ury)
+{
+ guchar *dest;
+ guchar *data, *bitline = NULL, *byteline = NULL, *byteptr, *temp;
+ guchar bit2byte[256*8];
+ int width, height, tile_height, scan_lines, total_scan_lines;
+ int image_width, image_height;
+ int skip_left, skip_bottom;
+ int i, j, pnmtype, maxval, bpp, nread;
+ GimpImageBaseType imagetype;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer = NULL;
+ int err = 0, e;
+
+ pnmtype = read_pnmraw_type (ifp, &width, &height, &maxval);
+
+ if ((width == urx+1) && (height == ury+1)) /* gs respected BoundingBox ? */
+ {
+ skip_left = llx; skip_bottom = lly;
+ image_width = width - skip_left;
+ image_height = height - skip_bottom;
+ }
+ else
+ {
+ skip_left = skip_bottom = 0;
+ image_width = width;
+ image_height = height;
+ }
+ if (pnmtype == 4) /* Portable Bitmap */
+ {
+ imagetype = GIMP_INDEXED;
+ nread = (width+7)/8;
+ bpp = 1;
+ bitline = g_new (guchar, nread);
+ byteline = g_new (guchar, nread * 8);
+
+ /* Get an array for mapping 8 bits in a byte to 8 bytes */
+ temp = bit2byte;
+ for (j = 0; j < 256; j++)
+ for (i = 7; i >= 0; i--)
+ *(temp++) = ((j & (1 << i)) != 0);
+ }
+ else if (pnmtype == 5) /* Portable Greymap */
+ {
+ imagetype = GIMP_GRAY;
+ nread = width;
+ bpp = 1;
+ byteline = g_new (guchar, nread);
+ }
+ else if (pnmtype == 6) /* Portable Pixmap */
+ {
+ imagetype = GIMP_RGB;
+ nread = width * 3;
+ bpp = 3;
+ byteline = g_new (guchar, nread);
+ }
+ else
+ return -1;
+
+ image_ID = create_new_image (filename, pagenum,
+ image_width, image_height, imagetype,
+ &layer_ID);
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * image_width * bpp);
+
+ dest = data;
+ total_scan_lines = scan_lines = 0;
+
+ if (pnmtype == 4) /* Read bitimage ? Must be mapped to indexed */
+ {
+ const guchar BWColorMap[2*3] = { 255, 255, 255, 0, 0, 0 };
+
+ gimp_image_set_colormap (image_ID, BWColorMap, 2);
+
+ for (i = 0; i < height; i++)
+ {
+ e = (fread (bitline, 1, nread, ifp) != nread);
+ if (total_scan_lines >= image_height)
+ continue;
+ err |= e;
+ if (err)
+ break;
+
+ j = width; /* Map 1 byte of bitimage to 8 bytes of indexed image */
+ temp = bitline;
+ byteptr = byteline;
+ while (j >= 8)
+ {
+ memcpy (byteptr, bit2byte + *(temp++)*8, 8);
+ byteptr += 8;
+ j -= 8;
+ }
+ if (j > 0)
+ memcpy (byteptr, bit2byte + *temp*8, j);
+
+ memcpy (dest, byteline+skip_left, image_width);
+ dest += image_width;
+ scan_lines++;
+ total_scan_lines++;
+
+ if ((scan_lines == tile_height) || ((i + 1) == image_height))
+ {
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, i-scan_lines+1,
+ image_width, scan_lines),
+ 0,
+ NULL,
+ data,
+ GEGL_AUTO_ROWSTRIDE);
+ scan_lines = 0;
+ dest = data;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) image_height);
+
+ if (err)
+ break;
+ }
+ }
+ else /* Read gray/rgb-image */
+ {
+ for (i = 0; i < height; i++)
+ {
+ e = (fread (byteline, bpp, width, ifp) != width);
+ if (total_scan_lines >= image_height)
+ continue;
+ err |= e;
+ if (err)
+ break;
+
+ memcpy (dest, byteline+skip_left*bpp, image_width*bpp);
+ dest += image_width*bpp;
+ scan_lines++;
+ total_scan_lines++;
+
+ if ((scan_lines == tile_height) || ((i + 1) == image_height))
+ {
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, i-scan_lines+1,
+ image_width, scan_lines),
+ 0,
+ NULL,
+ data,
+ GEGL_AUTO_ROWSTRIDE);
+ scan_lines = 0;
+ dest = data;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) image_height);
+
+ if (err)
+ break;
+ }
+ }
+ gimp_progress_update (1.0);
+
+ g_free (data);
+ g_free (byteline);
+ g_free (bitline);
+
+ if (err)
+ g_message ("EOF encountered on reading");
+
+ g_object_unref (buffer);
+
+ return (err ? -1 : image_ID);
+}
+
+
+/* Write out the PostScript file header */
+static gboolean
+save_ps_header (GOutputStream *output,
+ GFile *file,
+ GError **error)
+{
+ gchar *basename = g_path_get_basename (gimp_file_get_utf8_name (file));
+ time_t cutime = time (NULL);
+
+ if (! print (output, error,
+ "%%!PS-Adobe-3.0%s\n", psvals.eps ? " EPSF-3.0" : ""))
+ goto fail;
+
+ if (! print (output, error,
+ "%%%%Creator: GIMP PostScript file plug-in V %4.2f "
+ "by Peter Kirchgessner\n", VERSIO))
+ goto fail;
+
+ if (! print (output, error,
+ "%%%%Title: %s\n"
+ "%%%%CreationDate: %s"
+ "%%%%DocumentData: Clean7Bit\n",
+ basename, ctime (&cutime)))
+ goto fail;
+
+ if (psvals.eps || (psvals.level > 1))
+ if (! print (output, error,"%%%%LanguageLevel: 2\n"))
+ goto fail;
+
+ if (! print (output, error, "%%%%Pages: 1\n"))
+ goto fail;
+
+ g_free (basename);
+
+ return TRUE;
+
+ fail:
+
+ g_free (basename);
+
+ return FALSE;
+}
+
+
+/* Write out transformation for image */
+static gboolean
+save_ps_setup (GOutputStream *output,
+ gint32 drawable_ID,
+ gint width,
+ gint height,
+ gint bpp,
+ GError **error)
+{
+ gdouble x_offset, y_offset, x_size, y_size;
+ gdouble urx, ury;
+ gdouble width_inch, height_inch;
+ gdouble f1, f2, dx, dy;
+ gint xtrans, ytrans;
+ gint i_urx, i_ury;
+ gchar tmpbuf1[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar tmpbuf2[G_ASCII_DTOSTR_BUF_SIZE];
+
+ /* initialize */
+
+ dx = 0.0;
+ dy = 0.0;
+
+ x_offset = psvals.x_offset;
+ y_offset = psvals.y_offset;
+ width_inch = fabs (psvals.width);
+ height_inch = fabs (psvals.height);
+
+ if (psvals.unit_mm)
+ {
+ x_offset /= 25.4; y_offset /= 25.4;
+ width_inch /= 25.4; height_inch /= 25.4;
+ }
+
+ if (psvals.keep_ratio) /* Proportions to keep ? */
+ { /* Fit the image into the allowed size */
+ f1 = width_inch / width;
+ f2 = height_inch / height;
+ if (f1 < f2)
+ height_inch = width_inch * (gdouble) height / (gdouble) width;
+ else
+ width_inch = fabs (height_inch) * (gdouble) width / (gdouble) height;
+ }
+
+ if ((psvals.rotate == 0) || (psvals.rotate == 180))
+ {
+ x_size = width_inch;
+ y_size = height_inch;
+ }
+ else
+ {
+ y_size = width_inch;
+ x_size = height_inch;
+ }
+
+ /* Round up upper right corner only for non-integer values */
+ urx = (x_offset + x_size) * 72.0;
+ ury = (y_offset + y_size) * 72.0;
+ i_urx = (gint) urx;
+ i_ury = (gint) ury;
+ if (urx != (gdouble) i_urx) i_urx++; /* Check for non-integer value */
+ if (ury != (gdouble) i_ury) i_ury++;
+
+ if (! print (output, error,
+ "%%%%BoundingBox: %d %d %d %d\n"
+ "%%%%EndComments\n",
+ (gint) (x_offset * 72.0), (gint) (y_offset * 72.0), i_urx, i_ury))
+ return FALSE;
+
+ if (psvals.preview && (psvals.preview_size > 0))
+ {
+ if (! save_ps_preview (output, drawable_ID, error))
+ return FALSE;
+ }
+
+ if (! print (output, error,
+ "%%%%BeginProlog\n"
+ "%% Use own dictionary to avoid conflicts\n"
+ "10 dict begin\n"
+ "%%%%EndProlog\n"
+ "%%%%Page: 1 1\n"
+ "%% Translate for offset\n"
+ "%s %s translate\n",
+ g_ascii_dtostr (tmpbuf1, sizeof (tmpbuf1), x_offset * 72.0),
+ g_ascii_dtostr (tmpbuf2, sizeof (tmpbuf2), y_offset * 72.0)))
+ return FALSE;
+
+ /* Calculate translation to startpoint of first scanline */
+ switch (psvals.rotate)
+ {
+ case 0: dx = 0.0; dy = y_size * 72.0;
+ break;
+ case 90: dx = dy = 0.0;
+ break;
+ case 180: dx = x_size * 72.0; dy = 0.0;
+ break;
+ case 270: dx = x_size * 72.0; dy = y_size * 72.0;
+ break;
+ }
+
+ if ((dx != 0.0) || (dy != 0.0))
+ {
+ if (! print (output, error,
+ "%% Translate to begin of first scanline\n"
+ "%s %s translate\n",
+ g_ascii_dtostr (tmpbuf1, sizeof (tmpbuf1), dx),
+ g_ascii_dtostr (tmpbuf2, sizeof (tmpbuf2), dy)))
+ return FALSE;
+ }
+
+ if (psvals.rotate)
+ if (! print (output, error, "%d rotate\n", (gint) psvals.rotate))
+ return FALSE;
+
+ if (! print (output, error,
+ "%s %s scale\n",
+ g_ascii_dtostr (tmpbuf1, sizeof (tmpbuf1), 72.0 * width_inch),
+ g_ascii_dtostr (tmpbuf2, sizeof (tmpbuf2), -72.0 * height_inch)))
+ return FALSE;
+
+ /* Write the PostScript procedures to read the image */
+ if (psvals.level <= 1)
+ {
+ if (! print (output, error,
+ "%% Variable to keep one line of raster data\n"))
+ return FALSE;
+
+ if (bpp == 1)
+ {
+ if (! print (output, error,
+ "/scanline %d string def\n", (width + 7) / 8))
+ return FALSE;
+ }
+ else
+ {
+ if (! print (output, error,
+ "/scanline %d %d mul string def\n", width, bpp / 8))
+ return FALSE;
+ }
+ }
+
+ if (! print (output, error,
+ "%% Image geometry\n%d %d %d\n"
+ "%% Transformation matrix\n",
+ width, height, (bpp == 1) ? 1 : 8))
+ return FALSE;
+
+ xtrans = ytrans = 0;
+ if (psvals.width < 0.0) { width = -width; xtrans = -width; }
+ if (psvals.height < 0.0) { height = -height; ytrans = -height; }
+
+ if (! print (output, error,
+ "[ %d 0 0 %d %d %d ]\n", width, height, xtrans, ytrans))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+save_ps_trailer (GOutputStream *output,
+ GError **error)
+{
+ return print (output, error,
+ "%%%%Trailer\n"
+ "end\n%%%%EOF\n");
+}
+
+/* Do a Floyd-Steinberg dithering on a grayscale scanline. */
+/* linecount must keep the counter for the actual scanline (0, 1, 2, ...). */
+/* If linecount is less than zero, all used memory is freed. */
+
+static void
+dither_grey (const guchar *grey,
+ guchar *bw,
+ gint npix,
+ gint linecount)
+{
+ static gboolean do_init_arrays = TRUE;
+ static gint *fs_error = NULL;
+ static gint limit[1278];
+ static gint east_error[256];
+ static gint seast_error[256];
+ static gint south_error[256];
+ static gint swest_error[256];
+
+ const guchar *greyptr;
+ guchar *bwptr, mask;
+ gint *fse;
+ gint x, greyval, fse_inline;
+
+ if (linecount <= 0)
+ {
+ g_free (fs_error);
+
+ if (linecount < 0)
+ return;
+
+ fs_error = g_new0 (gint, npix + 2);
+
+ /* Initialize some arrays that speed up dithering */
+ if (do_init_arrays)
+ {
+ gint i;
+
+ do_init_arrays = FALSE;
+
+ for (i = 0, x = -511; x <= 766; i++, x++)
+ limit[i] = (x < 0) ? 0 : ((x > 255) ? 255 : x);
+
+ for (greyval = 0; greyval < 256; greyval++)
+ {
+ east_error[greyval] = (greyval < 128) ?
+ ((greyval * 79) >> 8) : (((greyval - 255) * 79) >> 8);
+ seast_error[greyval] = (greyval < 128) ?
+ ((greyval * 34) >> 8) : (((greyval - 255) * 34) >> 8);
+ south_error[greyval] = (greyval < 128) ?
+ ((greyval * 56) >> 8) : (((greyval - 255) * 56) >> 8);
+ swest_error[greyval] = (greyval < 128) ?
+ ((greyval * 12) >> 8) : (((greyval - 255) * 12) >> 8);
+ }
+ }
+ }
+
+ g_return_if_fail (fs_error != NULL);
+
+ memset (bw, 0, (npix + 7) / 8); /* Initialize with white */
+
+ greyptr = grey;
+ bwptr = bw;
+ mask = 0x80;
+
+ fse_inline = fs_error[1];
+
+ for (x = 0, fse = fs_error + 1; x < npix; x++, fse++)
+ {
+ greyval =
+ limit[*(greyptr++) + fse_inline + 512]; /* 0 <= greyval <= 255 */
+
+ if (greyval < 128)
+ *bwptr |= mask; /* Set a black pixel */
+
+ /* Error distribution */
+ fse_inline = east_error[greyval] + fse[1];
+ fse[1] = seast_error[greyval];
+ fse[0] += south_error[greyval];
+ fse[-1] += swest_error[greyval];
+
+ mask >>= 1; /* Get mask for next b/w-pixel */
+
+ if (!mask)
+ {
+ mask = 0x80;
+ bwptr++;
+ }
+ }
+}
+
+/* Write a device independent screen preview */
+static gboolean
+save_ps_preview (GOutputStream *output,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GimpImageType drawable_type;
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint bpp;
+ guchar *bwptr, *greyptr;
+ gint width, height, x, y, nbsl, out_count;
+ gint nchar_pl = 72, src_y;
+ gdouble f1, f2;
+ guchar *grey, *bw, *src_row, *src_ptr;
+ guchar *cmap;
+ gint ncols, cind;
+
+ if (psvals.preview_size <= 0)
+ return TRUE;
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ cmap = NULL;
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+ switch (drawable_type)
+ {
+ case GIMP_GRAY_IMAGE:
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ cmap = gimp_image_get_colormap (gimp_item_get_image (drawable_ID),
+ &ncols);
+ format = gimp_drawable_get_format (drawable_ID);
+ break;
+
+ case GIMP_RGB_IMAGE:
+ default:
+ format = babl_format ("R'G'B' u8");
+ break;
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ /* Calculate size of preview */
+ if ((width > psvals.preview_size) ||
+ (height > psvals.preview_size))
+ {
+ f1 = (gdouble) psvals.preview_size / (gdouble) width;
+ f2 = (gdouble) psvals.preview_size / (gdouble) height;
+
+ if (f1 < f2)
+ {
+ width = psvals.preview_size;
+ height *= f1;
+ if (height <= 0)
+ height = 1;
+ }
+ else
+ {
+ height = psvals.preview_size;
+ width *= f1;
+ if (width <= 0)
+ width = 1;
+ }
+ }
+
+ nbsl = (width + 7) / 8; /* Number of bytes per scanline in bitmap */
+
+ grey = g_new (guchar, width);
+ bw = g_new (guchar, nbsl);
+ src_row = g_new (guchar, gegl_buffer_get_width (buffer) * bpp);
+
+ if (! print (output, error,
+ "%%%%BeginPreview: %d %d 1 %d\n",
+ width, height,
+ ((nbsl * 2 + nchar_pl - 1) / nchar_pl) * height))
+ goto fail;
+
+ for (y = 0; y < height; y++)
+ {
+ /* Get a scanline from the input image and scale it to the desired
+ width */
+ src_y = (y * gegl_buffer_get_height (buffer)) / height;
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, src_y,
+ gegl_buffer_get_width (buffer), 1),
+ 1.0, format, src_row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ greyptr = grey;
+ if (bpp == 3) /* RGB-image */
+ {
+ for (x = 0; x < width; x++)
+ { /* Convert to grey */
+ src_ptr = src_row + ((x * gegl_buffer_get_width (buffer)) / width) * 3;
+ *(greyptr++) = (3*src_ptr[0] + 6*src_ptr[1] + src_ptr[2]) / 10;
+ }
+ }
+ else if (cmap) /* Indexed image */
+ {
+ for (x = 0; x < width; x++)
+ {
+ src_ptr = src_row + ((x * gegl_buffer_get_width (buffer)) / width);
+ cind = *src_ptr; /* Get color index and convert to grey */
+ src_ptr = (cind >= ncols) ? cmap : (cmap + 3*cind);
+ *(greyptr++) = (3*src_ptr[0] + 6*src_ptr[1] + src_ptr[2]) / 10;
+ }
+ }
+ else /* Grey image */
+ {
+ for (x = 0; x < width; x++)
+ *(greyptr++) = *(src_row + ((x * gegl_buffer_get_width (buffer)) / width));
+ }
+
+ /* Now we have a grayscale line for the desired width. */
+ /* Dither it to b/w */
+ dither_grey (grey, bw, width, y);
+
+ /* Write out the b/w line */
+ out_count = 0;
+ bwptr = bw;
+ for (x = 0; x < nbsl; x++)
+ {
+ if (out_count == 0)
+ if (! print (output, error, "%% "))
+ goto fail;
+
+ if (! print (output, error, "%02x", *(bwptr++)))
+ goto fail;
+
+ out_count += 2;
+ if (out_count >= nchar_pl)
+ {
+ if (! print (output, error, "\n"))
+ goto fail;
+
+ out_count = 0;
+ }
+ }
+
+ if (! print (output, error, "\n"))
+ goto fail;
+
+ if ((y % 20) == 0)
+ gimp_progress_update ((gdouble) y / (gdouble) height);
+ }
+
+ gimp_progress_update (1.0);
+
+ if (! print (output, error, "%%%%EndPreview\n"))
+ goto fail;
+
+ dither_grey (grey, bw, width, -1);
+ g_free (src_row);
+ g_free (bw);
+ g_free (grey);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ g_free (src_row);
+ g_free (bw);
+ g_free (grey);
+
+ g_object_unref (buffer);
+
+ return FALSE;
+}
+
+static gboolean
+save_gray (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint bpp;
+ gint height, width, i, j;
+ gint tile_height;
+ guchar *data;
+ guchar *src;
+ guchar *packb = NULL;
+ gboolean level2 = (psvals.level > 1);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ format = babl_format ("Y' u8");
+ bpp = babl_format_get_bytes_per_pixel (format);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ tile_height = gimp_tile_height ();
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = (guchar *) g_malloc (tile_height * width * bpp);
+
+ /* Set up transformation in PostScript */
+ if (! save_ps_setup (output, drawable_ID, width, height, 1 * 8, error))
+ goto fail;
+
+ /* Write read image procedure */
+ if (! level2)
+ {
+ if (! print (output, error,
+ "{ currentfile scanline readhexstring pop }\n"))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "currentfile /ASCII85Decode filter /RunLengthDecode filter\n"))
+ goto fail;
+
+ ascii85_init ();
+
+ /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
+ packb = (guchar *) g_malloc ((width * 105) / 100 + 2);
+ }
+
+ if (! ps_begin_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "image\n"))
+ goto fail;
+
+#define GET_GRAY_TILE(begin) \
+ { gint scan_lines; \
+ scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
+ 1.0, format, begin, \
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
+ src = begin; }
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_GRAY_TILE (data); /* Get more data */
+
+ if (! level2)
+ {
+ for (j = 0; j < width; j++)
+ {
+ if (! print (output, error, "%c", hex[(*src) >> 4]))
+ goto fail;
+
+ if (! print (output, error, "%c", hex[(*(src++)) & 0x0f]))
+ goto fail;
+
+ if (((j + 1) % 39) == 0)
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+ else
+ {
+ gint nout;
+
+ compress_packbits (width, src, &nout, packb);
+
+ if (! ascii85_nout (output, nout, packb, error))
+ goto fail;
+
+ src += width;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ gimp_progress_update (1.0);
+
+ if (level2)
+ {
+ /* Write EOD of RunLengthDecode filter */
+ if (! ascii85_out (output, 128, error))
+ goto fail;
+
+ if (! ascii85_done (output, error))
+ goto fail;
+ }
+
+ if (! ps_end_data (output, error))
+ return FALSE;
+
+ if (! print (output, error, "showpage\n"))
+ goto fail;
+
+ g_free (data);
+ g_free (packb);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ g_free (data);
+ g_free (packb);
+
+ g_object_unref (buffer);
+
+ return FALSE;
+
+#undef GET_GRAY_TILE
+}
+
+static gboolean
+save_bw (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint bpp;
+ gint height, width, i, j;
+ gint ncols, nbsl, nwrite;
+ gint tile_height;
+ guchar *cmap, *ct;
+ guchar *data, *src;
+ guchar *packb = NULL;
+ guchar *scanline, *dst, mask;
+ guchar *hex_scanline;
+ gboolean level2 = (psvals.level > 1);
+
+ cmap = gimp_image_get_colormap (image_ID, &ncols);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ format = gimp_drawable_get_format (drawable_ID);
+ bpp = babl_format_get_bytes_per_pixel (format);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ tile_height = gimp_tile_height ();
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = g_new (guchar, tile_height * width * bpp);
+
+ nbsl = (width + 7) / 8;
+
+ scanline = g_new (guchar, nbsl + 1);
+ hex_scanline = g_new (guchar, (nbsl + 1) * 2);
+
+ /* Set up transformation in PostScript */
+ if (! save_ps_setup (output, drawable_ID, width, height, 1, error))
+ goto fail;
+
+ /* Write read image procedure */
+ if (! level2)
+ {
+ if (! print (output, error,
+ "{ currentfile scanline readhexstring pop }\n"))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "currentfile /ASCII85Decode filter /RunLengthDecode filter\n"))
+ goto fail;
+
+ ascii85_init ();
+
+ /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
+ packb = g_new (guchar, ((nbsl+1) * 105) / 100 + 2);
+ }
+
+ if (! ps_begin_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "image\n"))
+ goto fail;
+
+#define GET_BW_TILE(begin) \
+ { gint scan_lines; \
+ scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
+ 1.0, format, begin, \
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
+ src = begin; }
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_BW_TILE (data); /* Get more data */
+
+ dst = scanline;
+ memset (dst, 0, nbsl);
+ mask = 0x80;
+
+ /* Build a bitmap for a scanline */
+ for (j = 0; j < width; j++)
+ {
+ ct = cmap + *(src++)*3;
+ if (ct[0] || ct[1] || ct[2])
+ *dst |= mask;
+
+ if (mask == 0x01)
+ {
+ mask = 0x80; dst++;
+ }
+ else
+ {
+ mask >>= 1;
+ }
+ }
+
+ if (! level2)
+ {
+ /* Convert to hexstring */
+ for (j = 0; j < nbsl; j++)
+ {
+ hex_scanline[j * 2] = (guchar) hex[scanline[j] >> 4];
+ hex_scanline[j * 2 + 1] = (guchar) hex[scanline[j] & 0x0f];
+ }
+
+ /* Write out hexstring */
+ j = nbsl * 2;
+ dst = hex_scanline;
+
+ while (j > 0)
+ {
+ nwrite = (j > 78) ? 78 : j;
+
+ if (! g_output_stream_write_all (output,
+ dst, nwrite, NULL,
+ NULL, error))
+ goto fail;
+
+ if (! print (output, error, "\n"))
+ goto fail;
+
+ j -= nwrite;
+ dst += nwrite;
+ }
+ }
+ else
+ {
+ gint nout;
+
+ compress_packbits (nbsl, scanline, &nout, packb);
+
+ if (! ascii85_nout (output, nout, packb, error))
+ goto fail;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ gimp_progress_update (1.0);
+
+ if (level2)
+ {
+ /* Write EOD of RunLengthDecode filter */
+ if (! ascii85_out (output, 128, error))
+ goto fail;
+
+ if (! ascii85_done (output, error))
+ goto fail;
+ }
+
+ if (! ps_end_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "showpage\n"))
+ goto fail;
+
+ g_free (hex_scanline);
+ g_free (scanline);
+ g_free (data);
+ g_free (packb);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ g_free (hex_scanline);
+ g_free (scanline);
+ g_free (data);
+ g_free (packb);
+
+ g_object_unref (buffer);
+
+ return FALSE;
+
+#undef GET_BW_TILE
+}
+
+static gboolean
+save_index (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint bpp;
+ gint height, width, i, j;
+ gint ncols, bw;
+ gint tile_height;
+ guchar *cmap, *cmap_start;
+ guchar *data, *src;
+ guchar *packb = NULL;
+ guchar *plane = NULL;
+ gchar coltab[256 * 6], *ct;
+ gboolean level2 = (psvals.level > 1);
+
+ cmap = cmap_start = gimp_image_get_colormap (image_ID, &ncols);
+
+ ct = coltab;
+ bw = 1;
+ for (j = 0; j < 256; j++)
+ {
+ if (j >= ncols)
+ {
+ memset (ct, 0, 6);
+ ct += 6;
+ }
+ else
+ {
+ bw &= ((cmap[0] == 0) && (cmap[1] == 0) && (cmap[2] == 0)) ||
+ ((cmap[0] == 255) && (cmap[1] == 255) && (cmap[2] == 255));
+
+ *(ct++) = (guchar) hex[(*cmap) >> 4];
+ *(ct++) = (guchar) hex[(*(cmap++)) & 0x0f];
+ *(ct++) = (guchar) hex[(*cmap) >> 4];
+ *(ct++) = (guchar) hex[(*(cmap++)) & 0x0f];
+ *(ct++) = (guchar) hex[(*cmap) >> 4];
+ *(ct++) = (guchar) hex[(*(cmap++)) & 0x0f];
+ }
+ }
+
+ if (bw)
+ return save_bw (output, image_ID, drawable_ID, error);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ format = gimp_drawable_get_format (drawable_ID);
+ bpp = babl_format_get_bytes_per_pixel (format);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ tile_height = gimp_tile_height ();
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = (guchar *) g_malloc (tile_height * width * bpp);
+
+ /* Set up transformation in PostScript */
+ if (! save_ps_setup (output, drawable_ID, width, height, 3 * 8, error))
+ goto fail;
+
+ /* Write read image procedure */
+ if (! level2)
+ {
+ if (! print (output, error,
+ "{ currentfile scanline readhexstring pop } false 3\n"))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "%% Strings to hold RGB-samples per scanline\n"
+ "/rstr %d string def\n"
+ "/gstr %d string def\n"
+ "/bstr %d string def\n",
+ width, width, width))
+ goto fail;
+
+ if (! print (output, error,
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ rstr readstring pop}\n"
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ gstr readstring pop}\n"
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ bstr readstring pop}\n"
+ "true 3\n"))
+ goto fail;
+
+ /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
+ packb = (guchar *) g_malloc ((width * 105) / 100 + 2);
+ plane = (guchar *) g_malloc (width);
+ }
+
+ if (! ps_begin_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "colorimage\n"))
+ goto fail;
+
+#define GET_INDEX_TILE(begin) \
+ { gint scan_lines; \
+ scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
+ 1.0, format, begin, \
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
+ src = begin; }
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_INDEX_TILE (data); /* Get more data */
+
+ if (! level2)
+ {
+ for (j = 0; j < width; j++)
+ {
+ if (! g_output_stream_write_all (output,
+ coltab + (*(src++)) * 6, 6, NULL,
+ NULL, error))
+ goto fail;
+
+ if (((j + 1) % 13) == 0)
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+ else
+ {
+ gint rgb;
+
+ for (rgb = 0; rgb < 3; rgb++)
+ {
+ guchar *src_ptr = src;
+ guchar *plane_ptr = plane;
+ gint nout;
+
+ for (j = 0; j < width; j++)
+ *(plane_ptr++) = cmap_start[3 * *(src_ptr++) + rgb];
+
+ compress_packbits (width, plane, &nout, packb);
+
+ ascii85_init ();
+
+ if (! ascii85_nout (output, nout, packb, error))
+ goto fail;
+
+ /* Write EOD of RunLengthDecode filter */
+ if (! ascii85_out (output, 128, error))
+ goto fail;
+
+ if (! ascii85_done (output, error))
+ goto fail;
+ }
+
+ src += width;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ gimp_progress_update (1.0);
+
+ if (! ps_end_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "showpage\n"))
+ goto fail;
+
+ g_free (data);
+ g_free (packb);
+ g_free (plane);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ g_free (data);
+ g_free (packb);
+ g_free (plane);
+
+ g_object_unref (buffer);
+
+ return FALSE;
+
+#undef GET_INDEX_TILE
+}
+
+static gboolean
+save_rgb (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint bpp;
+ gint height, width, tile_height;
+ gint i, j;
+ guchar *data, *src;
+ guchar *packb = NULL;
+ guchar *plane = NULL;
+ gboolean level2 = (psvals.level > 1);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ format = babl_format ("R'G'B' u8");
+ bpp = babl_format_get_bytes_per_pixel (format);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ tile_height = gimp_tile_height ();
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = g_new (guchar, tile_height * width * bpp);
+
+ /* Set up transformation in PostScript */
+ if (! save_ps_setup (output, drawable_ID, width, height, 3 * 8, error))
+ goto fail;
+
+ /* Write read image procedure */
+ if (! level2)
+ {
+ if (! print (output, error,
+ "{ currentfile scanline readhexstring pop } false 3\n"))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "%% Strings to hold RGB-samples per scanline\n"
+ "/rstr %d string def\n"
+ "/gstr %d string def\n"
+ "/bstr %d string def\n",
+ width, width, width))
+ goto fail;
+
+ if (! print (output, error,
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ rstr readstring pop}\n"
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ gstr readstring pop}\n"
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ bstr readstring pop}\n"
+ "true 3\n"))
+ goto fail;
+
+ /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
+ packb = g_new (guchar, (width * 105) / 100 + 2);
+ plane = g_new (guchar, width);
+ }
+
+ if (! ps_begin_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "colorimage\n"))
+ goto fail;
+
+#define GET_RGB_TILE(begin) \
+ { gint scan_lines; \
+ scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
+ 1.0, format, begin, \
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
+ src = begin; }
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_RGB_TILE (data); /* Get more data */
+
+ if (! level2)
+ {
+ for (j = 0; j < width; j++)
+ {
+ if (! print (output, error, "%c",
+ hex[(*src) >> 4])) /* Red */
+ goto fail;
+
+ if (! print (output, error, "%c",
+ hex[(*(src++)) & 0x0f]))
+ goto fail;
+
+ if (! print (output, error, "%c",
+ hex[(*src) >> 4])) /* Green */
+ goto fail;
+
+ if (! print (output, error, "%c",
+ hex[(*(src++)) & 0x0f]))
+ goto fail;
+
+ if (! print (output, error, "%c",
+ hex[(*src) >> 4])) /* Blue */
+ goto fail;
+
+ if (! print (output, error, "%c",
+ hex[(*(src++)) & 0x0f]))
+ goto fail;
+
+ if (((j+1) % 13) == 0)
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+ else
+ {
+ gint rgb;
+
+ for (rgb = 0; rgb < 3; rgb++)
+ {
+ guchar *src_ptr = src + rgb;
+ guchar *plane_ptr = plane;
+ gint nout;
+
+ for (j = 0; j < width; j++)
+ {
+ *(plane_ptr++) = *src_ptr;
+ src_ptr += 3;
+ }
+
+ compress_packbits (width, plane, &nout, packb);
+
+ ascii85_init ();
+
+ if (! ascii85_nout (output, nout, packb, error))
+ goto fail;
+
+ /* Write EOD of RunLengthDecode filter */
+ if (! ascii85_out (output, 128, error))
+ goto fail;
+
+ if (! ascii85_done (output, error))
+ goto fail;
+ }
+
+ src += 3 * width;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ gimp_progress_update (1.0);
+
+ if (! ps_end_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "showpage\n"))
+ goto fail;
+
+ g_free (data);
+ g_free (packb);
+ g_free (plane);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ g_free (data);
+ g_free (packb);
+ g_free (plane);
+
+ g_object_unref (buffer);
+
+ return FALSE;
+
+#undef GET_RGB_TILE
+}
+
+static gboolean
+print (GOutputStream *output,
+ GError **error,
+ const gchar *format,
+ ...)
+{
+ va_list args;
+ gboolean success;
+
+ va_start (args, format);
+ success = g_output_stream_vprintf (output, NULL, NULL,
+ error, format, args);
+ va_end (args);
+
+ return success;
+}
+
+/* Load interface functions */
+
+static gint32
+count_ps_pages (const gchar *filename)
+{
+ FILE *psfile;
+ gchar *extension;
+ gchar buf[1024];
+ gint32 num_pages = 0;
+ gint32 showpage_count = 0;
+
+ extension = strrchr (filename, '.');
+ if (extension)
+ {
+ extension = g_ascii_strdown (extension + 1, -1);
+
+ if (strcmp (extension, "eps") == 0)
+ {
+ g_free (extension);
+ return 1;
+ }
+
+ g_free (extension);
+ }
+
+ psfile = g_fopen (filename, "r");
+
+ if (psfile == NULL)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return 0;
+ }
+
+ while (num_pages == 0 && !feof (psfile))
+ {
+ fgets (buf, sizeof (buf), psfile);
+
+ if (strncmp (buf + 2, "Pages:", 6) == 0)
+ sscanf (buf + strlen ("%%Pages:"), "%d", &num_pages);
+ else if (strncmp (buf, "showpage", 8) == 0)
+ showpage_count++;
+ }
+
+ if (feof (psfile) && num_pages < 1 && showpage_count > 0)
+ num_pages = showpage_count;
+
+ fclose (psfile);
+
+ return num_pages;
+}
+
+static gboolean
+load_dialog (const gchar *filename)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ GtkWidget *entry = NULL;
+ GtkWidget *target = NULL;
+ GtkWidget *toggle;
+ GtkWidget *selector = NULL;
+ gint32 page_count;
+ gchar *range = NULL;
+ gboolean run;
+
+ page_count = count_ps_pages (filename);
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Import from PostScript"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, LOAD_PS_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Import"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ if (page_count > 1)
+ {
+ selector = gimp_page_selector_new ();
+ gtk_box_pack_start (GTK_BOX (main_vbox), selector, TRUE, TRUE, 0);
+ gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (selector),
+ page_count);
+ gimp_page_selector_set_target (GIMP_PAGE_SELECTOR (selector),
+ ps_pagemode);
+
+ gtk_widget_show (selector);
+
+ g_signal_connect_swapped (selector, "activate",
+ G_CALLBACK (gtk_window_activate_default),
+ dialog);
+ }
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* Rendering */
+ frame = gimp_frame_new (_("Rendering"));
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ /* Resolution/Width/Height/Pages labels */
+ table = gtk_table_new (4, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (plvals.resolution,
+ MIN_RESOLUTION, MAX_RESOLUTION,
+ 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Resolution:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (resolution_change_callback),
+ &plvals.resolution);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &plvals.resolution);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (plvals.width,
+ 1, GIMP_MAX_IMAGE_SIZE,
+ 1, 10, 0);
+ ps_width_spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Width:"), 0.0, 0.5,
+ ps_width_spinbutton, 1, FALSE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &plvals.width);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (plvals.height,
+ 1, GIMP_MAX_IMAGE_SIZE,
+ 1, 10, 0);
+ ps_height_spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_Height:"), 0.0, 0.5,
+ ps_height_spinbutton, 1, FALSE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &plvals.height);
+
+ if (page_count == 0)
+ {
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 80, -1);
+ gtk_entry_set_text (GTK_ENTRY (entry), plvals.pages);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("Pages:"), 0.0, 0.5,
+ entry, 1, FALSE);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (load_pages_entry_callback),
+ NULL);
+ gimp_help_set_help_data (GTK_WIDGET (entry),
+ _("Pages to load (e.g.: 1-4 or 1,3,5-7)"), NULL);
+
+ target = gtk_combo_box_text_new ();
+ gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (target),
+ GIMP_PAGE_SELECTOR_TARGET_LAYERS,
+ _("Layers"));
+ gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (target),
+ GIMP_PAGE_SELECTOR_TARGET_IMAGES,
+ _("Images"));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (target), (int) ps_pagemode);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
+ _("Open as"), 0.0, 0.5,
+ target, 1, FALSE);
+ }
+
+ toggle = gtk_check_button_new_with_label (_("Try Bounding Box"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), plvals.use_bbox);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &plvals.use_bbox);
+
+ gtk_widget_show (vbox);
+ gtk_widget_show (frame);
+
+ /* Coloring */
+ frame = gimp_int_radio_group_new (TRUE, _("Coloring"),
+ G_CALLBACK (gimp_radio_button_update),
+ &plvals.pnm_type, plvals.pnm_type,
+
+ _("B/W"), 4, NULL,
+ _("Gray"), 5, NULL,
+ _("Color"), 6, NULL,
+ _("Automatic"), 7, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Text antialiasing"),
+ G_CALLBACK (gimp_radio_button_update),
+ &plvals.textalpha, plvals.textalpha,
+
+ C_("antialiasing", "None"), 1, NULL,
+ _("Weak"), 2, NULL,
+ _("Strong"), 4, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Graphic antialiasing"),
+ G_CALLBACK (gimp_radio_button_update),
+ &plvals.graphicsalpha, plvals.graphicsalpha,
+
+ C_("antialiasing", "None"), 1, NULL,
+ _("Weak"), 2, NULL,
+ _("Strong"), 4, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (selector)
+ {
+ range = gimp_page_selector_get_selected_range (GIMP_PAGE_SELECTOR (selector));
+
+ if (strlen (range) < 1)
+ {
+ gimp_page_selector_select_all (GIMP_PAGE_SELECTOR (selector));
+ range = gimp_page_selector_get_selected_range (GIMP_PAGE_SELECTOR (selector));
+ }
+
+ strncpy (plvals.pages, range, sizeof (plvals.pages));
+ plvals.pages[strlen (range)] = '\0';
+
+ ps_pagemode = gimp_page_selector_get_target (GIMP_PAGE_SELECTOR (selector));
+ }
+ else if (page_count == 0)
+ {
+ ps_pagemode = gtk_combo_box_get_active (GTK_COMBO_BOX (target));
+ }
+ else
+ {
+ strncpy (plvals.pages, "1", 1);
+ plvals.pages[1] = '\0';
+ ps_pagemode = GIMP_PAGE_SELECTOR_TARGET_IMAGES;
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+load_pages_entry_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gsize nelem = sizeof (plvals.pages);
+
+ strncpy (plvals.pages, gtk_entry_get_text (GTK_ENTRY (widget)), nelem);
+ plvals.pages[nelem-1] = '\0';
+}
+
+
+/* Save interface functions */
+
+static gboolean
+save_dialog (void)
+{
+ SaveDialogVals *vals;
+ GtkWidget *dialog;
+ GtkWidget *toggle;
+ GtkWidget *frame, *uframe;
+ GtkWidget *hbox, *vbox;
+ GtkWidget *main_vbox[2];
+ GtkWidget *table;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ gint j;
+ gboolean run;
+
+ vals = g_new (SaveDialogVals, 1);
+ vals->level = (psvals.level > 1);
+
+ dialog = gimp_export_dialog_new (_("PostScript"), PLUG_IN_BINARY, SAVE_PS_PROC);
+
+ /* Main hbox */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ hbox, FALSE, FALSE, 0);
+ main_vbox[0] = main_vbox[1] = NULL;
+
+ for (j = 0; j < G_N_ELEMENTS (main_vbox); j++)
+ {
+ main_vbox[j] = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), main_vbox[j], FALSE, TRUE, 0);
+ gtk_widget_show (main_vbox[j]);
+ }
+
+ /* Image Size */
+ frame = gimp_frame_new (_("Image Size"));
+ gtk_box_pack_start (GTK_BOX (main_vbox[0]), frame, FALSE, TRUE, 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ /* Width/Height/X-/Y-offset labels */
+ table = gtk_table_new (4, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ vals->adjustment[0] = (GtkAdjustment *)
+ gtk_adjustment_new (psvals.width,
+ 1e-5, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (vals->adjustment[0], 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Width:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ g_signal_connect (vals->adjustment[0], "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &psvals.width);
+
+ vals->adjustment[1] = (GtkAdjustment *)
+ gtk_adjustment_new (psvals.height,
+ 1e-5, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (vals->adjustment[1], 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Height:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ g_signal_connect (vals->adjustment[1], "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &psvals.height);
+
+ vals->adjustment[2] = (GtkAdjustment *)
+ gtk_adjustment_new (psvals.x_offset,
+ 0.0, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (vals->adjustment[2], 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_X offset:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ g_signal_connect (vals->adjustment[2], "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &psvals.x_offset);
+
+ vals->adjustment[3] = (GtkAdjustment *)
+ gtk_adjustment_new (psvals.y_offset,
+ 0.0, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (vals->adjustment[3], 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("_Y offset:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ g_signal_connect (vals->adjustment[3], "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &psvals.y_offset);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Keep aspect ratio"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), psvals.keep_ratio);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("When toggled, the resulting image will be "
+ "scaled to fit into the given size without "
+ "changing the aspect ratio."),
+ "#keep_aspect_ratio"),
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &psvals.keep_ratio);
+
+ /* Unit */
+ uframe = gimp_int_radio_group_new (TRUE, _("Unit"),
+ G_CALLBACK (save_unit_toggle_update),
+ vals, psvals.unit_mm,
+
+ _("_Inch"), FALSE, NULL,
+ _("_Millimeter"), TRUE, NULL,
+
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (main_vbox[0]), uframe, TRUE, TRUE, 0);
+ gtk_widget_show (uframe);
+
+ gtk_widget_show (vbox);
+ gtk_widget_show (frame);
+
+ /* Rotation */
+ frame = gimp_int_radio_group_new (TRUE, _("Rotation"),
+ G_CALLBACK (gimp_radio_button_update),
+ &psvals.rotate, psvals.rotate,
+
+ "_0", 0, NULL,
+ "_90", 90, NULL,
+ "_180", 180, NULL,
+ "_270", 270, NULL,
+
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (main_vbox[1]), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ /* Format */
+ frame = gimp_frame_new (_("Output"));
+ gtk_box_pack_start (GTK_BOX (main_vbox[1]), frame, TRUE, TRUE, 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_PostScript level 2"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), vals->level);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &vals->level);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Encapsulated PostScript"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), psvals.eps);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &psvals.eps);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("P_review"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), psvals.preview);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &psvals.preview);
+
+ /* Preview size label/entry */
+ table = gtk_table_new (1, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ g_object_bind_property (toggle, "active",
+ table, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (psvals.preview_size,
+ 0, 1024, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Preview _size:"), 1.0, 0.5,
+ spinbutton, 1, FALSE);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &psvals.preview_size);
+
+ gtk_widget_show (vbox);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (hbox);
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ psvals.level = (vals->level) ? 2 : 1;
+
+ g_free (vals);
+
+ return run;
+}
+
+static void
+save_unit_toggle_update (GtkWidget *widget,
+ gpointer data)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ SaveDialogVals *vals = (SaveDialogVals *) data;
+ gdouble factor;
+ gdouble value;
+ gint unit_mm;
+ gint i;
+
+ unit_mm = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ "gimp-item-data"));
+
+ psvals.unit_mm = unit_mm;
+
+ if (unit_mm)
+ factor = 25.4;
+ else
+ factor = 1.0 / 25.4;
+
+ for (i = 0; i < 4; i++)
+ {
+ value = gtk_adjustment_get_value (vals->adjustment[i]) * factor;
+
+ gtk_adjustment_set_value (vals->adjustment[i], value);
+ }
+ }
+}
+
+static gboolean
+resolution_change_callback (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ guint *old_resolution = (guint *) data;
+ gdouble ratio;
+
+ if (*old_resolution)
+ ratio = (gdouble) gtk_adjustment_get_value (adjustment) / *old_resolution;
+ else
+ ratio = 1.0;
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (ps_width_spinbutton),
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (ps_width_spinbutton)) * ratio);
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (ps_height_spinbutton),
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (ps_height_spinbutton)) * ratio);
+
+ return TRUE;
+
+}