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