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