~ubuntu-branches/ubuntu/vivid/gimp/vivid

« back to all changes in this revision

Viewing changes to plug-ins/common/file-pdf-save.c

  • Committer: Package Import Robot
  • Author(s): Jordi Mallach
  • Date: 2012-05-08 18:50:03 UTC
  • mto: (1.1.26) (0.5.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 71.
  • Revision ID: package-import@ubuntu.com-20120508185003-tltkvbaysf8d2426
ImportĀ upstreamĀ versionĀ 2.8.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* GIMP - The GNU Image Manipulation Program
 
2
 *
 
3
 * file-pdf-save.c - PDF file exporter, based on the cairo PDF surface
 
4
 *
 
5
 * Copyright (C) 2010 Barak Itkin <lightningismyname@gmail.com>
 
6
 *
 
7
 * This program is free software: you can redistribute it and/or modify
 
8
 * it under the terms of the GNU General Public License as published by
 
9
 * the Free Software Foundation; either version 3 of the License, or
 
10
 * (at your option) any later version.
 
11
 *
 
12
 * This program is distributed in the hope that it will be useful,
 
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
 * GNU General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU General Public License
 
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
 */
 
20
 
 
21
/* The PDF export plugin has 3 main procedures:
 
22
 * 1. file-pdf-save
 
23
 *    This is the main procedure. It has 3 options for optimizations of
 
24
 *    the pdf file, and it can show a gui. This procedure works on a single
 
25
 *    image.
 
26
 * 2. file-pdf-save-defaults
 
27
 *    This procedures is the one that will be invoked by gimp's file-save,
 
28
 *    when the pdf extension is chosen. If it's in RUN_INTERACTIVE, it will
 
29
 *    pop a user interface with more options, like file-pdf-save. If it's in
 
30
 *    RUN_NONINTERACTIVE, it will simply use the default values. Note that on
 
31
 *    RUN_WITH_LAST_VALS there will be no gui, however the values will be the
 
32
 *    ones that were used in the last interactive run (or the defaults if none
 
33
 *    are available.
 
34
 * 3. file-pdf-save-multi
 
35
 *    This procedures is more advanced, and it allows the creation of multiple
 
36
 *    paged pdf files. It will be located in File/Create/Multiple page PDF...
 
37
 *
 
38
 * It was suggested that file-pdf-save-multi will be removed from the UI as it
 
39
 * does not match the product vision (GIMP isn't a program for editing multiple
 
40
 * paged documents).
 
41
 */
 
42
 
 
43
/* Known Issues (except for the coding style issues):
 
44
 * 1. Grayscale layers are inverted (although layer masks which are not grayscale,
 
45
 * are not inverted)
 
46
 * 2. Exporting some fonts doesn't work since gimp_text_layer_get_font Returns a
 
47
 * font which is sometimes incompatiable with pango_font_description_from_string
 
48
 * (gimp_text_layer_get_font sometimes returns suffixes such as "semi-expanded" to
 
49
 * the font's name although the GIMP's font selection dialog shows the don'ts name
 
50
 * normally - This should be checked again in GIMP 2.7)
 
51
 * 3. Indexed layers can't be optimized yet (Since gimp_histogram won't work on
 
52
 * indexed layers)
 
53
 * 4. Rendering the pango layout requires multiplying the size in PANGO_SCALE. This
 
54
 * means I'll need to do some hacking on the markup returned from GIMP.
 
55
 * 5. When accessing the contents of layer groups is supported, we should do use it
 
56
 * (since this plugin should preserve layers).
 
57
 *
 
58
 * Also, there are 2 things which we should warn the user about:
 
59
 * 1. Cairo does not support bitmap masks for text.
 
60
 * 2. Currently layer modes are ignored. We do support layers, including
 
61
 * transparency and opacity, but layer modes are not supported.
 
62
 */
 
63
 
 
64
/* Changelog
 
65
 *
 
66
 * April 29, 2009 | Barak Itkin <lightningismyname@gmail.com>
 
67
 *   First version of the plugin. This is only a proof of concept and not a full
 
68
 *   working plugin.
 
69
 *
 
70
 * May 6, 2009 Barak | Itkin <lightningismyname@gmail.com>
 
71
 *   Added new features and several bugfixes:
 
72
 *   - Added handling for image resolutions
 
73
 *   - fixed the behaviour of getting font sizes
 
74
 *   - Added various optimizations (solid rectangles instead of bitmaps, ignoring
 
75
 *     invisible layers, etc.) as a macro flag.
 
76
 *   - Added handling for layer masks, use CAIRO_FORMAT_A8 for grayscale drawables.
 
77
 *   - Indexed layers are now supported
 
78
 *
 
79
 * August 17, 2009 | Barak Itkin <lightningismyname@gmail.com>
 
80
 *   Most of the plugin was rewritten from scratch and it now has several new
 
81
 *   features:
 
82
 *   - Got rid of the optimization macros in the code. The gui now allows to
 
83
 *     select which optimizations to apply.
 
84
 *   - Added a procedure to allow the creation of multiple paged PDF's
 
85
 *   - Registered the plugin on "<Image>/File/Create/PDF"
 
86
 *
 
87
 * August 21, 2009 | Barak Itkin <lightningismyname@gmail.com>
 
88
 *   Fixed a typo that prevented the plugin from compiling...
 
89
 *   A migration to the new GIMP 2.8 api, which includes:
 
90
 *   - Now using gimp_export_dialog_new
 
91
 *   - Using gimp_text_layer_get_hint_style (2.8) instead of the depreceated
 
92
 *     gimp_text_layer_get_hinting (2.6).
 
93
 *
 
94
 * August 24, 2010 | Barak Itkin <lightningismyname@gmail.com>
 
95
 *   More migrations to the new GIMP 2.8 api:
 
96
 *   - Now using the GimpItem api
 
97
 *   - Using gimp_text_layer_get_markup where possible
 
98
 *   - Fixed some compiler warnings
 
99
 *   Also merged the header and c file into one file, Updated some of the comments
 
100
 *   and documentation, and moved this into the main source repository.
 
101
 */
 
102
 
 
103
#include "config.h"
 
104
 
 
105
#include <cairo-pdf.h>
 
106
#include <pango/pangocairo.h>
 
107
 
 
108
#include <libgimp/gimp.h>
 
109
#include <libgimp/gimpui.h>
 
110
 
 
111
#include "libgimp/stdplugins-intl.h"
 
112
 
 
113
#define SAVE_PROC               "file-pdf-save"
 
114
#define SAVE_MULTI_PROC         "file-pdf-save-multi"
 
115
#define PLUG_IN_BINARY          "file-pdf-save"
 
116
#define PLUG_IN_ROLE            "gimp-file-pdf-save"
 
117
 
 
118
#define DATA_OPTIMIZE           "file-pdf-data-optimize"
 
119
#define DATA_IMAGE_LIST         "file-pdf-data-multi-page"
 
120
 
 
121
/* Gimp will crash before you reach this limitation :D */
 
122
#define MAX_PAGE_COUNT           350
 
123
#define MAX_FILE_NAME_LENGTH     350
 
124
 
 
125
#define THUMB_WIDTH              90
 
126
#define THUMB_HEIGHT             120
 
127
 
 
128
typedef struct {
 
129
  gboolean vectorize;
 
130
  gboolean ignore_hidden;
 
131
  gboolean apply_masks;
 
132
} PdfOptimize;
 
133
 
 
134
typedef struct {
 
135
  gint32 images[MAX_PAGE_COUNT];
 
136
  guint32 image_count;
 
137
  gchar file_name[MAX_FILE_NAME_LENGTH];
 
138
} PdfMultiPage;
 
139
 
 
140
typedef struct {
 
141
  PdfOptimize optimize;
 
142
  GArray *images;
 
143
} PdfMultiVals;
 
144
 
 
145
enum {
 
146
  THUMB,
 
147
  PAGE_NUMBER,
 
148
  IMAGE_NAME,
 
149
  IMAGE_ID
 
150
};
 
151
 
 
152
typedef struct {
 
153
  GdkPixbuf *thumb;
 
154
  gint32 page_number;
 
155
  gchar* image_name;
 
156
} Page;
 
157
 
 
158
 
 
159
static gboolean           init_vals                  (const gchar *name,
 
160
                                                      gint nparams,
 
161
                                                      const GimpParam *param,
 
162
                                                      gboolean *single,
 
163
                                                      gboolean *defaults,
 
164
                                                      GimpRunMode  *run_mode);
 
165
 
 
166
static void               init_image_list_defaults   (gint32 image);
 
167
 
 
168
static void               validate_image_list        (void);
 
169
 
 
170
static gboolean           gui_single                 (void);
 
171
static gboolean           gui_multi                  (void);
 
172
 
 
173
static void               choose_file_call           (GtkWidget* browse_button,
 
174
                                                      gpointer file_entry);
 
175
 
 
176
static gboolean           get_image_list             (void);
 
177
static GtkTreeModel*      create_model               (void);
 
178
 
 
179
static void               add_image_call             (GtkWidget *widget,
 
180
                                                      gpointer img_combo);
 
181
static void               del_image_call             (GtkWidget *widget,
 
182
                                                      gpointer icon_view);
 
183
static void               remove_call                (GtkTreeModel *tree_model,
 
184
                                                      GtkTreePath  *path,
 
185
                                                      gpointer      user_data);
 
186
static void               recount_pages              (void);
 
187
 
 
188
static cairo_surface_t   *get_drawable_image         (GimpDrawable *drawable);
 
189
static GimpRGB            get_layer_color            (GimpDrawable *layer,
 
190
                                                      gboolean *single);
 
191
static void               drawText                   (GimpDrawable* text_layer,
 
192
                                                      gdouble opacity,
 
193
                                                      cairo_t *cr,
 
194
                                                      gdouble x_res,
 
195
                                                      gdouble y_res);
 
196
 
 
197
static void query (void);
 
198
static void run   (const gchar      *name,
 
199
                   gint              nparams,
 
200
                   const GimpParam  *param,
 
201
                   gint             *nreturn_vals,
 
202
                   GimpParam       **return_vals);
 
203
 
 
204
static gboolean dnd_remove = TRUE;
 
205
static PdfMultiPage multi_page;
 
206
 
 
207
static PdfOptimize optimize = {
 
208
  TRUE, /* vectorize */
 
209
  TRUE, /* ignore_hidden */
 
210
  TRUE  /* apply_masks */
 
211
};
 
212
 
 
213
static GtkTreeModel *model;
 
214
static GtkWidget *file_choose;
 
215
static gchar* file_name;
 
216
 
 
217
GimpPlugInInfo PLUG_IN_INFO =
 
218
  {
 
219
    NULL,
 
220
    NULL,
 
221
    query,
 
222
    run
 
223
  };
 
224
 
 
225
MAIN()
 
226
 
 
227
typedef enum {
 
228
  SA_RUN_MODE,
 
229
  SA_IMAGE,
 
230
  SA_DRAWABLE,
 
231
  SA_FILENAME,
 
232
  SA_RAW_FILENAME,
 
233
  SA_VECTORIZE,
 
234
  SA_IGNORE_HIDDEN,
 
235
  SA_APPLY_MASKS,
 
236
  SA_ARG_COUNT
 
237
} SaveArgs;
 
238
 
 
239
#define SA_ARG_COUNT_DEFAULT 5
 
240
 
 
241
typedef enum {
 
242
  SMA_RUN_MODE,
 
243
  SMA_IMAGES,
 
244
  SMA_COUNT,
 
245
  SMA_VECTORIZE,
 
246
  SMA_IGNORE_HIDDEN,
 
247
  SMA_APPLY_MASKS,
 
248
  SMA_FILENAME,
 
249
  SMA_RAWFILENAME,
 
250
  SMA_ARG_COUNT
 
251
} SaveMultiArgs;
 
252
 
 
253
static void
 
254
query (void)
 
255
{
 
256
  static GimpParamDef save_args[] =
 
257
    {
 
258
      {GIMP_PDB_INT32,    "run-mode",     "Run mode"},
 
259
      {GIMP_PDB_IMAGE,    "image",        "Input image"},
 
260
      {GIMP_PDB_DRAWABLE, "drawable",     "Input drawable"},
 
261
      {GIMP_PDB_STRING,   "filename",     "The name of the file to save the image in"},
 
262
      {GIMP_PDB_STRING,   "raw-filename", "The name of the file to save the image in"},
 
263
      {GIMP_PDB_INT32,    "vectorize",    "Convert bitmaps to vector graphics where possible. TRUE or FALSE"},
 
264
      {GIMP_PDB_INT32,    "ignore-hidden","Omit hidden layers and layers with zero opacity. TRUE or FALSE"},
 
265
      {GIMP_PDB_INT32,    "apply-masks",  "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)"}
 
266
    };
 
267
 
 
268
  static GimpParamDef save_multi_args[] =
 
269
    {
 
270
      {GIMP_PDB_INT32,      "run-mode",     "Run mode"},
 
271
      {GIMP_PDB_INT32ARRAY, "images",       "Input image for each page (An image can appear more than once)"},
 
272
      {GIMP_PDB_INT32,      "count",        "The amount of images entered (This will be the amount of pages). 1 <= count <= MAX_PAGE_COUNT"},
 
273
      {GIMP_PDB_INT32,      "vectorize",    "Convert bitmaps to vector graphics where possible. TRUE or FALSE"},
 
274
      {GIMP_PDB_INT32,      "ignore-hidden","Omit hidden layers and layers with zero opacity. TRUE or FALSE"},
 
275
      {GIMP_PDB_INT32,      "apply-masks",  "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)"},
 
276
      {GIMP_PDB_STRING,     "filename",     "The name of the file to save the image in"},
 
277
      {GIMP_PDB_STRING,     "raw-filename", "The name of the file to save the image in"}
 
278
    };
 
279
 
 
280
  gimp_install_procedure (SAVE_PROC,
 
281
                          "Save files in PDF format",
 
282
                          "Saves files in Adobe's Portable Document Format. "
 
283
                          "PDF is designed to be easily processed by a variety "
 
284
                          "of different platforms, and is a distant cousin of "
 
285
                          "PostScript.",
 
286
                          "Barak Itkin",
 
287
                          "Copyright Barak Itkin",
 
288
                          "August 2009",
 
289
                          N_("Portable Document Format"),
 
290
                          "RGB*, GRAY*, INDEXED*",
 
291
                          GIMP_PLUGIN,
 
292
                          G_N_ELEMENTS (save_args), 0,
 
293
                          save_args, NULL);
 
294
 
 
295
  gimp_install_procedure (SAVE_MULTI_PROC,
 
296
                          "Save files in PDF format",
 
297
                          "Saves files in Adobe's Portable Document Format. "
 
298
                          "PDF is designed to be easily processed by a variety "
 
299
                          "of different platforms, and is a distant cousin of "
 
300
                          "PostScript.",
 
301
                          "Barak Itkin",
 
302
                          "Copyright Barak Itkin",
 
303
                          "August 2009",
 
304
                          N_("_Create multipage PDF..."),
 
305
                          "RGB*, GRAY*, INDEXED*",
 
306
                          GIMP_PLUGIN,
 
307
                          G_N_ELEMENTS (save_multi_args), 0,
 
308
                          save_multi_args, NULL);
 
309
 
 
310
/*  gimp_plugin_menu_register (SAVE_MULTI_PROC,
 
311
                             "<Image>/File/Create/PDF"); */
 
312
 
 
313
  gimp_register_file_handler_mime (SAVE_PROC, "application/pdf");
 
314
  gimp_register_save_handler (SAVE_PROC, "pdf", "");
 
315
}
 
316
 
 
317
static void
 
318
run (const gchar      *name,
 
319
     gint              nparams,
 
320
     const GimpParam  *param,
 
321
     gint             *nreturn_vals,
 
322
     GimpParam       **return_vals)
 
323
{
 
324
  static GimpParam        values[1];
 
325
  GimpPDBStatusType       status = GIMP_PDB_SUCCESS;
 
326
  GimpRunMode             run_mode;
 
327
 
 
328
  /* Plug-in variables */
 
329
  gboolean                single_image;
 
330
  gboolean                defaults_proc;
 
331
 
 
332
  /* Plug-In variables */
 
333
  cairo_surface_t        *pdf_file;
 
334
  cairo_t                *cr;
 
335
  GimpExportCapabilities  capabilities;
 
336
 
 
337
  guint32                 i = 0;
 
338
  gint32                  j = 0;
 
339
 
 
340
  gdouble                 x_res, y_res;
 
341
  gdouble                 x_scale, y_scale;
 
342
 
 
343
  gint32                  image_id;
 
344
  gboolean                exported;
 
345
  GimpImageBaseType       type;
 
346
 
 
347
  gint32                  temp;
 
348
 
 
349
  gint                   *layers;
 
350
  gint32                  num_of_layers;
 
351
  GimpDrawable           *layer;
 
352
  cairo_surface_t        *layer_image;
 
353
  gdouble                 opacity;
 
354
  gint                    x, y;
 
355
  GimpRGB                 layer_color;
 
356
  gboolean                single_color;
 
357
 
 
358
  gint32                  mask_id = -1;
 
359
  GimpDrawable           *mask = NULL;
 
360
  cairo_surface_t        *mask_image = NULL;
 
361
 
 
362
  INIT_I18N ();
 
363
 
 
364
  /* Setting mandatory output values */
 
365
  *nreturn_vals = 1;
 
366
  *return_vals  = values;
 
367
 
 
368
  values[0].type = GIMP_PDB_STATUS;
 
369
  values[0].data.d_status = status;
 
370
 
 
371
  /* Initializing all the settings */
 
372
  multi_page.image_count = 0;
 
373
 
 
374
  if (! init_vals (name, nparams, param, &single_image,
 
375
             &defaults_proc, &run_mode))
 
376
    {
 
377
      values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
 
378
      return;
 
379
    }
 
380
 
 
381
  /* Starting the executions */
 
382
  if (run_mode == GIMP_RUN_INTERACTIVE)
 
383
    {
 
384
      if (single_image)
 
385
        {
 
386
          if (! gui_single ())
 
387
            {
 
388
              values[0].data.d_status = GIMP_PDB_CANCEL;
 
389
              return;
 
390
            }
 
391
        }
 
392
      else if (! gui_multi ())
 
393
        {
 
394
          values[0].data.d_status = GIMP_PDB_CANCEL;
 
395
          return;
 
396
        }
 
397
 
 
398
      if (file_name == NULL)
 
399
        {
 
400
          values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
 
401
          gimp_message (_("You must select a file to save!"));
 
402
          return;
 
403
        }
 
404
    }
 
405
 
 
406
  pdf_file = cairo_pdf_surface_create (file_name, 1, 1);
 
407
  if (cairo_surface_status (pdf_file) != CAIRO_STATUS_SUCCESS)
 
408
    {
 
409
      char *str = g_strdup_printf
 
410
        (_("An error occured while creating the PDF file:\n"
 
411
           "%s\n"
 
412
           "Make sure you entered a valid filename and that the selected location isn't read only!"),
 
413
         cairo_status_to_string (cairo_surface_status (pdf_file)));
 
414
 
 
415
      gimp_message (str);
 
416
      g_free (str);
 
417
 
 
418
      values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
 
419
      return;
 
420
    }
 
421
  cr = cairo_create (pdf_file);
 
422
 
 
423
  capabilities = GIMP_EXPORT_CAN_HANDLE_RGB | GIMP_EXPORT_CAN_HANDLE_ALPHA |
 
424
    GIMP_EXPORT_CAN_HANDLE_GRAY | GIMP_EXPORT_CAN_HANDLE_LAYERS |
 
425
    GIMP_EXPORT_CAN_HANDLE_INDEXED;
 
426
  if (optimize.apply_masks)
 
427
    capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS;
 
428
 
 
429
  for (i = 0; i < multi_page.image_count; i++)
 
430
    {
 
431
      /* Save the state of the surface before any changes, so that settings
 
432
       * from one page won't affect all the others */
 
433
      cairo_save (cr);
 
434
 
 
435
      image_id =  multi_page.images[i];
 
436
 
 
437
      /* We need the active layer in order to use gimp_image_export */
 
438
      temp = gimp_image_get_active_drawable (image_id);
 
439
      if (temp == -1)
 
440
        exported = gimp_export_image (&image_id, &temp, NULL, capabilities) == GIMP_EXPORT_EXPORT;
 
441
      else
 
442
        exported = FALSE;
 
443
      type = gimp_image_base_type (image_id);
 
444
 
 
445
      gimp_image_get_resolution (image_id, &x_res, &y_res);
 
446
      x_scale = 72.0 / x_res;
 
447
      y_scale = 72.0 / y_res;
 
448
 
 
449
      cairo_pdf_surface_set_size (pdf_file,
 
450
                                  gimp_image_width (image_id) * x_scale,
 
451
                                  gimp_image_height (image_id) * y_scale);
 
452
 
 
453
      /* This way we set how many pixels are there in every inch.
 
454
       * It's very important for PangoCairo */
 
455
      cairo_surface_set_fallback_resolution (pdf_file, x_res, y_res);
 
456
 
 
457
      /* PDF is usually 72 points per inch. If we have a different resolution,
 
458
       * we will need this to fit our drawings */
 
459
      cairo_scale (cr, x_scale, y_scale);
 
460
 
 
461
      /* Now, we should loop over the layers of each image */
 
462
      layers = gimp_image_get_layers (image_id, &num_of_layers);
 
463
 
 
464
      for (j = 0; j < num_of_layers; j++)
 
465
        {
 
466
          layer = gimp_drawable_get (layers [num_of_layers-j-1]);
 
467
          opacity = gimp_layer_get_opacity (layer->drawable_id)/100.0;
 
468
 
 
469
          /* Gimp doesn't display indexed layers with opacity below 50%
 
470
           * And if it's above 50%, it will be rounded to 100% */
 
471
          if (type == GIMP_INDEXED)
 
472
            {
 
473
              if (opacity <= 0.5)
 
474
                opacity = 0.0;
 
475
              else
 
476
                opacity = 1.0;
 
477
            }
 
478
 
 
479
          if (gimp_item_get_visible (layer->drawable_id)
 
480
              && (! optimize.ignore_hidden || (optimize.ignore_hidden && opacity > 0.0)))
 
481
            {
 
482
              mask_id = gimp_layer_get_mask (layer->drawable_id);
 
483
              if (mask_id != -1)
 
484
                {
 
485
                  mask = gimp_drawable_get (mask_id);
 
486
                  mask_image = get_drawable_image (mask);
 
487
                }
 
488
 
 
489
              gimp_drawable_offsets (layer->drawable_id, &x, &y);
 
490
 
 
491
              /* For raster layers */
 
492
              if (!gimp_item_is_text_layer (layer->drawable_id))
 
493
                {
 
494
                  layer_color = get_layer_color (layer, &single_color);
 
495
                  cairo_rectangle (cr, x, y, layer->width, layer->height);
 
496
 
 
497
                  if (optimize.vectorize && single_color)
 
498
                    {
 
499
                      cairo_set_source_rgba (cr, layer_color.r, layer_color.g, layer_color.b, layer_color.a * opacity);
 
500
                      if (mask_id != -1)
 
501
                        cairo_mask_surface (cr, mask_image, x, y);
 
502
                      else
 
503
                        cairo_fill (cr);
 
504
                    }
 
505
                  else
 
506
                    {
 
507
                      cairo_clip (cr);
 
508
                      layer_image = get_drawable_image (layer);
 
509
                      cairo_set_source_surface (cr, layer_image, x, y);
 
510
                      cairo_push_group (cr);
 
511
                      cairo_paint_with_alpha (cr, opacity);
 
512
                      cairo_pop_group_to_source (cr);
 
513
                      if (mask_id != -1)
 
514
                        cairo_mask_surface (cr, mask_image, x, y);
 
515
                      else
 
516
                        cairo_paint (cr);
 
517
                      cairo_reset_clip (cr);
 
518
 
 
519
                      cairo_surface_destroy (layer_image);
 
520
                    }
 
521
                }
 
522
              /* For text layers */
 
523
              else
 
524
                {
 
525
                  drawText (layer, opacity, cr, x_res, y_res);
 
526
                }
 
527
            }
 
528
 
 
529
          /* We are done with the layer - time to free some resources */
 
530
          gimp_drawable_detach (layer);
 
531
          if (mask_id != -1)
 
532
            {
 
533
              gimp_drawable_detach (mask);
 
534
              cairo_surface_destroy (mask_image);
 
535
            }
 
536
        }
 
537
      /* We are done with this image - Show it! */
 
538
      cairo_show_page (cr);
 
539
      cairo_restore (cr);
 
540
 
 
541
      if (exported)
 
542
        gimp_image_delete (image_id);
 
543
    }
 
544
 
 
545
  /* We are done with all the images - time to free the resources */
 
546
  cairo_surface_destroy (pdf_file);
 
547
  cairo_destroy (cr);
 
548
 
 
549
  /* Finally done, let's save the parameters */
 
550
  gimp_set_data (DATA_OPTIMIZE, &optimize, sizeof (optimize));
 
551
  if (!single_image)
 
552
    {
 
553
      g_strlcpy (multi_page.file_name, file_name, MAX_FILE_NAME_LENGTH);
 
554
      gimp_set_data (DATA_IMAGE_LIST, &multi_page, sizeof (multi_page));
 
555
    }
 
556
 
 
557
}
 
558
 
 
559
/******************************************************/
 
560
/* Begining of parameter handling functions           */
 
561
/******************************************************/
 
562
 
 
563
/* A function that takes care of loading the basic
 
564
 * parameters */
 
565
static gboolean
 
566
init_vals (const gchar      *name,
 
567
           gint              nparams,
 
568
           const GimpParam  *param,
 
569
           gboolean         *single_image,
 
570
           gboolean         *defaults_proc,
 
571
           GimpRunMode      *run_mode)
 
572
{
 
573
  gboolean had_saved_list = FALSE;
 
574
  gboolean single;
 
575
  gboolean defaults = FALSE;
 
576
  gint32   i;
 
577
  gint32   image;
 
578
 
 
579
  if (g_str_equal (name, SAVE_PROC))
 
580
    {
 
581
      single = TRUE;
 
582
      if (nparams != SA_ARG_COUNT && nparams != SA_ARG_COUNT_DEFAULT)
 
583
        return FALSE;
 
584
 
 
585
      *run_mode = param[SA_RUN_MODE].data.d_int32;
 
586
      image = param[SA_IMAGE].data.d_int32;
 
587
      file_name = param[SA_FILENAME].data.d_string;
 
588
 
 
589
      if (nparams == SA_ARG_COUNT)
 
590
        {
 
591
          optimize.apply_masks = param[SA_APPLY_MASKS].data.d_int32;
 
592
          optimize.vectorize = param[SA_VECTORIZE].data.d_int32;
 
593
          optimize.ignore_hidden = param[SA_IGNORE_HIDDEN].data.d_int32;
 
594
        }
 
595
      else
 
596
        defaults = TRUE;
 
597
    }
 
598
  else if (g_str_equal (name, SAVE_MULTI_PROC))
 
599
    {
 
600
      single = FALSE;
 
601
      if (nparams != SMA_ARG_COUNT)
 
602
        return FALSE;
 
603
 
 
604
      *run_mode = param[SMA_RUN_MODE].data.d_int32;
 
605
      image = -1;
 
606
      file_name = param[SA_FILENAME].data.d_string;
 
607
 
 
608
      optimize.apply_masks = param[SMA_APPLY_MASKS].data.d_int32;
 
609
      optimize.vectorize = param[SMA_VECTORIZE].data.d_int32;
 
610
      optimize.ignore_hidden = param[SMA_IGNORE_HIDDEN].data.d_int32;
 
611
    }
 
612
  else
 
613
    return FALSE;
 
614
 
 
615
  switch (*run_mode)
 
616
    {
 
617
 
 
618
    case GIMP_RUN_NONINTERACTIVE:
 
619
      if (single)
 
620
        {
 
621
          init_image_list_defaults (image);
 
622
        }
 
623
      else
 
624
        {
 
625
          multi_page.image_count = param[SMA_COUNT].data.d_int32;
 
626
          if (param[SMA_IMAGES].data.d_int32array != NULL)
 
627
            for (i = 0; i < param[SMA_COUNT].data.d_int32; i++)
 
628
              multi_page.images[i] = param[SMA_IMAGES].data.d_int32array[i];
 
629
        }
 
630
      break;
 
631
 
 
632
    case GIMP_RUN_INTERACTIVE:
 
633
      /* Possibly retrieve data */
 
634
      gimp_get_data (DATA_OPTIMIZE, &optimize);
 
635
      had_saved_list = gimp_get_data (DATA_IMAGE_LIST, &multi_page);
 
636
 
 
637
      if (had_saved_list && (file_name == NULL || strlen (file_name) == 0))
 
638
        {
 
639
          file_name = multi_page.file_name;
 
640
        }
 
641
 
 
642
      if (single || ! had_saved_list )
 
643
        init_image_list_defaults (image);
 
644
 
 
645
      break;
 
646
 
 
647
    case GIMP_RUN_WITH_LAST_VALS:
 
648
      /* Possibly retrieve data */
 
649
      if (!single)
 
650
        {
 
651
          had_saved_list = gimp_get_data (DATA_IMAGE_LIST, &multi_page);
 
652
          if (had_saved_list)
 
653
            {
 
654
              file_name = multi_page.file_name;
 
655
            }
 
656
        }
 
657
      else
 
658
        {
 
659
          init_image_list_defaults (image);
 
660
        }
 
661
      gimp_get_data (DATA_OPTIMIZE, &optimize);
 
662
 
 
663
      break;
 
664
    }
 
665
 
 
666
  *defaults_proc = defaults;
 
667
  *single_image = single;
 
668
 
 
669
  validate_image_list ();
 
670
 
 
671
  return TRUE;
 
672
}
 
673
 
 
674
/* A function that initializes the image list to default values */
 
675
static void
 
676
init_image_list_defaults (gint32 image)
 
677
{
 
678
  if (image != -1)
 
679
    {
 
680
      multi_page.images[0] = image;
 
681
      multi_page.image_count = 1;
 
682
    }
 
683
  else {
 
684
    multi_page.image_count = 0;
 
685
  }
 
686
}
 
687
 
 
688
/* A function that removes images that are no longer valid from
 
689
 * the image list */
 
690
static void
 
691
validate_image_list (void)
 
692
{
 
693
  gint32  valid = 0;
 
694
  guint32 i = 0;
 
695
 
 
696
  for (i = 0 ; i < MAX_PAGE_COUNT && i < multi_page.image_count ; i++)
 
697
    {
 
698
      if (gimp_image_is_valid (multi_page.images[i]))
 
699
        {
 
700
          multi_page.images[valid] = multi_page.images[i];
 
701
          valid++;
 
702
        }
 
703
    }
 
704
  multi_page.image_count = valid;
 
705
}
 
706
 
 
707
/******************************************************/
 
708
/* Begining of GUI functions                          */
 
709
/******************************************************/
 
710
/* The main GUI function for saving single-paged PDFs */
 
711
static gboolean
 
712
gui_single (void)
 
713
{
 
714
  GtkWidget *window;
 
715
  GtkWidget *vbox;
 
716
 
 
717
  GtkWidget *vectorize_c;
 
718
  GtkWidget *ignore_hidden_c;
 
719
  GtkWidget *apply_c;
 
720
 
 
721
  gboolean   run;
 
722
 
 
723
  gimp_ui_init (PLUG_IN_BINARY, FALSE);
 
724
 
 
725
  window = gimp_export_dialog_new ("PDF", PLUG_IN_ROLE, SAVE_PROC);
 
726
 
 
727
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 
728
  gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (window)),
 
729
                      vbox, TRUE, TRUE, 0);
 
730
 
 
731
  gtk_container_set_border_width (GTK_CONTAINER (window), 12);
 
732
 
 
733
  ignore_hidden_c = gtk_check_button_new_with_label (_("Omit hidden layers and layers with zero opacity"));
 
734
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ignore_hidden_c), optimize.ignore_hidden);
 
735
  gtk_box_pack_end (GTK_BOX (vbox), ignore_hidden_c, TRUE, TRUE, 0);
 
736
 
 
737
  vectorize_c = gtk_check_button_new_with_label (_("Convert bitmaps to vector graphics where possible"));
 
738
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vectorize_c), optimize.vectorize);
 
739
  gtk_box_pack_end (GTK_BOX (vbox), vectorize_c, TRUE, TRUE, 0);
 
740
 
 
741
  apply_c = gtk_check_button_new_with_label (_("Apply layer masks before saving"));
 
742
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_c), optimize.apply_masks);
 
743
  gtk_box_pack_end (GTK_BOX (vbox), apply_c, TRUE, TRUE, 0);
 
744
  gimp_help_set_help_data (apply_c, _("Keeping the masks will not change the output"), NULL);
 
745
 
 
746
  gtk_widget_show_all (window);
 
747
 
 
748
  run = gtk_dialog_run (GTK_DIALOG (window)) == GTK_RESPONSE_OK;
 
749
 
 
750
  optimize.ignore_hidden = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ignore_hidden_c));
 
751
  optimize.vectorize = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vectorize_c));
 
752
  optimize.apply_masks = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_c));
 
753
 
 
754
  gtk_widget_destroy (window);
 
755
  return run;
 
756
}
 
757
/* The main GUI function for saving multi-paged PDFs */
 
758
static gboolean
 
759
gui_multi (void)
 
760
{
 
761
  GtkWidget   *window;
 
762
  GtkWidget   *vbox;
 
763
 
 
764
  GtkWidget   *file_label;
 
765
  GtkWidget   *file_entry;
 
766
  GtkWidget   *file_browse;
 
767
  GtkWidget   *file_hbox;
 
768
 
 
769
  GtkWidget   *vectorize_c;
 
770
  GtkWidget   *ignore_hidden_c;
 
771
  GtkWidget   *apply_c;
 
772
 
 
773
  GtkWidget   *scroll;
 
774
  GtkWidget   *page_view;
 
775
 
 
776
  GtkWidget   *h_but_box;
 
777
  GtkWidget   *del;
 
778
 
 
779
  GtkWidget   *h_box;
 
780
  GtkWidget   *img_combo;
 
781
  GtkWidget   *add_image;
 
782
 
 
783
  gboolean     run;
 
784
  const gchar *temp;
 
785
 
 
786
  gimp_ui_init (PLUG_IN_BINARY, FALSE);
 
787
 
 
788
  window = gimp_export_dialog_new ("PDF", PLUG_IN_ROLE, SAVE_MULTI_PROC);
 
789
 
 
790
  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
 
791
  gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (window)),
 
792
                      vbox, TRUE, TRUE, 0);
 
793
 
 
794
  gtk_container_set_border_width (GTK_CONTAINER (window), 12);
 
795
 
 
796
  file_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
 
797
  file_label = gtk_label_new (_("Save to:"));
 
798
  file_entry = gtk_entry_new ();
 
799
  if (file_name != NULL)
 
800
    gtk_entry_set_text (GTK_ENTRY (file_entry), file_name);
 
801
  file_browse = gtk_button_new_with_label (_("Browse..."));
 
802
  file_choose = gtk_file_chooser_dialog_new (_("Multipage PDF export"),
 
803
                                             GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_SAVE,
 
804
                                             "gtk-save", GTK_RESPONSE_OK,
 
805
                                             "gtk-cancel", GTK_RESPONSE_CANCEL,
 
806
                                             NULL);
 
807
 
 
808
  gtk_box_pack_start (GTK_BOX (file_hbox), file_label, FALSE, FALSE, 0);
 
809
  gtk_box_pack_start (GTK_BOX (file_hbox), file_entry, TRUE, TRUE, 0);
 
810
  gtk_box_pack_start (GTK_BOX (file_hbox), file_browse, FALSE, FALSE, 0);
 
811
 
 
812
  gtk_box_pack_start (GTK_BOX (vbox), file_hbox, TRUE, TRUE, 0);
 
813
 
 
814
  page_view = gtk_icon_view_new ();
 
815
  model = create_model ();
 
816
  gtk_icon_view_set_model (GTK_ICON_VIEW (page_view), model);
 
817
  gtk_icon_view_set_reorderable (GTK_ICON_VIEW (page_view), TRUE);
 
818
  gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (page_view), GTK_SELECTION_MULTIPLE);
 
819
 
 
820
  gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (page_view), THUMB);
 
821
  gtk_icon_view_set_text_column (GTK_ICON_VIEW (page_view), PAGE_NUMBER);
 
822
  gtk_icon_view_set_tooltip_column (GTK_ICON_VIEW (page_view), IMAGE_NAME);
 
823
 
 
824
  scroll = gtk_scrolled_window_new (NULL, NULL);
 
825
  gtk_widget_set_size_request (scroll, -1, 300);
 
826
 
 
827
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
 
828
  gtk_container_add (GTK_CONTAINER (scroll), page_view);
 
829
 
 
830
  gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
 
831
 
 
832
  h_but_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
 
833
  gtk_button_box_set_layout (GTK_BUTTON_BOX (h_but_box), GTK_BUTTONBOX_START);
 
834
 
 
835
  del = gtk_button_new_with_label (_("Remove the selected pages"));
 
836
  gtk_box_pack_start (GTK_BOX (h_but_box), del, TRUE, TRUE, 0);
 
837
 
 
838
  gtk_box_pack_start (GTK_BOX (vbox), h_but_box, FALSE, FALSE, 0);
 
839
 
 
840
  h_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
 
841
 
 
842
  img_combo = gimp_image_combo_box_new (NULL, NULL);
 
843
  gtk_box_pack_start (GTK_BOX (h_box), img_combo, FALSE, FALSE, 0);
 
844
 
 
845
  add_image = gtk_button_new_with_label (_("Add this image"));
 
846
  gtk_box_pack_start (GTK_BOX (h_box), add_image, FALSE, FALSE, 0);
 
847
 
 
848
  gtk_box_pack_start (GTK_BOX (vbox), h_box, FALSE, FALSE, 0);
 
849
 
 
850
  ignore_hidden_c = gtk_check_button_new_with_label (_("Omit hidden layers and layers with zero opacity"));
 
851
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ignore_hidden_c), optimize.ignore_hidden);
 
852
  gtk_box_pack_end (GTK_BOX (vbox), ignore_hidden_c, FALSE, FALSE, 0);
 
853
 
 
854
  vectorize_c = gtk_check_button_new_with_label (_("Convert bitmaps to vector graphics where possible"));
 
855
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vectorize_c), optimize.vectorize);
 
856
  gtk_box_pack_end (GTK_BOX (vbox), vectorize_c, FALSE, FALSE, 0);
 
857
 
 
858
  apply_c = gtk_check_button_new_with_label (_("Apply layer masks before saving"));
 
859
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_c), optimize.apply_masks);
 
860
  gtk_box_pack_end (GTK_BOX (vbox), apply_c, FALSE, FALSE, 0);
 
861
  gimp_help_set_help_data (apply_c, _("Keeping the masks will not change the output"), NULL);
 
862
 
 
863
  gtk_widget_show_all (window);
 
864
 
 
865
  g_signal_connect (G_OBJECT (file_browse), "clicked",
 
866
                    G_CALLBACK (choose_file_call), G_OBJECT (file_entry));
 
867
 
 
868
  g_signal_connect (G_OBJECT (add_image), "clicked",
 
869
                    G_CALLBACK (add_image_call), G_OBJECT (img_combo));
 
870
 
 
871
  g_signal_connect (G_OBJECT (del), "clicked",
 
872
                    G_CALLBACK (del_image_call), G_OBJECT (page_view));
 
873
 
 
874
  g_signal_connect (G_OBJECT (model), "row-deleted",
 
875
                    G_CALLBACK (remove_call), NULL);
 
876
 
 
877
  run = gtk_dialog_run (GTK_DIALOG (window)) == GTK_RESPONSE_OK;
 
878
 
 
879
  run &= get_image_list ();
 
880
 
 
881
  temp = gtk_entry_get_text (GTK_ENTRY (file_entry));
 
882
  g_stpcpy (file_name, temp);
 
883
 
 
884
  optimize.ignore_hidden = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ignore_hidden_c));
 
885
  optimize.vectorize = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vectorize_c));
 
886
  optimize.apply_masks = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_c));
 
887
 
 
888
  gtk_widget_destroy (window);
 
889
  return run;
 
890
}
 
891
 
 
892
/* A function that is called when the button for browsing for file
 
893
 * locations was clicked */
 
894
static void
 
895
choose_file_call (GtkWidget *browse_button,
 
896
                  gpointer   file_entry)
 
897
{
 
898
  GFile *file = g_file_new_for_path (gtk_entry_get_text (GTK_ENTRY (file_entry)));
 
899
  gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (file_choose), g_file_get_uri (file));
 
900
 
 
901
  if (gtk_dialog_run (GTK_DIALOG (file_choose)) == GTK_RESPONSE_OK)
 
902
    {
 
903
      file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (file_choose));
 
904
      gtk_entry_set_text (GTK_ENTRY (file_entry), g_file_get_path (file));
 
905
    };
 
906
 
 
907
  file_name = g_file_get_path (file);
 
908
  gtk_widget_hide (file_choose);
 
909
}
 
910
 
 
911
/* A function to create the basic GtkTreeModel for the icon view */
 
912
static GtkTreeModel*
 
913
create_model (void)
 
914
{
 
915
  GtkListStore *model;
 
916
  GtkTreeIter   iter;
 
917
  guint32       i;
 
918
  gint32        image = multi_page.images[0];
 
919
 
 
920
  /* validate_image_list was called earlier, so all the images
 
921
   * up to multi_page.image_count are valid */
 
922
  model = gtk_list_store_new (4,
 
923
                              GDK_TYPE_PIXBUF, /* THUMB */
 
924
                              G_TYPE_STRING,   /* PAGE_NUMBER */
 
925
                              G_TYPE_STRING,   /* IMAGE_NAME */
 
926
                              G_TYPE_INT);     /* IMAGE_ID */
 
927
 
 
928
  for (i = 0 ; i < multi_page.image_count && i < MAX_PAGE_COUNT ; i++)
 
929
    {
 
930
      image = multi_page.images[i];
 
931
 
 
932
      gtk_list_store_append (model, &iter);
 
933
      gtk_list_store_set (model, &iter,
 
934
                          THUMB, gimp_image_get_thumbnail (image, THUMB_WIDTH, THUMB_HEIGHT, GIMP_PIXBUF_SMALL_CHECKS),
 
935
                          PAGE_NUMBER, g_strdup_printf ("Page %d", i+1),
 
936
                          IMAGE_NAME, gimp_image_get_name (image),
 
937
                          IMAGE_ID, image,
 
938
                          -1);
 
939
 
 
940
    }
 
941
 
 
942
  return GTK_TREE_MODEL (model);
 
943
}
 
944
 
 
945
/* A function that puts the images from the model inside the
 
946
 * images (pages) array */
 
947
static gboolean
 
948
get_image_list (void)
 
949
{
 
950
  GtkTreeIter iter;
 
951
  gboolean    valid = gtk_tree_model_get_iter_first (model, &iter);
 
952
  gint32      image;
 
953
 
 
954
  multi_page.image_count = 0;
 
955
 
 
956
  if (!valid)
 
957
    {
 
958
      gimp_message (_("Error! In order to save the file, at least one image should be added!"));
 
959
      return FALSE;
 
960
    }
 
961
 
 
962
  while (valid)
 
963
    {
 
964
      gtk_tree_model_get (model, &iter,
 
965
                          IMAGE_ID, &image,
 
966
                          -1);
 
967
      multi_page.images[multi_page.image_count] = image;
 
968
 
 
969
      valid = gtk_tree_model_iter_next (model, &iter);
 
970
      multi_page.image_count++;
 
971
    }
 
972
 
 
973
  return TRUE;
 
974
}
 
975
 
 
976
/* A function that is called when the button for adding an image
 
977
 * was clicked */
 
978
static void
 
979
add_image_call (GtkWidget *widget,
 
980
                gpointer   img_combo)
 
981
{
 
982
  GtkListStore *store;
 
983
  GtkTreeIter   iter;
 
984
  gint32        image;
 
985
 
 
986
  dnd_remove = FALSE;
 
987
 
 
988
  gimp_int_combo_box_get_active (img_combo, &image);
 
989
 
 
990
  store = GTK_LIST_STORE (model);
 
991
 
 
992
  gtk_list_store_append (store, &iter);
 
993
  gtk_list_store_set (store, &iter,
 
994
                      PAGE_NUMBER, g_strdup_printf ("Page %d", multi_page.image_count+1),
 
995
                      THUMB, gimp_image_get_thumbnail (image, THUMB_WIDTH, THUMB_HEIGHT, GIMP_PIXBUF_SMALL_CHECKS),
 
996
                      IMAGE_NAME, gimp_image_get_name (image),
 
997
                      IMAGE_ID, image,
 
998
                      -1
 
999
                      );
 
1000
 
 
1001
  multi_page.image_count++;
 
1002
 
 
1003
  dnd_remove = TRUE;
 
1004
}
 
1005
 
 
1006
/* A function that is called when the button for deleting the
 
1007
 * selected images was clicked */
 
1008
static void
 
1009
del_image_call (GtkWidget *widget,
 
1010
                gpointer   icon_view)
 
1011
{
 
1012
  GList                *list;
 
1013
  GtkTreeRowReference **items;
 
1014
  GtkTreePath          *item_path;
 
1015
  GtkTreeIter           item;
 
1016
  guint32               i;
 
1017
  gpointer              temp;
 
1018
 
 
1019
  guint32               len;
 
1020
 
 
1021
  GdkPixbuf            *thumb;
 
1022
  gchar*                name;
 
1023
 
 
1024
  dnd_remove = FALSE;
 
1025
 
 
1026
  list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view));
 
1027
 
 
1028
  len = g_list_length (list);
 
1029
  if (len > 0)
 
1030
    {
 
1031
      items = g_newa (GtkTreeRowReference*, len);
 
1032
 
 
1033
      for (i = 0; i < len; i++)
 
1034
        {
 
1035
          temp = g_list_nth_data (list, i);
 
1036
          items[i] = gtk_tree_row_reference_new (model, temp);
 
1037
          gtk_tree_path_free (temp);
 
1038
        }
 
1039
      g_list_free (list);
 
1040
 
 
1041
      for (i = 0; i < len; i++)
 
1042
        {
 
1043
          item_path = gtk_tree_row_reference_get_path (items[i]);
 
1044
          gtk_tree_model_get_iter (model, &item, item_path);
 
1045
 
 
1046
          /* Get the data that should be freed */
 
1047
          gtk_tree_model_get (model, &item,
 
1048
                              THUMB, &thumb, IMAGE_NAME, &name, -1);
 
1049
 
 
1050
          /* Only after you have the pointers, remove them from the tree */
 
1051
          gtk_list_store_remove (GTK_LIST_STORE (model), &item);
 
1052
 
 
1053
          /* Now you can free the data */
 
1054
          g_object_unref(thumb);
 
1055
          g_free (name);
 
1056
 
 
1057
          gtk_tree_path_free (item_path);
 
1058
          gtk_tree_row_reference_free (items[i]);
 
1059
          multi_page.image_count--;
 
1060
        }
 
1061
 
 
1062
      g_free (items);
 
1063
    }
 
1064
 
 
1065
  dnd_remove = TRUE;
 
1066
 
 
1067
  recount_pages ();
 
1068
}
 
1069
 
 
1070
/* A function that is called on rows-deleted signal. It will
 
1071
 * call the function to relabel the pages */
 
1072
static void
 
1073
remove_call (GtkTreeModel *tree_model,
 
1074
             GtkTreePath  *path,
 
1075
             gpointer      user_data)
 
1076
{
 
1077
 
 
1078
  if (dnd_remove)
 
1079
    /* The gtk documentation says that we should not free the indices array */
 
1080
    recount_pages ();
 
1081
}
 
1082
 
 
1083
/* A function to relabel the pages in the icon view, when
 
1084
 * their order was changed */
 
1085
static void
 
1086
recount_pages (void)
 
1087
{
 
1088
  GtkListStore *store;
 
1089
  GtkTreeIter   iter;
 
1090
  gboolean      valid;
 
1091
  gint32        i = 0;
 
1092
 
 
1093
  store = GTK_LIST_STORE (model);
 
1094
 
 
1095
  valid = gtk_tree_model_get_iter_first (model, &iter);
 
1096
  while (valid)
 
1097
    {
 
1098
      gtk_list_store_set (store, &iter,
 
1099
                          PAGE_NUMBER, g_strdup_printf ("Page %d", i + 1),
 
1100
                          -1);
 
1101
      valid = gtk_tree_model_iter_next (model, &iter);
 
1102
      i++;
 
1103
    }
 
1104
}
 
1105
 
 
1106
/******************************************************/
 
1107
/* Begining of the actual PDF functions               */
 
1108
/******************************************************/
 
1109
 
 
1110
/* A function to get a cairo image surface from a drawable.
 
1111
 * Some of the code was taken from the gimp-print plugin */
 
1112
 
 
1113
/* Gimp RGB (24 bit) to Cairo RGB (24 bit) */
 
1114
static inline void
 
1115
convert_from_rgb_to_rgb (const guchar *src,
 
1116
                         guchar       *dest,
 
1117
                         gint          pixels)
 
1118
{
 
1119
  while (pixels--)
 
1120
    {
 
1121
      GIMP_CAIRO_RGB24_SET_PIXEL (dest,
 
1122
                                  src[0], src[1], src[2]);
 
1123
 
 
1124
      src  += 3;
 
1125
      dest += 4;
 
1126
    }
 
1127
}
 
1128
 
 
1129
/* Gimp RGBA (32 bit) to Cairo RGBA (32 bit) */
 
1130
static inline void
 
1131
convert_from_rgba_to_rgba (const guchar *src,
 
1132
                           guchar       *dest,
 
1133
                           gint          pixels)
 
1134
{
 
1135
  while (pixels--)
 
1136
    {
 
1137
      GIMP_CAIRO_ARGB32_SET_PIXEL (dest,
 
1138
                                   src[0], src[1], src[2], src[3]);
 
1139
 
 
1140
      src  += 4;
 
1141
      dest += 4;
 
1142
    }
 
1143
}
 
1144
 
 
1145
/* Gimp Gray (8 bit) to Cairo RGB (24 bit) */
 
1146
static inline void
 
1147
convert_from_gray_to_rgb (const guchar *src,
 
1148
                           guchar       *dest,
 
1149
                           gint          pixels)
 
1150
{
 
1151
  while (pixels--)
 
1152
    {
 
1153
      GIMP_CAIRO_RGB24_SET_PIXEL (dest,
 
1154
                                  src[0], src[0], src[0]);
 
1155
 
 
1156
      src  += 1;
 
1157
      dest += 4;
 
1158
    }
 
1159
}
 
1160
 
 
1161
/* Gimp GrayA (16 bit) to Cairo RGBA (32 bit) */
 
1162
static inline void
 
1163
convert_from_graya_to_rgba (const guchar *src,
 
1164
                            guchar       *dest,
 
1165
                            gint          pixels)
 
1166
{
 
1167
  while (pixels--)
 
1168
    {
 
1169
      GIMP_CAIRO_ARGB32_SET_PIXEL (dest,
 
1170
                                   src[0], src[0], src[0], src[1]);
 
1171
 
 
1172
      src  += 2;
 
1173
      dest += 4;
 
1174
    }
 
1175
}
 
1176
 
 
1177
/* Gimp Indexed (8 bit) to Cairo RGB (24 bit) */
 
1178
static inline void
 
1179
convert_from_indexed_to_rgb (const guchar *src,
 
1180
                             guchar       *dest,
 
1181
                             gint          pixels,
 
1182
                             const guchar *cmap)
 
1183
{
 
1184
  while (pixels--)
 
1185
    {
 
1186
      const gint i = 3 * src[0];
 
1187
 
 
1188
      GIMP_CAIRO_RGB24_SET_PIXEL (dest,
 
1189
                                  cmap[i], cmap[i + 1], cmap[i + 2]);
 
1190
 
 
1191
      src  += 1;
 
1192
      dest += 4;
 
1193
    }
 
1194
}
 
1195
 
 
1196
/* Gimp IndexedA (16 bit) to Cairo RGBA (32 bit) */
 
1197
static inline void
 
1198
convert_from_indexeda_to_rgba (const guchar *src,
 
1199
                               guchar       *dest,
 
1200
                               gint          pixels,
 
1201
                               const guchar *cmap)
 
1202
{
 
1203
  while (pixels--)
 
1204
    {
 
1205
      const gint i = 3 * src[0];
 
1206
 
 
1207
      GIMP_CAIRO_ARGB32_SET_PIXEL (dest,
 
1208
                                   cmap[i], cmap[i + 1], cmap[i + 2], src[1]);
 
1209
 
 
1210
      src  += 2;
 
1211
      dest += 4;
 
1212
    }
 
1213
}
 
1214
 
 
1215
static cairo_surface_t *
 
1216
get_drawable_image (GimpDrawable *drawable)
 
1217
{
 
1218
  gint32           drawable_ID   = drawable->drawable_id;
 
1219
  GimpPixelRgn     region;
 
1220
  GimpImageType    image_type    = gimp_drawable_type (drawable_ID);
 
1221
  cairo_surface_t *surface;
 
1222
  cairo_format_t   format;
 
1223
  const gint       width         = drawable->width;
 
1224
  const gint       height        = drawable->height;
 
1225
  guchar           cmap[3 * 256] = { 0, };
 
1226
  guchar          *pixels;
 
1227
  gint             stride;
 
1228
  gpointer         pr;
 
1229
  gboolean         indexed       = FALSE;
 
1230
  int              bpp           = drawable->bpp;
 
1231
 
 
1232
  if (gimp_drawable_is_indexed (drawable_ID))
 
1233
    {
 
1234
      guchar *colors;
 
1235
      gint    num_colors;
 
1236
 
 
1237
      indexed = TRUE;
 
1238
      colors = gimp_image_get_colormap (gimp_item_get_image (drawable_ID),
 
1239
                                        &num_colors);
 
1240
      memcpy (cmap, colors, 3 * num_colors);
 
1241
      g_free (colors);
 
1242
    }
 
1243
 
 
1244
  switch (bpp)
 
1245
    {
 
1246
    case 1: /* GRAY or INDEXED */
 
1247
      if (! indexed)
 
1248
        {
 
1249
          format = CAIRO_FORMAT_RGB24;
 
1250
        }
 
1251
      else
 
1252
        {
 
1253
          format = CAIRO_FORMAT_RGB24;
 
1254
        }
 
1255
      break;
 
1256
    case 3: /* RGB */
 
1257
      format = CAIRO_FORMAT_RGB24;
 
1258
      break;
 
1259
 
 
1260
    case 2: /* GRAYA or INDEXEDA */
 
1261
    case 4: /* RGBA */
 
1262
      format = CAIRO_FORMAT_ARGB32;
 
1263
      break;
 
1264
 
 
1265
    default:
 
1266
      g_assert_not_reached ();
 
1267
      break;
 
1268
    }
 
1269
 
 
1270
  surface = cairo_image_surface_create (format, width, height);
 
1271
 
 
1272
  pixels = cairo_image_surface_get_data (surface);
 
1273
  stride = cairo_image_surface_get_stride (surface);
 
1274
 
 
1275
  gimp_pixel_rgn_init (&region, drawable, 0, 0, width, height, FALSE, FALSE);
 
1276
 
 
1277
  for (pr = gimp_pixel_rgns_register (1, &region);
 
1278
       pr != NULL;
 
1279
       pr = gimp_pixel_rgns_process (pr))
 
1280
    {
 
1281
      const guchar *src  = region.data;
 
1282
      guchar       *dest = pixels + region.y * stride + region.x * 4;
 
1283
      gint          y;
 
1284
 
 
1285
      for (y = 0; y < region.h; y++)
 
1286
        {
 
1287
          switch (image_type)
 
1288
            {
 
1289
            case GIMP_RGB_IMAGE:
 
1290
              convert_from_rgb_to_rgb (src, dest, region.w);
 
1291
              break;
 
1292
 
 
1293
            case GIMP_RGBA_IMAGE:
 
1294
              convert_from_rgba_to_rgba (src, dest, region.w);
 
1295
              break;
 
1296
 
 
1297
            case GIMP_GRAY_IMAGE:
 
1298
              convert_from_gray_to_rgb (src, dest, region.w);
 
1299
              break;
 
1300
 
 
1301
            case GIMP_GRAYA_IMAGE:
 
1302
              convert_from_graya_to_rgba (src, dest, region.w);
 
1303
              break;
 
1304
 
 
1305
            case GIMP_INDEXED_IMAGE:
 
1306
              convert_from_indexed_to_rgb (src, dest, region.w, cmap);
 
1307
              break;
 
1308
 
 
1309
            case GIMP_INDEXEDA_IMAGE:
 
1310
              convert_from_indexeda_to_rgba (src, dest, region.w, cmap);
 
1311
              break;
 
1312
            }
 
1313
 
 
1314
          src  += region.rowstride;
 
1315
          dest += stride;
 
1316
        }
 
1317
    }
 
1318
 
 
1319
  cairo_surface_mark_dirty (surface);
 
1320
 
 
1321
  return surface;
 
1322
}
 
1323
 
 
1324
/* A function to check if a drawable is single colored
 
1325
 * This allows to convert bitmaps to vector where possible */
 
1326
static GimpRGB
 
1327
get_layer_color (GimpDrawable *layer,
 
1328
                 gboolean     *single)
 
1329
{
 
1330
  GimpRGB col;
 
1331
  gdouble red, green, blue, alpha;
 
1332
  gdouble dev, devSum;
 
1333
  gdouble median, pixels, count, precentile;
 
1334
  gint32  id;
 
1335
 
 
1336
  id = layer->drawable_id;
 
1337
  devSum = 0;
 
1338
  red = 0;
 
1339
  green = 0;
 
1340
  blue = 0;
 
1341
  alpha = 0;
 
1342
  dev = 0;
 
1343
 
 
1344
  if (gimp_drawable_is_indexed (id))
 
1345
    {
 
1346
      /* FIXME: We can't do a propper histogram on indexed layers! */
 
1347
      *single = FALSE;
 
1348
      col. r = col.g = col.b = col.a = 0;
 
1349
      return col;
 
1350
    }
 
1351
 
 
1352
  /* Are we in RGB mode? */
 
1353
  if (layer->bpp >= 3)
 
1354
    {
 
1355
      gimp_histogram (id, GIMP_HISTOGRAM_RED, 0, 255, &red, &dev, &median, &pixels, &count, &precentile);
 
1356
      devSum += dev;
 
1357
      gimp_histogram (id, GIMP_HISTOGRAM_GREEN, 0, 255, &green, &dev, &median, &pixels, &count, &precentile);
 
1358
      devSum += dev;
 
1359
      gimp_histogram (id, GIMP_HISTOGRAM_BLUE, 0, 255, &blue, &dev, &median, &pixels, &count, &precentile);
 
1360
      devSum += dev;
 
1361
    }
 
1362
  /* We are in Grayscale mode (or Indexed) */
 
1363
  else
 
1364
    {
 
1365
      gimp_histogram (id, GIMP_HISTOGRAM_VALUE, 0, 255, &red, &dev, &median, &pixels, &count, &precentile);
 
1366
      devSum += dev;
 
1367
      green = red;
 
1368
      blue = red;
 
1369
    }
 
1370
  if (gimp_drawable_has_alpha (id))
 
1371
    gimp_histogram (id, GIMP_HISTOGRAM_ALPHA, 0, 255, &alpha, &dev, &median, &pixels, &count, &precentile);
 
1372
  else
 
1373
    alpha = 255;
 
1374
 
 
1375
  devSum += dev;
 
1376
  *single = devSum == 0;
 
1377
 
 
1378
  col.r = red/255;
 
1379
  col.g = green/255;
 
1380
  col.b = blue/255;
 
1381
  col.a = alpha/255;
 
1382
 
 
1383
  return col;
 
1384
}
 
1385
 
 
1386
/* A function that uses Pango to render the text to our cairo surface,
 
1387
 * in the same way it was the user saw it inside gimp.
 
1388
 * Needs some work on choosing the font name better, and on hinting
 
1389
 * (freetype and pango differences)
 
1390
 */
 
1391
static void
 
1392
drawText (GimpDrawable *text_layer,
 
1393
          gdouble       opacity,
 
1394
          cairo_t      *cr,
 
1395
          gdouble       x_res,
 
1396
          gdouble       y_res)
 
1397
{
 
1398
  gint32                text_id = text_layer->drawable_id;
 
1399
  GimpImageBaseType     type = gimp_drawable_type (text_id);
 
1400
 
 
1401
  gchar                *text = gimp_text_layer_get_text (text_id);
 
1402
  gchar                *markup = gimp_text_layer_get_markup (text_id);
 
1403
  gchar                *font_family;
 
1404
 
 
1405
  cairo_font_options_t *options;
 
1406
 
 
1407
  gint                  x;
 
1408
  gint                  y;
 
1409
 
 
1410
  GimpRGB               color;
 
1411
 
 
1412
  GimpUnit              unit;
 
1413
  gdouble               size;
 
1414
 
 
1415
  GimpTextHintStyle     hinting;
 
1416
 
 
1417
  GimpTextJustification j;
 
1418
  gboolean              justify;
 
1419
  PangoAlignment        align;
 
1420
  GimpTextDirection     dir;
 
1421
  PangoDirection        pango_dir;
 
1422
 
 
1423
  PangoLayout          *layout;
 
1424
  PangoContext         *context;
 
1425
  PangoFontDescription *font_description;
 
1426
 
 
1427
  gdouble               indent;
 
1428
  gdouble               line_spacing;
 
1429
  gdouble               letter_spacing;
 
1430
  PangoAttribute       *letter_spacing_at;
 
1431
  PangoAttrList        *attr_list = pango_attr_list_new ();
 
1432
 
 
1433
  cairo_save (cr);
 
1434
 
 
1435
  options = cairo_font_options_create ();
 
1436
  attr_list = pango_attr_list_new ();
 
1437
  cairo_get_font_options (cr, options);
 
1438
 
 
1439
  /* Position */
 
1440
  gimp_drawable_offsets (text_id, &x, &y);
 
1441
  cairo_move_to (cr, x, y);
 
1442
 
 
1443
  /* Color */
 
1444
  /* When dealing with a gray/indexed image, the viewed color of the text layer
 
1445
   * can be different than the one kept in the memory */
 
1446
  if (type == GIMP_RGB)
 
1447
    gimp_text_layer_get_color (text_id, &color);
 
1448
  else
 
1449
    gimp_image_pick_color (gimp_item_get_image (text_id), text_id, x, y, FALSE, FALSE, 0, &color);
 
1450
 
 
1451
  cairo_set_source_rgba (cr, color.r, color.g, color.b, opacity);
 
1452
 
 
1453
  /* Hinting */
 
1454
  hinting = gimp_text_layer_get_hint_style (text_id);
 
1455
  switch (hinting)
 
1456
    {
 
1457
    case GIMP_TEXT_HINT_STYLE_NONE:
 
1458
      cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
 
1459
      break;
 
1460
 
 
1461
    case GIMP_TEXT_HINT_STYLE_SLIGHT:
 
1462
      cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_SLIGHT);
 
1463
      break;
 
1464
 
 
1465
    case GIMP_TEXT_HINT_STYLE_MEDIUM:
 
1466
      cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_MEDIUM);
 
1467
      break;
 
1468
 
 
1469
    case GIMP_TEXT_HINT_STYLE_FULL:
 
1470
      cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_FULL);
 
1471
      break;
 
1472
    }
 
1473
 
 
1474
  /* Antialiasing */
 
1475
  if (gimp_text_layer_get_antialias (text_id))
 
1476
    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_DEFAULT);
 
1477
  else
 
1478
    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_NONE);
 
1479
 
 
1480
  /* We are done with cairo's settings.
 
1481
   * It's time to create the context */
 
1482
  cairo_set_font_options (cr, options);
 
1483
  context = pango_cairo_create_context (cr);
 
1484
  pango_cairo_context_set_font_options (context, options);
 
1485
 
 
1486
  /* Text Direction */
 
1487
  dir = gimp_text_layer_get_base_direction (text_id);
 
1488
 
 
1489
  if (dir == GIMP_TEXT_DIRECTION_RTL)
 
1490
    pango_dir = PANGO_DIRECTION_RTL;
 
1491
  else
 
1492
    pango_dir = PANGO_DIRECTION_LTR;
 
1493
 
 
1494
  pango_context_set_base_dir (context, pango_dir);
 
1495
 
 
1496
  /* We are done with the context's settings.
 
1497
   * It's time to create the layout */
 
1498
  layout = pango_layout_new (context);
 
1499
 
 
1500
  /* Font */
 
1501
  font_family = gimp_text_layer_get_font (text_id);
 
1502
  /* We need to find a way to convert GIMP's returned font name to
 
1503
   * a normal Pango name... Hopefully GIMP 2.8 with Pango will fix it. */
 
1504
  font_description = pango_font_description_from_string (font_family);
 
1505
 
 
1506
  /* Font Size */
 
1507
  size = gimp_text_layer_get_font_size (text_id, &unit);
 
1508
  if (! g_strcmp0 (gimp_unit_get_abbreviation (unit), "px") == 0)
 
1509
    size *= 1.0 / gimp_unit_get_factor (unit) * x_res / y_res;
 
1510
  pango_font_description_set_absolute_size (font_description, size * PANGO_SCALE);
 
1511
 
 
1512
  pango_layout_set_font_description (layout, font_description);
 
1513
 
 
1514
  /* Width and height */
 
1515
  pango_layout_set_width (layout, text_layer->width * PANGO_SCALE);
 
1516
  pango_layout_set_height (layout, text_layer->height * PANGO_SCALE);
 
1517
 
 
1518
  /* Justification, and Alignment */
 
1519
  justify = FALSE;
 
1520
  j = gimp_text_layer_get_justification (text_id);
 
1521
 
 
1522
  if (j == GIMP_TEXT_JUSTIFY_CENTER)
 
1523
    align = PANGO_ALIGN_CENTER;
 
1524
  else if (j == GIMP_TEXT_JUSTIFY_LEFT)
 
1525
    align = PANGO_ALIGN_LEFT;
 
1526
  else if (j == GIMP_TEXT_JUSTIFY_RIGHT)
 
1527
    align = PANGO_ALIGN_RIGHT;
 
1528
  else /* We have GIMP_TEXT_JUSTIFY_FILL */
 
1529
    {
 
1530
      if (dir == GIMP_TEXT_DIRECTION_LTR)
 
1531
        align = PANGO_ALIGN_LEFT;
 
1532
      else
 
1533
        align = PANGO_ALIGN_RIGHT;
 
1534
      justify = TRUE;
 
1535
    }
 
1536
 
 
1537
  /* Indentation */
 
1538
  indent = gimp_text_layer_get_indent (text_id);
 
1539
  pango_layout_set_indent (layout, (int)(PANGO_SCALE * indent));
 
1540
 
 
1541
  /* Line Spacing */
 
1542
  line_spacing = gimp_text_layer_get_line_spacing (text_id);
 
1543
  pango_layout_set_spacing (layout, (int)(PANGO_SCALE * line_spacing));
 
1544
 
 
1545
  /* Letter Spacing */
 
1546
  letter_spacing = gimp_text_layer_get_letter_spacing (text_id);
 
1547
  letter_spacing_at = pango_attr_letter_spacing_new ((int)(PANGO_SCALE * letter_spacing));
 
1548
  pango_attr_list_insert (attr_list, letter_spacing_at);
 
1549
 
 
1550
  pango_layout_set_justify (layout, justify);
 
1551
  pango_layout_set_alignment (layout, align);
 
1552
 
 
1553
  pango_layout_set_attributes (layout, attr_list);
 
1554
 
 
1555
  /* Use the pango markup of the text layer */
 
1556
 
 
1557
  if (markup != NULL && markup[0] != '\0')
 
1558
    pango_layout_set_markup (layout, markup, -1);
 
1559
  else /* If we can't find a markup, then it has just text */
 
1560
    pango_layout_set_text (layout, text, -1);
 
1561
 
 
1562
  pango_cairo_show_layout (cr, layout);
 
1563
 
 
1564
  g_free (text);
 
1565
  g_free (font_family);
 
1566
 
 
1567
  g_object_unref (layout);
 
1568
  pango_font_description_free (font_description);
 
1569
  g_object_unref (context);
 
1570
  pango_attr_list_unref (attr_list);
 
1571
 
 
1572
  cairo_font_options_destroy (options);
 
1573
 
 
1574
  cairo_restore (cr);
 
1575
}