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/dialog.h"
87#include "widgets/label.h"
88
89#include <gtk/gtk.h>
90#include <limits.h>
91#include <stdlib.h>
92
93#include <glib.h>
94
95DT_MODULE(7)
96
97#define EXPORT_MAX_IMAGE_SIZE UINT16_MAX
98#define CONFIG_PREFIX "plugins/lighttable/export/"
99
115
116
118{
119 DT_DIMENSIONS_PIXELS = 0, // set dimensions exactly in pixels
120 DT_DIMENSIONS_CM = 1, // set dimensions from physical size in centimeters * DPI
121 DT_DIMENSIONS_INCH = 2, // set dimensions from physical size in inch
122 DT_DIMENSIONS_SCALE = 3, // set dimensions by scale
123 DT_DIMENSIONS_ORIGINAL = 4 // user-friendly alias for scale = 1
125
126char *dt_lib_export_metadata_configuration_dialog(char *list, const gboolean ondisk);
132static void _get_max_output_dimension(dt_lib_export_t *d, uint32_t *width, uint32_t *height);
134
135#define INCH_TO_CM (2.54f)
136
137/* Each size mode keeps settings of its own, so switching modes loses none of them: the pixel box
138 * in "width"/"height", the print size in "print_width"/"print_height" -- always in cm, shown in
139 * the unit selected -- at "print_dpi", the factor in "resizing_factor". "original resolution"
140 * has none. The export resolves the selected mode into one size when it is dispatched
141 * (_resolve_export_size()), and nothing downstream reads these keys. */
142#define CONF_PRINT_WIDTH CONFIG_PREFIX "print_width"
143#define CONF_PRINT_HEIGHT CONFIG_PREFIX "print_height"
144
146{
147 return (dt_dimensions_type_t)dt_conf_get_int(CONFIG_PREFIX "dimensions_type");
148}
149
150static int _print_dpi(void)
151{
152 const int dpi = dt_conf_get_int(CONFIG_PREFIX "print_dpi");
153 return dpi > 0 ? dpi : 300;
154}
155
156// centimeters per unit the print size is shown in
157static float _print_unit_in_cm(const dt_dimensions_type_t d_type)
158{
159 return d_type == DT_DIMENSIONS_INCH ? INCH_TO_CM : 1.f;
160}
161
162static uint32_t _cm_to_pixels(const float cm, const int dpi)
163{
164 return cm > 0.f ? (uint32_t)ceilf(cm * (float)dpi / INCH_TO_CM) : 0;
165}
166
167static void _print_size_from_pixels(const uint32_t width, const uint32_t height)
168{
169 const int dpi = _print_dpi();
170 dt_conf_set_float(CONF_PRINT_WIDTH, (float)width * INCH_TO_CM / (float)dpi);
172}
173
174static void _ensure_print_size_conf(void)
175{
176 // settings written before the print size had keys of its own start from the pixel box
179}
180
181/* The size the selected mode asks for: with *scale_factor > 0 the image is reduced by it,
182 * otherwise it fits in *max_width x *max_height, 0 x 0 meaning full size. */
183static void _resolve_export_size(int *max_width, int *max_height, double *scale_factor)
184{
185 *max_width = 0;
186 *max_height = 0;
187 *scale_factor = 0.0;
188
189 switch(_dimensions_type())
190 {
192 *max_width = MAX(dt_conf_get_int(CONFIG_PREFIX "width"), 0);
193 *max_height = MAX(dt_conf_get_int(CONFIG_PREFIX "height"), 0);
194 break;
195 case DT_DIMENSIONS_CM:
197 {
198 const int dpi = _print_dpi();
199 *max_width = (int)_cm_to_pixels(dt_conf_get_float(CONF_PRINT_WIDTH), dpi);
200 *max_height = (int)_cm_to_pixels(dt_conf_get_float(CONF_PRINT_HEIGHT), dpi);
201 break;
202 }
204 {
205 double num = 1.0;
206 double denum = 1.0;
208 *scale_factor = num / denum;
209 break;
210 }
212 break;
213 }
214}
215
216const char *name(struct dt_lib_module_t *self)
217{
218 return _("export");
219}
220
221const char **views(dt_lib_module_t *self)
222{
223 // Not displayed in views, only in popup triggered from main menu
224 static const char *v[] = {"special", NULL};
225 return v;
226}
227
229{
231}
232
233static void _update(dt_lib_module_t *self)
234{
236 const dt_lib_export_t *d = (dt_lib_export_t *)self->data;
237
238 const gboolean has_act_on = (dt_act_on_get_images_nb(TRUE, FALSE) > 0);
239
240 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
241 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
242 const int format_index = dt_imageio_get_index_of_format(dt_imageio_get_format_by_name(format_name));
243 const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(storage_name));
244
245 gtk_widget_set_sensitive(GTK_WIDGET(d->export_button), has_act_on && format_index != -1 && storage_index != -1);
246}
247
248static void _image_selection_changed_callback(gpointer instance, dt_lib_module_t *self)
249{
250 _update(self);
251}
252
253static void _collection_updated_callback(gpointer instance, dt_collection_change_t query_change,
254 dt_collection_properties_t changed_property, gpointer imgs, int next,
255 dt_lib_module_t *self)
256{
257 _update(self);
258}
259
260// a positive whole number that fits the int it is printed as
261static gboolean _is_int(const double value)
262{
263 return value > 0.0 && value <= (double)INT_MAX && value == (int)value;
264}
265
266// rewrite the resizing factor in its shortest spelling: "2.0/4" becomes "2/4"
267static void _scale_optim()
268{
269 double num = 1.0;
270 double denum = 1.0;
271 gchar *scale_str = dt_conf_get_string(CONFIG_PREFIX "resizing_factor");
272 dt_imageio_resizing_factor_parse(scale_str, &num, &denum);
273 const gchar *pdiv = strchr(scale_str, '/');
274
275 gchar scale_buf[64] = "";
276 gchar num_str[16] = "";
277 gchar denum_str[16] = "";
278 if(_is_int(num)) snprintf(num_str, sizeof(num_str), "%d", (int)num);
279 if(_is_int(denum)) snprintf(denum_str, sizeof(denum_str), "%d", (int)denum);
280
281 if(IS_NULL_PTR(pdiv))
282 {
283 g_strlcat(scale_buf, num_str[0] ? num_str : scale_str, sizeof(scale_buf));
284 }
285 else if(pdiv == scale_str)
286 {
287 if(denum_str[0])
288 g_strlcat(scale_buf, denum_str, sizeof(scale_buf));
289 else
290 {
291 g_strlcat(scale_buf, "1/", sizeof(scale_buf));
292 g_strlcat(scale_buf, pdiv + 1, sizeof(scale_buf));
293 }
294 }
295 else
296 {
297 if(num_str[0])
298 g_strlcat(scale_buf, num_str, sizeof(scale_buf));
299 else
300 {
301 // the numerator alone, not the whole "num/denum" string
302 gchar *num_part = g_strndup(scale_str, pdiv - scale_str);
303 g_strlcat(scale_buf, num_part, sizeof(scale_buf));
304 dt_free(num_part);
305 }
306 g_strlcat(scale_buf, "/", sizeof(scale_buf));
307 g_strlcat(scale_buf, denum_str[0] ? denum_str : pdiv + 1, sizeof(scale_buf));
308 }
309 dt_conf_set_string(CONFIG_PREFIX "resizing_factor", scale_buf);
310
311 dt_free(scale_str);
312}
313
315{
316 /* write current history changes so nothing gets lost,
317 do that only in the darkroom as there is nothing to be saved
318 when in the lighttable (and it would write over current history stack) */
319 char style[128] = { 0 };
320
321 // get the format_name and storage_name settings which are plug-ins name and not necessary what is displayed on the combobox.
322 // note that we cannot take directly the combobox entry index as depending on the storage some format are not listed.
323 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
324 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
325 const int format_index = dt_imageio_get_index_of_format(dt_imageio_get_format_by_name(format_name));
326 const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(storage_name));
327
328 if(format_index == -1)
329 {
330 dt_control_log("invalid format for export selected");
331 return;
332 }
333 if(storage_index == -1)
334 {
335 dt_control_log("invalid storage for export selected");
336 return;
337 }
338
339 char *confirm_message = NULL;
341 if(mstorage->ask_user_confirmation)
342 confirm_message = mstorage->ask_user_confirmation(mstorage);
343 if(confirm_message)
344 {
345 const GtkWidget *win = dt_gui_main_window();
346 GtkWidget *dialog = gtk_message_dialog_new(
347 GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
348 "%s", confirm_message);
349#ifdef GDK_WINDOWING_QUARTZ
351#endif
352
353 gtk_window_set_title(GTK_WINDOW(dialog), _("export to disk"));
354 const gint res = gtk_dialog_run(GTK_DIALOG(dialog));
355 GtkWindow *dialog_parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
356 gtk_widget_destroy(dialog);
357 dt_gui_refocus_parent(dialog_parent);
358 dt_free(confirm_message);
359
360 if(res != GTK_RESPONSE_YES)
361 {
362 return;
363 }
364 }
365
366 // the size the selected mode asks for, resolved now: the job keeps it whatever happens to the settings
367 dt_control_export_request_t request = { .format_index = format_index, .storage_index = storage_index };
368 _resolve_export_size(&request.max_width, &request.max_height, &request.scale_factor);
369
370 request.export_masks = dt_conf_get_bool(CONFIG_PREFIX "export_masks");
371 const char *tmp = dt_conf_get_string_const(CONFIG_PREFIX "style");
372 if(tmp)
373 {
374 g_strlcpy(style, tmp, sizeof(style));
375 }
376 request.style = style;
377
379 gchar *icc_filename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
380 request.icc_filename = icc_filename;
381 request.icc_intent = dt_conf_get_int(CONFIG_PREFIX "iccintent");
382 request.metadata_export = d->metadata_export;
383
384 GList *list = dt_act_on_get_images();
385 dt_control_export(list, &request);
386
387 dt_free(icc_filename);
388
389 _scale_optim();
390 gtk_entry_set_text(GTK_ENTRY(d->scale), dt_conf_get_string_const(CONFIG_PREFIX "resizing_factor"));
391}
392
393static void _scale_changed(GtkEntry *spin, dt_lib_export_t *d)
394{
395 const char *validSign = ",.0123456789";
396 const gchar *value = gtk_entry_get_text(spin);
397
398 char new_value[30] = "";
399 // each character read writes at most one: keep room for the terminator
400 const int len = MIN((int)strlen(value), (int)sizeof(new_value) - 1);
401 int i, j = 0, idec = 0, idiv = 0, pdiv = 0;
402
403 for (i = 0; i < len; i++)
404 {
405 char *val = strchr(validSign, value[i]);
406 if(IS_NULL_PTR(val))
407 {
408 if(idiv==0)
409 {
410 if(i == 0)
411 {
412 new_value[j++] = '1';
413 }
414 else
415 {
416 if(atof(value) == 0.0)
417 {
418 new_value[0] = '1';
419 }
420 idec = 0;
421 idiv = 1;
422 new_value[j++] = '/';
423 pdiv = j;
424 }
425 }
426 }
427 else if((val[0] == '.') || (val[0] == ','))
428 {
429 if(idec == 0)
430 {
431 if((i == 0) || (i == pdiv))
432 {
433 new_value[j++] = '0';
434 }
435 else
436 {
437 idec = 1;
438 new_value[j++] = value[i];
439 }
440 }
441 }
442 else if(value[i] == '\0')
443 {
444 break;
445 }
446 else
447 {
448 new_value[j++] = value[i];
449 }
450 }
451 dt_conf_set_string(CONFIG_PREFIX "resizing_factor", new_value);
452 gtk_entry_set_text(spin, new_value);
453}
454
455/* A middle click resets a size entry to the value that means "no limit"; the entry's own "changed"
456 * handler stores it. The click is taken, or GTK would paste the primary selection right after the
457 * reset. Any other click is the entry's own: it is how it gets the focus and the cursor. */
458static gboolean _size_entry_reset_click(GtkEntry *entry, const GdkEventButton *event, gpointer reset_text)
459{
460 if(event->button != 2) return FALSE;
461 gtk_entry_set_text(entry, (const char *)reset_text);
462 return TRUE;
463}
464
466{
467 const dt_dimensions_type_t d_type = _dimensions_type();
468
469 if((d_type == DT_DIMENSIONS_SCALE) || (d_type == DT_DIMENSIONS_PIXELS) || (d_type == DT_DIMENSIONS_ORIGINAL))
470 {
471 gtk_widget_hide(d->size_in_px);
472 }
473 else
474 {
475 gtk_widget_show(d->size_in_px);
476 int max_width = 0;
477 int max_height = 0;
478 double scale_factor = 0.0;
479 _resolve_export_size(&max_width, &max_height, &scale_factor);
480 gchar width_txt[16];
481 gchar height_txt[16];
482 snprintf(width_txt, sizeof(width_txt), "%d", max_width);
483 snprintf(height_txt, sizeof(height_txt), "%d", max_height);
484 gchar size_in_px_txt[120];
485 snprintf(size_in_px_txt, sizeof(size_in_px_txt), _("which is equal to %s \303\227 %s px"), width_txt, height_txt);
486 gtk_label_set_text(GTK_LABEL(d->size_in_px), size_in_px_txt);
487 }
488}
489
490void _set_dimensions(dt_lib_export_t *d, uint32_t max_width, uint32_t max_height)
491{
492 gchar *max_width_char = g_strdup_printf("%d", max_width);
493 gchar *max_height_char = g_strdup_printf("%d", max_height);
494
496 gtk_entry_set_text(GTK_ENTRY(d->width), max_width_char);
497 gtk_entry_set_text(GTK_ENTRY(d->height), max_height_char);
500
501 dt_conf_set_int(CONFIG_PREFIX "width", max_width);
502 dt_conf_set_int(CONFIG_PREFIX "height", max_height);
503
504 dt_free(max_width_char);
505 dt_free(max_height_char);
506}
507
508
510{
512
513 gtk_widget_set_visible(self->px_size, d_type == DT_DIMENSIONS_PIXELS);
514 gtk_widget_set_visible(self->print_size, d_type == DT_DIMENSIONS_CM || d_type == DT_DIMENSIONS_INCH);
515 gtk_widget_set_visible(self->scale, d_type == DT_DIMENSIONS_SCALE);
516
517 gtk_label_set_text(GTK_LABEL(self->unit_label),
518 d_type == DT_DIMENSIONS_CM ? _("cm") : C_("unit", "in"));
519 // the print size is stored in cm and shown in the unit just selected
520 if(d_type == DT_DIMENSIONS_CM || d_type == DT_DIMENSIONS_INCH) _print_size_display(self);
521 _size_in_px_update(self);
522}
523
525{
526 // make sure we don't do anything useless:
527 if(!dt_control_running()) return;
529 gtk_entry_set_text(GTK_ENTRY(d->width), dt_confgen_get(CONFIG_PREFIX "width", DT_DEFAULT));
530 gtk_entry_set_text(GTK_ENTRY(d->height), dt_confgen_get(CONFIG_PREFIX "height", DT_DEFAULT));
533 dt_bauhaus_combobox_set(d->dimensions_type, dt_confgen_get_int(CONFIG_PREFIX "dimensions_type", DT_DEFAULT));
535
536 // Set storage
538 dt_bauhaus_combobox_set(d->storage, storage_index);
539
540 dt_bauhaus_combobox_set(d->export_masks, dt_confgen_get_bool(CONFIG_PREFIX "export_masks", DT_DEFAULT) ? 1 : 0);
541
543
544 // iccprofile
545 const int icctype = dt_confgen_get_int(CONFIG_PREFIX "icctype", DT_DEFAULT);
546 gchar *iccfilename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
547 dt_bauhaus_combobox_set(d->profile, 0);
548 if(icctype != DT_COLORSPACE_NONE)
549 {
550 // +1: the combo carries "same as original" ahead of the profiles.
551 const int pos = dt_colorspaces_profile_index(DT_PROFILE_ROLE_OUTPUT, icctype, iccfilename);
552 if(pos > -1) dt_bauhaus_combobox_set(d->profile, pos + 1);
553 }
554
555 dt_free(iccfilename);
556
557 // style
558 // set it to none if the var is not set or the style doesn't exist anymore
559 gboolean rc = FALSE;
560 const char *style = dt_confgen_get(CONFIG_PREFIX "style", DT_DEFAULT);
561 if(!IS_NULL_PTR(style) && strlen(style) > 0)
562 {
563 rc = dt_bauhaus_combobox_set_from_text(d->style, style);
564 if(rc == FALSE) dt_bauhaus_combobox_set(d->style, 0);
565 }
566 else
567 dt_bauhaus_combobox_set(d->style, 0);
568
569 // export metadata presets
570 dt_free(d->metadata_export);
571 d->metadata_export = dt_lib_export_metadata_get_conf();
572
574 if(mformat) mformat->gui_reset(mformat);
576 if(mstorage) mstorage->gui_reset(mstorage);
577
578 _update(self);
579}
580
581static void set_format_by_name(dt_lib_export_t *d, const char *name)
582{
583 // Find the selected format plugin among all existing plugins
584 dt_imageio_module_format_t *module = NULL;
585 for(GList *it = dt_imageio_get_global()->plugins_format; it; it = g_list_next(it))
586 {
587 if(g_strcmp0(((dt_imageio_module_format_t *)it->data)->name(), name) == 0
588 || g_strcmp0(((dt_imageio_module_format_t *)it->data)->plugin_name, name) == 0)
589 {
590 module = (dt_imageio_module_format_t *)it->data;
591 break;
592 }
593 }
594
595 if(!module)
596 {
597 gtk_widget_hide(d->format_extra_container);
598 return;
599 }
600 else if(module->widget)
601 {
602 gtk_widget_show_all(d->format_extra_container);
603 gtk_stack_set_visible_child(GTK_STACK(d->format_extra_container), module->widget);
604 }
605 else
606 {
607 gtk_widget_hide(d->format_extra_container);
608 }
609
610 // Store the new format
611 dt_conf_set_string(CONFIG_PREFIX "format_name", module->plugin_name);
612
613 if(dt_bauhaus_combobox_set_from_text(d->format, module->name()) == FALSE)
614 dt_bauhaus_combobox_set(d->format, 0);
615
616 // Let's also update combination of storage/format dimension restrictions
618
619 // only some modules support export of masks
620 // set it to 0 when insensitive and restore when making it sensitive again. this doesn't survive dt restarts.
621 const gboolean support_layers = (module->flags(NULL) & FORMAT_FLAGS_SUPPORT_LAYERS) == FORMAT_FLAGS_SUPPORT_LAYERS;
622 const gboolean is_enabled = gtk_widget_get_sensitive(d->export_masks);
623 if(support_layers && !is_enabled)
624 {
625 // combobox was disabled and shall be enabled. we want to restore the old setting.
626 const gboolean export_masks = dt_conf_get_bool(CONFIG_PREFIX "export_masks");
627 gtk_widget_set_sensitive(GTK_WIDGET(d->export_masks), TRUE);
628 dt_bauhaus_combobox_set(d->export_masks, export_masks ? 1 : 0);
629 }
630 else if(!support_layers && is_enabled)
631 {
632 // combobox was enabled but shall be disabled. we want to save the current setting.
633 const int export_masks = dt_bauhaus_combobox_get(d->export_masks);
634 dt_bauhaus_combobox_set(d->export_masks, 0);
635 dt_conf_set_bool(CONFIG_PREFIX "export_masks", export_masks == 1);
636 gtk_widget_set_sensitive(GTK_WIDGET(d->export_masks), FALSE);
637 }
638}
639
641{
642 const gchar *name = dt_bauhaus_combobox_get_text(d->format);
643 g_signal_handlers_block_by_func(widget, _format_changed, d);
645 g_signal_handlers_unblock_by_func(widget, _format_changed, d);
646}
647
648static void _get_max_output_dimension(dt_lib_export_t *d, uint32_t *width, uint32_t *height)
649{
650 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
652
653 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
655
656 if(storage && format)
657 {
658 uint32_t fw, fh, sw, sh;
659 fw = fh = sw = sh = 0; // We are all equals!!!
660 storage->dimension(storage, NULL, &sw, &sh);
661 format->dimension(format, NULL, &fw, &fh);
662
663 if(sw == 0 || fw == 0)
664 *width = sw > fw ? sw : fw;
665 else
666 *width = sw < fw ? sw : fw;
667
668 if(sh == 0 || fh == 0)
669 *height = sh > fh ? sh : fh;
670 else
671 *height = sh < fh ? sh : fh;
672 }
673}
674
676{
677 //reset dimensions to previously stored value if they exceed the maximum
678 uint32_t width = atoi(gtk_entry_get_text(GTK_ENTRY(d->width)));
679 uint32_t height = atoi(gtk_entry_get_text(GTK_ENTRY(d->height)));
680 if(width > d->max_allowed_width || height > d->max_allowed_height)
681 {
682 width = width > d->max_allowed_width ? dt_conf_get_int(CONFIG_PREFIX "width") : width;
683 height = height > d->max_allowed_height ? dt_conf_get_int(CONFIG_PREFIX "height") : height;
685 }
686}
687
689{
690 uint32_t max_w = 0, max_h = 0;
691 _get_max_output_dimension(d, &max_w, &max_h);
692 d->max_allowed_width = max_w > 0 ? max_w : EXPORT_MAX_IMAGE_SIZE;
693 d->max_allowed_height = max_h > 0 ? max_h : EXPORT_MAX_IMAGE_SIZE;
695}
696
697static void set_storage_by_name(dt_lib_export_t *d, const char *name)
698{
699 int k = -1;
700 dt_imageio_module_storage_t *module = NULL;
701
702 for(const GList *it = dt_imageio_get_global()->plugins_storage; it; it = g_list_next(it))
703 {
705 k++;
706 if(strcmp(storage->name(storage), name) == 0 || strcmp(storage->plugin_name, name) == 0)
707 {
708 module = storage;
709 break;
710 }
711 }
712
713 if(!module)
714 {
715 gtk_widget_hide(d->storage_extra_container);
716 return;
717 }
718 else if(module->widget)
719 {
720 gtk_widget_show_all(d->storage_extra_container);
721 gtk_stack_set_visible_child(GTK_STACK(d->storage_extra_container),module->widget);
722 }
723 else
724 {
725 gtk_widget_hide(d->storage_extra_container);
726 }
727 dt_bauhaus_combobox_set(d->storage, k);
728 dt_conf_set_string(CONFIG_PREFIX "storage_name", module->plugin_name);
729
730
731 // Check if plugin recommends a max dimension and set
732 // if not implemented the stored conf values are used..
733 uint32_t w = 0, h = 0;
734 module->recommended_dimension(module, NULL, &w, &h);
735
736 const uint32_t cw = dt_conf_get_int(CONFIG_PREFIX "width");
737 const uint32_t ch = dt_conf_get_int(CONFIG_PREFIX "height");
738
739 // If user's selected value is below the max, select it
740 if(w > cw || w == 0) w = cw;
741 if(h > ch || h == 0) h = ch;
742
743 // Set the recommended dimension
744 _set_dimensions(d, w, h);
745
746 // Let's update formats combobox with supported formats of selected storage module...
748
749 // Lets try to set selected format if fail select first in list..
750 const char *format_name = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
752
753 if(IS_NULL_PTR(format)
754 || dt_bauhaus_combobox_set_from_text(d->format, format->name()) == FALSE)
755 dt_bauhaus_combobox_set(d->format, 0);
756}
757
759{
760 const gchar *name = dt_bauhaus_combobox_get_text(d->storage);
761 g_signal_handlers_block_by_func(widget, _storage_changed, d);
763 g_signal_handlers_unblock_by_func(widget, _storage_changed, d);
764}
765
767{
768 int pos = dt_bauhaus_combobox_get(widget);
769 if(pos > 0)
770 {
771 pos--; // the combo carries "same as original" at 0
774 {
775 dt_conf_set_int(CONFIG_PREFIX "icctype", desc.type);
777 (desc.type == DT_COLORSPACE_FILE) ? desc.filename : "");
778 return;
779 }
780 }
782 dt_conf_set_string(CONFIG_PREFIX "iccprofile", "");
783}
784
786{
787 if(dt_gui_widgets_suppressed()) return;
788
790
791 // only the mode changes: each one keeps its own size settings
792 dt_conf_set_int(CONFIG_PREFIX "dimensions_type", d_type);
793
794 if(d_type == DT_DIMENSIONS_CM || d_type == DT_DIMENSIONS_INCH)
795 {
796 // set dpi to user-set dpi
797 dt_conf_set_int("metadata/resolution", _print_dpi());
798 }
799 else
800 {
801 // reset export dpi to default value for scale/pixel specific export
802 dt_conf_set_int("metadata/resolution", dt_confgen_get_int("metadata/resolution", DT_DEFAULT));
803 }
804
806}
807
808// show the stored print size in the unit selected, and the dpi
810{
811 const float unit = _print_unit_in_cm(_dimensions_type());
812 gchar *pwidth = g_strdup_printf("%.2f", dt_conf_get_float(CONF_PRINT_WIDTH) / unit);
813 gchar *pheight = g_strdup_printf("%.2f", dt_conf_get_float(CONF_PRINT_HEIGHT) / unit);
814 gchar *pdpi = g_strdup_printf("%d", _print_dpi());
815
817 gtk_entry_set_text(GTK_ENTRY(d->print_width), pwidth);
818 gtk_entry_set_text(GTK_ENTRY(d->print_height), pheight);
819 gtk_entry_set_text(GTK_ENTRY(d->print_dpi), pdpi);
821
822 dt_free(pwidth);
823 dt_free(pheight);
824 dt_free(pdpi);
825}
826
827static void _width_changed(GtkEditable *entry, gpointer user_data)
828{
829 if(dt_gui_widgets_suppressed()) return;
830
831 const dt_lib_export_t *d = (dt_lib_export_t *)user_data;
832 const uint32_t width = atoi(gtk_entry_get_text(GTK_ENTRY(d->width)));
834}
835
836// the print size is stored in cm, whatever unit it is typed in
837static void _print_size_changed(GtkEntry *entry, const char *key, dt_lib_export_t *d)
838{
839 const float value = atof(gtk_entry_get_text(entry));
842}
843
844static void _print_width_changed(GtkEditable *entry, gpointer user_data)
845{
846 if(dt_gui_widgets_suppressed()) return;
847 _print_size_changed(GTK_ENTRY(entry), CONF_PRINT_WIDTH, (dt_lib_export_t *)user_data);
848}
849
850static void _height_changed(GtkEditable *entry, gpointer user_data)
851{
852 if(dt_gui_widgets_suppressed()) return;
853
854 const dt_lib_export_t *d = (dt_lib_export_t *)user_data;
855 const uint32_t height = atoi(gtk_entry_get_text(GTK_ENTRY(d->height)));
857}
858
859static void _print_height_changed(GtkEditable *entry, gpointer user_data)
860{
861 if(dt_gui_widgets_suppressed()) return;
862 _print_size_changed(GTK_ENTRY(entry), CONF_PRINT_HEIGHT, (dt_lib_export_t *)user_data);
863}
864
865static void _print_dpi_changed(GtkWidget *widget, gpointer user_data)
866{
867 if(dt_gui_widgets_suppressed()) return;
868
869 dt_lib_export_t *d = (dt_lib_export_t *)user_data;
870 const int dpi = atoi(gtk_entry_get_text(GTK_ENTRY(d->print_dpi)));
871
872 dt_conf_set_int(CONFIG_PREFIX "print_dpi", dpi);
873 dt_conf_set_int("metadata/resolution", dpi);
874
875 // the print size stays what it is: its pixel equivalent is what moves
877}
878
879static void _callback_bool(GtkWidget *widget, gpointer user_data)
880{
881 const char *key = (const char *)user_data;
883}
884
886{
887 const int pos = dt_bauhaus_combobox_get(widget);
888 dt_conf_set_int(CONFIG_PREFIX "iccintent", pos - 1);
889}
890
892{
893 if(dt_bauhaus_combobox_get(d->style) == 0)
894 {
896 }
897 else
898 {
899 const gchar *style = dt_bauhaus_combobox_get_text(d->style);
900 dt_conf_set_string(CONFIG_PREFIX "style", style);
901 }
902}
903
905{
906 return 99;
907}
908
910{
911 // Clear format combo box
913
914 // Get current selected storage
915 const char *storage_name = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
917
918 // Add supported formats to combobox
919 gboolean empty = TRUE;
920 for(const GList *it = dt_imageio_get_global()->plugins_format; it; it = g_list_next(it))
921 {
923 if(storage->supported(storage, format))
924 {
925 dt_bauhaus_combobox_add(d->format, format->name());
926 empty = FALSE;
927 }
928 }
929
930 gtk_widget_set_sensitive(GTK_WIDGET(d->format), !empty);
931}
932
933static void _on_storage_list_changed(gpointer instance, dt_lib_module_t *self)
934{
935 dt_lib_export_t *d = self->data;
938
939 dt_gui_container_remove_children(GTK_CONTAINER(d->storage_extra_container));
940
941 for(const GList *it = dt_imageio_get_global()->plugins_storage; it; it = g_list_next(it))
942 {
943 const dt_imageio_module_storage_t *module = (dt_imageio_module_storage_t *)it->data;
944 dt_bauhaus_combobox_add(d->storage, module->name(module));
945 if(module->widget)
946 {
947 gtk_container_add(GTK_CONTAINER(d->storage_extra_container), module->widget);
948 }
949 }
951}
952
953static void _lib_export_styles_changed_callback(gpointer instance, gpointer user_data)
954{
955 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
956 dt_lib_export_t *d = self->data;
957
959 dt_bauhaus_combobox_add(d->style, _("none"));
960
961 GList *styles = dt_styles_get_list("");
962 for(const GList *st_iter = styles; st_iter; st_iter = g_list_next(st_iter))
963 {
964 const dt_style_t *style = (dt_style_t *)st_iter->data;
965 dt_bauhaus_combobox_add(d->style, style->name);
966 }
967 dt_bauhaus_combobox_set(d->style, 0);
968
969 g_list_free_full(styles, dt_style_free);
970 styles = NULL;
971}
972
973void _menuitem_preferences(GtkMenuItem *menuitem, dt_lib_module_t *self)
974{
976 const gchar *name = dt_bauhaus_combobox_get_text(d->storage);
977 const gboolean ondisk = name && !g_strcmp0(name, _("file on disk")); // FIXME: NO!!!!!one!
978 d->metadata_export = dt_lib_export_metadata_configuration_dialog(d->metadata_export, ondisk);
979}
980
981void set_preferences(void *menu, dt_lib_module_t *self)
982{
983 GtkWidget *mi = gtk_menu_item_new_with_label(_("preferences..."));
984 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(_menuitem_preferences), self);
985 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
986}
987
989{
990 dt_lib_export_t *d = (dt_lib_export_t *)malloc(sizeof(dt_lib_export_t));
991 self->timeout_handle = 0;
992 self->data = (void *)d;
993 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
994
995 GtkWidget *label = dt_ui_section_label_new(_("Storage options"));
996 gtk_box_pack_start(GTK_BOX(self->widget), label, FALSE, TRUE, 0);
997
999 dt_bauhaus_widget_set_label(d->storage, N_("Target storage"));
1000 gtk_box_pack_start(GTK_BOX(self->widget), d->storage, FALSE, TRUE, 0);
1001
1002 // add all storage widgets to the stack widget
1003 d->storage_extra_container = gtk_stack_new();
1004 gtk_stack_set_homogeneous(GTK_STACK(d->storage_extra_container),FALSE);
1005 gtk_box_pack_start(GTK_BOX(self->widget), d->storage_extra_container, FALSE, TRUE, 0);
1006 for(const GList *it = dt_imageio_get_global()->plugins_storage; it; it = g_list_next(it))
1007 {
1008 const dt_imageio_module_storage_t *module = (dt_imageio_module_storage_t *)it->data;
1009 dt_bauhaus_combobox_add(d->storage, module->name(module));
1010 if(module->widget)
1011 {
1012 gtk_container_add(GTK_CONTAINER(d->storage_extra_container), module->widget);
1013 }
1014 }
1015
1016 // postponed so we can do the two steps in one loop
1018 G_CALLBACK(_on_storage_list_changed), self);
1019 g_signal_connect(G_OBJECT(d->storage), "value-changed", G_CALLBACK(_storage_changed), (gpointer)d);
1020
1021 label = dt_ui_section_label_new(_("format options"));
1022 gtk_box_pack_start(GTK_BOX(self->widget), label, FALSE, TRUE, 0);
1023
1025 dt_bauhaus_widget_set_label(d->format, N_("file format"));
1026 gtk_box_pack_start(GTK_BOX(self->widget), d->format, FALSE, TRUE, 0);
1027 g_signal_connect(G_OBJECT(d->format), "value-changed", G_CALLBACK(_format_changed), (gpointer)d);
1028
1029 // add all format widgets to the stack widget
1030 d->format_extra_container = gtk_stack_new();
1031 gtk_stack_set_homogeneous(GTK_STACK(d->format_extra_container),FALSE);
1032 gtk_box_pack_start(GTK_BOX(self->widget), d->format_extra_container, FALSE, TRUE, 0);
1033 for(const GList *it = dt_imageio_get_global()->plugins_format; it; it = g_list_next(it))
1034 {
1035 const dt_imageio_module_format_t *module = (dt_imageio_module_format_t *)it->data;
1036 dt_bauhaus_combobox_add(d->format, module->name());
1037 if(module->widget)
1038 {
1039 gtk_container_add(GTK_CONTAINER(d->format_extra_container), module->widget);
1040 }
1041 }
1042
1043 label = dt_ui_section_label_new(_("global options"));
1044 gtk_box_pack_start(GTK_BOX(self->widget), label, FALSE, TRUE, 0);
1045
1046 DT_BAUHAUS_COMBOBOX_NEW_FULL(dt_bauhaus_get_global(), d->dimensions_type, NULL, N_("set size (bounding box)"),
1047 _("Choose a method for setting the output size.\n"
1048 "The width and height specified define the bounding box\n"
1049 "in which the image will be proportionnaly fitted.\n"),
1050 dt_conf_get_int(CONFIG_PREFIX "dimensions_type"),
1051 (GtkCallback)_dimensions_type_changed, d,
1052 N_("in pixels (for file)"),
1053 N_("in cm (for print)"),
1054 N_("in inch (for print)"),
1055 N_("by scale (for file)"),
1056 N_("original resolution"));
1057
1058 d->print_width = gtk_entry_new();
1060 gtk_widget_set_tooltip_text(d->print_width, _("maximum output width limit.\n"
1061 "click middle mouse button to reset to 0."));
1062 gtk_entry_set_width_chars(GTK_ENTRY(d->print_width), 5);
1063 d->print_height = gtk_entry_new();
1065 gtk_widget_set_tooltip_text(d->print_height, _("maximum output height limit.\n"
1066 "click middle mouse button to reset to 0."));
1067 gtk_entry_set_width_chars(GTK_ENTRY(d->print_height), 5);
1068 d->print_dpi = gtk_entry_new();
1070 gtk_widget_set_tooltip_text(d->print_dpi, _("resolution in dot per inch"));
1071 gtk_entry_set_width_chars(GTK_ENTRY(d->print_dpi), 4);
1072 const char *dpi = dt_conf_get_string_const(CONFIG_PREFIX "print_dpi");
1073 gtk_entry_set_text(GTK_ENTRY(d->print_dpi), dpi);
1074
1075
1076 d->width = gtk_entry_new();
1078 gtk_widget_set_tooltip_text(d->width, _("maximum output width limit.\n"
1079 "click middle mouse button to reset to 0."));
1080 gtk_entry_set_width_chars(GTK_ENTRY(d->width), 5);
1081 d->height = gtk_entry_new();
1083 gtk_widget_set_tooltip_text(d->height, _("maximum output height limit.\n"
1084 "click middle mouse button to reset to 0."));
1085 gtk_entry_set_width_chars(GTK_ENTRY(d->height), 5);
1086
1087 gtk_widget_add_events(d->width, GDK_BUTTON_PRESS_MASK);
1088 gtk_widget_add_events(d->height, GDK_BUTTON_PRESS_MASK);
1089
1090
1091 d->print_size = gtk_flow_box_new();
1092 gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(d->print_size), 5);
1093 gtk_flow_box_set_column_spacing (GTK_FLOW_BOX(d->print_size), 3);
1094 gtk_container_add(GTK_CONTAINER(d->print_size), d->print_width);
1095 gtk_container_add(GTK_CONTAINER(d->print_size), gtk_label_new(_("x")));
1096 gtk_container_add(GTK_CONTAINER(d->print_size), d->print_height);
1097 d->unit_label = gtk_label_new(_("cm"));
1098 gtk_container_add(GTK_CONTAINER(d->print_size), d->unit_label);
1099 GtkBox *dpi_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
1100 gtk_box_pack_start(dpi_box, gtk_label_new(_("@")), FALSE, FALSE, 0);
1101 gtk_box_pack_start(dpi_box, d->print_dpi, TRUE, TRUE, 0);
1102 gtk_box_pack_start(dpi_box, gtk_label_new(_("dpi")), FALSE, FALSE, 0);
1103 gtk_container_add(GTK_CONTAINER(d->print_size), GTK_WIDGET(dpi_box));
1104 dt_gui_flow_box_as_layout(GTK_FLOW_BOX(d->print_size));
1105
1106 d->px_size = gtk_flow_box_new();
1107 gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(d->px_size), 3);
1108 gtk_flow_box_set_column_spacing (GTK_FLOW_BOX(d->px_size), 3);
1109 gtk_container_add(GTK_CONTAINER(d->px_size), d->width);
1110 gtk_container_add(GTK_CONTAINER(d->px_size), gtk_label_new(_("x")));
1111 GtkBox *px_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
1112 gtk_box_pack_start(px_box, d->height, TRUE, TRUE, 0);
1113 gtk_box_pack_start(px_box, gtk_label_new(_("px")), FALSE, FALSE, 0);
1114 gtk_container_add(GTK_CONTAINER(d->px_size), GTK_WIDGET(px_box));
1115 dt_gui_flow_box_as_layout(GTK_FLOW_BOX(d->px_size));
1116
1117 d->scale = gtk_entry_new();
1119 gtk_entry_set_width_chars(GTK_ENTRY(d->scale), 5);
1120 gtk_entry_set_text (GTK_ENTRY(d->scale), dt_conf_get_string_const(CONFIG_PREFIX "resizing_factor"));
1121 gtk_widget_set_tooltip_text(d->scale, _("it can be an integer, decimal number or simple fraction.\n"
1122 "zero or empty values are equal to 1.\n"
1123 "click middle mouse button to reset to 1."));
1124 gtk_widget_set_halign(GTK_WIDGET(d->scale), GTK_ALIGN_END);
1125 gtk_widget_add_events(d->scale, GDK_BUTTON_PRESS_MASK);
1126
1127 d->size_in_px = gtk_label_new("");
1128 gtk_label_set_ellipsize(GTK_LABEL(d->size_in_px), PANGO_ELLIPSIZE_START);
1129 gtk_widget_set_sensitive(GTK_WIDGET(d->size_in_px), FALSE);
1130
1131 gtk_widget_set_halign(GTK_WIDGET(d->scale), GTK_ALIGN_FILL);
1132 gtk_widget_set_halign(GTK_WIDGET(d->size_in_px), GTK_ALIGN_END);
1133
1134 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->dimensions_type), FALSE, FALSE, 0);
1135 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->px_size), FALSE, FALSE, 0);
1136 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->print_size), FALSE, FALSE, 0);
1137 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->scale), FALSE, FALSE, 0);
1138 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->size_in_px), FALSE, FALSE, 0);
1139
1141 dt_bauhaus_widget_set_label(d->export_masks, N_("store masks"));
1142 dt_bauhaus_combobox_add(d->export_masks, _("no"));
1143 dt_bauhaus_combobox_add(d->export_masks, _("yes"));
1144 gtk_widget_set_tooltip_text(d->export_masks, _("store masks as layers in exported images. only works for some formats."));
1145 gtk_box_pack_start(GTK_BOX(self->widget), d->export_masks, FALSE, TRUE, 0);
1146
1147 // Add profile combo
1148
1149 char datadir[DT_PATH_MAX] = { 0 };
1150 char confdir[DT_PATH_MAX] = { 0 };
1151 dt_loc_get_user_config_dir(confdir, sizeof(confdir));
1152 dt_loc_get_datadir(datadir, sizeof(datadir));
1153
1155 dt_bauhaus_widget_set_label(d->profile, N_("Color space"));
1156 gtk_box_pack_start(GTK_BOX(self->widget), d->profile, FALSE, TRUE, 0);
1157 dt_bauhaus_combobox_add(d->profile, _("same as original"));
1158 dt_colorprofile_desc_t *out_profiles = NULL;
1159 const size_t n_out_profiles = dt_colorspaces_enumerate_profiles(DT_PROFILE_ROLE_OUTPUT, &out_profiles);
1160 for(size_t k = 0; k < n_out_profiles; k++)
1161 dt_bauhaus_combobox_add(d->profile, out_profiles[k].name);
1162 dt_free_align(out_profiles);
1163
1164 dt_bauhaus_combobox_set(d->profile, 1);
1165
1166 char *system_profile_dir = g_build_filename(datadir, "color", "out", NULL);
1167 char *user_profile_dir = g_build_filename(confdir, "color", "out", NULL);
1168 char *tooltip = g_strdup_printf(_(
1169 "RGB color space used to encode the output files.\n"
1170 "\"same as original\" exports to the same space as the original\n"
1171 "picture, which is linear sensor RGB for raw pictures.\n"
1172 "sRGB is recommended in most cases.\n"
1173 "You can use custom color spaces defined\n"
1174 "by ICC profiles stored in:\n- %s\n- %s"), user_profile_dir, system_profile_dir);
1175 gtk_widget_set_tooltip_text(d->profile, tooltip);
1176 dt_free(system_profile_dir);
1177 dt_free(user_profile_dir);
1179
1180 // Add intent combo
1181
1183 dt_bauhaus_widget_set_label(d->intent, N_("intent"));
1184 dt_bauhaus_combobox_add(d->intent, _("same as original"));
1185 dt_bauhaus_combobox_add(d->intent, _("perceptual"));
1186 dt_bauhaus_combobox_add(d->intent, _("relative colorimetric"));
1187 dt_bauhaus_combobox_add(d->intent, C_("rendering intent", "saturation"));
1188 dt_bauhaus_combobox_add(d->intent, _("absolute colorimetric"));
1189 gtk_box_pack_start(GTK_BOX(self->widget), d->intent, FALSE, TRUE, 0);
1190
1191 tooltip = g_strdup_printf(_("• perceptual: "
1192 "smoothly moves out-of-gamut colors into gamut,"
1193 "preserving gradations, but distorts in-gamut colors in the process."
1194 " note that perceptual is often a proprietary LUT that depends"
1195 " on the destination space."
1196 "\n\n"
1197
1198 "• relative colorimetric: "
1199 "keeps luminance while reducing as little as possible saturation"
1200 " until colors fit in gamut."
1201 "\n\n"
1202
1203 "• saturation: "
1204 "designed to present eye-catching business graphics"
1205 " by preserving the saturation. (not suited for photography)."
1206 "\n\n"
1207
1208 "• absolute colorimetric: "
1209 "adapt white point of the image to the white point of the"
1210 " destination medium and do nothing else. mainly used when"
1211 " proofing colors. (not suited for photography)."
1212 ""
1213 ));
1214 gtk_widget_set_tooltip_text(d->intent, tooltip);
1216
1217 // Add style combo
1218
1220 dt_bauhaus_widget_set_label(d->style, N_("style"));
1222 gtk_box_pack_start(GTK_BOX(self->widget), d->style, FALSE, TRUE, 0);
1223 gtk_widget_set_tooltip_text(d->style, _("temporary style to use while exporting"));
1224
1225 // Set callback signals
1226 g_signal_connect(G_OBJECT(d->export_masks), "value-changed", G_CALLBACK(_callback_bool),
1227 (gpointer)CONFIG_PREFIX "export_masks");
1228 g_signal_connect(G_OBJECT(d->intent), "value-changed", G_CALLBACK(_intent_changed), (gpointer)d);
1229 g_signal_connect(G_OBJECT(d->profile), "value-changed", G_CALLBACK(_profile_changed), (gpointer)d);
1230 g_signal_connect(G_OBJECT(d->style), "value-changed", G_CALLBACK(_style_changed), (gpointer)d);
1231
1233 G_CALLBACK(_lib_export_styles_changed_callback), self);
1234
1235 GtkBox *hbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
1236 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox), FALSE, TRUE, 0);
1237
1238 // Export button
1239 d->export_button = GTK_BUTTON(dt_action_button_new(self, N_("export"), _export_button_clicked, d,
1240 _("export with current settings"), GDK_KEY_e, DT_PRIMARY_MASK));
1241 gtk_box_pack_start(hbox, GTK_WIDGET(d->export_button), TRUE, TRUE, 0);
1242
1243 g_signal_connect(G_OBJECT(d->width), "changed", G_CALLBACK(_width_changed), (gpointer)d);
1244 g_signal_connect(G_OBJECT(d->height), "changed", G_CALLBACK(_height_changed), (gpointer)d);
1245 g_signal_connect(G_OBJECT(d->print_width), "changed", G_CALLBACK(_print_width_changed), (gpointer)d);
1246 g_signal_connect(G_OBJECT(d->print_height), "changed", G_CALLBACK(_print_height_changed), (gpointer)d);
1247 g_signal_connect(G_OBJECT(d->print_dpi), "changed", G_CALLBACK(_print_dpi_changed), (gpointer)d);
1248
1249 g_signal_connect(G_OBJECT(d->width), "button-press-event", G_CALLBACK(_size_entry_reset_click), (gpointer)"0");
1250 g_signal_connect(G_OBJECT(d->height), "button-press-event", G_CALLBACK(_size_entry_reset_click), (gpointer)"0");
1251 g_signal_connect(G_OBJECT(d->print_width), "button-press-event", G_CALLBACK(_size_entry_reset_click), (gpointer)"0");
1252 g_signal_connect(G_OBJECT(d->print_height), "button-press-event", G_CALLBACK(_size_entry_reset_click), (gpointer)"0");
1253
1254 g_signal_connect(G_OBJECT(d->scale), "button-press-event", G_CALLBACK(_size_entry_reset_click), (gpointer)"1");
1255 g_signal_connect(G_OBJECT(d->scale), "changed", G_CALLBACK(_scale_changed), (gpointer)d);
1256
1257 // this takes care of keeping hidden widgets hidden
1258 gtk_widget_show_all(self->widget);
1259 gtk_widget_set_no_show_all(self->widget, TRUE);
1260
1261 const char* setting = dt_conf_get_string_const(CONFIG_PREFIX "width");
1262 gtk_entry_set_text(GTK_ENTRY(d->width), setting);
1263 setting = dt_conf_get_string_const(CONFIG_PREFIX "height");
1264 gtk_entry_set_text(GTK_ENTRY(d->height), setting);
1265
1268
1269 // Set storage
1270 setting = dt_conf_get_string_const(CONFIG_PREFIX "storage_name");
1271 const int storage_index = dt_imageio_get_index_of_storage(dt_imageio_get_storage_by_name(setting));
1272 dt_bauhaus_combobox_set(d->storage, storage_index);
1273
1274 // Set format
1275 setting = dt_conf_get_string_const(CONFIG_PREFIX "format_name");
1276 const int format_index = dt_imageio_get_index_of_format(dt_imageio_get_format_by_name(setting));
1277 dt_bauhaus_combobox_set(d->format, format_index);
1278
1279 dt_bauhaus_combobox_set(d->export_masks, dt_conf_get_bool(CONFIG_PREFIX "export_masks") ? 1 : 0);
1280
1281 dt_bauhaus_combobox_set(d->intent, dt_conf_get_int(CONFIG_PREFIX "iccintent") + 1);
1282
1283 // iccprofile
1284 const int icctype = dt_conf_get_int(CONFIG_PREFIX "icctype");
1285 gchar *iccfilename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
1286 dt_bauhaus_combobox_set(d->profile, 0);
1287
1288 if(icctype != DT_COLORSPACE_NONE)
1289 {
1290 // +1: the combo carries "same as original" ahead of the profiles.
1291 const int pos = dt_colorspaces_profile_index(DT_PROFILE_ROLE_OUTPUT, icctype, iccfilename);
1292 if(pos > -1) dt_bauhaus_combobox_set(d->profile, pos + 1);
1293 }
1294
1295 dt_free(iccfilename);
1296
1297 // style
1298 // set it to none if the var is not set or the style doesn't exist anymore
1299 gboolean rc = FALSE;
1300 setting = dt_conf_get_string_const(CONFIG_PREFIX "style");
1301 if(!IS_NULL_PTR(setting) && strlen(setting) > 0)
1302 {
1303 rc = dt_bauhaus_combobox_set_from_text(d->style, setting);
1304 if(rc == FALSE)
1305 dt_bauhaus_combobox_set(d->style, 0);
1306 }
1307 else
1308 dt_bauhaus_combobox_set(d->style, 0);
1309
1310 // export metadata presets
1311 d->metadata_export = dt_lib_export_metadata_get_conf();
1312
1314 G_CALLBACK(_image_selection_changed_callback), self);
1316 G_CALLBACK(_collection_updated_callback), self);
1317}
1318
1320{
1321 if(IS_NULL_PTR(self->data)) return;
1324
1327
1330
1331 for(const GList *it = dt_imageio_get_global()->plugins_storage; it; it = g_list_next(it))
1332 {
1333 dt_imageio_module_storage_t *module = (dt_imageio_module_storage_t *)it->data;
1334 if(module->widget && GTK_IS_CONTAINER(d->storage_extra_container)) gtk_container_remove(GTK_CONTAINER(d->storage_extra_container), module->widget);
1335 }
1336
1337 for(const GList *it = dt_imageio_get_global()->plugins_format; it; it = g_list_next(it))
1338 {
1339 dt_imageio_module_format_t *module = (dt_imageio_module_format_t *)it->data;
1340 if(module->widget && GTK_IS_CONTAINER(d->format_extra_container)) gtk_container_remove(GTK_CONTAINER(d->format_extra_container), module->widget);
1341 }
1342
1343
1344 dt_free(d->metadata_export);
1345
1346 dt_free(self->data);
1347}
1348
1350{
1351 // TODO: store presets in db:
1352 // dt_lib_presets_add(const char *name, const char *plugin_name, const void *params, const int32_t
1353 // params_size)
1354
1355
1356 // I know that it is super ugly to have this inside a module, but then is export not your average module
1357 // since it handles the params blobs of imageio libs.
1358 // - get all existing presets for export from db,
1359 // - extract the versions of the embedded format/storage blob
1360 // - check if it's up to date
1361 // - if older than the module -> call its legacy_params and update the preset
1362 // - drop presets that cannot be updated
1363
1364 const int version = self->version();
1365
1366 // The whole result set is materialised before the first write. This loop DELETEs and UPDATEs
1367 // data.presets, and it used to do so while its own cursor was still scanning that table.
1368 GList *presets = dt_preset_repository_list_all_versions("export");
1369 for(GList *l = presets; l; l = g_list_next(l))
1370 {
1371 const dt_module_preset_t *p = (const dt_module_preset_t *)l->data;
1372 const int rowid = p->rowid;
1373 const int op_version = p->op_version;
1374 const void *op_params = p->op_params;
1375 const size_t op_params_size = p->op_params_size;
1376 const char *name = p->name;
1377
1378 if(op_version != version)
1379 {
1380 // shouldn't happen, we run legacy_params on the lib level before calling this
1381 fprintf(stderr, "[export_init_presets] found export preset '%s' with version %d, version %d was "
1382 "expected. dropping preset.\n",
1383 name, op_version, version);
1385 }
1386 else
1387 {
1388 // extract the interesting parts from the blob
1389 const char *buf = (const char *)op_params;
1390
1391 // skip 6*int32_t: max_width, max_height, upscale, high_quality and iccintent, icctype
1392 buf += 6 * sizeof(int32_t);
1393 // skip metadata presets string
1394 buf += strlen(buf) + 1;
1395 // next skip iccfilename
1396 buf += strlen(buf) + 1;
1397
1398 // parse both names to '\0'
1399 const char *fname = buf;
1400 buf += strlen(fname) + 1;
1401 const char *sname = buf;
1402 buf += strlen(sname) + 1;
1403
1404 // get module by name and skip if not there.
1407 if(!fmod || !smod) continue;
1408
1409 // next we have fversion, sversion, fsize, ssize, fdata, sdata which is the stuff that might change
1410 size_t copy_over_part = (void *)buf - (void *)op_params;
1411
1412 const int fversion = *(const int *)buf;
1413 buf += sizeof(int32_t);
1414 const int sversion = *(const int *)buf;
1415 buf += sizeof(int32_t);
1416 const int fsize = *(const int *)buf;
1417 buf += sizeof(int32_t);
1418 const int ssize = *(const int *)buf;
1419 buf += sizeof(int32_t);
1420
1421 const void *fdata = buf;
1422 buf += fsize;
1423 const void *sdata = buf;
1424
1425 void *new_fdata = NULL, *new_sdata = NULL;
1426 size_t new_fsize = fsize, new_ssize = ssize;
1427 const int32_t new_fversion = fmod->version(), new_sversion = smod->version();
1428
1429 if(fversion < new_fversion)
1430 {
1431 if(!(fmod->legacy_params
1432 && (new_fdata = fmod->legacy_params(fmod, fdata, fsize, fversion, new_fversion, &new_fsize))
1433 != NULL))
1434 goto delete_preset;
1435 }
1436
1437 if(sversion < new_sversion)
1438 {
1439 if(!(smod->legacy_params
1440 && (new_sdata = smod->legacy_params(smod, sdata, ssize, sversion, new_sversion, &new_ssize))
1441 != NULL))
1442 goto delete_preset;
1443 }
1444
1445 if(new_fdata || new_sdata)
1446 {
1447 // we got an updated blob -> reassemble the parts and update the preset
1448 const size_t new_params_size = op_params_size - (fsize + ssize) + (new_fsize + new_ssize);
1449 void *new_params = malloc(new_params_size);
1450 memcpy(new_params, op_params, copy_over_part);
1451 // next we have fversion, sversion, fsize, ssize, fdata, sdata which is the stuff that might change
1452 size_t pos = copy_over_part;
1453 memcpy((uint8_t *)new_params + pos, &new_fversion, sizeof(int32_t));
1454 pos += sizeof(int32_t);
1455 memcpy((uint8_t *)new_params + pos, &new_sversion, sizeof(int32_t));
1456 pos += sizeof(int32_t);
1457 memcpy((uint8_t *)new_params + pos, &new_fsize, sizeof(int32_t));
1458 pos += sizeof(int32_t);
1459 memcpy((uint8_t *)new_params + pos, &new_ssize, sizeof(int32_t));
1460 pos += sizeof(int32_t);
1461 if(new_fdata)
1462 memcpy((uint8_t *)new_params + pos, new_fdata, new_fsize);
1463 else
1464 memcpy((uint8_t *)new_params + pos, fdata, fsize);
1465 pos += new_fsize;
1466 if(new_sdata)
1467 memcpy((uint8_t *)new_params + pos, new_sdata, new_ssize);
1468 else
1469 memcpy((uint8_t *)new_params + pos, sdata, ssize);
1470
1471 // write the updated preset back to db
1472 fprintf(stderr,
1473 "[export_init_presets] updating export preset '%s' from versions %d/%d to versions %d/%d\n",
1474 name, fversion, sversion, new_fversion, new_sversion);
1475 // op_version is written back as the row's own value, so this stays the op_params-only
1476 // update the original was, whatever branch reaches it.
1477 dt_preset_repository_update_params_by_rowid(rowid, op_version, new_params, new_params_size);
1478
1479 dt_free(new_fdata);
1480 dt_free(new_sdata);
1481 dt_free(new_params);
1482 }
1483
1484 continue;
1485
1486 delete_preset:
1487 dt_free(new_fdata);
1488 dt_free(new_sdata);
1489 fprintf(stderr, "[export_init_presets] export preset '%s' can't be updated from versions %d/%d to "
1490 "versions %d/%d. dropping preset\n",
1491 name, fversion, sversion, new_fversion, new_sversion);
1493 }
1494 }
1495 g_list_free_full(presets, dt_module_preset_free);
1496}
1497
1498void *legacy_params(dt_lib_module_t *self, const void *const old_params, const size_t old_params_size,
1499 const int old_version, int *new_version, size_t *new_size)
1500{
1501 if(old_version == 1)
1502 {
1503 // add version of format & storage to params
1504 const size_t new_params_size = old_params_size + 2 * sizeof(int32_t);
1505 void *new_params = malloc(new_params_size);
1506
1507 const char *buf = (const char *)old_params;
1508
1509 // skip 3*int32_t: max_width, max_height and iccintent
1510 buf += 3 * sizeof(int32_t);
1511 // next skip iccprofile
1512 buf += strlen(buf) + 1;
1513
1514 // parse both names to '\0'
1515 const char *fname = buf;
1516 buf += strlen(fname) + 1;
1517 const char *sname = buf;
1518 buf += strlen(sname) + 1;
1519
1520 // get module by name and fail if not there.
1523 if(!fmod || !smod)
1524 {
1525 dt_free(new_params);
1526 return NULL;
1527 }
1528
1529 // now we are just behind the module/storage names and before their param sizes. this is the place where
1530 // we want their versions
1531 // copy everything until here to the new params
1532 size_t first_half = (void *)buf - (void *)old_params;
1533 memcpy(new_params, old_params, first_half);
1534 // add the versions. at the time this code was added all modules were at version 1, except of picasa which was at 2.
1535 // every newer version of the imageio modules should result in a preset that is not going through this code.
1536 int32_t fversion = 1;
1537 int32_t sversion = (strcmp(sname, "picasa") == 0 ? 2 : 1);
1538 memcpy((uint8_t *)new_params + first_half, &fversion, sizeof(int32_t));
1539 memcpy((uint8_t *)new_params + first_half + sizeof(int32_t), &sversion, sizeof(int32_t));
1540 // copy the rest of the old params over
1541 memcpy((uint8_t *)new_params + first_half + sizeof(int32_t) * 2, buf, old_params_size - first_half);
1542
1543 *new_size = new_params_size;
1544 *new_version = 2;
1545 return new_params;
1546 }
1547 else if(old_version == 2)
1548 {
1549 // add upscale to params
1550 const size_t new_params_size = old_params_size + sizeof(int32_t);
1551 void *new_params = calloc(1, new_params_size);
1552
1553 memcpy(new_params, old_params, sizeof(int32_t) * 2);
1554 memcpy((uint8_t *)new_params + sizeof(int32_t) * 3, (uint8_t *)old_params + sizeof(int32_t) * 2, old_params_size - sizeof(int32_t) * 2);
1555
1556 *new_size = new_params_size;
1557 *new_version = 3;
1558 return new_params;
1559 }
1560 else if(old_version == 3)
1561 {
1562 // replace iccprofile by type + filename
1563 // format of v3:
1564 // - 4 x int32_t (max_width, max_height, upscale, iccintent)
1565 // - char* (iccprofile)
1566 // - rest
1567 // format of v4:
1568 // - 5 x int32_t (max_width, max_height, upscale, iccintent, icctype)
1569 // - char* (iccfilename)
1570 // - old rest
1571
1572 const char *buf = (const char *)old_params;
1573
1574 // first get the old iccprofile to find out how big our new blob has to be
1575 const char *iccprofile = buf + 4 * sizeof(int32_t);
1576
1577 size_t new_params_size = old_params_size - strlen(iccprofile) + sizeof(int32_t);
1578 int icctype;
1579 const char *iccfilename = "";
1580
1581 if(!strcmp(iccprofile, "image"))
1582 icctype = DT_COLORSPACE_NONE;
1583 else if(!strcmp(iccprofile, "sRGB"))
1584 icctype = DT_COLORSPACE_SRGB;
1585 else if(!strcmp(iccprofile, "linear_rec709_rgb") || !strcmp(iccprofile, "linear_rgb"))
1586 icctype = DT_COLORSPACE_LIN_REC709;
1587 else if(!strcmp(iccprofile, "linear_rec2020_rgb"))
1588 icctype = DT_COLORSPACE_LIN_REC2020;
1589 else if(!strcmp(iccprofile, "adobergb"))
1590 icctype = DT_COLORSPACE_ADOBERGB;
1591 else
1592 {
1593 icctype = DT_COLORSPACE_FILE;
1594 iccfilename = iccprofile;
1595 new_params_size += strlen(iccfilename);
1596 }
1597
1598 void *new_params = calloc(1, new_params_size);
1599 size_t pos = 0;
1600 memcpy(new_params, old_params, sizeof(int32_t) * 4);
1601 pos += 4 * sizeof(int32_t);
1602 memcpy((uint8_t *)new_params + pos, &icctype, sizeof(int32_t));
1603 pos += sizeof(int32_t);
1604 memcpy((uint8_t *)new_params + pos, iccfilename, strlen(iccfilename) + 1);
1605 pos += strlen(iccfilename) + 1;
1606 size_t old_pos = 4 * sizeof(int32_t) + strlen(iccprofile) + 1;
1607 memcpy((uint8_t *)new_params + pos, (uint8_t *)old_params + old_pos, old_params_size - old_pos);
1608
1609 *new_size = new_params_size;
1610 *new_version = 4;
1611 return new_params;
1612 }
1613 else if(old_version == 4)
1614 {
1615 // add high_quality to params
1616
1617 // format of v4:
1618 // - 5 x int32_t (max_width, max_height, upscale, iccintent, icctype)
1619 // - char* (iccfilename)
1620 // - old rest
1621 // format of v5:
1622 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1623 // - char* (iccfilename)
1624 // - old rest
1625
1626 const size_t new_params_size = old_params_size + sizeof(int32_t);
1627 void *new_params = calloc(1, new_params_size);
1628
1629 size_t pos = 0;
1630 memcpy(new_params, old_params, sizeof(int32_t) * 3);
1631 pos += 4 * sizeof(int32_t);
1632 memcpy((uint8_t *)new_params + pos, (uint8_t *)old_params + pos - sizeof(int32_t), old_params_size - sizeof(int32_t) * 3);
1633
1634 *new_size = new_params_size;
1635 *new_version = 5;
1636 return new_params;
1637 }
1638 else if(old_version == 5)
1639 {
1640 // add metadata preset string
1641
1642 // format of v5:
1643 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1644 // - char* (iccfilename)
1645 // - old rest
1646 // format of v6:
1647 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1648 // - char* (metadata_export)
1649 // - char* (iccfilename)
1650 // - old rest
1651
1652 const gboolean omit = dt_conf_get_bool("omit_tag_hierarchy");
1653 gchar *flags = g_strdup_printf("%x", dt_lib_export_metadata_default_flags() | (omit ? DT_META_OMIT_HIERARCHY : 0));
1654 const int flags_size = strlen(flags) + 1;
1655 const size_t new_params_size = old_params_size + flags_size;
1656 void *new_params = calloc(1, new_params_size);
1657 size_t pos = 0;
1658 memcpy(new_params, old_params, sizeof(int32_t) * 6);
1659 pos += 6 * sizeof(int32_t);
1660 memcpy((uint8_t *)new_params + pos, flags, flags_size);
1661 pos += flags_size;
1662 memcpy((uint8_t *)new_params + pos, (uint8_t *)old_params + pos - flags_size, old_params_size - sizeof(int32_t) * 6);
1663
1664 dt_free(flags);
1665 *new_size = new_params_size;
1666 *new_version = 6;
1667 return new_params;
1668 }
1669 else if(old_version == 6)
1670 {
1671 // add export_masks
1672
1673 // format of v6:
1674 // - 6 x int32_t (max_width, max_height, upscale, high_quality, iccintent, icctype)
1675 // - old rest
1676 // format of v7:
1677 // - 7 x int32_t (max_width, max_height, upscale, high_quality, export_masks, iccintent, icctype)
1678 // - old rest
1679
1680 const size_t new_params_size = old_params_size + sizeof(int32_t);
1681 void *new_params = calloc(1, new_params_size);
1682
1683 size_t pos = 0;
1684 memcpy(new_params, old_params, sizeof(int32_t) * 4);
1685 pos += 5 * sizeof(int32_t);
1686 memcpy((uint8_t *)new_params + pos, (uint8_t *)old_params + pos - sizeof(int32_t), old_params_size - sizeof(int32_t) * 4);
1687
1688 *new_size = new_params_size;
1689 *new_version = 7;
1690 return new_params;
1691 }
1692
1693 return NULL;
1694}
1695
1697{
1699 // concat storage and format, size is max + header
1702 if(IS_NULL_PTR(mformat) || IS_NULL_PTR(mstorage)) return NULL;
1703
1704 // size will be only as large as needed to remove random pointers from params (stored at the end).
1705 size_t fsize = mformat->params_size(mformat);
1706 dt_imageio_module_data_t *fdata = mformat->get_params(mformat);
1707 size_t ssize = mstorage->params_size(mstorage);
1708 void *sdata = mstorage->get_params(mstorage);
1709 const int32_t fversion = mformat->version();
1710 const int32_t sversion = mstorage->version();
1711 // we allow null pointers (plugin not ready for export in current state), and just don't copy back the
1712 // settings later:
1713 if(IS_NULL_PTR(sdata)) ssize = 0;
1714 if(IS_NULL_PTR(fdata)) fsize = 0;
1715 if(fdata)
1716 {
1717 // clean up format global params (need to set all bytes to reliably detect which preset is active).
1718 // we happen to want to set it all to 0
1719 memset(fdata, 0, sizeof(dt_imageio_module_data_t));
1720 }
1721
1722 // FIXME: also the web preset has to be applied twice to be known as preset! (other dimension magic going on
1723 // here?)
1724 // TODO: get this stuff from gui and not from conf, so it will be sanity-checked (you can never delete an
1725 // insane preset)?
1726 // also store icc profile/intent here.
1727 const int32_t iccintent = dt_conf_get_int(CONFIG_PREFIX "iccintent");
1728 const int32_t icctype = dt_conf_get_int(CONFIG_PREFIX "icctype");
1729 // the blob holds a pixel box: the size the selected mode resolves to, or, for a factor it cannot
1730 // hold, the pixel mode's own box
1731 int32_t max_width = 0;
1732 int32_t max_height = 0;
1733 double scale_factor = 0.0;
1734 _resolve_export_size(&max_width, &max_height, &scale_factor);
1735 if(scale_factor > 0.0)
1736 {
1737 max_width = dt_conf_get_int(CONFIG_PREFIX "width");
1738 max_height = dt_conf_get_int(CONFIG_PREFIX "height");
1739 }
1740 const int32_t upscale = FALSE;
1741 const int32_t high_quality = TRUE;
1742 const int32_t export_masks = dt_conf_get_bool(CONFIG_PREFIX "export_masks") ? 1 : 0;
1743 gchar *iccfilename = dt_conf_get_string(CONFIG_PREFIX "iccprofile");
1744 gchar *style = dt_conf_get_string(CONFIG_PREFIX "style");
1745 const char *metadata_export = IS_NULL_PTR(d->metadata_export) ? "" : d->metadata_export;
1746
1747 if(fdata)
1748 {
1749 g_strlcpy(fdata->style, style, sizeof(fdata->style));
1750 }
1751
1752 if(icctype != DT_COLORSPACE_FILE)
1753 {
1754 dt_free(iccfilename);
1755 }
1756
1757 if(!iccfilename) iccfilename = g_strdup("");
1758
1759 const char *fname = mformat->plugin_name;
1760 const char *sname = mstorage->plugin_name;
1761 const int32_t fname_len = strlen(fname);
1762 const int32_t sname_len = strlen(sname);
1763
1764 *size = fname_len + sname_len + 2 + 4 * sizeof(int32_t) + fsize + ssize + 7 * sizeof(int32_t)
1765 + strlen(iccfilename) + 1 + strlen(metadata_export) + 1;
1766
1767 char *params = (char *)calloc(1, *size);
1768 int pos = 0;
1769 memcpy(params + pos, &max_width, sizeof(int32_t));
1770 pos += sizeof(int32_t);
1771 memcpy(params + pos, &max_height, sizeof(int32_t));
1772 pos += sizeof(int32_t);
1773 memcpy(params + pos, &upscale, sizeof(int32_t));
1774 pos += sizeof(int32_t);
1775 memcpy(params + pos, &high_quality, sizeof(int32_t));
1776 pos += sizeof(int32_t);
1777 memcpy(params + pos, &export_masks, sizeof(int32_t));
1778 pos += sizeof(int32_t);
1779 memcpy(params + pos, &iccintent, sizeof(int32_t));
1780 pos += sizeof(int32_t);
1781 memcpy(params + pos, &icctype, sizeof(int32_t));
1782 pos += sizeof(int32_t);
1783 memcpy(params + pos, metadata_export, strlen(metadata_export) + 1);
1784 pos += strlen(metadata_export) + 1;
1785 memcpy(params + pos, iccfilename, strlen(iccfilename) + 1);
1786 pos += strlen(iccfilename) + 1;
1787 memcpy(params + pos, fname, fname_len + 1);
1788 pos += fname_len + 1;
1789 memcpy(params + pos, sname, sname_len + 1);
1790 pos += sname_len + 1;
1791 memcpy(params + pos, &fversion, sizeof(int32_t));
1792 pos += sizeof(int32_t);
1793 memcpy(params + pos, &sversion, sizeof(int32_t));
1794 pos += sizeof(int32_t);
1795 memcpy(params + pos, &fsize, sizeof(int32_t));
1796 pos += sizeof(int32_t);
1797 memcpy(params + pos, &ssize, sizeof(int32_t));
1798 pos += sizeof(int32_t);
1799 if(!IS_NULL_PTR(fdata)) // otherwise fsize == 0, but clang doesn't like it ...
1800 {
1801 memcpy(params + pos, fdata, fsize);
1802 pos += fsize;
1803 }
1804 if(!IS_NULL_PTR(sdata)) // see above
1805 {
1806 memcpy(params + pos, sdata, ssize);
1807 pos += ssize;
1808 }
1809 g_assert(pos == *size);
1810
1811 dt_free(iccfilename);
1812 dt_free(style);
1813
1814 if(fdata) mformat->free_params(mformat, fdata);
1815 if(sdata) mstorage->free_params(mstorage, sdata);
1816 return params;
1817}
1818
1819// the NUL-terminated string at *buf, or NULL if the blob ends before its terminator
1820static const char *_blob_string(const char **buf, const char *const end)
1821{
1822 const char *str = *buf;
1823 const char *nul = memchr(str, '\0', end - str);
1824 if(IS_NULL_PTR(nul)) return NULL;
1825 *buf = nul + 1;
1826 return str;
1827}
1828
1829int set_params(dt_lib_module_t *self, const void *params, int size)
1830{
1832 if(IS_NULL_PTR(params) || size < 0) return 1;
1833
1834 // parse and validate the whole blob before applying anything of it
1835 const char *buf = (const char *)params;
1836 const char *const end = buf + size;
1837
1838 // max_width, max_height, upscale (unused now), high_quality (unused now), export_masks, iccintent, icctype
1839 int32_t header[7];
1840 if((size_t)(end - buf) < sizeof(header)) return 1;
1841 memcpy(header, buf, sizeof(header));
1842 buf += sizeof(header);
1843 const int max_width = header[0];
1844 const int max_height = header[1];
1845 const int export_masks = header[4];
1846 const int iccintent = header[5];
1847 const int icctype = header[6];
1848
1849 const char *metadata_export = _blob_string(&buf, end);
1850 const char *iccfilename = metadata_export ? _blob_string(&buf, end) : NULL;
1851 const char *fname = iccfilename ? _blob_string(&buf, end) : NULL;
1852 const char *sname = fname ? _blob_string(&buf, end) : NULL;
1853 if(IS_NULL_PTR(sname)) return 1;
1854
1855 // fversion, sversion, fsize, ssize
1856 int32_t modules[4];
1857 if((size_t)(end - buf) < sizeof(modules)) return 1;
1858 memcpy(modules, buf, sizeof(modules));
1859 buf += sizeof(modules);
1860 const int32_t fversion = modules[0];
1861 const int32_t sversion = modules[1];
1862 const int32_t fsize = modules[2];
1863 const int32_t ssize = modules[3];
1864 if(fsize < 0 || ssize < 0 || (size_t)(end - buf) != (size_t)fsize + (size_t)ssize) return 1;
1865
1866 // get module by name and fail if not there.
1869 if(!fmod || !smod) return 1;
1870 if(fversion != fmod->version() || sversion != smod->version()) return 1;
1871
1872 const dt_imageio_module_data_t *fdata = (const dt_imageio_module_data_t *)buf;
1873 const void *sdata = buf + fsize;
1874
1875 dt_free(d->metadata_export);
1876 d->metadata_export = g_strdup(metadata_export);
1877 dt_lib_export_metadata_set_conf(d->metadata_export);
1878
1879 // reverse these by setting the gui, not the conf vars!
1880 dt_bauhaus_combobox_set(d->intent, iccintent + 1);
1881
1882 dt_bauhaus_combobox_set(d->profile, 0);
1883 if(icctype != DT_COLORSPACE_NONE)
1884 {
1885 // +1: the combo carries "same as original" ahead of the profiles.
1886 const int pos = dt_colorspaces_profile_index(DT_PROFILE_ROLE_OUTPUT, icctype, iccfilename);
1887 if(pos > -1) dt_bauhaus_combobox_set(d->profile, pos + 1);
1888 }
1889
1890 // the style travels in the format's params: a preset saved while the format had none carries
1891 // no style, and the current one is left alone.
1892 if((size_t)fsize >= sizeof(dt_imageio_module_data_t))
1893 {
1894 char style[sizeof(fdata->style)];
1895 memcpy(style, fdata->style, sizeof(style));
1896 style[sizeof(style) - 1] = '\0';
1897 if(style[0] == '\0')
1898 dt_bauhaus_combobox_set(d->style, 0);
1899 else
1900 dt_bauhaus_combobox_set_from_text(d->style, style);
1901 }
1902
1903 // switch modules
1904 set_storage_by_name(d, sname);
1905 set_format_by_name(d, fname);
1906
1907 // set dimensions after switching, to have new range ready. The preset holds a pixel box: the
1908 // print mode gets its equivalent at the current dpi.
1909 _set_dimensions(d, max_width, max_height);
1910 _print_size_from_pixels(max_width, max_height);
1913 dt_bauhaus_combobox_set(d->export_masks, export_masks ? 1 : 0);
1914
1915 // propagate to modules
1916 int res = 0;
1917 if(ssize) res += smod->set_params(smod, sdata, ssize);
1918 if(fsize) res += fmod->set_params(fmod, fdata, fsize);
1919 return res;
1920}
1921
1922// clang-format off
1923// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1924// vim: shiftwidth=2 expandtab tabstop=2 cindent
1925// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1926// 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:2000
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2139
const char * dt_bauhaus_combobox_get_text(GtkWidget *widget)
Definition bauhaus.c:1973
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2093
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:1701
gboolean dt_bauhaus_combobox_set_from_text(GtkWidget *widget, const char *text)
Definition bauhaus.c:2103
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1827
#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)
int dt_conf_key_exists(const char *key)
Does key have a value?
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
Float for name, clamped to its declared bounds.
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
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:839
int dt_control_running()
Definition control.c:442
void dt_control_export(GList *imgid_list, const dt_control_export_request_t *request)
struct dt_imageio_t * dt_imageio_get_global(void)
Definition darktable.c:629
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:649
void dt_gui_refocus_parent(GtkWindow *parent)
Definition dialog.c:64
const int res
Definition dtpthread.h:351
static void _format_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:640
static float _print_unit_in_cm(const dt_dimensions_type_t d_type)
Definition export.c:157
void gui_reset(dt_lib_module_t *self)
Definition export.c:524
static void _callback_bool(GtkWidget *widget, gpointer user_data)
Definition export.c:879
void _menuitem_preferences(GtkMenuItem *menuitem, dt_lib_module_t *self)
Definition export.c:973
int set_params(dt_lib_module_t *self, const void *params, int size)
Definition export.c:1829
#define CONF_PRINT_WIDTH
Definition export.c:142
static void _update(dt_lib_module_t *self)
Definition export.c:233
static void _height_changed(GtkEditable *entry, gpointer user_data)
Definition export.c:850
#define INCH_TO_CM
Definition export.c:135
char * dt_lib_export_metadata_configuration_dialog(char *list, const gboolean ondisk)
static void _print_height_changed(GtkEditable *entry, gpointer user_data)
Definition export.c:859
void * get_params(dt_lib_module_t *self, int *size)
Definition export.c:1696
static int _print_dpi(void)
Definition export.c:150
static gboolean _is_int(const double value)
Definition export.c:261
void set_preferences(void *menu, dt_lib_module_t *self)
Definition export.c:981
static void _intent_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:885
static void _scale_changed(GtkEntry *spin, dt_lib_export_t *d)
Definition export.c:393
static void _profile_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:766
static void _scale_optim()
Definition export.c:267
static gboolean _size_entry_reset_click(GtkEntry *entry, const GdkEventButton *event, gpointer reset_text)
Definition export.c:458
static void _print_width_changed(GtkEditable *entry, gpointer user_data)
Definition export.c:844
void gui_cleanup(dt_lib_module_t *self)
Definition export.c:1319
static void _print_dpi_changed(GtkWidget *widget, gpointer user_data)
Definition export.c:865
static void _resolve_export_size(int *max_width, int *max_height, double *scale_factor)
Definition export.c:183
dt_dimensions_type_t
Definition export.c:118
@ DT_DIMENSIONS_PIXELS
Definition export.c:119
@ DT_DIMENSIONS_ORIGINAL
Definition export.c:123
@ DT_DIMENSIONS_SCALE
Definition export.c:122
@ DT_DIMENSIONS_INCH
Definition export.c:121
@ DT_DIMENSIONS_CM
Definition export.c:120
static void _on_storage_list_changed(gpointer instance, dt_lib_module_t *self)
Definition export.c:933
static void _dimensions_type_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:785
static void _style_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:891
static void set_format_by_name(dt_lib_export_t *d, const char *name)
Definition export.c:581
static dt_dimensions_type_t _dimensions_type(void)
Definition export.c:145
static void _validate_dimensions(dt_lib_export_t *d)
Definition export.c:675
static void _print_size_from_pixels(const uint32_t width, const uint32_t height)
Definition export.c:167
void _size_update_display(dt_lib_export_t *self)
Definition export.c:509
#define EXPORT_MAX_IMAGE_SIZE
Definition export.c:97
static void _print_size_display(dt_lib_export_t *d)
Definition export.c:809
static void _update_formats_combobox(dt_lib_export_t *d)
Definition export.c:909
static uint32_t _cm_to_pixels(const float cm, const int dpi)
Definition export.c:162
void init_presets(dt_lib_module_t *self)
Definition export.c:1349
static void _update_dimensions(dt_lib_export_t *d)
Definition export.c:688
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:253
#define CONF_PRINT_HEIGHT
Definition export.c:143
uint32_t container(dt_lib_module_t *self)
Definition export.c:228
#define CONFIG_PREFIX
Definition export.c:98
static void _size_in_px_update(dt_lib_export_t *d)
Definition export.c:465
static void _storage_changed(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:758
void _set_dimensions(dt_lib_export_t *d, uint32_t max_width, uint32_t max_height)
Definition export.c:490
static void _export_button_clicked(GtkWidget *widget, dt_lib_export_t *d)
Definition export.c:314
void gui_init(dt_lib_module_t *self)
Definition export.c:988
int position()
Definition export.c:904
const char ** views(dt_lib_module_t *self)
Definition export.c:221
static void _image_selection_changed_callback(gpointer instance, dt_lib_module_t *self)
Definition export.c:248
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:1498
static void _ensure_print_size_conf(void)
Definition export.c:174
static void _print_size_changed(GtkEntry *entry, const char *key, dt_lib_export_t *d)
Definition export.c:837
static void set_storage_by_name(dt_lib_export_t *d, const char *name)
Definition export.c:697
static void _width_changed(GtkEditable *entry, gpointer user_data)
Definition export.c:827
static const char * _blob_string(const char **buf, const char *const end)
Definition export.c:1820
static void _get_max_output_dimension(dt_lib_export_t *d, uint32_t *width, uint32_t *height)
Definition export.c:648
static void _lib_export_styles_changed_callback(gpointer instance, gpointer user_data)
Definition export.c:953
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:319
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)
void dt_imageio_resizing_factor_parse(const char *str, 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
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
void dt_lib_cancel_postponed_update(dt_lib_module_t *mod)
Definition lib.c:1320
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:1347
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_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:411
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:619
@ DT_SIGNAL_STYLE_CHANGED
This signal is raised when a style is added/deleted/changed
Definition signal.h:151
@ DT_SIGNAL_IMAGEIO_STORAGE_CHANGE
This signal is raised when a new storage module is loaded noparameters no return.
Definition signal.h:271
@ DT_SIGNAL_SELECTION_CHANGED
This signal is raised when the selection is changed no param, no returned value.
Definition signal.h:131
@ DT_SIGNAL_COLLECTION_CHANGED
This signal is raised when collection changed. To avoid leaking the list, dt_collection_t is connecte...
Definition signal.h:126
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:400
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
dt_colorspaces_color_profile_type_t icc_type
dt_iop_color_intent_t icc_intent
GtkWidget * storage
Definition export.c:106
GtkWidget * format
Definition export.c:106
GtkWidget * unit_label
Definition export.c:103
int format_lut[128]
Definition export.c:107
GtkWidget * scale
Definition export.c:105
GtkWidget * print_width
Definition export.c:102
GtkButton * export_button
Definition export.c:110
GtkWidget * storage_extra_container
Definition export.c:111
uint32_t max_allowed_width
Definition export.c:108
GtkWidget * export_masks
Definition export.c:112
GtkWidget * size_in_px
Definition export.c:105
GtkWidget * print_dpi
Definition export.c:102
GtkWidget * print_height
Definition export.c:102
GtkWidget * px_size
Definition export.c:105
GtkWidget * style
Definition export.c:109
GtkWidget * profile
Definition export.c:109
GtkWidget * width
Definition export.c:104
GtkWidget * dimensions_type
Definition export.c:102
GtkWidget * print_size
Definition export.c:105
GtkWidget * height
Definition export.c:104
GtkWidget * format_extra_container
Definition export.c:111
uint32_t max_allowed_height
Definition export.c:108
GtkWidget * intent
Definition export.c:109
char * metadata_export
Definition export.c:113
GModule *void * data
Definition lib.h:79
GtkWidget * widget
Definition lib.h:83
guint timeout_handle
Definition lib.h:89
gchar * name
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
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