Coverage Report

Created: 2025-08-26 06:58

/src/exiv2/include/exiv2/xmp_exiv2.hpp
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
3
#ifndef EXIV2_XMP_EXIV2_HPP
4
#define EXIV2_XMP_EXIV2_HPP
5
6
// *****************************************************************************
7
#include "exiv2lib_export.h"
8
9
// included header files
10
#include "datasets.hpp"
11
#include "metadatum.hpp"
12
13
// *****************************************************************************
14
// namespace extensions
15
namespace Exiv2 {
16
// *****************************************************************************
17
// class declarations
18
class ExifData;
19
class XmpKey;
20
21
// *****************************************************************************
22
// class definitions
23
24
/*!
25
  @brief Information related to an XMP property. An XMP metadatum consists
26
         of an XmpKey and a Value and provides methods to manipulate these.
27
 */
28
class EXIV2API Xmpdatum : public Metadatum {
29
 public:
30
  //! @name Creators
31
  //@{
32
  /*!
33
    @brief Constructor for new tags created by an application. The
34
           %Xmpdatum is created from a key / value pair. %Xmpdatum
35
           copies (clones) the value if one is provided. Alternatively, a
36
           program can create an 'empty' %Xmpdatum with only a key and
37
           set the value using setValue().
38
39
    @param key The key of the %Xmpdatum.
40
    @param pValue Pointer to a %Xmpdatum value.
41
    @throw Error if the key cannot be parsed and converted
42
           to a known schema namespace prefix and property name.
43
   */
44
  explicit Xmpdatum(const XmpKey& key, const Value* pValue = nullptr);
45
  //! Copy constructor
46
  Xmpdatum(const Xmpdatum& rhs);
47
  //! Destructor
48
  ~Xmpdatum() override;
49
  //@}
50
51
  //! @name Manipulators
52
  //@{
53
  //! Assignment operator
54
  Xmpdatum& operator=(const Xmpdatum& rhs);
55
  /*!
56
    @brief Assign std::string \em value to the %Xmpdatum.
57
           Calls setValue(const std::string&).
58
   */
59
  template <typename T>
60
  Xmpdatum& operator=(const T& value);
61
  /*!
62
    @brief Assign Value \em value to the %Xmpdatum.
63
           Calls setValue(const Value*).
64
   */
65
  void setValue(const Value* pValue) override;
66
  /*!
67
    @brief Set the value to the string \em value. Uses Value::read(const
68
           std::string&).  If the %Xmpdatum does not have a Value yet,
69
           then a %Value of the correct type for this %Xmpdatum is
70
           created. If the key is unknown, a XmpTextValue is used as
71
           default. Return 0 if the value was read successfully.
72
   */
73
  int setValue(const std::string& value) override;
74
  //@}
75
76
  //! @name Accessors
77
  //@{
78
  //! Not implemented. Calling this method will raise an exception.
79
  size_t copy(byte* buf, ByteOrder byteOrder) const override;
80
  std::ostream& write(std::ostream& os, const ExifData* pMetadata = nullptr) const override;
81
  /*!
82
    @brief Return the key of the Xmpdatum. The key is of the form
83
           '<b>Xmp</b>.prefix.property'. Note however that the
84
           key is not necessarily unique, i.e., an XmpData object may
85
           contain multiple metadata with the same key.
86
   */
87
  [[nodiscard]] std::string key() const override;
88
  [[nodiscard]] const char* familyName() const override;
89
  //! Return the (preferred) schema namespace prefix.
90
  [[nodiscard]] std::string groupName() const override;
91
  //! Return the property name.
92
  [[nodiscard]] std::string tagName() const override;
93
  [[nodiscard]] std::string tagLabel() const override;
94
  [[nodiscard]] std::string tagDesc() const override;
95
  //! Properties don't have a tag number. Return 0.
96
  [[nodiscard]] uint16_t tag() const override;
97
  [[nodiscard]] TypeId typeId() const override;
98
  [[nodiscard]] const char* typeName() const override;
99
  // Todo: Remove this method from the baseclass
100
  //! The Exif typeSize doesn't make sense here. Return 0.
101
  [[nodiscard]] size_t typeSize() const override;
102
  [[nodiscard]] size_t count() const override;
103
  [[nodiscard]] size_t size() const override;
104
  [[nodiscard]] std::string toString() const override;
105
  [[nodiscard]] std::string toString(size_t n) const override;
106
  [[nodiscard]] int64_t toInt64(size_t n = 0) const override;
107
  [[nodiscard]] float toFloat(size_t n = 0) const override;
108
  [[nodiscard]] Rational toRational(size_t n = 0) const override;
109
  [[nodiscard]] Value::UniquePtr getValue() const override;
110
  [[nodiscard]] const Value& value() const override;
111
  //@}
112
113
 private:
114
  // Pimpl idiom
115
  struct Impl;
116
  std::unique_ptr<Impl> p_;
117
118
};  // class Xmpdatum
119
120
//! Container type to hold all metadata
121
using XmpMetadata = std::vector<Xmpdatum>;
122
123
/*!
124
  @brief A container for XMP data. This is a top-level class of
125
         the %Exiv2 library.
126
127
  Provide high-level access to the XMP data of an image:
128
  - read XMP information from an XML block
129
  - access metadata through keys and standard C++ iterators
130
  - add, modify and delete metadata
131
  - serialize XMP data to an XML block
132
*/
133
class EXIV2API XmpData {
134
 public:
135
  //! Default constructor
136
44.9k
  XmpData() = default;
137
138
  //! XmpMetadata iterator type
139
  using iterator = XmpMetadata::iterator;
140
  //! XmpMetadata const iterator type
141
  using const_iterator = XmpMetadata::const_iterator;
142
143
  //! @name Manipulators
144
  //@{
145
  /*!
146
    @brief Returns a reference to the %Xmpdatum that is associated with a
147
           particular \em key. If %XmpData does not already contain such
148
           an %Xmpdatum, operator[] adds object \em Xmpdatum(key).
149
150
    @note  Since operator[] might insert a new element, it can't be a const
151
           member function.
152
   */
153
  Xmpdatum& operator[](const std::string& key);
154
  /*!
155
    @brief Add an %Xmpdatum from the supplied key and value pair. This
156
           method copies (clones) the value.
157
    @return 0 if successful.
158
   */
159
  int add(const XmpKey& key, const Value* value);
160
  /*!
161
    @brief Add a copy of the Xmpdatum to the XMP metadata.
162
    @return 0 if successful.
163
   */
164
  int add(const Xmpdatum& xmpDatum);
165
  /*
166
  @brief Delete the Xmpdatum at iterator position pos, return the
167
          position of the next Xmpdatum.
168
169
  @note  Iterators into the metadata, including pos, are potentially
170
          invalidated by this call.
171
  @brief Delete the Xmpdatum at iterator position pos and update pos
172
  */
173
  iterator erase(XmpData::iterator pos);
174
  /*!
175
    @brief Delete the Xmpdatum at iterator position pos and update pos
176
           erases all following keys from the same family
177
           See: https://github.com/Exiv2/exiv2/issues/521
178
   */
179
  void eraseFamily(XmpData::iterator& pos);
180
  //! Delete all Xmpdatum instances resulting in an empty container.
181
  void clear();
182
  //! Sort metadata by key
183
  void sortByKey();
184
  //! Begin of the metadata
185
  iterator begin();
186
  //! End of the metadata
187
  iterator end();
188
  /*!
189
    @brief Find the first Xmpdatum with the given key, return an iterator
190
           to it.
191
   */
192
  iterator findKey(const XmpKey& key);
193
  //@}
194
195
  //! @name Accessors
196
  //@{
197
  //! Begin of the metadata
198
  [[nodiscard]] const_iterator begin() const;
199
  //! End of the metadata
200
  [[nodiscard]] const_iterator end() const;
201
  /*!
202
    @brief Find the first Xmpdatum with the given key, return a const
203
           iterator to it.
204
   */
205
  [[nodiscard]] const_iterator findKey(const XmpKey& key) const;
206
  //! Return true if there is no XMP metadata
207
  [[nodiscard]] bool empty() const;
208
  //! Get the number of metadata entries
209
  [[nodiscard]] long count() const;
210
211
  //! are we to use the packet?
212
9.79k
  [[nodiscard]] bool usePacket() const {
213
9.79k
    return usePacket_;
214
9.79k
  }
215
216
  //! set usePacket_
217
7.68k
  bool usePacket(bool b) {
218
7.68k
    bool r = usePacket_;
219
7.68k
    usePacket_ = b;
220
7.68k
    return r;
221
7.68k
  }
222
  //! setPacket
223
6.31k
  void setPacket(std::string xmpPacket) {
224
6.31k
    xmpPacket_ = std::move(xmpPacket);
225
6.31k
    usePacket(false);
226
6.31k
  }
227
  // ! getPacket
228
0
  [[nodiscard]] const std::string& xmpPacket() const {
229
0
    return xmpPacket_;
230
0
  }
231
232
  //@}
233
234
 private:
235
  // DATA
236
  XmpMetadata xmpMetadata_;
237
  std::string xmpPacket_;
238
  bool usePacket_{};
239
};  // class XmpData
240
241
/*!
242
  @brief Stateless parser class for XMP packets. Images use this
243
         class to parse and serialize XMP packets. The parser uses
244
         the XMP toolkit to do the job.
245
 */
246
class EXIV2API XmpParser {
247
 public:
248
  //! Options to control the format of the serialized XMP packet.
249
  enum XmpFormatFlags {
250
    omitPacketWrapper = 0x0010UL,    //!< Omit the XML packet wrapper.
251
    readOnlyPacket = 0x0020UL,       //!< Default is a writeable packet.
252
    useCompactFormat = 0x0040UL,     //!< Use a compact form of RDF.
253
    includeThumbnailPad = 0x0100UL,  //!< Include a padding allowance for a thumbnail image.
254
    exactPacketLength = 0x0200UL,    //!< The padding parameter is the overall packet length.
255
    writeAliasComments = 0x0400UL,   //!< Show aliases as XML comments.
256
    omitAllFormatting = 0x0800UL     //!< Omit all formatting whitespace.
257
  };
258
  /*!
259
    @brief Decode XMP metadata from an XMP packet \em xmpPacket into
260
           \em xmpData. The format of the XMP packet must follow the
261
           XMP specification. This method clears any previous contents
262
           of \em xmpData.
263
264
    @param xmpData   Container for the decoded XMP properties
265
    @param xmpPacket The raw XMP packet to decode
266
    @return 0 if successful;<BR>
267
            1 if XMP support has not been compiled-in;<BR>
268
            2 if the XMP toolkit failed to initialize;<BR>
269
            3 if the XMP toolkit failed and raised an XMP_Error
270
  */
271
  static int decode(XmpData& xmpData, const std::string& xmpPacket);
272
  /*!
273
    @brief Encode (serialize) XMP metadata from \em xmpData into a
274
           string xmpPacket. The XMP packet returned in the string
275
           follows the XMP specification. This method only modifies
276
           \em xmpPacket if the operations succeeds (return code 0).
277
278
    @param xmpPacket   Reference to a string to hold the encoded XMP
279
                       packet.
280
    @param xmpData     XMP properties to encode.
281
    @param formatFlags Flags that control the format of the XMP packet,
282
                       see enum XmpFormatFlags.
283
    @param padding     Padding length.
284
    @return 0 if successful;<BR>
285
            1 if XMP support has not been compiled-in;<BR>
286
            2 if the XMP toolkit failed to initialize;<BR>
287
            3 if the XMP toolkit failed and raised an XMP_Error
288
  */
289
  static int encode(std::string& xmpPacket, const XmpData& xmpData, uint16_t formatFlags = useCompactFormat,
290
                    uint32_t padding = 0);
291
  /*!
292
    @brief Lock/unlock function type
293
294
    A function of this type can be passed to initialize() to
295
    make subsequent registration of XMP namespaces thread-safe.
296
    See the initialize() function for more information.
297
298
    @param pLockData Pointer to the pLockData passed to initialize()
299
    @param lockUnlock Indicates whether to lock (true) or unlock (false)
300
   */
301
  using XmpLockFct = void (*)(void* pLockData, bool lockUnlock);
302
303
  /*!
304
    @brief Initialize the XMP Toolkit.
305
306
    Calling this method is usually not needed, as encode() and
307
    decode() will initialize the XMP Toolkit if necessary.
308
309
    The function takes optional pointers to a callback function
310
    \em xmpLockFct and related data \em pLockData that the parser
311
    uses when XMP namespaces are subsequently registered.
312
313
    The initialize() function itself still is not thread-safe and
314
    needs to be called in a thread-safe manner (e.g., on program
315
    startup), but if used with suitable additional locking
316
    parameters, any subsequent registration of namespaces will be
317
    thread-safe.
318
319
    Example usage on Windows using a critical section:
320
321
    @code
322
    void main()
323
    {
324
        struct XmpLock
325
        {
326
            CRITICAL_SECTION cs;
327
            XmpLock()  { InitializeCriticalSection(&cs); }
328
            ~XmpLock() { DeleteCriticalSection(&cs); }
329
330
            static void LockUnlock(void* pData, bool fLock)
331
            {
332
                XmpLock* pThis = reinterpret_cast<XmpLock*>(pData);
333
                if (pThis)
334
                {
335
                    (fLock) ? EnterCriticalSection(&pThis->cs)
336
                            : LeaveCriticalSection(&pThis->cs);
337
                }
338
            }
339
        } xmpLock;
340
341
        // Pass the locking mechanism to the XMP parser on initialization.
342
        // Note however that this call itself is still not thread-safe.
343
        Exiv2::XmpParser::initialize(XmpLock::LockUnlock, &xmpLock);
344
345
        // Program continues here, subsequent registrations of XMP
346
        // namespaces are serialized using xmpLock.
347
348
    }
349
    @endcode
350
351
    @return True if the initialization was successful, else false.
352
   */
353
  static bool initialize(XmpParser::XmpLockFct xmpLockFct = nullptr, void* pLockData = nullptr);
354
  /*!
355
    @brief Terminate the XMP Toolkit and unregister custom namespaces.
356
357
    Call this method when the XmpParser is no longer needed to
358
    allow the XMP Toolkit to cleanly shutdown.
359
   */
360
  static void terminate();
361
362
 private:
363
  /*!
364
    @brief Register a namespace with the XMP Toolkit.
365
   */
366
  static void registerNs(const std::string& ns, const std::string& prefix);
367
  /*!
368
    @brief Delete a namespace from the XMP Toolkit.
369
370
    XmpProperties::unregisterNs calls this to synchronize namespaces.
371
  */
372
  static void unregisterNs(const std::string& ns);
373
374
  /*!
375
    @brief Get namespaces registered with XMPsdk
376
   */
377
  static void registeredNamespaces(Exiv2::Dictionary&);
378
379
  // DATA
380
  static bool initialized_;  //! Indicates if the XMP Toolkit has been initialized
381
  static XmpLockFct xmpLockFct_;
382
  static void* pLockData_;
383
384
  friend class XmpProperties;  // permit XmpProperties -> registerNs() and registeredNamespaces()
385
386
};  // class XmpParser
387
388
// *****************************************************************************
389
// free functions, template and inline definitions
390
391
#if __cpp_if_constexpr
392
template <typename T>
393
750k
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
0
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
214k
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
0
    setValue(&value);
400
  else
401
535k
    setValue(Exiv2::toString(value));
402
750k
  return *this;
403
750k
}
Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<Exiv2::Value>(Exiv2::Value const&)
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Line
Count
Source
393
197k
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
197k
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
    setValue(Exiv2::toString(value));
402
197k
  return *this;
403
197k
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<double>(double const&)
Line
Count
Source
393
184k
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
184k
    setValue(Exiv2::toString(value));
402
184k
  return *this;
403
184k
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<unsigned long>(unsigned long const&)
Line
Count
Source
393
3.05k
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
3.05k
    setValue(Exiv2::toString(value));
402
3.05k
  return *this;
403
3.05k
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<unsigned int>(unsigned int const&)
Line
Count
Source
393
4.97k
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
4.97k
    setValue(Exiv2::toString(value));
402
4.97k
  return *this;
403
4.97k
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<unsigned char const*>(unsigned char const* const&)
Line
Count
Source
393
2.85k
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
2.85k
    setValue(Exiv2::toString(value));
402
2.85k
  return *this;
403
2.85k
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<char [4]>(char const (&) [4])
Line
Count
Source
393
135
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
135
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
    setValue(Exiv2::toString(value));
402
135
  return *this;
403
135
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<long>(long const&)
Line
Count
Source
393
678
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
678
    setValue(Exiv2::toString(value));
402
678
  return *this;
403
678
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<float>(float const&)
Line
Count
Source
393
666
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
666
    setValue(Exiv2::toString(value));
402
666
  return *this;
403
666
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<char [18]>(char const (&) [18])
Line
Count
Source
393
77
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
77
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
    setValue(Exiv2::toString(value));
402
77
  return *this;
403
77
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<unsigned short>(unsigned short const&)
Line
Count
Source
393
144k
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
144k
    setValue(Exiv2::toString(value));
402
144k
  return *this;
403
144k
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<short>(short const&)
Line
Count
Source
393
481
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
481
    setValue(Exiv2::toString(value));
402
481
  return *this;
403
481
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<char const*>(char const* const&)
Line
Count
Source
393
16.8k
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
16.8k
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
    setValue(Exiv2::toString(value));
402
16.8k
  return *this;
403
16.8k
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<int>(int const&)
Line
Count
Source
393
194k
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
194k
    setValue(Exiv2::toString(value));
402
194k
  return *this;
403
194k
}
Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<char [12]>(char const (&) [12])
Line
Count
Source
393
7
Xmpdatum& Xmpdatum::operator=(const T& value) {
394
  if constexpr (std::is_same_v<T, bool>)
395
    setValue(value ? "True" : "False");
396
  else if constexpr (std::is_convertible_v<T, std::string>)
397
7
    setValue(value);
398
  else if constexpr (std::is_base_of_v<Value, T>)
399
    setValue(&value);
400
  else
401
    setValue(Exiv2::toString(value));
402
7
  return *this;
403
7
}
Unexecuted instantiation: Exiv2::Xmpdatum& Exiv2::Xmpdatum::operator=<bool>(bool const&)
404
#else
405
template <typename T>
406
std::enable_if_t<std::is_convertible<T, std::string>::value> operatorHelper(Xmpdatum* xmp, const T& value) {
407
  xmp->setValue(value);
408
}
409
410
template <typename T>
411
std::enable_if_t<std::is_base_of<Value, T>::value> operatorHelper(Xmpdatum* xmp, const T& value) {
412
  xmp->setValue(&value);
413
}
414
415
template <typename T>
416
std::enable_if_t<std::is_same<T, bool>::value> operatorHelper(Xmpdatum* xmp, const T& value) {
417
  xmp->setValue(value ? "True" : "False");
418
}
419
420
template <typename T>
421
std::enable_if_t<!(std::is_convertible<T, std::string>::value || std::is_base_of<Value, T>::value ||
422
                   std::is_same<T, bool>::value)>
423
operatorHelper(Xmpdatum* xmp, const T& value) {
424
  xmp->setValue(Exiv2::toString(value));
425
}
426
427
template <typename T>
428
Xmpdatum& Xmpdatum::operator=(const T& value) {
429
  operatorHelper(this, value);
430
  return *this;
431
}
432
#endif
433
}  // namespace Exiv2
434
435
#endif  // EXIV2_XMP_EXIV2_HPP