Coverage Report

Created: 2026-03-12 07:14

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