Coverage Report

Created: 2026-07-30 07:17

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