KExiv2

kexiv2.cpp
1/*
2 SPDX-FileCopyrightText: 2006-2015 Gilles Caulier <caulier dot gilles at gmail dot com>
3 SPDX-FileCopyrightText: 2006-2013 Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8#include "kexiv2.h"
9#include "kexiv2_p.h"
10
11// Local includes
12
13#include "libkexiv2_version.h"
14#include "libkexiv2_debug.h"
15
16namespace KExiv2Iface
17{
18
20 : d(new KExiv2Private)
21{
22}
23
24KExiv2::KExiv2(const KExiv2& metadata)
25 : d(new KExiv2Private)
26{
27 d->copyPrivateData(metadata.d.get());
28}
29
30KExiv2::KExiv2(const KExiv2Data& data)
31 : d(new KExiv2Private)
32{
33 setData(data);
34}
35
36KExiv2::KExiv2(const QString& filePath)
37 : d(new KExiv2Private)
38{
39 load(filePath);
40}
41
42KExiv2::~KExiv2() = default;
43
45{
46 d->copyPrivateData(metadata.d.get());
47
48 return *this;
49}
50
51//-- Statics methods ----------------------------------------------
52
54{
55#ifdef _XMP_SUPPORT_
56
57 if (!Exiv2::XmpParser::initialize())
58 return false;
59
60 registerXmpNameSpace(QString::fromLatin1("http://ns.adobe.com/lightroom/1.0/"), QString::fromLatin1("lr"));
61 registerXmpNameSpace(QString::fromLatin1("http://www.digikam.org/ns/kipi/1.0/"), QString::fromLatin1("kipi"));
62 registerXmpNameSpace(QString::fromLatin1("http://ns.microsoft.com/photo/1.2/"), QString::fromLatin1("MP"));
63 registerXmpNameSpace(QString::fromLatin1("http://ns.acdsee.com/iptc/1.0/"), QString::fromLatin1("acdsee"));
65
66#endif // _XMP_SUPPORT_
67
68#ifdef EXV_ENABLE_BMFF
69 Exiv2::enableBMFF(true);
70#endif
71
72 return true;
73}
74
76{
77 // Fix memory leak if Exiv2 support XMP.
78#ifdef _XMP_SUPPORT_
79
80 unregisterXmpNameSpace(QString::fromLatin1("http://ns.adobe.com/lightroom/1.0/"));
81 unregisterXmpNameSpace(QString::fromLatin1("http://www.digikam.org/ns/kipi/1.0/"));
82 unregisterXmpNameSpace(QString::fromLatin1("http://ns.microsoft.com/photo/1.2/"));
83 unregisterXmpNameSpace(QString::fromLatin1("http://ns.acdsee.com/iptc/1.0/"));
85
86 Exiv2::XmpParser::terminate();
87
88#endif // _XMP_SUPPORT_
89
90 return true;
91}
92
94{
95#ifdef _XMP_SUPPORT_
96 return true;
97#else
98 return false;
99#endif // _XMP_SUPPORT_
100}
101
103{
104 if (typeMime == QString::fromLatin1("image/jpeg"))
105 {
106 return true;
107 }
108 else if (typeMime == QString::fromLatin1("image/tiff"))
109 {
110 return true;
111 }
112 else if (typeMime == QString::fromLatin1("image/png"))
113 {
114 return true;
115 }
116 else if (typeMime == QString::fromLatin1("image/jp2"))
117 {
118 return true;
119 }
120 else if (typeMime == QString::fromLatin1("image/x-raw"))
121 {
122 return true;
123 }
124 else if (typeMime == QString::fromLatin1("image/pgf"))
125 {
126 return true;
127 }
128
129 return false;
130}
131
133{
134 // Since 0.14.0 release, we can extract run-time version of Exiv2.
135 // else we return make version.
136
137 return QString::fromStdString(Exiv2::versionString());
138}
139
141{
142 return QString::fromLatin1(KEXIV2_VERSION_STRING);
143}
144
146{
147 QString ret;
148
149 if (!path.isEmpty())
150 {
151 ret = path + QString::fromLatin1(".xmp");
152 }
153
154 return ret;
155}
156
164
169
171{
172 return sidecarFilePathForFile(path);
173}
174
176{
178}
179
180//-- General methods ----------------------------------------------
181
182KExiv2Data KExiv2::data() const
183{
184 KExiv2Data data;
185 data.d = d->data;
186 return data;
187}
188
189void KExiv2::setData(const KExiv2Data& data)
190{
191 if (data.d)
192 {
193 d->data = data.d;
194 }
195 else
196 {
197 // KExiv2Data can have a null pointer,
198 // but we never want a null pointer in Private.
199 d->data->clear();
200 }
201}
202
203bool KExiv2::loadFromData(const QByteArray& imgData) const
204{
205 if (imgData.isEmpty())
206 return false;
207
208 try
209 {
210#if EXIV2_TEST_VERSION(0,28,0)
211 Exiv2::Image::UniquePtr image = Exiv2::ImageFactory::open((Exiv2::byte*)imgData.data(), imgData.size());
212#else
213 Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((Exiv2::byte*)imgData.data(), imgData.size());
214#endif
215
216 d->filePath.clear();
217 image->readMetadata();
218
219 // Size and mimetype ---------------------------------
220
221 d->pixelSize = QSize(image->pixelWidth(), image->pixelHeight());
222 d->mimeType = QString::fromLatin1(image->mimeType().c_str());
223
224 // Image comments ---------------------------------
225
226 d->imageComments() = image->comment();
227
228 // Exif metadata ----------------------------------
229
230 d->exifMetadata() = image->exifData();
231
232 // Iptc metadata ----------------------------------
233
234 d->iptcMetadata() = image->iptcData();
235
236#ifdef _XMP_SUPPORT_
237
238 // Xmp metadata -----------------------------------
239
240 d->xmpMetadata() = image->xmpData();
241
242#endif // _XMP_SUPPORT_
243
244 return true;
245 }
246 catch( Exiv2::Error& e )
247 {
248 d->printExiv2ExceptionError(QString::fromLatin1("Cannot load metadata using Exiv2 "), e);
249 }
250 catch(...)
251 {
252 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
253 }
254
255 return false;
256}
257
258bool KExiv2::load(const QString& filePath) const
259{
260 if (filePath.isEmpty())
261 {
262 return false;
263 }
264
265 d->filePath = filePath;
266 bool hasLoaded = false;
267
268 try
269 {
270#if EXIV2_TEST_VERSION(0,28,0)
271 Exiv2::Image::UniquePtr image;
272#else
273 Exiv2::Image::AutoPtr image;
274#endif
275
276 image = Exiv2::ImageFactory::open((const char*)(QFile::encodeName(filePath)).constData());
277
278 image->readMetadata();
279
280 // Size and mimetype ---------------------------------
281
282 d->pixelSize = QSize(image->pixelWidth(), image->pixelHeight());
283 d->mimeType = QString::fromLatin1(image->mimeType().c_str());
284
285 // Image comments ---------------------------------
286
287 d->imageComments() = image->comment();
288
289 // Exif metadata ----------------------------------
290
291 d->exifMetadata() = image->exifData();
292
293 // Iptc metadata ----------------------------------
294
295 d->iptcMetadata() = image->iptcData();
296
297#ifdef _XMP_SUPPORT_
298
299 // Xmp metadata -----------------------------------
300 d->xmpMetadata() = image->xmpData();
301
302#endif // _XMP_SUPPORT_
303
304 hasLoaded = true;
305 }
306 catch( Exiv2::Error& e )
307 {
308 d->printExiv2ExceptionError(QString::fromLatin1("Cannot load metadata from file "), e);
309 }
310 catch(...)
311 {
312 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
313 }
314
315#ifdef _XMP_SUPPORT_
316 try
317 {
318 if (d->useXMPSidecar4Reading)
319 {
320 QString xmpSidecarPath = sidecarFilePathForFile(filePath);
321 QFileInfo xmpSidecarFileInfo(xmpSidecarPath);
322
323#if EXIV2_TEST_VERSION(0,28,0)
324 Exiv2::Image::UniquePtr xmpsidecar;
325#else
326 Exiv2::Image::AutoPtr xmpsidecar;
327#endif
328
329 if (xmpSidecarFileInfo.exists() && xmpSidecarFileInfo.isReadable())
330 {
331 // Read sidecar data
332 xmpsidecar = Exiv2::ImageFactory::open(QFile::encodeName(xmpSidecarPath).constData());
333 xmpsidecar->readMetadata();
334
335 // Merge
336#if EXIV2_TEST_VERSION(0,28,0)
337 d->loadSidecarData(std::move(xmpsidecar));
338#else
339 d->loadSidecarData(xmpsidecar);
340#endif
341 hasLoaded = true;
342 }
343 }
344 }
345 catch( Exiv2::Error& e )
346 {
347 d->printExiv2ExceptionError(QString::fromLatin1("Cannot load XMP sidecar"), e);
348 }
349 catch(...)
350 {
351 qCCritical(LIBKEXIV2_LOG) << "Default exception from Exiv2";
352 }
353
354#endif // _XMP_SUPPORT_
355
356 return hasLoaded;
357}
358
359bool KExiv2::save(const QString& imageFilePath) const
360{
361 // If our image is really a symlink, we should follow the symlink so that
362 // when we delete the file and rewrite it, we are honoring the symlink
363 // (rather than just deleting it and putting a file there).
364
365 // However, this may be surprising to the user when they are writing sidecar
366 // files. They might expect them to show up where the symlink is. So, we
367 // shouldn't follow the link when figuring out what the filename for the
368 // sidecar should be.
369
370 // Note, we are not yet handling the case where the sidecar itself is a
371 // symlink.
372 QString regularFilePath = imageFilePath; // imageFilePath might be a
373 // symlink. Below we will change
374 // regularFile to the pointed to
375 // file if so.
376 QFileInfo givenFileInfo(imageFilePath);
377
378 if (givenFileInfo.isSymLink())
379 {
380 qCDebug(LIBKEXIV2_LOG) << "filePath" << imageFilePath << "is a symlink."
381 << "Using target" << givenFileInfo.canonicalPath();
382
383 regularFilePath = givenFileInfo.canonicalPath();// Walk all the symlinks
384 }
385
386 // NOTE: see B.K.O #137770 & #138540 : never touch the file if is read only.
387 QFileInfo finfo(regularFilePath);
388 QFileInfo dinfo(finfo.path());
389
390 if (!dinfo.isWritable())
391 {
392 qCDebug(LIBKEXIV2_LOG) << "Dir '" << dinfo.filePath() << "' is read-only. Metadata not saved.";
393 return false;
394 }
395
396 bool writeToFile = false;
397 bool writeToSidecar = false;
398 bool writeToSidecarIfFileNotPossible = false;
399 bool writtenToFile = false;
400 bool writtenToSidecar = false;
401
402 qCDebug(LIBKEXIV2_LOG) << "KExiv2::metadataWritingMode" << d->metadataWritingMode;
403
404 switch(d->metadataWritingMode)
405 {
407 writeToSidecar = true;
408 break;
409 case WRITETOIMAGEONLY:
410 writeToFile = true;
411 break;
413 writeToFile = true;
414 writeToSidecar = true;
415 break;
417 writeToFile = true;
418 writeToSidecarIfFileNotPossible = true;
419 break;
420 }
421
422 if (writeToFile)
423 {
424 qCDebug(LIBKEXIV2_LOG) << "Will write Metadata to file" << finfo.absoluteFilePath();
425 writtenToFile = d->saveToFile(finfo);
426
427 if (writtenToFile)
428 {
429 qCDebug(LIBKEXIV2_LOG) << "Metadata for file" << finfo.fileName() << "written to file.";
430 }
431 }
432
433 if (writeToSidecar || (writeToSidecarIfFileNotPossible && !writtenToFile))
434 {
435 qCDebug(LIBKEXIV2_LOG) << "Will write XMP sidecar for file" << givenFileInfo.fileName();
436 writtenToSidecar = d->saveToXMPSidecar(QFileInfo(imageFilePath));
437
438 if (writtenToSidecar)
439 {
440 qCDebug(LIBKEXIV2_LOG) << "Metadata for file '" << givenFileInfo.fileName() << "' written to XMP sidecar.";
441 }
442 }
443
444 return writtenToFile || writtenToSidecar;
445}
446
448{
449 if (d->filePath.isEmpty())
450 {
451 qCDebug(LIBKEXIV2_LOG) << "Failed to apply changes: file path is empty!";
452 return false;
453 }
454
455 return save(d->filePath);
456}
457
458bool KExiv2::isEmpty() const
459{
460 if (!hasComments() && !hasExif() && !hasIptc() && !hasXmp())
461 return true;
462
463 return false;
464}
465
467{
468 d->filePath = path;
469}
470
472{
473 return d->filePath;
474}
475
477{
478 return d->pixelSize;
479}
480
482{
483 return d->mimeType;
484}
485
486void KExiv2::setWriteRawFiles(const bool on)
487{
488 d->writeRawFiles = on;
489}
490
492{
493 return d->writeRawFiles;
494}
495
497{
498 d->useXMPSidecar4Reading = on;
499}
500
502{
503 return d->useXMPSidecar4Reading;
504}
505
507{
508 d->metadataWritingMode = mode;
509}
510
512{
513 return d->metadataWritingMode;
514}
515
517{
518 d->updateFileTimeStamp = on;
519}
520
522{
523 return d->updateFileTimeStamp;
524}
525
526bool KExiv2::setProgramId(bool /*on*/) const
527{
528 return true;
529}
530
531} // NameSpace KExiv2Iface
virtual bool load(const QString &filePath) const
Load all metadata (Exif, Iptc, Xmp, and JFIF Comments) from a picture (JPEG, RAW, TIFF,...
Definition kexiv2.cpp:258
QString getFilePath() const
Return the file path of current image.
Definition kexiv2.cpp:471
bool applyChanges() const
The same than save() method, but it apply on current image.
Definition kexiv2.cpp:447
KExiv2()
Standard constructor.
Definition kexiv2.cpp:19
static QString sidecarPath(const QString &path)
Like sidecarFilePathForFile(), but works for local file path.
Definition kexiv2.cpp:170
void setWriteRawFiles(const bool on)
Enable or disable writing metadata operations to RAW tiff based files.
Definition kexiv2.cpp:486
static bool unregisterXmpNameSpace(const QString &uri)
Unregister a previously registered custom namespace.
virtual bool setProgramId(bool on=true) const
Re-implement this method to set automatically the Program Name and Program Version information in Exi...
Definition kexiv2.cpp:526
static QString sidecarFilePathForFile(const QString &path)
Return the XMP Sidecar file path for a image file path.
Definition kexiv2.cpp:145
bool loadFromData(const QByteArray &imgData) const
Load all metadata (Exif, Iptc, Xmp, and JFIF Comments) from a byte array.
Definition kexiv2.cpp:203
void setMetadataWritingMode(const int mode)
Set metadata writing mode.
Definition kexiv2.cpp:506
bool useXMPSidecar4Reading() const
Return true if using XMP sidecar for reading metadata is enabled.
Definition kexiv2.cpp:501
static bool cleanupExiv2()
Return true if Exiv2 library memory allocations are cleaned properly.
Definition kexiv2.cpp:75
bool hasXmp() const
Return 'true' if metadata container in memory as Xmp.
Definition kexiv2xmp.cpp:58
static bool supportMetadataWritting(const QString &typeMime)
Return true if library can write metadata to typeMime file format.
Definition kexiv2.cpp:102
bool isEmpty() const
Return 'true' if metadata container in memory as no Comments, Exif, Iptc, and Xmp.
Definition kexiv2.cpp:458
static QString Exiv2Version()
Return a string version of Exiv2 release in format "major.minor.patch".
Definition kexiv2.cpp:132
void setFilePath(const QString &path)
Set the file path of current image.
Definition kexiv2.cpp:466
@ WRITETOSIDECARANDIMAGE
Write metadata to image and sidecar files.
Definition kexiv2.h:67
@ WRITETOSIDECARONLY
Write metadata to sidecar file only.
Definition kexiv2.h:64
@ WRITETOIMAGEONLY
Write metadata to image file only.
Definition kexiv2.h:61
@ WRITETOSIDECARONLY4READONLYFILES
Write metadata to sidecar file only for read only images such as RAW files for example.
Definition kexiv2.h:70
static bool supportXmp()
Return true if library can handle Xmp metadata.
Definition kexiv2.cpp:93
static bool initializeExiv2()
Return true if Exiv2 library initialization is done properly.
Definition kexiv2.cpp:53
int metadataWritingMode() const
Return the metadata writing mode.
Definition kexiv2.cpp:511
bool hasIptc() const
Return 'true' if metadata container in memory as Iptc.
QSize getPixelSize() const
Returns the pixel size of the current image.
Definition kexiv2.cpp:476
bool hasComments() const
Return 'true' if metadata container in memory as Comments.
bool writeRawFiles() const
Return true if writing metadata operations on RAW tiff based files is enabled.
Definition kexiv2.cpp:491
static bool hasSidecar(const QString &path)
Performs a QFileInfo based check if the given local file has a sidecar.
Definition kexiv2.cpp:175
static QString version()
Return a string version of libkexiv2 release.
Definition kexiv2.cpp:140
static bool registerXmpNameSpace(const QString &uri, const QString &prefix)
Register a namespace which Exiv2 doesn't know yet.
static QUrl sidecarUrl(const QUrl &url)
Like sidecarFilePathForFile(), but works for remote URLs.
Definition kexiv2.cpp:157
bool updateFileTimeStamp() const
Return true if file timestamp is updated when metadata are saved.
Definition kexiv2.cpp:521
QString getMimeType() const
Returns the mime type of this image.
Definition kexiv2.cpp:481
void setUseXMPSidecar4Reading(const bool on)
Enable or disable using XMP sidecar for reading metadata.
Definition kexiv2.cpp:496
void setUpdateFileTimeStamp(bool on)
Enable or disable file timestamp updating when metadata are saved.
Definition kexiv2.cpp:516
bool hasExif() const
Return 'true' if metadata container in memory as Exif.
KExiv2 & operator=(const KExiv2 &metadata)
Create a copy of container.
Definition kexiv2.cpp:44
virtual ~KExiv2()
Standard destructor.
bool save(const QString &filePath) const
Save all metadata to a file.
Definition kexiv2.cpp:359
KExiv2Iface - Exiv2 library interface.
Definition kexiv2.cpp:17
char * data()
bool isEmpty() const const
qsizetype size() const const
QByteArray encodeName(const QString &fileName)
QString absoluteFilePath() const const
QString canonicalPath() const const
bool exists(const QString &path)
QString fileName() const const
QString filePath() const const
bool isReadable() const const
bool isWritable() const const
QString path() const const
QString fromLatin1(QByteArrayView str)
QString fromStdString(const std::string &str)
bool isEmpty() const const
QUrl fromLocalFile(const QString &localFile)
QString path(ComponentFormattingOptions options) const const
void setPath(const QString &path, ParsingMode mode)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:16:02 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.