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 <https://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 supports
83  *     selecting 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 <errno.h>
106 
107 #include <glib/gstdio.h>
108 #include <cairo-pdf.h>
109 #include <pango/pangocairo.h>
110 
111 #include <libgimp/gimp.h>
112 #include <libgimp/gimpui.h>
113 
114 #include "libgimp/stdplugins-intl.h"
115 
116 
117 #define SAVE_PROC               "file-pdf-save"
118 #define SAVE2_PROC              "file-pdf-save2"
119 #define SAVE_MULTI_PROC         "file-pdf-save-multi"
120 #define PLUG_IN_BINARY          "file-pdf-save"
121 #define PLUG_IN_ROLE            "gimp-file-pdf-save"
122 
123 #define DATA_OPTIMIZE           "file-pdf-data-optimize"
124 #define DATA_IMAGE_LIST         "file-pdf-data-multi-page"
125 
126 /* Gimp will crash before you reach this limitation :D */
127 #define MAX_PAGE_COUNT           350
128 #define MAX_FILE_NAME_LENGTH     350
129 
130 #define THUMB_WIDTH              90
131 #define THUMB_HEIGHT             120
132 
133 #define GIMP_PLUGIN_PDF_SAVE_ERROR gimp_plugin_pdf_save_error_quark ()
134 
135 typedef enum
136 {
137   GIMP_PLUGIN_PDF_SAVE_ERROR_FAILED
138 } GimpPluginPDFError;
139 
140 GQuark gimp_plugin_pdf_save_error_quark (void);
141 
142 typedef enum
143 {
144   SA_RUN_MODE,
145   SA_IMAGE,
146   SA_DRAWABLE,
147   SA_FILENAME,
148   SA_RAW_FILENAME,
149   SA_VECTORIZE,
150   SA_IGNORE_HIDDEN,
151   SA_APPLY_MASKS,
152   SA_LAYERS_AS_PAGES,
153   SA_REVERSE_ORDER,
154   SA_ARG_COUNT
155 } SaveArgs;
156 
157 typedef enum
158 {
159   SMA_RUN_MODE,
160   SMA_COUNT,
161   SMA_IMAGES,
162   SMA_VECTORIZE,
163   SMA_IGNORE_HIDDEN,
164   SMA_APPLY_MASKS,
165   SMA_FILENAME,
166   SMA_RAWFILENAME,
167   SMA_ARG_COUNT
168 } SaveMultiArgs;
169 
170 typedef struct
171 {
172   gboolean vectorize;
173   gboolean ignore_hidden;
174   gboolean apply_masks;
175   gboolean layers_as_pages;
176   gboolean reverse_order;
177 } PdfOptimize;
178 
179 typedef struct
180 {
181   gint32  images[MAX_PAGE_COUNT];
182   guint32 image_count;
183   gchar   file_name[MAX_FILE_NAME_LENGTH];
184 } PdfMultiPage;
185 
186 typedef struct
187 {
188   PdfOptimize  optimize;
189   GArray      *images;
190 } PdfMultiVals;
191 
192 enum
193 {
194   THUMB,
195   PAGE_NUMBER,
196   IMAGE_NAME,
197   IMAGE_ID
198 };
199 
200 typedef struct
201 {
202   GdkPixbuf *thumb;
203   gint32     page_number;
204   gchar     *image_name;
205 } Page;
206 
207 
208 static void              query                      (void);
209 
210 static void              run                        (const gchar     *name,
211                                                      gint             nparams,
212                                                      const GimpParam *param,
213                                                      gint            *nreturn_vals,
214                                                      GimpParam      **return_vals);
215 
216 static gboolean          init_vals                  (const gchar     *name,
217                                                      gint             nparams,
218                                                      const GimpParam *param,
219                                                      gboolean        *single,
220                                                      gboolean        *defaults,
221                                                      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 static void              reverse_order_toggled      (GtkToggleButton *reverse_order,
232                                                      GtkButton       *layers_as_pages);
233 
234 static void              choose_file_call           (GtkWidget       *browse_button,
235                                                      gpointer         file_entry);
236 
237 static gboolean          get_image_list             (void);
238 
239 static GtkTreeModel    * create_model               (void);
240 
241 static void              add_image_call             (GtkWidget       *widget,
242                                                      gpointer         img_combo);
243 static void              del_image_call             (GtkWidget       *widget,
244                                                      gpointer         icon_view);
245 static void              remove_call                (GtkTreeModel    *tree_model,
246                                                      GtkTreePath     *path,
247                                                      gpointer         user_data);
248 static void              recount_pages              (void);
249 
250 static cairo_surface_t * get_cairo_surface          (gint32           drawable_ID,
251                                                      gboolean         as_mask,
252                                                      GError         **error);
253 
254 static GimpRGB           get_layer_color            (gint32           layer_ID,
255                                                      gboolean        *single);
256 
257 static void              drawText                   (gint32           text_id,
258                                                      gdouble          opacity,
259                                                      cairo_t         *cr,
260                                                      gdouble          x_res,
261                                                      gdouble          y_res);
262 
263 static gboolean          draw_layer                 (gint32          *layers,
264                                                      gint             n_layers,
265                                                      gint             j,
266                                                      cairo_t         *cr,
267                                                      gdouble          x_res,
268                                                      gdouble          y_res,
269                                                      const gchar     *name,
270                                                      GError         **error);
271 
272 static gboolean     dnd_remove = TRUE;
273 static PdfMultiPage multi_page;
274 
275 static PdfOptimize optimize =
276 {
277   TRUE,  /* vectorize */
278   TRUE,  /* ignore_hidden */
279   TRUE,  /* apply_masks */
280   FALSE, /* layers_as_pages */
281   FALSE  /* reverse_order */
282 };
283 
284 static GtkTreeModel *model;
285 static GtkWidget    *file_choose;
286 static gchar        *file_name;
287 
288 GimpPlugInInfo PLUG_IN_INFO =
289 {
290   NULL,
291   NULL,
292   query,
293   run
294 };
295 
296 G_DEFINE_QUARK (gimp-plugin-pdf-save-error-quark, gimp_plugin_pdf_save_error)
297 
MAIN()298 MAIN()
299 
300 static void
301 query (void)
302 {
303   static GimpParamDef save_args[] =
304   {
305     { GIMP_PDB_INT32,    "run-mode",      "Run mode" },
306     { GIMP_PDB_IMAGE,    "image",         "Input image" },
307     { GIMP_PDB_DRAWABLE, "drawable",      "Input drawable" },
308     { GIMP_PDB_STRING,   "filename",      "The name of the file to save the image in" },
309     { GIMP_PDB_STRING,   "raw-filename",  "The name of the file to save the image in" },
310     { GIMP_PDB_INT32,    "vectorize",     "Convert bitmaps to vector graphics where possible. TRUE or FALSE" },
311     { GIMP_PDB_INT32,    "ignore-hidden", "Omit hidden layers and layers with zero opacity. TRUE or FALSE" },
312     { GIMP_PDB_INT32,    "apply-masks",   "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)" }
313   };
314 
315   static GimpParamDef save2_args[] =
316   {
317     { GIMP_PDB_INT32,    "run-mode",        "Run mode" },
318     { GIMP_PDB_IMAGE,    "image",           "Input image" },
319     { GIMP_PDB_DRAWABLE, "drawable",        "Input drawable" },
320     { GIMP_PDB_STRING,   "filename",        "The name of the file to save the image in" },
321     { GIMP_PDB_STRING,   "raw-filename",    "The name of the file to save the image in" },
322     { GIMP_PDB_INT32,    "vectorize",       "Convert bitmaps to vector graphics where possible. TRUE or FALSE" },
323     { GIMP_PDB_INT32,    "ignore-hidden",   "Omit hidden layers and layers with zero opacity. TRUE or FALSE" },
324     { GIMP_PDB_INT32,    "apply-masks",     "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)" },
325     { GIMP_PDB_INT32,    "layers-as-pages", "Layers as pages (bottom layers first). TRUE or FALSE" },
326     { GIMP_PDB_INT32,    "reverse-order",   "Reverse the pages order (top layers first). TRUE or FALSE" }
327   };
328 
329   static GimpParamDef save_multi_args[] =
330   {
331     { GIMP_PDB_INT32,      "run-mode",        "Run mode" },
332     { GIMP_PDB_INT32,      "count",           "The amount of images entered (This will be the amount of pages). 1 <= count <= MAX_PAGE_COUNT" },
333     { GIMP_PDB_INT32ARRAY, "images",          "Input image for each page (An image can appear more than once)" },
334     { GIMP_PDB_INT32,      "vectorize",       "Convert bitmaps to vector graphics where possible. TRUE or FALSE" },
335     { GIMP_PDB_INT32,      "ignore-hidden",   "Omit hidden layers and layers with zero opacity. TRUE or FALSE" },
336     { GIMP_PDB_INT32,      "apply-masks",     "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)" },
337     { GIMP_PDB_STRING,     "filename",        "The name of the file to save the image in" },
338     { GIMP_PDB_STRING,     "raw-filename",    "The name of the file to save the image in" }
339   };
340 
341   gimp_install_procedure (SAVE_PROC,
342                           "Save files in PDF format",
343                           "Saves files in Adobe's Portable Document Format. "
344                           "PDF is designed to be easily processed by a variety "
345                           "of different platforms, and is a distant cousin of "
346                           "PostScript.",
347                           "Barak Itkin",
348                           "Copyright Barak Itkin",
349                           "August 2009",
350                           N_("Portable Document Format"),
351                           "RGB*, GRAY*, INDEXED*",
352                           GIMP_PLUGIN,
353                           G_N_ELEMENTS (save_args), 0,
354                           save_args, NULL);
355 
356   gimp_install_procedure (SAVE2_PROC,
357                           "Save files in PDF format",
358                           "Saves files in Adobe's Portable Document Format. "
359                           "PDF is designed to be easily processed by a variety "
360                           "of different platforms, and is a distant cousin of "
361                           "PostScript.\n"
362                           "This procedure adds an extra parameter to "
363                           "file-pdf-save to save layers as pages.",
364                           "Barak Itkin, Lionel N., Jehan",
365                           "Copyright Barak Itkin, Lionel N., Jehan",
366                           "August 2009, 2017",
367                           N_("Portable Document Format"),
368                           "RGB*, GRAY*, INDEXED*",
369                           GIMP_PLUGIN,
370                           G_N_ELEMENTS (save2_args), 0,
371                           save2_args, NULL);
372 
373   gimp_install_procedure (SAVE_MULTI_PROC,
374                           "Save files in PDF format",
375                           "Saves files in Adobe's Portable Document Format. "
376                           "PDF is designed to be easily processed by a variety "
377                           "of different platforms, and is a distant cousin of "
378                           "PostScript.",
379                           "Barak Itkin",
380                           "Copyright Barak Itkin",
381                           "August 2009",
382                           N_("_Create multipage PDF..."),
383                           "RGB*, GRAY*, INDEXED*",
384                           GIMP_PLUGIN,
385                           G_N_ELEMENTS (save_multi_args), 0,
386                           save_multi_args, NULL);
387 
388 #if 0
389   gimp_plugin_menu_register (SAVE_MULTI_PROC,
390                              "<Image>/File/Create/PDF");
391 #endif
392 
393   gimp_register_file_handler_mime (SAVE2_PROC, "application/pdf");
394   gimp_register_save_handler (SAVE2_PROC, "pdf", "");
395 }
396 
397 static cairo_status_t
write_func(void * fp,const unsigned char * data,unsigned int size)398 write_func (void                *fp,
399             const unsigned char *data,
400             unsigned int         size)
401 {
402   return fwrite (data, 1, size, fp) == size ? CAIRO_STATUS_SUCCESS
403                                             : CAIRO_STATUS_WRITE_ERROR;
404 }
405 
406 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)407 run (const gchar      *name,
408      gint              nparams,
409      const GimpParam  *param,
410      gint             *nreturn_vals,
411      GimpParam       **return_vals)
412 {
413   static GimpParam        values[2];
414   GimpPDBStatusType       status = GIMP_PDB_SUCCESS;
415   GimpRunMode             run_mode;
416   gboolean                single_image;
417   gboolean                defaults_proc;
418   cairo_surface_t        *pdf_file;
419   cairo_t                *cr;
420   GimpExportCapabilities  capabilities;
421   FILE                   *fp;
422   gint                    i;
423   GError                 *error = NULL;
424 
425   INIT_I18N ();
426   gegl_init (NULL, NULL);
427 
428   /* Setting mandatory output values */
429   *nreturn_vals = 1;
430   *return_vals  = values;
431 
432   values[0].type          = GIMP_PDB_STATUS;
433   values[0].data.d_status = status;
434 
435   /* Initializing all the settings */
436   multi_page.image_count = 0;
437 
438   if (! init_vals (name, nparams, param, &single_image,
439                    &defaults_proc, &run_mode))
440     {
441       values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
442       return;
443     }
444 
445   /* Starting the executions */
446   if (run_mode == GIMP_RUN_INTERACTIVE)
447     {
448       if (single_image)
449         {
450           if (! gui_single ())
451             {
452               values[0].data.d_status = GIMP_PDB_CANCEL;
453               return;
454             }
455         }
456       else if (! gui_multi ())
457         {
458           values[0].data.d_status = GIMP_PDB_CANCEL;
459           return;
460         }
461 
462       if (file_name == NULL)
463         {
464           values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
465           gimp_message (_("You must select a file to save!"));
466           return;
467         }
468     }
469 
470   fp = g_fopen (file_name, "wb");
471   if (fp == NULL)
472     {
473       *nreturn_vals = 2;
474 
475       values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
476       values[1].type          = GIMP_PDB_STRING;
477       if (error == NULL)
478         {
479           g_set_error (&error, G_FILE_ERROR, g_file_error_from_errno (errno),
480                        _("Could not open '%s' for writing: %s"),
481                        gimp_filename_to_utf8 (file_name), g_strerror (errno));
482         }
483       values[1].data.d_string = error->message;
484       return;
485     }
486 
487   pdf_file = cairo_pdf_surface_create_for_stream (write_func, fp, 1, 1);
488 
489   if (cairo_surface_status (pdf_file) != CAIRO_STATUS_SUCCESS)
490     {
491       g_message (_("An error occurred while creating the PDF file:\n"
492                    "%s\n"
493                    "Make sure you entered a valid filename and that the "
494                    "selected location isn't read only!"),
495                  cairo_status_to_string (cairo_surface_status (pdf_file)));
496 
497       values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
498       return;
499     }
500 
501   cr = cairo_create (pdf_file);
502 
503   capabilities = (GIMP_EXPORT_CAN_HANDLE_RGB    |
504                   GIMP_EXPORT_CAN_HANDLE_ALPHA  |
505                   GIMP_EXPORT_CAN_HANDLE_GRAY   |
506                   GIMP_EXPORT_CAN_HANDLE_LAYERS |
507                   GIMP_EXPORT_CAN_HANDLE_INDEXED);
508   /* This seems counter-intuitive, but not setting the mask capability
509    * will apply any layer mask upon gimp_export_image().
510    */
511   if (! optimize.apply_masks)
512     capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS;
513 
514   for (i = 0; i < multi_page.image_count; i++)
515     {
516       gint32    image_ID = multi_page.images[i];
517       gint32   *layers;
518       gint32    n_layers;
519       gdouble   x_res, y_res;
520       gdouble   x_scale, y_scale;
521       gint32    temp;
522       gint      j;
523 
524       temp = gimp_image_get_active_drawable (image_ID);
525       if (temp < 1)
526         continue;
527 
528       /* Save the state of the surface before any changes, so that
529        * settings from one page won't affect all the others
530        */
531       cairo_save (cr);
532 
533       if (! (gimp_export_image (&image_ID, &temp, NULL,
534                                 capabilities) == GIMP_EXPORT_EXPORT))
535         {
536           /* gimp_drawable_histogram() only works within the bounds of
537            * the selection, which is a problem (see issue #2431).
538            * Instead of saving the selection, unselecting to later
539            * reselect, let's just always work on a duplicate of the
540            * image.
541            */
542           image_ID = gimp_image_duplicate (image_ID);
543         }
544       gimp_selection_none (image_ID);
545 
546       gimp_image_get_resolution (image_ID, &x_res, &y_res);
547       x_scale = 72.0 / x_res;
548       y_scale = 72.0 / y_res;
549 
550       cairo_pdf_surface_set_size (pdf_file,
551                                   gimp_image_width (image_ID) * x_scale,
552                                   gimp_image_height (image_ID) * y_scale);
553 
554       /* This way we set how many pixels are there in every inch.
555        * It's very important for PangoCairo
556        */
557       cairo_surface_set_fallback_resolution (pdf_file, x_res, y_res);
558 
559       /* Cairo has a concept of user-space vs device-space units.
560        * From what I understand, by default the user-space unit is the
561        * typographical "point". Since we work mostly with pixels, not
562        * points, the following call simply scales the transformation
563        * matrix from points to pixels, relatively to the image
564        * resolution, knowing that 1 typographical point == 1/72 inch.
565        */
566       cairo_scale (cr, x_scale, y_scale);
567 
568       layers = gimp_image_get_layers (image_ID, &n_layers);
569 
570       /* Fill image with background color -
571        * otherwise the output PDF will always show white for background,
572        * and may display artifacts at transparency boundaries
573        */
574       if (gimp_drawable_has_alpha (layers[n_layers - 1]))
575         {
576           GimpRGB color;
577 
578           cairo_rectangle (cr, 0.0, 0.0,
579                            gimp_image_width (image_ID),
580                            gimp_image_height (image_ID));
581           gimp_context_get_background (&color);
582           cairo_set_source_rgb (cr,
583                                 color.r,
584                                 color.g,
585                                 color.b);
586           cairo_fill (cr);
587         }
588 
589       /* Now, we should loop over the layers of each image */
590       for (j = 0; j < n_layers; j++)
591         {
592           if (! draw_layer (layers, n_layers, j, cr, x_res, y_res, name, &error))
593             {
594               *nreturn_vals = 2;
595 
596               /* free the resources */
597               g_free (layers);
598               cairo_surface_destroy (pdf_file);
599               cairo_destroy (cr);
600               fclose (fp);
601 
602               values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
603 
604               values[1].type          = GIMP_PDB_STRING;
605               values[1].data.d_string = error->message;
606               return;
607             }
608         }
609       g_free (layers);
610 
611       /* We are done with this image - Show it!
612        * Unless that's a multi-page to avoid blank page at the end
613        */
614       if (g_strcmp0 (name, SAVE2_PROC) != 0 ||
615           ! optimize.layers_as_pages)
616         cairo_show_page (cr);
617       cairo_restore (cr);
618 
619       gimp_image_delete (image_ID);
620     }
621 
622   /* We are done with all the images - time to free the resources */
623   cairo_surface_destroy (pdf_file);
624   cairo_destroy (cr);
625 
626   fclose (fp);
627 
628   /* Finally done, let's save the parameters */
629   gimp_set_data (DATA_OPTIMIZE, &optimize, sizeof (optimize));
630 
631   if (! single_image)
632     {
633       g_strlcpy (multi_page.file_name, file_name, MAX_FILE_NAME_LENGTH);
634       gimp_set_data (DATA_IMAGE_LIST, &multi_page, sizeof (multi_page));
635     }
636 }
637 
638 /******************************************************/
639 /* Beginning of parameter handling functions          */
640 /******************************************************/
641 
642 /* A function that takes care of loading the basic parameters
643  */
644 static gboolean
init_vals(const gchar * name,gint nparams,const GimpParam * param,gboolean * single_image,gboolean * defaults_proc,GimpRunMode * run_mode)645 init_vals (const gchar      *name,
646            gint              nparams,
647            const GimpParam  *param,
648            gboolean         *single_image,
649            gboolean         *defaults_proc,
650            GimpRunMode      *run_mode)
651 {
652   gboolean had_saved_list = FALSE;
653   gboolean single;
654   gboolean defaults = FALSE;
655   gint32   i;
656   gint32   image;
657 
658   if ((g_str_equal (name, SAVE_PROC) && nparams == SA_ARG_COUNT - 2) ||
659       (g_str_equal (name, SAVE2_PROC) && nparams == SA_ARG_COUNT))
660     {
661       single = TRUE;
662       *run_mode = param[SA_RUN_MODE].data.d_int32;
663       image = param[SA_IMAGE].data.d_int32;
664       file_name = param[SA_FILENAME].data.d_string;
665 
666       if (*run_mode == GIMP_RUN_NONINTERACTIVE)
667         {
668           optimize.apply_masks = param[SA_APPLY_MASKS].data.d_int32;
669           optimize.vectorize = param[SA_VECTORIZE].data.d_int32;
670           optimize.ignore_hidden = param[SA_IGNORE_HIDDEN].data.d_int32;
671           if (nparams == SA_ARG_COUNT)
672           {
673             optimize.layers_as_pages = param[SA_LAYERS_AS_PAGES].data.d_int32;
674             optimize.reverse_order = param[SA_REVERSE_ORDER].data.d_int32;
675           }
676         }
677       else
678         defaults = TRUE;
679     }
680   else if (g_str_equal (name, SAVE_MULTI_PROC))
681     {
682       single = FALSE;
683       if (nparams != SMA_ARG_COUNT)
684         return FALSE;
685 
686       *run_mode = param[SMA_RUN_MODE].data.d_int32;
687       image = -1;
688       file_name = param[SMA_FILENAME].data.d_string;
689 
690       optimize.apply_masks = param[SMA_APPLY_MASKS].data.d_int32;
691       optimize.vectorize = param[SMA_VECTORIZE].data.d_int32;
692       optimize.ignore_hidden = param[SMA_IGNORE_HIDDEN].data.d_int32;
693     }
694   else
695     {
696       return FALSE;
697     }
698 
699   switch (*run_mode)
700     {
701     case GIMP_RUN_NONINTERACTIVE:
702       if (single)
703         {
704           init_image_list_defaults (image);
705         }
706       else
707         {
708           multi_page.image_count = param[SMA_COUNT].data.d_int32;
709           if (param[SMA_IMAGES].data.d_int32array != NULL)
710             for (i = 0; i < param[SMA_COUNT].data.d_int32; i++)
711               multi_page.images[i] = param[SMA_IMAGES].data.d_int32array[i];
712         }
713       break;
714 
715     case GIMP_RUN_INTERACTIVE:
716       /* Possibly retrieve data */
717       gimp_get_data (DATA_OPTIMIZE, &optimize);
718       had_saved_list = gimp_get_data (DATA_IMAGE_LIST, &multi_page);
719 
720       if (had_saved_list && (file_name == NULL || strlen (file_name) == 0))
721         {
722           file_name = multi_page.file_name;
723         }
724 
725       if (single || ! had_saved_list )
726         init_image_list_defaults (image);
727       break;
728 
729     case GIMP_RUN_WITH_LAST_VALS:
730       /* Possibly retrieve data */
731       if (! single)
732         {
733           had_saved_list = gimp_get_data (DATA_IMAGE_LIST, &multi_page);
734           if (had_saved_list)
735             {
736               file_name = multi_page.file_name;
737             }
738         }
739       else
740         {
741           init_image_list_defaults (image);
742         }
743       gimp_get_data (DATA_OPTIMIZE, &optimize);
744       break;
745     }
746 
747   *defaults_proc = defaults;
748   *single_image = single;
749 
750   validate_image_list ();
751 
752   return TRUE;
753 }
754 
755 /* A function that initializes the image list to default values */
756 static void
init_image_list_defaults(gint32 image)757 init_image_list_defaults (gint32 image)
758 {
759   if (image != -1)
760     {
761       multi_page.images[0]   = image;
762       multi_page.image_count = 1;
763     }
764   else
765     {
766       multi_page.image_count = 0;
767     }
768 }
769 
770 /* A function that removes images that are no longer valid from the
771  * image list
772  */
773 static void
validate_image_list(void)774 validate_image_list (void)
775 {
776   gint32  valid = 0;
777   guint32 i     = 0;
778 
779   for (i = 0 ; i < MAX_PAGE_COUNT && i < multi_page.image_count ; i++)
780     {
781       if (gimp_image_is_valid (multi_page.images[i]))
782         {
783           multi_page.images[valid] = multi_page.images[i];
784           valid++;
785         }
786     }
787 
788   multi_page.image_count = valid;
789 }
790 
791 
792 /******************************************************/
793 /* Beginning of GUI functions                         */
794 /******************************************************/
795 
796 /* The main GUI function for saving single-paged PDFs */
797 
798 static gboolean
gui_single(void)799 gui_single (void)
800 {
801   GtkWidget *window;
802   GtkWidget *vbox;
803   GtkWidget *vectorize_c;
804   GtkWidget *ignore_hidden_c;
805   GtkWidget *apply_c;
806   GtkWidget *layers_as_pages_c;
807   GtkWidget *reverse_order_c;
808   GtkWidget *frame;
809   gchar     *text;
810   gboolean   run;
811   gint32     n_layers;
812 
813   gimp_ui_init (PLUG_IN_BINARY, FALSE);
814 
815   window = gimp_export_dialog_new ("PDF", PLUG_IN_ROLE, SAVE_PROC);
816 
817   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
818   gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (window)),
819                       vbox, TRUE, TRUE, 0);
820 
821   gtk_container_set_border_width (GTK_CONTAINER (window), 12);
822 
823   ignore_hidden_c = gtk_check_button_new_with_mnemonic (_("_Omit hidden layers and layers with zero opacity"));
824   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ignore_hidden_c),
825                                 optimize.ignore_hidden);
826   gtk_box_pack_end (GTK_BOX (vbox), ignore_hidden_c, TRUE, TRUE, 0);
827 
828   vectorize_c = gtk_check_button_new_with_mnemonic (_("Convert _bitmaps to vector graphics where possible"));
829   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vectorize_c),
830                                 optimize.vectorize);
831   gtk_box_pack_end (GTK_BOX (vbox), vectorize_c, TRUE, TRUE, 0);
832 
833   apply_c = gtk_check_button_new_with_mnemonic (_("_Apply layer masks before saving"));
834   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_c),
835                                 optimize.apply_masks);
836   gtk_box_pack_end (GTK_BOX (vbox), apply_c, TRUE, TRUE, 0);
837   gimp_help_set_help_data (apply_c, _("Keeping the masks will not change the output"), NULL);
838 
839   /* Frame for multi-page from layers. */
840   frame = gtk_frame_new (NULL);
841   gtk_box_pack_end (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
842 
843   text = g_strdup_printf (_("_Layers as pages (%s)"),
844                           optimize.reverse_order ?
845                           _("top layers first") : _("bottom layers first"));
846   layers_as_pages_c = gtk_check_button_new_with_mnemonic (text);
847   g_free (text);
848   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (layers_as_pages_c),
849                                 optimize.layers_as_pages);
850   gtk_frame_set_label_widget (GTK_FRAME (frame), layers_as_pages_c);
851   g_free (gimp_image_get_layers (multi_page.images[0], &n_layers));
852 
853   reverse_order_c = gtk_check_button_new_with_mnemonic (_("_Reverse the pages order"));
854   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (reverse_order_c),
855                                 optimize.reverse_order);
856   gtk_container_add (GTK_CONTAINER (frame), reverse_order_c);
857 
858   if (n_layers <= 1)
859     {
860       gtk_widget_set_sensitive (layers_as_pages_c, FALSE);
861       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (layers_as_pages_c),
862                                     FALSE);
863     }
864 
865   g_object_bind_property (layers_as_pages_c, "active",
866                           reverse_order_c,  "sensitive",
867                           G_BINDING_SYNC_CREATE);
868   g_signal_connect (G_OBJECT (reverse_order_c), "toggled",
869                     G_CALLBACK (reverse_order_toggled),
870                     layers_as_pages_c);
871 
872   gtk_widget_show_all (window);
873 
874   run = gtk_dialog_run (GTK_DIALOG (window)) == GTK_RESPONSE_OK;
875 
876   optimize.ignore_hidden =
877     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ignore_hidden_c));
878   optimize.vectorize =
879     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vectorize_c));
880   optimize.apply_masks =
881     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_c));
882   optimize.layers_as_pages =
883     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (layers_as_pages_c));
884   optimize.reverse_order =
885     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (reverse_order_c));
886 
887   gtk_widget_destroy (window);
888 
889   return run;
890 }
891 
892 /* The main GUI function for saving multi-paged PDFs */
893 
894 static gboolean
gui_multi(void)895 gui_multi (void)
896 {
897   GtkWidget   *window;
898   GtkWidget   *vbox;
899   GtkWidget   *file_label;
900   GtkWidget   *file_entry;
901   GtkWidget   *file_browse;
902   GtkWidget   *file_hbox;
903   GtkWidget   *vectorize_c;
904   GtkWidget   *ignore_hidden_c;
905   GtkWidget   *apply_c;
906   GtkWidget   *scroll;
907   GtkWidget   *page_view;
908   GtkWidget   *h_but_box;
909   GtkWidget   *del;
910   GtkWidget   *h_box;
911   GtkWidget   *img_combo;
912   GtkWidget   *add_image;
913   gboolean     run;
914   const gchar *temp;
915 
916   gimp_ui_init (PLUG_IN_BINARY, FALSE);
917 
918   window = gimp_export_dialog_new ("PDF", PLUG_IN_ROLE, SAVE_MULTI_PROC);
919 
920   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
921   gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (window)),
922                       vbox, TRUE, TRUE, 0);
923 
924   gtk_container_set_border_width (GTK_CONTAINER (window), 12);
925 
926   file_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
927   file_label = gtk_label_new (_("Save to:"));
928   file_entry = gtk_entry_new ();
929   if (file_name != NULL)
930     gtk_entry_set_text (GTK_ENTRY (file_entry), file_name);
931   file_browse = gtk_button_new_with_label (_("Browse..."));
932   file_choose = gtk_file_chooser_dialog_new (_("Multipage PDF export"),
933                                              GTK_WINDOW (window),
934                                              GTK_FILE_CHOOSER_ACTION_SAVE,
935                                              _("_Save"),   GTK_RESPONSE_OK,
936                                              _("_Cancel"), GTK_RESPONSE_CANCEL,
937                                              NULL);
938 
939   gtk_box_pack_start (GTK_BOX (file_hbox), file_label, FALSE, FALSE, 0);
940   gtk_box_pack_start (GTK_BOX (file_hbox), file_entry, TRUE, TRUE, 0);
941   gtk_box_pack_start (GTK_BOX (file_hbox), file_browse, FALSE, FALSE, 0);
942 
943   gtk_box_pack_start (GTK_BOX (vbox), file_hbox, TRUE, TRUE, 0);
944 
945   page_view = gtk_icon_view_new ();
946   model = create_model ();
947   gtk_icon_view_set_model (GTK_ICON_VIEW (page_view), model);
948   gtk_icon_view_set_reorderable (GTK_ICON_VIEW (page_view), TRUE);
949   gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (page_view),
950                                     GTK_SELECTION_MULTIPLE);
951 
952   gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (page_view), THUMB);
953   gtk_icon_view_set_text_column (GTK_ICON_VIEW (page_view), PAGE_NUMBER);
954   gtk_icon_view_set_tooltip_column (GTK_ICON_VIEW (page_view), IMAGE_NAME);
955 
956   scroll = gtk_scrolled_window_new (NULL, NULL);
957   gtk_widget_set_size_request (scroll, -1, 300);
958 
959   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
960                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
961   gtk_container_add (GTK_CONTAINER (scroll), page_view);
962 
963   gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
964 
965   h_but_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
966   gtk_button_box_set_layout (GTK_BUTTON_BOX (h_but_box), GTK_BUTTONBOX_START);
967 
968   del = gtk_button_new_with_label (_("Remove the selected pages"));
969   gtk_box_pack_start (GTK_BOX (h_but_box), del, TRUE, TRUE, 0);
970 
971   gtk_box_pack_start (GTK_BOX (vbox), h_but_box, FALSE, FALSE, 0);
972 
973   h_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
974 
975   img_combo = gimp_image_combo_box_new (NULL, NULL);
976   gtk_box_pack_start (GTK_BOX (h_box), img_combo, FALSE, FALSE, 0);
977 
978   add_image = gtk_button_new_with_label (_("Add this image"));
979   gtk_box_pack_start (GTK_BOX (h_box), add_image, FALSE, FALSE, 0);
980 
981   gtk_box_pack_start (GTK_BOX (vbox), h_box, FALSE, FALSE, 0);
982 
983   ignore_hidden_c = gtk_check_button_new_with_mnemonic (_("_Omit hidden layers and layers with zero opacity"));
984   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ignore_hidden_c),
985                                 optimize.ignore_hidden);
986   gtk_box_pack_end (GTK_BOX (vbox), ignore_hidden_c, FALSE, FALSE, 0);
987 
988   vectorize_c = gtk_check_button_new_with_mnemonic (_("Convert _bitmaps to vector graphics where possible"));
989   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vectorize_c),
990                                 optimize.vectorize);
991   gtk_box_pack_end (GTK_BOX (vbox), vectorize_c, FALSE, FALSE, 0);
992 
993   apply_c = gtk_check_button_new_with_mnemonic (_("_Apply layer masks before saving"));
994   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_c),
995                                 optimize.apply_masks);
996   gtk_box_pack_end (GTK_BOX (vbox), apply_c, FALSE, FALSE, 0);
997   gimp_help_set_help_data (apply_c, _("Keeping the masks will not change the output"), NULL);
998 
999   gtk_widget_show_all (window);
1000 
1001   g_signal_connect (G_OBJECT (file_browse), "clicked",
1002                     G_CALLBACK (choose_file_call),
1003                     file_entry);
1004 
1005   g_signal_connect (G_OBJECT (add_image), "clicked",
1006                     G_CALLBACK (add_image_call),
1007                     img_combo);
1008 
1009   g_signal_connect (G_OBJECT (del), "clicked",
1010                     G_CALLBACK (del_image_call),
1011                     page_view);
1012 
1013   g_signal_connect (G_OBJECT (model), "row-deleted",
1014                     G_CALLBACK (remove_call),
1015                     NULL);
1016 
1017   run = gtk_dialog_run (GTK_DIALOG (window)) == GTK_RESPONSE_OK;
1018 
1019   run &= get_image_list ();
1020 
1021   temp = gtk_entry_get_text (GTK_ENTRY (file_entry));
1022   g_stpcpy (file_name, temp);
1023 
1024   optimize.ignore_hidden =
1025     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ignore_hidden_c));
1026   optimize.vectorize =
1027     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vectorize_c));
1028   optimize.apply_masks =
1029     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_c));
1030 
1031   gtk_widget_destroy (window);
1032 
1033   return run;
1034 }
1035 
1036 static void
reverse_order_toggled(GtkToggleButton * reverse_order,GtkButton * layers_as_pages)1037 reverse_order_toggled (GtkToggleButton *reverse_order,
1038                        GtkButton       *layers_as_pages)
1039 {
1040   gchar *text;
1041 
1042   text = g_strdup_printf (_("Layers as pages (%s)"),
1043                           gtk_toggle_button_get_active (reverse_order) ?
1044                           _("top layers first") : _("bottom layers first"));
1045   gtk_button_set_label (layers_as_pages, text);
1046   g_free (text);
1047 }
1048 
1049 /* A function that is called when the button for browsing for file
1050  * locations was clicked
1051  */
1052 static void
choose_file_call(GtkWidget * browse_button,gpointer file_entry)1053 choose_file_call (GtkWidget *browse_button,
1054                   gpointer   file_entry)
1055 {
1056   GFile *file = g_file_new_for_path (gtk_entry_get_text (GTK_ENTRY (file_entry)));
1057 
1058   gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (file_choose),
1059                             g_file_get_uri (file));
1060 
1061   if (gtk_dialog_run (GTK_DIALOG (file_choose)) == GTK_RESPONSE_OK)
1062     {
1063       file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (file_choose));
1064       gtk_entry_set_text (GTK_ENTRY (file_entry), g_file_get_path (file));
1065     }
1066 
1067   file_name = g_file_get_path (file);
1068   gtk_widget_hide (file_choose);
1069 }
1070 
1071 /* A function to create the basic GtkTreeModel for the icon view */
1072 static GtkTreeModel*
create_model(void)1073 create_model (void)
1074 {
1075   GtkListStore *model;
1076   guint32       i;
1077 
1078   /* validate_image_list was called earlier, so all the images
1079    * up to multi_page.image_count are valid
1080    */
1081   model = gtk_list_store_new (4,
1082                               GDK_TYPE_PIXBUF, /* THUMB */
1083                               G_TYPE_STRING,   /* PAGE_NUMBER */
1084                               G_TYPE_STRING,   /* IMAGE_NAME */
1085                               G_TYPE_INT);     /* IMAGE_ID */
1086 
1087   for (i = 0 ; i < multi_page.image_count && i < MAX_PAGE_COUNT ; i++)
1088     {
1089       GtkTreeIter iter;
1090       gint32      image = multi_page.images[i];
1091       GdkPixbuf  *pixbuf;
1092 
1093       pixbuf = gimp_image_get_thumbnail (image, THUMB_WIDTH, THUMB_HEIGHT,
1094                                          GIMP_PIXBUF_SMALL_CHECKS);
1095 
1096       gtk_list_store_append (model, &iter);
1097       gtk_list_store_set (model, &iter,
1098                           THUMB,       pixbuf,
1099                           PAGE_NUMBER, g_strdup_printf (_("Page %d"), i + 1),
1100                           IMAGE_NAME,  gimp_image_get_name (image),
1101                           IMAGE_ID,    image,
1102                           -1);
1103 
1104       g_object_unref (pixbuf);
1105     }
1106 
1107   return GTK_TREE_MODEL (model);
1108 }
1109 
1110 /* A function that puts the images from the model inside the images
1111  * (pages) array
1112  */
1113 static gboolean
get_image_list(void)1114 get_image_list (void)
1115 {
1116   GtkTreeIter iter;
1117   gboolean    valid;
1118 
1119   multi_page.image_count = 0;
1120 
1121   for (valid = gtk_tree_model_get_iter_first (model, &iter);
1122        valid;
1123        valid = gtk_tree_model_iter_next (model, &iter))
1124     {
1125       gint32 image;
1126 
1127       gtk_tree_model_get (model, &iter,
1128                           IMAGE_ID, &image,
1129                           -1);
1130       multi_page.images[multi_page.image_count] = image;
1131       multi_page.image_count++;
1132     }
1133 
1134   if (multi_page.image_count == 0)
1135     {
1136       g_message (_("Error! In order to save the file, at least one image "
1137                    "should be added!"));
1138       return FALSE;
1139     }
1140 
1141   return TRUE;
1142 }
1143 
1144 /* A function that is called when the button for adding an image was
1145  * clicked
1146  */
1147 static void
add_image_call(GtkWidget * widget,gpointer img_combo)1148 add_image_call (GtkWidget *widget,
1149                 gpointer   img_combo)
1150 {
1151   GtkListStore *store;
1152   GtkTreeIter   iter;
1153   gint32        image;
1154   GdkPixbuf    *pixbuf;
1155 
1156   dnd_remove = FALSE;
1157 
1158   gimp_int_combo_box_get_active (img_combo, &image);
1159 
1160   store = GTK_LIST_STORE (model);
1161 
1162   pixbuf = gimp_image_get_thumbnail (image, THUMB_WIDTH, THUMB_HEIGHT,
1163                                      GIMP_PIXBUF_SMALL_CHECKS);
1164 
1165   gtk_list_store_append (store, &iter);
1166   gtk_list_store_set (store, &iter,
1167                       PAGE_NUMBER, g_strdup_printf (_("Page %d"),
1168                                                     multi_page.image_count+1),
1169                       THUMB,       pixbuf,
1170                       IMAGE_NAME,  gimp_image_get_name (image),
1171                       IMAGE_ID,    image,
1172                       -1);
1173 
1174   g_object_unref (pixbuf);
1175 
1176   multi_page.image_count++;
1177 
1178   dnd_remove = TRUE;
1179 }
1180 
1181 /* A function that is called when the button for deleting the selected
1182  * images was clicked
1183  */
1184 static void
del_image_call(GtkWidget * widget,gpointer icon_view)1185 del_image_call (GtkWidget *widget,
1186                 gpointer   icon_view)
1187 {
1188   GList                *list;
1189   GtkTreeRowReference **items;
1190   GtkTreePath          *item_path;
1191   GtkTreeIter           item;
1192   gpointer              temp;
1193   guint32               len;
1194 
1195   dnd_remove = FALSE;
1196 
1197   list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view));
1198 
1199   len = g_list_length (list);
1200   if (len > 0)
1201     {
1202       gint i;
1203 
1204       items = g_newa (GtkTreeRowReference*, len);
1205 
1206       for (i = 0; i < len; i++)
1207         {
1208           temp = g_list_nth_data (list, i);
1209           items[i] = gtk_tree_row_reference_new (model, temp);
1210           gtk_tree_path_free (temp);
1211         }
1212       g_list_free (list);
1213 
1214       for (i = 0; i < len; i++)
1215         {
1216           item_path = gtk_tree_row_reference_get_path (items[i]);
1217 
1218           gtk_tree_model_get_iter (model, &item, item_path);
1219           gtk_list_store_remove (GTK_LIST_STORE (model), &item);
1220 
1221           gtk_tree_path_free (item_path);
1222 
1223           gtk_tree_row_reference_free (items[i]);
1224           multi_page.image_count--;
1225         }
1226     }
1227 
1228   dnd_remove = TRUE;
1229 
1230   recount_pages ();
1231 }
1232 
1233 /* A function that is called on rows-deleted signal. It will call the
1234  * function to relabel the pages
1235  */
1236 static void
remove_call(GtkTreeModel * tree_model,GtkTreePath * path,gpointer user_data)1237 remove_call (GtkTreeModel *tree_model,
1238              GtkTreePath  *path,
1239              gpointer      user_data)
1240 {
1241 
1242   if (dnd_remove)
1243     /* The gtk documentation says that we should not free the indices array */
1244     recount_pages ();
1245 }
1246 
1247 /* A function to relabel the pages in the icon view, when their order
1248  * was changed
1249  */
1250 static void
recount_pages(void)1251 recount_pages (void)
1252 {
1253   GtkListStore *store;
1254   GtkTreeIter   iter;
1255   gboolean      valid;
1256   gint32        i = 0;
1257 
1258   store = GTK_LIST_STORE (model);
1259 
1260   for (valid = gtk_tree_model_get_iter_first (model, &iter);
1261        valid;
1262        valid = gtk_tree_model_iter_next (model, &iter))
1263     {
1264       gtk_list_store_set (store, &iter,
1265                           PAGE_NUMBER, g_strdup_printf (_("Page %d"), i + 1),
1266                           -1);
1267       i++;
1268     }
1269 }
1270 
1271 
1272 /******************************************************/
1273 /* Beginning of the actual PDF functions              */
1274 /******************************************************/
1275 
1276 static cairo_surface_t *
get_cairo_surface(gint32 drawable_ID,gboolean as_mask,GError ** error)1277 get_cairo_surface (gint32     drawable_ID,
1278                    gboolean   as_mask,
1279                    GError   **error)
1280 {
1281   GeglBuffer      *src_buffer;
1282   GeglBuffer      *dest_buffer;
1283   cairo_surface_t *surface;
1284   cairo_status_t   status;
1285   cairo_format_t   format;
1286   gint             width;
1287   gint             height;
1288 
1289   src_buffer = gimp_drawable_get_buffer (drawable_ID);
1290 
1291   width  = gegl_buffer_get_width  (src_buffer);
1292   height = gegl_buffer_get_height (src_buffer);
1293 
1294   if (as_mask)
1295     format = CAIRO_FORMAT_A8;
1296   else if (gimp_drawable_has_alpha (drawable_ID))
1297     format = CAIRO_FORMAT_ARGB32;
1298   else
1299     format = CAIRO_FORMAT_RGB24;
1300 
1301   surface = cairo_image_surface_create (format, width, height);
1302 
1303   status = cairo_surface_status (surface);
1304   if (status != CAIRO_STATUS_SUCCESS)
1305     {
1306       switch (status)
1307         {
1308         case CAIRO_STATUS_INVALID_SIZE:
1309           g_set_error_literal (error,
1310                                GIMP_PLUGIN_PDF_SAVE_ERROR,
1311                                GIMP_PLUGIN_PDF_SAVE_ERROR_FAILED,
1312                                _("Cannot handle the size (either width or height) of the image."));
1313           break;
1314         default:
1315           g_set_error (error,
1316                        GIMP_PLUGIN_PDF_SAVE_ERROR,
1317                        GIMP_PLUGIN_PDF_SAVE_ERROR_FAILED,
1318                        "Cairo error: %s",
1319                        cairo_status_to_string (status));
1320           break;
1321         }
1322 
1323       return NULL;
1324     }
1325 
1326   dest_buffer = gimp_cairo_surface_create_buffer (surface);
1327   if (as_mask)
1328     {
1329       /* src_buffer represents a mask in "Y u8", "Y u16", etc. formats.
1330        * Yet cairo_mask*() functions only care about the alpha channel of
1331        * the surface. Hence I change the format of dest_buffer so that the
1332        * Y channel of src_buffer actually refers to the A channel of
1333        * dest_buffer/surface in Cairo.
1334        */
1335       gegl_buffer_set_format (dest_buffer, babl_format ("Y u8"));
1336     }
1337 
1338   gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE, dest_buffer, NULL);
1339 
1340   cairo_surface_mark_dirty (surface);
1341 
1342   g_object_unref (src_buffer);
1343   g_object_unref (dest_buffer);
1344 
1345   return surface;
1346 }
1347 
1348 /* A function to check if a drawable is single colored This allows to
1349  * convert bitmaps to vector where possible
1350  */
1351 static GimpRGB
get_layer_color(gint32 layer_ID,gboolean * single)1352 get_layer_color (gint32    layer_ID,
1353                  gboolean *single)
1354 {
1355   GimpRGB col;
1356   gdouble red, green, blue, alpha;
1357   gdouble dev, devSum;
1358   gdouble median, pixels, count, precentile;
1359 
1360   devSum = 0;
1361   red = 0;
1362   green = 0;
1363   blue = 0;
1364   alpha = 0;
1365   dev = 0;
1366 
1367   if (gimp_drawable_is_indexed (layer_ID))
1368     {
1369       /* FIXME: We can't do a proper histogram on indexed layers! */
1370       *single = FALSE;
1371       col. r = col.g = col.b = col.a = 0;
1372       return col;
1373     }
1374 
1375   if (gimp_drawable_bpp (layer_ID) >= 3)
1376     {
1377       /* Are we in RGB mode? */
1378 
1379       gimp_drawable_histogram (layer_ID, GIMP_HISTOGRAM_RED, 0.0, 1.0,
1380                                &red, &dev, &median, &pixels, &count, &precentile);
1381       devSum += dev;
1382       gimp_drawable_histogram (layer_ID, GIMP_HISTOGRAM_GREEN, 0.0, 1.0,
1383                                &green, &dev, &median, &pixels, &count, &precentile);
1384       devSum += dev;
1385       gimp_drawable_histogram (layer_ID, GIMP_HISTOGRAM_BLUE, 0.0, 1.0,
1386                                &blue, &dev, &median, &pixels, &count, &precentile);
1387       devSum += dev;
1388     }
1389   else
1390     {
1391       /* We are in Grayscale mode (or Indexed) */
1392 
1393       gimp_drawable_histogram (layer_ID, GIMP_HISTOGRAM_VALUE, 0.0, 1.0,
1394                                &red, &dev, &median, &pixels, &count, &precentile);
1395       devSum += dev;
1396       green = red;
1397       blue = red;
1398     }
1399 
1400   if (gimp_drawable_has_alpha (layer_ID))
1401     gimp_drawable_histogram (layer_ID, GIMP_HISTOGRAM_ALPHA, 0.0, 1.0,
1402                              &alpha, &dev, &median, &pixels, &count, &precentile);
1403   else
1404     alpha = 255;
1405 
1406   devSum += dev;
1407   *single = devSum == 0;
1408 
1409   col.r = red/255;
1410   col.g = green/255;
1411   col.b = blue/255;
1412   col.a = alpha/255;
1413 
1414   return col;
1415 }
1416 
1417 /* A function that uses Pango to render the text to our cairo surface,
1418  * in the same way it was the user saw it inside gimp.
1419  * Needs some work on choosing the font name better, and on hinting
1420  * (freetype and pango differences)
1421  */
1422 static void
drawText(gint32 text_id,gdouble opacity,cairo_t * cr,gdouble x_res,gdouble y_res)1423 drawText (gint32    text_id,
1424           gdouble   opacity,
1425           cairo_t  *cr,
1426           gdouble   x_res,
1427           gdouble   y_res)
1428 {
1429   GimpImageType         type   = gimp_drawable_type (text_id);
1430   gchar                *text   = gimp_text_layer_get_text (text_id);
1431   gchar                *markup = gimp_text_layer_get_markup (text_id);
1432   gchar                *font_family;
1433   gchar                *language;
1434   cairo_font_options_t *options;
1435   gint                  x;
1436   gint                  y;
1437   GimpRGB               color;
1438   GimpUnit              unit;
1439   gdouble               size;
1440   GimpTextHintStyle     hinting;
1441   GimpTextJustification j;
1442   gboolean              justify;
1443   PangoAlignment        align;
1444   GimpTextDirection     dir;
1445   PangoLayout          *layout;
1446   PangoContext         *context;
1447   PangoFontDescription *font_description;
1448   gdouble               indent;
1449   gdouble               line_spacing;
1450   gdouble               letter_spacing;
1451   PangoAttribute       *letter_spacing_at;
1452   PangoAttrList        *attr_list = pango_attr_list_new ();
1453   PangoFontMap         *fontmap;
1454 
1455   cairo_save (cr);
1456 
1457   options = cairo_font_options_create ();
1458   attr_list = pango_attr_list_new ();
1459   cairo_get_font_options (cr, options);
1460 
1461   /* Position */
1462   gimp_drawable_offsets (text_id, &x, &y);
1463   cairo_translate (cr, x, y);
1464 
1465   /* Color */
1466   /* When dealing with a gray/indexed image, the viewed color of the text layer
1467    * can be different than the one kept in the memory */
1468   if (type == GIMP_RGBA_IMAGE)
1469     gimp_text_layer_get_color (text_id, &color);
1470   else
1471     gimp_image_pick_color (gimp_item_get_image (text_id),
1472                            text_id, x, y, FALSE, FALSE, 0, &color);
1473 
1474   cairo_set_source_rgba (cr, color.r, color.g, color.b, opacity);
1475 
1476   /* Hinting */
1477   hinting = gimp_text_layer_get_hint_style (text_id);
1478   switch (hinting)
1479     {
1480     case GIMP_TEXT_HINT_STYLE_NONE:
1481       cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
1482       break;
1483 
1484     case GIMP_TEXT_HINT_STYLE_SLIGHT:
1485       cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_SLIGHT);
1486       break;
1487 
1488     case GIMP_TEXT_HINT_STYLE_MEDIUM:
1489       cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_MEDIUM);
1490       break;
1491 
1492     case GIMP_TEXT_HINT_STYLE_FULL:
1493       cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_FULL);
1494       break;
1495     }
1496 
1497   /* Antialiasing */
1498   if (gimp_text_layer_get_antialias (text_id))
1499     cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_DEFAULT);
1500   else
1501     cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_NONE);
1502 
1503   /* We are done with cairo's settings. It's time to create the
1504    * context
1505    */
1506   fontmap = pango_cairo_font_map_new_for_font_type (CAIRO_FONT_TYPE_FT);
1507 
1508   pango_cairo_font_map_set_resolution (PANGO_CAIRO_FONT_MAP (fontmap), y_res);
1509 
1510   context = pango_font_map_create_context (fontmap);
1511   g_object_unref (fontmap);
1512 
1513   pango_cairo_context_set_font_options (context, options);
1514 
1515   /* Language */
1516   language = gimp_text_layer_get_language (text_id);
1517   if (language)
1518     pango_context_set_language (context,
1519                                 pango_language_from_string(language));
1520 
1521   /* Text Direction */
1522   dir = gimp_text_layer_get_base_direction (text_id);
1523 
1524   switch (dir)
1525     {
1526     case GIMP_TEXT_DIRECTION_LTR:
1527       pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
1528       pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_NATURAL);
1529       pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
1530       break;
1531 
1532     case GIMP_TEXT_DIRECTION_RTL:
1533       pango_context_set_base_dir (context, PANGO_DIRECTION_RTL);
1534       pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_NATURAL);
1535       pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
1536       break;
1537 
1538     case GIMP_TEXT_DIRECTION_TTB_RTL:
1539       pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
1540       pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_LINE);
1541       pango_context_set_base_gravity (context, PANGO_GRAVITY_EAST);
1542       break;
1543 
1544     case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
1545       pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
1546       pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_STRONG);
1547       pango_context_set_base_gravity (context, PANGO_GRAVITY_EAST);
1548       break;
1549 
1550     case GIMP_TEXT_DIRECTION_TTB_LTR:
1551       pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
1552       pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_LINE);
1553       pango_context_set_base_gravity (context, PANGO_GRAVITY_WEST);
1554       break;
1555 
1556     case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
1557       pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
1558       pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_STRONG);
1559       pango_context_set_base_gravity (context, PANGO_GRAVITY_WEST);
1560       break;
1561     }
1562 
1563   /* We are done with the context's settings. It's time to create the
1564    * layout
1565    */
1566   layout = pango_layout_new (context);
1567   pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
1568 
1569   /* Font */
1570   font_family = gimp_text_layer_get_font (text_id);
1571 
1572   /* We need to find a way to convert GIMP's returned font name to a
1573    * normal Pango name... Hopefully GIMP 2.8 with Pango will fix it.
1574    */
1575   font_description = pango_font_description_from_string (font_family);
1576 
1577   /* Font Size */
1578   size = gimp_text_layer_get_font_size (text_id, &unit);
1579   size = gimp_units_to_pixels (size, unit, y_res);
1580   pango_font_description_set_absolute_size (font_description, size * PANGO_SCALE);
1581 
1582   pango_layout_set_font_description (layout, font_description);
1583 
1584   /* Width */
1585   if (! PANGO_GRAVITY_IS_VERTICAL (pango_context_get_base_gravity (context)))
1586     pango_layout_set_width (layout, gimp_drawable_width (text_id) * PANGO_SCALE);
1587   else
1588     pango_layout_set_width (layout, gimp_drawable_height (text_id) * PANGO_SCALE);
1589 
1590   /* Justification, and Alignment */
1591   justify = FALSE;
1592   j = gimp_text_layer_get_justification (text_id);
1593   align = PANGO_ALIGN_LEFT;
1594   switch (j)
1595     {
1596     case GIMP_TEXT_JUSTIFY_LEFT:
1597       align = PANGO_ALIGN_LEFT;
1598       break;
1599     case GIMP_TEXT_JUSTIFY_RIGHT:
1600       align = PANGO_ALIGN_RIGHT;
1601       break;
1602     case GIMP_TEXT_JUSTIFY_CENTER:
1603       align = PANGO_ALIGN_CENTER;
1604       break;
1605     case GIMP_TEXT_JUSTIFY_FILL:
1606       align = PANGO_ALIGN_LEFT;
1607       justify = TRUE;
1608       break;
1609     }
1610   pango_layout_set_alignment (layout, align);
1611   pango_layout_set_justify (layout, justify);
1612 
1613   /* Indentation */
1614   indent = gimp_text_layer_get_indent (text_id);
1615   pango_layout_set_indent (layout, (int)(PANGO_SCALE * indent));
1616 
1617   /* Line Spacing */
1618   line_spacing = gimp_text_layer_get_line_spacing (text_id);
1619   pango_layout_set_spacing (layout, (int)(PANGO_SCALE * line_spacing));
1620 
1621   /* Letter Spacing */
1622   letter_spacing = gimp_text_layer_get_letter_spacing (text_id);
1623   letter_spacing_at = pango_attr_letter_spacing_new ((int)(PANGO_SCALE * letter_spacing));
1624   pango_attr_list_insert (attr_list, letter_spacing_at);
1625 
1626 
1627   pango_layout_set_attributes (layout, attr_list);
1628 
1629   /* Use the pango markup of the text layer */
1630 
1631   if (markup != NULL && markup[0] != '\0')
1632     pango_layout_set_markup (layout, markup, -1);
1633   else /* If we can't find a markup, then it has just text */
1634     pango_layout_set_text (layout, text, -1);
1635 
1636   if (dir == GIMP_TEXT_DIRECTION_TTB_RTL ||
1637       dir == GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT)
1638     {
1639       cairo_translate (cr, gimp_drawable_width (text_id), 0);
1640       cairo_rotate (cr, G_PI_2);
1641     }
1642 
1643   if (dir == GIMP_TEXT_DIRECTION_TTB_LTR ||
1644       dir == GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT)
1645     {
1646       cairo_translate (cr, 0, gimp_drawable_height (text_id));
1647       cairo_rotate (cr, -G_PI_2);
1648     }
1649 
1650   pango_cairo_show_layout (cr, layout);
1651 
1652   g_free (text);
1653   g_free (font_family);
1654   g_free (language);
1655 
1656   g_object_unref (layout);
1657   pango_font_description_free (font_description);
1658   g_object_unref (context);
1659   pango_attr_list_unref (attr_list);
1660 
1661   cairo_font_options_destroy (options);
1662 
1663   cairo_restore (cr);
1664 }
1665 
1666 static gboolean
draw_layer(gint32 * layers,gint n_layers,gint j,cairo_t * cr,gdouble x_res,gdouble y_res,const gchar * name,GError ** error)1667 draw_layer (gint32       *layers,
1668             gint          n_layers,
1669             gint          j,
1670             cairo_t      *cr,
1671             gdouble       x_res,
1672             gdouble       y_res,
1673             const gchar  *name,
1674             GError      **error)
1675 {
1676   gint32  layer_ID;
1677   gdouble opacity;
1678 
1679   if (optimize.reverse_order && optimize.layers_as_pages)
1680     layer_ID = layers [j];
1681   else
1682     layer_ID = layers [n_layers - j - 1];
1683 
1684   opacity = gimp_layer_get_opacity (layer_ID) / 100.0;
1685   if ((! gimp_item_get_visible (layer_ID) || opacity == 0.0) &&
1686       optimize.ignore_hidden)
1687     return TRUE;
1688 
1689   if (gimp_item_is_group (layer_ID))
1690     {
1691       gint *children;
1692       gint  children_num;
1693       gint  i;
1694 
1695       children = gimp_item_get_children (layer_ID, &children_num);
1696       for (i = 0; i < children_num; i++)
1697         {
1698           if (! draw_layer (children, children_num, i,
1699                             cr, x_res, y_res, name, error))
1700             {
1701               g_free (children);
1702               return FALSE;
1703             }
1704         }
1705       g_free (children);
1706     }
1707   else
1708     {
1709       cairo_surface_t *mask_image = NULL;
1710       gint32           mask_ID    = -1;
1711       gint             x, y;
1712 
1713       mask_ID = gimp_layer_get_mask (layer_ID);
1714       if (mask_ID != -1)
1715         {
1716           mask_image = get_cairo_surface (mask_ID, TRUE, error);
1717 
1718           if (*error)
1719             return FALSE;
1720         }
1721 
1722       gimp_drawable_offsets (layer_ID, &x, &y);
1723 
1724       if (! gimp_item_is_text_layer (layer_ID))
1725         {
1726           /* For raster layers */
1727           GimpRGB  layer_color;
1728           gboolean single_color = FALSE;
1729 
1730           layer_color = get_layer_color (layer_ID, &single_color);
1731 
1732           cairo_rectangle (cr, x, y,
1733                            gimp_drawable_width (layer_ID),
1734                            gimp_drawable_height (layer_ID));
1735 
1736           if (optimize.vectorize && single_color)
1737             {
1738               cairo_set_source_rgba (cr,
1739                                      layer_color.r,
1740                                      layer_color.g,
1741                                      layer_color.b,
1742                                      layer_color.a * opacity);
1743               if (mask_ID != -1)
1744                 cairo_mask_surface (cr, mask_image, x, y);
1745               else
1746                 cairo_fill (cr);
1747             }
1748           else
1749             {
1750               cairo_surface_t *layer_image;
1751 
1752               layer_image = get_cairo_surface (layer_ID, FALSE, error);
1753 
1754               if (*error)
1755                 return FALSE;
1756 
1757               cairo_clip (cr);
1758 
1759               cairo_set_source_surface (cr, layer_image, x, y);
1760               cairo_push_group (cr);
1761               cairo_paint_with_alpha (cr, opacity);
1762               cairo_pop_group_to_source (cr);
1763 
1764               if (mask_ID != -1)
1765                 cairo_mask_surface (cr, mask_image, x, y);
1766               else
1767                 cairo_paint (cr);
1768 
1769               cairo_reset_clip (cr);
1770 
1771               cairo_surface_destroy (layer_image);
1772             }
1773         }
1774       else
1775         {
1776           /* For text layers */
1777           drawText (layer_ID, opacity, cr, x_res, y_res);
1778         }
1779 
1780       /* draw new page if "layers as pages" option is checked */
1781       if (optimize.layers_as_pages &&
1782           g_strcmp0 (name, SAVE2_PROC) == 0)
1783         cairo_show_page (cr);
1784 
1785       /* We are done with the layer - time to free some resources */
1786       if (mask_ID != -1)
1787         cairo_surface_destroy (mask_image);
1788     }
1789 
1790   return TRUE;
1791 }
1792