|
| 1 | +--[[ |
| 2 | +
|
| 3 | + gimp.lua - export and edit with GIMP |
| 4 | +
|
| 5 | + Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>. |
| 6 | +
|
| 7 | + Portions are lifted from hugin.lua and thus are |
| 8 | +
|
| 9 | + Copyright (c) 2014 Wolfgang Goetz |
| 10 | + Copyright (c) 2015 Christian Kanzian |
| 11 | + Copyright (c) 2015 Tobias Jakobs |
| 12 | +
|
| 13 | +
|
| 14 | + This program is free software: you can redistribute it and/or modify |
| 15 | + it under the terms of the GNU General Public License as published by |
| 16 | + the Free Software Foundation; either version 3 of the License, or |
| 17 | + (at your option) any later version. |
| 18 | +
|
| 19 | + This program is distributed in the hope that it will be useful, |
| 20 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 21 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 22 | + GNU General Public License for more details. |
| 23 | +
|
| 24 | + You should have received a copy of the GNU General Public License |
| 25 | + along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 26 | +]] |
| 27 | +--[[ |
| 28 | + gimp - export an image and open with GIMP for editing |
| 29 | +
|
| 30 | + This script provides another storage (export target) for darktable. Selected |
| 31 | + images are exported in the specified format to temporary storage. GIMP is launched |
| 32 | + and opens the files. After editing, the exported images are overwritten to save the |
| 33 | + changes. When GIMP exits, the exported files are moved into the current collection |
| 34 | + and imported into the database. The imported files then show up grouped with the |
| 35 | + originally selected images. |
| 36 | +
|
| 37 | + ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT |
| 38 | + * GIMP - http://www.gimp.org |
| 39 | +
|
| 40 | + USAGE |
| 41 | + * require this script from your main lua file |
| 42 | + * select an image or images for editing with GIMP |
| 43 | + * in the export dialog select "Edit with GIMP" and select the format and bit depth for the |
| 44 | + exported image |
| 45 | + * Press "export" |
| 46 | + * Edit the image with GIMP then save the changes with File->Overwrite.... |
| 47 | + * Exit GIMP |
| 48 | + * The edited image will be imported and grouped with the original image |
| 49 | +
|
| 50 | + CAVEATS |
| 51 | + * Developed and tested on Ubuntu 14.04 LTS with darktable 2.0.3 and GIMP 2.9.3 (development version with |
| 52 | + > 8 bit color) |
| 53 | + * There is no provision for dealing with the xcf files generated by GIMP, since darktable doesn't deal with |
| 54 | + them. You may want to save the xcf file if you intend on doing further edits to the image or need to save |
| 55 | + the layers used. Where you save them is up to you. |
| 56 | +
|
| 57 | + BUGS, COMMENTS, SUGGESTIONS |
| 58 | + * Send to Bill Ferguson, wpferguson@gmail.com |
| 59 | +]] |
| 60 | + |
| 61 | +local dt = require "darktable" |
| 62 | +local gettext = dt.gettext |
| 63 | + |
| 64 | +dt.configuration.check_version(...,{3,0,0}) |
| 65 | + |
| 66 | +-- Tell gettext where to find the .mo file translating messages for a particular domain |
| 67 | +gettext.bindtextdomain("gimp",dt.configuration.config_dir.."/lua/") |
| 68 | + |
| 69 | +local function split_filepath(str) |
| 70 | + local result = {} |
| 71 | + -- Thank you Tobias Jakobs for the awesome regular expression, which I tweaked a little |
| 72 | + result["path"], result["filename"], result["basename"], result["filetype"] = string.match(str, "(.-)(([^\\/]-)%.?([^%.\\/]*))$") |
| 73 | + return result |
| 74 | +end |
| 75 | + |
| 76 | +local function get_path(str) |
| 77 | + local parts = split_filepath(str) |
| 78 | + return parts["path"] |
| 79 | +end |
| 80 | + |
| 81 | +local function get_filename(str) |
| 82 | + local parts = split_filepath(str) |
| 83 | + return parts["filename"] |
| 84 | +end |
| 85 | + |
| 86 | +local function get_basename(str) |
| 87 | + local parts = split_filepath(str) |
| 88 | + return parts["basename"] |
| 89 | +end |
| 90 | + |
| 91 | +local function get_filetype(str) |
| 92 | + local parts = split_filepath(str) |
| 93 | + return parts["filetype"] |
| 94 | +end |
| 95 | + |
| 96 | +local function _(msgid) |
| 97 | + return gettext.dgettext("gimp", msgid) |
| 98 | +end |
| 99 | + |
| 100 | +local function checkIfBinExists(bin) |
| 101 | + local handle = io.popen("which "..bin) |
| 102 | + local result = handle:read() |
| 103 | + local ret |
| 104 | + handle:close() |
| 105 | + if (result) then |
| 106 | + dt.print_error("true checkIfBinExists: "..bin) |
| 107 | + ret = true |
| 108 | + else |
| 109 | + dt.print_error(bin.." not found") |
| 110 | + ret = false |
| 111 | + end |
| 112 | + return ret |
| 113 | +end |
| 114 | + |
| 115 | +-- Thanks Tobias Jakobs for the idea and the correction |
| 116 | +function checkIfFileExists(filepath) |
| 117 | + local file = io.open(filepath,"r") |
| 118 | + local ret |
| 119 | + if file ~= nil then |
| 120 | + io.close(file) |
| 121 | + dt.print_error("true checkIfFileExists: "..filepath) |
| 122 | + ret = true |
| 123 | + else |
| 124 | + dt.print_error(filepath.." not found") |
| 125 | + ret = false |
| 126 | + end |
| 127 | + return ret |
| 128 | +end |
| 129 | + |
| 130 | +local function filename_increment(filepath) |
| 131 | + |
| 132 | + -- break up the filepath into parts |
| 133 | + local path = get_path(filepath) |
| 134 | + local basename = get_basename(filepath) |
| 135 | + local filetype = get_filetype(filepath) |
| 136 | + |
| 137 | + -- check to see if we've incremented before |
| 138 | + local increment = string.match(basename, "_(%d-)$") |
| 139 | + |
| 140 | + if increment then |
| 141 | + -- we do 2 digit increments so make sure we didn't grab part of the filename |
| 142 | + if string.len(increment) > 2 then |
| 143 | + -- we got the filename so set the increment to 01 |
| 144 | + increment = "01" |
| 145 | + else |
| 146 | + increment = string.format("%02d", tonumber(increment) + 1) |
| 147 | + basename = string.gsub(basename, "_(%d-)$", "") |
| 148 | + end |
| 149 | + else |
| 150 | + increment = "01" |
| 151 | + end |
| 152 | + local incremented_filepath = path .. basename .. "_" .. increment .. "." .. filetype |
| 153 | + |
| 154 | + dt.print_error("original file was " .. filepath) |
| 155 | + dt.print_error("incremented file is " .. incremented_filepath) |
| 156 | + |
| 157 | + return incremented_filepath |
| 158 | +end |
| 159 | + |
| 160 | +local function groupIfNotMember(img, new_img) |
| 161 | + local image_table = img:get_group_members() |
| 162 | + local is_member = false |
| 163 | + for _,image in ipairs(image_table) do |
| 164 | + dt.print_error(image.filename .. " is a member") |
| 165 | + if image.filename == new_img.filename then |
| 166 | + is_member = true |
| 167 | + dt.print_error("Already in group") |
| 168 | + end |
| 169 | + end |
| 170 | + if not is_member then |
| 171 | + dt.print_error("group leader is "..img.group_leader.filename) |
| 172 | + new_img:group_with(img.group_leader) |
| 173 | + dt.print_error("Added to group") |
| 174 | + end |
| 175 | +end |
| 176 | + |
| 177 | +local function sanitize_filename(filepath) |
| 178 | + local path = get_path(filepath) |
| 179 | + local basename = get_basename(filepath) |
| 180 | + local filetype = get_filetype(filepath) |
| 181 | + |
| 182 | + local sanitized = string.gsub(basename, " ", "\\ ") |
| 183 | + |
| 184 | + return path .. sanitized .. "." .. filetype |
| 185 | +end |
| 186 | + |
| 187 | +local function show_status(storage, image, format, filename, |
| 188 | + number, total, high_quality, extra_data) |
| 189 | + dt.print(string.format(_("Export Image %i/%i"), number, total)) |
| 190 | +end |
| 191 | + |
| 192 | +local function gimp_edit(storage, image_table, extra_data) --finalize |
| 193 | + if not checkIfBinExists("gimp") then |
| 194 | + dt.print_error(_("GIMP not found")) |
| 195 | + return |
| 196 | + end |
| 197 | + |
| 198 | + -- list of exported images |
| 199 | + local img_list |
| 200 | + |
| 201 | + -- reset and create image list |
| 202 | + img_list = "" |
| 203 | + |
| 204 | + for _,exp_img in pairs(image_table) do |
| 205 | + exp_img = sanitize_filename(exp_img) |
| 206 | + img_list = img_list ..exp_img.. " " |
| 207 | + end |
| 208 | + |
| 209 | + dt.print(_("Launching GIMP...")) |
| 210 | + |
| 211 | + local gimpStartCommand |
| 212 | + gimpStartCommand = "gimp "..img_list |
| 213 | + |
| 214 | + dt.print_error(gimpStartCommand) |
| 215 | + |
| 216 | + coroutine.yield("RUN_COMMAND", gimpStartCommand) |
| 217 | + |
| 218 | + -- for each of the image, exported image pairs |
| 219 | + -- move the exported image into the directory with the original |
| 220 | + -- then import the image into the database which will group it with the original |
| 221 | + -- and then copy over any tags other than darktable tags |
| 222 | + |
| 223 | + for image,exported_image in pairs(image_table) do |
| 224 | + |
| 225 | + local myimage_name = image.path .. "/" .. get_filename(exported_image) |
| 226 | + |
| 227 | + while checkIfFileExists(myimage_name) do |
| 228 | + myimage_name = filename_increment(myimage_name) |
| 229 | + -- limit to 99 more exports of the original export |
| 230 | + if string.match(get_basename(myimage_name), "_(d-)$") == "99" then |
| 231 | + break |
| 232 | + end |
| 233 | + end |
| 234 | + |
| 235 | + dt.print_error("moving " .. exported_image .. " to " .. myimage_name) |
| 236 | + result = os.rename(exported_image, myimage_name) |
| 237 | + |
| 238 | + dt.print_error("importing file") |
| 239 | + local myimage = dt.database.import(myimage_name) |
| 240 | + |
| 241 | + groupIfNotMember(image, myimage) |
| 242 | + |
| 243 | + for _,tag in pairs(dt.tags.get_tags(image)) do |
| 244 | + if not (string.sub(tag.name,1,9) == "darktable") then |
| 245 | + dt.print_error("attaching tag") |
| 246 | + dt.tags.attach(tag,myimage) |
| 247 | + end |
| 248 | + end |
| 249 | + end |
| 250 | + |
| 251 | +end |
| 252 | + |
| 253 | +-- Register |
| 254 | +dt.register_storage("module_gimp", _("Edit with GIMP"), show_status, gimp_edit) |
| 255 | + |
| 256 | +-- |
| 257 | + |
| 258 | + |
0 commit comments