file-ps.c 112 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
Elliot Lee's avatar
Elliot Lee committed
2 3 4
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 * PostScript file plugin
 * PostScript writing and GhostScript interfacing code
Manish Singh's avatar
Manish Singh committed
5 6
 * Copyright (C) 1997-98 Peter Kirchgessner
 * (email: peter@kirchgessner.net, WWW: http://www.kirchgessner.net)
Elliot Lee's avatar
Elliot Lee committed
7 8 9 10
 *
 * Added controls for TextAlphaBits and GraphicsAlphaBits
 *   George White <aa056@chebucto.ns.ca>
 *
11 12 13
 * Added Ascii85 encoding
 *   Austin Donnelly <austin@gimp.org>
 *
14
 * This program is free software: you can redistribute it and/or modify
Elliot Lee's avatar
Elliot Lee committed
15
 * it under the terms of the GNU General Public License as published by
16
 * the Free Software Foundation; either version 3 of the License, or
Elliot Lee's avatar
Elliot Lee committed
17 18 19 20 21 22 23 24
 * (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
25
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
26 27 28 29 30 31 32 33 34 35 36 37 38 39
 *
 */

/* 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"
40 41
 * V 1.03, nn, 20-Dec-97: Initialize some variables
 * V 1.04, PK, 20-Dec-97: Add Encapsulated PostScript output and preview
Manish Singh's avatar
Manish Singh committed
42 43 44
 * 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.
45
 * V 1.07, PK, 14-Sep-99: Add resolution to image
46
 * V 1.08, PK, 16-Jan-2000: Add PostScript-Level 2 by Austin Donnelly
47 48 49
 * V 1.09, PK, 15-Feb-2000: Force showpage on EPS-files
 *                          Add "RunLength" compression
 *                          Fix problem with "Level 2" toggle
50
 * V 1.10, PK, 15-Mar-2000: For load EPSF, allow negative Bounding Box Values
51
 *                          Save PS: don't start lines of image data with %%
52 53
 *                          to prevent problems with stupid PostScript
 *                          analyzer programs (Stanislav Brabec)
54 55
 *                          Add BeginData/EndData comments
 *                          Save PS: Set default rotation to 0
56 57 58 59
 * 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.
60
 *         PK, 31-Aug-2000: Load PS: Add checks for space in filename.
61 62
 * V 1.12  PK, 19-Jun-2001: Fix problem with command line switch --
 *                          (reported by Ferenc Wagner)
63
 * V 1.13  PK, 07-Apr-2002: Fix problem with DOS binary EPS files
64
 * V 1.14  PK, 14-May-2002: Workaround EPS files of Adb. Ill. 8.0
65
 * V 1.15  PK, 04-Oct-2002: Be more accurate with using BoundingBox
66 67
 * V 1.16  PK, 22-Jan-2004: Don't use popen(), use g_spawn_async_with_pipes()
 *                          or g_spawn_sync().
68
 * V 1.17  PK, 19-Sep-2004: Fix problem with interpretation of bounding box
Elliot Lee's avatar
Elliot Lee committed
69 70
 */

71
#include "config.h"
72

73
#include <errno.h>
Elliot Lee's avatar
Elliot Lee committed
74 75
#include <string.h>
#include <time.h>
76

77 78 79 80 81 82
#include <sys/types.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

83 84
#include <glib/gstdio.h>

85 86 87
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

88 89
#include "libgimp/stdplugins-intl.h"

90 91 92
#include <ghostscript/ierrors.h>
#include <ghostscript/iapi.h>
#include <ghostscript/gdevdsp.h>
93

Sven Neumann's avatar
Sven Neumann committed
94 95
#define VERSIO 1.17
static const gchar dversio[] = "v1.17  19-Sep-2004";
96

97 98 99 100 101 102
#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"
103
#define PLUG_IN_BINARY       "file-ps"
104
#define PLUG_IN_ROLE         "gimp-file-ps"
105 106


107 108 109
#define STR_LENGTH     64
#define MIN_RESOLUTION 5
#define MAX_RESOLUTION 8192
Elliot Lee's avatar
Elliot Lee committed
110 111 112 113

/* Load info */
typedef struct
{
114 115 116 117 118 119 120
  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 */
Elliot Lee's avatar
Elliot Lee committed
121 122 123 124
} PSLoadVals;

static PSLoadVals plvals =
{
125 126
  100,         /* 100 dpi                        */
  826, 1170,   /* default width/height (A4)      */
127
  TRUE,        /* try to use BoundingBox         */
128
  "1",         /* pages to load                  */
129
  6,           /* use ppm (color)               */
130 131
  1,           /* don't use text antialiasing     */
  1            /* don't use graphics antialiasing */
Elliot Lee's avatar
Elliot Lee committed
132 133
};

134
/* Widgets for width and height of PostScript image to
135 136 137
*  be loaded, so that they can be updated when desired resolution is
*  changed
*/
Sven Neumann's avatar
Sven Neumann committed
138 139
static GtkWidget *ps_width_spinbutton;
static GtkWidget *ps_height_spinbutton;
140

Elliot Lee's avatar
Elliot Lee committed
141 142 143
/* Save info  */
typedef struct
{
144 145 146 147 148 149 150 151 152
  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 */
Elliot Lee's avatar
Elliot Lee committed
153 154 155 156 157 158
} PSSaveVals;

static PSSaveVals psvals =
{
  287.0, 200.0,   /* Image size (A4) */
  5.0, 5.0,       /* Offset */
159 160
  TRUE,           /* Unit is mm */
  TRUE,           /* Keep edge ratio */
161
  0,              /* Rotate */
162
  2,              /* PostScript Level */
163 164
  FALSE,          /* Encapsulated PostScript flag */
  FALSE,          /* Preview flag */
165
  256             /* Preview size */
Elliot Lee's avatar
Elliot Lee committed
166 167
};

Sven Neumann's avatar
Sven Neumann committed
168 169
static const char hex[] = "0123456789abcdef";

Elliot Lee's avatar
Elliot Lee committed
170 171 172

/* Declare some local functions.
 */
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
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);

static void      ps_close         (FILE              *ifp);

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);
Elliot Lee's avatar
Elliot Lee committed
268 269 270


/* Dialog-handling */
271

272
static gint32    count_ps_pages             (const gchar *filename);
273
static gboolean  load_dialog                (const gchar *filename);
274 275
static void      load_pages_entry_callback  (GtkWidget   *widget,
                                             gpointer     data);
276

277 278
static gboolean  resolution_change_callback (GtkAdjustment *adjustment,
                                             gpointer   data);
Elliot Lee's avatar
Elliot Lee committed
279 280 281

typedef struct
{
282 283
  GtkAdjustment *adjustment[4];
  gint           level;
Elliot Lee's avatar
Elliot Lee committed
284 285
} SaveDialogVals;

286 287 288
static gboolean  save_dialog              (void);
static void      save_unit_toggle_update  (GtkWidget *widget,
                                           gpointer   data);
Elliot Lee's avatar
Elliot Lee committed
289

290
const GimpPlugInInfo PLUG_IN_INFO =
Elliot Lee's avatar
Elliot Lee committed
291
{
292 293 294 295
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
Elliot Lee's avatar
Elliot Lee committed
296 297 298 299
};


/* The run mode */
300
static GimpRunMode l_run_mode;
Elliot Lee's avatar
Elliot Lee committed
301

302
static void compress_packbits (int            nin,
303
                               unsigned char *src,
304
                               int           *nout,
305 306
                               unsigned char *dst);

Elliot Lee's avatar
Elliot Lee committed
307

308 309 310 311
static guint32  ascii85_buf       = 0;
static gint     ascii85_len       = 0;
static gint     ascii85_linewidth = 0;

312
static GimpPageSelectorTarget ps_pagemode = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
313 314 315 316

static void
ascii85_init (void)
{
317
  ascii85_len       = 0;
318
  ascii85_linewidth = 0;
319 320
}

321 322 323
static gboolean
ascii85_flush (GOutputStream  *output,
               GError        **error)
324
{
325 326
  gchar    c[5];
  gint     i;
327
  gboolean zero_case = (ascii85_buf == 0);
328 329 330
  GString  *string   = g_string_new (NULL);

  static gint max_linewidth = 75;
331

332
  for (i = 4; i >= 0; i--)
333
    {
334 335
      c[i] = (ascii85_buf % 85) + '!';
      ascii85_buf /= 85;
336
    }
337

338 339 340
  /* check for special case: "!!!!!" becomes "z", but only if not
   * at end of data. */
  if (zero_case && (ascii85_len == 4))
341
    {
342
      if (ascii85_linewidth >= max_linewidth)
343 344 345 346 347 348 349 350
        {
          g_string_append_c (string, '\n');

          ascii85_linewidth = 0;
        }

      g_string_append_c (string, 'z');

351
      ascii85_linewidth++;
352
    }
353
  else
354
    {
355
      for (i = 0; i < ascii85_len + 1; i++)
356
        {
357 358 359 360 361 362 363 364 365 366
          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++;
367
        }
368 369
    }

370 371
  ascii85_len = 0;
  ascii85_buf = 0;
372 373 374 375 376 377 378 379 380 381 382 383 384 385

  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;
386 387
}

388 389 390 391
static inline gboolean
ascii85_out (GOutputStream  *output,
             guchar          byte,
             GError        **error)
392
{
393
  if (ascii85_len == 4)
394 395
    if (! ascii85_flush (output, error))
      return FALSE;
396

397 398 399
  ascii85_buf <<= 8;
  ascii85_buf |= byte;
  ascii85_len++;
400 401

  return TRUE;
402 403
}

404 405 406 407 408
static gboolean
ascii85_nout (GOutputStream  *output,
              gint            n,
              guchar         *uptr,
              GError        **error)
409
{
410 411 412 413 414 415 416 417 418
  while (n-- > 0)
    {
      if (! ascii85_out (output, *uptr, error))
        return FALSE;

      uptr++;
    }

  return TRUE;
419 420
}

421 422 423
static gboolean
ascii85_done (GOutputStream  *output,
              GError        **error)
424
{
425
  if (ascii85_len)
426
    {
427
      /* zero any unfilled buffer portion, then flush */
428 429 430 431
      ascii85_buf <<= (8 * (4 - ascii85_len));

      if (! ascii85_flush (output, error))
        return FALSE;
432 433
    }

434 435 436 437
  if (! print (output, error, "~>\n"))
    return FALSE;

  return TRUE;
438 439 440
}


441 442 443 444 445 446
static void
compress_packbits (int nin,
                   unsigned char *src,
                   int *nout,
                   unsigned char *dst)

447 448
{
 unsigned char c;
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
 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;
}

524

525 526
typedef struct
{
527 528
  goffset eol;
  goffset begin_data;
529 530 531 532
} PS_DATA_POS;

static PS_DATA_POS ps_data_pos = { 0, 0 };

533 534 535
static gboolean
ps_begin_data (GOutputStream  *output,
               GError        **error)
536
{
537 538 539 540 541 542 543 544 545 546 547 548
  /*                                 %%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;
549 550
}

551 552 553 554 555 556
static gboolean
ps_end_data (GOutputStream  *output,
             GError        **error)
{
  goffset end_data;
  gchar   s[64];
557

558 559 560
  if ((ps_data_pos.begin_data > 0) && (ps_data_pos.eol > 0))
    {
      end_data = g_seekable_tell (G_SEEKABLE (output));
561

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
      if (end_data > 0)
        {
          g_snprintf (s, sizeof (s),
                      "%ld 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;
586 587 588
}


Elliot Lee's avatar
Elliot Lee committed
589 590 591 592 593
MAIN ()

static void
query (void)
{
594
  static const GimpParamDef load_args[] =
Elliot Lee's avatar
Elliot Lee committed
595
  {
596
    { GIMP_PDB_INT32,  "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
597 598
    { GIMP_PDB_STRING, "filename",     "The name of the file to load" },
    { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
Elliot Lee's avatar
Elliot Lee committed
599
  };
600
  static const GimpParamDef load_return_vals[] =
Elliot Lee's avatar
Elliot Lee committed
601
  {
602
    { GIMP_PDB_IMAGE, "image", "Output image" }
Elliot Lee's avatar
Elliot Lee committed
603 604
  };

605
  static const GimpParamDef set_load_args[] =
Elliot Lee's avatar
Elliot Lee committed
606
  {
607
    { GIMP_PDB_INT32,  "resolution", "Resolution to interpret image (dpi)"     },
608 609 610 611
    { 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)"           },
612
    { GIMP_PDB_INT32,  "coloring",   "4: b/w, 5: grey, 6: color image, 7: automatic" },
Sven Neumann's avatar
Sven Neumann committed
613 614
    { GIMP_PDB_INT32,  "text-alpha-bits",    "1, 2, or 4" },
    { GIMP_PDB_INT32,  "graphic-alpha-bits", "1, 2, or 4" }
Elliot Lee's avatar
Elliot Lee committed
615 616
  };

617
  static const GimpParamDef thumb_args[] =
618 619
  {
    { GIMP_PDB_STRING, "filename",     "The name of the file to load"  },
620
    { GIMP_PDB_INT32,  "thumb-size",   "Preferred thumbnail size"      }
621
  };
622
  static const GimpParamDef thumb_return_vals[] =
623 624 625 626
  {
    { GIMP_PDB_IMAGE, "image",         "Output image" }
  };

627
  static const GimpParamDef save_args[] =
Elliot Lee's avatar
Elliot Lee committed
628
  {
629
    { GIMP_PDB_INT32,    "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
630
    { GIMP_PDB_IMAGE,    "image",        "Input image" },
631 632 633
    { 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" },
634 635 636 637 638 639 640 641 642 643
    { 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" }
Elliot Lee's avatar
Elliot Lee committed
644 645
  };

646
  gimp_install_procedure (LOAD_PS_PROC,
647 648
                          "load PostScript documents",
                          "load PostScript documents",
649
                          "Peter Kirchgessner <peter@kirchgessner.net>",
Elliot Lee's avatar
Elliot Lee committed
650
                          "Peter Kirchgessner",
651
                          dversio,
652
                          N_("PostScript document"),
Elliot Lee's avatar
Elliot Lee committed
653
                          NULL,
Sven Neumann's avatar
Sven Neumann committed
654
                          GIMP_PLUGIN,
655 656
                          G_N_ELEMENTS (load_args),
                          G_N_ELEMENTS (load_return_vals),
Elliot Lee's avatar
Elliot Lee committed
657 658
                          load_args, load_return_vals);

659 660
  gimp_register_file_handler_mime (LOAD_PS_PROC, "application/postscript");
  gimp_register_magic_load_handler (LOAD_PS_PROC,
661 662
                                    "ps",
                                    "",
663 664
                                    "0,string,%!,0,long,0xc5d0d3c6");

665
  gimp_install_procedure (LOAD_EPS_PROC,
666 667 668 669 670 671 672 673 674 675 676 677
                          "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);

678 679
  gimp_register_file_handler_mime (LOAD_EPS_PROC, "image/x-eps");
  gimp_register_magic_load_handler (LOAD_EPS_PROC,
680 681
                                    "eps",
                                    "",
682 683
                                    "0,string,%!,0,long,0xc5d0d3c6");

684
  gimp_install_procedure (LOAD_PS_SETARGS_PROC,
685 686
                          "set additional parameters for procedure file-ps-load",
                          "set additional parameters for procedure file-ps-load",
687
                          "Peter Kirchgessner <peter@kirchgessner.net>",
Elliot Lee's avatar
Elliot Lee committed
688
                          "Peter Kirchgessner",
689
                          dversio,
Elliot Lee's avatar
Elliot Lee committed
690 691
                          NULL,
                          NULL,
Sven Neumann's avatar
Sven Neumann committed
692
                          GIMP_PLUGIN,
693
                          G_N_ELEMENTS (set_load_args), 0,
Elliot Lee's avatar
Elliot Lee committed
694 695
                          set_load_args, NULL);

696
  gimp_install_procedure (LOAD_PS_THUMB_PROC,
697
                          "Loads a small preview from a PostScript or PDF document",
698 699 700 701
                          "",
                          "Peter Kirchgessner <peter@kirchgessner.net>",
                          "Peter Kirchgessner",
                          dversio,
702 703
                          NULL,
                          NULL,
704 705 706 707 708
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (thumb_args),
                          G_N_ELEMENTS (thumb_return_vals),
                          thumb_args, thumb_return_vals);

709 710
  gimp_register_thumbnail_loader (LOAD_PS_PROC,  LOAD_PS_THUMB_PROC);
  gimp_register_thumbnail_loader (LOAD_EPS_PROC, LOAD_PS_THUMB_PROC);
711

712
  gimp_install_procedure (SAVE_PS_PROC,
713 714
                          "export image as PostScript document",
                          "PostScript exporting handles all image types except "
715
                          "those with alpha channels.",
716
                          "Peter Kirchgessner <peter@kirchgessner.net>",
Elliot Lee's avatar
Elliot Lee committed
717
                          "Peter Kirchgessner",
718
                          dversio,
719
                          N_("PostScript document"),
720
                          "RGB, GRAY, INDEXED",
Sven Neumann's avatar
Sven Neumann committed
721
                          GIMP_PLUGIN,
722
                          G_N_ELEMENTS (save_args), 0,
Elliot Lee's avatar
Elliot Lee committed
723 724
                          save_args, NULL);

725
  gimp_register_file_handler_mime (SAVE_PS_PROC, "application/postscript");
726
  gimp_register_file_handler_uri (SAVE_PS_PROC);
727
  gimp_register_save_handler (SAVE_PS_PROC, "ps", "");
728

729
  gimp_install_procedure (SAVE_EPS_PROC,
730 731
                          "export image as Encapsulated PostScript image",
                          "PostScript exporting handles all image types except "
732
                          "those with alpha channels.",
733 734 735 736 737 738 739 740 741
                          "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);

742
  gimp_register_file_handler_mime (SAVE_EPS_PROC, "application/x-eps");
743
  gimp_register_file_handler_uri (SAVE_EPS_PROC);
744
  gimp_register_save_handler (SAVE_EPS_PROC, "eps", "");
Elliot Lee's avatar
Elliot Lee committed
745 746
}

747 748
static void
ps_set_save_size (PSSaveVals *vals,
749 750
                  gint32      image_ID)
{
751 752 753
  gdouble  xres, yres, factor, iw, ih;
  guint    width, height;
  GimpUnit unit;
754 755

  gimp_image_get_resolution (image_ID, &xres, &yres);
Sven Neumann's avatar
Sven Neumann committed
756

757
  if ((xres < 1e-5) || (yres < 1e-5))
Sven Neumann's avatar
Sven Neumann committed
758 759
    xres = yres = 72.0;

760
  /* Calculate size of image in inches */
761
  width  = gimp_image_width (image_ID);
762
  height = gimp_image_height (image_ID);
763 764 765 766 767
  iw = width  / xres;
  ih = height / yres;

  unit = gimp_image_get_unit (image_ID);
  factor = gimp_unit_get_factor (unit);
768

769 770 771 772 773 774 775 776
  if (factor == 0.0254 ||
      factor == 0.254 ||
      factor == 2.54 ||
      factor == 25.4)
    {
      vals->unit_mm = TRUE;
    }

777
  if (vals->unit_mm)
778 779 780 781
    {
      iw *= 25.4;
      ih *= 25.4;
    }
782

783
  vals->width  = iw;
784 785 786
  vals->height = ih;
}

Elliot Lee's avatar
Elliot Lee committed
787
static void
788 789 790 791 792
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
Elliot Lee's avatar
Elliot Lee committed
793
{
794 795 796 797 798 799 800 801
  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;
Elliot Lee's avatar
Elliot Lee committed
802 803 804

  l_run_mode = run_mode = param[0].data.d_int32;

805
  INIT_I18N ();
806
  gegl_init (NULL, NULL);
807

Elliot Lee's avatar
Elliot Lee committed
808
  *nreturn_vals = 1;
809
  *return_vals  = values;
810

Sven Neumann's avatar
Sven Neumann committed
811 812
  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
Elliot Lee's avatar
Elliot Lee committed
813

814 815
  if (strcmp (name, LOAD_PS_PROC)  == 0 ||
      strcmp (name, LOAD_EPS_PROC) == 0)
Elliot Lee's avatar
Elliot Lee committed
816 817
    {
      switch (run_mode)
818
        {
Sven Neumann's avatar
Sven Neumann committed
819
        case GIMP_RUN_INTERACTIVE:
Elliot Lee's avatar
Elliot Lee committed
820
          /*  Possibly retrieve data  */
821
          gimp_get_data (LOAD_PS_PROC, &plvals);
Elliot Lee's avatar
Elliot Lee committed
822

823
          if (! load_dialog (param[1].data.d_string))
824
            status = GIMP_PDB_CANCEL;
Elliot Lee's avatar
Elliot Lee committed
825 826
          break;

Sven Neumann's avatar
Sven Neumann committed
827
        case GIMP_RUN_NONINTERACTIVE:
Elliot Lee's avatar
Elliot Lee committed
828 829
          /*  Make sure all the arguments are there!  */
          if (nparams != 3)
Sven Neumann's avatar
Sven Neumann committed
830
            status = GIMP_PDB_CALLING_ERROR;
Elliot Lee's avatar
Elliot Lee committed
831
          else    /* Get additional interpretation arguments */
832
            gimp_get_data (LOAD_PS_PROC, &plvals);
Elliot Lee's avatar
Elliot Lee committed
833 834
          break;

Sven Neumann's avatar
Sven Neumann committed
835
        case GIMP_RUN_WITH_LAST_VALS:
Elliot Lee's avatar
Elliot Lee committed
836
          /* Possibly retrieve data */
837
          gimp_get_data (LOAD_PS_PROC, &plvals);
Elliot Lee's avatar
Elliot Lee committed
838 839 840 841
          break;

        default:
          break;
842
        }
843

Sven Neumann's avatar
Sven Neumann committed
844
      if (status == GIMP_PDB_SUCCESS)
845 846
        {
          check_load_vals ();
847
          image_ID = load_image (param[1].data.d_string, &error);
848 849 850 851 852 853 854 855 856 857 858 859

          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;
            }
        }
Elliot Lee's avatar
Elliot Lee committed
860

861
      /*  Store plvals data  */
Sven Neumann's avatar
Sven Neumann committed
862
      if (status == GIMP_PDB_SUCCESS)
863
        gimp_set_data (LOAD_PS_PROC, &plvals, sizeof (PSLoadVals));
Elliot Lee's avatar
Elliot Lee committed
864
    }
865
  else if (strcmp (name, LOAD_PS_THUMB_PROC) == 0)
866 867 868 869 870 871 872
    {
      if (nparams < 2)
        {
          status = GIMP_PDB_CALLING_ERROR;
        }
      else
        {
873
          gint size = param[1].data.d_int32;
874 875 876 877 878 879

          /*  We should look for an embedded preview but for now we
           *  just load the document at a small resolution and the
           *  first page only.
           */

880
          plvals.resolution = size / 4;
881 882
          plvals.width      = size;
          plvals.height     = size;
883
          strncpy (plvals.pages, "1", sizeof (plvals.pages) - 1);
884 885

          check_load_vals ();
886
          image_ID = load_image (param[0].data.d_string, &error);
887 888 889

          if (image_ID != -1)
            {
890 891 892
              *nreturn_vals = 2;
              values[1].type         = GIMP_PDB_IMAGE;
              values[1].data.d_image = image_ID;
893 894 895 896 897 898 899
            }
          else
            {
              status = GIMP_PDB_EXECUTION_ERROR;
            }
        }
    }
900 901
  else if (strcmp (name, SAVE_PS_PROC)  == 0 ||
           strcmp (name, SAVE_EPS_PROC) == 0)
Elliot Lee's avatar
Elliot Lee committed
902
    {
903
      psvals.eps = strcmp (name, SAVE_PS_PROC);
904

905
      image_ID    = orig_image_ID = param[1].data.d_int32;
906 907
      drawable_ID = param[2].data.d_int32;

908
      /* eventually export the image */
909
      switch (run_mode)
910 911 912 913
        {
        case GIMP_RUN_INTERACTIVE:
        case GIMP_RUN_WITH_LAST_VALS:
          gimp_ui_init (PLUG_IN_BINARY, FALSE);
914 915 916 917 918 919 920

          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);

921 922 923 924 925 926 927 928 929
          if (export == GIMP_EXPORT_CANCEL)
            {
              values[0].data.d_status = GIMP_PDB_CANCEL;
              return;
            }
          break;
        default:
          break;
        }
930

Elliot Lee's avatar
Elliot Lee committed
931 932
      switch (run_mode)
        {
Sven Neumann's avatar
Sven Neumann committed
933
        case GIMP_RUN_INTERACTIVE:
Elliot Lee's avatar
Elliot Lee committed
934
          /*  Possibly retrieve data  */
935
          gimp_get_data (name, &psvals);
936

937
          ps_set_save_size (&psvals, orig_image_ID);
938

Elliot Lee's avatar
Elliot Lee committed
939 940
          /*  First acquire information with a dialog  */
          if (! save_dialog ())
Sven Neumann's avatar
Sven Neumann committed
941
            status = GIMP_PDB_CANCEL;
Elliot Lee's avatar
Elliot Lee committed
942 943
          break;

Sven Neumann's avatar
Sven Neumann committed
944
        case GIMP_RUN_NONINTERACTIVE:
Elliot Lee's avatar
Elliot Lee committed
945
          /*  Make sure all the arguments are there!  */
946
          if (nparams != 15)
947 948 949
            {
              status = GIMP_PDB_CALLING_ERROR;
            }
Elliot Lee's avatar
Elliot Lee committed
950
          else
951 952 953 954 955 956 957 958
            {
              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;
959
              psvals.eps          = (param[12].data.d_int32 != 0);
960 961 962 963
              psvals.preview      = (param[13].data.d_int32 != 0);
              psvals.preview_size = param[13].data.d_int32;
              psvals.level        = param[14].data.d_int32;
            }
Elliot Lee's avatar
Elliot Lee committed
964 965
          break;

Sven Neumann's avatar
Sven Neumann committed
966
        case GIMP_RUN_WITH_LAST_VALS:
Elliot Lee's avatar
Elliot Lee committed
967
          /*  Possibly retrieve data  */
968
          gimp_get_data (name, &psvals);
Elliot Lee's avatar
Elliot Lee committed
969 970 971 972 973 974
          break;

        default:
          break;
        }

Sven Neumann's avatar
Sven Neumann committed
975
      if (status == GIMP_PDB_SUCCESS)
976 977 978 979 980
        {
          if ((psvals.width == 0.0) || (psvals.height == 0.0))
            ps_set_save_size (&psvals, orig_image_ID);

          check_save_vals ();
981 982 983

          if (save_image (g_file_new_for_uri (param[3].data.d_string),
                          image_ID, drawable_ID,
984
                          &error))
985 986 987 988 989 990 991 992 993
            {
              /*  Store psvals data  */
              gimp_set_data (name, &psvals, sizeof (PSSaveVals));
            }
          else
            {
              status = GIMP_PDB_EXECUTION_ERROR;
            }
        }
994

995
      if (export == GIMP_EXPORT_EXPORT)
996
        gimp_image_delete (image_ID);
Elliot Lee's avatar
Elliot Lee committed
997
    }
998
  else if (strcmp (name, LOAD_PS_SETARGS_PROC) == 0)
Elliot Lee's avatar
Elliot Lee committed
999 1000 1001
    {
      /*  Make sure all the arguments are there!  */
      if (nparams != 8)
1002 1003 1004
        {
          status = GIMP_PDB_CALLING_ERROR;
        }
Elliot Lee's avatar
Elliot Lee committed
1005
      else
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
        {
          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));
        }
Elliot Lee's avatar
Elliot Lee committed
1024
    }
1025 1026
  else
    {
Sven Neumann's avatar
Sven Neumann committed
1027
      status = GIMP_PDB_CALLING_ERROR;
1028 1029
    }

1030 1031 1032 1033 1034 1035 1036
  if (status != GIMP_PDB_SUCCESS && error)
    {
      *nreturn_vals = 2;
      values[1].type          = GIMP_PDB_STRING;
      values[1].data.d_string = error->message;
    }

1037
  values[0].data.d_status = status;
Elliot Lee's avatar
Elliot Lee committed
1038 1039 1040 1041
}


static gint32
1042 1043
load_image (const gchar  *filename,
            GError      **error)
Elliot Lee's avatar
Elliot Lee committed
1044
{
Sven Neumann's avatar
Sven Neumann committed
1045 1046 1047 1048 1049 1050 1051 1052
  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;
Elliot Lee's avatar
Elliot Lee committed
1053 1054

#ifdef PS_DEBUG
1055 1056 1057
  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);
1058
  g_print (" Coloring: %d\n", plvals.pnm_type);
1059 1060
  g_print (" TextAlphaBits: %d\n", plvals.textalpha);
  g_print (" GraphicsAlphaBits: %d\n", plvals.graphicsalpha);
Elliot Lee's avatar
Elliot Lee committed
1061 1062
#endif

1063 1064 1065
  gimp_progress_init_printf (_("Opening '%s'"),
                             gimp_filename_to_utf8 (filename));

1066
  /* Try to see if PostScript file is available */
1067
  ifp = g_fopen (filename, "r");
1068 1069
  if (ifp == NULL)
    {
1070 1071 1072
      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));
1073
      return -1;
1074 1075 1076
    }
  fclose (ifp);

1077
  ifp = ps_open (filename, &plvals, &llx, &lly, &urx, &ury, &is_epsf);
1078 1079
  if (!ifp)
    {
1080
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR,
1081
                   _("Could not interpret PostScript file '%s'"),
1082
                   gimp_filename_to_utf8 (filename));
1083
      return -1;
1084 1085
    }

1086
  image_list = g_new (gint32, 10);
1087 1088 1089 1090
  n_images = 0;
  max_images = 10;

  max_pagenum = 9999;  /* Try to get the maximum pagenumber to read */
1091 1092 1093
  if (is_epsf)
    max_pagenum = 1;

1094 1095 1096 1097
  if (!page_in_list (plvals.pages, max_pagenum)) /* Is there a limit in list ? */
    {
      max_pagenum = -1;
      for (temp = plvals.pages; *temp != '\0'; temp++)
1098 1099
        {
          if ((*temp < '0') || (*temp > '9'))
1100
            continue; /* Search next digit */
1101 1102
          sscanf (temp, "%d", &k);
          if (k > max_pagenum)
1103
            max_pagenum = k;
1104
          while ((*temp >= '0') && (*temp <= '9'))
1105
            temp++;
1106 1107
          temp--;
        }
1108 1109 1110

      if (max_pagenum < 1)
        max_pagenum = 9999;
1111 1112 1113 1114 1115 1116
    }

  /* Load all images */
  for (page_count = 1; page_count <= max_pagenum; page_count++)
    {
      if (page_in_list (plvals.pages, page_count))
1117 1118 1119
        {
          image_ID = load_ps (filename, page_count, ifp, llx, lly, urx, ury);
          if (image_ID == -1)
1120
            break;
1121

1122
          gimp_image_set_resolution (image_ID,
1123 1124
                                     (gdouble) plvals.resolution,
                                     (gdouble) plvals.resolution);
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135

          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;
        }
1136
      else  /* Skip an image */
1137
        {
1138
          image_ID = -1;
1139
          if (! skip_ps (ifp))
1140
            break;
1141
        }
1142
    }
1143

1144
  ps_close (ifp);
1145

1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
  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]);

1167
              name = gimp_item_get_name (tmp_ID);
1168 1169

              current_layer = gimp_layer_new_from_drawable (tmp_ID, image_ID);
1170
              gimp_item_set_name (current_layer, name);
1171
              gimp_image_insert_layer (image_ID, current_layer, -1, -1);
1172 1173 1174 1175 1176 1177 1178 1179 1180
              gimp_image_delete (image_list[k]);

              g_free (name);
            }
        }

      gimp_image_undo_enable (image_ID);
    }
  else
1181
    {
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
      /* 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;
1195 1196 1197 1198
    }

  g_free (image_list);

1199
  return image_ID;
Elliot Lee's avatar
Elliot Lee committed
1200 1201 1202
}


1203 1204 1205 1206 1207
static gboolean
save_image (GFile   *file,
            gint32   image_ID,
            gint32   drawable_ID,
            GError **error)
Elliot Lee's avatar
Elliot Lee committed
1208
{
1209
  GOutputStream *output;
1210
  GCancellable  *cancellable;
1211
  GimpImageType  drawable_type;
Elliot Lee's avatar
Elliot Lee committed
1212 1213 1214

  drawable_type = gimp_drawable_type (drawable_ID);

1215
  /*  Make sure we're not exporting an image with an alpha channel  */
Elliot Lee's avatar
Elliot Lee committed
1216
  if (gimp_drawable_has_alpha (drawable_ID))
1217
    {
1218
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
1219
                   _("PostScript export cannot handle images with alpha channels"));
1220 1221
      return FALSE;
    }
Elliot Lee's avatar
Elliot Lee committed
1222 1223

  switch (drawable_type)
1224
    {
Sven Neumann's avatar
Sven Neumann committed
1225 1226 1227
    case GIMP_INDEXED_IMAGE:
    case GIMP_GRAY_IMAGE:
    case GIMP_RGB_IMAGE:
Elliot Lee's avatar
Elliot Lee committed
1228
      break;
1229

Elliot Lee's avatar
Elliot Lee committed
1230
    default:
1231 1232
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Cannot operate on unknown image types."));
1233
      return FALSE;
Elliot Lee's avatar
Elliot Lee committed
1234
      break;
1235
    }
Elliot Lee's avatar
Elliot Lee committed
1236

1237
  gimp_progress_init_printf (_("Exporting '%s'"),
1238
                             gimp_file_get_utf8_name (file));
1239

1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
  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
1253
    {
1254
      return FALSE;
1255
    }
Elliot Lee's avatar
Elliot Lee committed
1256

1257 1258
  if (! save_ps_header (output, file, error))
    goto fail;
Elliot Lee's avatar
Elliot Lee committed
1259

1260 1261 1262
  switch (drawable_type)
    {
    case GIMP_INDEXED_IMAGE:
1263 1264
      if (! save_index (output, image_ID, drawable_ID, error))
        goto fail;
1265
      break;
1266

1267
    case GIMP_GRAY_IMAGE:
1268 1269
      if (! save_gray (output, image_ID, drawable_ID, error))
        goto fail;
1270
      break;
1271

1272
    case GIMP_RGB_IMAGE:
1273 1274
      if (! save_rgb (output, image_ID, drawable_ID, error))
        goto fail;
1275
      break;
1276

1277
    default:
1278
      g_return_val_if_reached (FALSE);
1279
    }
Elliot Lee's avatar
Elliot Lee committed
1280

1281 1282
  if (! save_ps_trailer (output, error))
    goto fail;
Elliot Lee's avatar
Elliot Lee committed
1283

1284 1285
  if (! g_output_stream_close (output, NULL, error))
    goto fail;
Elliot Lee's avatar
Elliot Lee committed
1286

1287 1288 1289 1290 1291 1292
  g_object_unref (output);

  return TRUE;

 fail:

1293 1294 1295 1296
  cancellable = g_cancellable_new ();
  g_cancellable_cancel (cancellable);
  g_output_stream_close (output, cancellable, NULL);

1297
  g_object_unref (output);
1298
  g_object_unref (cancellable);
1299 1300

  return FALSE;
Elliot Lee's avatar
Elliot Lee committed
1301 1302 1303 1304 1305 1306 1307
}


/* Check (and correct) the load values plvals */
static void
check_load_vals (void)
{
1308 1309 1310 1311
  if (plvals.resolution < MIN_RESOLUTION)
    plvals.resolution = MIN_RESOLUTION;
  else if (plvals.resolution > MAX_RESOLUTION)
    plvals.resolution = MAX_RESOLUTION;
1312 1313 1314 1315 1316

  if (plvals.width < 2)
    plvals.width = 2;
  if (plvals.height < 2)
    plvals.height = 2;
Elliot Lee's avatar
Elliot Lee committed
1317 1318
  plvals.use_bbox = (plvals.use_bbox != 0);
  if (plvals.pages[0] == '\0')
1319
    strncpy (plvals.pages, "1-99", sizeof (plvals.pages) - 1);
Elliot Lee's avatar
Elliot Lee committed
1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333
  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)
1334 1335
{
  int i;
Elliot Lee's avatar
Elliot Lee committed
1336

1337 1338 1339 1340 1341
  i = psvals.rotate;
  if ((i != 0) && (i != 90) && (i != 180) && (i != 270))
    psvals.rotate = 90;
  if (psvals.preview_size <= 0)
    psvals.preview = FALSE;
Elliot Lee's avatar
Elliot Lee committed
1342 1343 1344 1345 1346
}


/* Check if a page is in a given list */
static gint
1347 1348 1349 1350 1351
page_in_list (gchar *list,
              guint  page_num)
{
  char tmplist[STR_LENGTH], *c0, *c1;
  int state, start_num, end_num;
Elliot Lee's avatar
Elliot Lee committed
1352 1353 1354 1355 1356 1357 1358
#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); } }

1359 1360
  if ((list == NULL) || (*list == '\0'))
    return 1;
1361 1362 1363 1364 1365 1366 1367 1368

  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'))
1369 1370 1371
        {
          *(c0++) = *c1;
        }
1372
      else if ((*c1 == '-') || (*c1 == ','))