1 /**************************************************************************
2  **                                                                      **
3  ** Copyright (C) 2018 Lukas Spies                                       **
4  ** Contact: http://photoqt.org                                          **
5  **                                                                      **
6  ** This file is part of PhotoQt.                                        **
7  **                                                                      **
8  ** PhotoQt 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 2 of the License, or    **
11  ** (at your option) any later version.                                  **
12  **                                                                      **
13  ** PhotoQt 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 PhotoQt. If not, see <http://www.gnu.org/licenses/>.      **
20  **                                                                      **
21  **************************************************************************/
22 
23 #include "manipulation.h"
24 
GetAndDoStuffManipulation(QObject * parent)25 GetAndDoStuffManipulation::GetAndDoStuffManipulation(QObject *parent) : QObject(parent) { }
~GetAndDoStuffManipulation()26 GetAndDoStuffManipulation::~GetAndDoStuffManipulation() { }
27 
canBeScaled(QString filename)28 bool GetAndDoStuffManipulation::canBeScaled(QString filename) {
29 
30     // These image formats known by exiv2 are also supported by PhotoQt
31     QStringList formats;
32     formats << "jpeg"
33             << "jpg"
34             << "tif"
35             << "tiff"
36             << "png"
37             << "psd"
38             << "jpeg2000"
39             << "jp2"
40             << "jpc"
41             << "j2k"
42             << "jpf"
43             << "jpx"
44             << "jpm"
45             << "mj2"
46             << "bmp"
47             << "bitmap"
48             << "gif"
49             << "tga";
50 
51     return formats.contains(QFileInfo(filename).suffix().toLower());
52 
53 }
54 
scaleImage(QString filename,int width,int height,int quality,QString newfilename)55 bool GetAndDoStuffManipulation::scaleImage(QString filename, int width, int height, int quality, QString newfilename) {
56 
57     if(qgetenv("PHOTOQT_DEBUG") == "yes")
58         LOG << CURDATE << "GetAndDoStuffManipulation::scaleImage() - " << filename.toStdString() << " / " << width << " / " << height << " / " << quality << " / " << newfilename.toStdString() << NL;
59 
60     // These image formats known by exiv2 are also supported by PhotoQt
61     QStringList formats;
62     formats << "jpeg"
63             << "jpg"
64             << "tif"
65             << "tiff"
66             << "png"
67             << "psd"
68             << "jpeg2000"
69             << "jp2"
70             << "jpc"
71             << "j2k"
72             << "jpf"
73             << "jpx"
74             << "jpm"
75             << "mj2"
76             << "bmp"
77             << "bitmap"
78             << "gif"
79             << "tga";
80 
81 #ifdef EXIV2
82 
83     // This will store all the exif data
84     Exiv2::ExifData exifData;
85     bool gotExifData = false;
86 
87     if(formats.contains(QFileInfo(filename).suffix().toLower()) && formats.contains(QFileInfo(newfilename).suffix().toLower())) {
88 
89         if(qgetenv("PHOTOQT_DEBUG") == "yes") std::clog << "scale: image format supported by exiv2" << NL;
90 
91         try {
92 
93             // Open image for exif reading
94             Exiv2::Image::AutoPtr image_read = Exiv2::ImageFactory::open(filename.toStdString());
95 
96             if(image_read.get() != 0) {
97 
98                 // YAY, WE FOUND SOME!!!!!
99                 gotExifData = true;
100 
101                 // read exif
102                 image_read->readMetadata();
103                 exifData = image_read->exifData();
104 
105                 // Update dimensions
106                 exifData["Exif.Photo.PixelXDimension"] = int32_t(width);
107                 exifData["Exif.Photo.PixelYDimension"] = int32_t(height);
108 
109             }
110 
111         }
112 
113         catch (Exiv2::Error& e) {
114             std::cerr << "ERROR [scale]: reading exif data (caught exception): " << e.what() << NL;
115         }
116 
117     } else {
118         std::cerr << "ERROR [scale]: image format NOT supported by exiv2" << NL;
119         return false;
120     }
121 
122 
123 #endif
124 
125     // We need to do the actual scaling in between reading the exif data above and writing it below,
126     // since we might be scaling the image in place and thus would overwrite old exif data
127     QImageReader reader(filename);
128     reader.setScaledSize(QSize(width,height));
129     QImage img = reader.read();
130     if(!img.save(newfilename,0,quality)) {
131         std::cerr << "ERROR [scale]: Unable to save file";
132         return false;
133     }
134 
135 #ifdef EXIV2
136 
137     // We don't need to check again, if both files are actually supported formats, since if either one isn't supported, this bool cannot be true
138     if(gotExifData) {
139 
140         try {
141 
142             // And write exif data to new image file
143             Exiv2::Image::AutoPtr image_write = Exiv2::ImageFactory::open(newfilename.toStdString());
144             image_write->setExifData(exifData);
145             image_write->writeMetadata();
146 
147         }
148 
149         catch (Exiv2::Error& e) {
150             std::cerr << "ERROR [scale]: writing exif data (caught exception): " << e.what() << NL;
151         }
152 
153     }
154 
155 #endif
156 
157     return true;
158 
159 }
160 
161 
deleteImage(QString filename,bool trash)162 void GetAndDoStuffManipulation::deleteImage(QString filename, bool trash) {
163 
164     if(qgetenv("PHOTOQT_DEBUG") == "yes")
165         LOG << CURDATE << "GetAndDoStuffManipulation::deleteImage() - " << filename.toStdString() << " / " << trash << NL;
166 
167     filename = QByteArray::fromPercentEncoding(filename.toUtf8());
168 
169 #ifdef Q_OS_LINUX
170 
171     if(trash) {
172 
173         if(qgetenv("PHOTOQT_DEBUG") == "yes") std::clog << "fhd: Move to trash" << NL;
174 
175         // The file to delete
176         QFile f(filename);
177 
178         // Of course we only proceed if the file actually exists
179         if(f.exists()) {
180 
181             // Create the meta .trashinfo file
182             QString info = "[Trash Info]\n";
183             info += "Path=" + QUrl(filename).toEncoded() + "\n";
184             info += "DeletionDate=" + QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss");
185 
186             // The base patzh for the Trah (files on external devices  use the external device for Trash)
187             QString baseTrash = "";
188 
189             // If file lies in the home directory
190             if(QFileInfo(filename).absoluteFilePath().startsWith(QDir::homePath())) {
191 
192                 // Set the base path and make sure all the dirs exist
193                 baseTrash = ConfigFiles::GENERIC_DATA_DIR() + "/Trash/";
194 
195                 if(!QDir(baseTrash).exists())
196                     QDir().mkpath(baseTrash);
197                 if(!QDir(baseTrash + "files").exists())
198                     QDir().mkdir(baseTrash + "files");
199                 if(!QDir(baseTrash + "info").exists())
200                     QDir().mkdir(baseTrash + "info");
201             } else {
202                 // Set the base path and make sure all the dirs exist
203                 baseTrash = "/" + filename.split("/").at(1) + "/" + filename.split("/").at(2) + QString("/.Trash-%1/").arg(getuid());
204                 if(!QDir(baseTrash).exists())
205                     QDir().mkdir(baseTrash);
206                 if(!QDir(baseTrash + "files").exists())
207                     QDir().mkdir(baseTrash + "files");
208                 if(!QDir(baseTrash + "info").exists())
209                     QDir().mkdir(baseTrash + "info");
210 
211             }
212 
213             // that's the new trash file
214             QString trashFile = baseTrash + "files/" + QUrl::toPercentEncoding(QFileInfo(f).fileName(),""," ");
215             QString backupTrashFile = trashFile;
216 
217             // If there exists already a file with that name, we simply append the next higher number (sarting at 1)
218             QFile ensure(trashFile);
219             int j = 1;
220             while(ensure.exists()) {
221                 trashFile = backupTrashFile + QString(" (%1)").arg(j++);
222                 ensure.setFileName(trashFile);
223             }
224 
225             // Copy the file to the Trash
226             if(f.copy(trashFile)) {
227 
228                 // And remove the old file
229                 if(!f.remove())
230                     LOG << CURDATE << "GetAndDoStuffManipulation: ERROR: Old file couldn't be removed!" << NL;
231 
232                 // Write the .trashinfo file
233                 QFile i(baseTrash + "info/" + QFileInfo(trashFile).fileName() + ".trashinfo");
234                 if(i.open(QIODevice::WriteOnly)) {
235                     QTextStream out(&i);
236                     out << info;
237                     i.close();
238                 } else
239                     LOG << CURDATE << "GetAndDoStuffManipulation: ERROR: *.trashinfo file couldn't be created!" << NL;
240 
241             } else
242                 LOG << CURDATE << "GetAndDoStuffManipulation: ERROR: File couldn't be deleted (moving file failed)" << NL;
243 
244         } else
245             LOG << CURDATE << "GetAndDoStuffManipulation: ERROR: File '" << filename.toStdString() << "' doesn't exist...?" << NL;
246 
247     } else {
248 
249         if(qgetenv("PHOTOQT_DEBUG") == "yes") LOG << CURDATE << "GetAndDoStuffManipulation: fhd: Hard delete file" << NL;
250 
251         // current file
252         QFile file(filename);
253 
254         // Delete it if it exists (if it got here, the file should exist)
255         if(file.exists()) {
256 
257             file.remove();
258 
259         } else {
260             LOG << CURDATE << "GetAndDoStuffManipulation: ERROR! File '" << filename.toStdString() << "' doesn't exist...?" << NL;
261         }
262 
263     }
264 
265 #else
266 
267     if(qgetenv("PHOTOQT_DEBUG") == "yes") LOG << CURDATE << "GetAndDoStuffManipulation: fhd: Delete file" << NL;
268 
269     // current file
270     QFile file(filename);
271 
272     // Delete it if it exists (if it got here, the file should exist)
273     if(file.exists()) {
274 
275         file.remove();
276 
277     } else {
278         LOG << CURDATE << "GetAndDoStuffManipulation: ERROR! File doesn't exist...?" << NL;
279     }
280 
281 
282 #endif
283 
284 }
285 
copyImage(QString imagePath,QString destinationPath)286 void GetAndDoStuffManipulation::copyImage(QString imagePath, QString destinationPath) {
287 
288     if(qgetenv("PHOTOQT_DEBUG") == "yes")
289         LOG << CURDATE << "GetAndDoStuffManipulation::copyImage() - " << imagePath.toStdString() << " / " << destinationPath.toStdString() << NL;
290 
291     if(destinationPath.startsWith("file:/"))
292         destinationPath = destinationPath.remove(0,6);
293 #ifdef Q_OS_WIN
294     while(destinationPath.startsWith("/"))
295         destinationPath = destinationPath.remove(0,1);
296 #endif
297 
298     // Don't do anything here
299     if(destinationPath.trimmed() == "") return;
300     if(destinationPath == imagePath) return;
301 
302     // And copy file
303     QFile file(imagePath);
304     if(file.copy(destinationPath)) {
305         if(QFileInfo(destinationPath).absolutePath() == QFileInfo(imagePath).absolutePath())
306             emit reloadDirectory(destinationPath);
307     } else
308         LOG << CURDATE << "GetAndDoStuffManipulation: ERROR: Couldn't copy file" << NL;
309 
310 }
311 
moveImage(QString imagePath,QString destinationPath)312 void GetAndDoStuffManipulation::moveImage(QString imagePath, QString destinationPath) {
313 
314     if(qgetenv("PHOTOQT_DEBUG") == "yes")
315         LOG << CURDATE << "GetAndDoStuffManipulation::moveImage() - " << imagePath.toStdString() << " / " << destinationPath.toStdString() << NL;
316 
317     if(destinationPath.startsWith("file:/"))
318         destinationPath = destinationPath.remove(0,6);
319 #ifdef Q_OS_WIN
320     while(destinationPath.startsWith("/"))
321         destinationPath = destinationPath.remove(0,1);
322 #endif
323 
324     // Don't do anything here
325     if(destinationPath.trimmed() == "") return;
326     if(destinationPath == imagePath) return;
327 
328     // Make sure, that the right suffix is there...
329     if(QFileInfo(destinationPath).completeSuffix().toLower() != QFileInfo(imagePath).completeSuffix().toLower())
330         destinationPath += QFileInfo(imagePath).completeSuffix();
331 
332     // And move file
333     QFile file(imagePath);
334     if(file.copy(destinationPath)) {
335         if(!file.remove()) {
336             LOG << CURDATE << "GetAndDoStuffManipulation: ERROR: Couldn't remove old file" << NL;
337             if(QFileInfo(destinationPath).absolutePath() == QFileInfo(imagePath).absolutePath())
338                 emit reloadDirectory(destinationPath);
339         } else {
340             if(QFileInfo(destinationPath).absolutePath() == QFileInfo(imagePath).absolutePath())
341                 emit reloadDirectory(destinationPath);
342             else
343                 // A value of true signals, that the file has been moved to a different directory (i.e. "deleted" from current directory)
344                 reloadDirectory(imagePath,true);
345         }
346 
347     } else
348         LOG << CURDATE << "GetAndDoStuffManipulation: ERROR: Couldn't move file" << NL;
349 
350 }
351 
getImageBaseName(QString imagePath)352 QString GetAndDoStuffManipulation::getImageBaseName(QString imagePath) {
353 
354     return QFileInfo(imagePath).baseName();
355 
356 }
357