1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * file-darktable.c -- raw file format plug-in that uses darktable
5 * Copyright (C) 2012 Simon Budig <simon@gimp.org>
6 * Copyright (C) 2016 Tobias Ellinghaus <me@houz.org>
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22 #include "config.h"
23
24 #include <glib/gstdio.h>
25
26 #include <libgimp/gimp.h>
27
28 #include "libgimp/stdplugins-intl.h"
29
30 #include "file-raw-formats.h"
31 #include "file-raw-utils.h"
32
33
34 #define LOAD_THUMB_PROC "file-darktable-load-thumb"
35 #define REGISTRY_KEY_BASE "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\darktable"
36
37
38 static void init (void);
39 static void query (void);
40 static void run (const gchar *name,
41 gint nparams,
42 const GimpParam *param,
43 gint *nreturn_vals,
44 GimpParam **return_vals);
45 static gint32 load_image (const gchar *filename,
46 GimpRunMode run_mode,
47 GError **error);
48
49 static gint32 load_thumbnail_image (const gchar *filename,
50 gint thumb_size,
51 gint *width,
52 gint *height,
53 GError **error);
54
55
56 const GimpPlugInInfo PLUG_IN_INFO =
57 {
58 init, /* init_proc */
59 NULL, /* quit_proc */
60 query, /* query proc */
61 run, /* run_proc */
62 };
63
MAIN()64 MAIN ()
65
66 static void
67 init (void)
68 {
69 static const GimpParamDef load_args[] =
70 {
71 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
72 { GIMP_PDB_STRING, "filename", "The name of the file to load." },
73 { GIMP_PDB_STRING, "raw-filename", "The name entered" },
74 };
75
76 static const GimpParamDef load_return_vals[] =
77 {
78 { GIMP_PDB_IMAGE, "image", "Output image" }
79 };
80
81 static const GimpParamDef thumb_args[] =
82 {
83 { GIMP_PDB_STRING, "filename", "The name of the file to load" },
84 { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
85 };
86
87 static const GimpParamDef thumb_return_vals[] =
88 {
89 { GIMP_PDB_IMAGE, "image", "Thumbnail image" },
90 { GIMP_PDB_INT32, "image-width", "Width of full-sized image" },
91 { GIMP_PDB_INT32, "image-height", "Height of full-sized image" }
92 };
93
94 /* check if darktable is installed
95 */
96 gboolean search_path = FALSE;
97 gchar *exec_path = file_raw_get_executable_path ("darktable", NULL,
98 "DARKTABLE_EXECUTABLE",
99 "org.darktable",
100 REGISTRY_KEY_BASE,
101 &search_path);
102 gchar *argv[] = { exec_path, "--version", NULL };
103 gchar *darktable_stdout = NULL;
104 gchar *darktable_stderr = NULL;
105 gboolean have_darktable = FALSE;
106 GError *error = NULL;
107 gint i;
108
109 /* allow the user to have some insight into why darktable may fail. */
110 gboolean debug_prints = g_getenv ("DARKTABLE_DEBUG") != NULL;
111
112 if (debug_prints)
113 g_printf ("[%s] trying to call '%s'\n", __FILE__, exec_path);
114
115 if (g_spawn_sync (NULL,
116 argv,
117 NULL,
118 (search_path ? G_SPAWN_SEARCH_PATH : 0),
119 NULL,
120 NULL,
121 &darktable_stdout,
122 &darktable_stderr,
123 NULL,
124 &error))
125 {
126 GRegex *regex;
127 GMatchInfo *matches;
128 gint major;
129 gint minor;
130
131 /* A default darktable would apparently output something like
132 * "this is darktable 2.2.5", but this version string is
133 * customizable. In the official Fedora package for instance, I
134 * encountered a "this is darktable darktable-2.2.5-4.fc27".
135 * Therefore make the version recognition a bit more flexible.
136 */
137 regex = g_regex_new ("this is darktable [^0-9]*([0-9]+)\\.([0-9]+)\\.([0-9]+)",
138 0, 0, NULL);
139 if (g_regex_match (regex, darktable_stdout, 0, &matches))
140 {
141 gchar *match;
142
143 match = g_match_info_fetch (matches, 1);
144 major = g_ascii_strtoll (match, NULL, 10);
145 g_free (match);
146
147 match = g_match_info_fetch (matches, 2);
148 minor = g_ascii_strtoll (match, NULL, 10);
149 g_free (match);
150
151 if (((major == 1 && minor >= 7) || major >= 2))
152 {
153 if (g_strstr_len (darktable_stdout, -1,
154 "Lua support enabled"))
155 {
156 have_darktable = TRUE;
157 }
158 }
159 }
160
161 g_match_info_free (matches);
162 g_regex_unref (regex);
163 }
164 else if (debug_prints)
165 {
166 g_printf ("[%s] g_spawn_sync failed\n", __FILE__);
167 }
168
169 if (debug_prints)
170 {
171 if (error)
172 g_printf ("[%s] error: %s\n", __FILE__, error->message);
173
174 if (darktable_stdout && *darktable_stdout)
175 g_printf ("[%s] stdout:\n%s\n", __FILE__, darktable_stdout);
176
177 if (darktable_stderr && *darktable_stderr)
178 g_printf ("[%s] stderr:\n%s\n", __FILE__, darktable_stderr);
179
180 g_printf ("[%s] have_darktable: %d\n", __FILE__, have_darktable);
181 }
182
183 g_clear_error (&error);
184
185 g_free (darktable_stdout);
186 g_free (darktable_stderr);
187 g_free (exec_path);
188
189 if (! have_darktable)
190 return;
191
192 gimp_install_procedure (LOAD_THUMB_PROC,
193 "Load thumbnail from a raw image via darktable",
194 "This plug-in loads a thumbnail from a raw image by calling darktable-cli.",
195 "Tobias Ellinghaus",
196 "Tobias Ellinghaus",
197 "2016",
198 NULL,
199 NULL,
200 GIMP_PLUGIN,
201 G_N_ELEMENTS (thumb_args),
202 G_N_ELEMENTS (thumb_return_vals),
203 thumb_args, thumb_return_vals);
204
205 for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
206 {
207 const FileFormat *format = &file_formats[i];
208 gchar *load_proc;
209 gchar *load_blurb;
210 gchar *load_help;
211
212 load_proc = g_strdup_printf (format->load_proc_format, "darktable");
213 load_blurb = g_strdup_printf (format->load_blurb_format, "darktable");
214 load_help = g_strdup_printf (format->load_help_format, "darktable");
215
216 gimp_install_procedure (load_proc,
217 load_blurb,
218 load_help,
219 "Tobias Ellinghaus",
220 "Tobias Ellinghaus",
221 "2016",
222 format->file_type,
223 NULL,
224 GIMP_PLUGIN,
225 G_N_ELEMENTS (load_args),
226 G_N_ELEMENTS (load_return_vals),
227 load_args, load_return_vals);
228
229 gimp_register_file_handler_mime (load_proc,
230 format->mime_type);
231 gimp_register_file_handler_raw (load_proc);
232 gimp_register_magic_load_handler (load_proc,
233 format->extensions,
234 "",
235 format->magic);
236
237 gimp_register_thumbnail_loader (load_proc, LOAD_THUMB_PROC);
238
239 g_free (load_proc);
240 g_free (load_blurb);
241 g_free (load_help);
242 }
243 }
244
245 static void
query(void)246 query (void)
247 {
248 /* query() is run only the first time for efficiency. Yet this plugin
249 * is dependent on the presence of darktable which may be installed
250 * or uninstalled between GIMP startups. Therefore we should move the
251 * usual gimp_install_procedure() to init() so that the check is done
252 * at every startup instead.
253 */
254 }
255
256 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)257 run (const gchar *name,
258 gint nparams,
259 const GimpParam *param,
260 gint *nreturn_vals,
261 GimpParam **return_vals)
262 {
263 static GimpParam values[6];
264 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
265 GimpRunMode run_mode;
266 gint image_ID;
267 GError *error = NULL;
268 gint i;
269
270 INIT_I18N ();
271
272 run_mode = param[0].data.d_int32;
273
274 *nreturn_vals = 1;
275 *return_vals = values;
276
277 values[0].type = GIMP_PDB_STATUS;
278 values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
279
280 /* check if the format passed is actually supported & load */
281 for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
282 {
283 const FileFormat *format = &file_formats[i];
284 gchar *load_proc = NULL;
285
286 if (format->load_proc_format)
287 load_proc = g_strdup_printf (format->load_proc_format, "darktable");
288
289 if (load_proc && ! strcmp (name, load_proc))
290 {
291 image_ID = load_image (param[1].data.d_string, run_mode, &error);
292
293 if (image_ID != -1)
294 {
295 *nreturn_vals = 2;
296 values[1].type = GIMP_PDB_IMAGE;
297 values[1].data.d_image = image_ID;
298 }
299 else
300 {
301 status = GIMP_PDB_EXECUTION_ERROR;
302 }
303
304 break;
305 }
306 else if (! strcmp (name, LOAD_THUMB_PROC))
307 {
308 gint width = 0;
309 gint height = 0;
310
311 image_ID = load_thumbnail_image (param[0].data.d_string,
312 param[1].data.d_int32,
313 &width,
314 &height,
315 &error);
316
317 if (image_ID != -1)
318 {
319 *nreturn_vals = 6;
320 values[1].type = GIMP_PDB_IMAGE;
321 values[1].data.d_image = image_ID;
322 values[2].type = GIMP_PDB_INT32;
323 values[2].data.d_int32 = width;
324 values[3].type = GIMP_PDB_INT32;
325 values[3].data.d_int32 = height;
326 values[4].type = GIMP_PDB_INT32;
327 values[4].data.d_int32 = GIMP_RGB_IMAGE;
328 values[5].type = GIMP_PDB_INT32;
329 values[5].data.d_int32 = 1; /* num_layers */
330 }
331 else
332 {
333 status = GIMP_PDB_EXECUTION_ERROR;
334 }
335
336 break;
337 }
338 }
339
340 if (i == G_N_ELEMENTS (file_formats))
341 status = GIMP_PDB_CALLING_ERROR;
342
343 if (status != GIMP_PDB_SUCCESS && error)
344 {
345 *nreturn_vals = 2;
346 values[1].type = GIMP_PDB_STRING;
347 values[1].data.d_string = error->message;
348 }
349
350 values[0].data.d_status = status;
351 }
352
353 static gint32
load_image(const gchar * filename,GimpRunMode run_mode,GError ** error)354 load_image (const gchar *filename,
355 GimpRunMode run_mode,
356 GError **error)
357 {
358 gint32 image_ID = -1;
359 GFile *lua_file = gimp_data_directory_file ("file-raw",
360 "file-darktable-export-on-exit.lua",
361 NULL);
362 gchar *lua_script = g_file_get_path (lua_file);
363 gchar *lua_script_escaped = g_strescape (lua_script, "");
364 gchar *lua_quoted = g_shell_quote (lua_script_escaped);
365 gchar *lua_cmd = g_strdup_printf ("dofile(%s)", lua_quoted);
366 gchar *filename_out = gimp_temp_name ("exr");
367 gchar *export_filename = g_strdup_printf ("lua/export_on_exit/export_filename=%s",
368 filename_out);
369
370 gchar *darktable_stdout = NULL;
371 gchar *darktable_stderr = NULL;
372
373 /* allow the user to have some insight into why darktable may fail. */
374 gboolean debug_prints = g_getenv ("DARKTABLE_DEBUG") != NULL;
375
376 /* linear sRGB for now as GIMP uses that internally in many places anyway */
377 gboolean search_path = FALSE;
378 gchar *exec_path = file_raw_get_executable_path ("darktable", NULL,
379 "DARKTABLE_EXECUTABLE",
380 "org.darktable",
381 REGISTRY_KEY_BASE,
382 &search_path);
383 gchar *argv[] =
384 {
385 exec_path,
386 "--library", ":memory:",
387 "--luacmd", lua_cmd,
388 "--conf", "plugins/lighttable/export/icctype=3",
389 "--conf", export_filename,
390 (gchar *) filename,
391 NULL
392 };
393
394 g_object_unref (lua_file);
395 g_free (lua_script);
396 g_free (lua_script_escaped);
397 g_free (lua_quoted);
398
399 gimp_progress_init_printf (_("Opening '%s'"),
400 gimp_filename_to_utf8 (filename));
401
402 if (debug_prints)
403 {
404 gchar **environ = g_get_environ ();
405 gint i;
406
407 g_printf ("[%s] trying to call\n", __FILE__);
408 for (gchar **iter = argv; *iter; iter++)
409 g_printf (" %s\n", *iter);
410 g_printf ("\n");
411
412 g_printf ("## Environment ##\n");
413 for (i = 0; environ[i]; i++)
414 g_printf ("- %s\n", environ[i]);
415 g_strfreev (environ) ;
416 }
417
418 if (g_spawn_sync (NULL,
419 argv,
420 NULL,
421 /*G_SPAWN_STDOUT_TO_DEV_NULL |*/
422 /*G_SPAWN_STDERR_TO_DEV_NULL |*/
423 (search_path ? G_SPAWN_SEARCH_PATH : 0),
424 NULL,
425 NULL,
426 &darktable_stdout,
427 &darktable_stderr,
428 NULL,
429 error))
430 {
431 image_ID = gimp_file_load (run_mode, filename_out, filename_out);
432 if (image_ID != -1)
433 gimp_image_set_filename (image_ID, filename);
434 }
435
436 if (debug_prints)
437 {
438 if (darktable_stdout && *darktable_stdout)
439 g_printf ("\n## stdout ##\n%s\n", darktable_stdout);
440
441 if (darktable_stderr && *darktable_stderr)
442 g_printf ("\n## stderr ##\n%s\n", darktable_stderr);
443 }
444
445 g_free (darktable_stdout);
446 g_free (darktable_stderr);
447
448 g_unlink (filename_out);
449 g_free (lua_cmd);
450 g_free (filename_out);
451 g_free (export_filename);
452 g_free (exec_path);
453
454 gimp_progress_update (1.0);
455
456 return image_ID;
457 }
458
459 static gint32
load_thumbnail_image(const gchar * filename,gint thumb_size,gint * width,gint * height,GError ** error)460 load_thumbnail_image (const gchar *filename,
461 gint thumb_size,
462 gint *width,
463 gint *height,
464 GError **error)
465 {
466 gint32 image_ID = -1;
467 gchar *filename_out = gimp_temp_name ("jpg");
468 gchar *size = g_strdup_printf ("%d", thumb_size);
469 GFile *lua_file = gimp_data_directory_file ("file-raw",
470 "file-darktable-get-size.lua",
471 NULL);
472 gchar *lua_script = g_file_get_path (lua_file);
473 gchar *lua_script_escaped = g_strescape (lua_script, "");
474 gchar *lua_quoted = g_shell_quote (lua_script_escaped);
475 gchar *lua_cmd = g_strdup_printf ("dofile(%s)", lua_quoted);
476 gchar *darktable_stdout = NULL;
477
478 gboolean search_path = FALSE;
479 gchar *exec_path = file_raw_get_executable_path ("darktable", "-cli",
480 "DARKTABLE_EXECUTABLE",
481 "org.darktable",
482 REGISTRY_KEY_BASE,
483 &search_path);
484 gchar *argv[] =
485 {
486 exec_path,
487 (gchar *) filename, filename_out,
488 "--width", size,
489 "--height", size,
490 "--hq", "false",
491 "--core",
492 "--conf", "plugins/lighttable/export/icctype=3",
493 "--luacmd", lua_cmd,
494 NULL
495 };
496
497 g_object_unref (lua_file);
498 g_free (lua_script);
499 g_free (lua_script_escaped);
500 g_free (lua_quoted);
501
502 gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
503 gimp_filename_to_utf8 (filename));
504
505 *width = *height = thumb_size;
506
507 if (g_spawn_sync (NULL,
508 argv,
509 NULL,
510 G_SPAWN_STDERR_TO_DEV_NULL |
511 (search_path ? G_SPAWN_SEARCH_PATH : 0),
512 NULL,
513 NULL,
514 &darktable_stdout,
515 NULL,
516 NULL,
517 error))
518 {
519 gimp_progress_update (0.5);
520
521 image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
522 filename_out,
523 filename_out);
524 if (image_ID != -1)
525 {
526 /* the size reported by raw files isn't precise,
527 * but it should be close enough to get an idea.
528 */
529 gchar *start_of_size = g_strstr_len (darktable_stdout,
530 -1,
531 "[dt4gimp]");
532 if (start_of_size)
533 sscanf (start_of_size, "[dt4gimp] %d %d", width, height);
534
535 /* is this needed for thumbnails? */
536 gimp_image_set_filename (image_ID, filename);
537 }
538 }
539
540 gimp_progress_update (1.0);
541
542 g_unlink (filename_out);
543 g_free (filename_out);
544 g_free (size);
545 g_free (lua_cmd);
546 g_free (darktable_stdout);
547 g_free (exec_path);
548
549 return image_ID;
550 }
551