1 /* LIBGIMP - The GIMP Library
2  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3  *
4  * gimpexport.c
5  * Copyright (C) 1999-2004 Sven Neumann <sven@gimp.org>
6  *
7  * This library is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 3 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library.  If not, see
19  * <https://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include <string.h>
25 
26 #include <gtk/gtk.h>
27 
28 #include "gimp.h"
29 #include "gimpui.h"
30 
31 #include "libgimp-intl.h"
32 
33 
34 /**
35  * SECTION: gimpexport
36  * @title: gimpexport
37  * @short_description: Export an image before it is saved.
38  *
39  * This function should be called by all save_plugins unless they are
40  * able to save all image formats GIMP knows about. It takes care of
41  * asking the user if she wishes to export the image to a format the
42  * save_plugin can handle. It then performs the necessary conversions
43  * (e.g. Flatten) on a copy of the image so that the image can be
44  * saved without changing the original image.
45  *
46  * The capabilities of the save_plugin are specified by combining
47  * #GimpExportCapabilities using a bitwise OR.
48  *
49  * Make sure you have initialized GTK+ before you call this function
50  * as it will most probably have to open a dialog.
51  **/
52 
53 
54 typedef void (* ExportFunc) (gint32  imageID,
55                              gint32 *drawable_ID);
56 
57 
58 /* the export action structure */
59 typedef struct
60 {
61   ExportFunc   default_action;
62   ExportFunc   alt_action;
63   const gchar *reason;
64   const gchar *possibilities[2];
65   gint         choice;
66 } ExportAction;
67 
68 
69 /* the functions that do the actual export */
70 
71 static void
export_merge(gint32 image_ID,gint32 * drawable_ID)72 export_merge (gint32  image_ID,
73               gint32 *drawable_ID)
74 {
75   gint32  nlayers;
76   gint32  nvisible = 0;
77   gint32  i;
78   gint32 *layers;
79   gint32  merged;
80   gint32  transp;
81 
82   layers = gimp_image_get_layers (image_ID, &nlayers);
83   for (i = 0; i < nlayers; i++)
84     {
85       if (gimp_item_get_visible (layers[i]))
86         nvisible++;
87     }
88 
89   if (nvisible <= 1)
90     {
91       /* if there is only one (or zero) visible layer, add a new
92        * transparent layer that has the same size as the canvas.  The
93        * merge that follows will ensure that the offset, opacity and
94        * size are correct
95        */
96       transp = gimp_layer_new (image_ID, "-",
97                                gimp_image_width (image_ID),
98                                gimp_image_height (image_ID),
99                                gimp_drawable_type (*drawable_ID) | 1,
100                                100.0, GIMP_LAYER_MODE_NORMAL);
101       gimp_image_insert_layer (image_ID, transp, -1, 1);
102       gimp_selection_none (image_ID);
103       gimp_drawable_edit_clear (transp);
104       nvisible++;
105     }
106 
107   if (nvisible > 1)
108     {
109       g_free (layers);
110       merged = gimp_image_merge_visible_layers (image_ID, GIMP_CLIP_TO_IMAGE);
111 
112       if (merged != -1)
113         *drawable_ID = merged;
114       else
115         return;  /* shouldn't happen */
116 
117       layers = gimp_image_get_layers (image_ID, &nlayers);
118 
119       /*  make sure that the merged drawable matches the image size  */
120       if (gimp_drawable_width  (merged) != gimp_image_width  (image_ID) ||
121           gimp_drawable_height (merged) != gimp_image_height (image_ID))
122         {
123           gint off_x, off_y;
124 
125           gimp_drawable_offsets (merged, &off_x, &off_y);
126           gimp_layer_resize (merged,
127                              gimp_image_width (image_ID),
128                              gimp_image_height (image_ID),
129                              off_x, off_y);
130         }
131     }
132 
133   /* remove any remaining (invisible) layers */
134   for (i = 0; i < nlayers; i++)
135     {
136       if (layers[i] != *drawable_ID)
137         gimp_image_remove_layer (image_ID, layers[i]);
138     }
139   g_free (layers);
140 }
141 
142 static void
export_flatten(gint32 image_ID,gint32 * drawable_ID)143 export_flatten (gint32  image_ID,
144                 gint32 *drawable_ID)
145 {
146   gint32 flattened;
147 
148   flattened = gimp_image_flatten (image_ID);
149 
150   if (flattened != -1)
151     *drawable_ID = flattened;
152 }
153 
154 static void
export_remove_alpha(gint32 image_ID,gint32 * drawable_ID)155 export_remove_alpha (gint32  image_ID,
156                      gint32 *drawable_ID)
157 {
158   gint32  n_layers;
159   gint32 *layers;
160   gint    i;
161 
162   layers = gimp_image_get_layers (image_ID, &n_layers);
163 
164   for (i = 0; i < n_layers; i++)
165     {
166       if (gimp_drawable_has_alpha (layers[i]))
167         gimp_layer_flatten (layers[i]);
168     }
169 
170   g_free (layers);
171 }
172 
173 static void
export_apply_masks(gint32 image_ID,gint * drawable_ID)174 export_apply_masks (gint32  image_ID,
175                     gint   *drawable_ID)
176 {
177   gint32  n_layers;
178   gint32 *layers;
179   gint    i;
180 
181   layers = gimp_image_get_layers (image_ID, &n_layers);
182 
183   for (i = 0; i < n_layers; i++)
184     {
185       if (gimp_layer_get_mask (layers[i]) != -1)
186         {
187           /* we can't apply the mask directly to a layer group, so merge it
188            * first
189            */
190           if (gimp_item_is_group (layers[i]))
191             layers[i] = gimp_image_merge_layer_group (image_ID, layers[i]);
192 
193           gimp_layer_remove_mask (layers[i], GIMP_MASK_APPLY);
194         }
195     }
196 
197   g_free (layers);
198 }
199 
200 static void
export_convert_rgb(gint32 image_ID,gint32 * drawable_ID)201 export_convert_rgb (gint32  image_ID,
202                     gint32 *drawable_ID)
203 {
204   gimp_image_convert_rgb (image_ID);
205 }
206 
207 static void
export_convert_grayscale(gint32 image_ID,gint32 * drawable_ID)208 export_convert_grayscale (gint32  image_ID,
209                           gint32 *drawable_ID)
210 {
211   gimp_image_convert_grayscale (image_ID);
212 }
213 
214 static void
export_convert_indexed(gint32 image_ID,gint32 * drawable_ID)215 export_convert_indexed (gint32  image_ID,
216                         gint32 *drawable_ID)
217 {
218   gint32 nlayers;
219 
220   /* check alpha */
221   g_free (gimp_image_get_layers (image_ID, &nlayers));
222   if (nlayers > 1 || gimp_drawable_has_alpha (*drawable_ID))
223     gimp_image_convert_indexed (image_ID,
224                                 GIMP_CONVERT_DITHER_NONE,
225                                 GIMP_CONVERT_PALETTE_GENERATE,
226                                 255, FALSE, FALSE, "");
227   else
228     gimp_image_convert_indexed (image_ID,
229                                 GIMP_CONVERT_DITHER_NONE,
230                                 GIMP_CONVERT_PALETTE_GENERATE,
231                                 256, FALSE, FALSE, "");
232 }
233 
234 static void
export_convert_bitmap(gint32 image_ID,gint32 * drawable_ID)235 export_convert_bitmap (gint32  image_ID,
236                        gint32 *drawable_ID)
237 {
238   if (gimp_image_base_type (image_ID) == GIMP_INDEXED)
239     gimp_image_convert_rgb (image_ID);
240 
241   gimp_image_convert_indexed (image_ID,
242                               GIMP_CONVERT_DITHER_FS,
243                               GIMP_CONVERT_PALETTE_GENERATE,
244                               2, FALSE, FALSE, "");
245 }
246 
247 static void
export_add_alpha(gint32 image_ID,gint32 * drawable_ID)248 export_add_alpha (gint32  image_ID,
249                   gint32 *drawable_ID)
250 {
251   gint32  nlayers;
252   gint32  i;
253   gint32 *layers;
254 
255   layers = gimp_image_get_layers (image_ID, &nlayers);
256   for (i = 0; i < nlayers; i++)
257     {
258       if (!gimp_drawable_has_alpha (layers[i]))
259         gimp_layer_add_alpha (layers[i]);
260     }
261   g_free (layers);
262 }
263 
264 static void
export_crop_image(gint32 image_ID,gint32 * drawable_ID)265 export_crop_image (gint32  image_ID,
266                    gint32 *drawable_ID)
267 {
268   gimp_image_crop (image_ID,
269                    gimp_image_width  (image_ID),
270                    gimp_image_height (image_ID),
271                    0, 0);
272 }
273 
274 static void
export_resize_image(gint32 image_ID,gint32 * drawable_ID)275 export_resize_image (gint32  image_ID,
276                      gint32 *drawable_ID)
277 {
278   gimp_image_resize_to_layers (image_ID);
279 }
280 
281 static void
export_void(gint32 image_ID,gint32 * drawable_ID)282 export_void (gint32  image_ID,
283              gint32 *drawable_ID)
284 {
285   /* do nothing */
286 }
287 
288 
289 /* a set of predefined actions */
290 
291 static ExportAction export_action_merge =
292 {
293   export_merge,
294   NULL,
295   N_("%s plug-in can't handle layers"),
296   { N_("Merge Visible Layers"), NULL },
297   0
298 };
299 
300 static ExportAction export_action_merge_single =
301 {
302   export_merge,
303   NULL,
304   N_("%s plug-in can't handle layer offsets, size or opacity"),
305   { N_("Merge Visible Layers"), NULL },
306   0
307 };
308 
309 static ExportAction export_action_animate_or_merge =
310 {
311   NULL,
312   export_merge,
313   N_("%s plug-in can only handle layers as animation frames"),
314   { N_("Save as Animation"), N_("Merge Visible Layers") },
315   0
316 };
317 
318 static ExportAction export_action_animate_or_flatten =
319 {
320   NULL,
321   export_flatten,
322   N_("%s plug-in can only handle layers as animation frames"),
323   { N_("Save as Animation"), N_("Flatten Image") },
324   0
325 };
326 
327 static ExportAction export_action_merge_or_flatten =
328 {
329   export_flatten,
330   export_merge,
331   N_("%s plug-in can't handle layers"),
332   { N_("Flatten Image"), N_("Merge Visible Layers") },
333   1
334 };
335 
336 static ExportAction export_action_flatten =
337 {
338   export_flatten,
339   NULL,
340   N_("%s plug-in can't handle transparency"),
341   { N_("Flatten Image"), NULL },
342   0
343 };
344 
345 static ExportAction export_action_remove_alpha =
346 {
347   export_remove_alpha,
348   NULL,
349   N_("%s plug-in can't handle transparent layers"),
350   { N_("Flatten Image"), NULL },
351   0
352 };
353 
354 static ExportAction export_action_apply_masks =
355 {
356   export_apply_masks,
357   NULL,
358   N_("%s plug-in can't handle layer masks"),
359   { N_("Apply Layer Masks"), NULL },
360   0
361 };
362 
363 static ExportAction export_action_convert_rgb =
364 {
365   export_convert_rgb,
366   NULL,
367   N_("%s plug-in can only handle RGB images"),
368   { N_("Convert to RGB"), NULL },
369   0
370 };
371 
372 static ExportAction export_action_convert_grayscale =
373 {
374   export_convert_grayscale,
375   NULL,
376   N_("%s plug-in can only handle grayscale images"),
377   { N_("Convert to Grayscale"), NULL },
378   0
379 };
380 
381 static ExportAction export_action_convert_indexed =
382 {
383   export_convert_indexed,
384   NULL,
385   N_("%s plug-in can only handle indexed images"),
386   { N_("Convert to Indexed using default settings\n"
387        "(Do it manually to tune the result)"), NULL },
388   0
389 };
390 
391 static ExportAction export_action_convert_bitmap =
392 {
393   export_convert_bitmap,
394   NULL,
395   N_("%s plug-in can only handle bitmap (two color) indexed images"),
396   { N_("Convert to Indexed using bitmap default settings\n"
397        "(Do it manually to tune the result)"), NULL },
398   0
399 };
400 
401 static ExportAction export_action_convert_rgb_or_grayscale =
402 {
403   export_convert_rgb,
404   export_convert_grayscale,
405   N_("%s plug-in can only handle RGB or grayscale images"),
406   { N_("Convert to RGB"), N_("Convert to Grayscale")},
407   0
408 };
409 
410 static ExportAction export_action_convert_rgb_or_indexed =
411 {
412   export_convert_rgb,
413   export_convert_indexed,
414   N_("%s plug-in can only handle RGB or indexed images"),
415   { N_("Convert to RGB"), N_("Convert to Indexed using default settings\n"
416                              "(Do it manually to tune the result)")},
417   0
418 };
419 
420 static ExportAction export_action_convert_indexed_or_grayscale =
421 {
422   export_convert_indexed,
423   export_convert_grayscale,
424   N_("%s plug-in can only handle grayscale or indexed images"),
425   { N_("Convert to Indexed using default settings\n"
426        "(Do it manually to tune the result)"),
427     N_("Convert to Grayscale") },
428   0
429 };
430 
431 static ExportAction export_action_add_alpha =
432 {
433   export_add_alpha,
434   NULL,
435   N_("%s plug-in needs an alpha channel"),
436   { N_("Add Alpha Channel"), NULL},
437   0
438 };
439 
440 static ExportAction export_action_crop_or_resize =
441 {
442   export_crop_image,
443   export_resize_image,
444   N_("%s plug-in needs to crop the layers to the image bounds"),
445   { N_("Crop Layers"), N_("Resize Image to Layers")},
446   0
447 };
448 
449 
450 static ExportFunc
export_action_get_func(const ExportAction * action)451 export_action_get_func (const ExportAction *action)
452 {
453   if (action->choice == 0 && action->default_action)
454     {
455       return action->default_action;
456     }
457 
458   if (action->choice == 1 && action->alt_action)
459     {
460       return action->alt_action;
461     }
462 
463   return export_void;
464 }
465 
466 static void
export_action_perform(const ExportAction * action,gint32 image_ID,gint32 * drawable_ID)467 export_action_perform (const ExportAction *action,
468                        gint32              image_ID,
469                        gint32             *drawable_ID)
470 {
471   export_action_get_func (action) (image_ID, drawable_ID);
472 }
473 
474 
475 /* dialog functions */
476 
477 static void
export_toggle_callback(GtkWidget * widget,gpointer data)478 export_toggle_callback (GtkWidget *widget,
479                         gpointer   data)
480 {
481   gint *choice = (gint *) data;
482 
483   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
484     *choice = FALSE;
485   else
486     *choice = TRUE;
487 }
488 
489 static GimpExportReturn
confirm_save_dialog(const gchar * message,const gchar * format_name)490 confirm_save_dialog (const gchar *message,
491                      const gchar *format_name)
492 {
493   GtkWidget        *dialog;
494   GtkWidget        *hbox;
495   GtkWidget        *image;
496   GtkWidget        *main_vbox;
497   GtkWidget        *label;
498   gchar            *text;
499   GimpExportReturn  retval;
500 
501   g_return_val_if_fail (message != NULL, GIMP_EXPORT_CANCEL);
502   g_return_val_if_fail (format_name != NULL, GIMP_EXPORT_CANCEL);
503 
504   dialog = gimp_dialog_new (_("Confirm Save"), "gimp-export-image-confirm",
505                             NULL, 0,
506                             gimp_standard_help_func,
507                             "gimp-export-confirm-dialog",
508 
509                             _("_Cancel"),  GTK_RESPONSE_CANCEL,
510                             _("C_onfirm"), GTK_RESPONSE_OK,
511 
512                             NULL);
513 
514   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
515                                            GTK_RESPONSE_OK,
516                                            GTK_RESPONSE_CANCEL,
517                                            -1);
518 
519   gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
520   gimp_window_set_transient (GTK_WINDOW (dialog));
521 
522   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
523   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
524                       hbox, TRUE, TRUE, 0);
525   gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
526   gtk_widget_show (hbox);
527 
528   image = gtk_image_new_from_icon_name ("dialog-warning",
529                                         GTK_ICON_SIZE_DIALOG);
530   gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
531   gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
532   gtk_widget_show (image);
533 
534   main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
535   gtk_box_pack_start (GTK_BOX (hbox), main_vbox, FALSE, FALSE, 0);
536   gtk_widget_show (main_vbox);
537 
538   text = g_strdup_printf (message, format_name);
539   label = gtk_label_new (text);
540   g_free (text);
541 
542   gimp_label_set_attributes (GTK_LABEL (label),
543                              PANGO_ATTR_SCALE,  PANGO_SCALE_LARGE,
544                              PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
545                              -1);
546   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
547   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
548   gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
549   gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
550   gtk_widget_show (label);
551 
552   gtk_widget_show (dialog);
553 
554   switch (gimp_dialog_run (GIMP_DIALOG (dialog)))
555     {
556     case GTK_RESPONSE_OK:
557       retval = GIMP_EXPORT_EXPORT;
558       break;
559 
560     default:
561       retval = GIMP_EXPORT_CANCEL;
562       break;
563     }
564 
565   gtk_widget_destroy (dialog);
566 
567   return retval;
568 }
569 
570 static GimpExportReturn
export_dialog(GSList * actions,const gchar * format_name)571 export_dialog (GSList      *actions,
572                const gchar *format_name)
573 {
574   GtkWidget        *dialog;
575   GtkWidget        *hbox;
576   GtkWidget        *image;
577   GtkWidget        *main_vbox;
578   GtkWidget        *label;
579   GSList           *list;
580   gchar            *text;
581   GimpExportReturn  retval;
582 
583   g_return_val_if_fail (actions != NULL, GIMP_EXPORT_CANCEL);
584   g_return_val_if_fail (format_name != NULL, GIMP_EXPORT_CANCEL);
585 
586   dialog = gimp_dialog_new (_("Export File"), "gimp-export-image",
587                             NULL, 0,
588                             gimp_standard_help_func, "gimp-export-dialog",
589 
590                             _("_Ignore"), GTK_RESPONSE_NO,
591                             _("_Cancel"), GTK_RESPONSE_CANCEL,
592                             _("_Export"), GTK_RESPONSE_OK,
593 
594                             NULL);
595 
596   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
597                                            GTK_RESPONSE_NO,
598                                            GTK_RESPONSE_OK,
599                                            GTK_RESPONSE_CANCEL,
600                                            -1);
601 
602   gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
603   gimp_window_set_transient (GTK_WINDOW (dialog));
604 
605   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
606   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
607                       hbox, TRUE, TRUE, 0);
608   gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
609   gtk_widget_show (hbox);
610 
611   image = gtk_image_new_from_icon_name ("dialog-information",
612                                         GTK_ICON_SIZE_DIALOG);
613   gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
614   gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
615   gtk_widget_show (image);
616 
617   main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
618   gtk_box_pack_start (GTK_BOX (hbox), main_vbox, FALSE, FALSE, 0);
619   gtk_widget_show (main_vbox);
620 
621   /* the headline */
622   text = g_strdup_printf (_("Your image should be exported before it "
623                             "can be saved as %s for the following reasons:"),
624                           format_name);
625   label = gtk_label_new (text);
626   g_free (text);
627 
628   gimp_label_set_attributes (GTK_LABEL (label),
629                              PANGO_ATTR_SCALE,  PANGO_SCALE_LARGE,
630                              -1);
631   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
632   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
633   gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
634   gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
635   gtk_widget_show (label);
636 
637   for (list = actions; list; list = g_slist_next (list))
638     {
639       ExportAction *action = list->data;
640       GtkWidget    *frame;
641       GtkWidget    *vbox;
642 
643       text = g_strdup_printf (gettext (action->reason), format_name);
644       frame = gimp_frame_new (text);
645       g_free (text);
646 
647       gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
648       gtk_widget_show (frame);
649 
650       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
651       gtk_container_add (GTK_CONTAINER (frame), vbox);
652 
653       if (action->possibilities[0] && action->possibilities[1])
654         {
655           GtkWidget *button;
656           GSList    *radio_group = NULL;
657 
658           button = gtk_radio_button_new_with_label (radio_group,
659                                                     gettext (action->possibilities[0]));
660           gtk_label_set_justify (GTK_LABEL (gtk_bin_get_child (GTK_BIN (button))),
661                                  GTK_JUSTIFY_LEFT);
662           radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
663           gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
664           g_signal_connect (button, "toggled",
665                             G_CALLBACK (export_toggle_callback),
666                             &action->choice);
667           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
668                                         (action->choice == 0));
669           gtk_widget_show (button);
670 
671           button = gtk_radio_button_new_with_label (radio_group,
672                                                     gettext (action->possibilities[1]));
673           gtk_label_set_justify (GTK_LABEL (gtk_bin_get_child (GTK_BIN (button))),
674                                  GTK_JUSTIFY_LEFT);
675           radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
676           gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
677           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
678                                         (action->choice == 1));
679           gtk_widget_show (button);
680         }
681       else if (action->possibilities[0])
682         {
683           label = gtk_label_new (gettext (action->possibilities[0]));
684           gtk_label_set_xalign (GTK_LABEL (label), 0.0);
685           gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
686           gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
687           gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
688           gtk_widget_show (label);
689           action->choice = 0;
690         }
691 
692       gtk_widget_show (vbox);
693     }
694 
695   /* the footline */
696   label = gtk_label_new (_("The export conversion won't modify your "
697                            "original image."));
698   gimp_label_set_attributes (GTK_LABEL (label),
699                              PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
700                              -1);
701   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
702   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
703   gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
704   gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
705   gtk_widget_show (label);
706 
707   gtk_widget_show (dialog);
708 
709   switch (gimp_dialog_run (GIMP_DIALOG (dialog)))
710     {
711     case GTK_RESPONSE_OK:
712       retval = GIMP_EXPORT_EXPORT;
713       break;
714 
715     case GTK_RESPONSE_NO:
716       retval = GIMP_EXPORT_IGNORE;
717       break;
718 
719     default:
720       retval = GIMP_EXPORT_CANCEL;
721       break;
722     }
723 
724   gtk_widget_destroy (dialog);
725 
726   return retval;
727 }
728 
729 /**
730  * gimp_export_image:
731  * @image_ID: Pointer to the image_ID.
732  * @drawable_ID: Pointer to the drawable_ID.
733  * @format_name: The (short) name of the image_format (e.g. JPEG or GIF).
734  * @capabilities: What can the image_format do?
735  *
736  * Takes an image and a drawable to be saved together with a
737  * description of the capabilities of the image_format. If the
738  * type of image doesn't match the capabilities of the format
739  * a dialog is opened that informs the user that the image has
740  * to be exported and offers to do the necessary conversions.
741  *
742  * If the user chooses to export the image, a copy is created.
743  * This copy is then converted, the image_ID and drawable_ID
744  * are changed to point to the new image and the procedure returns
745  * GIMP_EXPORT_EXPORT. The save_plugin has to take care of deleting the
746  * created image using gimp_image_delete() when it has saved it.
747  *
748  * If the user chooses to Ignore the export problem, the image_ID
749  * and drawable_ID is not altered, GIMP_EXPORT_IGNORE is returned and
750  * the save_plugin should try to save the original image. If the
751  * user chooses Cancel, GIMP_EXPORT_CANCEL is returned and the
752  * save_plugin should quit itself with status %GIMP_PDB_CANCEL.
753  *
754  * If @format_name is NULL, no dialogs will be shown and this function
755  * will behave as if the user clicked on the 'Export' button, if a
756  * dialog would have been shown.
757  *
758  * Returns: An enum of #GimpExportReturn describing the user_action.
759  **/
760 GimpExportReturn
gimp_export_image(gint32 * image_ID,gint32 * drawable_ID,const gchar * format_name,GimpExportCapabilities capabilities)761 gimp_export_image (gint32                 *image_ID,
762                    gint32                 *drawable_ID,
763                    const gchar            *format_name,
764                    GimpExportCapabilities  capabilities)
765 {
766   GSList            *actions = NULL;
767   GimpImageBaseType  type;
768   gint32             i;
769   gint32             n_layers;
770   gint32            *layers;
771   gboolean           interactive          = FALSE;
772   gboolean           added_flatten        = FALSE;
773   gboolean           has_layer_masks      = FALSE;
774   gboolean           background_has_alpha = TRUE;
775   GimpExportReturn   retval               = GIMP_EXPORT_CANCEL;
776 
777   g_return_val_if_fail (*image_ID > -1 && *drawable_ID > -1, FALSE);
778 
779   /* do some sanity checks */
780   if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
781     capabilities |= GIMP_EXPORT_CAN_HANDLE_ALPHA;
782 
783   if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION)
784     capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
785 
786   if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS)
787     capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
788 
789   if (format_name && g_getenv ("GIMP_INTERACTIVE_EXPORT"))
790     interactive = TRUE;
791 
792   /* ask for confirmation if the user is not saving a layer (see bug #51114) */
793   if (interactive &&
794       ! gimp_item_is_layer (*drawable_ID) &&
795       ! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
796     {
797       if (gimp_item_is_layer_mask (*drawable_ID))
798         {
799           retval = confirm_save_dialog
800             (_("You are about to save a layer mask as %s.\n"
801                "This will not save the visible layers."), format_name);
802         }
803       else if (gimp_item_is_channel (*drawable_ID))
804         {
805           retval = confirm_save_dialog
806             (_("You are about to save a channel (saved selection) as %s.\n"
807                "This will not save the visible layers."), format_name);
808         }
809       else
810         {
811           /* this should not happen */
812           g_warning ("%s: unknown drawable type!", G_STRFUNC);
813         }
814 
815       /* cancel - the user can then select an appropriate layer to save */
816       if (retval == GIMP_EXPORT_CANCEL)
817         return GIMP_EXPORT_CANCEL;
818     }
819 
820 
821   /* check alpha and layer masks */
822   layers = gimp_image_get_layers (*image_ID, &n_layers);
823 
824   for (i = 0; i < n_layers; i++)
825     {
826       if (gimp_drawable_has_alpha (layers[i]))
827         {
828           if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_ALPHA))
829             {
830               if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
831                 {
832                   actions = g_slist_prepend (actions, &export_action_flatten);
833                   added_flatten = TRUE;
834                   break;
835                 }
836               else
837                 {
838                   actions = g_slist_prepend (actions, &export_action_remove_alpha);
839                   break;
840                 }
841             }
842         }
843       else
844         {
845           /*  If this is the last layer, it's visible and has no alpha
846            *  channel, then the image has a "flat" background
847            */
848           if (i == n_layers - 1 && gimp_item_get_visible (layers[i]))
849             background_has_alpha = FALSE;
850 
851           if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
852             {
853               actions = g_slist_prepend (actions, &export_action_add_alpha);
854               break;
855             }
856         }
857     }
858 
859   if (! added_flatten)
860     {
861       for (i = 0; i < n_layers; i++)
862         {
863           if (gimp_layer_get_mask (layers[i]) != -1)
864             has_layer_masks = TRUE;
865         }
866     }
867 
868   if (! added_flatten)
869     {
870       gint32  n_children;
871       gint32 *children;
872 
873       children = gimp_item_get_children (layers[0], &n_children);
874 
875       if ((capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS) &&
876           (capabilities & GIMP_EXPORT_NEEDS_CROP))
877         {
878           GeglRectangle image_bounds;
879           gboolean      needs_crop = FALSE;
880 
881           image_bounds.x      = 0;
882           image_bounds.y      = 0;
883           image_bounds.width  = gimp_image_width  (*image_ID);
884           image_bounds.height = gimp_image_height (*image_ID);
885 
886           for (i = 0; i < n_layers; i++)
887             {
888               GeglRectangle layer_bounds;
889 
890               gimp_drawable_offsets (layers[i],
891                                      &layer_bounds.x, &layer_bounds.y);
892 
893               layer_bounds.width  = gimp_drawable_width  (layers[i]);
894               layer_bounds.height = gimp_drawable_height (layers[i]);
895 
896               if (! gegl_rectangle_contains (&image_bounds, &layer_bounds))
897                 {
898                   needs_crop = TRUE;
899 
900                   break;
901                 }
902             }
903 
904           if (needs_crop)
905             {
906               actions = g_slist_prepend (actions,
907                                          &export_action_crop_or_resize);
908             }
909         }
910 
911       /* check if layer size != canvas size, opacity != 100%, or offsets != 0 */
912       if (n_layers == 1                     &&
913           ! children                        &&
914           gimp_item_is_layer (*drawable_ID) &&
915           ! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
916         {
917           gint offset_x;
918           gint offset_y;
919 
920           gimp_drawable_offsets (*drawable_ID, &offset_x, &offset_y);
921 
922           if ((gimp_layer_get_opacity (*drawable_ID) < 100.0) ||
923               (gimp_image_width (*image_ID) !=
924                gimp_drawable_width (*drawable_ID))            ||
925               (gimp_image_height (*image_ID) !=
926                gimp_drawable_height (*drawable_ID))           ||
927               offset_x || offset_y)
928             {
929               if (capabilities & GIMP_EXPORT_CAN_HANDLE_ALPHA)
930                 {
931                   actions = g_slist_prepend (actions,
932                                              &export_action_merge_single);
933                 }
934               else
935                 {
936                   actions = g_slist_prepend (actions,
937                                              &export_action_flatten);
938                 }
939             }
940         }
941       /* check multiple layers */
942       else if (n_layers > 1)
943         {
944           if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION)
945             {
946               if (background_has_alpha ||
947                   capabilities & GIMP_EXPORT_NEEDS_ALPHA)
948                 actions = g_slist_prepend (actions,
949                                            &export_action_animate_or_merge);
950               else
951                 actions = g_slist_prepend (actions,
952                                            &export_action_animate_or_flatten);
953             }
954           else if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
955             {
956               if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
957                 actions = g_slist_prepend (actions,
958                                            &export_action_merge);
959               else
960                 actions = g_slist_prepend (actions,
961                                            &export_action_merge_or_flatten);
962             }
963         }
964       /* check for a single toplevel layer group */
965       else if (children)
966         {
967           if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
968             {
969               if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
970                 actions = g_slist_prepend (actions,
971                                            &export_action_merge);
972               else
973                 actions = g_slist_prepend (actions,
974                                            &export_action_merge_or_flatten);
975             }
976         }
977 
978       g_free (children);
979 
980       /* check layer masks */
981       if (has_layer_masks &&
982           ! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS))
983         actions = g_slist_prepend (actions, &export_action_apply_masks);
984     }
985 
986   g_free (layers);
987 
988   /* check the image type */
989   type = gimp_image_base_type (*image_ID);
990   switch (type)
991     {
992     case GIMP_RGB:
993       if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB))
994         {
995           if ((capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED) &&
996               (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
997             actions = g_slist_prepend (actions,
998                                        &export_action_convert_indexed_or_grayscale);
999           else if (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED)
1000             actions = g_slist_prepend (actions,
1001                                        &export_action_convert_indexed);
1002           else if (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY)
1003             actions = g_slist_prepend (actions,
1004                                        &export_action_convert_grayscale);
1005           else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
1006             actions = g_slist_prepend (actions,
1007                                        &export_action_convert_bitmap);
1008         }
1009       break;
1010 
1011     case GIMP_GRAY:
1012       if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
1013         {
1014           if ((capabilities & GIMP_EXPORT_CAN_HANDLE_RGB) &&
1015               (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED))
1016             actions = g_slist_prepend (actions,
1017                                        &export_action_convert_rgb_or_indexed);
1018           else if (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB)
1019             actions = g_slist_prepend (actions,
1020                                        &export_action_convert_rgb);
1021           else if (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED)
1022             actions = g_slist_prepend (actions,
1023                                        &export_action_convert_indexed);
1024           else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
1025             actions = g_slist_prepend (actions,
1026                                        &export_action_convert_bitmap);
1027         }
1028       break;
1029 
1030     case GIMP_INDEXED:
1031       if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED))
1032         {
1033           if ((capabilities & GIMP_EXPORT_CAN_HANDLE_RGB) &&
1034               (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
1035             actions = g_slist_prepend (actions,
1036                                        &export_action_convert_rgb_or_grayscale);
1037           else if (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB)
1038             actions = g_slist_prepend (actions,
1039                                        &export_action_convert_rgb);
1040           else if (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY)
1041             actions = g_slist_prepend (actions,
1042                                        &export_action_convert_grayscale);
1043           else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
1044             {
1045               gint n_colors;
1046 
1047               g_free (gimp_image_get_colormap (*image_ID, &n_colors));
1048 
1049               if (n_colors > 2)
1050                 actions = g_slist_prepend (actions,
1051                                            &export_action_convert_bitmap);
1052             }
1053         }
1054       break;
1055     }
1056 
1057   if (actions)
1058     {
1059       actions = g_slist_reverse (actions);
1060 
1061       if (interactive)
1062         retval = export_dialog (actions, format_name);
1063       else
1064         retval = GIMP_EXPORT_EXPORT;
1065     }
1066   else
1067     {
1068       retval = GIMP_EXPORT_IGNORE;
1069     }
1070 
1071   if (retval == GIMP_EXPORT_EXPORT)
1072     {
1073       GSList *list;
1074 
1075       *image_ID = gimp_image_duplicate (*image_ID);
1076       *drawable_ID = gimp_image_get_active_layer (*image_ID);
1077 
1078       gimp_image_undo_disable (*image_ID);
1079 
1080       for (list = actions; list; list = list->next)
1081         {
1082           export_action_perform (list->data, *image_ID, drawable_ID);
1083         }
1084     }
1085 
1086   g_slist_free (actions);
1087 
1088   return retval;
1089 }
1090 
1091 /**
1092  * gimp_export_dialog_new:
1093  * @format_name: The short name of the image_format (e.g. JPEG or PNG).
1094  * @role:        The dialog's @role which will be set with
1095  *               gtk_window_set_role().
1096  * @help_id:     The GIMP help id.
1097  *
1098  * Creates a new export dialog. All file plug-ins should use this
1099  * dialog to get a consistent look on the export dialogs. Use
1100  * gimp_export_dialog_get_content_area() to get a #GtkVBox to be
1101  * filled with export options. The export dialog is a wrapped
1102  * #GimpDialog.
1103  *
1104  * The dialog response when the user clicks on the Export button is
1105  * %GTK_RESPONSE_OK, and when the Cancel button is clicked it is
1106  * %GTK_RESPONSE_CANCEL.
1107  *
1108  * Returns: The new export dialog.
1109  *
1110  * Since: 2.8
1111  **/
1112 GtkWidget *
gimp_export_dialog_new(const gchar * format_name,const gchar * role,const gchar * help_id)1113 gimp_export_dialog_new (const gchar *format_name,
1114                         const gchar *role,
1115                         const gchar *help_id)
1116 {
1117   GtkWidget *dialog;
1118   /* TRANSLATORS: the %s parameter is an image format name (ex: PNG). */
1119   gchar     *title  = g_strdup_printf (_("Export Image as %s"), format_name);
1120 
1121   dialog = gimp_dialog_new (title, role,
1122                             NULL, 0,
1123                             gimp_standard_help_func, help_id,
1124 
1125                             _("_Cancel"), GTK_RESPONSE_CANCEL,
1126                             _("_Export"), GTK_RESPONSE_OK,
1127 
1128                             NULL);
1129 
1130   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1131                                            GTK_RESPONSE_OK,
1132                                            GTK_RESPONSE_CANCEL,
1133                                            -1);
1134 
1135   gimp_window_set_transient (GTK_WINDOW (dialog));
1136 
1137   g_free (title);
1138 
1139   return dialog;
1140 }
1141 
1142 /**
1143  * gimp_export_dialog_get_content_area:
1144  * @dialog: A dialog created with gimp_export_dialog_new()
1145  *
1146  * Returns the #GtkVBox of the passed export dialog to be filled with
1147  * export options.
1148  *
1149  * Returns: The #GtkVBox to fill with export options.
1150  *
1151  * Since: 2.8
1152  **/
1153 GtkWidget *
gimp_export_dialog_get_content_area(GtkWidget * dialog)1154 gimp_export_dialog_get_content_area (GtkWidget *dialog)
1155 {
1156   return gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1157 }
1158