Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
export.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2013 johannes hanika.
4 Copyright (C) 2010 Alexandre Prokoudine.
5 Copyright (C) 2010-2011, 2013 Henrik Andersson.
6 Copyright (C) 2010 Milan Knížek.
7 Copyright (C) 2010, 2013-2014 Pascal de Bruijn.
8 Copyright (C) 2010 Stuart Henderson.
9 Copyright (C) 2011, 2013 Antony Dovgal.
10 Copyright (C) 2011-2013, 2015 Jérémy Rosen.
11 Copyright (C) 2011 Robert Bieber.
12 Copyright (C) 2011-2017, 2019-2020 Tobias Ellinghaus.
13 Copyright (C) 2012 Christian Tellefsen.
14 Copyright (C) 2012 Moritz Lipp.
15 Copyright (C) 2012-2015, 2019-2021 Pascal Obry.
16 Copyright (C) 2012 Richard Wonka.
17 Copyright (C) 2012-2013 Simon Spannagel.
18 Copyright (C) 2013 José Carlos García Sogo.
19 Copyright (C) 2013-2016 Roman Lebedev.
20 Copyright (C) 2013 Thomas Pryds.
21 Copyright (C) 2017 luzpaz.
22 Copyright (C) 2018 Maurizio Paglia.
23 Copyright (C) 2018 rawfiner.
24 Copyright (C) 2019-2020, 2022-2023, 2025-2026 Aurélien PIERRE.
25 Copyright (C) 2019 Edgardo Hoszowski.
26 Copyright (C) 2019 jakubfi.
27 Copyright (C) 2019 parafin.
28 Copyright (C) 2019-2021 Philippe Weyland.
29 Copyright (C) 2020 a.
30 Copyright (C) 2020-2022 Aldric Renaudin.
31 Copyright (C) 2020-2021 Chris Elston.
32 Copyright (C) 2020-2022 Diederik Ter Rahe.
33 Copyright (C) 2020 Heiko Bauke.
34 Copyright (C) 2020-2021 Hubert Kowalski.
35 Copyright (C) 2020 junkyardsparkle.
36 Copyright (C) 2020 Marco.
37 Copyright (C) 2020-2021 Ralf Brown.
38 Copyright (C) 2021 lhietal.
39 Copyright (C) 2022 Martin Bařinka.
40 Copyright (C) 2022 Miloš Komarčević.
41 Copyright (C) 2022 Nicolas Auffray.
42 Copyright (C) 2022 Victor Forsiuk.
43 Copyright (C) 2023 Luca Zulberti.
44 Copyright (C) 2024 Guillaume Stutin.
45
46 darktable is free software: you can redistribute it and/or modify
47 it under the terms of the GNU General Public License as published by
48 the Free Software Foundation, either version 3 of the License, or
49 (at your option) any later version.
50
51 darktable is distributed in the hope that it will be useful,
52 but WITHOUT ANY WARRANTY; without even the implied warranty of
53 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54 GNU General Public License for more details.
55
56 You should have received a copy of the GNU General Public License
57 along with darktable. If not, see <http://www.gnu.org/licenses/>.
58*/
59
60#include "widgets/bauhaus.h"
61#include "common/paths.h" // DT_PATH_MAX
63#include "common/act_on.h"
65#include "common/collection.h"
67#include "system/macros.h"
68#include "system/mem_alloc.h"
72#include "common/styles.h"
73#include "common/conf.h"
74#include "control/control.h"
75#include "control/signal.h"
76
77#include "gui/application.h"
79#include "libs/lib.h"
80#include "libs/lib_api.h"
81#ifdef GDK_WINDOWING_QUARTZ
82#include "osx/osx.h"
83#endif
84#include <gdk/gdkkeysyms.h>
85#include "widgets/container.h"
86#include "widgets/label.h"
87
88#include <gtk/gtk.h>
89#include <stdlib.h>
90
91#include <glib.h>
92
93DT_MODULE(7)
94
95#define EXPORT_MAX_IMAGE_SIZE UINT16_MAX
96#define CONFIG_PREFIX "plugins/lighttable/export/"
97
113
114
116{
117 DT_DIMENSIONS_PIXELS = 0, // set dimensions exactly in pixels
118 DT_DIMENSIONS_CM = 1, // set dimensions from physical size in centimeters * DPI
119 DT_DIMENSIONS_INCH = 2, // set dimensions from physical size in inch
120 DT_DIMENSIONS_SCALE = 3, // set dimensions by scale
121 DT_DIMENSIONS_ORIGINAL = 4 // user-friendly alias for scale = 1
123
124char *dt_lib_export_metadata_configuration_dialog(char *list, const gboolean ondisk);
130static void _get_max_output_dimension(dt_lib_export_t *d, uint32_t *width, uint32_t *height);
133
134#define INCH_TO_CM (2.54f)
135
136static inline float pixels2cm(dt_lib_export_t *self, const uint32_t pix)
137{
138 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(self->print_dpi)));
139 return ((float)pix * INCH_TO_CM) / (float)dpi;
140}
141
142static inline float pixels2inch(dt_lib_export_t *self, const uint32_t pix)
143{
144 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(self->print_dpi)));
145 return (float)pix / (float)dpi;
146}
147
148static inline uint32_t cm2pixels(dt_lib_export_t *self, const float cm)
149{
150 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(self->print_dpi)));
151 return ceilf((cm * (float)dpi) / INCH_TO_CM);
152}
153
154static inline uint32_t inch2pixels(dt_lib_export_t *self, const float inch)
155{
156 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(self->print_dpi)));
157 return ceilf(inch * (float)dpi);
158}
159
160static inline uint32_t print2pixels(dt_lib_export_t *self, const float value)
161{
163 switch(d_type)
164 {
167 return ceilf(value);
168 case(DT_DIMENSIONS_CM):
169 return cm2pixels(self, value);
170 case(DT_DIMENSIONS_INCH):
171 return inch2pixels(self, value);
173 ;
174 }
175
176 // should never run this
177 return ceilf(value);
178}
179
180static inline float pixels2print(dt_lib_export_t *self, const uint32_t pix)
181{
183 switch(d_type)
184 {
187 return (float)pix;
188 case(DT_DIMENSIONS_CM):
189 return pixels2cm(self, pix);
190 case(DT_DIMENSIONS_INCH):
191 return pixels2inch(self, pix);
193 ;
194 }
195
196 // should never run this
197 return (float)pix;
198}
199
200const char *name(struct dt_lib_module_t *self)
201{
202 return _("export");
203}
204
205const char **views(dt_lib_module_t *self)
206{
207 // Not displayed in views, only in popup triggered from main menu
208 static const char *v[] = {"special", NULL};
209 return v;
210}
211
213{
215}
216
217static void _update(dt_lib_module_t *self)
218{
220 const dt_lib_export_t *d = (dt_lib_export_t *)self->data;
221
222 const gboolean has_act_on = (dt_act_on_get_images_nb(TRUE, FALSE) > 0);
223
224 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
225 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
226 const int format_index = dt_imageio_get_index_of_format(dt_imageio_get_format_by_name(format_name));
227 const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(storage_name));
228
229 gtk_widget_set_sensitive(GTK_WIDGET(d->export_button), has_act_on && format_index != -1 && storage_index != -1);
230}
231
232static void _image_selection_changed_callback(gpointer instance, dt_lib_module_t *self)
233{
234 _update(self);
235}
236
237static void _collection_updated_callback(gpointer instance, dt_collection_change_t query_change,
238 dt_collection_properties_t changed_property, gpointer imgs, int next,
239 dt_lib_module_t *self)
240{
241 _update(self);
242}
243
244gboolean _is_int(double value)
245{
246 return (value == (int)value);
247}
248
249static void _scale_optim()
250{
251 double num = 1.0, denum = 1.0;
253 gchar *scale_str = dt_conf_get_string(CONFIG_PREFIX "resizing_factor");
254 gchar _str[6] = "";
255
256 gchar *pdiv = strchr(scale_str, '/');
257
258 gchar scale_buf[64] = "";
259 if(IS_NULL_PTR(pdiv))
260 {
261 if(_is_int(num) && num > 0.0)
262 {
263 sprintf(_str, "%d", (int) num);
264 g_strlcat(scale_buf, _str, sizeof(scale_buf));
265 }
266 else
267 {
268 g_strlcat(scale_buf, scale_str, sizeof(scale_buf));
269 }
270 }
271 else if(pdiv-scale_str == 0)
272 {
273 if(_is_int(denum) && denum > 0.0)
274 {
275 sprintf(_str, "%d", (int) denum);
276 g_strlcat(scale_buf, _str, sizeof(scale_buf));
277 }
278 else
279 {
280 g_strlcat(scale_buf, "1/", sizeof(scale_buf));
281 g_strlcat(scale_buf, pdiv+1, sizeof(scale_buf));
282 }
283 }
284 else
285 {
286 if(_is_int(num) && num > 0.0)
287 {
288 sprintf(_str, "%d", (int) num);
289 g_strlcat(scale_buf, _str, sizeof(scale_buf));
290 }
291 else
292 {
293 g_strlcat(scale_buf, scale_str, sizeof(scale_buf));
294 }
295 g_strlcat(scale_buf, "/", sizeof(scale_buf));
296 if(_is_int(denum) && denum > 0.0)
297 {
298 sprintf(_str, "%d", (int) denum);
299 g_strlcat(scale_buf, _str, sizeof(scale_buf));
300 }
301 else
302 {
303 g_strlcat(scale_buf, pdiv+1, sizeof(scale_buf));
304 }
305 }
306 dt_conf_set_string(CONFIG_PREFIX "resizing_factor", scale_buf);
307
308 dt_free(scale_str);
309}
310
312{
313 /* write current history changes so nothing gets lost,
314 do that only in the darkroom as there is nothing to be saved
315 when in the lighttable (and it would write over current history stack) */
316 char style[128] = { 0 };
317
318 // get the format_name and storage_name settings which are plug-ins name and not necessary what is displayed on the combobox.
319 // note that we cannot take directly the combobox entry index as depending on the storage some format are not listed.
320 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
321 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
322 const int format_index = dt_imageio_get_index_of_format(dt_imageio_get_format_by_name(format_name));
323 const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(storage_name));
324
325 if(format_index == -1)
326 {
327 dt_control_log("invalid format for export selected");
328 return;
329 }
330 if(storage_index == -1)
331 {
332 dt_control_log("invalid storage for export selected");
333 return;
334 }
335
336 char *confirm_message = NULL;
338 if(mstorage->ask_user_confirmation)
339 confirm_message = mstorage->ask_user_confirmation(mstorage);
340 if(confirm_message)
341 {
342 const GtkWidget *win = dt_gui_main_window();
343 GtkWidget *dialog = gtk_message_dialog_new(
344 GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
345 "%s", confirm_message);
346#ifdef GDK_WINDOWING_QUARTZ
348#endif
349
350 gtk_window_set_title(GTK_WINDOW(dialog), _("export to disk"));
351 const gint res = gtk_dialog_run(GTK_DIALOG(dialog));
352 gtk_widget_destroy(dialog);
353 dt_free(confirm_message);
354
355 if(res != GTK_RESPONSE_YES)
356 {
357 return;
358 }
359 }
360
361 // Let's get the max dimension restriction if any...
362 uint32_t max_width = dt_conf_get_int(CONFIG_PREFIX "width");
363 uint32_t max_height = dt_conf_get_int(CONFIG_PREFIX "height");
364
365 const gboolean export_masks = dt_conf_get_bool(CONFIG_PREFIX "export_masks");
366 const char *tmp = dt_conf_get_string_const(CONFIG_PREFIX "style");
367 if(tmp)
368 {
369 g_strlcpy(style, tmp, sizeof(style));
370 }
371
373 gchar *icc_filename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
374 const dt_iop_color_intent_t icc_intent = dt_conf_get_int(CONFIG_PREFIX "iccintent");
375
376 GList *list = dt_act_on_get_images();
377 dt_control_export(list, max_width, max_height, format_index, storage_index, TRUE, export_masks,
378 style, icc_type, icc_filename, icc_intent, d->metadata_export);
379
380 dt_free(icc_filename);
381
382 _scale_optim();
383 gtk_entry_set_text(GTK_ENTRY(d->scale), dt_conf_get_string_const(CONFIG_PREFIX "resizing_factor"));
384}
385
386static void _scale_changed(GtkEntry *spin, dt_lib_export_t *d)
387{
388 const char *validSign = ",.0123456789";
389 const gchar *value = gtk_entry_get_text(spin);
390
391 const int len = sizeof(value);
392 int i, j = 0, idec = 0, idiv = 0, pdiv = 0;
393 char new_value[30] = "";
394
395 for (i = 0; i < len; i++)
396 {
397 char *val = strchr(validSign, value[i]);
398 if(IS_NULL_PTR(val))
399 {
400 if(idiv==0)
401 {
402 if(i == 0)
403 {
404 new_value[j++] = '1';
405 }
406 else
407 {
408 if(atof(value) == 0.0)
409 {
410 new_value[0] = '1';
411 }
412 idec = 0;
413 idiv = 1;
414 new_value[j++] = '/';
415 pdiv = j;
416 }
417 }
418 }
419 else if((val[0] == '.') || (val[0] == ','))
420 {
421 if(idec == 0)
422 {
423 if((i == 0) || (i == pdiv))
424 {
425 new_value[j++] = '0';
426 }
427 else
428 {
429 idec = 1;
430 new_value[j++] = value[i];
431 }
432 }
433 }
434 else if(value[i] == '\0')
435 {
436 break;
437 }
438 else
439 {
440 new_value[j++] = value[i];
441 }
442 }
443 dt_conf_set_string(CONFIG_PREFIX "resizing_factor", new_value);
444 gtk_entry_set_text(spin, new_value);
445}
446
447static void _width_changed(GtkEditable *entry, gpointer user_data);
448static void _height_changed(GtkEditable *entry, gpointer user_data);
449
450static gboolean _scale_mdlclick(GtkEntry *spin, GdkEventButton *event, dt_lib_export_t *d)
451{
452 if(event->button == 2)
453 {
454 dt_conf_set_string(CONFIG_PREFIX "resizing_factor", "1");
455 g_signal_handlers_block_by_func(spin, _scale_changed, d);
456 gtk_entry_set_text(GTK_ENTRY(spin), "1");
457 g_signal_handlers_unblock_by_func(spin, _scale_changed, d);
458 }
459 else
460 {
461 _scale_changed(spin, d);
462 }
463 return FALSE;
464}
465
466static void _widht_mdlclick(GtkEntry *spin, GdkEventButton *event, gpointer user_data)
467{
468 if(event->button == 2)
469 {
470 dt_conf_set_int(CONFIG_PREFIX "width", 0);
471 g_signal_handlers_block_by_func(spin, _width_changed, user_data);
472 gtk_entry_set_text(GTK_ENTRY(spin), "0");
473 g_signal_handlers_unblock_by_func(spin, _width_changed, user_data);
474 }
475 else
476 {
477 _width_changed(GTK_EDITABLE(spin), user_data);
478 }
479}
480
481static void _height_mdlclick(GtkEntry *spin, GdkEventButton *event, gpointer user_data)
482{
483 if(event->button == 2)
484 {
485 dt_conf_set_int(CONFIG_PREFIX "height", 0);
486 g_signal_handlers_block_by_func(spin, _height_changed, user_data);
487 gtk_entry_set_text(GTK_ENTRY(spin), "0");
488 g_signal_handlers_unblock_by_func(spin, _height_changed, user_data);
489 }
490 else
491 {
492 _height_changed(GTK_EDITABLE(spin), user_data);
493 }
494}
495
497{
498 const dt_dimensions_type_t d_type = (dt_dimensions_type_t)dt_bauhaus_combobox_get(d->dimensions_type);
499
500 if((d_type == DT_DIMENSIONS_SCALE) || (d_type == DT_DIMENSIONS_PIXELS) || (d_type == DT_DIMENSIONS_ORIGINAL))
501 {
502 gtk_widget_hide(d->size_in_px);
503 }
504 else
505 {
506 gtk_widget_show(d->size_in_px);
507 gchar size_in_px_txt[120];
508 snprintf(size_in_px_txt, sizeof(size_in_px_txt) / sizeof(size_in_px_txt[0]), _("which is equal to %s \303\227 %s px"),
509 gtk_entry_get_text(GTK_ENTRY(d->width)), gtk_entry_get_text(GTK_ENTRY(d->height)));
510 gtk_label_set_text(GTK_LABEL(d->size_in_px), size_in_px_txt);
511 }
512}
513
514void _set_dimensions(dt_lib_export_t *d, uint32_t max_width, uint32_t max_height)
515{
516 gchar *max_width_char = g_strdup_printf("%d", max_width);
517 gchar *max_height_char = g_strdup_printf("%d", max_height);
518
520 gtk_entry_set_text(GTK_ENTRY(d->width), max_width_char);
521 gtk_entry_set_text(GTK_ENTRY(d->height), max_height_char);
524
525 dt_conf_set_int(CONFIG_PREFIX "width", max_width);
526 dt_conf_set_int(CONFIG_PREFIX "height", max_height);
527
528 dt_free(max_width_char);
529 dt_free(max_height_char);
531}
532
533
535{
537
538 gtk_widget_set_visible(self->px_size, d_type == DT_DIMENSIONS_PIXELS);
539 gtk_widget_set_visible(self->print_size, d_type == DT_DIMENSIONS_CM || d_type == DT_DIMENSIONS_INCH);
540 gtk_widget_set_visible(self->scale, d_type == DT_DIMENSIONS_SCALE);
541
542 gtk_label_set_text(GTK_LABEL(self->unit_label),
543 d_type == DT_DIMENSIONS_CM ? _("cm") : C_("unit", "in"));
544 _size_in_px_update(self);
545}
546
548{
549 // make sure we don't do anything useless:
550 if(!dt_control_running()) return;
552 gtk_entry_set_text(GTK_ENTRY(d->width), dt_confgen_get(CONFIG_PREFIX "width", DT_DEFAULT));
553 gtk_entry_set_text(GTK_ENTRY(d->height), dt_confgen_get(CONFIG_PREFIX "height", DT_DEFAULT));
554 dt_bauhaus_combobox_set(d->dimensions_type, dt_confgen_get_int(CONFIG_PREFIX "dimensions_type", DT_DEFAULT));
556
557 // Set storage
559 dt_bauhaus_combobox_set(d->storage, storage_index);
560
561 dt_bauhaus_combobox_set(d->export_masks, dt_confgen_get_bool(CONFIG_PREFIX "export_masks", DT_DEFAULT) ? 1 : 0);
562
564
565 // iccprofile
566 const int icctype = dt_confgen_get_int(CONFIG_PREFIX "icctype", DT_DEFAULT);
567 gchar *iccfilename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
568 dt_bauhaus_combobox_set(d->profile, 0);
569 if(icctype != DT_COLORSPACE_NONE)
570 {
571 // +1: the combo carries "same as original" ahead of the profiles.
572 const int pos = dt_colorspaces_profile_index(DT_PROFILE_ROLE_OUTPUT, icctype, iccfilename);
573 if(pos > -1) dt_bauhaus_combobox_set(d->profile, pos + 1);
574 }
575
576 dt_free(iccfilename);
577
578 // style
579 // set it to none if the var is not set or the style doesn't exist anymore
580 gboolean rc = FALSE;
581 const char *style = dt_confgen_get(CONFIG_PREFIX "style", DT_DEFAULT);
582 if(!IS_NULL_PTR(style) && strlen(style) > 0)
583 {
584 rc = dt_bauhaus_combobox_set_from_text(d->style, style);
585 if(rc == FALSE) dt_bauhaus_combobox_set(d->style, 0);
586 }
587 else
588 dt_bauhaus_combobox_set(d->style, 0);
589
590 // export metadata presets
591 dt_free(d->metadata_export);
592 d->metadata_export = dt_lib_export_metadata_get_conf();
593
595 if(mformat) mformat->gui_reset(mformat);
597 if(mstorage) mstorage->gui_reset(mstorage);
598
599 _update(self);
600}
601
602static void set_format_by_name(dt_lib_export_t *d, const char *name)
603{
604 // Find the selected format plugin among all existing plugins
605 dt_imageio_module_format_t *module = NULL;
606 for(GList *it = dt_imageio_get_global()->plugins_format; it; it = g_list_next(it))
607 {
608 if(g_strcmp0(((dt_imageio_module_format_t *)it->data)->name(), name) == 0
609 || g_strcmp0(((dt_imageio_module_format_t *)it->data)->plugin_name, name) == 0)
610 {
611 module = (dt_imageio_module_format_t *)it->data;
612 break;
613 }
614 }
615
616 if(!module)
617 {
618 gtk_widget_hide(d->format_extra_container);
619 return;
620 }
621 else if(module->widget)
622 {
623 gtk_widget_show_all(d->format_extra_container);
624 gtk_stack_set_visible_child(GTK_STACK(d->format_extra_container), module->widget);
625 }
626 else
627 {
628 gtk_widget_hide(d->format_extra_container);
629 }
630
631 // Store the new format
632 dt_conf_set_string(CONFIG_PREFIX "format_name", module->plugin_name);
633
634 if(dt_bauhaus_combobox_set_from_text(d->format, module->name()) == FALSE)
635 dt_bauhaus_combobox_set(d->format, 0);
636
637 // Let's also update combination of storage/format dimension restrictions
639
640 // only some modules support export of masks
641 // set it to 0 when insensitive and restore when making it sensitive again. this doesn't survive dt restarts.
642 const gboolean support_layers = (module->flags(NULL) & FORMAT_FLAGS_SUPPORT_LAYERS) == FORMAT_FLAGS_SUPPORT_LAYERS;
643 const gboolean is_enabled = gtk_widget_get_sensitive(d->export_masks);
644 if(support_layers && !is_enabled)
645 {
646 // combobox was disabled and shall be enabled. we want to restore the old setting.
647 const gboolean export_masks = dt_conf_get_bool(CONFIG_PREFIX "export_masks");
648 gtk_widget_set_sensitive(GTK_WIDGET(d->export_masks), TRUE);
649 dt_bauhaus_combobox_set(d->export_masks, export_masks ? 1 : 0);
650 }
651 else if(!support_layers && is_enabled)
652 {
653 // combobox was enabled but shall be disabled. we want to save the current setting.
654 const int export_masks = dt_bauhaus_combobox_get(d->export_masks);
655 dt_bauhaus_combobox_set(d->export_masks, 0);
656 dt_conf_set_bool(CONFIG_PREFIX "export_masks", export_masks == 1);
657 gtk_widget_set_sensitive(GTK_WIDGET(d->export_masks), FALSE);
658 }
659}
660
662{
663 const gchar *name = dt_bauhaus_combobox_get_text(d->format);
664 g_signal_handlers_block_by_func(widget, _format_changed, d);
666 g_signal_handlers_unblock_by_func(widget, _format_changed, d);
667}
668
669static void _get_max_output_dimension(dt_lib_export_t *d, uint32_t *width, uint32_t *height)
670{
671 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
673
674 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
676
677 if(storage && format)
678 {
679 uint32_t fw, fh, sw, sh;
680 fw = fh = sw = sh = 0; // We are all equals!!!
681 storage->dimension(storage, NULL, &sw, &sh);
682 format->dimension(format, NULL, &fw, &fh);
683
684 if(sw == 0 || fw == 0)
685 *width = sw > fw ? sw : fw;
686 else
687 *width = sw < fw ? sw : fw;
688
689 if(sh == 0 || fh == 0)
690 *height = sh > fh ? sh : fh;
691 else
692 *height = sh < fh ? sh : fh;
693 }
694}
695
697{
698 //reset dimensions to previously stored value if they exceed the maximum
699 uint32_t width = atoi(gtk_entry_get_text(GTK_ENTRY(d->width)));
700 uint32_t height = atoi(gtk_entry_get_text(GTK_ENTRY(d->height)));
701 if(width > d->max_allowed_width || height > d->max_allowed_height)
702 {
703 width = width > d->max_allowed_width ? dt_conf_get_int(CONFIG_PREFIX "width") : width;
704 height = height > d->max_allowed_height ? dt_conf_get_int(CONFIG_PREFIX "height") : height;
706 }
707}
708
710{
711 uint32_t max_w = 0, max_h = 0;
712 _get_max_output_dimension(d, &max_w, &max_h);
713 d->max_allowed_width = max_w > 0 ? max_w : EXPORT_MAX_IMAGE_SIZE;
714 d->max_allowed_height = max_h > 0 ? max_h : EXPORT_MAX_IMAGE_SIZE;
716}
717
718static void set_storage_by_name(dt_lib_export_t *d, const char *name)
719{
720 int k = -1;
721 dt_imageio_module_storage_t *module = NULL;
722
723 for(const GList *it = dt_imageio_get_global()->plugins_storage; it; it = g_list_next(it))
724 {
726 k++;
727 if(strcmp(storage->name(storage), name) == 0 || strcmp(storage->plugin_name, name) == 0)
728 {
729 module = storage;
730 break;
731 }
732 }
733
734 if(!module)
735 {
736 gtk_widget_hide(d->storage_extra_container);
737 return;
738 }
739 else if(module->widget)
740 {
741 gtk_widget_show_all(d->storage_extra_container);
742 gtk_stack_set_visible_child(GTK_STACK(d->storage_extra_container),module->widget);
743 }
744 else
745 {
746 gtk_widget_hide(d->storage_extra_container);
747 }
748 dt_bauhaus_combobox_set(d->storage, k);
749 dt_conf_set_string(CONFIG_PREFIX "storage_name", module->plugin_name);
750
751
752 // Check if plugin recommends a max dimension and set
753 // if not implemented the stored conf values are used..
754 uint32_t w = 0, h = 0;
755 module->recommended_dimension(module, NULL, &w, &h);
756
757 const uint32_t cw = dt_conf_get_int(CONFIG_PREFIX "width");
758 const uint32_t ch = dt_conf_get_int(CONFIG_PREFIX "height");
759
760 // If user's selected value is below the max, select it
761 if(w > cw || w == 0) w = cw;
762 if(h > ch || h == 0) h = ch;
763
764 // Set the recommended dimension
765 _set_dimensions(d, w, h);
766
767 // Let's update formats combobox with supported formats of selected storage module...
769
770 // Lets try to set selected format if fail select first in list..
771 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
773
774 if(IS_NULL_PTR(format)
775 || dt_bauhaus_combobox_set_from_text(d->format, format->name()) == FALSE)
776 dt_bauhaus_combobox_set(d->format, 0);
777}
778
780{
781 const gchar *name = dt_bauhaus_combobox_get_text(d->storage);
782 g_signal_handlers_block_by_func(widget, _storage_changed, d);
784 g_signal_handlers_unblock_by_func(widget, _storage_changed, d);
785}
786
788{
789 int pos = dt_bauhaus_combobox_get(widget);
790 if(pos > 0)
791 {
792 pos--; // the combo carries "same as original" at 0
795 {
796 dt_conf_set_int(CONFIG_PREFIX "icctype", desc.type);
798 (desc.type == DT_COLORSPACE_FILE) ? desc.filename : "");
799 return;
800 }
801 }
803 dt_conf_set_string(CONFIG_PREFIX "iccprofile", "");
804}
805
807{
808 if(dt_gui_widgets_suppressed()) return;
809
811
812 dt_conf_set_int(CONFIG_PREFIX "dimensions_type", d_type);
814 d_type == DT_DIMENSIONS_SCALE ? "scaling" : "max_size");
815
816 if(d_type == DT_DIMENSIONS_CM || d_type == DT_DIMENSIONS_INCH)
817 {
818 // set dpi to user-set dpi
819 dt_conf_set_int("metadata/resolution", dt_conf_get_int(CONFIG_PREFIX "print_dpi"));
821 }
822 else
823 {
824 // reset export dpi to default value for scale/pixel specific export
825 dt_conf_set_int("metadata/resolution", dt_confgen_get_int("metadata/resolution", DT_DEFAULT));
826 }
827
828 if(d_type == DT_DIMENSIONS_ORIGINAL)
829 {
830 // The "same as original" is really just an user-friendly alias
831 // for size = 0x0 pixels, which we read as "full size"
832 dt_conf_set_int(CONFIG_PREFIX "width", 0);
833 dt_conf_set_int(CONFIG_PREFIX "height", 0);
834 dt_conf_set_string(CONFIG_PREFIX "resizing_factor", "1");
835 }
836
838}
839
841{
842 if(dt_gui_widgets_suppressed()) return;
843
844 const uint32_t width = dt_conf_get_int(CONFIG_PREFIX "width");
845 const uint32_t height = dt_conf_get_int(CONFIG_PREFIX "height");
846 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(self->print_dpi)));
847
848 const float p_width = pixels2print(self, width);
849 const float p_height = pixels2print(self, height);
850
852 gchar *pwidth = g_strdup_printf("%.2f", p_width);
853 gchar *pheight = g_strdup_printf("%.2f", p_height);
854 gchar *pdpi = g_strdup_printf("%d", dpi);
855 gtk_entry_set_text(GTK_ENTRY(self->print_width), pwidth);
856 gtk_entry_set_text(GTK_ENTRY(self->print_height), pheight);
857 gtk_entry_set_text(GTK_ENTRY(self->print_dpi), pdpi);
858 dt_free(pwidth);
859 dt_free(pheight);
860 dt_free(pdpi);
862}
863
865{
866 if(dt_gui_widgets_suppressed()) return;
867
868 const float p_width = atof(gtk_entry_get_text(GTK_ENTRY(self->print_width)));
869 const float p_height = atof(gtk_entry_get_text(GTK_ENTRY(self->print_height)));
870
871 const uint32_t width = print2pixels(self, p_width);
872 const uint32_t height = print2pixels(self, p_height);
873
876
878 gchar *pwidth = g_strdup_printf("%d", width);
879 gchar *pheight = g_strdup_printf("%d", height);
880 gtk_entry_set_text(GTK_ENTRY(self->width), pwidth);
881 gtk_entry_set_text(GTK_ENTRY(self->height), pheight);
882 dt_free(pwidth);
883 dt_free(pheight);
885}
886
887static void _width_changed(GtkEditable *entry, gpointer user_data)
888{
889 if(dt_gui_widgets_suppressed()) return;
890
891 const dt_lib_export_t *d = (dt_lib_export_t *)user_data;
892 const uint32_t width = atoi(gtk_entry_get_text(GTK_ENTRY(d->width)));
894}
895
896static void _print_width_changed(GtkEditable *entry, gpointer user_data)
897{
898 if(dt_gui_widgets_suppressed()) return;
899
900 dt_lib_export_t *d = (dt_lib_export_t *)user_data;
901
902 const float p_width = atof(gtk_entry_get_text(GTK_ENTRY(d->print_width)));
903 const uint32_t width = print2pixels(d, p_width);
905
907 gchar *pwidth = g_strdup_printf("%d", width);
908 gtk_entry_set_text(GTK_ENTRY(d->width), pwidth);
909 dt_free(pwidth);
912}
913
914static void _height_changed(GtkEditable *entry, gpointer user_data)
915{
916 if(dt_gui_widgets_suppressed()) return;
917
918 const dt_lib_export_t *d = (dt_lib_export_t *)user_data;
919 const uint32_t height = atoi(gtk_entry_get_text(GTK_ENTRY(d->height)));
921}
922
923static void _print_height_changed(GtkEditable *entry, gpointer user_data)
924{
925 if(dt_gui_widgets_suppressed()) return;
926
927 dt_lib_export_t *d = (dt_lib_export_t *)user_data;
928
929 const float p_height = atof(gtk_entry_get_text(GTK_ENTRY(d->print_height)));
930 const uint32_t height = print2pixels(d, p_height);
932
934 gchar *pheight = g_strdup_printf("%d", height);
935 gtk_entry_set_text(GTK_ENTRY(d->height), pheight);
936 dt_free(pheight);
939}
940
941static void _print_dpi_changed(GtkWidget *widget, gpointer user_data)
942{
943 if(dt_gui_widgets_suppressed()) return;
944
945 dt_lib_export_t *d = (dt_lib_export_t *)user_data;
946 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(d->print_dpi)));
947
948 dt_conf_set_int(CONFIG_PREFIX "print_dpi", dpi);
949 dt_conf_set_int("metadata/resolution", dpi);
950
953}
954
955static void _callback_bool(GtkWidget *widget, gpointer user_data)
956{
957 const char *key = (const char *)user_data;
959}
960
962{
963 const int pos = dt_bauhaus_combobox_get(widget);
964 dt_conf_set_int(CONFIG_PREFIX "iccintent", pos - 1);
965}
966
968{
969 if(dt_bauhaus_combobox_get(d->style) == 0)
970 {
972 }
973 else
974 {
975 const gchar *style = dt_bauhaus_combobox_get_text(d->style);
976 dt_conf_set_string(CONFIG_PREFIX "style", style);
977 }
978}
979
981{
982 return 99;
983}
984
986{
987 // Clear format combo box
989
990 // Get current selected storage
991 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
993
994 // Add supported formats to combobox
995 gboolean empty = TRUE;
996 for(const GList *it = dt_imageio_get_global()->plugins_format; it; it = g_list_next(it))
997 {
999 if(storage->supported(storage, format))
1000 {
1001 dt_bauhaus_combobox_add(d->format, format->name());
1002 empty = FALSE;
1003 }
1004 }
1005
1006 gtk_widget_set_sensitive(GTK_WIDGET(d->format), !empty);
1007}
1008
1009static void _on_storage_list_changed(gpointer instance, dt_lib_module_t *self)
1010{
1011 dt_lib_export_t *d = self->data;
1013 dt_bauhaus_combobox_clear(d->storage);
1014
1015 dt_gui_container_remove_children(GTK_CONTAINER(d->storage_extra_container));
1016
1017 for(const GList *it = dt_imageio_get_global()->plugins_storage; it; it = g_list_next(it))
1018 {
1019 const dt_imageio_module_storage_t *module = (dt_imageio_module_storage_t *)it->data;
1020 dt_bauhaus_combobox_add(d->storage, module->name(module));
1021 if(module->widget)
1022 {
1023 gtk_container_add(GTK_CONTAINER(d->storage_extra_container), module->widget);
1024 }
1025 }
1027}
1028
1029static void _lib_export_styles_changed_callback(gpointer instance, gpointer user_data)
1030{
1031 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
1032 dt_lib_export_t *d = self->data;
1033
1035 dt_bauhaus_combobox_add(d->style, _("none"));
1036
1037 GList *styles = dt_styles_get_list("");
1038 for(const GList *st_iter = styles; st_iter; st_iter = g_list_next(st_iter))
1039 {
1040 const dt_style_t *style = (dt_style_t *)st_iter->data;
1041 dt_bauhaus_combobox_add(d->style, style->name);
1042 }
1043 dt_bauhaus_combobox_set(d->style, 0);
1044
1045 g_list_free_full(styles, dt_style_free);
1046 styles = NULL;
1047}
1048
1049void _menuitem_preferences(GtkMenuItem *menuitem, dt_lib_module_t *self)
1050{
1052 const gchar *name = dt_bauhaus_combobox_get_text(d->storage);
1053 const gboolean ondisk = name && !g_strcmp0(name, _("file on disk")); // FIXME: NO!!!!!one!
1054 d->metadata_export = dt_lib_export_metadata_configuration_dialog(d->metadata_export, ondisk);
1055}
1056
1057void set_preferences(void *menu, dt_lib_module_t *self)
1058{
1059 GtkWidget *mi = gtk_menu_item_new_with_label(_("preferences..."));
1060 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_menuitem_preferences), self);
1061 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1062}
1063
1065{
1066 dt_lib_export_t *d = (dt_lib_export_t *)malloc(sizeof(dt_lib_export_t));
1067 self->timeout_handle = 0;
1068 self->data = (void *)d;
1069 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1070
1071 GtkWidget *label = dt_ui_section_label_new(_("Storage options"));
1072 gtk_box_pack_start(GTK_BOX(self->widget), label, FALSE, TRUE, 0);
1073
1075 dt_bauhaus_widget_set_label(d->storage, N_("Target storage"));
1076 gtk_box_pack_start(GTK_BOX(self->widget), d->storage, FALSE, TRUE, 0);
1077
1078 // add all storage widgets to the stack widget
1079 d->storage_extra_container = gtk_stack_new();
1080 gtk_stack_set_homogeneous(GTK_STACK(d->storage_extra_container),FALSE);
1081 gtk_box_pack_start(GTK_BOX(self->widget), d->storage_extra_container, FALSE, TRUE, 0);
1082 for(const GList *it = dt_imageio_get_global()->plugins_storage; it; it = g_list_next(it))
1083 {
1084 const dt_imageio_module_storage_t *module = (dt_imageio_module_storage_t *)it->data;
1085 dt_bauhaus_combobox_add(d->storage, module->name(module));
1086 if(module->widget)
1087 {
1088 gtk_container_add(GTK_CONTAINER(d->storage_extra_container), module->widget);
1089 }
1090 }
1091
1092 // postponed so we can do the two steps in one loop
1094 G_CALLBACK(_on_storage_list_changed), self);
1095 g_signal_connect(G_OBJECT(d->storage), "value-changed", G_CALLBACK(_storage_changed), (gpointer)d);
1096
1097 label = dt_ui_section_label_new(_("format options"));
1098 gtk_box_pack_start(GTK_BOX(self->widget), label, FALSE, TRUE, 0);
1099
1101 dt_bauhaus_widget_set_label(d->format, N_("file format"));
1102 gtk_box_pack_start(GTK_BOX(self->widget), d->format, FALSE, TRUE, 0);
1103 g_signal_connect(G_OBJECT(d->format), "value-changed", G_CALLBACK(_format_changed), (gpointer)d);
1104
1105 // add all format widgets to the stack widget
1106 d->format_extra_container = gtk_stack_new();
1107 gtk_stack_set_homogeneous(GTK_STACK(d->format_extra_container),FALSE);
1108 gtk_box_pack_start(GTK_BOX(self->widget), d->format_extra_container, FALSE, TRUE, 0);
1109 for(const GList *it = dt_imageio_get_global()->plugins_format; it; it = g_list_next(it))
1110 {
1111 const dt_imageio_module_format_t *module = (dt_imageio_module_format_t *)it->data;
1112 dt_bauhaus_combobox_add(d->format, module->name());
1113 if(module->widget)
1114 {
1115 gtk_container_add(GTK_CONTAINER(d->format_extra_container), module->widget);
1116 }
1117 }
1118
1119 label = dt_ui_section_label_new(_("global options"));
1120 gtk_box_pack_start(GTK_BOX(self->widget), label, FALSE, TRUE, 0);
1121
1122 DT_BAUHAUS_COMBOBOX_NEW_FULL(dt_bauhaus_get_global(), d->dimensions_type, NULL, N_("set size (bounding box)"),
1123 _("Choose a method for setting the output size.\n"
1124 "The width and height specified define the bounding box\n"
1125 "in which the image will be proportionnaly fitted.\n"),
1126 dt_conf_get_int(CONFIG_PREFIX "dimensions_type"),
1127 (GtkCallback)_dimensions_type_changed, d,
1128 N_("in pixels (for file)"),
1129 N_("in cm (for print)"),
1130 N_("in inch (for print)"),
1131 N_("by scale (for file)"),
1132 N_("original resolution"));
1133
1134 d->print_width = gtk_entry_new();
1136 gtk_widget_set_tooltip_text(d->print_width, _("maximum output width limit.\n"
1137 "click middle mouse button to reset to 0."));
1138 gtk_entry_set_width_chars(GTK_ENTRY(d->print_width), 5);
1139 d->print_height = gtk_entry_new();
1141 gtk_widget_set_tooltip_text(d->print_height, _("maximum output height limit.\n"
1142 "click middle mouse button to reset to 0."));
1143 gtk_entry_set_width_chars(GTK_ENTRY(d->print_height), 5);
1144 d->print_dpi = gtk_entry_new();
1146 gtk_widget_set_tooltip_text(d->print_dpi, _("resolution in dot per inch"));
1147 gtk_entry_set_width_chars(GTK_ENTRY(d->print_dpi), 4);
1148 const char *dpi = dt_conf_get_string_const(CONFIG_PREFIX "print_dpi");
1149 gtk_entry_set_text(GTK_ENTRY(d->print_dpi), dpi);
1150
1151
1152 d->width = gtk_entry_new();
1154 gtk_widget_set_tooltip_text(d->width, _("maximum output width limit.\n"
1155 "click middle mouse button to reset to 0."));
1156 gtk_entry_set_width_chars(GTK_ENTRY(d->width), 5);
1157 d->height = gtk_entry_new();
1159 gtk_widget_set_tooltip_text(d->height, _("maximum output height limit.\n"
1160 "click middle mouse button to reset to 0."));
1161 gtk_entry_set_width_chars(GTK_ENTRY(d->height), 5);
1162
1163 gtk_widget_add_events(d->width, GDK_BUTTON_PRESS_MASK);
1164 gtk_widget_add_events(d->height, GDK_BUTTON_PRESS_MASK);
1165
1166
1167 d->print_size = gtk_flow_box_new();
1168 gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(d->print_size), 5);
1169 gtk_flow_box_set_column_spacing (GTK_FLOW_BOX(d->print_size), 3);
1170 gtk_container_add(GTK_CONTAINER(d->print_size), d->print_width);
1171 gtk_container_add(GTK_CONTAINER(d->print_size), gtk_label_new(_("x")));
1172 gtk_container_add(GTK_CONTAINER(d->print_size), d->print_height);
1173 d->unit_label = gtk_label_new(_("cm"));
1174 gtk_container_add(GTK_CONTAINER(d->print_size), d->unit_label);
1175 GtkBox *dpi_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
1176 gtk_box_pack_start(dpi_box, gtk_label_new(_("@")), FALSE, FALSE, 0);
1177 gtk_box_pack_start(dpi_box, d->print_dpi, TRUE, TRUE, 0);
1178 gtk_box_pack_start(dpi_box, gtk_label_new(_("dpi")), FALSE, FALSE, 0);
1179 gtk_container_add(GTK_CONTAINER(d->print_size), GTK_WIDGET(dpi_box));
1180 dt_gui_flow_box_as_layout(GTK_FLOW_BOX(d->print_size));
1181
1182 d->px_size = gtk_flow_box_new();
1183 gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(d->px_size), 3);
1184 gtk_flow_box_set_column_spacing (GTK_FLOW_BOX(d->px_size), 3);
1185 gtk_container_add(GTK_CONTAINER(d->px_size), d->width);
1186 gtk_container_add(GTK_CONTAINER(d->px_size), gtk_label_new(_("x")));
1187 GtkBox *px_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
1188 gtk_box_pack_start(px_box, d->height, TRUE, TRUE, 0);
1189 gtk_box_pack_start(px_box, gtk_label_new(_("px")), FALSE, FALSE, 0);
1190 gtk_container_add(GTK_CONTAINER(d->px_size), GTK_WIDGET(px_box));
1191 dt_gui_flow_box_as_layout(GTK_FLOW_BOX(d->px_size));
1192
1193 d->scale = gtk_entry_new();
1195 gtk_entry_set_width_chars(GTK_ENTRY(d->scale), 5);
1196 gtk_entry_set_text (GTK_ENTRY(d->scale), dt_conf_get_string_const(CONFIG_PREFIX "resizing_factor"));
1197 gtk_widget_set_tooltip_text(d->scale, _("it can be an integer, decimal number or simple fraction.\n"
1198 "zero or empty values are equal to 1.\n"
1199 "click middle mouse button to reset to 1."));
1200 gtk_widget_set_halign(GTK_WIDGET(d->scale), GTK_ALIGN_END);
1201 gtk_widget_add_events(d->scale, GDK_BUTTON_PRESS_MASK);
1202
1203 d->size_in_px = gtk_label_new("");
1204 gtk_label_set_ellipsize(GTK_LABEL(d->size_in_px), PANGO_ELLIPSIZE_START);
1205 gtk_widget_set_sensitive(GTK_WIDGET(d->size_in_px), FALSE);
1206
1207 gtk_widget_set_halign(GTK_WIDGET(d->scale), GTK_ALIGN_FILL);
1208 gtk_widget_set_halign(GTK_WIDGET(d->size_in_px), GTK_ALIGN_END);
1209
1210 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->dimensions_type), FALSE, FALSE, 0);
1211 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->px_size), FALSE, FALSE, 0);
1212 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->print_size), FALSE, FALSE, 0);
1213 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->scale), FALSE, FALSE, 0);
1214 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->size_in_px), FALSE, FALSE, 0);
1215
1217 dt_bauhaus_widget_set_label(d->export_masks, N_("store masks"));
1218 dt_bauhaus_combobox_add(d->export_masks, _("no"));
1219 dt_bauhaus_combobox_add(d->export_masks, _("yes"));
1220 gtk_widget_set_tooltip_text(d->export_masks, _("store masks as layers in exported images. only works for some formats."));
1221 gtk_box_pack_start(GTK_BOX(self->widget), d->export_masks, FALSE, TRUE, 0);
1222
1223 // Add profile combo
1224
1225 char datadir[DT_PATH_MAX] = { 0 };
1226 char confdir[DT_PATH_MAX] = { 0 };
1227 dt_loc_get_user_config_dir(confdir, sizeof(confdir));
1228 dt_loc_get_datadir(datadir, sizeof(datadir));
1229
1231 dt_bauhaus_widget_set_label(d->profile, N_("Color space"));
1232 gtk_box_pack_start(GTK_BOX(self->widget), d->profile, FALSE, TRUE, 0);
1233 dt_bauhaus_combobox_add(d->profile, _("same as original"));
1234 dt_colorprofile_desc_t *out_profiles = NULL;
1235 const size_t n_out_profiles = dt_colorspaces_enumerate_profiles(DT_PROFILE_ROLE_OUTPUT, &out_profiles);
1236 for(size_t k = 0; k < n_out_profiles; k++)
1237 dt_bauhaus_combobox_add(d->profile, out_profiles[k].name);
1238 dt_free_align(out_profiles);
1239
1240 dt_bauhaus_combobox_set(d->profile, 1);
1241
1242 char *system_profile_dir = g_build_filename(datadir, "color", "out", NULL);
1243 char *user_profile_dir = g_build_filename(confdir, "color", "out", NULL);
1244 char *tooltip = g_strdup_printf(_(
1245 "RGB color space used to encode the output files.\n"
1246 "\"same as original\" exports to the same space as the original\n"
1247 "picture, which is linear sensor RGB for raw pictures.\n"
1248 "sRGB is recommended in most cases.\n"
1249 "You can use custom color spaces defined\n"
1250 "by ICC profiles stored in:\n- %s\n- %s"), user_profile_dir, system_profile_dir);
1251 gtk_widget_set_tooltip_text(d->profile, tooltip);
1252 dt_free(system_profile_dir);
1253 dt_free(user_profile_dir);
1255
1256 // Add intent combo
1257
1259 dt_bauhaus_widget_set_label(d->intent, N_("intent"));
1260 dt_bauhaus_combobox_add(d->intent, _("same as original"));
1261 dt_bauhaus_combobox_add(d->intent, _("perceptual"));
1262 dt_bauhaus_combobox_add(d->intent, _("relative colorimetric"));
1263 dt_bauhaus_combobox_add(d->intent, C_("rendering intent", "saturation"));
1264 dt_bauhaus_combobox_add(d->intent, _("absolute colorimetric"));
1265 gtk_box_pack_start(GTK_BOX(self->widget), d->intent, FALSE, TRUE, 0);
1266
1267 tooltip = g_strdup_printf(_("• perceptual: "
1268 "smoothly moves out-of-gamut colors into gamut,"
1269 "preserving gradations, but distorts in-gamut colors in the process."
1270 " note that perceptual is often a proprietary LUT that depends"
1271 " on the destination space."
1272 "\n\n"
1273
1274 "• relative colorimetric: "
1275 "keeps luminance while reducing as little as possible saturation"
1276 " until colors fit in gamut."
1277 "\n\n"
1278
1279 "• saturation: "
1280 "designed to present eye-catching business graphics"
1281 " by preserving the saturation. (not suited for photography)."
1282 "\n\n"
1283
1284 "• absolute colorimetric: "
1285 "adapt white point of the image to the white point of the"
1286 " destination medium and do nothing else. mainly used when"
1287 " proofing colors. (not suited for photography)."
1288 ""
1289 ));
1290 gtk_widget_set_tooltip_text(d->intent, tooltip);
1292
1293 // Add style combo
1294
1296 dt_bauhaus_widget_set_label(d->style, N_("style"));
1298 gtk_box_pack_start(GTK_BOX(self->widget), d->style, FALSE, TRUE, 0);
1299 gtk_widget_set_tooltip_text(d->style, _("temporary style to use while exporting"));
1300
1301 // Set callback signals
1302 g_signal_connect(G_OBJECT(d->export_masks), "value-changed", G_CALLBACK(_callback_bool),
1303 (gpointer)CONFIG_PREFIX "export_masks");
1304 g_signal_connect(G_OBJECT(d->intent), "value-changed", G_CALLBACK(_intent_changed), (gpointer)d);
1305 g_signal_connect(G_OBJECT(d->profile), "value-changed", G_CALLBACK(_profile_changed), (gpointer)d);
1306 g_signal_connect(G_OBJECT(d->style), "value-changed", G_CALLBACK(_style_changed), (gpointer)d);
1307
1309 G_CALLBACK(_lib_export_styles_changed_callback), self);
1310
1311 GtkBox *hbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
1312 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox), FALSE, TRUE, 0);
1313
1314 // Export button
1315 d->export_button = GTK_BUTTON(dt_action_button_new(self, N_("export"), _export_button_clicked, d,
1316 _("export with current settings"), GDK_KEY_e, DT_PRIMARY_MASK));
1317 gtk_box_pack_start(hbox, GTK_WIDGET(d->export_button), TRUE, TRUE, 0);
1318
1319 g_signal_connect(G_OBJECT(d->width), "changed", G_CALLBACK(_width_changed), (gpointer)d);
1320 g_signal_connect(G_OBJECT(d->height), "changed", G_CALLBACK(_height_changed), (gpointer)d);
1321 g_signal_connect(G_OBJECT(d->print_width), "changed", G_CALLBACK(_print_width_changed), (gpointer)d);
1322 g_signal_connect(G_OBJECT(d->print_height), "changed", G_CALLBACK(_print_height_changed), (gpointer)d);
1323 g_signal_connect(G_OBJECT(d->print_dpi), "changed", G_CALLBACK(_print_dpi_changed), (gpointer)d);
1324
1325 g_signal_connect(G_OBJECT(d->width), "changed", G_CALLBACK(_width_changed), (gpointer)d);
1326 g_signal_connect(G_OBJECT(d->height), "changed", G_CALLBACK(_height_changed), (gpointer)d);
1327
1328 g_signal_connect(G_OBJECT(d->width), "button-press-event", G_CALLBACK(_widht_mdlclick), (gpointer)d);
1329 g_signal_connect(G_OBJECT(d->height), "button-press-event", G_CALLBACK(_height_mdlclick), (gpointer)d);
1330 g_signal_connect(G_OBJECT(d->print_width), "button-press-event", G_CALLBACK(_widht_mdlclick), (gpointer)d);
1331 g_signal_connect(G_OBJECT(d->print_height), "button-press-event", G_CALLBACK(_height_mdlclick), (gpointer)d);
1332
1333 g_signal_connect(G_OBJECT(d->scale), "button-press-event", G_CALLBACK(_scale_mdlclick), (gpointer)d);
1334 g_signal_connect(G_OBJECT(d->scale), "changed", G_CALLBACK(_scale_changed), (gpointer)d);
1335
1336 // this takes care of keeping hidden widgets hidden
1337 gtk_widget_show_all(self->widget);
1338 gtk_widget_set_no_show_all(self->widget, TRUE);
1339
1340 const char* setting = dt_conf_get_string_const(CONFIG_PREFIX "width");
1341 gtk_entry_set_text(GTK_ENTRY(d->width), setting);
1342 setting = dt_conf_get_string_const(CONFIG_PREFIX "height");
1343 gtk_entry_set_text(GTK_ENTRY(d->height), setting);
1344
1346
1347 // Set storage
1348 setting = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
1349 const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(setting));
1350 dt_bauhaus_combobox_set(d->storage, storage_index);
1351
1352 // Set format
1353 setting = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
1354 const int format_index = dt_imageio_get_index_of_format(dt_imageio_get_format_by_name(setting));
1355 dt_bauhaus_combobox_set(d->format, format_index);
1356
1357 dt_bauhaus_combobox_set(d->export_masks, dt_conf_get_bool(CONFIG_PREFIX "export_masks") ? 1 : 0);
1358
1359 dt_bauhaus_combobox_set(d->intent, dt_conf_get_int(CONFIG_PREFIX "iccintent") + 1);
1360
1361 // iccprofile
1362 const int icctype = dt_conf_get_int(CONFIG_PREFIX "icctype");
1363 gchar *iccfilename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
1364 dt_bauhaus_combobox_set(d->profile, 0);
1365
1366 if(icctype != DT_COLORSPACE_NONE)
1367 {
1368 // +1: the combo carries "same as original" ahead of the profiles.
1369 const int pos = dt_colorspaces_profile_index(DT_PROFILE_ROLE_OUTPUT, icctype, iccfilename);
1370 if(pos > -1) dt_bauhaus_combobox_set(d->profile, pos + 1);
1371 }
1372
1373 dt_free(iccfilename);
1374
1375 // style
1376 // set it to none if the var is not set or the style doesn't exist anymore
1377 gboolean rc = FALSE;
1378 setting = dt_conf_get_string_const(CONFIG_PREFIX "style");
1379 if(!IS_NULL_PTR(setting) && strlen(setting) > 0)
1380 {
1381 rc = dt_bauhaus_combobox_set_from_text(d->style, setting);
1382 if(rc == FALSE)
1383 dt_bauhaus_combobox_set(d->style, 0);
1384 }
1385 else
1386 dt_bauhaus_combobox_set(d->style, 0);
1387
1388 // export metadata presets
1389 d->metadata_export = dt_lib_export_metadata_get_conf();
1390
1392 G_CALLBACK(_image_selection_changed_callback), self);
1394 G_CALLBACK(_collection_updated_callback), self);
1395}
1396
1398{
1399 if(IS_NULL_PTR(self->data)) return;
1402
1405
1408
1409 for(const GList *it = dt_imageio_get_global()->plugins_storage; it; it = g_list_next(it))
1410 {
1411 dt_imageio_module_storage_t *module = (dt_imageio_module_storage_t *)it->data;
1412 if(module->widget && GTK_IS_CONTAINER(d->storage_extra_container)) gtk_container_remove(GTK_CONTAINER(d->storage_extra_container), module->widget);
1413 }
1414
1415 for(const GList *it = dt_imageio_get_global()->plugins_format; it; it = g_list_next(it))
1416 {
1417 dt_imageio_module_format_t *module = (dt_imageio_module_format_t *)it->data;
1418 if(module->widget && GTK_IS_CONTAINER(d->format_extra_container)) gtk_container_remove(GTK_CONTAINER(d->format_extra_container), module->widget);
1419 }
1420
1421
1422 dt_free(d->metadata_export);
1423
1424 dt_free(self->data);
1425}
1426
1428{
1429 // TODO: store presets in db:
1430 // dt_lib_presets_add(const char *name, const char *plugin_name, const void *params, const int32_t
1431 // params_size)
1432
1433
1434 // I know that it is super ugly to have this inside a module, but then is export not your average module
1435 // since it handles the params blobs of imageio libs.
1436 // - get all existing presets for export from db,
1437 // - extract the versions of the embedded format/storage blob
1438 // - check if it's up to date
1439 // - if older than the module -> call its legacy_params and update the preset
1440 // - drop presets that cannot be updated
1441
1442 const int version = self->version();
1443
1444 // The whole result set is materialised before the first write. This loop DELETEs and UPDATEs
1445 // data.presets, and it used to do so while its own cursor was still scanning that table.
1446 GList *presets = dt_preset_repository_list_all_versions("export");
1447 for(GList *l = presets; l; l = g_list_next(l))
1448 {
1449 const dt_module_preset_t *p = (const dt_module_preset_t *)l->data;
1450 const int rowid = p->rowid;
1451 const int op_version = p->op_version;
1452 const void *op_params = p->op_params;
1453 const size_t op_params_size = p->op_params_size;
1454 const char *name = p->name;
1455
1456 if(op_version != version)
1457 {
1458 // shouldn't happen, we run legacy_params on the lib level before calling this
1459 fprintf(stderr, "[export_init_presets] found export preset '%s' with version %d, version %d was "
1460 "expected. dropping preset.\n",
1461 name, op_version, version);
1463 }
1464 else
1465 {
1466 // extract the interesting parts from the blob
1467 const char *buf = (const char *)op_params;
1468
1469 // skip 6*int32_t: max_width, max_height, upscale, high_quality and iccintent, icctype
1470 buf += 6 * sizeof(int32_t);
1471 // skip metadata presets string
1472 buf += strlen(buf) + 1;
1473 // next skip iccfilename
1474 buf += strlen(buf) + 1;
1475
1476 // parse both names to '\0'
1477 const char *fname = buf;
1478 buf += strlen(fname) + 1;
1479 const char *sname = buf;
1480 buf += strlen(sname) + 1;
1481
1482 // get module by name and skip if not there.
1485 if(!fmod || !smod) continue;
1486
1487 // next we have fversion, sversion, fsize, ssize, fdata, sdata which is the stuff that might change
1488 size_t copy_over_part = (void *)buf - (void *)op_params;
1489
1490 const int fversion = *(const int *)buf;
1491 buf += sizeof(int32_t);
1492 const int sversion = *(const int *)buf;
1493 buf += sizeof(int32_t);
1494 const int fsize = *(const int *)buf;
1495 buf += sizeof(int32_t);
1496 const int ssize = *(const int *)buf;
1497 buf += sizeof(int32_t);
1498
1499 const void *fdata = buf;
1500 buf += fsize;
1501 const void *sdata = buf;
1502
1503 void *new_fdata = NULL, *new_sdata = NULL;
1504 size_t new_fsize = fsize, new_ssize = ssize;
1505 const int32_t new_fversion = fmod->version(), new_sversion = smod->version();
1506
1507 if(fversion < new_fversion)
1508 {
1509 if(!(fmod->legacy_params
1510 && (new_fdata = fmod->legacy_params(fmod, fdata, fsize, fversion, new_fversion, &new_fsize))
1511 != NULL))
1512 goto delete_preset;
1513 }
1514
1515 if(sversion < new_sversion)
1516 {
1517 if(!(smod->legacy_params
1518 && (new_sdata = smod->legacy_params(smod, sdata, ssize, sversion, new_sversion, &new_ssize))
1519 != NULL))
1520 goto delete_preset;
1521 }
1522
1523 if(new_fdata || new_sdata)
1524 {
1525 // we got an updated blob -> reassemble the parts and update the preset
1526 const size_t new_params_size = op_params_size - (fsize + ssize) + (new_fsize + new_ssize);
1527 void *new_params = malloc(new_params_size);
1528 memcpy(new_params, op_params, copy_over_part);
1529 // next we have fversion, sversion, fsize, ssize, fdata, sdata which is the stuff that might change
1530 size_t pos = copy_over_part;
1531 memcpy((uint8_t *)new_params + pos, &new_fversion, sizeof(int32_t));
1532 pos += sizeof(int32_t);
1533 memcpy((uint8_t *)new_params + pos, &new_sversion, sizeof(int32_t));
1534 pos += sizeof(int32_t);
1535 memcpy((uint8_t *)new_params + pos, &new_fsize, sizeof(int32_t));
1536 pos += sizeof(int32_t);
1537 memcpy((uint8_t *)new_params + pos, &new_ssize, sizeof(int32_t));
1538 pos += sizeof(int32_t);
1539 if(new_fdata)
1540 memcpy((uint8_t *)new_params + pos, new_fdata, new_fsize);
1541 else
1542 memcpy((uint8_t *)new_params + pos, fdata, fsize);
1543 pos += new_fsize;
1544 if(new_sdata)
1545 memcpy((uint8_t *)new_params + pos, new_sdata, new_ssize);
1546 else
1547 memcpy((uint8_t *)new_params + pos, sdata, ssize);
1548
1549 // write the updated preset back to db
1550 fprintf(stderr,
1551 "[export_init_presets] updating export preset '%s' from versions %d/%d to versions %d/%d\n",
1552 name, fversion, sversion, new_fversion, new_sversion);
1553 // op_version is written back as the row's own value, so this stays the op_params-only
1554 // update the original was, whatever branch reaches it.
1555 dt_preset_repository_update_params_by_rowid(rowid, op_version, new_params, new_params_size);
1556
1557 dt_free(new_fdata);
1558 dt_free(new_sdata);
1559 dt_free(new_params);
1560 }
1561
1562 continue;
1563
1564 delete_preset:
1565 dt_free(new_fdata);
1566 dt_free(new_sdata);
1567 fprintf(stderr, "[export_init_presets] export preset '%s' can't be updated from versions %d/%d to "
1568 "versions %d/%d. dropping preset\n",
1569 name, fversion, sversion, new_fversion, new_sversion);
1571 }
1572 }
1573 g_list_free_full(presets, dt_module_preset_free);
1574}
1575
1576void *legacy_params(dt_lib_module_t *self, const void *const old_params, const size_t old_params_size,
1577 const int old_version, int *new_version, size_t *new_size)
1578{
1579 if(old_version == 1)
1580 {
1581 // add version of format & storage to params
1582 const size_t new_params_size = old_params_size + 2 * sizeof(int32_t);
1583 void *new_params = malloc(new_params_size);
1584
1585 const char *buf = (const char *)old_params;
1586
1587 // skip 3*int32_t: max_width, max_height and iccintent
1588 buf += 3 * sizeof(int32_t);
1589 // next skip iccprofile
1590 buf += strlen(buf) + 1;
1591
1592 // parse both names to '\0'
1593 const char *fname = buf;
1594 buf += strlen(fname) + 1;
1595 const char *sname = buf;
1596 buf += strlen(sname) + 1;
1597
1598 // get module by name and fail if not there.
1601 if(!fmod || !smod)
1602 {
1603 dt_free(new_params);
1604 return NULL;
1605 }
1606
1607 // now we are just behind the module/storage names and before their param sizes. this is the place where
1608 // we want their versions
1609 // copy everything until here to the new params
1610 size_t first_half = (void *)buf - (void *)old_params;
1611 memcpy(new_params, old_params, first_half);
1612 // add the versions. at the time this code was added all modules were at version 1, except of picasa which was at 2.
1613 // every newer version of the imageio modules should result in a preset that is not going through this code.
1614 int32_t fversion = 1;
1615 int32_t sversion = (strcmp(sname, "picasa") == 0 ? 2 : 1);
1616 memcpy((uint8_t *)new_params + first_half, &fversion, sizeof(int32_t));
1617 memcpy((uint8_t *)new_params + first_half + sizeof(int32_t), &sversion, sizeof(int32_t));
1618 // copy the rest of the old params over
1619 memcpy((uint8_t *)new_params + first_half + sizeof(int32_t) * 2, buf, old_params_size - first_half);
1620
1621 *new_size = new_params_size;
1622 *new_version = 2;
1623 return new_params;
1624 }
1625 else if(old_version == 2)
1626 {
1627 // add upscale to params
1628 const size_t new_params_size = old_params_size + sizeof(int32_t);
1629 void *new_params = calloc(1, new_params_size);
1630
1631 memcpy(new_params, old_params, sizeof(int32_t) * 2);
1632 memcpy((uint8_t *)new_params + sizeof(int32_t) * 3, (uint8_t *)old_params + sizeof(int32_t) * 2, old_params_size - sizeof(int32_t) * 2);
1633
1634 *new_size = new_params_size;
1635 *new_version = 3;
1636 return new_params;
1637 }
1638 else if(old_version == 3)
1639 {
1640 // replace iccprofile by type + filename
1641 // format of v3:
1642 // - 4 x int32_t (max_width, max_height, upscale, iccintent)
1643 // - char* (iccprofile)
1644 // - rest
1645 // format of v4:
1646 // - 5 x int32_t (max_width, max_height, upscale, iccintent, icctype)
1647 // - char* (iccfilename)
1648 // - old rest
1649
1650 const char *buf = (const char *)old_params;
1651
1652 // first get the old iccprofile to find out how big our new blob has to be
1653 const char *iccprofile = buf + 4 * sizeof(int32_t);
1654
1655 size_t new_params_size = old_params_size - strlen(iccprofile) + sizeof(int32_t);
1656 int icctype;
1657 const char *iccfilename = "";
1658
1659 if(!strcmp(iccprofile, "image"))
1660 icctype = DT_COLORSPACE_NONE;
1661 else if(!strcmp(iccprofile, "sRGB"))
1662 icctype = DT_COLORSPACE_SRGB;
1663 else if(!strcmp(iccprofile, "linear_rec709_rgb") || !strcmp(iccprofile, "linear_rgb"))
1664 icctype = DT_COLORSPACE_LIN_REC709;
1665 else if(!strcmp(iccprofile, "linear_rec2020_rgb"))
1666 icctype = DT_COLORSPACE_LIN_REC2020;
1667 else if(!strcmp(iccprofile, "adobergb"))
1668 icctype = DT_COLORSPACE_ADOBERGB;
1669 else
1670 {
1671 icctype = DT_COLORSPACE_FILE;
1672 iccfilename = iccprofile;
1673 new_params_size += strlen(iccfilename);
1674 }
1675
1676 void *new_params = calloc(1, new_params_size);
1677 size_t pos = 0;
1678 memcpy(new_params, old_params, sizeof(int32_t) * 4);
1679 pos += 4 * sizeof(int32_t);
1680 memcpy((uint8_t *)new_params + pos, &icctype, sizeof(int32_t));
1681 pos += sizeof(int32_t);
1682 memcpy((uint8_t *)new_params + pos, iccfilename, strlen(iccfilename) + 1);
1683 pos += strlen(iccfilename) + 1;
1684 size_t old_pos = 4 * sizeof(int32_t) + strlen(iccprofile) + 1;
1685 memcpy((uint8_t *)new_params + pos, (uint8_t *)old_params + old_pos, old_params_size - old_pos);
1686
1687 *new_size = new_params_size;
1688 *new_version = 4;
1689 return new_params;
1690 }
1691 else if(old_version == 4)
1692 {
1693 // add high_quality to params
1694
1695 // format of v4:
1696 // - 5 x int32_t (max_width, max_height, upscale, iccintent, icctype)
1697 // - char* (iccfilename)
1698 // - old rest
1699 // format of v5:
1700 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1701 // - char* (iccfilename)
1702 // - old rest
1703
1704 const size_t new_params_size = old_params_size + sizeof(int32_t);
1705 void *new_params = calloc(1, new_params_size);
1706
1707 size_t pos = 0;
1708 memcpy(new_params, old_params, sizeof(int32_t) * 3);
1709 pos += 4 * sizeof(int32_t);
1710 memcpy((uint8_t *)new_params + pos, (uint8_t *)old_params + pos - sizeof(int32_t), old_params_size - sizeof(int32_t) * 3);
1711
1712 *new_size = new_params_size;
1713 *new_version = 5;
1714 return new_params;
1715 }
1716 else if(old_version == 5)
1717 {
1718 // add metadata preset string
1719
1720 // format of v5:
1721 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1722 // - char* (iccfilename)
1723 // - old rest
1724 // format of v6:
1725 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1726 // - char* (metadata_export)
1727 // - char* (iccfilename)
1728 // - old rest
1729
1730 const gboolean omit = dt_conf_get_bool("omit_tag_hierarchy");
1731 gchar *flags = g_strdup_printf("%x", dt_lib_export_metadata_default_flags() | (omit ? DT_META_OMIT_HIERARCHY : 0));
1732 const int flags_size = strlen(flags) + 1;
1733 const size_t new_params_size = old_params_size + flags_size;
1734 void *new_params = calloc(1, new_params_size);
1735 size_t pos = 0;
1736 memcpy(new_params, old_params, sizeof(int32_t) * 6);
1737 pos += 6 * sizeof(int32_t);
1738 memcpy((uint8_t *)new_params + pos, flags, flags_size);
1739 pos += flags_size;
1740 memcpy((uint8_t *)new_params + pos, (uint8_t *)old_params + pos - flags_size, old_params_size - sizeof(int32_t) * 6);
1741
1742 dt_free(flags);
1743 *new_size = new_params_size;
1744 *new_version = 6;
1745 return new_params;
1746 }
1747 else if(old_version == 6)
1748 {
1749 // add export_masks
1750
1751 // format of v6:
1752 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1753 // - old rest
1754 // format of v7:
1755 // - 7 x int32_t (max_width, max_height, upscale, high_quality, export_masks, iccintent, icctype)
1756 // - old rest
1757
1758 const size_t new_params_size = old_params_size + sizeof(int32_t);
1759 void *new_params = calloc(1, new_params_size);
1760
1761 size_t pos = 0;
1762 memcpy(new_params, old_params, sizeof(int32_t) * 4);
1763 pos += 5 * sizeof(int32_t);
1764 memcpy((uint8_t *)new_params + pos, (uint8_t *)old_params + pos - sizeof(int32_t), old_params_size - sizeof(int32_t) * 4);
1765
1766 *new_size = new_params_size;
1767 *new_version = 7;
1768 return new_params;
1769 }
1770
1771 return NULL;
1772}
1773
1775{
1777 // concat storage and format, size is max + header
1780 if(IS_NULL_PTR(mformat) || IS_NULL_PTR(mstorage)) return NULL;
1781
1782 // size will be only as large as needed to remove random pointers from params (stored at the end).
1783 size_t fsize = mformat->params_size(mformat);
1784 dt_imageio_module_data_t *fdata = mformat->get_params(mformat);
1785 size_t ssize = mstorage->params_size(mstorage);
1786 void *sdata = mstorage->get_params(mstorage);
1787 const int32_t fversion = mformat->version();
1788 const int32_t sversion = mstorage->version();
1789 // we allow null pointers (plugin not ready for export in current state), and just don't copy back the
1790 // settings later:
1791 if(IS_NULL_PTR(sdata)) ssize = 0;
1792 if(IS_NULL_PTR(fdata)) fsize = 0;
1793 if(fdata)
1794 {
1795 // clean up format global params (need to set all bytes to reliably detect which preset is active).
1796 // we happen to want to set it all to 0
1797 memset(fdata, 0, sizeof(dt_imageio_module_data_t));
1798 }
1799
1800 // FIXME: also the web preset has to be applied twice to be known as preset! (other dimension magic going on
1801 // here?)
1802 // TODO: get this stuff from gui and not from conf, so it will be sanity-checked (you can never delete an
1803 // insane preset)?
1804 // also store icc profile/intent here.
1805 const int32_t iccintent = dt_conf_get_int(CONFIG_PREFIX "iccintent");
1806 const int32_t icctype = dt_conf_get_int(CONFIG_PREFIX "icctype");
1807 const int32_t max_width = dt_conf_get_int(CONFIG_PREFIX "width");
1808 const int32_t max_height = dt_conf_get_int(CONFIG_PREFIX "height");
1809 const int32_t upscale = FALSE;
1810 const int32_t high_quality = TRUE;
1811 const int32_t export_masks = dt_conf_get_bool(CONFIG_PREFIX "export_masks") ? 1 : 0;
1812 gchar *iccfilename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
1813 gchar *style = dt_conf_get_string(CONFIG_PREFIX "style");
1814 const char *metadata_export = d->metadata_export;
1815
1816 if(fdata)
1817 {
1818 g_strlcpy(fdata->style, style, sizeof(fdata->style));
1819 }
1820
1821 if(icctype != DT_COLORSPACE_FILE)
1822 {
1823 dt_free(iccfilename);
1824 }
1825
1826 if(!iccfilename) iccfilename = g_strdup("");
1827 if(IS_NULL_PTR(metadata_export)) metadata_export = g_strdup("");
1828
1829 const char *fname = mformat->plugin_name;
1830 const char *sname = mstorage->plugin_name;
1831 const int32_t fname_len = strlen(fname);
1832 const int32_t sname_len = strlen(sname);
1833
1834 *size = fname_len + sname_len + 2 + 4 * sizeof(int32_t) + fsize + ssize + 7 * sizeof(int32_t)
1835 + strlen(iccfilename) + 1 + strlen(metadata_export) + 1;
1836
1837 char *params = (char *)calloc(1, *size);
1838 int pos = 0;
1839 memcpy(params + pos, &max_width, sizeof(int32_t));
1840 pos += sizeof(int32_t);
1841 memcpy(params + pos, &max_height, sizeof(int32_t));
1842 pos += sizeof(int32_t);
1843 memcpy(params + pos, &upscale, sizeof(int32_t));
1844 pos += sizeof(int32_t);
1845 memcpy(params + pos, &high_quality, sizeof(int32_t));
1846 pos += sizeof(int32_t);
1847 memcpy(params + pos, &export_masks, sizeof(int32_t));
1848 pos += sizeof(int32_t);
1849 memcpy(params + pos, &iccintent, sizeof(int32_t));
1850 pos += sizeof(int32_t);
1851 memcpy(params + pos, &icctype, sizeof(int32_t));
1852 pos += sizeof(int32_t);
1853 memcpy(params + pos, metadata_export, strlen(metadata_export) + 1);
1854 pos += strlen(metadata_export) + 1;
1855 memcpy(params + pos, iccfilename, strlen(iccfilename) + 1);
1856 pos += strlen(iccfilename) + 1;
1857 memcpy(params + pos, fname, fname_len + 1);
1858 pos += fname_len + 1;
1859 memcpy(params + pos, sname, sname_len + 1);
1860 pos += sname_len + 1;
1861 memcpy(params + pos, &fversion, sizeof(int32_t));
1862 pos += sizeof(int32_t);
1863 memcpy(params + pos, &sversion, sizeof(int32_t));
1864 pos += sizeof(int32_t);
1865 memcpy(params + pos, &fsize, sizeof(int32_t));
1866 pos += sizeof(int32_t);
1867 memcpy(params + pos, &ssize, sizeof(int32_t));
1868 pos += sizeof(int32_t);
1869 if(!IS_NULL_PTR(fdata)) // otherwise fsize == 0, but clang doesn't like it ...
1870 {
1871 memcpy(params + pos, fdata, fsize);
1872 pos += fsize;
1873 }
1874 if(!IS_NULL_PTR(sdata)) // see above
1875 {
1876 memcpy(params + pos, sdata, ssize);
1877 pos += ssize;
1878 }
1879 g_assert(pos == *size);
1880
1881 dt_free(iccfilename);
1882 dt_free(style);
1883
1884 if(fdata) mformat->free_params(mformat, fdata);
1885 if(sdata) mstorage->free_params(mstorage, sdata);
1886 return params;
1887}
1888
1889int set_params(dt_lib_module_t *self, const void *params, int size)
1890{
1892 // apply these stored presets again (parse blob)
1893 const char *buf = (const char *)params;
1894
1895 const int max_width = *(const int *)buf;
1896 buf += sizeof(int32_t);
1897 const int max_height = *(const int *)buf;
1898 buf += sizeof(int32_t);
1899 // const int upscale = *(const int *)buf; // unused now
1900 buf += sizeof(int32_t);
1901 //const int high_quality = *(const int *)buf; // unused now
1902 buf += sizeof(int32_t);
1903 const int export_masks = *(const int *)buf;
1904 buf += sizeof(int32_t);
1905 const int iccintent = *(const int *)buf;
1906 buf += sizeof(int32_t);
1907 const int icctype = *(const int *)buf;
1908 buf += sizeof(int32_t);
1909 const char *metadata_export = buf;
1910 buf += strlen(metadata_export) + 1;
1911 dt_free(d->metadata_export);
1912 d->metadata_export = g_strdup(metadata_export);
1913 dt_lib_export_metadata_set_conf(d->metadata_export);
1914 const char *iccfilename = buf;
1915 buf += strlen(iccfilename) + 1;
1916
1917 // reverse these by setting the gui, not the conf vars!
1918 dt_bauhaus_combobox_set(d->intent, iccintent + 1);
1919
1920 dt_bauhaus_combobox_set(d->profile, 0);
1921 if(icctype != DT_COLORSPACE_NONE)
1922 {
1923 // +1: the combo carries "same as original" ahead of the profiles.
1924 const int pos = dt_colorspaces_profile_index(DT_PROFILE_ROLE_OUTPUT, icctype, iccfilename);
1925 if(pos > -1) dt_bauhaus_combobox_set(d->profile, pos + 1);
1926 }
1927
1928 // parse both names to '\0'
1929 const char *fname = buf;
1930 buf += strlen(fname) + 1;
1931 const char *sname = buf;
1932 buf += strlen(sname) + 1;
1933
1934 // get module by name and fail if not there.
1937 if(!fmod || !smod) return 1;
1938
1939 const int32_t fversion = *(const int32_t *)buf;
1940 buf += sizeof(int32_t);
1941 const int32_t sversion = *(const int32_t *)buf;
1942 buf += sizeof(int32_t);
1943
1944 const int fsize = *(const int *)buf;
1945 buf += sizeof(int32_t);
1946 const int ssize = *(const int *)buf;
1947 buf += sizeof(int32_t);
1948
1949 if(size
1950 != strlen(fname) + strlen(sname) + 2 + 4 * sizeof(int32_t) + fsize + ssize + 7 * sizeof(int32_t)
1951 + strlen(iccfilename) + 1 + strlen(metadata_export) + 1)
1952 return 1;
1953 if(fversion != fmod->version() || sversion != smod->version()) return 1;
1954
1955 const dt_imageio_module_data_t *fdata = (const dt_imageio_module_data_t *)buf;
1956
1957 if(fdata->style[0] == '\0')
1958 dt_bauhaus_combobox_set(d->style, 0);
1959 else
1961
1962 buf += fsize;
1963 const void *sdata = buf;
1964
1965 // switch modules
1966 set_storage_by_name(d, sname);
1967 set_format_by_name(d, fname);
1968
1969 // set dimensions after switching, to have new range ready.
1970 _set_dimensions(d, max_width, max_height);
1971 dt_bauhaus_combobox_set(d->export_masks, export_masks ? 1 : 0);
1972
1973 // propagate to modules
1974 int res = 0;
1975 if(ssize) res += smod->set_params(smod, sdata, ssize);
1976 if(fsize) res += fmod->set_params(fmod, fdata, fsize);
1977 return res;
1978}
1979
1980// clang-format off
1981// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1982// vim: shiftwidth=2 expandtab tabstop=2 cindent
1983// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1984// clang-format on
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnect accels while a text or search entry has the focus, and reconnect them when it loses it....
Handle default and user-set shortcuts (accelerators)
#define DT_PRIMARY_MASK
int dt_act_on_get_images_nb(const gboolean only_visible, const gboolean force)
Definition act_on.c:53
GList * dt_act_on_get_images()
Definition act_on.c:38
GtkWidget * dt_gui_main_window(void)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:1997
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2136
const char * dt_bauhaus_combobox_get_text(GtkWidget *widget)
Definition bauhaus.c:1970
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1698
gboolean dt_bauhaus_combobox_set_from_text(GtkWidget *widget, const char *text)
Definition bauhaus.c:2100
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1824
#define DT_BAUHAUS_COMBOBOX_NEW_FULL(bauhaus, widget, action, label, tip, pos, callback, data,...)
Definition bauhaus.h:379
dt_collection_properties_t
Definition collection.h:120
dt_collection_change_t
Definition collection.h:160
const float v
size_t dt_colorspaces_enumerate_profiles(const dt_colorspaces_profile_role_t role, dt_colorprofile_desc_t **out)
Ordered snapshot of every profile registered for one role.
int dt_colorspaces_profile_index(const dt_colorspaces_profile_role_t role, const dt_colorspaces_color_profile_type_t type, const char *const filename)
Combo position of (type, filename) within direction.
gboolean dt_colorspaces_profile_at(const dt_colorspaces_profile_role_t role, const int index, dt_colorprofile_desc_t *const out)
Identity of the profile at index within direction.
The colour-profile module's API: which profiles exist, and how to apply one.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
gchar * dt_conf_get_string(const char *name)
Read the stored string for name as a private copy.
void dt_conf_set_int(const char *name, int val)
gboolean dt_confgen_get_bool(const char *name, dt_confgen_value_kind_t kind)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
int dt_confgen_get_int(const char *name, dt_confgen_value_kind_t kind)
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
const char * dt_confgen_get(const char *name, dt_confgen_value_kind_t kind)
One declared attribute of name from the generated XML, as text.
@ DT_DEFAULT
Definition conf.h:135
void dt_gui_container_remove_children(GtkContainer *container)
Definition container.c:68
void dt_gui_flow_box_as_layout(GtkFlowBox *box)
Definition container.c:98
void dt_control_log(const char *msg,...)
Definition control.c:824
int dt_control_running()
Definition control.c:442
void dt_control_export(GList *imgid_list, int max_width, int max_height, int format_index, int storage_index, gboolean high_quality, gboolean export_masks, char *style, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, const gchar *metadata_export)
struct dt_imageio_t * dt_imageio_get_global(void)
Definition darktable.c:626
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
const int res
Definition dtpthread.h:351
static float pixels2cm(dt_lib_export_t *self, const uint32_t pix)
Definition export.c:136
static void _format_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:661
void gui_reset(dt_lib_module_t *self)
Definition export.c:547
static void _callback_bool(GtkWidget *widget, gpointer user_data)
Definition export.c:955
void _menuitem_preferences(GtkMenuItem *menuitem, dt_lib_module_t *self)
Definition export.c:1049
int set_params(dt_lib_module_t *self, const void *params, int size)
Definition export.c:1889
static void _update(dt_lib_module_t *self)
Definition export.c:217
static void _height_changed(GtkEditable *entry, gpointer user_data)
Definition export.c:914
#define INCH_TO_CM
Definition export.c:134
char * dt_lib_export_metadata_configuration_dialog(char *list, const gboolean ondisk)
static float pixels2inch(dt_lib_export_t *self, const uint32_t pix)
Definition export.c:142
static void _print_height_changed(GtkEditable *entry, gpointer user_data)
Definition export.c:923
void * get_params(dt_lib_module_t *self, int *size)
Definition export.c:1774
static float pixels2print(dt_lib_export_t *self, const uint32_t pix)
Definition export.c:180
void set_preferences(void *menu, dt_lib_module_t *self)
Definition export.c:1057
static void _intent_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:961
static uint32_t inch2pixels(dt_lib_export_t *self, const float inch)
Definition export.c:154
static void _scale_changed(GtkEntry *spin, dt_lib_export_t *d)
Definition export.c:386
static void _profile_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:787
static void _scale_optim()
Definition export.c:249
static void _print_width_changed(GtkEditable *entry, gpointer user_data)
Definition export.c:896
static gboolean _scale_mdlclick(GtkEntry *spin, GdkEventButton *event, dt_lib_export_t *d)
Definition export.c:450
void gui_cleanup(dt_lib_module_t *self)
Definition export.c:1397
static uint32_t print2pixels(dt_lib_export_t *self, const float value)
Definition export.c:160
static void _print_dpi_changed(GtkWidget *widget, gpointer user_data)
Definition export.c:941
dt_dimensions_type_t
Definition export.c:116
@ DT_DIMENSIONS_PIXELS
Definition export.c:117
@ DT_DIMENSIONS_ORIGINAL
Definition export.c:121
@ DT_DIMENSIONS_SCALE
Definition export.c:120
@ DT_DIMENSIONS_INCH
Definition export.c:119
@ DT_DIMENSIONS_CM
Definition export.c:118
static void _on_storage_list_changed(gpointer instance, dt_lib_module_t *self)
Definition export.c:1009
static void _dimensions_type_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:806
static void _style_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:967
static void set_format_by_name(dt_lib_export_t *d, const char *name)
Definition export.c:602
static void _validate_dimensions(dt_lib_export_t *d)
Definition export.c:696
void _size_update_display(dt_lib_export_t *self)
Definition export.c:534
#define EXPORT_MAX_IMAGE_SIZE
Definition export.c:95
static void _update_formats_combobox(dt_lib_export_t *d)
Definition export.c:985
void init_presets(dt_lib_module_t *self)
Definition export.c:1427
static void _update_dimensions(dt_lib_export_t *d)
Definition export.c:709
static void _resync_print_dimensions(dt_lib_export_t *self)
Definition export.c:840
static void _collection_updated_callback(gpointer instance, dt_collection_change_t query_change, dt_collection_properties_t changed_property, gpointer imgs, int next, dt_lib_module_t *self)
Definition export.c:237
static void _widht_mdlclick(GtkEntry *spin, GdkEventButton *event, gpointer user_data)
Definition export.c:466
uint32_t container(dt_lib_module_t *self)
Definition export.c:212
static void _height_mdlclick(GtkEntry *spin, GdkEventButton *event, gpointer user_data)
Definition export.c:481
#define CONFIG_PREFIX
Definition export.c:96
static void _size_in_px_update(dt_lib_export_t *d)
Definition export.c:496
static void _storage_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:779
void _set_dimensions(dt_lib_export_t *d, uint32_t max_width, uint32_t max_height)
Definition export.c:514
static void _export_button_clicked(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:311
void gui_init(dt_lib_module_t *self)
Definition export.c:1064
int position()
Definition export.c:980
gboolean _is_int(double value)
Definition export.c:244
const char ** views(dt_lib_module_t *self)
Definition export.c:205
static void _image_selection_changed_callback(gpointer instance, dt_lib_module_t *self)
Definition export.c:232
void * legacy_params(dt_lib_module_t *self, const void *const old_params, const size_t old_params_size, const int old_version, int *new_version, size_t *new_size)
Definition export.c:1576
static uint32_t cm2pixels(dt_lib_export_t *self, const float cm)
Definition export.c:148
static void set_storage_by_name(dt_lib_export_t *d, const char *name)
Definition export.c:718
static void _width_changed(GtkEditable *entry, gpointer user_data)
Definition export.c:887
static void _get_max_output_dimension(dt_lib_export_t *d, uint32_t *width, uint32_t *height)
Definition export.c:669
static void _resync_pixel_dimensions(dt_lib_export_t *self)
Definition export.c:864
static void _lib_export_styles_changed_callback(gpointer instance, gpointer user_data)
Definition export.c:1029
void dt_loc_get_datadir(char *datadir, size_t bufsize)
void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
#define DT_GUI_MODULE(x)
const char * tooltip
Definition image.h:310
dt_imageio_module_storage_t * dt_imageio_get_storage()
int dt_imageio_get_index_of_format(dt_imageio_module_format_t *format)
int dt_imageio_get_index_of_storage(dt_imageio_module_storage_t *storage)
gchar * dt_imageio_resizing_factor_get_and_parsing(double *num, double *denum)
dt_imageio_module_format_t * dt_imageio_get_format()
dt_imageio_module_format_t * dt_imageio_get_format_by_name(const char *name)
dt_imageio_module_storage_t * dt_imageio_get_storage_by_name(const char *name)
GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition label.c:114
void dt_lib_cancel_postponed_update(dt_lib_module_t *mod)
Definition lib.c:1309
GtkWidget * dt_action_button_new(dt_lib_module_t *self, const gchar *label, gpointer callback, gpointer data, const gchar *tooltip, guint accel_key, GdkModifierType mods)
Definition lib.c:1336
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
char * key
char * dt_lib_export_metadata_get_conf(void)
uint32_t dt_lib_export_metadata_default_flags(void)
void dt_lib_export_metadata_set_conf(const char *metadata_presets)
@ DT_META_OMIT_HIERARCHY
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
size_t size
Definition mipmap_cache.c:3
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:105
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
const char * name
Definition pdf.h:90
void dt_module_preset_free(gpointer data)
Release one dt_module_preset_t. Suits g_list_free_full().
GList * dt_preset_repository_list_all_versions(const char *operation)
Every preset of operation at ANY version, with rowid and op_version filled in, SHIPPED FIRST then by ...
void dt_preset_repository_update_params_by_rowid(const int rowid, const int op_version, const void *params, const int params_size)
Replace the parameters of one row, by rowid.
void dt_preset_repository_delete_by_rowid(const int rowid)
Delete one row by rowid.
data.presets: a module's saved parameters, with the conditions under which it auto-applies.
static dt_colorspaces_color_profile_type_t sanitize_colorspaces(dt_colorspaces_color_profile_type_t colorspace)
Coerce an integer read back from storage into a colour space this build still has.
dt_iop_color_intent_t
ICC rendering intent, as stored in iop params and in conf.
dt_colorspaces_color_profile_type_t
@ DT_COLORSPACE_ADOBERGB
@ DT_COLORSPACE_FILE
A user ICC file on disk; the only type whose filename is meaningful.
@ DT_COLORSPACE_SRGB
sRGB – registered TWICE, and the two entries are not interchangeable.
@ DT_COLORSPACE_NONE
No profile / "take it from the image settings". Never matches a list entry.
@ DT_COLORSPACE_LIN_REC2020
@ DT_COLORSPACE_LIN_REC709
@ DT_PROFILE_ROLE_OUTPUT
Listed in the output/export-profile combo (colorout, export).
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:407
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_STYLE_CHANGED
This signal is raised when a style is added/deleted/changed
Definition signal.h:150
@ DT_SIGNAL_IMAGEIO_STORAGE_CHANGE
This signal is raised when a new storage module is loaded noparameters no return.
Definition signal.h:270
@ DT_SIGNAL_SELECTION_CHANGED
This signal is raised when the selection is changed no param, no returned value.
Definition signal.h:130
@ DT_SIGNAL_COLLECTION_CHANGED
This signal is raised when collection changed. To avoid leaking the list, dt_collection_t is connecte...
Definition signal.h:125
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:396
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
void dt_style_free(gpointer data)
GList * dt_styles_get_list(const char *filter)
A profile's public identity: what the GUI displays and stores, and nothing else.
dt_colorspaces_color_profile_type_t type
what to store in params / conf
char name[512]
translated, display-ready
char filename[DT_IOP_COLOR_ICC_LEN]
"" unless type == DT_COLORSPACE_FILE
GtkWidget * storage
Definition export.c:104
GtkWidget * format
Definition export.c:104
GtkWidget * unit_label
Definition export.c:101
int format_lut[128]
Definition export.c:105
GtkWidget * scale
Definition export.c:103
GtkWidget * print_width
Definition export.c:100
GtkButton * export_button
Definition export.c:108
GtkWidget * storage_extra_container
Definition export.c:109
uint32_t max_allowed_width
Definition export.c:106
GtkWidget * export_masks
Definition export.c:110
GtkWidget * size_in_px
Definition export.c:103
GtkWidget * print_dpi
Definition export.c:100
GtkWidget * print_height
Definition export.c:100
GtkWidget * px_size
Definition export.c:103
GtkWidget * style
Definition export.c:107
GtkWidget * profile
Definition export.c:107
GtkWidget * width
Definition export.c:102
GtkWidget * dimensions_type
Definition export.c:100
GtkWidget * print_size
Definition export.c:103
GtkWidget * height
Definition export.c:102
GtkWidget * format_extra_container
Definition export.c:109
uint32_t max_allowed_height
Definition export.c:106
GtkWidget * intent
Definition export.c:107
char * metadata_export
Definition export.c:111
GModule *void * data
Definition lib.h:79
GtkWidget * widget
Definition lib.h:83
guint timeout_handle
Definition lib.h:89
gchar * name
gboolean dt_gui_widgets_suppressed(void)
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()
#define DT_GUI_BOX_SPACING
@ DT_UI_CONTAINER_SIZE