1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2006-09-15
7  * Description : Exiv2 library interface
8  *               Exiv2: https://www.exiv2.org
9  *               Exif : https://www.exif.org/Exif2-2.PDF
10  *               Iptc : https://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf
11  *               Xmp  : https://www.adobe.com/devnet/xmp/pdfs/xmp_specification.pdf
12  *                      https://www.iptc.org/std/Iptc4xmpCore/1.0/specification/Iptc4xmpCore_1.0-spec-XMPSchema_8.pdf
13  *               Paper: www.metadataworkinggroup.com/pdf/mwg_guidance.pdf
14  *
15  * Copyright (C) 2006-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
16  * Copyright (C) 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
17  *
18  * This program is free software; you can redistribute it
19  * and/or modify it under the terms of the GNU General
20  * Public License as published by the Free Software Foundation;
21  * either version 2, or (at your option)
22  * any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * ============================================================ */
30 
31 #ifndef DIGIKAM_META_ENGINE_H
32 #define DIGIKAM_META_ENGINE_H
33 
34 // QT includes
35 
36 #include <QByteArray>
37 #include <QString>
38 #include <QDateTime>
39 #include <QMap>
40 #include <QSharedDataPointer>
41 #include <QStringList>
42 #include <QVariant>
43 #include <QRegExp>
44 #include <QUrl>
45 #include <QImage>
46 
47 // Local includes
48 
49 #include "metaengine_data.h"
50 #include "digikam_export.h"
51 
52 namespace Digikam
53 {
54 
55 // TODO: merge with DMetadata class.
56 
57 class DIGIKAM_EXPORT MetaEngine
58 {
59 
60 public:
61 
62     /**
63      * The item metadata writing mode, between item file metadata and XMP sidecar file, depending on the context.
64      * @sa MetadataWritingMode(), metadataWritingMode()
65      */
66     enum MetadataWritingMode
67     {
68         /// Write metadata to item file only.
69         WRITE_TO_FILE_ONLY                        = 0,
70 
71         /// Write metadata to sidecar file only.
72         WRITE_TO_SIDECAR_ONLY                     = 1,
73 
74         /// Write metadata to item and sidecar files.
75         WRITE_TO_SIDECAR_AND_FILE                 = 2,
76 
77         /// Write metadata to sidecar file only for read only items such as RAW files for example.
78         WRITE_TO_SIDECAR_ONLY_FOR_READ_ONLY_FILES = 3
79     };
80 
81     /**
82      * The item color workspace values given by Exif metadata.
83      */
84     enum ImageColorWorkSpace
85     {
86         WORKSPACE_UNSPECIFIED  = 0,
87         WORKSPACE_SRGB         = 1,
88         WORKSPACE_ADOBERGB     = 2,
89         WORKSPACE_UNCALIBRATED = 65535
90     };
91 
92     /**
93      * The item orientation values given by Exif metadata.
94      */
95     enum ImageOrientation
96     {
97         ORIENTATION_UNSPECIFIED  = 0,
98         ORIENTATION_NORMAL       = 1,
99         ORIENTATION_HFLIP        = 2,
100         ORIENTATION_ROT_180      = 3,
101         ORIENTATION_VFLIP        = 4,
102         ORIENTATION_ROT_90_HFLIP = 5,
103         ORIENTATION_ROT_90       = 6,
104         ORIENTATION_ROT_90_VFLIP = 7,
105         ORIENTATION_ROT_270      = 8
106     };
107 
108     /**
109      * Xmp tag types, used by setXmpTag, only first three types are used
110      */
111     enum XmpTagType
112     {
113         NormalTag    = 0,
114         ArrayBagTag  = 1,
115         StructureTag = 2,
116         ArrayLangTag = 3,
117         ArraySeqTag  = 4
118     };
119 
120     /**
121      * Metadata Backend used to populate information.
122      */
123     enum Backend
124     {
125         Exiv2Backend       = 0,   ///< Default backend used by MetaEngine
126         LibRawBackend,            ///< DMetadata only
127         LibHeifBackend,           ///< DMetadata only
128         ImageMagickBackend,       ///< DMetadata only
129         FFMpegBackend,            ///< DMetadata only
130         NoBackend                 ///< No backend used (aka file cannot be read)
131     };
132 
133     /**
134      * A map used to store Tags Key and Tags Value.
135      */
136     typedef QMap<QString, QString> MetaDataMap;
137 
138     /**
139      * A map used to store a list of Alternative Language values.
140      * The map key is the language code following RFC3066 notation
141      * (like "fr-FR" for French), and the map value the text.
142      */
143     typedef QMap<QString, QString> AltLangMap;
144 
145     /**
146      * A map used to store Tags Key and a list of Tags properties :
147      *  - name,
148      *  - title,
149      *  - description.
150      */
151     typedef QMap<QString, QStringList> TagsMap;
152 
153 public:
154 
155     /**
156      * Standard constructor.
157      */
158     MetaEngine();
159 
160     /**
161      * Constructor to load from parsed data.
162      */
163     explicit MetaEngine(const MetaEngineData& data);
164 
165     /**
166      * Contructor to Load Metadata from item file.
167      */
168     explicit MetaEngine(const QString& filePath);
169 
170     /**
171      * Standard destructor
172      */
173     virtual ~MetaEngine();
174 
175 public:
176 
177     //-----------------------------------------------------------------
178     /// @name Static methods
179     //@{
180 
181     /**
182      * Return true if Exiv2 library initialization is done properly.
183      * This method must be called before using libMetaEngine with multithreading.
184      * It initialize several non re-entrancy code from Adobe XMP SDK, and register
185      * a function to cleanup automatically all XMP SDK memory allocation.
186      * See Bug #166424 for details.
187      */
188     static bool initializeExiv2();
189 
190     /**
191      * Return true if Exiv2 library is compiled with Xmp metadata support.
192      */
193     static bool supportXmp();
194 
195     /**
196      * Return true if library support Base Media File Format (aka CR3, HEIF, HEIC, and AVIF).
197      * Note: use this function only after to call initializeExiv2(), else false will aways returned.
198      * The function return true only if Exiv2 >= 0.27.4 compiled with BMFF support.
199      */
200     static bool supportBmff();
201 
202     /**
203      * Return true if library can write metadata to typeMime file format.
204      */
205     static bool supportMetadataWriting(const QString& typeMime);
206 
207     /**
208      * Return a string version of Exiv2 release in format "major.minor.patch"
209      */
210     static QString Exiv2Version();
211 
212     //@}
213 
214     //-----------------------------------------------------------------
215     /// @name General methods
216     //@{
217 
218     MetaEngineData data() const;
219     void setData(const MetaEngineData& data);
220 
221     /**
222      * Load all metadata (Exif, Iptc, Xmp, and JFIF Comments) from a byte array.
223      * Return true if metadata have been loaded successfully from item data.
224      */
225     bool loadFromData(const QByteArray& imgData);
226 
227     /**
228      * Return 'true' if metadata container in memory as no Comments, Exif, Iptc, and Xmp.
229      */
230     bool isEmpty() const;
231 
232     /**
233      * Returns the pixel size of the current item. This information is read from the file,
234      * not from the metadata. The returned QSize is valid if the MetaEngine object was _constructed_
235      * by reading a file or item data; the information is not available when the object
236      * was created from MetaEngineData.
237      * Note that in the Exif or XMP metadata, there may be fields describing the item size.
238      * These fields are not accessed by this method.
239      * When replacing the metadata with setData(), the metadata may change; this information
240      * always keeps referring to the file it was initially read from.
241      */
242     QSize getPixelSize() const;
243 
244     /**
245      * Returns the mime type of this item. The information is read from the file;
246      * see the docs for getPixelSize() to know when it is available.
247      */
248     QString getMimeType() const;
249 
250     /**
251      * Enable or disable writing metadata operations to RAW tiff based files.
252      * It requires Exiv2 0.18. By default RAW files are untouched.
253      */
254     void setWriteRawFiles(const bool on);
255 
256     /**
257      * Return true if writing metadata operations on RAW tiff based files is enabled.
258      * It's require at least Exiv2 0.18.
259      */
260     bool writeRawFiles() const;
261 
262     /**
263      * Enable or disable writing metadata operations to DNG files.
264      */
265     void setWriteDngFiles(const bool on);
266 
267     /**
268      * Return true if writing metadata operations on DNG files is enabled.
269      */
270     bool writeDngFiles() const;
271 
272     /**
273      * Enable or disable using XMP sidecar for reading metadata.
274      */
275     void setUseXMPSidecar4Reading(const bool on);
276 
277     /**
278      * Return true if using XMP sidecar for reading metadata is enabled.
279      */
280     bool useXMPSidecar4Reading() const;
281 
282     /**
283      * Enable or disable using compatible file name for sidecar files.
284      */
285     void setUseCompatibleFileName(const bool on);
286 
287     /**
288      * Return true if using compatible file name for sidecar files.
289      */
290     bool useCompatibleFileName() const;
291 
292     /**
293      * Set metadata writing mode.
294      * @param mode Metadata writing mode as defined by the #MetadataWritingMode enum.
295      * @sa MetadataWritingMode, metadataWritingMode()
296      */
297     void setMetadataWritingMode(const int mode);
298 
299     /**
300      * Return the metadata writing mode.
301      * @returns Metadata writing mode as defined by the #MetadataWritingMode enum.
302      * @sa MetadataWritingMode, setMetadataWritingMode()
303      */
304     int metadataWritingMode() const;
305 
306     /**
307      * Enable or disable file timestamp updating when metadata are saved.
308      * By default files timestamp are untouched.
309      */
310     void setUpdateFileTimeStamp(bool on);
311 
312     /**
313      * Return true if file timestamp is updated when metadata are saved.
314      */
315     bool updateFileTimeStamp() const;
316 
317     //@}
318 
319     //-------------------------------------------------------------------
320     /// @name File I/O methods
321     //@{
322 
323     /**
324      * Set the file path of current item.
325      */
326     void setFilePath(const QString& path);
327 
328     /**
329      * Return the file path of current item.
330      */
331     QString getFilePath() const;
332 
333     /**
334      * Return the XMP Sidecar file path for a item file path.
335      * If item file path do not include a file name or is empty, this function return a null string.
336      */
337     static QString sidecarFilePathForFile(const QString& path, bool useLR = false);
338 
339     /**
340      * Like sidecarFilePathForFile(), but works for local file path.
341      */
342     static QString sidecarPath(const QString& path);
343 
344     /**
345      * Like sidecarFilePathForFile(), but works for remote URLs.
346      */
347     static QUrl sidecarUrl(const QUrl& url);
348 
349     /**
350      * Gives a file url for a local path.
351      */
352     static QUrl sidecarUrl(const QString& path);
353 
354     /**
355      * Performs a QFileInfo based check if the given local file has a sidecar.
356      */
357     static bool hasSidecar(const QString& path);
358 
359     /**
360      * Return a string of backend name used to parse metadata from file.
361      * See Backend enum for details.
362      */
363     static QString backendName(Backend t);
364 
365     /**
366      * Load all metadata (Exif, Iptc, Xmp, and JFIF Comments) from a picture (JPEG, RAW, TIFF, PNG,
367      * DNG, etc...). Return true if metadata have been loaded successfully from file.
368      * If backend is non null, return the backend used to populate metadata (Exiv2).
369      * See Backend enum for details.
370      */
371     bool load(const QString& filePath, Backend* backend = nullptr);
372 
373     /**
374      * Load metadata from a sidecar file and merge.
375      * Return true if metadata have been loaded successfully from file.
376      */
377     bool loadFromSidecarAndMerge(const QString& filePath);
378 
379     /**
380      * Save all metadata to a file. This one can be different than original picture to perform
381      * transfert operation Return true if metadata have been saved into file.
382      */
383     bool save(const QString& filePath, bool setVersion = false) const;
384 
385     /**
386      * The same than save() method, but it apply on current item. Return true if metadata
387      * have been saved into file.
388      */
389     bool applyChanges(bool setVersion = false) const;
390 
391     /**
392      * List changed tags compared to original file contents and export result to
393      * a temporary EXV file container.
394      * 'exvTmpFile' is the path to the temporary EXV container to create.
395      * 'removedTags' is populated with the list of tags removed.
396      */
397     bool exportChanges(const QString& exvTmpFile, QStringList& removedTags) const;
398 
399     //@}
400 
401     //-------------------------------------------------------------------
402     /// @name Metadata item information manipulation methods
403     //@{
404 
405     /**
406      * Set Program name and program version in Exif and Iptc Metadata. Return true if information
407      * have been changed in metadata.
408      */
409     bool setItemProgramId(const QString& program, const QString& version) const;
410 
411     /**
412      * Return the size of item in pixels using Exif tags. Return a null dimension if size cannot
413      * be found.
414      */
415     QSize getItemDimensions() const;
416 
417     /**
418      * Set the size of item in pixels in Exif tags. Return true if size have been changed
419      * in metadata.
420      */
421     bool setItemDimensions(const QSize& size) const;
422 
423     /**
424      * Return the item orientation set in Exif metadata. The makernotes of item are also parsed to
425      * get this information. See ImageOrientation values for details.
426      */
427     MetaEngine::ImageOrientation getItemOrientation() const;
428 
429     /**
430      * Set the Exif orientation tag of item. See ImageOrientation values for details
431      * Return true if orientation have been changed in metadata.
432      */
433     bool setItemOrientation(ImageOrientation orientation) const;
434 
435     /**
436      * Return the item color-space set in Exif metadata. The makernotes of item are also parsed to
437      * get this information. See ImageColorWorkSpace values for details.
438      */
439     MetaEngine::ImageColorWorkSpace getItemColorWorkSpace() const;
440 
441     /**
442      * Set the Exif color-space tag of item. See ImageColorWorkSpace values for details
443      * Return true if work-space have been changed in metadata.
444      */
445     bool setItemColorWorkSpace(ImageColorWorkSpace workspace) const;
446 
447     /**
448      * Return the time stamp of item. Exif information are check in first, IPTC in second
449      * if item don't have Exif information. If no time stamp is found, a null date is returned.
450      */
451     QDateTime getItemDateTime() const;
452 
453     /**
454      * Set the Exif and Iptc time stamp. If 'setDateTimeDigitized' parameter is true, the 'Digitalized'
455      * time stamp is set, else only 'Created' time stamp is set.
456      */
457     bool setImageDateTime(const QDateTime& dateTime, bool setDateTimeDigitized = false) const;
458 
459     /**
460      * Return the digitization time stamp of the item. First Exif information is checked, then IPTC.
461      * If no digitization time stamp is found, getItemDateTime() is called if fallbackToCreationTime
462      * is true, or a null QDateTime is returned if fallbackToCreationTime is false.
463      */
464     QDateTime getDigitizationDateTime(bool fallbackToCreationTime = false) const;
465 
466     /**
467      * Return a QImage copy of Iptc preview image. Return a null item if preview cannot
468      * be found.
469      */
470     bool getItemPreview(QImage& preview) const;
471 
472     /**
473      * Set the Iptc preview image. The thumbnail item must have the right size before (64Kb max
474      * with JPEG file, else 256Kb). Look Iptc specification for details. Return true if preview
475      * have been changed in metadata.
476      * Re-implement this method if you want to use another item file format than JPEG to
477      * save preview.
478      */
479     bool setItemPreview(const QImage& preview) const;
480 
481     //@}
482 
483     //-----------------------------------------------------------------
484     /// @name Comments manipulation methods
485     //@{
486 
487     /**
488      * Return 'true' if Comments can be written in file.
489      */
490     static bool canWriteComment(const QString& filePath);
491 
492     /**
493      * Return 'true' if metadata container in memory as Comments.
494      */
495     bool hasComments() const;
496 
497     /**
498      * Clear the Comments metadata container in memory.
499      */
500     bool clearComments() const;
501 
502     /**
503      * Return a Qt byte array copy of Comments container get from current item.
504      * Comments are JFIF section of JPEG images. Look Exiv2 API for more information.
505      * Return a null Qt byte array if there is no Comments metadata in memory.
506      */
507     QByteArray getComments() const;
508 
509     /**
510      * Return a Qt string object of Comments from current item decoded using
511      * the 'detectEncodingAndDecode()' method. Return a null string if there is no
512      * Comments metadata available.
513      */
514     QString getCommentsDecoded() const;
515 
516     /**
517      * Set the Comments data using a Qt byte array. Return true if Comments metadata
518      * have been changed in memory.
519      */
520     bool setComments(const QByteArray& data) const;
521 
522     /**
523      * Language Alternative autodetection. Return a QString without language alternative
524      * header. Header is saved into 'lang'. If no language alternative is founf, value is returned
525      * as well and 'lang' is set to a null string.
526      */
527     static QString detectLanguageAlt(const QString& value, QString& lang);
528 
529     //@}
530 
531     //-----------------------------------------------------------------
532     /// @name Exif manipulation methods
533     //@{
534 
535     /**
536      * Return a map of all standard Exif tags supported by Exiv2.
537      */
538     TagsMap getStdExifTagsList() const;
539 
540     /**
541      * Return a map of all non-standard Exif tags (makernotes) supported by Exiv2.
542      */
543     TagsMap getMakernoteTagsList() const;
544 
545     /**
546      * Return 'true' if Exif can be written in file.
547      */
548     static bool canWriteExif(const QString& filePath);
549 
550     /**
551      * Return 'true' if metadata container in memory as Exif.
552      */
553     bool hasExif() const;
554 
555     /**
556      * Clear the Exif metadata container in memory.
557      */
558     bool clearExif() const;
559 
560     /**
561      * Returns the exif data encoded to a QByteArray in a form suitable
562      * for storage in a JPEG image.
563      * Note that this encoding is a lossy operation.
564      *
565      * Set true 'addExifHeader' parameter to add an Exif header to Exif metadata.
566      * Returns a null Qt byte array if there is no Exif metadata in memory.
567      */
568     QByteArray getExifEncoded(bool addExifHeader = false) const;
569 
570     /**
571      * Set the Exif data using a Qt byte array. Return true if Exif metadata
572      * have been changed in memory.
573      */
574     bool setExif(const QByteArray& data) const;
575 
576     /**
577      * Return a QImage copy of Exif thumbnail image. Return a null image if thumbnail cannot
578      * be found. The 'fixOrientation' parameter will rotate automatically the thumbnail if Exif
579      * orientation tags information are attached with thumbnail.
580      */
581     QImage getExifThumbnail(bool fixOrientation) const;
582 
583     /**
584      * Fix orientation of a QImage image accordingly with Exif orientation tag.
585      * Return true if image is rotated, else false.
586      */
587     bool rotateExifQImage(QImage& image, ImageOrientation orientation) const;
588 
589     /**
590      * Set the Exif Thumbnail image. The thumbnail image must have the right dimensions before.
591      * Look Exif specification for details. Return true if thumbnail have been changed in metadata.
592      */
593     bool setExifThumbnail(const QImage& thumb) const;
594 
595     /**
596      * Remove the Exif Thumbnail from the item
597      */
598     bool removeExifThumbnail() const;
599 
600     /**
601      * Adds a JPEG thumbnail to a TIFF images. Use this instead of setExifThumbnail for TIFF images.
602      */
603     bool setTiffThumbnail(const QImage& thumb) const;
604 
605     /**
606      * Return a QString copy of Exif user comments. Return a null string if user comments cannot
607      * be found.
608      */
609     QString getExifComment(bool readDescription = true) const;
610 
611     /**
612      * Set the Exif user comments from item. Look Exif specification for more details about this tag.
613      * Return true if Exif user comments have been changed in metadata.
614      */
615     bool setExifComment(const QString& comment, bool writeDescription = true) const;
616 
617     /**
618      * Get an Exif tags content like a string. If 'escapeCR' parameter is true, the CR characters
619      * will be removed. If Exif tag cannot be found a null string is returned.
620      */
621     QString getExifTagString(const char* exifTagName, bool escapeCR = true) const;
622 
623     /**
624      * Set an Exif tag content using a string. Return true if tag is set successfully.
625      */
626     bool setExifTagString(const char* exifTagName, const QString& value) const;
627 
628     /**
629      * Get an Exif tag content like a long value. Return true if Exif tag be found.
630      */
631     bool getExifTagLong(const char* exifTagName, long &val) const;
632 
633     /**
634      * Get an Exif tag content like a long value. Return true if Exif tag be found.
635      */
636     bool getExifTagLong(const char* exifTagName, long &val, int component) const;
637 
638     /**
639      * Set an Exif tag content using a long value. Return true if tag is set successfully.
640      */
641     bool setExifTagLong(const char* exifTagName, long val) const;
642 
643     /**
644      * Get the 'component' index of an Exif tags content like a rational value.
645      * 'num' and 'den' are the numerator and the denominator of the rational value.
646      * Return true if Exif tag be found.
647      */
648     bool getExifTagRational(const char* exifTagName, long int& num, long int& den, int component = 0) const;
649 
650     /**
651      * Set an Exif tag content using a rational value.
652      * 'num' and 'den' are the numerator and the denominator of the rational value.
653      * Return true if tag is set successfully.
654      */
655     bool setExifTagRational(const char* exifTagName, long int num, long int den) const;
656 
657     /**
658      * Get an Exif tag content like a bytes array. Return an empty bytes array if Exif
659      * tag cannot be found.
660      */
661     QByteArray getExifTagData(const char* exifTagName) const;
662 
663     /**
664      * Set an Exif tag content using a bytes array. Return true if tag is set successfully.
665      */
666     bool setExifTagData(const char* exifTagName, const QByteArray& data) const;
667 
668     /**
669      * Get an Exif tags content as a QVariant. Returns a null QVariant if the Exif
670      * tag cannot be found.
671      * For string and integer values the matching QVariant types will be used,
672      * for date and time values QVariant::DateTime.
673      * Rationals will be returned as QVariant::List with two integer QVariants (numerator, denominator)
674      * if rationalAsListOfInts is true, as double if rationalAsListOfInts is false.
675      * An exif tag of numerical type may contain more than one value; set component to the desired index.
676      */
677     QVariant getExifTagVariant(const char* exifTagName, bool rationalAsListOfInts = true,
678                                bool escapeCR = true, int component = 0) const;
679 
680     /**
681      * Set an Exif tag content using a QVariant. Returns true if tag is set successfully.
682      * All types described for the getExifTagVariant() method are supported.
683      * Calling with a QVariant of type ByteArray is equivalent to calling setExifTagData.
684      * For the meaning of rationalWantSmallDenominator, see the documentation of the convertToRational methods.
685      * Setting a value with multiple components is currently not supported.
686      */
687     bool setExifTagVariant(const char* exifTagName, const QVariant& data,
688                            bool rationalWantSmallDenominator = true) const;
689 
690     /**
691      * Remove the Exif tag 'exifTagName' from Exif metadata. Return true if tag is
692      * removed successfully or if no tag was present.
693      */
694     bool removeExifTag(const char* exifTagName) const;
695 
696     /**
697      * Return the Exif Tag title or a null string.
698      */
699     QString getExifTagTitle(const char* exifTagName);
700 
701     /**
702      * Return the Exif Tag description or a null string.
703      */
704     QString getExifTagDescription(const char* exifTagName);
705 
706     /**
707      * Takes a QVariant value as it could have been retrieved by getExifTagVariant with the given exifTagName,
708      * and returns its value properly converted to a string (including translations from Exiv2).
709      * This is equivalent to calling getExifTagString directly.
710      * If escapeCR is true CR characters will be removed from the result.
711      */
712     QString createExifUserStringFromValue(const char* exifTagName, const QVariant& val, bool escapeCR = true);
713 
714     /**
715      * Return a map of Exif tags name/value found in metadata sorted by
716      * Exif keys given by 'exifKeysFilter'.
717      *
718      * 'exifKeysFilter' is a QStringList of Exif keys.
719      * For example, if you use the string list given below:
720      *
721      * "Iop"
722      * "Thumbnail"
723      * "Image"
724      * "Photo"
725      *
726      * List can be empty to not filter output.
727      *
728      * ... this method will return a map of all Exif tags which :
729      *
730      * - include "Iop", or "Thumbnail", or "Image", or "Photo" in the Exif tag keys
731      *   if 'inverSelection' is false.
732      * - not include "Iop", or "Thumbnail", or "Image", or "Photo" in the Exif tag keys
733      * if 'inverSelection' is true.
734      * if 'extractBinary" is true, tags with undefined types of data are extracted (default),
735      * else contents is replaced by "Binary data ... bytes". Take a care as large binary data as
736      * origianl RAW data from DNG container can be huge and listing Exif tags from GUI can take a while.
737      */
738     MetaEngine::MetaDataMap getExifTagsDataList(const QStringList& exifKeysFilter = QStringList(),
739                                                 bool invertSelection = false,
740                                                 bool extractBinary = true) const;
741 
742     //@}
743 
744     //-------------------------------------------------------------
745     /// @name IPTC manipulation methods
746     //@{
747 
748     /**
749      * Return a map of all standard Iptc tags supported by Exiv2.
750      */
751     MetaEngine::TagsMap getIptcTagsList() const;
752 
753     /**
754      * Return 'true' if Iptc can be written in file.
755      */
756     static bool canWriteIptc(const QString& filePath);
757 
758     /**
759      * Return 'true' if metadata container in memory as Iptc.
760      */
761     bool hasIptc() const;
762 
763     /**
764      * Clear the Iptc metadata container in memory.
765      */
766     bool clearIptc() const;
767 
768     /**
769      * Return a Qt byte array copy of Iptc container get from current item.
770      * Set true 'addIrbHeader' parameter to add an Irb header to Iptc metadata.
771      * Return a null Qt byte array if there is no Iptc metadata in memory.
772      */
773     QByteArray  getIptc(bool addIrbHeader = false) const;
774 
775     /**
776      * Set the Iptc data using a Qt byte array. Return true if Iptc metadata
777      * have been changed in memory.
778      */
779     bool setIptc(const QByteArray& data) const;
780 
781     /**
782      * Get an Iptc tag content like a string. If 'escapeCR' parameter is true, the CR characters
783      * will be removed. If Iptc tag cannot be found a null string is returned.
784      */
785     QString getIptcTagString(const char* iptcTagName, bool escapeCR = true) const;
786 
787     /**
788      * Set an Iptc tag content using a string. Return true if tag is set successfully.
789      */
790     bool setIptcTagString(const char* iptcTagName, const QString& value) const;
791 
792     /**
793      * Returns a strings list with of multiple Iptc tags from the item. Return an empty list if no tag is found.
794      * Get the values of all IPTC tags with the given tag name in a string list.
795      * (In Iptc, there can be multiple tags with the same name)
796      * If the 'escapeCR' parameter is true, the CR characters
797      * will be removed.
798      * If no tag can be found an empty list is returned.
799      */
800     QStringList getIptcTagsStringList(const char* iptcTagName, bool escapeCR = true) const;
801 
802     /**
803      * Set multiple Iptc tags contents using a strings list. 'maxSize' is the max characters size
804      * of one entry. Return true if all tags have been set successfully.
805      */
806     bool setIptcTagsStringList(const char* iptcTagName, int maxSize,
807                                const QStringList& oldValues, const QStringList& newValues) const;
808 
809     /**
810      * Get an Iptc tag content as a bytes array. Return an empty bytes array if Iptc
811      * tag cannot be found.
812      */
813     QByteArray getIptcTagData(const char* iptcTagName) const;
814 
815     /**
816      * Set an Iptc tag content using a bytes array. Return true if tag is set successfully.
817      */
818     bool setIptcTagData(const char* iptcTagName, const QByteArray& data) const;
819 
820     /**
821      * Remove the all instance of Iptc tags 'iptcTagName' from Iptc metadata. Return true if all
822      * tags have been removed successfully (or none were present).
823      */
824     bool removeIptcTag(const char* iptcTagName) const;
825 
826     /**
827      * Return the Iptc Tag title or a null string.
828      */
829     QString getIptcTagTitle(const char* iptcTagName);
830 
831     /**
832      * Return the Iptc Tag description or a null string.
833      */
834     QString getIptcTagDescription(const char* iptcTagName);
835 
836     /**
837      * Return a map of Iptc tags name/value found in metadata sorted by
838      * Iptc keys given by 'iptcKeysFilter'.
839      *
840      * 'iptcKeysFilter' is a QStringList of Iptc keys.
841      * For example, if you use the string list given below:
842      *
843      * "Envelope"
844      * "Application2"
845      *
846      * List can be empty to not filter output.
847      *
848      * ... this method will return a map of all Iptc tags which :
849      *
850      * - include "Envelope", or "Application2" in the Iptc tag keys
851      *   if 'inverSelection' is false.
852      * - not include "Envelope", or "Application2" in the Iptc tag keys
853      *   if 'inverSelection' is true.
854      */
855     MetaEngine::MetaDataMap getIptcTagsDataList(const QStringList& iptcKeysFilter = QStringList(),
856                                                 bool invertSelection = false) const;
857 
858     /**
859      * Return a strings list of Iptc keywords from item. Return an empty list if no keyword are set.
860      */
861     QStringList getIptcKeywords() const;
862 
863     /**
864      * Set Iptc keywords using a list of strings defined by 'newKeywords' parameter. Use 'getImageKeywords()'
865      * method to set 'oldKeywords' parameter with existing keywords from item. The method will compare
866      * all new keywords with all old keywords to prevent duplicate entries in item. Return true if keywords
867      * have been changed in metadata.
868      */
869     bool setIptcKeywords(const QStringList& oldKeywords, const QStringList& newKeywords) const;
870 
871     /**
872      * Return a strings list of Iptc subjects from item. Return an empty list if no subject are set.
873      */
874     QStringList getIptcSubjects() const;
875 
876     /**
877      * Set Iptc subjects using a list of strings defined by 'newSubjects' parameter. Use 'getImageSubjects()'
878      * method to set 'oldSubjects' parameter with existing subjects from item. The method will compare
879      * all new subjects with all old subjects to prevent duplicate entries in item. Return true if subjects
880      * have been changed in metadata.
881      */
882     bool setIptcSubjects(const QStringList& oldSubjects, const QStringList& newSubjects) const;
883 
884     /**
885      * Return a strings list of Iptc sub-categories from item. Return an empty list if no sub-category
886      * are set.
887      */
888     QStringList getIptcSubCategories() const;
889 
890     /**
891      * Set Iptc sub-categories using a list of strings defined by 'newSubCategories' parameter. Use
892      * 'getImageSubCategories()' method to set 'oldSubCategories' parameter with existing sub-categories
893      * from item. The method will compare all new sub-categories with all old sub-categories to prevent
894      * duplicate entries in item. Return true if sub-categories have been changed in metadata.
895      */
896     bool setIptcSubCategories(const QStringList& oldSubCategories, const QStringList& newSubCategories) const;
897 
898     //@}
899 
900     //------------------------------------------------------------
901     /// @name XMP manipulation methods
902     //@{
903 
904     /**
905      * Return a map of all standard Xmp tags supported by Exiv2.
906      */
907     MetaEngine::TagsMap getXmpTagsList() const;
908 
909     /**
910      * Return 'true' if Xmp can be written in file.
911      */
912     static bool canWriteXmp(const QString& filePath);
913 
914     /**
915      * Return 'true' if metadata container in memory as Xmp.
916      */
917     bool hasXmp() const;
918 
919     /**
920      * Clear the Xmp metadata container in memory.
921      */
922     bool clearXmp() const;
923 
924     /**
925      * Return a Qt byte array copy of XMp container get from current item.
926      * Return a null Qt byte array if there is no Xmp metadata in memory.
927      */
928     QByteArray getXmp() const;
929 
930     /**
931      * Set the Xmp data using a Qt byte array. Return true if Xmp metadata
932      * have been changed in memory.
933      */
934     bool setXmp(const QByteArray& data) const;
935 
936     /**
937      * Get a Xmp tag content like a string. If 'escapeCR' parameter is true, the CR characters
938      * will be removed. If Xmp tag cannot be found a null string is returned.
939      */
940     QString getXmpTagString(const char* xmpTagName, bool escapeCR = true) const;
941 
942     /**
943      * Set a Xmp tag content using a string. Return true if tag is set successfully.
944      */
945     bool setXmpTagString(const char* xmpTagName, const QString& value) const;
946 
947     /**
948      * Set a Xmp tag with a specific type. Return true if tag is set successfully.
949      * This method only accept NormalTag, ArrayBagTag and StructureTag.
950      * Other XmpTagTypes do nothing
951      */
952     bool setXmpTagString(const char* xmpTagName, const QString& value,
953                          XmpTagType type) const;
954 
955     /**
956      * Return the Xmp Tag title or a null string.
957      */
958     QString getXmpTagTitle(const char* xmpTagName);
959 
960     /**
961      * Return the Xmp Tag description or a null string.
962      */
963     QString getXmpTagDescription(const char* xmpTagName);
964 
965     /**
966      * Return a map of Xmp tags name/value found in metadata sorted by
967      * Xmp keys given by 'xmpKeysFilter'.
968      *
969      * 'xmpKeysFilter' is a QStringList of Xmp keys.
970      * For example, if you use the string list given below:
971      *
972      * "dc"           // Dubling Core schema.
973      * "xmp"          // Standard Xmp schema.
974      *
975      * List can be empty to not filter output.
976      *
977      * ... this method will return a map of all Xmp tags which :
978      *
979      * - include "dc", or "xmp" in the Xmp tag keys
980      *   if 'inverSelection' is false.
981      * - not include "dc", or "xmp" in the Xmp tag keys
982      *   if 'inverSelection' is true.
983      */
984     MetaEngine::MetaDataMap getXmpTagsDataList(const QStringList& xmpKeysFilter = QStringList(),
985                                                bool invertSelection = false) const;
986 
987     /**
988      * Get all redondant Alternative Language Xmp tags content like a map.
989      * See AltLangMap class description for details.
990      * If 'escapeCR' parameter is true, the CR characters will be removed from strings.
991      * If Xmp tag cannot be found a null string list is returned.
992      */
993     MetaEngine::AltLangMap getXmpTagStringListLangAlt(const char* xmpTagName,
994                                                       bool escapeCR = true) const;
995 
996     /**
997      * Set an Alternative Language Xmp tag content using a map. See AltLangMap class
998      * description for details. If tag already exist, it will be removed before.
999      * Return true if tag is set successfully.
1000      */
1001     bool setXmpTagStringListLangAlt(const char* xmpTagName, const MetaEngine::AltLangMap& values) const;
1002 
1003     /**
1004      * Get a Xmp tag content like a string set with an alternative language
1005      * header 'langAlt' (like "fr-FR" for French - RFC3066 notation)
1006      * If 'escapeCR' parameter is true, the CR characters will be removed.
1007      * If Xmp tag cannot be found a null string is returned.
1008      */
1009     QString getXmpTagStringLangAlt(const char* xmpTagName, const QString& langAlt, bool escapeCR) const;
1010 
1011     /**
1012      * Set a Xmp tag content using a string with an alternative language header. 'langAlt' contain the
1013      * language alternative information (like "fr-FR" for French - RFC3066 notation) or is null to
1014      * set alternative language to default settings ("x-default").
1015      * Return true if tag is set successfully.
1016      */
1017     bool setXmpTagStringLangAlt(const char* xmpTagName, const QString& value,
1018                                 const QString& langAlt) const;
1019 
1020     /**
1021      * Get a Xmp tag content like a sequence of strings. If 'escapeCR' parameter is true, the CR characters
1022      * will be removed from strings. If Xmp tag cannot be found a null string list is returned.
1023      */
1024     QStringList getXmpTagStringSeq(const char* xmpTagName, bool escapeCR = true) const;
1025 
1026     /**
1027      * Set a Xmp tag content using the sequence of strings 'seq'.
1028      * Return true if tag is set successfully.
1029      */
1030     bool setXmpTagStringSeq(const char* xmpTagName, const QStringList& seq) const;
1031 
1032     /**
1033      * Get a Xmp tag content like a bag of strings. If 'escapeCR' parameter is true, the CR characters
1034      * will be removed from strings. If Xmp tag cannot be found a null string list is returned.
1035      */
1036     QStringList getXmpTagStringBag(const char* xmpTagName, bool escapeCR) const;
1037 
1038     /**
1039      * Set a Xmp tag content using the bag of strings 'bag'.
1040      * Return true if tag is set successfully.
1041      */
1042     bool setXmpTagStringBag(const char* xmpTagName, const QStringList& bag) const;
1043 
1044     /**
1045      * Set an Xmp tag content using a list of strings defined by the 'entriesToAdd' parameter.
1046      * The existing entries are preserved. The method will compare
1047      * all new with all already existing entries to prevent duplicates in the item.
1048      * Return true if the entries have been added to metadata.
1049      */
1050     bool addToXmpTagStringBag(const char* xmpTagName, const QStringList& entriesToAdd) const;
1051 
1052     /**
1053      * Remove those Xmp tag entries that are listed in entriesToRemove from the entries in metadata.
1054      * Return true if tag entries are no longer contained in metadata.
1055      * All other entries are preserved.
1056      */
1057     bool removeFromXmpTagStringBag(const char* xmpTagName, const QStringList& entriesToRemove) const;
1058 
1059     /**
1060      * Get an Xmp tag content as a QVariant. Returns a null QVariant if the Xmp
1061      * tag cannot be found.
1062      * For string and integer values the matching QVariant types will be used,
1063      * for date and time values QVariant::DateTime.
1064      * Rationals will be returned as QVariant::List with two integer QVariants (numerator, denominator)
1065      * if rationalAsListOfInts is true, as double if rationalAsListOfInts is false.
1066      * Arrays (ordered, unordered, alternative) are returned as type StringList.
1067      * LangAlt values will have type Map (QMap<QString, QVariant>) with the language
1068      * code as key and the contents as value, of type String.
1069      */
1070     QVariant getXmpTagVariant(const char* xmpTagName, bool rationalAsListOfInts = true, bool stringEscapeCR = true) const;
1071 
1072     /**
1073      * Return a strings list of Xmp keywords from item. Return an empty list if no keyword are set.
1074      */
1075     QStringList getXmpKeywords() const;
1076 
1077     /**
1078      * Set Xmp keywords using a list of strings defined by 'newKeywords' parameter.
1079      * The existing keywords from item are preserved. The method will compare
1080      * all new keywords with all already existing keywords to prevent duplicate entries in item.
1081      * Return true if keywords have been changed in metadata.
1082      */
1083     bool setXmpKeywords(const QStringList& newKeywords) const;
1084 
1085     /**
1086      * Remove those Xmp keywords that are listed in keywordsToRemove from the keywords in metadata.
1087      * Return true if keywords are no longer contained in metadata.
1088      */
1089     bool removeXmpKeywords(const QStringList& keywordsToRemove);
1090 
1091     /**
1092      * Return a strings list of Xmp subjects from item. Return an empty list if no subject are set.
1093      */
1094     QStringList getXmpSubjects() const;
1095 
1096     /**
1097      * Set Xmp subjects using a list of strings defined by 'newSubjects' parameter.
1098      * The existing subjects from item are preserved. The method will compare
1099      * all new subject with all already existing subject to prevent duplicate entries in item.
1100      * Return true if subjects have been changed in metadata.
1101      */
1102     bool setXmpSubjects(const QStringList& newSubjects) const;
1103 
1104     /**
1105      * Remove those Xmp subjects that are listed in subjectsToRemove from the subjects in metadata.
1106      * Return true if subjects are no longer contained in metadata.
1107      */
1108     bool removeXmpSubjects(const QStringList& subjectsToRemove);
1109 
1110     /**
1111      * Return a strings list of Xmp sub-categories from item. Return an empty list if no sub-category
1112      * are set.
1113      */
1114     QStringList getXmpSubCategories() const;
1115 
1116     /**
1117      * Set Xmp sub-categories using a list of strings defined by 'newSubCategories' parameter.
1118      * The existing sub-categories from item are preserved. The method will compare
1119      * all new sub-categories with all already existing sub-categories to prevent duplicate entries in item.
1120      * Return true if sub-categories have been changed in metadata.
1121      */
1122     bool setXmpSubCategories(const QStringList& newSubCategories) const;
1123 
1124     /**
1125      * Remove those Xmp sub-categories that are listed in categoriesToRemove from the sub-categories in metadata.
1126      * Return true if subjects are no longer contained in metadata.
1127      */
1128     bool removeXmpSubCategories(const QStringList& categoriesToRemove);
1129 
1130     /**
1131      * Remove the Xmp tag 'xmpTagName' from Xmp metadata. Return true if tag is
1132      * removed successfully or if no tag was present.
1133      */
1134     bool removeXmpTag(const char* xmpTagName, bool family = false) const;
1135 
1136     /**
1137      * Register a namespace which Exiv2 doesn't know yet. This is only needed
1138      * when new Xmp properties are added manually. 'uri' is the namespace url and 'prefix' the
1139      * string used to construct new Xmp key (ex. "Xmp.digiKam.tagList").
1140      * NOTE: If the Xmp metadata is read from an item, namespaces are decoded and registered
1141      * by Exiv2 at the same time.
1142      */
1143     static bool registerXmpNameSpace(const QString& uri, const QString& prefix);
1144 
1145     /**
1146      * Unregister a previously registered custom namespace
1147      */
1148     static bool unregisterXmpNameSpace(const QString& uri);
1149 
1150     //@}
1151 
1152     //------------------------------------------------------------
1153     /// @name GPS manipulation methods
1154     //@{
1155 
1156     /**
1157      * Make sure all static required GPS EXIF and XMP tags exist
1158      */
1159     bool initializeGPSInfo();
1160 
1161     /**
1162      * Get all GPS location information set in item. Return true if all information can be found.
1163      */
1164     bool getGPSInfo(double& altitude, double& latitude, double& longitude) const;
1165 
1166     /**
1167      * Get GPS location information set in the item, in the GPSCoordinate format
1168      * as described in the XMP specification. Returns a null string in the information cannot be found.
1169      */
1170     QString getGPSLatitudeString()  const;
1171     QString getGPSLongitudeString() const;
1172 
1173     /**
1174      * Get GPS location information set in the item, as a double floating point number as in degrees
1175      * where the sign determines the direction ref (North + / South - ; East + / West -).
1176      * Returns true if the information is available.
1177      */
1178     bool getGPSLatitudeNumber(double* const latitude)   const;
1179     bool getGPSLongitudeNumber(double* const longitude) const;
1180 
1181     /**
1182      * Get GPS altitude information, in meters, relative to sea level (positive sign above sea level)
1183      */
1184     bool getGPSAltitude(double* const altitude) const;
1185 
1186     /**
1187      * Set all GPS location information into item. Return true if all information have been
1188      * changed in metadata.
1189      */
1190     bool setGPSInfo(const double altitude, const double latitude, const double longitude);
1191 
1192     /**
1193      * Set all GPS location information into item. Return true if all information have been
1194      * changed in metadata. If you do not want altitude to be set, pass a null pointer.
1195      */
1196     bool setGPSInfo(const double* const altitude, const double latitude, const double longitude);
1197 
1198     /**
1199      * Set all GPS location information into item. Return true if all information have been
1200      * changed in metadata.
1201      */
1202     bool setGPSInfo(const double altitude, const QString& latitude, const QString& longitude);
1203 
1204     /**
1205      * Remove all Exif tags relevant of GPS location information. Return true if all tags have been
1206      * removed successfully in metadata.
1207      */
1208     bool removeGPSInfo();
1209 
1210     /**
1211      * This method converts 'number' to a rational value, returned in the 'numerator' and
1212      * 'denominator' parameters. Set the precision using 'rounding' parameter.
1213      * Use this method if you want to retrieve a most exact rational for a number
1214      * without further properties, without any requirements to the denominator.
1215      */
1216     static void convertToRational(const double number,
1217                                   long int* const numerator,
1218                                   long int* const denominator,
1219                                   const int rounding);
1220 
1221     /**
1222      * This method convert a 'number' to a rational value, returned in 'numerator' and
1223      * 'denominator' parameters.
1224      * This method will be able to retrieve a rational number from a double - if you
1225      * constructed your double with 1.0 / 4786.0, this method will retrieve 1 / 4786.
1226      * If your number is not expected to be rational, use the method above which is just as
1227      * exact with rounding = 4 and more exact with rounding > 4.
1228      */
1229     static void convertToRationalSmallDenominator(const double number,
1230                                                   long int* const numerator,
1231                                                   long int* const denominator);
1232 
1233     /**
1234      * Converts degrees values as a double representation. This code take a care about hemisphere position.
1235      */
1236     static double convertDegreeAngleToDouble(double degrees, double minutes, double seconds);
1237 
1238 
1239     /**
1240      * Converts a GPS position stored as rationals in Exif to the form described
1241      * as GPSCoordinate in the XMP specification, either in the from "256,45,34N" or "256,45.566667N"
1242      */
1243     static QString convertToGPSCoordinateString(const long int numeratorDegrees,
1244                                                 const long int denominatorDegrees,
1245                                                 const long int numeratorMinutes,
1246                                                 const long int denominatorMinutes,
1247                                                 const long int numeratorSeconds,
1248                                                 const long int denominatorSeconds,
1249                                                 const char directionReference);
1250 
1251     /**
1252      * Converts a GPS position stored as double floating point number in degrees to the form described
1253      * as GPSCoordinate in the XMP specification.
1254      */
1255     static QString convertToGPSCoordinateString(const bool isLatitude,
1256                                                 double coordinate);
1257 
1258     /**
1259      * Converts a GPSCoordinate string as defined by XMP to three rationals and the direction reference.
1260      * Returns true if the conversion was successful.
1261      * If minutes is given in the fractional form, a denominator of 1000000 for the minutes will be used.
1262      */
1263     static bool convertFromGPSCoordinateString(const QString& coordinate,
1264                                                long int* const numeratorDegrees,
1265                                                long int* const denominatorDegrees,
1266                                                long int* const numeratorMinutes,
1267                                                long int* const denominatorMinutes,
1268                                                long int* const numeratorSeconds,
1269                                                long int* const denominatorSeconds,
1270                                                char* const directionReference);
1271 
1272     /**
1273      * Convert a GPSCoordinate string as defined by XMP to a double floating point number in degrees
1274      * where the sign determines the direction ref (North + / South - ; East + / West -).
1275      * Returns true if the conversion was successful.
1276      */
1277     static bool convertFromGPSCoordinateString(const QString& gpsString,
1278                                                double* const coordinate);
1279 
1280     /**
1281      * Converts a GPSCoordinate string to user presentable numbers, integer degrees and minutes and
1282      * double floating point seconds, and a direction reference ('N' or 'S', 'E' or 'W')
1283      */
1284     static bool convertToUserPresentableNumbers(const QString& coordinate,
1285                                                 int* const degrees,
1286                                                 int* const minutes,
1287                                                 double* const seconds,
1288                                                 char* const directionReference);
1289 
1290     /**
1291      * Converts a double floating point number to user presentable numbers, integer degrees and minutes and
1292      * double floating point seconds, and a direction reference ('N' or 'S', 'E' or 'W').
1293      * The method needs to know for the direction reference
1294      * if the latitude or the longitude is meant by the double parameter.
1295      */
1296     static void convertToUserPresentableNumbers(const bool isLatitude,
1297                                                 double coordinate,
1298                                                 int* const degrees,
1299                                                 int* const minutes,
1300                                                 double* const seconds,
1301                                                 char* const directionReference);
1302 
1303     //@}
1304 
1305 protected:
1306 
1307     /**
1308      * Set the Program Name and Program Version
1309      * information in Exif and Iptc metadata
1310      */
1311     bool setProgramId() const;
1312 
1313 private:
1314 
1315     // Disable copy constructor and operator to prevent potential slicing with this class, reported by Clazy static analyzer.
1316     // https://github.com/KDE/clazy/blob/master/docs/checks/README-copyable-polymorphic.md
1317     // This methods was implemented to be able to pass this class or a derived version to signals and slots. This is very
1318     // Dangerous as virtual methods are present in this polymorphic class and is copyable.
1319     // Instead to use this class in signals and slots, use MetaEngineData container.
1320     // TODO: remove legacy implementations for these methods later if no side effect.
1321     MetaEngine(const MetaEngine& metadata);
1322     MetaEngine& operator=(const MetaEngine& metadata);
1323 
1324 private:
1325 
1326     /**
1327      * Internal container to store private members. Used to improve binary compatibility
1328      */
1329     class Private;
1330     Private* const d;
1331 
1332     friend class MetaEnginePreviews;
1333 };
1334 
1335 } // namespace Digikam
1336 
1337 #endif // DIGIKAM_META_ENGINE_H
1338