file-pdf-save.c 58.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* GIMP - The GNU Image Manipulation Program
 *
 * file-pdf-save.c - PDF file exporter, based on the cairo PDF surface
 *
 * Copyright (C) 2010 Barak Itkin <lightningismyname@gmail.com>
 *
 * 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
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
 */

/* The PDF export plugin has 3 main procedures:
 * 1. file-pdf-save
 *    This is the main procedure. It has 3 options for optimizations of
 *    the pdf file, and it can show a gui. This procedure works on a single
 *    image.
 * 2. file-pdf-save-defaults
 *    This procedures is the one that will be invoked by gimp's file-save,
 *    when the pdf extension is chosen. If it's in RUN_INTERACTIVE, it will
 *    pop a user interface with more options, like file-pdf-save. If it's in
 *    RUN_NONINTERACTIVE, it will simply use the default values. Note that on
 *    RUN_WITH_LAST_VALS there will be no gui, however the values will be the
 *    ones that were used in the last interactive run (or the defaults if none
 *    are available.
 * 3. file-pdf-save-multi
 *    This procedures is more advanced, and it allows the creation of multiple
 *    paged pdf files. It will be located in File/Create/Multiple page PDF...
 *
 * It was suggested that file-pdf-save-multi will be removed from the UI as it
 * does not match the product vision (GIMP isn't a program for editing multiple
 * paged documents).
 */

/* Known Issues (except for the coding style issues):
 * 1. Grayscale layers are inverted (although layer masks which are not grayscale,
 * are not inverted)
 * 2. Exporting some fonts doesn't work since gimp_text_layer_get_font Returns a
 * font which is sometimes incompatiable with pango_font_description_from_string
 * (gimp_text_layer_get_font sometimes returns suffixes such as "semi-expanded" to
 * the font's name although the GIMP's font selection dialog shows the don'ts name
 * normally - This should be checked again in GIMP 2.7)
 * 3. Indexed layers can't be optimized yet (Since gimp_histogram won't work on
 * indexed layers)
 * 4. Rendering the pango layout requires multiplying the size in PANGO_SCALE. This
 * means I'll need to do some hacking on the markup returned from GIMP.
 * 5. When accessing the contents of layer groups is supported, we should do use it
 * (since this plugin should preserve layers).
 *
 * Also, there are 2 things which we should warn the user about:
 * 1. Cairo does not support bitmap masks for text.
 * 2. Currently layer modes are ignored. We do support layers, including
 * transparency and opacity, but layer modes are not supported.
 */

/* Changelog
 *
 * April 29, 2009 | Barak Itkin <lightningismyname@gmail.com>
 *   First version of the plugin. This is only a proof of concept and not a full
 *   working plugin.
 *
 * May 6, 2009 Barak | Itkin <lightningismyname@gmail.com>
 *   Added new features and several bugfixes:
 *   - Added handling for image resolutions
 *   - fixed the behaviour of getting font sizes
 *   - Added various optimizations (solid rectangles instead of bitmaps, ignoring
 *     invisible layers, etc.) as a macro flag.
 *   - Added handling for layer masks, use CAIRO_FORMAT_A8 for grayscale drawables.
 *   - Indexed layers are now supported
 *
 * August 17, 2009 | Barak Itkin <lightningismyname@gmail.com>
 *   Most of the plugin was rewritten from scratch and it now has several new
 *   features:
82 83
 *   - Got rid of the optimization macros in the code. The gui now supports
 *     selecting which optimizations to apply.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
 *   - Added a procedure to allow the creation of multiple paged PDF's
 *   - Registered the plugin on "<Image>/File/Create/PDF"
 *
 * August 21, 2009 | Barak Itkin <lightningismyname@gmail.com>
 *   Fixed a typo that prevented the plugin from compiling...
 *   A migration to the new GIMP 2.8 api, which includes:
 *   - Now using gimp_export_dialog_new
 *   - Using gimp_text_layer_get_hint_style (2.8) instead of the depreceated
 *     gimp_text_layer_get_hinting (2.6).
 *
 * August 24, 2010 | Barak Itkin <lightningismyname@gmail.com>
 *   More migrations to the new GIMP 2.8 api:
 *   - Now using the GimpItem api
 *   - Using gimp_text_layer_get_markup where possible
 *   - Fixed some compiler warnings
 *   Also merged the header and c file into one file, Updated some of the comments
 *   and documentation, and moved this into the main source repository.
 */

103
#include "config.h"
104

105 106
#include <errno.h>

107
#include <glib/gstdio.h>
108 109
#include <cairo-pdf.h>
#include <pango/pangocairo.h>
110 111 112 113

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

114
#include "libgimp/stdplugins-intl.h"
115

116

117
#define SAVE_PROC               "file-pdf-save"
118
#define SAVE2_PROC              "file-pdf-save2"
119 120
#define SAVE_MULTI_PROC         "file-pdf-save-multi"
#define PLUG_IN_BINARY          "file-pdf-save"
121
#define PLUG_IN_ROLE            "gimp-file-pdf-save"
122 123 124 125 126 127 128 129 130 131 132

#define DATA_OPTIMIZE           "file-pdf-data-optimize"
#define DATA_IMAGE_LIST         "file-pdf-data-multi-page"

/* Gimp will crash before you reach this limitation :D */
#define MAX_PAGE_COUNT           350
#define MAX_FILE_NAME_LENGTH     350

#define THUMB_WIDTH              90
#define THUMB_HEIGHT             120

133 134 135 136 137
#define GIMP_PLUGIN_PDF_SAVE_ERROR gimp_plugin_pdf_save_error_quark ()

typedef enum
{
  GIMP_PLUGIN_PDF_SAVE_ERROR_FAILED
138
} GimpPluginPDFError;
139 140

GQuark gimp_plugin_pdf_save_error_quark (void);
141

142 143 144 145 146 147 148 149 150 151
typedef enum
{
  SA_RUN_MODE,
  SA_IMAGE,
  SA_DRAWABLE,
  SA_FILENAME,
  SA_RAW_FILENAME,
  SA_VECTORIZE,
  SA_IGNORE_HIDDEN,
  SA_APPLY_MASKS,
152
  SA_LAYERS_AS_PAGES,
153
  SA_REVERSE_ORDER,
154 155 156 157 158 159 160
  SA_ARG_COUNT
} SaveArgs;

typedef enum
{
  SMA_RUN_MODE,
  SMA_COUNT,
161
  SMA_IMAGES,
162 163 164 165 166 167 168 169
  SMA_VECTORIZE,
  SMA_IGNORE_HIDDEN,
  SMA_APPLY_MASKS,
  SMA_FILENAME,
  SMA_RAWFILENAME,
  SMA_ARG_COUNT
} SaveMultiArgs;

170 171
typedef struct
{
172 173
  gboolean vectorize;
  gboolean ignore_hidden;
174
  gboolean apply_masks;
175
  gboolean layers_as_pages;
176
  gboolean reverse_order;
177 178
} PdfOptimize;

179 180 181
typedef struct
{
  gint32  images[MAX_PAGE_COUNT];
182
  guint32 image_count;
183
  gchar   file_name[MAX_FILE_NAME_LENGTH];
184 185
} PdfMultiPage;

186 187 188 189
typedef struct
{
  PdfOptimize  optimize;
  GArray      *images;
190 191
} PdfMultiVals;

192 193
enum
{
194 195 196 197 198 199
  THUMB,
  PAGE_NUMBER,
  IMAGE_NAME,
  IMAGE_ID
};

200 201
typedef struct
{
202
  GdkPixbuf *thumb;
203 204
  gint32     page_number;
  gchar     *image_name;
205 206
} Page;

207

208
static void              query                      (void);
209

210 211 212 213 214
static void              run                        (const gchar     *name,
                                                     gint             nparams,
                                                     const GimpParam *param,
                                                     gint            *nreturn_vals,
                                                     GimpParam      **return_vals);
215

216 217 218 219 220 221
static gboolean          init_vals                  (const gchar     *name,
                                                     gint             nparams,
                                                     const GimpParam *param,
                                                     gboolean        *single,
                                                     gboolean        *defaults,
                                                     GimpRunMode     *run_mode);
222

223
static void              init_image_list_defaults   (gint32           image);
224

225
static void              validate_image_list        (void);
226

227
static gboolean          gui_single                 (void);
228

229
static gboolean          gui_multi                  (void);
230

231 232 233
static void              reverse_order_toggled      (GtkToggleButton *reverse_order,
                                                     GtkButton       *layers_as_pages);

234 235
static void              choose_file_call           (GtkWidget       *browse_button,
                                                     gpointer         file_entry);
236

237
static gboolean          get_image_list             (void);
238

239
static GtkTreeModel    * create_model               (void);
240

241 242 243 244 245 246 247 248 249
static void              add_image_call             (GtkWidget       *widget,
                                                     gpointer         img_combo);
static void              del_image_call             (GtkWidget       *widget,
                                                     gpointer         icon_view);
static void              remove_call                (GtkTreeModel    *tree_model,
                                                     GtkTreePath     *path,
                                                     gpointer         user_data);
static void              recount_pages              (void);

250 251
static cairo_surface_t * get_cairo_surface          (gint32           drawable_ID,
                                                     gboolean         as_mask,
252 253
                                                     GError         **error);

254 255
static GimpRGB           get_layer_color            (gint32           layer_ID,
                                                     gboolean        *single);
256

257 258 259 260 261
static void              drawText                   (gint32           text_id,
                                                     gdouble          opacity,
                                                     cairo_t         *cr,
                                                     gdouble          x_res,
                                                     gdouble          y_res);
262

263
static gboolean     dnd_remove = TRUE;
264 265
static PdfMultiPage multi_page;

266 267
static PdfOptimize optimize =
{
268
  TRUE,  /* vectorize */
269
  TRUE,  /* ignore_hidden */
270
  TRUE,  /* apply_masks */
271 272
  FALSE, /* layers_as_pages */
  FALSE  /* reverse_order */
273 274 275
};

static GtkTreeModel *model;
276 277
static GtkWidget    *file_choose;
static gchar        *file_name;
278

279 280 281 282 283 284 285 286
GimpPlugInInfo PLUG_IN_INFO =
{
  NULL,
  NULL,
  query,
  run
};

287 288
G_DEFINE_QUARK (gimp-plugin-pdf-save-error-quark, gimp_plugin_pdf_save_error)

289 290
MAIN()

291
static void
292 293 294
query (void)
{
  static GimpParamDef save_args[] =
295 296 297 298 299 300 301 302 303 304 305 306
  {
    { GIMP_PDB_INT32,    "run-mode",      "Run mode" },
    { GIMP_PDB_IMAGE,    "image",         "Input image" },
    { GIMP_PDB_DRAWABLE, "drawable",      "Input drawable" },
    { GIMP_PDB_STRING,   "filename",      "The name of the file to save the image in" },
    { GIMP_PDB_STRING,   "raw-filename",  "The name of the file to save the image in" },
    { GIMP_PDB_INT32,    "vectorize",     "Convert bitmaps to vector graphics where possible. TRUE or FALSE" },
    { GIMP_PDB_INT32,    "ignore-hidden", "Omit hidden layers and layers with zero opacity. TRUE or FALSE" },
    { GIMP_PDB_INT32,    "apply-masks",   "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)" }
  };

  static GimpParamDef save2_args[] =
307
  {
308 309 310 311 312 313 314 315
    { GIMP_PDB_INT32,    "run-mode",        "Run mode" },
    { GIMP_PDB_IMAGE,    "image",           "Input image" },
    { GIMP_PDB_DRAWABLE, "drawable",        "Input drawable" },
    { GIMP_PDB_STRING,   "filename",        "The name of the file to save the image in" },
    { GIMP_PDB_STRING,   "raw-filename",    "The name of the file to save the image in" },
    { GIMP_PDB_INT32,    "vectorize",       "Convert bitmaps to vector graphics where possible. TRUE or FALSE" },
    { GIMP_PDB_INT32,    "ignore-hidden",   "Omit hidden layers and layers with zero opacity. TRUE or FALSE" },
    { GIMP_PDB_INT32,    "apply-masks",     "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)" },
316 317
    { GIMP_PDB_INT32,    "layers-as-pages", "Layers as pages (bottom layers first). TRUE or FALSE" },
    { GIMP_PDB_INT32,    "reverse-order",   "Reverse the pages order (top layers first). TRUE or FALSE" }
318
  };
319 320

  static GimpParamDef save_multi_args[] =
321
  {
322 323 324 325 326 327 328 329
    { GIMP_PDB_INT32,      "run-mode",        "Run mode" },
    { GIMP_PDB_INT32,      "count",           "The amount of images entered (This will be the amount of pages). 1 <= count <= MAX_PAGE_COUNT" },
    { GIMP_PDB_INT32ARRAY, "images",          "Input image for each page (An image can appear more than once)" },
    { GIMP_PDB_INT32,      "vectorize",       "Convert bitmaps to vector graphics where possible. TRUE or FALSE" },
    { GIMP_PDB_INT32,      "ignore-hidden",   "Omit hidden layers and layers with zero opacity. TRUE or FALSE" },
    { GIMP_PDB_INT32,      "apply-masks",     "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)" },
    { GIMP_PDB_STRING,     "filename",        "The name of the file to save the image in" },
    { GIMP_PDB_STRING,     "raw-filename",    "The name of the file to save the image in" }
330
  };
331 332

  gimp_install_procedure (SAVE_PROC,
333 334 335 336 337
                          "Save files in PDF format",
                          "Saves files in Adobe's Portable Document Format. "
                          "PDF is designed to be easily processed by a variety "
                          "of different platforms, and is a distant cousin of "
                          "PostScript.",
338 339 340
                          "Barak Itkin",
                          "Copyright Barak Itkin",
                          "August 2009",
341
                          N_("Portable Document Format"),
342 343 344 345 346
                          "RGB*, GRAY*, INDEXED*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (save_args), 0,
                          save_args, NULL);

347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
  gimp_install_procedure (SAVE2_PROC,
                          "Save files in PDF format",
                          "Saves files in Adobe's Portable Document Format. "
                          "PDF is designed to be easily processed by a variety "
                          "of different platforms, and is a distant cousin of "
                          "PostScript.\n"
                          "This procedure adds an extra parameter to "
                          "file-pdf-save to save layers as pages.",
                          "Barak Itkin, Lionel N., Jehan",
                          "Copyright Barak Itkin, Lionel N., Jehan",
                          "August 2009, 2017",
                          N_("Portable Document Format"),
                          "RGB*, GRAY*, INDEXED*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (save2_args), 0,
                          save2_args, NULL);

364
  gimp_install_procedure (SAVE_MULTI_PROC,
365 366 367 368 369
                          "Save files in PDF format",
                          "Saves files in Adobe's Portable Document Format. "
                          "PDF is designed to be easily processed by a variety "
                          "of different platforms, and is a distant cousin of "
                          "PostScript.",
370 371 372
                          "Barak Itkin",
                          "Copyright Barak Itkin",
                          "August 2009",
373
                          N_("_Create multipage PDF..."),
374 375 376 377 378
                          "RGB*, GRAY*, INDEXED*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (save_multi_args), 0,
                          save_multi_args, NULL);

379 380 381 382
#if 0
  gimp_plugin_menu_register (SAVE_MULTI_PROC,
                             "<Image>/File/Create/PDF");
#endif
383

384 385
  gimp_register_file_handler_mime (SAVE2_PROC, "application/pdf");
  gimp_register_save_handler (SAVE2_PROC, "pdf", "");
386 387
}

388 389 390 391 392 393 394 395 396
static cairo_status_t
write_func (void                *fp,
            const unsigned char *data,
            unsigned int         size)
{
  return fwrite (data, 1, size, fp) == size ? CAIRO_STATUS_SUCCESS
                                            : CAIRO_STATUS_WRITE_ERROR;
}

397 398 399 400 401 402 403
static void
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
{
404
  static GimpParam        values[2];
405 406 407 408 409 410 411
  GimpPDBStatusType       status = GIMP_PDB_SUCCESS;
  GimpRunMode             run_mode;
  gboolean                single_image;
  gboolean                defaults_proc;
  cairo_surface_t        *pdf_file;
  cairo_t                *cr;
  GimpExportCapabilities  capabilities;
412
  FILE                   *fp;
413
  gint                    i;
414
  GError                 *error = NULL;
415

416
  INIT_I18N ();
417
  gegl_init (NULL, NULL);
418

419 420 421 422
  /* Setting mandatory output values */
  *nreturn_vals = 1;
  *return_vals  = values;

423
  values[0].type          = GIMP_PDB_STATUS;
424 425 426 427 428
  values[0].data.d_status = status;

  /* Initializing all the settings */
  multi_page.image_count = 0;

429
  if (! init_vals (name, nparams, param, &single_image,
430
                   &defaults_proc, &run_mode))
431 432 433 434
    {
      values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
      return;
    }
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455

  /* Starting the executions */
  if (run_mode == GIMP_RUN_INTERACTIVE)
    {
      if (single_image)
        {
          if (! gui_single ())
            {
              values[0].data.d_status = GIMP_PDB_CANCEL;
              return;
            }
        }
      else if (! gui_multi ())
        {
          values[0].data.d_status = GIMP_PDB_CANCEL;
          return;
        }

      if (file_name == NULL)
        {
          values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
456
          gimp_message (_("You must select a file to save!"));
457 458 459 460
          return;
        }
    }

461
  fp = g_fopen (file_name, "wb");
462 463
  if (fp == NULL)
    {
464 465
      *nreturn_vals = 2;

466
      values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
467 468 469 470 471 472 473 474
      values[1].type          = GIMP_PDB_STRING;
      if (error == NULL)
        {
          g_set_error (&error, G_FILE_ERROR, g_file_error_from_errno (errno),
                       _("Could not open '%s' for writing: %s"),
                       gimp_filename_to_utf8 (file_name), g_strerror (errno));
        }
      values[1].data.d_string = error->message;
475 476
      return;
    }
477

478
  pdf_file = cairo_pdf_surface_create_for_stream (write_func, fp, 1, 1);
479

480 481
  if (cairo_surface_status (pdf_file) != CAIRO_STATUS_SUCCESS)
    {
482
      g_message (_("An error occurred while creating the PDF file:\n"
483 484 485 486
                   "%s\n"
                   "Make sure you entered a valid filename and that the "
                   "selected location isn't read only!"),
                 cairo_status_to_string (cairo_surface_status (pdf_file)));
487

488 489 490
      values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
      return;
    }
491

492 493
  cr = cairo_create (pdf_file);

494 495 496 497 498
  capabilities = (GIMP_EXPORT_CAN_HANDLE_RGB    |
                  GIMP_EXPORT_CAN_HANDLE_ALPHA  |
                  GIMP_EXPORT_CAN_HANDLE_GRAY   |
                  GIMP_EXPORT_CAN_HANDLE_LAYERS |
                  GIMP_EXPORT_CAN_HANDLE_INDEXED);
499 500 501 502
  /* This seems counter-intuitive, but not setting the mask capability
   * will apply any layer mask upon gimp_export_image().
   */
  if (! optimize.apply_masks)
503 504 505 506
    capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS;

  for (i = 0; i < multi_page.image_count; i++)
    {
507 508 509 510 511 512 513 514 515 516 517
      gint32    image_ID = multi_page.images[i];
      gint32   *layers;
      gint32    n_layers;
      gdouble   x_res, y_res;
      gdouble   x_scale, y_scale;
      gint32    temp;
      gint      j;

      temp = gimp_image_get_active_drawable (image_ID);
      if (temp < 1)
        continue;
518 519 520 521

      /* Save the state of the surface before any changes, so that
       * settings from one page won't affect all the others
       */
522 523
      cairo_save (cr);

524 525
      if (! (gimp_export_image (&image_ID, &temp, NULL,
                                capabilities) == GIMP_EXPORT_EXPORT))
526
        {
527 528 529 530 531 532 533
          /* gimp_drawable_histogram() only works within the bounds of
           * the selection, which is a problem (see issue #2431).
           * Instead of saving the selection, unselecting to later
           * reselect, let's just always work on a duplicate of the
           * image.
           */
          image_ID = gimp_image_duplicate (image_ID);
534
        }
535
      gimp_selection_none (image_ID);
536 537

      gimp_image_get_resolution (image_ID, &x_res, &y_res);
538 539 540 541
      x_scale = 72.0 / x_res;
      y_scale = 72.0 / y_res;

      cairo_pdf_surface_set_size (pdf_file,
542 543
                                  gimp_image_width (image_ID) * x_scale,
                                  gimp_image_height (image_ID) * y_scale);
544 545

      /* This way we set how many pixels are there in every inch.
546 547
       * It's very important for PangoCairo
       */
548 549
      cairo_surface_set_fallback_resolution (pdf_file, x_res, y_res);

550 551 552 553 554 555
      /* Cairo has a concept of user-space vs device-space units.
       * From what I understand, by default the user-space unit is the
       * typographical "point". Since we work mostly with pixels, not
       * points, the following call simply scales the transformation
       * matrix from points to pixels, relatively to the image
       * resolution, knowing that 1 typographical point == 1/72 inch.
556
       */
557 558
      cairo_scale (cr, x_scale, y_scale);

559
      layers = gimp_image_get_layers (image_ID, &n_layers);
560

561 562 563 564
      /* Fill image with background color -
       * otherwise the output PDF will always show white for background,
       * and may display artifacts at transparency boundaries
       */
565
      if (gimp_drawable_has_alpha (layers[n_layers - 1]))
566 567
        {
          GimpRGB color;
568 569 570 571

          cairo_rectangle (cr, 0.0, 0.0,
                           gimp_image_width (image_ID),
                           gimp_image_height (image_ID));
572 573 574 575 576 577 578 579 580
          gimp_context_get_background (&color);
          cairo_set_source_rgb (cr,
                                color.r,
                                color.g,
                                color.b);
          cairo_fill (cr);
        }

      /* Now, we should loop over the layers of each image */
581
      for (j = 0; j < n_layers; j++)
582
        {
583
          gint32           layer_ID;
584 585 586 587 588 589
          gint32           mask_ID    = -1;
          cairo_surface_t *mask_image = NULL;
          gdouble          opacity;
          gboolean         single_color;
          gint             x, y;

590 591 592 593 594
          if (optimize.reverse_order && optimize.layers_as_pages)
            layer_ID = layers [j];
          else
            layer_ID = layers [n_layers - j - 1];

595 596
          opacity = gimp_layer_get_opacity (layer_ID) / 100.0;

597 598
          if ((gimp_item_get_visible (layer_ID) && opacity > 0.0) ||
              ! optimize.ignore_hidden)
599
            {
600 601
              mask_ID = gimp_layer_get_mask (layer_ID);
              if (mask_ID != -1)
602
                {
603 604
                  mask_image = get_cairo_surface (mask_ID, TRUE,
                                                  &error);
605 606 607
                  if (error != NULL)
                    {
                      *nreturn_vals = 2;
608

609 610 611 612 613 614 615 616 617 618 619 620
                      /* free the resources */
                      cairo_surface_destroy (pdf_file);
                      cairo_destroy (cr);
                      fclose (fp);

                      values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;

                      values[1].type          = GIMP_PDB_STRING;
                      values[1].data.d_string = error->message;
                      return;
                    }
                }
621 622 623
              gimp_drawable_offsets (layer_ID, &x, &y);

              if (! gimp_item_is_text_layer (layer_ID))
624
                {
625
                  /* For raster layers */
626

627
                  GimpRGB layer_color;
628

629 630 631 632 633
                  layer_color = get_layer_color (layer_ID, &single_color);

                  cairo_rectangle (cr, x, y,
                                   gimp_drawable_width (layer_ID),
                                   gimp_drawable_height (layer_ID));
634

635
                  if (optimize.vectorize && single_color)
636
                    {
637 638 639 640 641 642
                      cairo_set_source_rgba (cr,
                                             layer_color.r,
                                             layer_color.g,
                                             layer_color.b,
                                             layer_color.a * opacity);
                      if (mask_ID != -1)
643 644 645 646 647 648
                        cairo_mask_surface (cr, mask_image, x, y);
                      else
                        cairo_fill (cr);
                    }
                  else
                    {
649 650
                      cairo_surface_t *layer_image;

651 652
                      layer_image = get_cairo_surface (layer_ID, FALSE,
                                                       &error);
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
                      if (error != NULL)
                        {
                          *nreturn_vals = 2;

                          /* free the resources */
                          cairo_surface_destroy (pdf_file);
                          cairo_destroy (cr);
                          fclose (fp);

                          values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;

                          values[1].type          = GIMP_PDB_STRING;
                          values[1].data.d_string = error->message;
                          return;
                        }
668

669
                      cairo_clip (cr);
670

671 672 673 674
                      cairo_set_source_surface (cr, layer_image, x, y);
                      cairo_push_group (cr);
                      cairo_paint_with_alpha (cr, opacity);
                      cairo_pop_group_to_source (cr);
675 676

                      if (mask_ID != -1)
677 678 679
                        cairo_mask_surface (cr, mask_image, x, y);
                      else
                        cairo_paint (cr);
680

681 682 683 684 685 686 687
                      cairo_reset_clip (cr);

                      cairo_surface_destroy (layer_image);
                    }
                }
              else
                {
688 689
                  /* For text layers */
                  drawText (layer_ID, opacity, cr, x_res, y_res);
690
                }
691
              /* draw new page if "layers as pages" option is checked */
692 693
              if (optimize.layers_as_pages &&
                  g_strcmp0 (name, SAVE2_PROC) == 0)
694
                cairo_show_page (cr);
695 696
            }
          /* We are done with the layer - time to free some resources */
697 698
          if (mask_ID != -1)
            cairo_surface_destroy (mask_image);
699
        }
700

701 702 703
      /* We are done with this image - Show it!
       * Unless that's a multi-page to avoid blank page at the end
       */
704 705
      if (g_strcmp0 (name, SAVE2_PROC) != 0 ||
          ! optimize.layers_as_pages)
706
        cairo_show_page (cr);
707 708
      cairo_restore (cr);

709
      gimp_image_delete (image_ID);
710 711 712 713 714 715
    }

  /* We are done with all the images - time to free the resources */
  cairo_surface_destroy (pdf_file);
  cairo_destroy (cr);

716
  fclose (fp);
717

718 719
  /* Finally done, let's save the parameters */
  gimp_set_data (DATA_OPTIMIZE, &optimize, sizeof (optimize));
720 721

  if (! single_image)
722 723 724 725 726 727 728
    {
      g_strlcpy (multi_page.file_name, file_name, MAX_FILE_NAME_LENGTH);
      gimp_set_data (DATA_IMAGE_LIST, &multi_page, sizeof (multi_page));
    }
}

/******************************************************/
729
/* Beginning of parameter handling functions          */
730 731
/******************************************************/

732 733
/* A function that takes care of loading the basic parameters
 */
734
static gboolean
735 736 737 738 739 740 741 742
init_vals (const gchar      *name,
           gint              nparams,
           const GimpParam  *param,
           gboolean         *single_image,
           gboolean         *defaults_proc,
           GimpRunMode      *run_mode)
{
  gboolean had_saved_list = FALSE;
743
  gboolean single;
744 745 746 747
  gboolean defaults = FALSE;
  gint32   i;
  gint32   image;

748
  if ((g_str_equal (name, SAVE_PROC) && nparams == SA_ARG_COUNT - 2) ||
749
      (g_str_equal (name, SAVE2_PROC) && nparams == SA_ARG_COUNT))
750 751 752 753 754
    {
      single = TRUE;
      *run_mode = param[SA_RUN_MODE].data.d_int32;
      image = param[SA_IMAGE].data.d_int32;
      file_name = param[SA_FILENAME].data.d_string;
755

756
      if (*run_mode == GIMP_RUN_NONINTERACTIVE)
757 758 759 760
        {
          optimize.apply_masks = param[SA_APPLY_MASKS].data.d_int32;
          optimize.vectorize = param[SA_VECTORIZE].data.d_int32;
          optimize.ignore_hidden = param[SA_IGNORE_HIDDEN].data.d_int32;
761
          if (nparams == SA_ARG_COUNT)
762
          {
763
            optimize.layers_as_pages = param[SA_LAYERS_AS_PAGES].data.d_int32;
764 765
            optimize.reverse_order = param[SA_REVERSE_ORDER].data.d_int32;
          }
766 767 768 769 770
        }
      else
        defaults = TRUE;
    }
  else if (g_str_equal (name, SAVE_MULTI_PROC))
771
    {
772 773 774 775 776 777
      single = FALSE;
      if (nparams != SMA_ARG_COUNT)
        return FALSE;

      *run_mode = param[SMA_RUN_MODE].data.d_int32;
      image = -1;
778
      file_name = param[SMA_FILENAME].data.d_string;
779 780 781 782

      optimize.apply_masks = param[SMA_APPLY_MASKS].data.d_int32;
      optimize.vectorize = param[SMA_VECTORIZE].data.d_int32;
      optimize.ignore_hidden = param[SMA_IGNORE_HIDDEN].data.d_int32;
783
    }
784
  else
785 786 787
    {
      return FALSE;
    }
788 789 790 791 792 793 794 795 796 797

  switch (*run_mode)
    {
    case GIMP_RUN_NONINTERACTIVE:
      if (single)
        {
          init_image_list_defaults (image);
        }
      else
        {
798 799 800 801
          multi_page.image_count = param[SMA_COUNT].data.d_int32;
          if (param[SMA_IMAGES].data.d_int32array != NULL)
            for (i = 0; i < param[SMA_COUNT].data.d_int32; i++)
              multi_page.images[i] = param[SMA_IMAGES].data.d_int32array[i];
802 803 804 805 806 807 808
        }
      break;

    case GIMP_RUN_INTERACTIVE:
      /* Possibly retrieve data */
      gimp_get_data (DATA_OPTIMIZE, &optimize);
      had_saved_list = gimp_get_data (DATA_IMAGE_LIST, &multi_page);
809 810

      if (had_saved_list && (file_name == NULL || strlen (file_name) == 0))
811 812 813 814
        {
          file_name = multi_page.file_name;
        }

815
      if (single || ! had_saved_list )
816 817 818 819 820
        init_image_list_defaults (image);
      break;

    case GIMP_RUN_WITH_LAST_VALS:
      /* Possibly retrieve data */
821
      if (! single)
822 823 824 825 826 827 828 829
        {
          had_saved_list = gimp_get_data (DATA_IMAGE_LIST, &multi_page);
          if (had_saved_list)
            {
              file_name = multi_page.file_name;
            }
        }
      else
830 831 832
        {
          init_image_list_defaults (image);
        }
833 834 835 836 837 838 839 840
      gimp_get_data (DATA_OPTIMIZE, &optimize);
      break;
    }

  *defaults_proc = defaults;
  *single_image = single;

  validate_image_list ();
841 842

  return TRUE;
843 844 845 846 847 848 849 850
}

/* A function that initializes the image list to default values */
static void
init_image_list_defaults (gint32 image)
{
  if (image != -1)
    {
851
      multi_page.images[0]   = image;
852 853
      multi_page.image_count = 1;
    }
854 855 856 857
  else
    {
      multi_page.image_count = 0;
    }
858 859
}

860 861 862
/* A function that removes images that are no longer valid from the
 * image list
 */
863 864 865 866
static void
validate_image_list (void)
{
  gint32  valid = 0;
867
  guint32 i     = 0;
868 869 870 871 872 873 874 875 876

  for (i = 0 ; i < MAX_PAGE_COUNT && i < multi_page.image_count ; i++)
    {
      if (gimp_image_is_valid (multi_page.images[i]))
        {
          multi_page.images[valid] = multi_page.images[i];
          valid++;
        }
    }
877

878 879 880
  multi_page.image_count = valid;
}

881

882
/******************************************************/
883
/* Beginning of GUI functions                         */
884
/******************************************************/
885

886
/* The main GUI function for saving single-paged PDFs */
887

888 889 890 891 892
static gboolean
gui_single (void)
{
  GtkWidget *window;
  GtkWidget *vbox;
893 894
  GtkWidget *vectorize_c;
  GtkWidget *ignore_hidden_c;
895
  GtkWidget *apply_c;
896
  GtkWidget *layers_as_pages_c;
897
  GtkWidget *reverse_order_c;
898 899
  GtkWidget *frame;
  gchar     *text;
900
  gboolean   run;
901
  gint32     n_layers;
902 903 904

  gimp_ui_init (PLUG_IN_BINARY, FALSE);

905
  window = gimp_export_dialog_new ("PDF", PLUG_IN_ROLE, SAVE_PROC);
906

907
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
908 909 910 911 912
  gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (window)),
                      vbox, TRUE, TRUE, 0);

  gtk_container_set_border_width (GTK_CONTAINER (window), 12);

913
  ignore_hidden_c = gtk_check_button_new_with_label (_("Omit hidden layers and layers with zero opacity"));
914 915
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ignore_hidden_c),
                                optimize.ignore_hidden);
916
  gtk_box_pack_end (GTK_BOX (vbox), ignore_hidden_c, TRUE, TRUE, 0);
917

918
  vectorize_c = gtk_check_button_new_with_label (_("Convert bitmaps to vector graphics where possible"));
919 920
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vectorize_c),
                                optimize.vectorize);
921
  gtk_box_pack_end (GTK_BOX (vbox), vectorize_c, TRUE, TRUE, 0);
922

923
  apply_c = gtk_check_button_new_with_label (_("Apply layer masks before saving"));
924 925
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_c),
                                optimize.apply_masks);
926
  gtk_box_pack_end (GTK_BOX (vbox), apply_c, TRUE, TRUE, 0);
927
  gimp_help_set_help_data (apply_c, _("Keeping the masks will not change the output"), NULL);
928

929 930 931
  /* Frame for multi-page from layers. */
  frame = gtk_frame_new (NULL);
  gtk_box_pack_end (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
932

933 934 935 936 937
  text = g_strdup_printf (_("Layers as pages (%s)"),
                          optimize.reverse_order ?
                          _("top layers first") : _("bottom layers first"));
  layers_as_pages_c = gtk_check_button_new_with_label (text);
  g_free (text);
938 939
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (layers_as_pages_c),
                                optimize.layers_as_pages);
940
  gtk_frame_set_label_widget (GTK_FRAME (frame), layers_as_pages_c);
941
  gimp_image_get_layers (multi_page.images[0], &n_layers);
942 943 944 945 946

  reverse_order_c = gtk_check_button_new_with_label (_("Reverse the pages order"));
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (reverse_order_c),
                                optimize.reverse_order);
  gtk_container_add (GTK_CONTAINER (frame), reverse_order_c);
947

948
  if (n_layers <= 1)
949 950 951 952 953
    {
      gtk_widget_set_sensitive (layers_as_pages_c, FALSE);
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (layers_as_pages_c),
                                    FALSE);
    }
954 955 956 957

  g_object_bind_property (layers_as_pages_c, "active",
                          reverse_order_c,  "sensitive",
                          G_BINDING_SYNC_CREATE);
958 959 960
  g_signal_connect (G_OBJECT (reverse_order_c), "toggled",
                    G_CALLBACK (reverse_order_toggled),
                    layers_as_pages_c);
961

962 963 964 965
  gtk_widget_show_all (window);

  run = gtk_dialog_run (GTK_DIALOG (window)) == GTK_RESPONSE_OK;

966 967 968 969 970 971
  optimize.ignore_hidden =
    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ignore_hidden_c));
  optimize.vectorize =
    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vectorize_c));
  optimize.apply_masks =
    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_c));
972 973
  optimize.layers_as_pages =
    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (layers_as_pages_c));
974 975
  optimize.reverse_order =
    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (reverse_order_c));
976 977

  gtk_widget_destroy (window);
978

979 980
  return run;
}
981

982
/* The main GUI function for saving multi-paged PDFs */
983

984 985 986 987 988 989 990 991 992
static gboolean
gui_multi (void)
{
  GtkWidget   *window;
  GtkWidget   *vbox;
  GtkWidget   *file_label;
  GtkWidget   *file_entry;
  GtkWidget   *file_browse;
  GtkWidget   *file_hbox;
993 994
  GtkWidget   *vectorize_c;
  GtkWidget   *ignore_hidden_c;
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
  GtkWidget   *apply_c;
  GtkWidget   *scroll;
  GtkWidget   *page_view;
  GtkWidget   *h_but_box;
  GtkWidget   *del;
  GtkWidget   *h_box;
  GtkWidget   *img_combo;
  GtkWidget   *add_image;
  gboolean     run;
  const gchar *temp;

  gimp_ui_init (PLUG_IN_BINARY, FALSE);

1008
  window = gimp_export_dialog_new ("PDF", PLUG_IN_ROLE, SAVE_MULTI_PROC);
1009

1010
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
1011 1012 1013 1014 1015
  gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (window)),
                      vbox, TRUE, TRUE, 0);

  gtk_container_set_border_width (GTK_CONTAINER (window), 12);

1016
  file_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
1017
  file_label = gtk_label_new (_("Save to:"));
1018 1019 1020
  file_entry = gtk_entry_new ();
  if (file_name != NULL)
    gtk_entry_set_text (GTK_ENTRY (file_entry), file_name);
1021
  file_browse = gtk_button_new_with_label (_("Browse..."));
1022
  file_choose = gtk_file_chooser_dialog_new (_("Multipage PDF export"),
1023 1024 1025 1026
                                             GTK_WINDOW (window),
                                             GTK_FILE_CHOOSER_ACTION_SAVE,
                                             _("_Save"),   GTK_RESPONSE_OK,
                                             _("_Cancel"), GTK_RESPONSE_CANCEL,
1027 1028 1029
                                             NULL);

  gtk_box_pack_start (GTK_BOX (file_hbox), file_label, FALSE, FALSE, 0);
1030
  gtk_box_pack_start (GTK_BOX (file_hbox), file_entry, TRUE, TRUE, 0);
1031 1032
  gtk_box_pack_start (GTK_BOX (file_hbox), file_browse, FALSE, FALSE, 0);

1033
  gtk_box_pack_start (GTK_BOX (vbox), file_hbox, TRUE, TRUE, 0);
1034 1035 1036 1037 1038

  page_view = gtk_icon_view_new ();
  model = create_model ();
  gtk_icon_view_set_model (GTK_ICON_VIEW (page_view), model);
  gtk_icon_view_set_reorderable (GTK_ICON_VIEW (page_view), TRUE);
1039 1040
  gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (page_view),
                                    GTK_SELECTION_MULTIPLE);
1041 1042 1043 1044 1045 1046 1047 1048

  gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (page_view), THUMB);
  gtk_icon_view_set_text_column (GTK_ICON_VIEW (page_view), PAGE_NUMBER);
  gtk_icon_view_set_tooltip_column (GTK_ICON_VIEW (page_view), IMAGE_NAME);

  scroll = gtk_scrolled_window_new (NULL, NULL);
  gtk_widget_set_size_request (scroll, -1, 300);

1049 1050
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1051 1052
  gtk_container_add (GTK_CONTAINER (scroll), page_view);

1053
  gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
1054

1055
  h_but_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
1056 1057
  gtk_button_box_set_layout (GTK_BUTTON_BOX (h_but_box), GTK_BUTTONBOX_START);

1058
  del = gtk_button_new_with_label (_("Remove the selected pages"));
1059
  gtk_box_pack_start (GTK_BOX (h_but_box), del, TRUE, TRUE, 0);
1060 1061 1062

  gtk_box_pack_start (GTK_BOX (vbox), h_but_box, FALSE, FALSE, 0);

1063
  h_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
1064 1065 1066 1067

  img_combo = gimp_image_combo_box_new (NULL, NULL);
  gtk_box_pack_start (GTK_BOX (h_box), img_combo, FALSE, FALSE, 0);

1068
  add_image = gtk_button_new_with_label (_("Add this image"));
1069 1070 1071 1072
  gtk_box_pack_start (GTK_BOX (h_box), add_image, FALSE, FALSE, 0);

  gtk_box_pack_start (GTK_BOX (vbox), h_box, FALSE, FALSE, 0);

1073
  ignore_hidden_c = gtk_check_button_new_with_label (_("Omit hidden layers and layers with zero opacity"));
1074 1075
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ignore_hidden_c),
                                optimize.ignore_hidden);
1076
  gtk_box_pack_end (GTK_BOX (vbox), ignore_hidden_c, FALSE, FALSE, 0);
1077

1078
  vectorize_c = gtk_check_button_new_with_label (_("Convert bitmaps to vector graphics where possible"));
1079 1080
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vectorize_c),
                                optimize.vectorize);
1081
  gtk_box_pack_end (GTK_BOX (vbox), vectorize_c, FALSE, FALSE, 0);
1082

1083
  apply_c = gtk_check_button_new_with_label (_("Apply layer masks before saving"));
1084 1085
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_c),
                                optimize.apply_masks);
1086
  gtk_box_pack_end (GTK_BOX (vbox), apply_c, FALSE, FALSE, 0);
1087
  gimp_help_set_help_data (apply_c, _("Keeping the masks will not change the output"), NULL);
1088 1089 1090 1091

  gtk_widget_show_all (window);

  g_signal_connect (G_OBJECT (file_browse), "clicked",
1092 1093
                    G_CALLBACK (choose_file_call),
                    file_entry);
1094 1095

  g_signal_connect (G_OBJECT (add_image), "clicked",
1096 1097
                    G_CALLBACK (add_image_call),
                    img_combo);
1098 1099

  g_signal_connect (G_OBJECT (del), "clicked",
1100 1101
                    G_CALLBACK (del_image_call),
                    page_view);
1102 1103

  g_signal_connect (G_OBJECT (model), "row-deleted",
1104 1105
                    G_CALLBACK (remove_call),
                    NULL);
1106 1107 1108 1109 1110 1111 1112 1113

  run = gtk_dialog_run (GTK_DIALOG (window)) == GTK_RESPONSE_OK;

  run &= get_image_list ();

  temp = gtk_entry_get_text (GTK_ENTRY (file_entry));
  g_stpcpy (file_name, temp);

1114 1115 1116 1117 1118 1119
  optimize.ignore_hidden =
    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ignore_hidden_c));
  optimize.vectorize =
    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vectorize_c));
  optimize.apply_masks =
    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_c));
1120 1121

  gtk_widget_destroy (window);
1122

1123 1124 1125
  return run;
}

1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138
static void
reverse_order_toggled (GtkToggleButton *reverse_order,
                       GtkButton       *layers_as_pages)
{
  gchar *text;

  text = g_strdup_printf (_("Layers as pages (%s)"),
                          gtk_toggle_button_get_active (reverse_order) ?
                          _("top layers first") : _("bottom layers first"));
  gtk_button_set_label (layers_as_pages, text);
  g_free (text);
}

1139
/* A function that is called when the button for browsing for file
1140 1141
 * locations was clicked
 */
1142 1143 1144 1145 1146
static void
choose_file_call (GtkWidget *browse_button,
                  gpointer   file_entry)
{
  GFile *file = g_file_new_for_path (gtk_entry_get_text (GTK_ENTRY (file_entry)));
1147 1148 1149

  gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (file_choose),
                            g_file_get_uri (file));
1150 1151 1152 1153 1154

  if (gtk_dialog_run (GTK_DIALOG (file_choose)) == GTK_RESPONSE_OK)
    {
      file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (file_choose));
      gtk_entry_set_text (GTK_ENTRY (file_entry), g_file_get_path (file));
1155
    }
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168

  file_name = g_file_get_path (file);
  gtk_widget_hide (file_choose);
}

/* A function to create the basic GtkTreeModel for the icon view */
static GtkTreeModel*
create_model (void)
{
  GtkListStore *model;
  guint32       i;

  /* validate_image_list was called earlier, so all the images
1169 1170
   * up to multi_page.image_count are valid
   */
1171 1172 1173 1174 1175 1176 1177 1178
  model = gtk_list_store_new (4,
                              GDK_TYPE_PIXBUF, /* THUMB */
                              G_TYPE_STRING,   /* PAGE_NUMBER */
                              G_TYPE_STRING,   /* IMAGE_NAME */
                              G_TYPE_INT);     /* IMAGE_ID */

  for (i = 0 ; i < multi_page.image_count && i < MAX_PAGE_COUNT ; i++)
    {
1179 1180 1181 1182 1183 1184
      GtkTreeIter iter;
      gint32      image = multi_page.images[i];
      GdkPixbuf  *pixbuf;

      pixbuf = gimp_image_get_thumbnail (image, THUMB_WIDTH, THUMB_HEIGHT,
                                         GIMP_PIXBUF_SMALL_CHECKS);
1185 1186 1187

      gtk_list_store_append (model, &iter);
      gtk_list_store_set (model, &iter,
1188 1189 1190 1191
                          THUMB,       pixbuf,
                          PAGE_NUMBER, g_strdup_printf (_("Page %d"), i + 1),
                          IMAGE_NAME,  gimp_image_get_name (image),
                          IMAGE_ID,    image,
1192 1193
                          -1);

1194
      g_object_unref (pixbuf);
1195 1196 1197 1198 1199
    }

  return GTK_TREE_MODEL (model);
}

1200 1201 1202
/* A function that puts the images from the model inside the images
 * (pages) array
 */
1203 1204 1205 1206
static gboolean
get_image_list (void)
{
  GtkTreeIter iter;
1207
  gboolean    valid;
1208 1209 1210

  multi_page.image_count = 0;

1211 1212 1213
  for (valid = gtk_tree_model_get_iter_first (model, &iter);
       valid;
       valid = gtk_tree_model_iter_next (model, &iter))
1214
    {
1215
      gint32 image;
1216 1217 1218 1219 1220 1221 1222 1223

      gtk_tree_model_get (model, &iter,
                          IMAGE_ID, &image,
                          -1);
      multi_page.images[multi_page.image_count] = image;
      multi_page.image_count++;
    }

1224 1225 1226 1227 1228 1229 1230
  if (multi_page.image_count == 0)
    {
      g_message (_("Error! In order to save the file, at least one image "
                   "should be added!"));
      return FALSE;
    }

1231 1232 1233
  return TRUE;
}

1234 1235 1236
/* A function that is called when the button for adding an image was
 * clicked
 */
1237 1238 1239 1240 1241 1242 1243
static void
add_image_call (GtkWidget *widget,
                gpointer   img_combo)
{
  GtkListStore *store;
  GtkTreeIter   iter;
  gint32        image;
1244
  GdkPixbuf    *pixbuf;
1245 1246 1247 1248 1249 1250 1251

  dnd_remove = FALSE;

  gimp_int_combo_box_get_active (img_combo, &image);

  store = GTK_LIST_STORE (model);

1252 1253 1254
  pixbuf = gimp_image_get_thumbnail (image, THUMB_WIDTH, THUMB_HEIGHT,
                                     GIMP_PIXBUF_SMALL_CHECKS);

1255 1256
  gtk_list_store_append (store, &iter);
  gtk_list_store_set (store, &iter,
1257 1258 1259 1260 1261 1262 1263 1264
                      PAGE_NUMBER, g_strdup_printf (_("Page %d"),
                                                    multi_page.image_count+1),
                      THUMB,       pixbuf,
                      IMAGE_NAME,  gimp_image_get_name (image),
                      IMAGE_ID,    image,
                      -1);

  g_object_unref (pixbuf);
1265 1266 1267 1268 1269 1270

  multi_page.image_count++;

  dnd_remove = TRUE;
}

1271 1272 1273
/* A function that is called when the button for deleting the selected
 * images was clicked
 */
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
static void
del_image_call (GtkWidget *widget,
                gpointer   icon_view)
{
  GList                *list;
  GtkTreeRowReference **items;
  GtkTreePath          *item_path;
  GtkTreeIter           item;
  gpointer              temp;
  guint32               len;

  dnd_remove = FALSE;

  list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view));

  len = g_list_length (list);
  if (len > 0)
    {
1292 1293
      gint i;

1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
      items = g_newa (GtkTreeRowReference*, len);

      for (i = 0; i < len; i++)
        {
          temp = g_list_nth_data (list, i);
          items[i] = gtk_tree_row_reference_new (model, temp);
          gtk_tree_path_free (temp);
        }
      g_list_free (list);

      for (i = 0; i < len; i++)
        {
          item_path = gtk_tree_row_reference_get_path (items[i]);

1308
          gtk_tree_model_get_iter (model, &item, item_path);
1309 1310 1311
          gtk_list_store_remove (GTK_LIST_STORE (model), &item);

          gtk_tree_path_free (item_path);
1312

1313 1314 1315 1316 1317 1318 1319 1320 1321 1322
          gtk_tree_row_reference_free (items[i]);
          multi_page.image_count--;
        }
    }

  dnd_remove = TRUE;

  recount_pages ();
}

1323 1324 1325
/* A function that is called on rows-deleted signal. It will call the
 * function to relabel the pages
 */
1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336
static void
remove_call (GtkTreeModel *tree_model,
             GtkTreePath  *path,
             gpointer      user_data)
{

  if (dnd_remove)
    /* The gtk documentation says that we should not free the indices array */
    recount_pages ();
}

1337 1338 1339
/* A function to relabel the pages in the icon view, when their order
 * was changed
 */
1340 1341 1342 1343 1344 1345 1346 1347 1348 1349
static void
recount_pages (void)
{
  GtkListStore *store;
  GtkTreeIter   iter;
  gboolean      valid;
  gint32        i = 0;

  store = GTK_LIST_STORE (model);

1350 1351 1352
  for (valid = gtk_tree_model_get_iter_first (model, &iter);
       valid;
       valid = gtk_tree_model_iter_next (model, &iter))
1353 1354
    {
      gtk_list_store_set (store, &iter,
1355
                          PAGE_NUMBER, g_strdup_printf (_("Page %d"), i + 1),
1356 1357 1358 1359 1360
                          -1);
      i++;
    }
}

1361

1362
/******************************************************/
1363
/* Beginning of the actual PDF functions              */
1364 1365
/******************************************************/

1366
static cairo_surface_t *
1367 1368 1369
get_cairo_surface (gint32     drawable_ID,
                   gboolean   as_mask,
                   GError   **error)
1370
{
1371 1372
  GeglBuffer      *src_buffer;
  GeglBuffer      *dest_buffer;
1373
  cairo_surface_t *surface;
1374
  cairo_status_t   status;
1375
  cairo_format_t   format;
1376 1377 1378
  gint             width;
  gint             height;

1379
  src_buffer = gimp_drawable_get_buffer (drawable_ID);
1380

1381 1382
  width  = gegl_buffer_get_width  (src_buffer);
  height = gegl_buffer_get_height (src_buffer);
1383

1384 1385 1386
  if (as_mask)
    format = CAIRO_FORMAT_A8;
  else if (gimp_drawable_has_alpha (drawable_ID))
1387 1388 1389
    format = CAIRO_FORMAT_ARGB32;
  else
    format = CAIRO_FORMAT_RGB24;
1390 1391 1392

  surface = cairo_image_surface_create (format, width, height);

1393 1394 1395 1396 1397 1398 1399 1400 1401
  status = cairo_surface_status (surface);
  if (status != CAIRO_STATUS_SUCCESS)
    {
      switch (status)
        {
        case CAIRO_STATUS_INVALID_SIZE:
          g_set_error_literal (error,
                               GIMP_PLUGIN_PDF_SAVE_ERROR,
                               GIMP_PLUGIN_PDF_SAVE_ERROR_FAILED,
Piotr Drąg's avatar
Piotr Drąg committed
1402
                               _("Cannot handle the size (either width or height) of the image."));
1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415
          break;
        default:
          g_set_error (error,
                       GIMP_PLUGIN_PDF_SAVE_ERROR,
                       GIMP_PLUGIN_PDF_SAVE_ERROR_FAILED,
                       "Cairo error: %s",
                       cairo_status_to_string (status));
          break;
        }

      return NULL;
    }

1416
  dest_buffer = gimp_cairo_surface_create_buffer (surface);
1417 1418 1419 1420 1421 1422 1423 1424 1425 1426
  if (as_mask)
    {
      /* src_buffer represents a mask in "Y u8", "Y u16", etc. formats.
       * Yet cairo_mask*() functions only care about the alpha channel of
       * the surface. Hence I change the format of dest_buffer so that the
       * Y channel of src_buffer actually refers to the A channel of
       * dest_buffer/surface in Cairo.
       */
      gegl_buffer_set_format (dest_buffer, babl_format ("Y u8"));
    }
1427

1428
  gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE, dest_buffer, NULL);
1429 1430

  cairo_surface_mark_dirty (surface);
1431

1432 1433
  g_object_unref (src_buffer);
  g_object_unref (dest_buffer);
1434

1435 1436 1437
  return surface;
}

1438 1439 1440
/* A function to check if a drawable is single colored This allows to
 * convert bitmaps to vector where possible
 */
1441
static GimpRGB
1442 1443
get_layer_color (gint32    layer_ID,
                 gboolean *single)
1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456
{
  GimpRGB col;
  gdouble red, green, blue, alpha;
  gdouble dev, devSum;
  gdouble median, pixels, count, precentile;

  devSum = 0;
  red = 0;
  green = 0;
  blue = 0;
  alpha = 0;
  dev = 0;

1457
  if (gimp_drawable_is_indexed (layer_ID))
1458
    {
1459
      /* FIXME: We can't do a proper histogram on indexed layers! */