diff --git a/app/actions.cpp b/app/actions.cpp
index 1f5502dd..0cb99382 100644
--- a/app/actions.cpp
+++ b/app/actions.cpp
@@ -498,6 +498,12 @@ bool Print::printMetadatum(const Exiv2::Metadatum& md, const Exiv2::Image* pImag
first = false;
std::cout << std::setw(30) << std::setfill(' ') << std::left << md.tagLabel();
}
+ if (Params::instance().printItems_ & Params::prDesc) {
+ if (!first)
+ std::cout << " ";
+ first = false;
+ std::cout << std::setw(30) << std::setfill(' ') << std::left << md.tagDesc();
+ }
if (Params::instance().printItems_ & Params::prType) {
if (!first)
std::cout << " ";
diff --git a/app/exiv2.cpp b/app/exiv2.cpp
index 6f04e6e3..12e1d434 100644
--- a/app/exiv2.cpp
+++ b/app/exiv2.cpp
@@ -283,14 +283,16 @@ void Params::help(std::ostream& os) const {
<< _(" X : Extract \"raw\" XMP\n")
<< _(" -P flgs Print flags for fine control of tag lists ('print' action):\n")
<< _(" E : Exif tags\n") << _(" I : IPTC tags\n") << _(" X : XMP tags\n")
- << _(" x : Tag number (Exif and IPTC only)\n")
+ << _(" x : Tag number for Exif or IPTC tags (in hexadecimal)\n")
<< _(" g : Group name (e.g. Exif.Photo.UserComment, Photo)\n")
<< _(" k : Key (e.g. Exif.Photo.UserComment)\n")
<< _(" l : Tag label (e.g. Exif.Photo.UserComment, 'User comment')\n")
+ << _(" d : Tag description\n")
<< _(" n : Tag name (e.g. Exif.Photo.UserComment, UserComment)\n") << _(" y : Type\n")
- << _(" c : Number of components (count)\n")
- << _(" s : Size in bytes (Ascii and Comment types include NULL)\n")
- << _(" v : Plain data value, untranslated (vanilla)\n")
+ << _(" y : Type\n") << _(" c : Number of components (count)\n")
+ << _(" s : Size in bytes of vanilla value (may include NULL)\n")
+ << _(" v : Plain data value of untranslated (vanilla)\n")
+ << _(" V : Plain data value, data type and the word 'set'\n")
<< _(" t : Interpreted (translated) human readable values\n")
<< _(" h : Hex dump of the data\n")
<< _(" -d tgt1 Delete target(s) for the 'delete' action. Possible targets are:\n")
@@ -720,6 +722,9 @@ int Params::evalPrintFlags(const std::string& optArg) {
case 'V':
printItems_ |= prSet | prKey | prType | prValue;
break;
+ case 'd':
+ printItems_ |= prDesc;
+ break;
default:
std::cerr << progname() << ": " << _("Unrecognized print item") << " `" << i << "'\n";
rc = 1;
diff --git a/app/exiv2app.hpp b/app/exiv2app.hpp
index 5c278f9a..02fd72fc 100644
--- a/app/exiv2app.hpp
+++ b/app/exiv2app.hpp
@@ -151,7 +151,8 @@ class Params : public Util::Getopt {
prValue = 256,
prTrans = 512,
prHex = 1024,
- prSet = 2048
+ prSet = 2048,
+ prDesc = 4096
};
//! Enumerates common targets, bitmap
diff --git a/exiv2.md b/exiv2.md
index 6049327f..86dd7fd2 100644
--- a/exiv2.md
+++ b/exiv2.md
@@ -566,34 +566,33 @@ as well as data columns included in the print output. Valid flags are:
| g | Group name (e.g., for Exif.Photo.UserComment, outputs Photo) |
| k | Key (e.g., Exif.Photo.UserComment) |
| l | Tag label (human-readable tagname, e.g., for Exif.Photo.UserComment, outputs 'User comment') |
-| n | Tagname (e.g., for Exif.Photo.UserComment, outputs UserComment) |
+| d | Tag description |
+| n | Tag name (e.g., for Exif.Photo.UserComment, outputs UserComment) |
| y | Type (for available types, see [Exif/IPTC/XMP types](#exiv2_types)) |
| c | Number of components (for single entry types, the number of **sizeof('type')** in 'size'. For multi-entry types, the number of entries. See [Exif/IPTC/XMP types](#exiv2_types)) |
| s | Size in bytes of vanilla output (see note in [Exif 'Comment' values](#exif_comment_values)). Some types include a *NULL* character in the size (see [Exif/IPTC/XMP types](#exiv2_types)) |
| v | Plain data value (vanilla values, i.e., untranslated) |
-| V | Plain data value, data type and the word 'set ' (see ['MODIFY' COMMANDS](#modify_cmds))|
+| V | Plain data value, data type and the word 'set' (see ['MODIFY' COMMANDS](#modify_cmds))|
| t | Interpreted (translated) human-readable data values (includes plain vanilla values) |
| h | Hex dump of the data |
+**--Print** *flgs* can be combined with [--grep str](#grep_str) or
+[--key key](#key_key) to further filter the output.
+
-The order of the values in *flgs* is not respected. For example, the order
-of the columns, using some tags from *Stonehenge.jpg*, is as follows:
+The order of the values in *flgs* is not respected and is output as follows:
-```
-$ curl --silent -O https://www.exiv2.org/Stonehenge.jpg
-$ exiv2 --Print xgknlycst Stonehenge.jpg
-```
+| Tag number
(x) | Plain 'set'
(V) | Group
(g) | Key
(k) | Tagname
(n) | Tagname label
(l) |Description
(d) | Type
(y) | Comp
(c) | Size
(s) | Value
(v) | Translated
(t) |
+|:------ |:---- |:------ |:------ |:------ |:------ |:------ |:------ |:------ |:------ |:--- |:------ |
-| Tag number
(x) | Plain 'set'
(V) | Group
(g) | Key
(k) | Tagname
(n) | Tagname label
(l) | Type
(y) | Comp
(c) | Size
(s) | Value
(E, I, X, v, t) | Translated
(t) |
-|:------ |:---- |:------ |:------ |:------ |:------ |:------ |:------ |:------ |:------ |:------ |
-| 0x0110 | set | Image | Exif.Image.Model | Model | Model | Ascii | 12 | 12 | NIKON D5300 | NIKON D5300 |
-| 0x0006 | set | NikonIi | Exif.NikonIi.ISO2 | ISO2 | ISO 2 | Byte | 1 | 1 | 72 | 200 |
-| 0x0000 | set | xmp | Xmp.xmp.Rating | Rating | Rating | XmpText | 1 | 1 | 0 | 0 |
-| 0x0000 | set | dc | Xmp.dc.Family | Family | Family | XmpBag | 1 | 5 | Robin | Robin |
+For example,
-**--Print** *flgs* can be combined with [--grep str](#grep_str) or
-[--key key](#key_key) to further filter the output.
+```bash
+$ curl --silent -O https://www.exiv2.org/Stonehenge.jpg
+$ exiv2 --Print xVgknldycsvt -K Exif.Nikon3.Quality Stonehenge.jpg
+0x0004 set Nikon3 Exif.Nikon3.Quality Quality Quality Image quality setting Ascii 8 8 NORMAL NORMAL
+```
diff --git a/include/exiv2/datasets.hpp b/include/exiv2/datasets.hpp
index 2d06e9e3..491a379d 100644
--- a/include/exiv2/datasets.hpp
+++ b/include/exiv2/datasets.hpp
@@ -280,6 +280,7 @@ class EXIV2API IptcKey : public Key {
[[nodiscard]] std::string groupName() const override;
[[nodiscard]] std::string tagName() const override;
[[nodiscard]] std::string tagLabel() const override;
+ [[nodiscard]] std::string tagDesc() const override;
[[nodiscard]] uint16_t tag() const override;
[[nodiscard]] UniquePtr clone() const;
//! Return the name of the record
diff --git a/include/exiv2/exif.hpp b/include/exiv2/exif.hpp
index c0367ed6..29796541 100644
--- a/include/exiv2/exif.hpp
+++ b/include/exiv2/exif.hpp
@@ -140,6 +140,7 @@ class EXIV2API Exifdatum : public Metadatum {
[[nodiscard]] std::string groupName() const override;
[[nodiscard]] std::string tagName() const override;
[[nodiscard]] std::string tagLabel() const override;
+ [[nodiscard]] std::string tagDesc() const override;
[[nodiscard]] uint16_t tag() const override;
//! Return the IFD id as an integer. (Do not use, this is meant for library internal use.)
[[nodiscard]] IfdId ifdId() const;
diff --git a/include/exiv2/iptc.hpp b/include/exiv2/iptc.hpp
index 56dde0e5..64caba10 100644
--- a/include/exiv2/iptc.hpp
+++ b/include/exiv2/iptc.hpp
@@ -113,6 +113,7 @@ class EXIV2API Iptcdatum : public Metadatum {
*/
[[nodiscard]] std::string tagName() const override;
[[nodiscard]] std::string tagLabel() const override;
+ [[nodiscard]] std::string tagDesc() const override;
//! Return the tag (aka dataset) number
[[nodiscard]] uint16_t tag() const override;
[[nodiscard]] TypeId typeId() const override;
diff --git a/include/exiv2/metadatum.hpp b/include/exiv2/metadatum.hpp
index d54b2ac3..982b641b 100644
--- a/include/exiv2/metadatum.hpp
+++ b/include/exiv2/metadatum.hpp
@@ -52,6 +52,8 @@ class EXIV2API Key {
[[nodiscard]] virtual std::string tagName() const = 0;
//! Return a label for the tag
[[nodiscard]] virtual std::string tagLabel() const = 0;
+ //! Return a description for the tag
+ [[nodiscard]] virtual std::string tagDesc() const = 0;
//! Return the tag number
[[nodiscard]] virtual uint16_t tag() const = 0;
/*!
@@ -181,6 +183,8 @@ class EXIV2API Metadatum {
[[nodiscard]] virtual std::string tagName() const = 0;
//! Return a label for the tag
[[nodiscard]] virtual std::string tagLabel() const = 0;
+ //! Return a description for the tag
+ [[nodiscard]] virtual std::string tagDesc() const = 0;
//! Return the tag
[[nodiscard]] virtual uint16_t tag() const = 0;
//! Return the type id of the value
diff --git a/include/exiv2/properties.hpp b/include/exiv2/properties.hpp
index 295306fb..a2f1ce88 100644
--- a/include/exiv2/properties.hpp
+++ b/include/exiv2/properties.hpp
@@ -263,6 +263,7 @@ class EXIV2API XmpKey : public Key {
[[nodiscard]] std::string groupName() const override;
[[nodiscard]] std::string tagName() const override;
[[nodiscard]] std::string tagLabel() const override;
+ [[nodiscard]] std::string tagDesc() const override;
//! Properties don't have a tag number. Return 0.
[[nodiscard]] uint16_t tag() const override;
diff --git a/include/exiv2/tags.hpp b/include/exiv2/tags.hpp
index 356f9dce..20f75212 100644
--- a/include/exiv2/tags.hpp
+++ b/include/exiv2/tags.hpp
@@ -339,8 +339,7 @@ class EXIV2API ExifKey : public Key {
[[nodiscard]] std::string tagName() const override;
[[nodiscard]] uint16_t tag() const override;
[[nodiscard]] std::string tagLabel() const override;
- //! Return the tag description.
- [[nodiscard]] std::string tagDesc() const; // Todo: should be in the base class
+ [[nodiscard]] std::string tagDesc() const override;
//! Return the default type id for this tag.
[[nodiscard]] TypeId defaultTypeId() const; // Todo: should be in the base class
diff --git a/include/exiv2/xmp_exiv2.hpp b/include/exiv2/xmp_exiv2.hpp
index f7e78a7c..8301ff58 100644
--- a/include/exiv2/xmp_exiv2.hpp
+++ b/include/exiv2/xmp_exiv2.hpp
@@ -103,6 +103,7 @@ class EXIV2API Xmpdatum : public Metadatum {
//! Return the property name.
[[nodiscard]] std::string tagName() const override;
[[nodiscard]] std::string tagLabel() const override;
+ [[nodiscard]] std::string tagDesc() const override;
//! Properties don't have a tag number. Return 0.
[[nodiscard]] uint16_t tag() const override;
[[nodiscard]] TypeId typeId() const override;
diff --git a/src/datasets.cpp b/src/datasets.cpp
index 02d64a51..5cb5807b 100644
--- a/src/datasets.cpp
+++ b/src/datasets.cpp
@@ -531,6 +531,10 @@ std::string IptcKey::tagLabel() const {
return IptcDataSets::dataSetTitle(tag_, record_);
}
+std::string IptcKey::tagDesc() const {
+ return IptcDataSets::dataSetDesc(tag_, record_);
+}
+
uint16_t IptcKey::tag() const {
return tag_;
}
diff --git a/src/exif.cpp b/src/exif.cpp
index 867a2ded..79695b7d 100644
--- a/src/exif.cpp
+++ b/src/exif.cpp
@@ -292,6 +292,10 @@ std::string Exifdatum::tagLabel() const {
return key_ ? key_->tagLabel() : "";
}
+std::string Exifdatum::tagDesc() const {
+ return key_ ? key_->tagDesc() : "";
+}
+
uint16_t Exifdatum::tag() const {
return key_ ? key_->tag() : 0xffff;
}
diff --git a/src/iptc.cpp b/src/iptc.cpp
index 39abd1f7..0c70843f 100644
--- a/src/iptc.cpp
+++ b/src/iptc.cpp
@@ -101,6 +101,10 @@ std::string Iptcdatum::tagLabel() const {
return key_ ? key_->tagLabel() : "";
}
+std::string Iptcdatum::tagDesc() const {
+ return key_ ? key_->tagDesc() : "";
+}
+
uint16_t Iptcdatum::tag() const {
return key_ ? key_->tag() : 0;
}
diff --git a/src/properties.cpp b/src/properties.cpp
index ebe089b8..e3413342 100644
--- a/src/properties.cpp
+++ b/src/properties.cpp
@@ -5139,6 +5139,13 @@ std::string XmpKey::tagLabel() const {
return pt;
}
+std::string XmpKey::tagDesc() const {
+ const char* pt = XmpProperties::propertyDesc(*this);
+ if (!pt)
+ return "";
+ return pt;
+}
+
uint16_t XmpKey::tag() const {
return 0;
}
diff --git a/src/xmp.cpp b/src/xmp.cpp
index ed54b340..8a628774 100644
--- a/src/xmp.cpp
+++ b/src/xmp.cpp
@@ -326,6 +326,10 @@ std::string Xmpdatum::tagLabel() const {
return p_->key_ ? p_->key_->tagLabel() : "";
}
+std::string Xmpdatum::tagDesc() const {
+ return p_->key_ ? p_->key_->tagDesc() : "";
+}
+
uint16_t Xmpdatum::tag() const {
return p_->key_ ? p_->key_->tag() : 0;
}
diff --git a/test/data/test_reference_files/exiv2-test.out b/test/data/test_reference_files/exiv2-test.out
index 6023f884..0770f3a9 100644
--- a/test/data/test_reference_files/exiv2-test.out
+++ b/test/data/test_reference_files/exiv2-test.out
@@ -90,15 +90,18 @@ Options:
E : Exif tags
I : IPTC tags
X : XMP tags
- x : Tag number (Exif and IPTC only)
+ x : Tag number for Exif or IPTC tags (in hexadecimal)
g : Group name (e.g. Exif.Photo.UserComment, Photo)
k : Key (e.g. Exif.Photo.UserComment)
l : Tag label (e.g. Exif.Photo.UserComment, 'User comment')
+ d : Tag description
n : Tag name (e.g. Exif.Photo.UserComment, UserComment)
y : Type
+ y : Type
c : Number of components (count)
- s : Size in bytes (Ascii and Comment types include NULL)
- v : Plain data value, untranslated (vanilla)
+ s : Size in bytes of vanilla value (may include NULL)
+ v : Plain data value of untranslated (vanilla)
+ V : Plain data value, data type and the word 'set'
t : Interpreted (translated) human readable values
h : Hex dump of the data
-d tgt1 Delete target(s) for the 'delete' action. Possible targets are:
diff --git a/tests/bash_tests/test_pr_2279.py b/tests/bash_tests/test_pr_2279.py
new file mode 100644
index 00000000..ab5b15e2
--- /dev/null
+++ b/tests/bash_tests/test_pr_2279.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+
+import system_tests
+
+class TestExifIPTCXmpTagOutput(metaclass=system_tests.CaseMeta):
+ url = "https://github.com/Exiv2/exiv2/pull/2279"
+
+ # Test the exiv2 application's `--Print` command
+ filename = "$data_path/Stonehenge.exv"
+ commands = ["$exiv2 --Print xVgknldycsvt --key Exif.Image.Model $filename",
+ "$exiv2 --Print xVgknldycsvt --key Iptc.Application2.Caption $filename",
+ "$exiv2 --Print xVgknldycsvt --key Xmp.dc.description $filename"]
+ stderr = [""] * len(commands)
+ stdout = ["""0x0110 set Image Exif.Image.Model Model Model The model name or model number of the equipment. This is the model name or number of the DSC, scanner, video digitizer or other equipment that generated the image. When the field is left blank, it is treated as unknown. Ascii 12 12 NIKON D5300 NIKON D5300
+""",
+"""0x0078 set Application2 Iptc.Application2.Caption Caption Caption A textual description of the object data. String 12 12 Classic View Classic View
+""",
+"""0x0000 set dc Xmp.dc.description description Description A textual description of the content of the resource. Multiple values may be present for different languages. LangAlt 1 29 lang="x-default" Classic View lang="x-default" Classic View
+"""
+]
+ retval = [0] * len(commands)