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

KDE's Doxygen guidelines are available online.