Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
cli/main.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2012, 2014-2015 johannes hanika.
4 Copyright (C) 2012, 2014-2016, 2019-2021 Pascal Obry.
5 Copyright (C) 2012-2017, 2019-2020 Tobias Ellinghaus.
6 Copyright (C) 2013-2014 Jérémy Rosen.
7 Copyright (C) 2013-2014 Pascal de Bruijn.
8 Copyright (C) 2013 Thomas Pryds.
9 Copyright (C) 2014 Pedro Côrte-Real.
10 Copyright (C) 2014-2016 Roman Lebedev.
11 Copyright (C) 2017, 2020 Heiko Bauke.
12 Copyright (C) 2018 Bill Ferguson.
13 Copyright (C) 2018 Michael Mayer.
14 Copyright (C) 2019 Denis Dyakov.
15 Copyright (C) 2019 parafin.
16 Copyright (C) 2019 Philippe Weyland.
17 Copyright (C) 2020 bobobo1618.
18 Copyright (C) 2020 David-Tillmann Schaefer.
19 Copyright (C) 2020 Hubert Kowalski.
20 Copyright (C) 2021 Aldric Renaudin.
21 Copyright (C) 2021 Christian Kreibich.
22 Copyright (C) 2021 Hanno Schwalm.
23 Copyright (C) 2021 Ralf Brown.
24 Copyright (C) 2022-2023, 2025 Aurélien PIERRE.
25 Copyright (C) 2022 Martin Bařinka.
26 Copyright (C) 2022 Miloš Komarčević.
27 Copyright (C) 2023 Alynx Zhou.
28
29 darktable is free software: you can redistribute it and/or modify
30 it under the terms of the GNU General Public License as published by
31 the Free Software Foundation, either version 3 of the License, or
32 (at your option) any later version.
33
34 darktable is distributed in the hope that it will be useful,
35 but WITHOUT ANY WARRANTY; without even the implied warranty of
36 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 GNU General Public License for more details.
38
39 You should have received a copy of the GNU General Public License
40 along with darktable. If not, see <http://www.gnu.org/licenses/>.
41*/
42
52#include "common/collection.h"
53#include "common/darktable.h"
54#include "common/debug.h"
55#include "common/exif.h"
56#include "common/film.h"
58#include "common/history.h"
59#include "common/image.h"
60#include "common/image_cache.h"
61#include "common/imageio.h"
62#include "common/imageio_jpeg.h"
64#include "common/l10n.h"
65#include "common/points.h"
66#include "control/conf.h"
67#include "develop/imageop.h"
68
69#include <inttypes.h>
70#include <libintl.h>
71#include <sys/time.h>
72#include <unistd.h>
73
74#ifdef __APPLE__
75#include "osx/osx.h"
76#endif
77
78#ifdef _WIN32
79#include "win/main_wrapper.h"
80#endif
81
82#define DT_MAX_STYLE_NAME_LENGTH 128
83
84// Make sure it's OK to limit output extension length
85#define DT_MAX_OUTPUT_EXT_LENGTH 5
86
87static void usage(const char *progname)
88{
89 fprintf(stderr, "usage: %s [<input file or dir>] [<xmp file>] <output destination> [options] [--core <darktable options>]\n", progname);
90 fprintf(stderr, "\n");
91 fprintf(stderr, "options:\n");
92 fprintf(stderr, " --width <max width> default: 0 = full resolution\n");
93 fprintf(stderr, " --height <max height> default: 0 = full resolution\n");
94 fprintf(stderr, " --bpp <bpp>, unsupported\n");
95 fprintf(stderr, " --export_masks <0|1|false|true>, default: false\n");
96 fprintf(stderr, " --style <style name>\n");
97 fprintf(stderr, " --apply-custom-presets <0|1|false|true>, default: true\n");
98 fprintf(stderr, " disable for multiple instances\n");
99 fprintf(stderr, " --out-ext <extension>, default from output destination or '.jpg'\n");
100 fprintf(stderr, " if specified, takes preference over output\n");
101 fprintf(stderr, " --import <file or dir> specify input file or dir, can be used'\n");
102 fprintf(stderr, " multiple times instead of input file\n");
103 fprintf(stderr, " --imgid <id> process an image already in the library, using its\n");
104 fprintf(stderr, " editing history from library.db (no input file, no XMP;\n");
105 fprintf(stderr, " can be used multiple times; only the output is positional)\n");
106 fprintf(stderr, "\n");
107 fprintf(stderr, " the output destination may contain $(VARIABLES) expanded by the disk\n");
108 fprintf(stderr, " storage, e.g. '/path/$(FILE_NAME)-myfix' with --out-ext tiff produces\n");
109 fprintf(stderr, " /path/<original basename>-myfix.tiff\n");
110 fprintf(stderr, " --icc-type <type> specify icc type, default to NONE\n");
111 fprintf(stderr, " use --help icc-type for list of supported types\n");
112 fprintf(stderr, " --icc-file <file> specify icc filename, default to NONE\n");
113 fprintf(stderr, " --icc-intent <intent> specify icc intent, default to LAST\n");
114 fprintf(stderr, " use --help icc-intent for list of supported intents\n");
115 fprintf(stderr, " --verbose\n");
116 fprintf(stderr, " --help,-h [option]\n");
117 fprintf(stderr, " --version\n");
118}
119
120static void icc_types()
121{
122 // TODO: Can this be automated to keep in sync with colorspaces.h?
123 fprintf(stderr, "available ICC types:\n");
124 fprintf(stderr, " NONE\n");
125 fprintf(stderr, " FILE\n");
126 fprintf(stderr, " SRGB\n");
127 fprintf(stderr, " ADOBERGB\n");
128 fprintf(stderr, " LIN_REC709\n");
129 fprintf(stderr, " LIN_REC2020\n");
130 fprintf(stderr, " XYZ\n");
131 fprintf(stderr, " LAB\n");
132 fprintf(stderr, " INFRARED\n");
133 fprintf(stderr, " DISPLAY\n");
134 fprintf(stderr, " EMBEDDED_ICC\n");
135 fprintf(stderr, " EMBEDDED_MATRIX\n");
136 fprintf(stderr, " STANDARD_MATRIX\n");
137 fprintf(stderr, " ENHANCED_MATRIX\n");
138 fprintf(stderr, " VENDOR_MATRIX\n");
139 fprintf(stderr, " ALTERNATE_MATRIX\n");
140 fprintf(stderr, " BRG\n");
141 fprintf(stderr, " EXPORT\n"); // export and softproof are categories and will return NULL with dt_colorspaces_get_profile()
142 fprintf(stderr, " SOFTPROOF\n");
143 fprintf(stderr, " WORK\n");
144 fprintf(stderr, " REC709\n");
145 fprintf(stderr, " PROPHOTO_RGB\n");
146 fprintf(stderr, " PQ_REC2020\n");
147 fprintf(stderr, " HLG_REC2020\n");
148 fprintf(stderr, " PQ_P3\n");
149 fprintf(stderr, " HLG_P3\n");
150 fprintf(stderr, " DISPLAY_P3\n");
151}
152
153#define ICC_FROM_STR(name) if(!strcmp(option, #name)) return DT_COLORSPACE_ ## name;
155{
157 ICC_FROM_STR(FILE);
158 ICC_FROM_STR(SRGB);
159 ICC_FROM_STR(ADOBERGB);
160 ICC_FROM_STR(LIN_REC709);
161 ICC_FROM_STR(LIN_REC2020);
163 ICC_FROM_STR(LAB);
164 ICC_FROM_STR(INFRARED);
165 ICC_FROM_STR(DISPLAY);
166 ICC_FROM_STR(EMBEDDED_ICC);
167 ICC_FROM_STR(EMBEDDED_MATRIX);
168 ICC_FROM_STR(STANDARD_MATRIX);
169 ICC_FROM_STR(ENHANCED_MATRIX);
170 ICC_FROM_STR(VENDOR_MATRIX);
171 ICC_FROM_STR(ALTERNATE_MATRIX);
172 ICC_FROM_STR(BRG);
173 ICC_FROM_STR(EXPORT); // export and softproof are categories and will return NULL with dt_colorspaces_get_profile()
174 ICC_FROM_STR(SOFTPROOF);
175 ICC_FROM_STR(WORK);
176 ICC_FROM_STR(REC709);
177 ICC_FROM_STR(PROPHOTO_RGB);
178 ICC_FROM_STR(PQ_REC2020);
179 ICC_FROM_STR(HLG_REC2020);
180 ICC_FROM_STR(PQ_P3);
181 ICC_FROM_STR(HLG_P3);
182 ICC_FROM_STR(DISPLAY_P3);
183 return DT_COLORSPACE_LAST;
184}
185#undef ICC_FROM_STR
186
187static void icc_intents()
188{
189 // TODO: Can this be automated to keep in sync with colorspaces.h?
190 fprintf(stderr, "available ICC intents:\n");
191 fprintf(stderr, " PERCEPTUAL\n");
192 fprintf(stderr, " RELATIVE_COLORIMETRIC\n");
193 fprintf(stderr, " SATURATION\n");
194 fprintf(stderr, " ABSOLUTE_COLORIMETRIC\n");
195}
196#define ICC_INTENT_FROM_STR(name) if(!strcmp(option, #name)) return DT_INTENT_ ## name;
197static dt_iop_color_intent_t get_icc_intent(const char* option)
198{
199 ICC_INTENT_FROM_STR(PERCEPTUAL);
200 ICC_INTENT_FROM_STR(RELATIVE_COLORIMETRIC);
201 ICC_INTENT_FROM_STR(SATURATION);
202 ICC_INTENT_FROM_STR(ABSOLUTE_COLORIMETRIC);
203 return DT_INTENT_LAST;
204}
205#undef ICC_INTENT_FROM_STR
206
207int main(int argc, char *arg[])
208{
209#ifdef __APPLE__
211#endif
212
213 // get valid locale dir
214 dt_loc_init(NULL, NULL, NULL, NULL, NULL, NULL, NULL);
215 char localedir[PATH_MAX] = { 0 };
216 dt_loc_get_localedir(localedir, sizeof(localedir));
217 bindtextdomain(GETTEXT_PACKAGE, localedir);
218
219 // gtk_parse_args may trigger GTK initialization, so disable GTK locale handling first.
221 if(!gtk_parse_args(&argc, &arg)) exit(1);
222
223 // parse command line arguments
224 char *input_filename = NULL;
225 char *xmp_filename = NULL;
226 gchar *output_filename = NULL;
227 gchar *output_ext = NULL;
228 char *style = NULL;
229 int file_counter = 0;
230 int width = 0, height = 0, bpp = 0;
231 gboolean verbose = FALSE, custom_presets = TRUE, export_masks = FALSE,
232 output_to_dir = FALSE;
233
234 GList* inputs = NULL;
235 GList* imgids = NULL;
236
238 gchar *icc_filename = NULL;
240
241 int k;
242 for(k = 1; k < argc; k++)
243 {
244 if(arg[k][0] == '-')
245 {
246 if(!strcmp(arg[k], "--help") || !strcmp(arg[k], "-h"))
247 {
248 usage(arg[0]);
249 if(k+1 < argc) {
250 if(!strcmp(arg[k+1], "icc-type"))
251 icc_types();
252 if(!strcmp(arg[k+1], "icc-intent"))
253 icc_intents();
254 }
255 exit(1);
256 }
257 else if(!strcmp(arg[k], "--version"))
258 {
259 printf("this is ansel-cli %s\ncopyright (c) 2012-%s johannes hanika, tobias ellinghaus\n",
261 exit(0);
262 }
263 else if(!strcmp(arg[k], "--width") && argc > k + 1)
264 {
265 k++;
266 width = MAX(atoi(arg[k]), 0);
267 }
268 else if(!strcmp(arg[k], "--height") && argc > k + 1)
269 {
270 k++;
271 height = MAX(atoi(arg[k]), 0);
272 }
273 else if(!strcmp(arg[k], "--bpp") && argc > k + 1)
274 {
275 k++;
276 bpp = MAX(atoi(arg[k]), 0);
277 fprintf(stderr, _("TODO: sorry, due to API restrictions we currently cannot set the BPP to %d.\n"), bpp);
278 }
279 else if(!strcmp(arg[k], "--export_masks") && argc > k + 1)
280 {
281 k++;
282 gchar *str = g_ascii_strup(arg[k], -1);
283 if(!g_strcmp0(str, "0") || !g_strcmp0(str, "FALSE"))
284 export_masks = FALSE;
285 else if(!g_strcmp0(str, "1") || !g_strcmp0(str, "TRUE"))
286 export_masks = TRUE;
287 else
288 {
289 fprintf(stderr, _("unknown option for --export_masks: %s.\n"), arg[k]);
290 usage(arg[0]);
291 exit(1);
292 }
293 dt_free(str);
294 }
295 else if(!strcmp(arg[k], "--style") && argc > k + 1)
296 {
297 k++;
298 style = arg[k];
299 }
300 else if(!strcmp(arg[k], "--apply-custom-presets") && argc > k + 1)
301 {
302 k++;
303 gchar *str = g_ascii_strup(arg[k], -1);
304 if(!g_strcmp0(str, "0") || !g_strcmp0(str, "FALSE"))
305 custom_presets = FALSE;
306 else if(!g_strcmp0(str, "1") || !g_strcmp0(str, "TRUE"))
307 custom_presets = TRUE;
308 else
309 {
310 fprintf(stderr, _("unknown option for --apply-custom-presets: %s.\n"), arg[k]);
311 usage(arg[0]);
312 exit(1);
313 }
314 dt_free(str);
315 }
316 else if(!strcmp(arg[k], "--out-ext") && argc > k + 1)
317 {
318 k++;
319 if(strlen(arg[k])> DT_MAX_OUTPUT_EXT_LENGTH)
320 {
321 fprintf(stderr, _("too long ext for --out-ext: %s.\n"), arg[k]);
322 usage(arg[0]);
323 exit(1);
324 }
325 if (*arg[k] == '.')
326 {
327 //remove dot ;)
328 arg[k]++;
329 }
330 output_ext = g_strdup(arg[k]);
331 }
332 else if(!strcmp(arg[k], "--import") && argc > k + 1)
333 {
334 k++;
335 if(g_file_test(arg[k], G_FILE_TEST_EXISTS))
336 inputs = g_list_prepend(inputs, g_strdup(arg[k]));
337 else
338 fprintf(stderr, _("notice: input file or dir '%s' doesn't exist, skipping\n"), arg[k]);
339 }
340 else if(!strcmp(arg[k], "--imgid") && argc > k + 1)
341 {
342 k++;
343 const int imgid = atoi(arg[k]);
344
345 if(imgid > 0)
346 imgids = g_list_append(imgids, GINT_TO_POINTER(imgid));
347 else
348 {
349 fprintf(stderr, _("incorrect image id for --imgid: '%s'\n"), arg[k]);
350 usage(arg[0]);
351 exit(1);
352 }
353 }
354 else if(!strcmp(arg[k], "--icc-type") && argc > k + 1)
355 {
356 k++;
357 gchar *str = g_ascii_strup(arg[k], -1);
358 icc_type = get_icc_type(str);
359 dt_free(str);
360 if(icc_type >= DT_COLORSPACE_LAST){
361 fprintf(stderr, _("incorrect ICC type for --icc-type: '%s'\n"), arg[k]);
362 icc_types();
363 usage(arg[0]);
364 exit(1);
365 }
366 }
367 else if(!strcmp(arg[k], "--icc-file") && argc > k + 1)
368 {
369 k++;
370 if(g_file_test(arg[k], G_FILE_TEST_EXISTS) && ! g_file_test(arg[k], G_FILE_TEST_IS_DIR))
371 {
372 if(icc_filename)
373 dt_free(icc_filename);
374 icc_filename = g_strdup(arg[k]);
375 }
376 else
377 fprintf(stderr, _("notice: ICC file '%s' doesn't exist, skipping\n"), arg[k]);
378 }
379 else if(!strcmp(arg[k], "--icc-intent") && argc > k + 1)
380 {
381 k++;
382 gchar *str = g_ascii_strup(arg[k], -1);
383 icc_intent = get_icc_intent(str);
384 dt_free(str);
385 if(icc_intent >= DT_INTENT_LAST){
386 fprintf(stderr, _("incorrect ICC intent for --icc-intent: '%s'\n"), arg[k]);
387 icc_intents();
388 usage(arg[0]);
389 exit(1);
390 }
391 }
392 else if(!strcmp(arg[k], "-v") || !strcmp(arg[k], "--verbose"))
393 {
394 verbose = TRUE;
395 }
396 else if(!strcmp(arg[k], "--core"))
397 {
398 // everything from here on should be passed to the core
399 k++;
400 break;
401 }
402 else
403 {
404 fprintf(stderr, _("warning: unknown option '%s'\n"), arg[k]);
405 }
406 }
407 else
408 {
409 if(file_counter == 0)
410 input_filename = arg[k];
411 else if(file_counter == 1)
412 xmp_filename = arg[k];
413 else if(file_counter == 2)
414 {
415 output_filename = g_strdup(arg[k]);
416 }
417 file_counter++;
418 }
419 }
420
421 int m_argc = 0;
422 char **m_arg = malloc(sizeof(char *) * (5 + argc - k + 1));
423 m_arg[m_argc++] = "ansel-cli";
424
425 // --imgid mode reads the image and its editing history from the user's library.db, so the
426 // default library must be used. Every other mode keeps the throwaway in-memory library.
427 if(IS_NULL_PTR(imgids))
428 {
429 m_arg[m_argc++] = "--library";
430 m_arg[m_argc++] = ":memory:";
431 }
432
433 m_arg[m_argc++] = "--conf";
434 m_arg[m_argc++] = "write_sidecar_files=FALSE";
435 for(; k < argc; k++) m_arg[m_argc++] = arg[k];
436 m_arg[m_argc] = NULL;
437
438 if(imgids && (inputs || file_counter != 1))
439 {
440 // --imgid takes no input file and no XMP: the only positional argument is the output
441 if(inputs || file_counter > 1)
442 fprintf(stderr, _("error: --imgid cannot be combined with input files or an XMP (history comes from library.db)\n"));
443 else
444 fprintf(stderr, _("error: --imgid requires an output destination\n"));
445
446 usage(arg[0]);
447 dt_free(m_arg);
448 if(output_filename)
449 {
450 dt_free(output_filename);
451 }
452 if(output_ext)
453 {
454 dt_free(output_ext);
455 }
456 g_list_free(imgids);
457 if(inputs)
458 {
459 g_list_free_full(inputs, dt_free_gpointer);
460 inputs = NULL;
461 }
462 exit(1);
463 }
464 else if(imgids)
465 {
466 // the single positional argument is the output destination
467 if(output_filename)
468 dt_free(output_filename);
469 output_filename = g_strdup(input_filename);
470 input_filename = xmp_filename = NULL;
471 }
472 else if( (inputs && file_counter < 1) || (IS_NULL_PTR(inputs) && file_counter < 2) || file_counter > 3)
473 {
474 usage(arg[0]);
475 dt_free(m_arg);
476 if(output_filename)
477 {
478 dt_free(output_filename);
479 }
480 if(output_ext)
481 {
482 dt_free(output_ext);
483 }
484 if(inputs)
485 {
486 g_list_free_full(inputs, dt_free_gpointer);
487 inputs = NULL;
488 }
489 exit(1);
490 }
491 else if(inputs && file_counter == 1)
492 {
493 //user specified inputs as options, and only dest is present
494 if(output_filename)
495 dt_free(output_filename);
496 output_filename = g_strdup(input_filename);
497 input_filename = xmp_filename = NULL;
498 }
499 else if (inputs && file_counter == 2)
500 {
501 // inputs as options, xmp & output specified
502 if(output_filename)
503 dt_free(output_filename);
504 output_filename = g_strdup(xmp_filename);
505 xmp_filename = input_filename;
506 input_filename = NULL;
507 }
508 else if (inputs && file_counter == 3)
509 {
510 fprintf(stderr, _("error: input file and import opts specified! that's not supported!\n"));
511 usage(arg[0]);
512 dt_free(m_arg);
513 if(output_filename)
514 {
515 dt_free(output_filename);
516 }
517 if(output_ext)
518 {
519 dt_free(output_ext);
520 }
521 g_list_free_full(inputs, dt_free_gpointer);
522 inputs = NULL;
523 exit(1);
524 }
525 else if(file_counter == 2)
526 {
527 // assume no xmp file given
528 if(output_filename)
529 dt_free(output_filename);
530 output_filename = g_strdup(xmp_filename);
531 xmp_filename = NULL;
532 }
533
534 if(IS_NULL_PTR(inputs) && input_filename)
535 {
536 // input is present as param
537 inputs = g_list_prepend(inputs, g_strdup(input_filename));
538 input_filename = NULL;
539 }
540
541 if(g_file_test(output_filename, G_FILE_TEST_IS_DIR))
542 {
543 output_to_dir = TRUE;
544 if(IS_NULL_PTR(output_ext))
545 {
546 output_ext = g_strdup("jpg");
547 }
548 fprintf(stderr, _("notice: output location is a directory. assuming '%s/$(FILE_NAME).%s' output pattern"), output_filename, output_ext);
549 fprintf(stderr, "\n");
550 gchar* temp_of = g_strdup(output_filename);
551 dt_free(output_filename);
552 if(g_str_has_suffix(temp_of, "/"))
553 temp_of[strlen(temp_of) - 1] = '\0';
554 output_filename = g_strconcat(temp_of, "/$(FILE_NAME)", NULL);
555 dt_free(temp_of);
556 }
557
558 // the output file already exists, so there will be a sequence number added
559 if(g_file_test(output_filename, G_FILE_TEST_EXISTS) && !output_to_dir)
560 {
561 if(IS_NULL_PTR(output_ext) || (output_ext && g_str_has_suffix(output_filename, output_ext) && !g_strcmp0(output_ext,strrchr(output_filename, '.')+1))){
562 //output file exists or there's output ext specified and it's same as file...
563 fprintf(stderr, "%s\n", _("output file already exists, it will get renamed"));
564 }
565 //TODO: test if file with replaced ext exists
566 // or not if we decide we don't replace file ext with output ext specified
567 }
568
569 // init dt without gui and without data.db:
570 if(dt_init(m_argc, m_arg, FALSE, custom_presets))
571 {
572 dt_free(m_arg);
573 dt_free(output_filename);
574 if(output_ext)
575 {
576 dt_free(output_ext);
577 }
578 if(inputs)
579 {
580 g_list_free_full(inputs, dt_free_gpointer);
581 inputs = NULL;
582 }
583 exit(1);
584 }
585
586 GList *id_list = NULL;
587
588 // --imgid mode: the images are already in the library with their history; just check they exist
589 for(GList *l = imgids; !IS_NULL_PTR(l); l = g_list_next(l))
590 {
591 const int imgid = GPOINTER_TO_INT(l->data);
592 const dt_image_t *image = dt_image_cache_get(darktable.image_cache, imgid, 'r');
593
594 if(image && image->id == imgid)
595 {
597 id_list = g_list_append(id_list, GINT_TO_POINTER(imgid));
598 }
599 else
600 {
601 if(image)
603 fprintf(stderr, _("error: no image with id %d in the library"), imgid);
604 fprintf(stderr, "\n");
605 }
606 }
607
608 g_list_free(imgids);
609 imgids = NULL;
610
611 for(GList *l = inputs; !IS_NULL_PTR(l); l=g_list_next(l))
612 {
613 gchar* input = l->data;
614
615 if(g_file_test(input, G_FILE_TEST_IS_DIR))
616 {
617 const int filmid = dt_film_import(input);
618 if(!filmid)
619 {
620 // one of inputs was a failure, no prob
621 fprintf(stderr, _("error: can't open folder %s"), input);
622 fprintf(stderr, "\n");
623 continue;
624 }
625 id_list = g_list_concat(id_list, dt_film_get_image_ids(filmid));
626 }
627 else
628 {
629 dt_film_t film;
630 int filmid = 0;
631
632 gchar *directory = g_path_get_dirname(input);
633 filmid = dt_film_new(&film, directory);
634 const int32_t id = dt_image_import(filmid, input, TRUE);
635 dt_free(directory);
636 if(!id)
637 {
638 fprintf(stderr, _("error: can't open file %s"), input);
639 fprintf(stderr, "\n");
640 continue;
641 }
642 id_list = g_list_append(id_list, GINT_TO_POINTER(id));
643 }
644 }
645
646 //we no longer need inputs
647 if(inputs)
648 {
649 g_list_free_full(inputs, dt_free_gpointer);
650 inputs = NULL;
651 }
652
653 const int total = g_list_length(id_list);
654
655 if(total == 0)
656 {
657 fprintf(stderr, _("no images to export, aborting\n"));
658 dt_free(m_arg);
659 dt_free(output_filename);
660 if(output_ext)
661 {
662 dt_free(output_ext);
663 }
664 exit(1);
665 }
666
667 // attach xmp, if requested:
668 if(xmp_filename)
669 {
670 for(GList *iter = id_list; iter; iter = g_list_next(iter))
671 {
672 int id = GPOINTER_TO_INT(iter->data);
674 if(dt_exif_xmp_read(image, xmp_filename, 1) != 0)
675 {
676 fprintf(stderr, _("error: can't open xmp file %s"), xmp_filename);
677 fprintf(stderr, "\n");
678 dt_free(m_arg);
679 dt_free(output_filename);
680 if(output_ext)
681 {
682 dt_free(output_ext);
683 }
684 exit(1);
685 }
686 // don't write new xmp:
688 }
689 }
690
691 // print the history stack. only look at the first image and assume all got the same processing applied
692 if(verbose)
693 {
694 int id = GPOINTER_TO_INT(id_list->data);
695 gchar *history = dt_history_get_items_as_string(id);
696 if(history)
697 printf("%s\n", history);
698 else
699 printf("[%s]\n", _("empty history stack"));
700 }
701
702 if(IS_NULL_PTR(output_ext))
703 {
704 // by this point we're sure output is not dir, there's no output ext specified
705 // so only place to look for it is in filename
706 // try to find out the export format from the output_filename
707 char *ext = strrchr(output_filename, '.');
708 if(ext && strlen(ext) > DT_MAX_OUTPUT_EXT_LENGTH)
709 {
710 // too long ext, no point in wasting time
711 fprintf(stderr, _("too long output file extension: %s\n"), ext);
712 usage(arg[0]);
713 dt_free(output_filename);
714 exit(1);
715 }
716 else if(!ext || strlen(ext) <= 1)
717 {
718 // no ext or empty ext, no point in wasting time
719 fprintf(stderr, _("no output file extension given\n"));
720 usage(arg[0]);
721 dt_free(output_filename);
722 exit(1);
723 }
724 *ext = '\0';
725 ext++;
726 output_ext = g_strdup(ext);
727 } else {
728 // check and remove redundant file ext
729 char *ext = strrchr(output_filename, '.');
730 if(ext && !strcmp(output_ext, ext+1))
731 {
732 *ext = '\0';
733 }
734 }
735
736 if(!strcmp(output_ext, "jpg"))
737 {
738 dt_free(output_ext);
739 output_ext = g_strdup("jpeg");
740 }
741
742 if(!strcmp(output_ext, "tif"))
743 {
744 dt_free(output_ext);
745 output_ext = g_strdup("tiff");
746 }
747
748 // init the export data structures
751 dt_imageio_module_data_t *sdata, *fdata;
752
753 storage = dt_imageio_get_storage_by_name("disk"); // only exporting to disk makes sense
754 if(IS_NULL_PTR(storage))
755 {
756 fprintf(
757 stderr, "%s\n",
758 _("cannot find disk storage module. please check your installation, something seems to be broken."));
759 dt_free(m_arg);
760 dt_free(output_filename);
761 dt_free(output_ext);
762 exit(1);
763 }
764
765 sdata = storage->get_params(storage);
766 if(IS_NULL_PTR(sdata))
767 {
768 fprintf(stderr, "%s\n", _("failed to get parameters from storage module, aborting export ..."));
769 dt_free(m_arg);
770 dt_free(output_filename);
771 dt_free(output_ext);
772 exit(1);
773 }
774
775 // and now for the really ugly hacks. don't tell your children about this one or they won't sleep at night
776 // any longer ...
777 g_strlcpy((char *)sdata, output_filename, DT_MAX_PATH_FOR_PARAMS);
778 // all is good now, the last line didn't happen.
779 dt_free(output_filename);
780
781 format = dt_imageio_get_format_by_name(output_ext);
782 if(IS_NULL_PTR(format))
783 {
784 fprintf(stderr, _("unknown extension '.%s'"), output_ext);
785 fprintf(stderr, "\n");
786 dt_free(m_arg);
787 dt_free(output_ext);
788 exit(1);
789 }
790
791 fdata = format->get_params(format);
792 if(IS_NULL_PTR(fdata))
793 {
794 fprintf(stderr, "%s\n", _("failed to get parameters from format module, aborting export ..."));
795 dt_free(m_arg);
796 dt_free(output_ext);
797 exit(1);
798 }
799
800 uint32_t w, h, fw, fh, sw, sh;
801 fw = fh = sw = sh = 0;
802 storage->dimension(storage, sdata, &sw, &sh);
803 format->dimension(format, fdata, &fw, &fh);
804
805 if(sw == 0 || fw == 0)
806 w = sw > fw ? sw : fw;
807 else
808 w = sw < fw ? sw : fw;
809
810 if(sh == 0 || fh == 0)
811 h = sh > fh ? sh : fh;
812 else
813 h = sh < fh ? sh : fh;
814
815 fdata->max_width = width;
816 fdata->max_height = height;
817 fdata->max_width = (w != 0 && fdata->max_width > w) ? w : fdata->max_width;
818 fdata->max_height = (h != 0 && fdata->max_height > h) ? h : fdata->max_height;
819 fdata->style[0] = '\0';
820
821 if(style)
822 {
823 g_strlcpy((char *)fdata->style, style, DT_MAX_STYLE_NAME_LENGTH);
824 fdata->style[127] = '\0';
825 }
826
827 if(storage->initialize_store)
828 {
829 storage->initialize_store(storage, sdata, &format, &fdata, &id_list, TRUE);
830
831 format->set_params(format, fdata, format->params_size(format));
832 storage->set_params(storage, sdata, storage->params_size(storage));
833 }
834
835 // TODO: add a callback to set the bpp without going through the config
836
837 int num = 1, res = 0;
838 for(GList *iter = id_list; iter; iter = g_list_next(iter), num++)
839 {
840 const int id = GPOINTER_TO_INT(iter->data);
841 // TODO: have a parameter in command line to get the export presets
842 dt_export_metadata_t metadata;
844 metadata.list = NULL;
845 if(storage->store(storage, sdata, id, format, fdata, num, total, TRUE, export_masks,
846 icc_type, icc_filename, icc_intent, &metadata) != 0)
847 res = 1;
848 }
849
850 // cleanup time
851 if(storage->finalize_store) storage->finalize_store(storage, sdata);
852 storage->free_params(storage, sdata);
853 format->free_params(format, fdata);
854 g_list_free(id_list);
855 id_list = NULL;
856
857 if(icc_filename)
858 {
859 dt_free(icc_filename);
860 }
861
862 dt_cleanup();
863
864 dt_free(m_arg);
865 exit(res);
866}
867
868// clang-format off
869// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
870// vim: shiftwidth=2 expandtab tabstop=2 cindent
871// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
872// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static void usage(const char *progname)
Definition cli/main.c:87
static void icc_types()
Definition cli/main.c:120
static dt_iop_color_intent_t get_icc_intent(const char *option)
Definition cli/main.c:197
#define DT_MAX_OUTPUT_EXT_LENGTH
Definition cli/main.c:85
#define ICC_INTENT_FROM_STR(name)
Definition cli/main.c:196
static dt_colorspaces_color_profile_type_t get_icc_type(const char *option)
Definition cli/main.c:154
#define DT_MAX_STYLE_NAME_LENGTH
Definition cli/main.c:82
#define ICC_FROM_STR(name)
Definition cli/main.c:153
static void icc_intents()
Definition cli/main.c:187
dt_iop_color_intent_t
Definition colorspaces.h:63
@ DT_INTENT_LAST
Definition colorspaces.h:68
dt_colorspaces_color_profile_type_t
Definition colorspaces.h:81
@ DT_COLORSPACE_NONE
Definition colorspaces.h:82
@ DT_COLORSPACE_LAST
static dt_aligned_pixel_t XYZ
char * dt_history_get_items_as_string(const int32_t imgid)
int32_t dt_image_import(const int32_t film_id, const char *filename, gboolean raise_signals)
const char darktable_package_version[]
#define GETTEXT_PACKAGE
const char darktable_last_commit_year[]
void dt_cleanup()
Definition darktable.c:1366
darktable_t darktable
Definition darktable.c:183
int dt_init(int argc, char *argv[], const gboolean init_gui, const gboolean load_data)
Definition darktable.c:489
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:485
#define dt_free(ptr)
Definition darktable.h:478
#define DT_MAX_PATH_FOR_PARAMS
Definition darktable.h:1198
#define PATH_MAX
Definition darktable.h:1189
#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 darktable.h:293
int dt_exif_xmp_read(dt_image_t *img, const char *filename, const int history_only)
Definition exif.cc:3109
void dt_loc_get_localedir(char *localedir, size_t bufsize)
void dt_loc_init(const char *datadir, const char *moduledir, const char *localedir, const char *configdir, const char *cachedir, const char *tmpdir, const char *kerneldir)
int dt_film_new(dt_film_t *film, const char *directory)
Definition film.c:161
GList * dt_film_get_image_ids(const int filmid)
Definition film.c:497
int dt_film_import(const char *dirname)
Definition film.c:236
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_image_cache_write_mode_t mode)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:51
int bpp
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)
void dt_l10n_disable_setlocale_early(void)
Definition l10n.c:90
float *const restrict const size_t k
uint32_t dt_lib_export_metadata_default_flags(void)
void dt_osx_prepare_environment()
Definition osx.mm:212
int main()
Definition prova.c:47
struct dt_image_cache_t * image_cache
Definition darktable.h:805
int32_t id
Definition image.h:319
#define MAX(a, b)
Definition thinplate.c:29