1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/* ============================================================
 *
 * This file is a part of digiKam project
 * https://www.digikam.org
 *
 * Date        : 2006-02-23
 * Description : item metadata interface
 *
 * SPDX-FileCopyrightText: 2006-2025 by Gilles Caulier <caulier dot gilles at gmail dot com>
 * SPDX-FileCopyrightText: 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
 * SPDX-FileCopyrightText: 2013      by Veaceslav Munteanu <veaceslav dot munteanu90 at gmail dot com>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * ============================================================ */

#pragma once

// Qt includes

#include <QByteArray>
#include <QUrl>

// Local includes

#include "digikam_config.h"
#include "digikam_export.h"
#include "metaengine.h"
#include "metaengine_data.h"
#include "metaenginesettingscontainer.h"
#include "metadatainfo.h"
#include "captionvalues.h"
#include "photoinfocontainer.h"
#include "videoinfocontainer.h"
#include "dmetadatasettings.h"

namespace Digikam
{

class Template;
class IccProfile;

class DIGIKAM_EXPORT DMetadata : public MetaEngine
{

public:

    /**
     * Video color model reported by FFMPEG following XMP DM Spec from Adobe.
     * These values are stored in DB as Image color model properties (extension of DImg::ColorModel)
     */
    enum VIDEOCOLORMODEL
    {
        VIDEOCOLORMODEL_UNKNOWN = 1000,
        VIDEOCOLORMODEL_OTHER,
        VIDEOCOLORMODEL_SRGB,
        VIDEOCOLORMODEL_BT709,
        VIDEOCOLORMODEL_BT601
    };

public:

    typedef QMap<QString, QString> CountryCodeMap;

public:

    DMetadata();
    explicit DMetadata(const QString& filePath);
    explicit DMetadata(const MetaEngineData& data);
    ~DMetadata() = default;<--- Destructor in derived class

public: // Settings helpers

    void registerMetadataSettings();
    void setSettings(const MetaEngineSettingsContainer& settings);

public: // File I/O helpers

    /**
     * Re-implemented from MetaEngine to use libraw identify, libheif,
     * ffmpeg probe, and ImageMAgick identify methods if Exiv2 failed.
     * If backend is non null, return the backend used to populate metadata (Exiv2).
     * See MetaEngine::Backend enum for details.
     */
    bool load(const QString& filePath, bool videoAll = false, Backend* backend = nullptr);
    bool save(const QString& filePath, bool setVersion = false)                                                         const override;
    bool applyChanges(bool setVersion = false)                                                                          const override;

    /**
     * Try to extract metadata using Raw Engine identify method (libraw).
     */
    bool loadUsingRawEngine(const QString& filePath);

public: // History helpers

    QString getItemHistory()                                                                                            const;
    bool    setItemHistory(const QString& imageHistoryXml)                                                              const;
    bool    hasItemHistoryTag()                                                                                         const;

    QString getItemUniqueId()                                                                                           const;
    bool    setItemUniqueId(const QString& uuid)                                                                        const;

public: // Faces helpers

    /**
     * Get Images Face Map based on tags stored in Picassa/Metadatagroup
     * format.
     */
    bool getItemFacesMap(QMultiMap<QString, QVariant>& facesPath)                                                       const;

    /**
     * Set Images Face Map tags in Picassa/Metadatagroup format.
     *
     * @param facesPath The face  map to register in metadata based on tags stored in Picassa/Metadatagroup
     * @param write If true all faces will be written, else update mode
     *              search if at least a face tag exist and write if true.
     * @param size The size of the area grouping all faces in image.
     */
    bool setItemFacesMap(const QMultiMap<QString, QVariant>& facesPath, bool write, const QSize& size = QSize())       const;

    /**
     * Remove Images Face Map tags from Picassa/Metadatagroup format.
     */
    bool removeItemFacesMap()                                                                                           const;

public: // Tags helpers

    bool getItemTagsPath(QStringList& tagsPath,
                         const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings())        const;
    bool setItemTagsPath(const QStringList& tagsPath,
                         const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings())        const;

    bool getACDSeeTagsPath(QStringList& tagsPath)                                                                       const;

    bool setACDSeeTagsPath(const QStringList& tagsPath)                                                                 const;

public: // Comments helpers

    CaptionsMap getItemComments(const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings()) const;
    bool setItemComments(const CaptionsMap& comments,
                         const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings())        const;

    CaptionsMap getItemTitles(const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings())   const;
    bool setItemTitles(const CaptionsMap& title,
                       const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings())          const;

    static MetaEngine::AltLangMap toAltLangMap(const QVariant& var);

public: // Labels helpers

    int  getItemRating(const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings())          const;
    bool setItemRating(int rating,
                       const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings())          const;

    int  getItemPickLabel(const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings())       const;
    bool setItemPickLabel(int pickId,
                          const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings())       const;

    int  getItemColorLabel(const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings())      const;
    bool setItemColorLabel(int colorId,
                           const DMetadataSettingsContainer& settings = DMetadataSettings::instance()->settings())      const;

public: // Template helpers

    bool     setMetadataTemplate(const Template& t)                                                                     const;
    Template getMetadataTemplate()                                                                                      const;
    bool     removeMetadataTemplate()                                                                                   const;

    /**
     * Fills only the copyright values in the template. Use getMetadataTemplate() usually.
     * Returns true if valid fields were read.
     */
    bool getCopyrightInformation(Template& t)                                                                           const;

public: // EXIF helpers

    /**
     * Reads an IccProfile that is described or embedded in the metadata.
     * This method does not retrieve profiles embedded in the image but from the Exif metadata,
     * e.g. embedded profiles in JPEG images.
     * Returns a null profile if no profile is found.
     */
    IccProfile getIccProfile()                                                                                          const;

    /**
     * Sets the IccProfile embedded in the Exif metadata.
     */
    bool setIccProfile(const IccProfile& profile);

    /**
     * Remove the Exif color space identification from the image.
     */
    bool removeExifColorSpace()                                                                                         const;

    /**
     * Returns millisecond time-stamp from Exif tags or 0 if not found.
     */
    int  getMSecsInfo()                                                                                                 const;

    /**
     * Extract milliseconds time-stamp of photo from an Exif tag and store it to 'ms'.
     * Returns true if data are extracted.
     */
    bool mSecTimeStamp(const char* const exifTagName, int& ms)                                                          const;

    bool removeExifTags(const QStringList& tagFilters);

private:

    QString getExifTagStringFromTagsList(const QStringList& tagsList)                                                   const;

public: // IPTC helpers

    IptcCoreContactInfo getCreatorContactInfo()                                                                         const;
    bool setCreatorContactInfo(const IptcCoreContactInfo& info)                                                         const;

    IptcCoreLocationInfo getIptcCoreLocation()                                                                          const;
    bool setIptcCoreLocation(const IptcCoreLocationInfo& location)                                                      const;

    QStringList getIptcCoreSubjects()                                                                                   const;

    bool removeIptcTags(const QStringList& tagFilters);

    /**
     * Return a map of ISO-639-1 2 letters country codes with country names.
     */
    static CountryCodeMap countryCodeMap();

    /**
     * Return a map of ISO-639-2 3 letters country codes with country names.
     */
    static CountryCodeMap countryCodeMap2();

private:

    bool setIptcTag(const QString& text,
                    int maxLength,
                    const char* const debugLabel,
                    const char* const tagKey)                                                                           const;

    QVariant fromIptcEmulateList(const char* const iptcTagName)                                                         const;
    QVariant fromIptcEmulateLangAlt(const char* const iptcTagName)                                                      const;

public: // XMP helpers

    bool removeXmpTags(const QStringList& tagFilters);

private:

    QVariant fromXmpList(const char* const xmpTagName)                                                                  const;
    QVariant fromXmpLangAlt(const char* const xmpTagName)                                                               const;

public: // Video helpers

    /**
     * Try to extract metadata using FFMpeg probe method (libav).
     */
    bool loadUsingFFmpeg(const QString& filePath);

    /**
     * Returns video metadata from Xmp tags.
     */
    VideoInfoContainer getVideoInformation()                                                                            const;

    /**
     * Helper method to translate enum values to user presentable strings
     */
    static QString videoColorModelToString(VIDEOCOLORMODEL videoColorModel);

public: // Photo helpers

    /**
     * Return a string with Lens mounted on the front of camera.
     * There no standard Exif tag for Lens information.
     * Camera makernotes and Xmp tags are parsed.
     * Take a care : lens information are not standardized and string content is not homogeneous between
     * camera model/maker.
     */
    QString getLensDescription()                                                                                        const;

    /**
     * Return a string with Camera serial number.
     */
    QString getCameraSerialNumber()                                                                                     const;

    PhotoInfoContainer getPhotographInformation()                                                                       const;

    static double apexApertureToFNumber(double aperture);
    static double apexShutterSpeedToExposureTime(double shutterSpeed);

public: // Generic helpers

    /**
     * Returns the requested metadata field as a QVariant. See metadatainfo.h for a specification
     * of the format of the QVariant.
     */
    QVariant     getMetadataField(MetadataInfo::Field field)                                                            const;
    QVariantList getMetadataFields(const MetadataFields& fields)                                                        const;

    /**
     * Convert a QVariant value of the specified field to a user-presentable, i18n'ed string.
     * The QVariant must be of the type as specified in metadatainfo.h and as obtained by getMetadataField.
     */
    static QString     valueToString (const QVariant& value, MetadataInfo::Field field);
    static QStringList valuesToString(const QVariantList& list, const MetadataFields& fields);

    /**
     * Returns a map of possible enum values and their user-presentable, i18n'ed representation.
     * Valid fields are those which are described as "enum from" or "bit mask from" in metadatainfo.h.
     */
    static QMap<int, QString> possibleValuesForEnumField(MetadataInfo::Field field);

private:

    QVariant fromExifOrXmp(const char* const exifTagName, const char* const xmpTagName)                                 const;
    QVariant fromIptcOrXmp(const char* const iptcTagName, const char* const xmpTagName)                                 const;
    QVariant fromExifOrXmpList(const QStringList& tagList)                                                              const;
    bool hasValidField(const QVariantList& list)                                                                        const;
    QVariant toStringListVariant(const QStringList& list)                                                               const;
    QVariant toStringVariant(const QString& str)                                                                        const;

#ifdef HAVE_HEIF

private:

    /**
     * Libheif helper methods.
     */
    bool loadUsingLibheif(const QString& filePath);

#endif

private:

    /**
     * ImageMagick helper methods.
     */
    bool loadUsingImageMagick(const QString& filePath);

private:

    /**
     * ExifTool helper methods.
     */
    bool loadUsingExifTool(const QString& filePath, bool videoAll = false);
    bool saveUsingExifTool(const QString& filePath) const;
};

} // namespace Digikam