File indexing completed on 2025-01-19 03:53:16
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2019-09-18 0007 * Description : DarkTable raw import plugin. 0008 * 0009 * SPDX-FileCopyrightText: 2019-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * Lua script inspired from Darktable Gimp plugin by Tobias Ellinghaus 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "rawimportdarktableplugin.h" 0018 0019 // Qt includes 0020 0021 #include <QMessageBox> 0022 #include <QApplication> 0023 #include <QPointer> 0024 #include <QByteArray> 0025 #include <QTextStream> 0026 #include <QFileInfo> 0027 #include <QSettings> 0028 #include <QTemporaryFile> 0029 0030 // KDE includes 0031 0032 #include <klocalizedstring.h> 0033 0034 // Local includes 0035 0036 #include "digikam_debug.h" 0037 #include "digikam_globals_p.h" // For KF6::Ki18n deprecated 0038 #include "dimg.h" 0039 #include "filteraction.h" 0040 #include "dfileoperations.h" 0041 #include "loadingdescription.h" 0042 0043 namespace DigikamRawImportDarkTablePlugin 0044 { 0045 0046 class Q_DECL_HIDDEN DarkTableRawImportPlugin::Private 0047 { 0048 public: 0049 0050 explicit Private() 0051 : darktable(nullptr) 0052 { 0053 } 0054 0055 static const QString luaScriptData; 0056 0057 QProcess* darktable; 0058 DImg decoded; 0059 LoadingDescription props; 0060 QString tempName; 0061 QTemporaryFile luaFile; 0062 }; 0063 0064 const QString DarkTableRawImportPlugin::Private::luaScriptData = QLatin1String( 0065 "\n" \ 0066 "local dt = require \"darktable\"\n" \ 0067 "\n" \ 0068 "local min_api_version = \"2.1.0\"\n" \ 0069 "if dt.configuration.api_version_string < min_api_version then\n" \ 0070 " dt.print(\"the exit export script requires at least darktable version 1.7.0\")\n" \ 0071 " dt.print_error(\"the exit export script requires at least darktable version 1.7.0\")\n" \ 0072 " return\n" \ 0073 "else\n" \ 0074 " dt.print(\"closing darktable will export the image and make image editor load it\")\n" \ 0075 "end\n" \ 0076 "\n" \ 0077 "local export_filename = dt.preferences.read(\"export_on_exit\", \"export_filename\", \"string\")\n" \ 0078 "\n" \ 0079 "function exit_function()\n" \ 0080 " -- safegurad against someone using this with their library containing 50k images\n" \ 0081 " if #dt.database > 1 then\n" \ 0082 " dt.print_error(\"too many images, only exporting the first\")\n" \ 0083 " -- return\n" \ 0084 " end\n" \ 0085 "\n" \ 0086 " -- change the view first to force writing of the history stack\n" \ 0087 " dt.gui.current_view(dt.gui.views.lighttable)\n" \ 0088 " -- now export\n" \ 0089 " local format = dt.new_format(\"png\")\n" \ 0090 " format.max_width = 0\n" \ 0091 " format.max_height = 0\n" \ 0092 " -- lets have the export in a loop so we could easily support > 1 images\n" \ 0093 " for _, image in ipairs(dt.database) do\n" \ 0094 " dt.print_error(\"exporting `\"..tostring(image)..\"' to `\"..export_filename..\"'\")\n" \ 0095 " format:write_image(image, export_filename)\n" \ 0096 " break -- only export one image. see above for the reason\n" \ 0097 " end\n" \ 0098 "end\n" \ 0099 "\n" \ 0100 "if dt.configuration.api_version_string >= \"6.2.1\" then\n" \ 0101 "dt.register_event(\"fileraw\", \"exit\", exit_function)\n" \ 0102 "else\n" \ 0103 "dt.register_event(\"exit\", exit_function)\n" \ 0104 "end\n" \ 0105 ); 0106 0107 DarkTableRawImportPlugin::DarkTableRawImportPlugin(QObject* const parent) 0108 : DPluginRawImport(parent), 0109 d (new Private) 0110 { 0111 d->luaFile.open(); 0112 QTextStream stream(&d->luaFile); 0113 stream << d->luaScriptData; 0114 stream.flush(); 0115 } 0116 0117 DarkTableRawImportPlugin::~DarkTableRawImportPlugin() 0118 { 0119 delete d; 0120 } 0121 0122 QString DarkTableRawImportPlugin::name() const 0123 { 0124 return QString::fromUtf8("Raw Import using DarkTable"); 0125 } 0126 0127 QString DarkTableRawImportPlugin::iid() const 0128 { 0129 return QLatin1String(DPLUGIN_IID); 0130 } 0131 0132 QIcon DarkTableRawImportPlugin::icon() const 0133 { 0134 return QIcon::fromTheme(QLatin1String("image-x-adobe-dng")); 0135 } 0136 0137 QString DarkTableRawImportPlugin::description() const 0138 { 0139 return QString::fromUtf8("A RAW import plugin based on DarkTable"); 0140 } 0141 0142 QString DarkTableRawImportPlugin::details() const 0143 { 0144 return QString::fromUtf8("<p>This RAW Import plugin use DarkTable tool to pre-process file in Image Editor.</p>" 0145 "<p>It requires at least DarkTable version 1.7.0 to work.</p>" 0146 "<p>See DarkTable web site for details: <a href='https://www.darktable.org/'>https://www.darktable.org/</a></p>"); 0147 } 0148 0149 QString DarkTableRawImportPlugin::handbookSection() const 0150 { 0151 return QLatin1String("setup_application"); 0152 } 0153 0154 QString DarkTableRawImportPlugin::handbookChapter() const 0155 { 0156 return QLatin1String("editor_settings"); 0157 } 0158 0159 QString DarkTableRawImportPlugin::handbookReference() const 0160 { 0161 return QLatin1String("setup-raw"); 0162 } 0163 0164 QList<DPluginAuthor> DarkTableRawImportPlugin::authors() const 0165 { 0166 return QList<DPluginAuthor>() 0167 << DPluginAuthor(QString::fromUtf8("Gilles Caulier"), 0168 QString::fromUtf8("caulier dot gilles at gmail dot com"), 0169 QString::fromUtf8("(C) 2019-2022")) 0170 ; 0171 } 0172 0173 void DarkTableRawImportPlugin::setup(QObject* const /*parent*/) 0174 { 0175 // Nothing to do 0176 } 0177 0178 QString DarkTableRawImportPlugin::getRawProgram() const 0179 { 0180 return DFileOperations::findExecutable(QLatin1String("darktable")); 0181 } 0182 0183 bool DarkTableRawImportPlugin::run(const QString& filePath, const DRawDecoding& /*def*/) 0184 { 0185 QFileInfo fileInfo(filePath); 0186 d->props = LoadingDescription(fileInfo.filePath(), LoadingDescription::ConvertForEditor); 0187 d->decoded = DImg(); 0188 0189 QTemporaryFile tempFile; 0190 tempFile.open(); 0191 d->tempName = tempFile.fileName(); 0192 0193 d->darktable = new QProcess(this); 0194 d->darktable->setProcessChannelMode(QProcess::MergedChannels); 0195 d->darktable->setWorkingDirectory(fileInfo.path()); 0196 d->darktable->setProcessEnvironment(adjustedEnvironmentForAppImage()); 0197 0198 connect(d->darktable, SIGNAL(errorOccurred(QProcess::ProcessError)), 0199 this, SLOT(slotErrorOccurred(QProcess::ProcessError))); 0200 0201 connect(d->darktable, SIGNAL(finished(int,QProcess::ExitStatus)), 0202 this, SLOT(slotProcessFinished(int,QProcess::ExitStatus))); 0203 0204 connect(d->darktable, SIGNAL(readyRead()), 0205 this, SLOT(slotProcessReadyRead())); 0206 0207 // -------- 0208 0209 d->darktable->setProgram(getRawProgram()); 0210 d->darktable->setArguments(QStringList() << QLatin1String("--library") 0211 << QLatin1String(":memory:") // Run DarkTable to process only one file 0212 << QLatin1String("--luacmd") 0213 << QString::fromUtf8("dofile('%1')") 0214 .arg(d->luaFile.fileName()) // LUA script to run in DarkTable 0215 << QLatin1String("--conf") 0216 << QLatin1String("plugins/lighttable/export/icctype=3") // Output color-space 0217 << QLatin1String("--conf") 0218 << QString::fromUtf8("lua/export_on_exit/export_filename=%1") 0219 .arg(d->tempName) // Output file 0220 << filePath); // Input file 0221 0222 qCDebug(DIGIKAM_GENERAL_LOG) << "DarkTable arguments:" << d->darktable->arguments(); 0223 0224 d->darktable->start(); 0225 0226 return d->darktable->waitForStarted(10000); 0227 } 0228 0229 void DarkTableRawImportPlugin::slotErrorOccurred(QProcess::ProcessError error) 0230 { 0231 switch (error) 0232 { 0233 case QProcess::FailedToStart: 0234 { 0235 qCDebug(DIGIKAM_GENERAL_LOG) << "DarkTable :: Process has failed to start"; 0236 break; 0237 } 0238 0239 case QProcess::Crashed: 0240 { 0241 qCDebug(DIGIKAM_GENERAL_LOG) << "DarkTable :: Process has crashed"; 0242 break; 0243 } 0244 0245 case QProcess::Timedout: 0246 { 0247 qCDebug(DIGIKAM_GENERAL_LOG) << "DarkTable :: Process time-out"; 0248 break; 0249 } 0250 0251 case QProcess::WriteError: 0252 { 0253 qCDebug(DIGIKAM_GENERAL_LOG) << "DarkTable :: Process write error"; 0254 break; 0255 } 0256 0257 case QProcess::ReadError: 0258 { 0259 qCDebug(DIGIKAM_GENERAL_LOG) << "DarkTable :: Process read error"; 0260 break; 0261 } 0262 0263 default: 0264 { 0265 qCDebug(DIGIKAM_GENERAL_LOG) << "DarkTable :: Process error unknown"; 0266 break; 0267 } 0268 } 0269 } 0270 0271 void DarkTableRawImportPlugin::slotProcessFinished(int code, QProcess::ExitStatus status) 0272 { 0273 qCDebug(DIGIKAM_GENERAL_LOG) << "DarkTable :: return code:" << code << ":: Exit status:" << status; 0274 0275 d->decoded = DImg(d->tempName); 0276 d->decoded.setAttribute(QLatin1String("isReadOnly"), true); 0277 0278 if (d->decoded.isNull()) 0279 { 0280 QString message = i18n("Error to import RAW image with DarkTable\nClose this dialog to load RAW image with native import tool"); 0281 QMessageBox::information(nullptr, qApp->applicationName(), message); 0282 0283 qCDebug(DIGIKAM_GENERAL_LOG) << "Decoded image is null! Load with Native tool..."; 0284 qCDebug(DIGIKAM_GENERAL_LOG) << d->props.filePath; 0285 0286 Q_EMIT signalLoadRaw(d->props); 0287 } 0288 else 0289 { 0290 qCDebug(DIGIKAM_GENERAL_LOG) << "Decoded image is not null..."; 0291 qCDebug(DIGIKAM_GENERAL_LOG) << d->props.filePath; 0292 d->props = LoadingDescription(d->tempName, LoadingDescription::ConvertForEditor); 0293 0294 FilterAction action(QLatin1String("darktable:RawConverter"), 1, FilterAction::DocumentedHistory); 0295 action.setDisplayableName(QString::fromUtf8(I18N_NOOP("DarkTable Raw Conversion"))); 0296 d->decoded.addFilterAction(action); 0297 0298 Q_EMIT signalDecodedImage(d->props, d->decoded); 0299 } 0300 0301 delete d->darktable; 0302 d->darktable = nullptr; 0303 0304 QFile::remove(d->tempName); 0305 } 0306 0307 void DarkTableRawImportPlugin::slotProcessReadyRead() 0308 { 0309 QByteArray data = d->darktable->readAllStandardError(); 0310 QStringList lines = QString::fromUtf8(data).split(QLatin1Char('\n'), QT_SKIP_EMPTY_PARTS); 0311 0312 Q_FOREACH (const QString& one, lines) 0313 { 0314 qCDebug(DIGIKAM_GENERAL_LOG) << "DarkTable ::" << one; 0315 } 0316 } 0317 0318 } // namespace DigikamRawImportDarkTablePlugin 0319 0320 #include "moc_rawimportdarktableplugin.cpp"