Intermediate state: Merged rev. 1153-1196 from branches/xmp.
This commit is contained in:
parent
77bec2c60e
commit
ac314ddfbc
77
README-XMP
Normal file
77
README-XMP
Normal file
@ -0,0 +1,77 @@
|
||||
XMP support
|
||||
===========
|
||||
Exiv2 uses the Adobe XMP toolkit (XMP SDK) to parse
|
||||
and serialize XMP packets (only the XMPCore component).
|
||||
|
||||
Todo: XMP support is controlled with the ENABLE_XMP directive
|
||||
in config/config.mk.in. This will be a configure option
|
||||
eventually.
|
||||
|
||||
Top-level classes to access XMP metadata are XmpData,
|
||||
XmpDatum and XmpKey. They work similar to the corresponding
|
||||
Exif and IPTC classes. The property-repository is XmpProperties.
|
||||
In addition to the expected new members, class Image also
|
||||
has a new interface to access the raw XMP packet.
|
||||
|
||||
Supported XMP types
|
||||
-------------------
|
||||
Simple types : supported
|
||||
Structures : not supported
|
||||
Arrays : unordered and ordered arrays are supported
|
||||
alternative arrays are not supported
|
||||
|
||||
Property Qualifiers : not supported
|
||||
Language Alternatives : not supported
|
||||
|
||||
XMP properties are accessed through keys of the form
|
||||
"Xmp.<Prefix>.<Property>", where <Prefix> is the preferred
|
||||
(or rather, registered) prefix for a schema namespace and
|
||||
<Property> is the name of a property in that namespace. Only
|
||||
known properties in known namespaces are supported at the
|
||||
moment. Functions to register namespaces and properterties
|
||||
are not provided yet.
|
||||
|
||||
Note: Unlike Exif and IPTC tags, XMP properties do not have
|
||||
a tag number.
|
||||
|
||||
XMP toolkit installation
|
||||
========================
|
||||
This is what worked for me on a Debian GNU/Linux (testing)
|
||||
system. Your mileage may vary. Please check with Adobe if
|
||||
you encounter problems with the XMP toolkit installation.
|
||||
|
||||
Installation directory
|
||||
----------------------
|
||||
The top-level exiv2 directory ($HOME/src/exiv2/ on my system).
|
||||
|
||||
If you install XMP elsewhere, or use different OS or STAGE
|
||||
options to the make command below, you will need to manually
|
||||
modify the exiv2/config/config.mk.in (before running the
|
||||
configure script). There is no autoconf-magic yet to set
|
||||
this via configure options.
|
||||
|
||||
External packages (non-Debian)
|
||||
-----------------
|
||||
xmp_v411_sdk.zip - from adobe.com: http://www.adobe.com/devnet/xmp/sdk/eula.html
|
||||
expat-2.0.1.tar.gz - from sourceforge.net: http://sourceforge.net/project/showfiles.php?group_id=10127
|
||||
exiv2 - from SVN
|
||||
|
||||
Installation steps
|
||||
------------------
|
||||
cd $HOME/src/exiv2/
|
||||
cp /path/to/xmp_v411_sdk.zip ./
|
||||
unzip xmp_v411_sdk.zip
|
||||
|
||||
cp /path/to/expat-2.0.1.tar.gz XMP-SDK/third-party/
|
||||
cd XMP-SDK/third-party/
|
||||
tar zxvf expat-2.0.1.tar.gz
|
||||
mv expat expat-orig
|
||||
mv expat-2.0.1 expat
|
||||
|
||||
cd ../build/gcc
|
||||
make -f XMPCore.mak OS=i80386linux STAGE=debug
|
||||
|
||||
Expected result
|
||||
---------------
|
||||
Now there should be a library
|
||||
../../public/libraries/i80386linux/debug/libXMPCoreStaticDebug.a
|
||||
@ -46,7 +46,7 @@ GXX = @GXX@
|
||||
# Common compiler flags (warnings, symbols [-ggdb], optimization [-O2], etc)
|
||||
CXXFLAGS = @CXXFLAGS@
|
||||
ifeq ($(GXX),yes)
|
||||
CXXFLAGS += -pedantic -Wall -Wundef -Wcast-align -Wconversion -Wpointer-arith -Wformat-security -Wmissing-format-attribute -W
|
||||
CXXFLAGS += -Wall -Wcast-align -Wconversion -Wpointer-arith -Wformat-security -Wmissing-format-attribute -W
|
||||
endif
|
||||
|
||||
# Command to run only the preprocessor
|
||||
@ -75,6 +75,20 @@ ifeq ($(GCC),yes)
|
||||
CFLAGS += -Wall
|
||||
endif
|
||||
|
||||
# **********************************************************************
|
||||
# XMP support
|
||||
ENABLE_XMP = 1
|
||||
|
||||
ifdef ENABLE_XMP
|
||||
CPPFLAGS += -DEXV_HAVE_XMP_TOOLKIT -DUNIX_ENV -I$(top_srcdir)/XMP-SDK/public/include
|
||||
LDFLAGS += -L$(top_srcdir)/XMP-SDK/public/libraries/i80386linux/debug -lXMPCoreStaticDebug
|
||||
else
|
||||
# XMP Toolkit doesn't compile cleanly with these
|
||||
ifeq ($(GXX),yes)
|
||||
CXXFLAGS += -Wundef -pedantic
|
||||
endif
|
||||
endif
|
||||
|
||||
# **********************************************************************
|
||||
# Libraries and Functions
|
||||
HAVE_LIBZ = @HAVE_LIBZ@
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT(exiv2, 0.15, ahuggel@gmx.net)
|
||||
AC_INIT(exiv2, 0.16, ahuggel@gmx.net)
|
||||
# See http://www.gnu.org/software/libtool/manual.html#Updating-version-info
|
||||
EXIV2_LTVERSION=1:0:1
|
||||
PACKAGE=$PACKAGE_NAME
|
||||
|
||||
@ -92,6 +92,10 @@ CCSRC += rafimage.cpp \
|
||||
types.cpp \
|
||||
value.cpp \
|
||||
version.cpp
|
||||
ifdef ENABLE_XMP
|
||||
CCSRC += properties.cpp \
|
||||
xmp.cpp
|
||||
endif
|
||||
|
||||
# Add library C source files to this list
|
||||
ifndef HAVE_TIMEGM
|
||||
@ -118,7 +122,8 @@ BINSRC = addmoddel.cpp \
|
||||
write-test.cpp \
|
||||
write2-test.cpp \
|
||||
tiffparse.cpp \
|
||||
xmpdump.cpp
|
||||
xmpparse.cpp \
|
||||
xmpparser-test.cpp
|
||||
|
||||
# Main source file of the Exiv2 application
|
||||
EXIV2MAIN = exiv2.cpp
|
||||
|
||||
201
src/actions.cpp
201
src/actions.cpp
@ -49,6 +49,7 @@ EXIV2_RCSID("@(#) $Id$")
|
||||
#include "exif.hpp"
|
||||
#include "canonmn.hpp"
|
||||
#include "iptc.hpp"
|
||||
#include "xmp.hpp"
|
||||
#include "futils.hpp"
|
||||
#include "i18n.h" // NLS support.
|
||||
|
||||
@ -128,6 +129,23 @@ namespace {
|
||||
@return 0 if successful, -1 if the file was skipped, 1 on error.
|
||||
*/
|
||||
int renameFile(std::string& path, const struct tm* tm);
|
||||
|
||||
/*!
|
||||
@brief Make a file path from the current file path, destination
|
||||
directory (if any) and the filename extension passed in.
|
||||
@param path Path of the existing file
|
||||
@param ext New filename extension (incl. the dot '.' if required)
|
||||
@return 0 if successful, 1 if the new file exists and the user
|
||||
chose not to overwrite it.
|
||||
*/
|
||||
std::string newFilePath(const std::string& path, const std::string& ext);
|
||||
|
||||
/*!
|
||||
@brief Check if file \em path exists and whether it should be
|
||||
overwritten. Ask user if necessary. Return 1 if the file
|
||||
exists and shouldn't be overwritten, else 0.
|
||||
*/
|
||||
int dontOverwrite(const std::string& path);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
@ -201,6 +219,7 @@ namespace Action {
|
||||
case Params::pmSummary: rc = printSummary(); break;
|
||||
case Params::pmList: rc = printList(); break;
|
||||
case Params::pmIptc: rc = printIptc(); break;
|
||||
case Params::pmXmp: rc = printXmp(); break;
|
||||
case Params::pmComment: rc = printComment(); break;
|
||||
}
|
||||
return rc;
|
||||
@ -700,7 +719,7 @@ namespace Action {
|
||||
Exiv2::IptcData& iptcData = image->iptcData();
|
||||
if (iptcData.empty()) {
|
||||
std::cerr << path_
|
||||
<< ": " << _("No Iptc data found in the file\n");
|
||||
<< ": " << _("No IPTC data found in the file\n");
|
||||
return -3;
|
||||
}
|
||||
Exiv2::IptcData::const_iterator end = iptcData.end();
|
||||
@ -725,6 +744,44 @@ namespace Action {
|
||||
return 0;
|
||||
} // Print::printIptc
|
||||
|
||||
int Print::printXmp()
|
||||
{
|
||||
if (!Exiv2::fileExists(path_, true)) {
|
||||
std::cerr << path_
|
||||
<< ": " << _("Failed to open the file\n");
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
|
||||
assert(image.get() != 0);
|
||||
image->readMetadata();
|
||||
Exiv2::XmpData& xmpData = image->xmpData();
|
||||
if (xmpData.empty()) {
|
||||
std::cerr << path_
|
||||
<< ": " << _("No XMP data found in the file\n");
|
||||
return -3;
|
||||
}
|
||||
Exiv2::XmpData::const_iterator end = xmpData.end();
|
||||
Exiv2::XmpData::const_iterator md;
|
||||
bool manyFiles = Params::instance().files_.size() > 1;
|
||||
for (md = xmpData.begin(); md != end; ++md) {
|
||||
std::cout << std::setfill(' ') << std::left;
|
||||
if (manyFiles) {
|
||||
std::cout << std::setw(20) << path_ << " ";
|
||||
}
|
||||
std::cout << std::setw(44)
|
||||
<< md->key() << " "
|
||||
<< std::setw(9) << std::setfill(' ') << std::left
|
||||
<< md->typeName() << " "
|
||||
<< std::dec << std::setw(3)
|
||||
<< std::setfill(' ') << std::right
|
||||
<< md->count() << " "
|
||||
<< std::dec << md->value()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} // Print::printXmp
|
||||
|
||||
int Print::printComment()
|
||||
{
|
||||
if (!Exiv2::fileExists(path_, true)) {
|
||||
@ -736,7 +793,7 @@ namespace Action {
|
||||
assert(image.get() != 0);
|
||||
image->readMetadata();
|
||||
if (Params::instance().verbose_) {
|
||||
std::cout << _("Jpeg comment") << ": ";
|
||||
std::cout << _("JPEG comment") << ": ";
|
||||
}
|
||||
std::cout << image->comment() << std::endl;
|
||||
return 0;
|
||||
@ -867,6 +924,12 @@ namespace Action {
|
||||
if (0 == rc && Params::instance().target_ & Params::ctComment) {
|
||||
rc = eraseComment(image.get());
|
||||
}
|
||||
if (0 == rc && Params::instance().target_ & Params::ctXmp) {
|
||||
// Todo: Implement me!
|
||||
if (!image->xmpData().empty()) {
|
||||
std::cerr << "Deletion of XMP data not implemented yet.\n";
|
||||
}
|
||||
}
|
||||
if (0 == rc) {
|
||||
image->writeMetadata();
|
||||
}
|
||||
@ -910,7 +973,7 @@ namespace Action {
|
||||
int Erase::eraseIptcData(Exiv2::Image* image) const
|
||||
{
|
||||
if (Params::instance().verbose_ && image->iptcData().count() > 0) {
|
||||
std::cout << _("Erasing Iptc data from the file") << std::endl;
|
||||
std::cout << _("Erasing IPTC data from the file") << std::endl;
|
||||
}
|
||||
image->clearIptcData();
|
||||
return 0;
|
||||
@ -919,7 +982,7 @@ namespace Action {
|
||||
int Erase::eraseComment(Exiv2::Image* image) const
|
||||
{
|
||||
if (Params::instance().verbose_ && image->comment().size() > 0) {
|
||||
std::cout << _("Erasing Jpeg comment from the file") << std::endl;
|
||||
std::cout << _("Erasing JPEG comment from the file") << std::endl;
|
||||
}
|
||||
image->clearComment();
|
||||
return 0;
|
||||
@ -942,18 +1005,12 @@ namespace Action {
|
||||
if (Params::instance().target_ & Params::ctThumb) {
|
||||
rc = writeThumbnail();
|
||||
}
|
||||
if (Params::instance().target_ & ~Params::ctThumb) {
|
||||
std::string directory = Params::instance().directory_;
|
||||
if (directory.empty()) directory = Util::dirname(path_);
|
||||
std::string exvPath = directory + EXV_SEPERATOR_STR
|
||||
+ Util::basename(path_, true) + ".exv";
|
||||
if (!Params::instance().force_ && Exiv2::fileExists(exvPath)) {
|
||||
std::cout << Params::instance().progname()
|
||||
<< ": " << _("Overwrite") << " `" << exvPath << "'? ";
|
||||
std::string s;
|
||||
std::cin >> s;
|
||||
if (s[0] != 'y' && s[0] != 'Y') return 0;
|
||||
}
|
||||
if (Params::instance().target_ & Params::ctXmpPacket) {
|
||||
rc = writeXmp();
|
||||
}
|
||||
if (Params::instance().target_ & ~Params::ctThumb & ~Params::ctXmpPacket) {
|
||||
std::string exvPath = newFilePath(path_, ".exv");
|
||||
if (dontOverwrite(exvPath)) return 0;
|
||||
rc = metacopy(path_, exvPath, false);
|
||||
}
|
||||
return rc;
|
||||
@ -965,6 +1022,37 @@ namespace Action {
|
||||
return 1;
|
||||
} // Extract::run
|
||||
|
||||
int Extract::writeXmp() const
|
||||
{
|
||||
if (!Exiv2::fileExists(path_, true)) {
|
||||
std::cerr << path_
|
||||
<< ": " << _("Failed to open the file\n");
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
|
||||
assert(image.get() != 0);
|
||||
image->readMetadata();
|
||||
const std::string& xmpPacket = image->xmpPacket();
|
||||
if (xmpPacket.empty()) {
|
||||
return -3;
|
||||
}
|
||||
std::string xmpPath = newFilePath(path_, ".xmp");
|
||||
if (dontOverwrite(xmpPath)) return 0;
|
||||
if (Params::instance().verbose_) {
|
||||
std::cout << _("Writing XMP packet from") << " " << path_
|
||||
<< " " << _("to") << " " << xmpPath << std::endl;
|
||||
}
|
||||
std::ofstream file(xmpPath.c_str());
|
||||
if (!file) {
|
||||
std::cerr << Params::instance().progname() << ": "
|
||||
<< _("Failed to open file ") << " " << xmpPath << ": "
|
||||
<< Exiv2::strError() << "\n";
|
||||
return 1;
|
||||
}
|
||||
file << xmpPacket;
|
||||
return 0;
|
||||
} // Extract::writeXmp
|
||||
|
||||
int Extract::writeThumbnail() const
|
||||
{
|
||||
if (!Exiv2::fileExists(path_, true)) {
|
||||
@ -981,34 +1069,25 @@ namespace Action {
|
||||
<< ": " << _("No Exif data found in the file\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
std::string directory = Params::instance().directory_;
|
||||
if (directory.empty()) directory = Util::dirname(path_);
|
||||
std::string thumb = directory + EXV_SEPERATOR_STR
|
||||
+ Util::basename(path_, true) + "-thumb";
|
||||
std::string thumbExt = exifData.thumbnailExtension();
|
||||
int rc = 0;
|
||||
std::string thumbExt = exifData.thumbnailExtension();
|
||||
if (thumbExt.empty()) {
|
||||
std::cerr << path_ << ": " << _("Image does not contain an Exif thumbnail\n");
|
||||
}
|
||||
else {
|
||||
std::string thumb = newFilePath(path_, "-thumb");
|
||||
std::string thumbPath = thumb + thumbExt;
|
||||
if (dontOverwrite(thumbPath)) return 0;
|
||||
if (Params::instance().verbose_) {
|
||||
Exiv2::DataBuf buf = exifData.copyThumbnail();
|
||||
std::cout << _("Writing") << " "
|
||||
<< exifData.thumbnailFormat() << " " << _("thumbnail") << " ("
|
||||
<< buf.size_ << " " << _("Bytes") << ") " << _("to file") << " "
|
||||
<< thumb << thumbExt << std::endl;
|
||||
}
|
||||
if (!Params::instance().force_ && Exiv2::fileExists(thumb + thumbExt)) {
|
||||
std::cout << Params::instance().progname()
|
||||
<< ": " << _("Overwrite") << " `" << thumb + thumbExt << "'? ";
|
||||
std::string s;
|
||||
std::cin >> s;
|
||||
if (s[0] != 'y' && s[0] != 'Y') return 0;
|
||||
<< thumbPath << std::endl;
|
||||
}
|
||||
rc = exifData.writeThumbnail(thumb);
|
||||
if (rc) {
|
||||
std::cerr << thumb << ": " << _("Exif data doesn't contain a thumbnail\n");
|
||||
std::cerr << path_ << ": " << _("Exif data doesn't contain a thumbnail\n");
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
@ -1042,15 +1121,17 @@ namespace Action {
|
||||
if ( rc == 0
|
||||
&& Params::instance().target_ & Params::ctExif
|
||||
|| Params::instance().target_ & Params::ctIptc
|
||||
|| Params::instance().target_ & Params::ctComment) {
|
||||
std::string directory = Params::instance().directory_;
|
||||
if (directory.empty()) directory = Util::dirname(path);
|
||||
|| Params::instance().target_ & Params::ctComment
|
||||
|| Params::instance().target_ & Params::ctXmp) {
|
||||
std::string suffix = Params::instance().suffix_;
|
||||
if (suffix.empty()) suffix = ".exv";
|
||||
std::string exvPath = directory + EXV_SEPERATOR_STR
|
||||
+ Util::basename(path, true) + suffix;
|
||||
std::string exvPath = newFilePath(path, suffix);
|
||||
rc = metacopy(exvPath, path, true);
|
||||
}
|
||||
if (0 == rc && Params::instance().target_ & Params::ctXmpPacket) {
|
||||
// Todo: Implement me!
|
||||
std::cerr << "Insertion of XMP packet from *.xmp file not implemented yet.\n";
|
||||
}
|
||||
if (Params::instance().preserve_) {
|
||||
ts.touch(path);
|
||||
}
|
||||
@ -1065,10 +1146,7 @@ namespace Action {
|
||||
|
||||
int Insert::insertThumbnail(const std::string& path) const
|
||||
{
|
||||
std::string directory = Params::instance().directory_;
|
||||
if (directory.empty()) directory = Util::dirname(path);
|
||||
std::string thumbPath = directory + EXV_SEPERATOR_STR
|
||||
+ Util::basename(path, true) + "-thumb.jpg";
|
||||
std::string thumbPath = newFilePath(path, "-thumb.jpg");
|
||||
if (!Exiv2::fileExists(thumbPath, true)) {
|
||||
std::cerr << thumbPath
|
||||
<< ": " << _("Failed to open the file\n");
|
||||
@ -1137,7 +1215,7 @@ namespace Action {
|
||||
{
|
||||
if (!Params::instance().jpegComment_.empty()) {
|
||||
if (Params::instance().verbose_) {
|
||||
std::cout << _("Setting Jpeg comment") << " '"
|
||||
std::cout << _("Setting JPEG comment") << " '"
|
||||
<< Params::instance().jpegComment_
|
||||
<< "'"
|
||||
<< std::endl;
|
||||
@ -1238,6 +1316,13 @@ namespace Action {
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << _("Warning") << ": " << modifyCmd.key_ << ": "
|
||||
<< _("Failed to read") << " "
|
||||
<< Exiv2::TypeInfo::typeName(value->typeId())
|
||||
<< " " << _("value")
|
||||
<< " \"" << modifyCmd.value_ << "\"\n";
|
||||
}
|
||||
}
|
||||
|
||||
void Modify::delMetadatum(Exiv2::Image* pImage, const ModifyCmd& modifyCmd)
|
||||
@ -1550,15 +1635,24 @@ namespace {
|
||||
if ( Params::instance().target_ & Params::ctIptc
|
||||
&& !sourceImage->iptcData().empty()) {
|
||||
if (Params::instance().verbose_) {
|
||||
std::cout << _("Writing Iptc data from") << " " << source
|
||||
std::cout << _("Writing IPTC data from") << " " << source
|
||||
<< " " << _("to") << " " << target << std::endl;
|
||||
}
|
||||
targetImage->setIptcData(sourceImage->iptcData());
|
||||
}
|
||||
if ( Params::instance().target_ & Params::ctXmp
|
||||
&& !sourceImage->xmpData().empty()) {
|
||||
if (Params::instance().verbose_) {
|
||||
std::cout << _("Writing XMP data from") << " " << source
|
||||
<< " " << _("to") << " " << target << std::endl;
|
||||
}
|
||||
// Todo: Should use XMP packet if there are no XMP modification commands
|
||||
targetImage->setXmpData(sourceImage->xmpData());
|
||||
}
|
||||
if ( Params::instance().target_ & Params::ctComment
|
||||
&& !sourceImage->comment().empty()) {
|
||||
if (Params::instance().verbose_) {
|
||||
std::cout << _("Writing Jpeg comment from") << " " << source
|
||||
std::cout << _("Writing JPEG comment from") << " " << source
|
||||
<< " " << _("to") << " " << target << std::endl;
|
||||
}
|
||||
targetImage->setComment(sourceImage->comment());
|
||||
@ -1668,4 +1762,25 @@ namespace {
|
||||
return 0;
|
||||
} // renameFile
|
||||
|
||||
std::string newFilePath(const std::string& path, const std::string& ext)
|
||||
{
|
||||
std::string directory = Params::instance().directory_;
|
||||
if (directory.empty()) directory = Util::dirname(path);
|
||||
std::string newPath = directory + EXV_SEPERATOR_STR
|
||||
+ Util::basename(path, true) + ext;
|
||||
return newPath;
|
||||
}
|
||||
|
||||
int dontOverwrite(const std::string& path)
|
||||
{
|
||||
if (!Params::instance().force_ && Exiv2::fileExists(path)) {
|
||||
std::cout << Params::instance().progname()
|
||||
<< ": " << _("Overwrite") << " `" << path << "'? ";
|
||||
std::string s;
|
||||
std::cin >> s;
|
||||
if (s[0] != 'y' && s[0] != 'Y') return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -164,6 +164,8 @@ namespace Action {
|
||||
int printComment();
|
||||
//! Print uninterpreted Iptc information
|
||||
int printIptc();
|
||||
//! print uninterpreted XMP information
|
||||
int printXmp();
|
||||
//! Print Exif summary information
|
||||
int printSummary();
|
||||
//! Print the list of Exif data in user defined format
|
||||
@ -268,6 +270,8 @@ namespace Action {
|
||||
on the format of the Exif thumbnail image.
|
||||
*/
|
||||
int writeThumbnail() const;
|
||||
//! Write the XMP packet to a file.
|
||||
int writeXmp() const;
|
||||
|
||||
private:
|
||||
virtual Extract* clone_() const;
|
||||
|
||||
@ -592,4 +592,25 @@ namespace Exiv2 {
|
||||
return "MemIo";
|
||||
}
|
||||
|
||||
// *************************************************************************
|
||||
// free functions
|
||||
|
||||
DataBuf readFile(const std::string& path)
|
||||
{
|
||||
FileIo file(path);
|
||||
if (file.open("rb") != 0) {
|
||||
throw Error(10, path, "rb", strError());
|
||||
}
|
||||
struct stat st;
|
||||
if (0 != stat(path.c_str(), &st)) {
|
||||
throw Error(2, path, strError(), "stat");
|
||||
}
|
||||
DataBuf buf(st.st_size);
|
||||
long len = file.read(buf.pData_, buf.size_);
|
||||
if (len != buf.size_) {
|
||||
throw Error(2, path, strError(), "FileIo::read");
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
} // namespace Exiv2
|
||||
|
||||
@ -676,6 +676,13 @@ namespace Exiv2 {
|
||||
// METHODS
|
||||
void reserve(long wcount);
|
||||
}; // class MemIo
|
||||
|
||||
// *****************************************************************************
|
||||
// template, inline and free functions
|
||||
|
||||
//! Read file \em path into a DataBuf, which is returned.
|
||||
DataBuf readFile(const std::string& path);
|
||||
|
||||
} // namespace Exiv2
|
||||
|
||||
#endif // #ifndef BASICIO_HPP_
|
||||
|
||||
@ -77,10 +77,11 @@ namespace Exiv2 {
|
||||
ErrMsg( 32, N_("Setting %1 in %2 images is not supported")), // %1=metadata type, %2=image format
|
||||
ErrMsg( 33, N_("This does not look like a CRW image")),
|
||||
ErrMsg( 34, N_("%1: Not supported")), // %1=function
|
||||
// 35
|
||||
// 36
|
||||
ErrMsg( 35, N_("Unknown XMP prefix `%1'")), // %1=prefix
|
||||
ErrMsg( 36, N_("No XMP property list for prefix `%1'")), // %1=prefix
|
||||
ErrMsg( 37, N_("Size of %1 JPEG segment is larger than 65535 bytes")), // %1=type of metadata (Exif, IPTC, JPEG comment)
|
||||
|
||||
ErrMsg( 38, N_("Unknown XMP property `%1:%2'")), // %1=prefix, %2=property name
|
||||
ErrMsg( 39, N_("XMP Toolkit error %1: %2")), // %1=XMP_Error::GetID(), %2=XMP_Error::GetErrMsg()
|
||||
// Last error message (message is not used)
|
||||
ErrMsg( -2, N_("(Unknown Error)"))
|
||||
};
|
||||
|
||||
21
src/exif.cpp
21
src/exif.cpp
@ -82,9 +82,6 @@ namespace {
|
||||
uint32_t offset,
|
||||
Exiv2::ByteOrder byteOrder);
|
||||
|
||||
// Read file path into a DataBuf, which is returned.
|
||||
Exiv2::DataBuf readFile(const std::string& path);
|
||||
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
@ -1300,22 +1297,4 @@ namespace {
|
||||
pos->setValue(offset, byteOrder);
|
||||
}
|
||||
|
||||
Exiv2::DataBuf readFile(const std::string& path)
|
||||
{
|
||||
Exiv2::FileIo file(path);
|
||||
if (file.open("rb") != 0) {
|
||||
throw Exiv2::Error(10, path, "rb", Exiv2::strError());
|
||||
}
|
||||
struct stat st;
|
||||
if (0 != stat(path.c_str(), &st)) {
|
||||
throw Exiv2::Error(2, path, Exiv2::strError(), "::stat");
|
||||
}
|
||||
Exiv2::DataBuf buf(st.st_size);
|
||||
long len = file.read(buf.pData_, buf.size_);
|
||||
if (len != buf.size_) {
|
||||
throw Exiv2::Error(2, path, Exiv2::strError(), "FileIo::read");
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
57
src/exif.hpp
57
src/exif.hpp
@ -135,10 +135,6 @@ namespace Exiv2 {
|
||||
Calls setValue(const Value*).
|
||||
*/
|
||||
Exifdatum& operator=(const Value& value);
|
||||
/*!
|
||||
@brief Set the value. This method copies (clones) the value pointed
|
||||
to by \em pValue.
|
||||
*/
|
||||
void setValue(const Value* pValue);
|
||||
/*!
|
||||
@brief Set the value to the string \em value. Uses Value::read(const
|
||||
@ -224,64 +220,16 @@ namespace Exiv2 {
|
||||
//! Return the value as a string.
|
||||
std::string toString() const
|
||||
{ return value_.get() == 0 ? "" : value_->toString(); }
|
||||
/*!
|
||||
@brief Return the <EM>n</EM>-th component of the value converted to
|
||||
long. The return value is -1 if the value of the Exifdatum is
|
||||
not set and the behaviour of the method is undefined if there
|
||||
is no n-th component.
|
||||
*/
|
||||
std::string toString(long n) const
|
||||
{ return value_.get() == 0 ? "" : value_->toString(n); }
|
||||
long toLong(long n =0) const
|
||||
{ return value_.get() == 0 ? -1 : value_->toLong(n); }
|
||||
/*!
|
||||
@brief Return the <EM>n</EM>-th component of the value converted to
|
||||
float. The return value is -1 if the value of the Exifdatum is
|
||||
not set and the behaviour of the method is undefined if there
|
||||
is no n-th component.
|
||||
*/
|
||||
float toFloat(long n =0) const
|
||||
{ return value_.get() == 0 ? -1 : value_->toFloat(n); }
|
||||
/*!
|
||||
@brief Return the <EM>n</EM>-th component of the value converted to
|
||||
Rational. The return value is -1/1 if the value of the
|
||||
Exifdatum is not set and the behaviour of the method is
|
||||
undefined if there is no n-th component.
|
||||
*/
|
||||
Rational toRational(long n =0) const
|
||||
{ return value_.get() == 0 ? Rational(-1, 1) : value_->toRational(n); }
|
||||
/*!
|
||||
@brief Return an auto-pointer to a copy (clone) of the value. The
|
||||
caller owns this copy and the auto-pointer ensures that it will
|
||||
be deleted.
|
||||
|
||||
This method is provided for users who need full control over the
|
||||
value. A caller may, e.g., downcast the pointer to the appropriate
|
||||
subclass of Value to make use of the interface of the subclass to set
|
||||
or modify its contents.
|
||||
|
||||
@return An auto-pointer to a copy (clone) of the value, 0 if the value
|
||||
is not set.
|
||||
*/
|
||||
Value::AutoPtr getValue() const
|
||||
{ return value_.get() == 0 ? Value::AutoPtr(0) : value_->clone(); }
|
||||
/*!
|
||||
@brief Return a constant reference to the value.
|
||||
|
||||
This method is provided mostly for convenient and versatile output of
|
||||
the value which can (to some extent) be formatted through standard
|
||||
stream manipulators. Do not attempt to write to the value through
|
||||
this reference.
|
||||
|
||||
<b>Example:</b> <br>
|
||||
@code
|
||||
ExifData::const_iterator i = exifData.findKey(key);
|
||||
if (i != exifData.end()) {
|
||||
std::cout << i->key() << " " << std::hex << i->value() << "\n";
|
||||
}
|
||||
@endcode
|
||||
|
||||
@return A constant reference to the value.
|
||||
@throw Error if the value is not set.
|
||||
*/
|
||||
const Value& value() const;
|
||||
//! Return the size of the data area.
|
||||
long sizeDataArea() const
|
||||
@ -300,7 +248,6 @@ namespace Exiv2 {
|
||||
*/
|
||||
DataBuf dataArea() const
|
||||
{ return value_.get() == 0 ? DataBuf(0, 0) : value_->dataArea(); }
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
|
||||
48
src/exiv2.1
48
src/exiv2.1
@ -3,7 +3,7 @@
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.TH EXIV2 1 "June 3rd, 2007"
|
||||
.TH EXIV2 1 "August 27th, 2007"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.\"
|
||||
.\" Some roff macros, for reference:
|
||||
@ -17,7 +17,7 @@
|
||||
.\" .sp <n> insert n+1 empty lines
|
||||
.\" for manpage-specific macros, see man(7)
|
||||
.SH NAME
|
||||
exiv2 \- Exif/IPTC metadata manipulation tool
|
||||
exiv2 \- Image metadata manipulation tool
|
||||
.SH SYNOPSIS
|
||||
.B exiv2
|
||||
[\fIoptions\fP] [\fIaction\fP] \fIfile\fP ...
|
||||
@ -28,11 +28,12 @@ exiv2 \- Exif/IPTC metadata manipulation tool
|
||||
.\" \fI<whatever>\fP escape sequences to invode bold face and italics,
|
||||
.\" respectively.
|
||||
.B exiv2
|
||||
is a program to read and write Exif and IPTC image metadata and image
|
||||
comments. Supported image formats are JPEG, Canon CRW and Canon THM.
|
||||
Read-only support is currently available for PNG and TIFF format and
|
||||
includes TIFF-based RAW formats such as Adobe DNG, Canon CR2, Fujifilm
|
||||
RAF, Minolta MRW, Nikon NEF, Pentax PEF, Sony ARW and Sony SR2.
|
||||
is a program to read and write Exif, IPTC and XMP image metadata and
|
||||
image comments. Supported image formats are JPEG, Canon CRW and Canon
|
||||
THM. Read-only support is currently available for PNG and TIFF format
|
||||
and includes TIFF-based RAW formats such as Adobe DNG, Canon CR2,
|
||||
Fujifilm RAF, Minolta MRW, Nikon NEF, Pentax PEF, Sony ARW and Sony
|
||||
SR2.
|
||||
.SH ACTIONS
|
||||
The \fIaction\fP argument is only required if it is not clear from the
|
||||
\fIoptions\fP which action is implied.
|
||||
@ -42,15 +43,15 @@ Print image metadata. This is the default action, i.e., the command
|
||||
\fIexiv2 image.jpg\fP will print a summary of the image Exif metadata.
|
||||
.TP
|
||||
.B ex | extract
|
||||
Extract metadata to *.exv and thumbnail image files. Modification
|
||||
commands can be applied on-the-fly.
|
||||
Extract metadata to *.exv, *.xmp and thumbnail image files.
|
||||
Modification commands can be applied on-the-fly.
|
||||
.TP
|
||||
.B in | insert
|
||||
Insert metadata from corresponding *.exv files. Use option \fB\-S\fP
|
||||
\fI.suf\fP to change the suffix of the input files. Since files of any
|
||||
supported format can be used as input files, this command can be used
|
||||
to copy the metadata between files of different formats. Modification
|
||||
commands can be applied on-the-fly.
|
||||
Insert metadata from corresponding *.exv, *.xmp and thumbnail files.
|
||||
Use option \fB\-S\fP \fI.suf\fP to change the suffix of the input
|
||||
files. Since files of any supported format can be used as input files,
|
||||
this command can be used to copy the metadata between files of
|
||||
different formats. Modification commands can be applied on-the-fly.
|
||||
.TP
|
||||
.B rm | delete
|
||||
Delete image metadata from the files.
|
||||
@ -132,6 +133,8 @@ h : hexdump of the Exif data (shortcut for -Pxgnycsh)
|
||||
.br
|
||||
i : IPTC data values
|
||||
.br
|
||||
x : XMP properties
|
||||
.br
|
||||
c : JPEG comment
|
||||
.TP
|
||||
.B \-P \fIcols\fP
|
||||
@ -171,17 +174,24 @@ t : Exif thumbnail only
|
||||
.br
|
||||
i : IPTC data
|
||||
.br
|
||||
x : XMP packet
|
||||
.br
|
||||
c : JPEG comment
|
||||
.TP
|
||||
.B \-i \fItgt\fP
|
||||
Insert target(s) for the 'insert' action. Possible targets are the
|
||||
same as those for the \fB\-d\fP option. Only JPEG thumbnails can be
|
||||
inserted (not TIFF thumbnails), they need to be named
|
||||
\fIfile\fP\-thumb.jpg.
|
||||
same as those for the \fB\-d\fP option, plus:
|
||||
.br
|
||||
X : Insert XMP packet from <file>.xmp
|
||||
.br
|
||||
Only JPEG thumbnails can be inserted (not TIFF thumbnails), they need to
|
||||
be named \fIfile\fP\-thumb.jpg.
|
||||
.TP
|
||||
.B \-e \fItgt\fP
|
||||
Extract target(s) for the 'extract' action. Possible targets are the same
|
||||
as those for the \fB\-d\fP option.
|
||||
as those for the \fB\-d\fP option, plus:
|
||||
.br
|
||||
X : Extract XMP packet to <file>.xmp
|
||||
.TP
|
||||
.B \-r \fIfmt\fP
|
||||
Filename format for the 'rename' action. The format string follows
|
||||
@ -349,7 +359,7 @@ Sample command file.
|
||||
Taglists with \fIkey\fP and default \fItype\fP values.
|
||||
.SH AUTHORS
|
||||
.B exiv2
|
||||
was written by Andreas HUGGEL <ahuggel@gmx.net>.
|
||||
was written by Andreas Huggel <ahuggel@gmx.net>.
|
||||
.PP
|
||||
This manual page was originally written by KELEMEN Peter <fuji@debian.org>,
|
||||
for the Debian project.
|
||||
|
||||
@ -214,12 +214,12 @@ void Params::help(std::ostream& os) const
|
||||
<< _(" rm | delete Delete image metadata from the files.\n")
|
||||
<< _(" in | insert Insert metadata from corresponding *.exv files.\n"
|
||||
" Use option -S to change the suffix of the input files.\n")
|
||||
<< _(" ex | extract Extract metadata to *.exv and thumbnail image files.\n")
|
||||
<< _(" ex | extract Extract metadata to *.exv, *.xmp and thumbnail image files.\n")
|
||||
<< _(" mv | rename Rename files and/or set file timestamps according to the\n"
|
||||
" Exif create timestamp. The filename format can be set with\n"
|
||||
" -r format, timestamp options are controlled with -t and -T.\n")
|
||||
<< _(" mo | modify Apply commands to modify (add, set, delete) the Exif and\n"
|
||||
" Iptc metadata of image files or set the Jpeg comment.\n"
|
||||
" IPTC metadata of image files or set the JPEG comment.\n"
|
||||
" Requires option -c, -m or -M.\n")
|
||||
<< _(" fi | fixiso Copy ISO setting from the Nikon Makernote to the regular\n"
|
||||
" Exif tag.\n")
|
||||
@ -242,8 +242,9 @@ void Params::help(std::ostream& os) const
|
||||
<< _(" t : interpreted (translated) Exif data (shortcut for -Pkyct)\n")
|
||||
<< _(" v : plain Exif data values (shortcut for -Pxgnycv)\n")
|
||||
<< _(" h : hexdump of the Exif data (shortcut for -Pxgnycsh)\n")
|
||||
<< _(" i : Iptc data values\n")
|
||||
<< _(" c : Jpeg comment\n")
|
||||
<< _(" i : IPTC data values\n")
|
||||
<< _(" x : XMP properties\n")
|
||||
<< _(" c : JPEG comment\n")
|
||||
<< _(" -P cols Print columns for the Exif taglist ('print' action). Valid are:\n")
|
||||
<< _(" x : print a column with the tag value\n")
|
||||
<< _(" g : group name\n")
|
||||
@ -260,13 +261,17 @@ void Params::help(std::ostream& os) const
|
||||
<< _(" a : all supported metadata (the default)\n")
|
||||
<< _(" e : Exif section\n")
|
||||
<< _(" t : Exif thumbnail only\n")
|
||||
<< _(" i : Iptc data\n")
|
||||
<< _(" c : Jpeg comment\n")
|
||||
<< _(" i : IPTC data\n")
|
||||
<< _(" x : XMP packet\n")
|
||||
<< _(" c : JPEG comment\n")
|
||||
<< _(" -i tgt Insert target(s) for the 'insert' action. Possible targets are\n"
|
||||
" the same as those for the -d option. Only Jpeg thumbnails can\n"
|
||||
" be inserted, they need to be named <file>-thumb.jpg\n")
|
||||
" the same as those for the -d option, plus:\n"
|
||||
" X : Insert XMP packet from <file>.xmp\n"
|
||||
" Only JPEG thumbnails can be inserted, they need to be named\n"
|
||||
" <file>-thumb.jpg\n")
|
||||
<< _(" -e tgt Extract target(s) for the 'extract' action. Possible targets\n"
|
||||
" are the same as those for the -d option.\n")
|
||||
" are the same as those for the -i option, plus:\n"
|
||||
" X : Extract XMP packet to <file>.xmp\n")
|
||||
<< _(" -r fmt Filename format for the 'rename' action. The format string\n"
|
||||
" follows strftime(3). The following keywords are supported:\n")
|
||||
<< _(" :basename: - original filename without extension\n")
|
||||
@ -274,7 +279,7 @@ void Params::help(std::ostream& os) const
|
||||
<< _(" :parentname: - name of parent directory\n")
|
||||
<< _(" Default filename format is ")
|
||||
<< format_ << ".\n"
|
||||
<< _(" -c txt Jpeg comment string to set in the image.\n")
|
||||
<< _(" -c txt JPEG comment string to set in the image.\n")
|
||||
<< _(" -m file Command file for the modify action. The format for commands is\n"
|
||||
" set|add|del <key> [[<type>] <value>].\n")
|
||||
<< _(" -M cmd Command line for the modify action. The format for the\n"
|
||||
@ -401,6 +406,7 @@ int Params::evalPrint(const std::string& optarg)
|
||||
case 'v': rc = evalPrintCols("xgnycv"); break;
|
||||
case 'h': rc = evalPrintCols("xgnycsh"); break;
|
||||
case 'i': printMode_ = pmIptc; break;
|
||||
case 'x': printMode_ = pmXmp; break;
|
||||
case 'c': printMode_ = pmComment; break;
|
||||
default:
|
||||
std::cerr << progname() << ": " << _("Unrecognized print mode") << " `"
|
||||
@ -780,11 +786,14 @@ namespace {
|
||||
switch (optarg[i]) {
|
||||
case 'e': target |= Params::ctExif; break;
|
||||
case 'i': target |= Params::ctIptc; break;
|
||||
case 'x': target |= Params::ctXmp; break;
|
||||
case 'X': target |= Params::ctXmpPacket; break;
|
||||
case 'c': target |= Params::ctComment; break;
|
||||
case 't': target |= Params::ctThumb; break;
|
||||
case 'a': target |= Params::ctExif
|
||||
| Params::ctIptc
|
||||
| Params::ctComment; break;
|
||||
| Params::ctComment
|
||||
| Params::ctXmp; break;
|
||||
default:
|
||||
std::cerr << Params::instance().progname() << ": " << _("Unrecognized ")
|
||||
<< action << " " << _("target") << " `" << optarg[i] << "'\n";
|
||||
|
||||
@ -123,7 +123,7 @@ public:
|
||||
void cleanup();
|
||||
|
||||
//! Enumerates print modes
|
||||
enum PrintMode { pmSummary, pmList, pmIptc, pmComment };
|
||||
enum PrintMode { pmSummary, pmList, pmIptc, pmXmp, pmComment };
|
||||
|
||||
//! Individual items to print
|
||||
enum PrintItem {
|
||||
@ -141,7 +141,7 @@ public:
|
||||
};
|
||||
|
||||
//! Enumerates common targets, bitmap
|
||||
enum CommonTarget { ctExif = 1, ctIptc = 2, ctComment = 4, ctThumb = 8 };
|
||||
enum CommonTarget { ctExif = 1, ctIptc = 2, ctComment = 4, ctThumb = 8, ctXmp = 16, ctXmpPacket = 32 };
|
||||
//! Enumerates the policies to handle existing files in rename action
|
||||
enum FileExistsPolicy { overwritePolicy, renamePolicy, askPolicy };
|
||||
|
||||
@ -193,7 +193,7 @@ private:
|
||||
printMode_(pmSummary),
|
||||
printItems_(0),
|
||||
action_(0),
|
||||
target_(ctExif|ctIptc|ctComment),
|
||||
target_(ctExif|ctIptc|ctComment|ctXmp),
|
||||
adjustment_(0),
|
||||
format_("%Y%m%d_%H%M%S"),
|
||||
formatSet_(false),
|
||||
|
||||
@ -23,13 +23,13 @@
|
||||
#define EXV_PACKAGE_NAME "exiv2"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define EXV_PACKAGE_STRING "exiv2 0.15"
|
||||
#define EXV_PACKAGE_STRING "exiv2 0.16"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define EXV_PACKAGE_TARNAME "exiv2"
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define EXV_PACKAGE_VERSION "0.15"
|
||||
#define EXV_PACKAGE_VERSION "0.16"
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define pid_t. */
|
||||
#define pid_t int
|
||||
|
||||
@ -74,21 +74,21 @@ EXIV2_RCSID("@(#) $Id$")
|
||||
namespace Exiv2 {
|
||||
|
||||
const ImageFactory::Registry ImageFactory::registry_[] = {
|
||||
//image type creation fct type check Exif mode IPTC mode Comment mode
|
||||
//--------------- --------------- ---------- ----------- ----------- ------------
|
||||
{ ImageType::jpeg, newJpegInstance, isJpegType, amReadWrite, amReadWrite, amReadWrite },
|
||||
{ ImageType::exv, newExvInstance, isExvType, amReadWrite, amReadWrite, amReadWrite },
|
||||
{ ImageType::cr2, newCr2Instance, isCr2Type, amRead, amRead, amNone },
|
||||
{ ImageType::crw, newCrwInstance, isCrwType, amReadWrite, amNone, amReadWrite },
|
||||
{ ImageType::mrw, newMrwInstance, isMrwType, amRead, amRead, amNone },
|
||||
{ ImageType::tiff, newTiffInstance, isTiffType, amRead, amRead, amNone },
|
||||
{ ImageType::orf, newOrfInstance, isOrfType, amRead, amRead, amNone },
|
||||
#ifdef EXV_HAVE_LIBZ
|
||||
{ ImageType::png, newPngInstance, isPngType, amRead, amRead, amNone },
|
||||
#endif // EXV_HAVE_LIBZ
|
||||
{ ImageType::raf, newRafInstance, isRafType, amRead, amRead, amNone },
|
||||
// End of list marker
|
||||
{ ImageType::none, 0, 0, amNone, amNone, amNone }
|
||||
//image type creation fct type check Exif mode IPTC mode XMP mode Comment mode
|
||||
//--------------- --------------- ---------- ----------- ----------- ----------- ------------
|
||||
{ ImageType::jpeg, newJpegInstance, isJpegType, amReadWrite, amReadWrite, amReadWrite, amReadWrite },
|
||||
{ ImageType::exv, newExvInstance, isExvType, amReadWrite, amReadWrite, amReadWrite, amReadWrite },
|
||||
{ ImageType::cr2, newCr2Instance, isCr2Type, amRead, amRead, amRead, amNone },
|
||||
{ ImageType::crw, newCrwInstance, isCrwType, amReadWrite, amNone, amNone, amReadWrite },
|
||||
{ ImageType::mrw, newMrwInstance, isMrwType, amRead, amRead, amRead, amNone },
|
||||
{ ImageType::tiff, newTiffInstance, isTiffType, amRead, amRead, amRead, amNone },
|
||||
{ ImageType::orf, newOrfInstance, isOrfType, amRead, amRead, amRead, amNone },
|
||||
#ifdef EXV_HAVE_LIBZ
|
||||
{ ImageType::png, newPngInstance, isPngType, amRead, amRead, amRead, amNone },
|
||||
#endif // EXV_HAVE_LIBZ
|
||||
{ ImageType::raf, newRafInstance, isRafType, amRead, amRead, amRead, amNone },
|
||||
// End of list marker
|
||||
{ ImageType::none, 0, 0, amNone, amNone, amNone, amNone }
|
||||
};
|
||||
|
||||
bool ImageFactory::Registry::operator==(const int& imageType) const
|
||||
@ -110,6 +110,7 @@ namespace Exiv2 {
|
||||
clearExifData();
|
||||
clearIptcData();
|
||||
clearXmpPacket();
|
||||
clearXmpData();
|
||||
clearComment();
|
||||
}
|
||||
|
||||
@ -117,6 +118,7 @@ namespace Exiv2 {
|
||||
{
|
||||
setExifData(image.exifData());
|
||||
setIptcData(image.iptcData());
|
||||
setXmpData(image.xmpData());
|
||||
setComment(image.comment());
|
||||
}
|
||||
|
||||
@ -150,6 +152,16 @@ namespace Exiv2 {
|
||||
xmpPacket_ = xmpPacket;
|
||||
}
|
||||
|
||||
void Image::clearXmpData()
|
||||
{
|
||||
xmpData_.clear();
|
||||
}
|
||||
|
||||
void Image::setXmpData(const XmpData& xmpData)
|
||||
{
|
||||
xmpData_ = xmpData;
|
||||
}
|
||||
|
||||
void Image::clearComment()
|
||||
{
|
||||
comment_.erase();
|
||||
@ -189,6 +201,9 @@ namespace Exiv2 {
|
||||
case mdIptc:
|
||||
am = r->iptcSupport_;
|
||||
break;
|
||||
case mdXmp:
|
||||
am = r->xmpSupport_;
|
||||
break;
|
||||
case mdComment:
|
||||
am = r->commentSupport_;
|
||||
break;
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
#include "basicio.hpp"
|
||||
#include "exif.hpp"
|
||||
#include "iptc.hpp"
|
||||
#include "xmp.hpp"
|
||||
|
||||
// + standard includes
|
||||
#include <string>
|
||||
@ -151,6 +152,17 @@ namespace Exiv2 {
|
||||
@brief Set the raw XMP packet to \em xmpPacket.
|
||||
*/
|
||||
virtual void clearXmpPacket();
|
||||
/*!
|
||||
@brief Assign new XMP data. The new XMP data is not written
|
||||
to the image until the writeMetadata() method is called.
|
||||
@param xmpData An XmpData instance holding XMP data to be copied
|
||||
*/
|
||||
virtual void setXmpData(const XmpData& xmpData);
|
||||
/*!
|
||||
@brief Erase any buffered XMP data. XMP data is not removed from
|
||||
the actual image until the writeMetadata() method is called.
|
||||
*/
|
||||
virtual void clearXmpData();
|
||||
/*!
|
||||
@brief Set the image comment. The new comment is not written
|
||||
to the image until the writeMetadata() method is called.
|
||||
@ -198,6 +210,18 @@ namespace Exiv2 {
|
||||
@return modifiable IptcData instance containing IPTC values
|
||||
*/
|
||||
virtual IptcData& iptcData() { return iptcData_; }
|
||||
/*!
|
||||
@brief Returns an XmpData instance containing currently buffered
|
||||
XMP data.
|
||||
|
||||
The contained XMP data may have been read from the image by
|
||||
a previous call to readMetadata() or added directly. The XMP
|
||||
data in the returned instance will be written to the image when
|
||||
writeMetadata() is called.
|
||||
|
||||
@return modifiable XmpData instance containing XMP values
|
||||
*/
|
||||
virtual XmpData& xmpData() { return xmpData_; }
|
||||
/*!
|
||||
@brief Return a modifiable reference to the raw XMP packet.
|
||||
*/
|
||||
@ -247,6 +271,18 @@ namespace Exiv2 {
|
||||
@return modifiable IptcData instance containing IPTC values
|
||||
*/
|
||||
virtual const IptcData& iptcData() const { return iptcData_; }
|
||||
/*!
|
||||
@brief Returns an XmpData instance containing currently buffered
|
||||
XMP data.
|
||||
|
||||
The contained XMP data may have been read from the image by
|
||||
a previous call to readMetadata() or added directly. The XMP
|
||||
data in the returned instance will be written to the image when
|
||||
writeMetadata() is called.
|
||||
|
||||
@return modifiable XmpData instance containing XMP values
|
||||
*/
|
||||
virtual const XmpData& xmpData() const { return xmpData_; }
|
||||
/*!
|
||||
@brief Return a copy of the image comment. May be an empty string.
|
||||
*/
|
||||
@ -289,6 +325,7 @@ namespace Exiv2 {
|
||||
BasicIo::AutoPtr io_; //!< Image data IO pointer
|
||||
ExifData exifData_; //!< Exif data container
|
||||
IptcData iptcData_; //!< IPTC data container
|
||||
XmpData xmpData_; //!< XMP data container
|
||||
std::string comment_; //!< User comment
|
||||
std::string xmpPacket_; //!< XMP packet
|
||||
|
||||
@ -461,6 +498,7 @@ namespace Exiv2 {
|
||||
IsThisTypeFct isThisType_;
|
||||
AccessMode exifSupport_;
|
||||
AccessMode iptcSupport_;
|
||||
AccessMode xmpSupport_;
|
||||
AccessMode commentSupport_;
|
||||
};
|
||||
|
||||
|
||||
75
src/iptc.hpp
75
src/iptc.hpp
@ -95,10 +95,6 @@ namespace Exiv2 {
|
||||
Calls setValue(const Value*).
|
||||
*/
|
||||
Iptcdatum& operator=(const Value& value);
|
||||
/*!
|
||||
@brief Set the Value. This method copies (clones) the %Value pointed
|
||||
to by \em pValue.
|
||||
*/
|
||||
void setValue(const Value* pValue);
|
||||
/*!
|
||||
@brief Set the value to the string \em value, using
|
||||
@ -113,23 +109,12 @@ namespace Exiv2 {
|
||||
|
||||
//! @name Accessors
|
||||
//@{
|
||||
/*!
|
||||
@brief Write value to a data buffer and return the number
|
||||
of bytes written.
|
||||
|
||||
The user must ensure that the buffer has enough memory. Otherwise
|
||||
the call results in undefined behaviour.
|
||||
|
||||
@param buf Data buffer to write to.
|
||||
@param byteOrder Applicable byte order (little or big endian).
|
||||
@return Number of characters written.
|
||||
*/
|
||||
long copy(byte* buf, ByteOrder byteOrder) const
|
||||
{ return value_.get() == 0 ? 0 : value_->copy(buf, byteOrder); }
|
||||
/*!
|
||||
@brief Return the key of the Iptcdatum. The key is of the form
|
||||
'<b>Iptc</b>.recordName.datasetName'. Note however that the key
|
||||
is not necessarily unique, i.e., an IptcData may contain
|
||||
is not necessarily unique, i.e., an IptcData object may contain
|
||||
multiple metadata with the same key.
|
||||
*/
|
||||
std::string key() const { return key_.get() == 0 ? "" : key_->key(); }
|
||||
@ -156,78 +141,24 @@ namespace Exiv2 {
|
||||
//! Return the tag (aka dataset) number
|
||||
uint16_t tag() const
|
||||
{ return key_.get() == 0 ? 0 : key_->tag(); }
|
||||
//! Return the type id of the value
|
||||
TypeId typeId() const
|
||||
{ return value_.get() == 0 ? invalidTypeId : value_->typeId(); }
|
||||
//! Return the name of the type
|
||||
const char* typeName() const { return TypeInfo::typeName(typeId()); }
|
||||
//! Return the size in bytes of one component of this type
|
||||
long typeSize() const { return TypeInfo::typeSize(typeId()); }
|
||||
//! Return the number of components in the value
|
||||
long count() const { return value_.get() == 0 ? 0 : value_->count(); }
|
||||
//! Return the size of the value in bytes
|
||||
long size() const { return value_.get() == 0 ? 0 : value_->size(); }
|
||||
//! Return the value as a string.
|
||||
std::string toString() const
|
||||
{ return value_.get() == 0 ? "" : value_->toString(); }
|
||||
/*!
|
||||
@brief Return the n-th component of the value converted to long. The
|
||||
return value is -1 if the value of the Iptcdatum is not set and
|
||||
the behaviour of the method is undefined if there is no n-th
|
||||
component.
|
||||
*/
|
||||
std::string toString(long n) const
|
||||
{ return value_.get() == 0 ? "" : value_->toString(n); }
|
||||
long toLong(long n =0) const
|
||||
{ return value_.get() == 0 ? -1 : value_->toLong(n); }
|
||||
/*!
|
||||
@brief Return the n-th component of the value converted to float. The
|
||||
return value is -1 if the value of the Iptcdatum is not set and
|
||||
the behaviour of the method is undefined if there is no n-th
|
||||
component.
|
||||
*/
|
||||
float toFloat(long n =0) const
|
||||
{ return value_.get() == 0 ? -1 : value_->toFloat(n); }
|
||||
/*!
|
||||
@brief Return the n-th component of the value converted to
|
||||
Rational. The return value is -1/1 if the value of the
|
||||
Iptcdatum is not set and the behaviour of the method is
|
||||
undefined if there is no n-th component.
|
||||
*/
|
||||
Rational toRational(long n =0) const
|
||||
{ return value_.get() == 0 ? Rational(-1, 1) : value_->toRational(n); }
|
||||
/*!
|
||||
@brief Return an auto-pointer to a copy (clone) of the value. The
|
||||
caller owns this copy and the auto-pointer ensures that it will
|
||||
be deleted.
|
||||
|
||||
This method is provided for users who need full control over the
|
||||
value. A caller may, e.g., downcast the pointer to the appropriate
|
||||
subclass of Value to make use of the interface of the subclass to set
|
||||
or modify its contents.
|
||||
|
||||
@return An auto-pointer to a copy (clone) of the value, 0 if the value
|
||||
is not set.
|
||||
*/
|
||||
Value::AutoPtr getValue() const
|
||||
{ return value_.get() == 0 ? Value::AutoPtr(0) : value_->clone(); }
|
||||
/*!
|
||||
@brief Return a constant reference to the value.
|
||||
|
||||
This method is provided mostly for convenient and versatile output of
|
||||
the value which can (to some extent) be formatted through standard
|
||||
stream manipulators. Do not attempt to write to the value through
|
||||
this reference.
|
||||
|
||||
<b>Example:</b> <br>
|
||||
@code
|
||||
IptcData::const_iterator i = iptcData.findKey(key);
|
||||
if (i != iptcData.end()) {
|
||||
std::cout << i->key() << " " << std::hex << i->value() << "\n";
|
||||
}
|
||||
@endcode
|
||||
|
||||
@return A constant reference to the value.
|
||||
@throw Error If the value is not set.
|
||||
*/
|
||||
const Value& value() const;
|
||||
//@}
|
||||
|
||||
|
||||
@ -291,7 +291,12 @@ namespace Exiv2 {
|
||||
io_->read(xmpPacket.pData_, xmpPacket.size_);
|
||||
if (io_->error() || io_->eof()) throw Error(14);
|
||||
xmpPacket_.assign(reinterpret_cast<char*>(xmpPacket.pData_), xmpPacket.size_);
|
||||
// Todo: Update here when merging branches/xmp
|
||||
if (XmpParser::decode(xmpData_, xmpPacket_)) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: Failed to decode XMP metadata.\n";
|
||||
#endif
|
||||
xmpData_.clear();
|
||||
}
|
||||
--search;
|
||||
}
|
||||
else if ( marker == app13_
|
||||
|
||||
@ -146,7 +146,8 @@ namespace Exiv2 {
|
||||
/*!
|
||||
@brief Set the value to the string buf.
|
||||
Uses Value::read(const std::string& buf). If the metadatum does
|
||||
not have a value yet, then an AsciiValue is created.
|
||||
not have a value yet, then one is created. See subclasses for
|
||||
more details.
|
||||
*/
|
||||
virtual void setValue(const std::string& buf) =0;
|
||||
//@}
|
||||
@ -168,8 +169,8 @@ namespace Exiv2 {
|
||||
/*!
|
||||
@brief Return the key of the metadatum. The key is of the form
|
||||
'familyName.ifdItem.tagName'. Note however that the key
|
||||
is not necessarily unique, i.e., an ExifData may contain
|
||||
multiple metadata with the same key.
|
||||
is not necessarily unique, i.e., an ExifData object may
|
||||
contain multiple metadata with the same key.
|
||||
*/
|
||||
virtual std::string key() const =0;
|
||||
//! Return the name of the tag (which is also the third part of the key)
|
||||
@ -191,24 +192,27 @@ namespace Exiv2 {
|
||||
//! Return the value as a string.
|
||||
virtual std::string toString() const =0;
|
||||
/*!
|
||||
@brief Return the n-th component of the value converted to long. The
|
||||
return value is -1 if the value of the Metadatum is not set and
|
||||
the behaviour of the method is undefined if there is no n-th
|
||||
component.
|
||||
@brief Return the <EM>n</EM>-th component of the value converted to
|
||||
a string. The behaviour of the method is undefined if there
|
||||
is no <EM>n</EM>-th component.
|
||||
*/
|
||||
virtual std::string toString(long n) const =0;
|
||||
/*!
|
||||
@brief Return the <EM>n</EM>-th component of the value converted to long.
|
||||
The return value is -1 if the value is not set and the behaviour
|
||||
of the method is undefined if there is no <EM>n</EM>-th component.
|
||||
*/
|
||||
virtual long toLong(long n =0) const =0;
|
||||
/*!
|
||||
@brief Return the n-th component of the value converted to float. The
|
||||
return value is -1 if the value of the Metadatum is not set and
|
||||
the behaviour of the method is undefined if there is no n-th
|
||||
component.
|
||||
@brief Return the <EM>n</EM>-th component of the value converted to float.
|
||||
The return value is -1 if the value is not set and the behaviour
|
||||
of the method is undefined if there is no <EM>n</EM>-th component.
|
||||
*/
|
||||
virtual float toFloat(long n =0) const =0;
|
||||
/*!
|
||||
@brief Return the n-th component of the value converted to
|
||||
Rational. The return value is -1/1 if the value of the
|
||||
Metadatum is not set and the behaviour of the method is
|
||||
undefined if there is no n-th component.
|
||||
@brief Return the <EM>n</EM>-th component of the value converted to Rational.
|
||||
The return value is -1/1 if the value is not set and the behaviour
|
||||
of the method is undefined if there is no <EM>n</EM>-th component.
|
||||
*/
|
||||
virtual Rational toRational(long n =0) const =0;
|
||||
/*!
|
||||
|
||||
@ -24,13 +24,13 @@
|
||||
<a href="http://www.dalibor.cz/minolta/makernote.htm">Minolta Makernote Format Specification</a> by Dalibor Jelinek,<br>
|
||||
<a href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Minolta.html">Minolta Makernote list</a> by Phil Harvey<br>
|
||||
<a href="http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html">Minolta Makernote list from PHP JPEG Metadata Toolkit</a><br>
|
||||
Email communication with <a href="mailto:caulier dot gilles at kdemail dot net">caulier dot gilles at kdemail dot net</a><br>
|
||||
Email communication with <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a><br>
|
||||
Some Minolta camera settings have been decoded by <a href="mailto:xraynaud@gmail.com">Xavier Raynaud</a> from digiKam project and added by Gilles Caulier.
|
||||
@version $Rev$
|
||||
@author Andreas Huggel (ahu)
|
||||
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
|
||||
@author Gilles Caulier (gc)
|
||||
<a href="mailto:caulier dot gilles at kdemail dot net">caulier dot gilles at kdemail dot net</a>
|
||||
<a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
@date 06-May-06, gc: submitted
|
||||
*/
|
||||
#ifndef MINOLTAMN_HPP_
|
||||
|
||||
648
src/pngchunk.cpp
648
src/pngchunk.cpp
@ -36,7 +36,7 @@ EXIV2_RCSID("@(#) $Id: pngchunk.cpp 823 2006-06-23 07:35:00Z cgilles $")
|
||||
# include "exv_conf.h"
|
||||
#endif
|
||||
|
||||
//#define DEBUG 1
|
||||
#define DEBUG 1
|
||||
|
||||
// some defines to make it easier
|
||||
#define PNG_CHUNK_TYPE(data, index) &data[index+4]
|
||||
@ -73,9 +73,9 @@ PNG tags : http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PN
|
||||
// class member definitions
|
||||
namespace Exiv2 {
|
||||
|
||||
void PngChunk::decode(Image* pImage,
|
||||
const byte* pData,
|
||||
long size)
|
||||
void PngChunk::decode(Image* pImage,
|
||||
const byte* pData,
|
||||
long size)
|
||||
{
|
||||
assert(pImage != 0);
|
||||
assert(pData != 0);
|
||||
@ -88,7 +88,8 @@ namespace Exiv2 {
|
||||
{
|
||||
while (index < size-PNG_CHUNK_HEADER_SIZE &&
|
||||
strncmp((char*)PNG_CHUNK_TYPE(pData, index), "tEXt", 4) &&
|
||||
strncmp((char*)PNG_CHUNK_TYPE(pData, index), "zTXt", 4))
|
||||
strncmp((char*)PNG_CHUNK_TYPE(pData, index), "zTXt", 4) &&
|
||||
strncmp((char*)PNG_CHUNK_TYPE(pData, index), "iTXt", 4))
|
||||
{
|
||||
if (!strncmp((char*)PNG_CHUNK_TYPE(pData, index), "IEND", 4))
|
||||
throw Error(14);
|
||||
@ -98,7 +99,7 @@ namespace Exiv2 {
|
||||
|
||||
if (index < size-PNG_CHUNK_HEADER_SIZE)
|
||||
{
|
||||
// we found a tEXt or zTXt field
|
||||
// we found a tEXt, zTXt, or iTXt field
|
||||
|
||||
// get the key, it's a null terminated string at the chunk start
|
||||
const byte *key = &PNG_CHUNK_DATA(pData, index, 0);
|
||||
@ -111,158 +112,15 @@ namespace Exiv2 {
|
||||
throw Error(14);
|
||||
}
|
||||
|
||||
DataBuf arr;
|
||||
|
||||
if(!strncmp((char*)PNG_CHUNK_TYPE(pData, index), "zTXt", 4))
|
||||
{
|
||||
// Extract a deflate compressed Latin-1 text chunk
|
||||
DataBuf arr = parsePngChunk(pData, size, index, keysize);
|
||||
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::decode: We found a zTXt field\n";
|
||||
#endif
|
||||
// we get the compression method after the key
|
||||
const byte* compressionMethod = &PNG_CHUNK_DATA(pData, index, keysize+1);
|
||||
if ( *compressionMethod != 0x00 )
|
||||
{
|
||||
// then it isn't zlib compressed and we are sunk
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::decode: Non-standard compression method.\n";
|
||||
#endif
|
||||
throw Error(14);
|
||||
}
|
||||
|
||||
// compressed string after the compression technique spec
|
||||
const byte* compressedText = &PNG_CHUNK_DATA(pData, index, keysize+2);
|
||||
unsigned int compressedTextSize = getLong(&pData[index], bigEndian)-keysize-2;
|
||||
|
||||
// security check, also considering overflow wraparound from the addition --
|
||||
// we may endup with a /smaller/ index if we wrap all the way around
|
||||
long firstIndex = (long)(compressedText - pData);
|
||||
long onePastLastIndex = firstIndex + compressedTextSize;
|
||||
if ( onePastLastIndex > size || onePastLastIndex <= firstIndex)
|
||||
throw Error(14);
|
||||
|
||||
uLongf uncompressedLen = compressedTextSize * 2; // just a starting point
|
||||
int zlibResult;
|
||||
|
||||
do
|
||||
{
|
||||
arr.alloc(uncompressedLen);
|
||||
zlibResult = uncompress((Bytef*)arr.pData_, &uncompressedLen,
|
||||
compressedText, compressedTextSize);
|
||||
|
||||
if (Z_OK == zlibResult)
|
||||
{
|
||||
// then it is all OK
|
||||
arr.alloc(uncompressedLen);
|
||||
}
|
||||
else if (Z_BUF_ERROR == zlibResult)
|
||||
{
|
||||
// the uncompressedArray needs to be larger
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::decode: doubling size for decompression.\n";
|
||||
#endif
|
||||
uncompressedLen *= 2;
|
||||
|
||||
// DoS protection. can't be bigger than 64k
|
||||
if ( uncompressedLen > 131072 )
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// something bad happened
|
||||
throw Error(14);
|
||||
}
|
||||
}
|
||||
while (Z_BUF_ERROR == zlibResult);
|
||||
|
||||
if (zlibResult != Z_OK)
|
||||
throw Error(14);
|
||||
}
|
||||
else if (!strncmp((char*)PNG_CHUNK_TYPE(pData, index), "tEXt", 4))
|
||||
{
|
||||
// Extract a non-compressed Latin-1 text chunk
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::decode: We found a tEXt field\n";
|
||||
#endif
|
||||
// the text comes after the key, but isn't null terminated
|
||||
const byte* text = &PNG_CHUNK_DATA(pData, index, keysize+1);
|
||||
long textsize = getLong(&pData[index], bigEndian)-keysize-1;
|
||||
|
||||
// security check, also considering overflow wraparound from the addition --
|
||||
// we may endup with a /smaller/ index if we wrap all the way around
|
||||
long firstIndex = (long)(text - pData);
|
||||
long onePastLastIndex = firstIndex + textsize;
|
||||
|
||||
if ( onePastLastIndex > size || onePastLastIndex <= firstIndex)
|
||||
throw Error(14);
|
||||
|
||||
arr.alloc(textsize);
|
||||
arr = DataBuf(text, textsize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO : Add 'iTXt' chunk 'Description' tag support here
|
||||
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::decode: We found a field, not expected though\n";
|
||||
#endif
|
||||
throw Error(14);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::decode: Found PNG entry " << std::string((const char*)key) << " / "
|
||||
<< std::string((const char*)arr.pData_, 64) << "\n";
|
||||
std::cerr << "Exiv2::PngChunk::decode: Found PNG chunk: "
|
||||
<< std::string((const char*)key) << " :: "
|
||||
<< std::string((const char*)arr.pData_, 32) << "\n";
|
||||
#endif
|
||||
|
||||
// We look if an EXIF raw profile exist.
|
||||
|
||||
if ( memcmp("Raw profile type exif", key, 21) == 0 ||
|
||||
memcmp("Raw profile type APP1", key, 21) == 0 )
|
||||
{
|
||||
DataBuf exifData = readRawProfile(arr);
|
||||
long length = exifData.size_;
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
// Find the position of Exif header in bytes array.
|
||||
|
||||
const byte exifHeader[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
|
||||
long pos = -1;
|
||||
|
||||
for (long i=0 ; i < length-(long)sizeof(exifHeader) ; i++)
|
||||
{
|
||||
if (memcmp(exifHeader, &exifData.pData_[i], sizeof(exifHeader)) == 0)
|
||||
{
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If found it, store only these data at from this place.
|
||||
|
||||
if (pos !=-1)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::decode: Exif header found at position " << pos << "\n";
|
||||
#endif
|
||||
pos = pos + sizeof(exifHeader);
|
||||
TiffParser::decode(pImage, exifData.pData_ + pos, length - pos,
|
||||
TiffCreator::create, TiffDecoder::findDecoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We look if an IPTC raw profile exist.
|
||||
|
||||
if ( memcmp("Raw profile type iptc", key, 21) == 0 )
|
||||
{
|
||||
DataBuf iptcData = readRawProfile(arr);
|
||||
long length = iptcData.size_;
|
||||
|
||||
if (length > 0)
|
||||
pImage->iptcData().load(iptcData.pData_, length);
|
||||
}
|
||||
parseChunkContent(pImage, key, arr);
|
||||
|
||||
index += getLong(&pData[index], bigEndian) + PNG_CHUNK_HEADER_SIZE;
|
||||
}
|
||||
@ -270,6 +128,261 @@ namespace Exiv2 {
|
||||
|
||||
} // PngChunk::decode
|
||||
|
||||
DataBuf PngChunk::parsePngChunk(const byte* pData, long size, long& index, int keysize)
|
||||
{
|
||||
DataBuf arr;
|
||||
|
||||
if(!strncmp((char*)PNG_CHUNK_TYPE(pData, index), "zTXt", 4))
|
||||
{
|
||||
// Extract a deflate compressed Latin-1 text chunk
|
||||
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::parsePngChunk: We found a zTXt field\n";
|
||||
#endif
|
||||
// we get the compression method after the key
|
||||
const byte* compressionMethod = &PNG_CHUNK_DATA(pData, index, keysize+1);
|
||||
if ( *compressionMethod != 0x00 )
|
||||
{
|
||||
// then it isn't zlib compressed and we are sunk
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::parsePngChunk: Non-standard zTXt compression method.\n";
|
||||
#endif
|
||||
throw Error(14);
|
||||
}
|
||||
|
||||
// compressed string after the compression technique spec
|
||||
const byte* compressedText = &PNG_CHUNK_DATA(pData, index, keysize+2);
|
||||
unsigned int compressedTextSize = getLong(&pData[index], bigEndian)-keysize-2;
|
||||
|
||||
// security check, also considering overflow wraparound from the addition --
|
||||
// we may endup with a /smaller/ index if we wrap all the way around
|
||||
long firstIndex = (long)(compressedText - pData);
|
||||
long onePastLastIndex = firstIndex + compressedTextSize;
|
||||
if ( onePastLastIndex > size || onePastLastIndex <= firstIndex)
|
||||
throw Error(14);
|
||||
|
||||
zlibUncompress(compressedText, compressedTextSize, arr);
|
||||
}
|
||||
else if (!strncmp((char*)PNG_CHUNK_TYPE(pData, index), "tEXt", 4))
|
||||
{
|
||||
// Extract a non-compressed Latin-1 text chunk
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::parsePngChunk: We found a tEXt field\n";
|
||||
#endif
|
||||
// the text comes after the key, but isn't null terminated
|
||||
const byte* text = &PNG_CHUNK_DATA(pData, index, keysize+1);
|
||||
long textsize = getLong(&pData[index], bigEndian)-keysize-1;
|
||||
|
||||
// security check, also considering overflow wraparound from the addition --
|
||||
// we may endup with a /smaller/ index if we wrap all the way around
|
||||
long firstIndex = (long)(text - pData);
|
||||
long onePastLastIndex = firstIndex + textsize;
|
||||
|
||||
if ( onePastLastIndex > size || onePastLastIndex <= firstIndex)
|
||||
throw Error(14);
|
||||
|
||||
arr.alloc(textsize);
|
||||
arr = DataBuf(text, textsize);
|
||||
}
|
||||
else if(!strncmp((char*)PNG_CHUNK_TYPE(pData, index), "iTXt", 4))
|
||||
{
|
||||
// Extract a deflate compressed or uncompressed UTF-8 text chunk
|
||||
|
||||
// we get the compression flag after the key
|
||||
const byte* compressionFlag = &PNG_CHUNK_DATA(pData, index, keysize+1);
|
||||
// we get the compression method after the compression flag
|
||||
const byte* compressionMethod = &PNG_CHUNK_DATA(pData, index, keysize+1);
|
||||
// language description string after the compression technique spec
|
||||
const byte* languageText = &PNG_CHUNK_DATA(pData, index, keysize+1);
|
||||
unsigned int languageTextSize = getLong(&pData[index], bigEndian)-keysize-1;
|
||||
// translated keyword string after the language description
|
||||
const byte* translatedKeyText = &PNG_CHUNK_DATA(pData, index, keysize+1);
|
||||
unsigned int translatedKeyTextSize = getLong(&pData[index], bigEndian)-keysize-1;
|
||||
|
||||
if ( *compressionFlag == 0x00 )
|
||||
{
|
||||
// then it's an uncompressed iTXt chunk
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::parsePngChunk: We found an uncompressed iTXt field\n";
|
||||
#endif
|
||||
|
||||
// the text comes after the translated keyword, but isn't null terminated
|
||||
const byte* text = &PNG_CHUNK_DATA(pData, index, keysize+1);
|
||||
long textsize = getLong(&pData[index], bigEndian)-keysize-1;
|
||||
|
||||
// security check, also considering overflow wraparound from the addition --
|
||||
// we may endup with a /smaller/ index if we wrap all the way around
|
||||
long firstIndex = (long)(text - pData);
|
||||
long onePastLastIndex = firstIndex + textsize;
|
||||
|
||||
if ( onePastLastIndex > size || onePastLastIndex <= firstIndex)
|
||||
throw Error(14);
|
||||
|
||||
arr.alloc(textsize);
|
||||
arr = DataBuf(text, textsize);
|
||||
}
|
||||
else if ( *compressionMethod == 0x00 )
|
||||
{
|
||||
// then it's a zlib compressed iTXt chunk
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::parsePngChunk: We found a zlib compressed iTXt field\n";
|
||||
#endif
|
||||
|
||||
// the compressed text comes after the translated keyword, but isn't null terminated
|
||||
const byte* compressedText = &PNG_CHUNK_DATA(pData, index, keysize+1);
|
||||
long compressedTextSize = getLong(&pData[index], bigEndian)-keysize-1;
|
||||
|
||||
// security check, also considering overflow wraparound from the addition --
|
||||
// we may endup with a /smaller/ index if we wrap all the way around
|
||||
long firstIndex = (long)(compressedText - pData);
|
||||
long onePastLastIndex = firstIndex + compressedTextSize;
|
||||
if ( onePastLastIndex > size || onePastLastIndex <= firstIndex)
|
||||
throw Error(14);
|
||||
|
||||
zlibUncompress(compressedText, compressedTextSize, arr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// then it isn't zlib compressed and we are sunk
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::parsePngChunk: Non-standard iTXt compression method.\n";
|
||||
#endif
|
||||
throw Error(14);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::parsePngChunk: We found a field, not expected though\n";
|
||||
#endif
|
||||
throw Error(14);
|
||||
}
|
||||
|
||||
return arr;
|
||||
|
||||
} // PngChunk::parsePngChunk
|
||||
|
||||
void PngChunk::parseChunkContent(Image* pImage, const byte *key, const DataBuf arr)
|
||||
{
|
||||
// We look if an ImageMagick EXIF raw profile exist.
|
||||
|
||||
if ( (memcmp("Raw profile type exif", key, 21) == 0 ||
|
||||
memcmp("Raw profile type APP1", key, 21) == 0) &&
|
||||
pImage->exifData().empty())
|
||||
{
|
||||
DataBuf exifData = readRawProfile(arr);
|
||||
long length = exifData.size_;
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
// Find the position of Exif header in bytes array.
|
||||
|
||||
const byte exifHeader[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
|
||||
long pos = -1;
|
||||
|
||||
for (long i=0 ; i < length-(long)sizeof(exifHeader) ; i++)
|
||||
{
|
||||
if (memcmp(exifHeader, &exifData.pData_[i], sizeof(exifHeader)) == 0)
|
||||
{
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If found it, store only these data at from this place.
|
||||
|
||||
if (pos !=-1)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::decode: Exif header found at position " << pos << "\n";
|
||||
#endif
|
||||
pos = pos + sizeof(exifHeader);
|
||||
TiffParser::decode(pImage, exifData.pData_ + pos, length - pos,
|
||||
TiffCreator::create, TiffDecoder::findDecoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We look if an ImageMagick IPTC raw profile exist.
|
||||
|
||||
if ( memcmp("Raw profile type iptc", key, 21) == 0 &&
|
||||
pImage->iptcData().empty())
|
||||
{
|
||||
DataBuf iptcData = readRawProfile(arr);
|
||||
long length = iptcData.size_;
|
||||
|
||||
if (length > 0)
|
||||
pImage->iptcData().load(iptcData.pData_, length);
|
||||
}
|
||||
|
||||
// We look if an ImageMagick XMP raw profile exist.
|
||||
|
||||
if ( memcmp("Raw profile type xmp", key, 20) == 0 &&
|
||||
pImage->xmpData().empty())
|
||||
{
|
||||
DataBuf xmpBuf = readRawProfile(arr);
|
||||
long length = xmpBuf.size_;
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
std::string& xmpPacket = pImage->xmpPacket();
|
||||
xmpPacket.assign(reinterpret_cast<char*>(xmpBuf.pData_), length);
|
||||
std::string::size_type idx = xmpPacket.find_first_of('<');
|
||||
if (idx != std::string::npos && idx > 0)
|
||||
{
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: Removing " << idx << " characters "
|
||||
<< "from the beginning of the XMP packet\n";
|
||||
#endif
|
||||
xmpPacket = xmpPacket.substr(idx);
|
||||
}
|
||||
if (XmpParser::decode(pImage->xmpData(), xmpPacket))
|
||||
{
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: Failed to decode XMP metadata.\n";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We look if an Adobe XMP string exist.
|
||||
|
||||
if ( memcmp("XML:com.adobe.xmp", key, 17) == 0 &&
|
||||
pImage->xmpData().empty())
|
||||
{
|
||||
if (arr.size_ > 0)
|
||||
{
|
||||
std::string& xmpPacket = pImage->xmpPacket();
|
||||
xmpPacket.assign(reinterpret_cast<char*>(arr.pData_), arr.size_);
|
||||
std::string::size_type idx = xmpPacket.find_first_of('<');
|
||||
if (idx != std::string::npos && idx > 0)
|
||||
{
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: Removing " << idx << " characters "
|
||||
<< "from the beginning of the XMP packet\n";
|
||||
#endif
|
||||
xmpPacket = xmpPacket.substr(idx);
|
||||
}
|
||||
if (XmpParser::decode(pImage->xmpData(), xmpPacket))
|
||||
{
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: Failed to decode XMP metadata.\n";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We look if a comments string exist. Note than we use only 'Description' keyword which
|
||||
// is dedicaced to store long comments. 'Comment' keyword is ignored.
|
||||
|
||||
if ( memcmp("Description", key, 11) == 0 &&
|
||||
pImage->comment().empty())
|
||||
{
|
||||
pImage->comment().assign(reinterpret_cast<char*>(arr.pData_), arr.size_);
|
||||
}
|
||||
|
||||
} // PngChunk::parseChunkContent
|
||||
|
||||
DataBuf PngChunk::readRawProfile(const DataBuf& text)
|
||||
{
|
||||
DataBuf info;
|
||||
@ -349,5 +462,232 @@ namespace Exiv2 {
|
||||
}
|
||||
|
||||
return info;
|
||||
|
||||
} // PngChunk::readRawProfile
|
||||
|
||||
void PngChunk::zlibUncompress(const byte* compressedText,
|
||||
unsigned int compressedTextSize,
|
||||
DataBuf& arr)
|
||||
{
|
||||
uLongf uncompressedLen = compressedTextSize * 2; // just a starting point
|
||||
int zlibResult;
|
||||
|
||||
do
|
||||
{
|
||||
arr.alloc(uncompressedLen);
|
||||
zlibResult = uncompress((Bytef*)arr.pData_, &uncompressedLen,
|
||||
compressedText, compressedTextSize);
|
||||
|
||||
if (zlibResult == Z_OK)
|
||||
{
|
||||
// then it is all OK
|
||||
arr.alloc(uncompressedLen);
|
||||
}
|
||||
else if (zlibResult == Z_BUF_ERROR)
|
||||
{
|
||||
// the uncompressedArray needs to be larger
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Exiv2::PngChunk::parsePngChunk: doubling size for decompression.\n";
|
||||
#endif
|
||||
uncompressedLen *= 2;
|
||||
|
||||
// DoS protection. can't be bigger than 64k
|
||||
if ( uncompressedLen > 131072 )
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// something bad happened
|
||||
throw Error(14);
|
||||
}
|
||||
}
|
||||
while (zlibResult == Z_BUF_ERROR);
|
||||
|
||||
if (zlibResult != Z_OK)
|
||||
throw Error(14);
|
||||
|
||||
} // PngChunk::zlibUncompress
|
||||
|
||||
/* TODO : code backported from digiKam. Not yet adapted and used.
|
||||
|
||||
void PngChunk::writeRawProfile(png_struct *ping,
|
||||
png_info* ping_info,
|
||||
char* profile_type,
|
||||
char* profile_data,
|
||||
png_uint_32 length)
|
||||
{
|
||||
png_textp text;
|
||||
|
||||
register long i;
|
||||
|
||||
uchar *sp;
|
||||
|
||||
png_charp dp;
|
||||
|
||||
png_uint_32 allocated_length, description_length;
|
||||
|
||||
const uchar hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
|
||||
|
||||
DDebug() << "Writing Raw profile: type=" << profile_type << ", length=" << length << endl;
|
||||
|
||||
text = (png_textp) png_malloc(ping, (png_uint_32) sizeof(png_text));
|
||||
description_length = strlen((const char *) profile_type);
|
||||
allocated_length = (png_uint_32) (length*2 + (length >> 5) + 20 + description_length);
|
||||
|
||||
text[0].text = (png_charp) png_malloc(ping, allocated_length);
|
||||
text[0].key = (png_charp) png_malloc(ping, (png_uint_32) 80);
|
||||
text[0].key[0] = '\0';
|
||||
|
||||
concatenateString(text[0].key, "Raw profile type ", 4096);
|
||||
concatenateString(text[0].key, (const char *) profile_type, 62);
|
||||
|
||||
sp = (uchar*)profile_data;
|
||||
dp = text[0].text;
|
||||
*dp++='\n';
|
||||
|
||||
copyString(dp, (const char *) profile_type, allocated_length);
|
||||
|
||||
dp += description_length;
|
||||
*dp++='\n';
|
||||
|
||||
formatString(dp, allocated_length-strlen(text[0].text), "%8lu ", length);
|
||||
|
||||
dp += 8;
|
||||
|
||||
for (i=0; i < (long) length; i++)
|
||||
{
|
||||
if (i%36 == 0)
|
||||
*dp++='\n';
|
||||
|
||||
*(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
|
||||
*(dp++)=(char) hex[((*sp++ ) & 0x0f)];
|
||||
}
|
||||
|
||||
*dp++='\n';
|
||||
*dp='\0';
|
||||
text[0].text_length = (png_size_t) (dp-text[0].text);
|
||||
text[0].compression = -1;
|
||||
|
||||
if (text[0].text_length <= allocated_length)
|
||||
png_set_text(ping, ping_info,text, 1);
|
||||
|
||||
png_free(ping, text[0].text);
|
||||
png_free(ping, text[0].key);
|
||||
png_free(ping, text);
|
||||
|
||||
} // PngChunk::writeRawProfile
|
||||
|
||||
size_t PngChunk::concatenateString(char* destination,
|
||||
const char* source,
|
||||
const size_t length)
|
||||
{
|
||||
register char *q;
|
||||
|
||||
register const char *p;
|
||||
|
||||
register size_t i;
|
||||
|
||||
size_t count;
|
||||
|
||||
if ( !destination || !source || length == 0 )
|
||||
return 0;
|
||||
|
||||
p = source;
|
||||
q = destination;
|
||||
i = length;
|
||||
|
||||
while ((i-- != 0) && (*q != '\0'))
|
||||
q++;
|
||||
|
||||
count = (size_t) (q-destination);
|
||||
i = length-count;
|
||||
|
||||
if (i == 0)
|
||||
return(count+strlen(p));
|
||||
|
||||
while (*p != '\0')
|
||||
{
|
||||
if (i != 1)
|
||||
{
|
||||
*q++=(*p);
|
||||
i--;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
*q='\0';
|
||||
|
||||
return(count+(p-source));
|
||||
|
||||
} // PngChunk::concatenateString
|
||||
|
||||
size_t PngChunk::copyString(char* destination,
|
||||
const char* source,
|
||||
const size_t length)
|
||||
{
|
||||
register char *q;
|
||||
|
||||
register const char *p;
|
||||
|
||||
register size_t i;
|
||||
|
||||
if ( !destination || !source || length == 0 )
|
||||
return 0;
|
||||
|
||||
p = source;
|
||||
q = destination;
|
||||
i = length;
|
||||
|
||||
if ((i != 0) && (--i != 0))
|
||||
{
|
||||
do
|
||||
{
|
||||
if ((*q++=(*p++)) == '\0')
|
||||
break;
|
||||
}
|
||||
while (--i != 0);
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
if (length != 0)
|
||||
*q='\0';
|
||||
|
||||
while (*p++ != '\0');
|
||||
}
|
||||
|
||||
return((size_t) (p-source-1));
|
||||
|
||||
} // PngChunk::copyString
|
||||
|
||||
long PngChunk::formatString(char* string,
|
||||
const size_t length,
|
||||
const char* format,
|
||||
...)
|
||||
{
|
||||
long n;
|
||||
|
||||
va_list operands;
|
||||
|
||||
va_start(operands,format);
|
||||
n = (long) formatStringList(string, length, format, operands);
|
||||
va_end(operands);
|
||||
return(n);
|
||||
|
||||
} // PngChunk::formatString
|
||||
|
||||
long PngChunk::formatStringList(char* string,
|
||||
const size_t length,
|
||||
const char* format,
|
||||
va_list operands)
|
||||
{
|
||||
int n = vsnprintf(string, length, format, operands);
|
||||
|
||||
if (n < 0)
|
||||
string[length-1] = '\0';
|
||||
|
||||
return((long) n);
|
||||
} // PngChunk::formatStringList
|
||||
*/
|
||||
|
||||
} // namespace Exiv2
|
||||
|
||||
@ -20,12 +20,16 @@
|
||||
*/
|
||||
/*!
|
||||
@file pngchunk.hpp
|
||||
@brief Class PngChunk to parse PNG chunk data.
|
||||
@brief Class PngChunk to parse PNG chunk data implemented using the following references:<br>
|
||||
<a href="http://www.vias.org/pngguide/chapter11_05.html">PNG iTXt chunk structure</a> from PNG definitive guide,<br>
|
||||
<a href="http://www.vias.org/pngguide/chapter11_04.html">PNG tTXt and zTXt chunks structures</a> from PNG definitive guide,<br>
|
||||
<a href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PNG.html">PNG tags list</a> by Phil Harvey<br>
|
||||
Email communication with <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a><br>
|
||||
@version $Rev: 823 $
|
||||
@author Andreas Huggel (ahu)
|
||||
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
|
||||
@author Gilles Caulier (gc)
|
||||
<a href="mailto:caulier dot gilles at kdemail dot net">caulier dot gilles at kdemail dot net</a>
|
||||
<a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
@date 12-Jun-06, gc: submitted
|
||||
*/
|
||||
#ifndef PNGCHUNK_HPP_
|
||||
@ -66,19 +70,69 @@ namespace Exiv2 {
|
||||
no checks are performed.
|
||||
@param size Length of the data buffer.
|
||||
*/
|
||||
static void decode(Image* pImage,
|
||||
const byte* pData,
|
||||
long size);
|
||||
static void decode(Image* pImage,
|
||||
const byte* pData,
|
||||
long size);
|
||||
|
||||
private:
|
||||
private:
|
||||
//! @name Accessors
|
||||
//@{
|
||||
|
||||
/*!
|
||||
@brief Todo: Decode ImageMagick raw text profile including encoded Exif/Iptc metadata byte array.
|
||||
@brief Parse PNG chunk to determine type and extract content.
|
||||
Supported Chunk types are tTXt, zTXt, and iTXt.
|
||||
*/
|
||||
static DataBuf parsePngChunk(const byte* pData,
|
||||
long size,
|
||||
long& index,
|
||||
int keysize);
|
||||
|
||||
/*!
|
||||
@brief Parse PNG chunk contents to extract metadata container and assign it to image.
|
||||
Supported contents are:
|
||||
Exif raw text profile generated by ImageMagick ==> Image Exif metadata.
|
||||
Iptc raw text profile generated by ImageMagick ==> Image Iptc metadata.
|
||||
Xmp raw text profile generated by ImageMagick ==> Image Xmp metadata.
|
||||
Xmp packet generated by Adobe ==> Image Xmp metadata.
|
||||
Description string ==> Image Comments.
|
||||
*/
|
||||
static void parseChunkContent(Image* pImage,
|
||||
const byte* key,
|
||||
const DataBuf arr);
|
||||
|
||||
/*!
|
||||
@brief Decode from ImageMagick raw text profile which host encoded Exif/Iptc/Xmp metadata byte array.
|
||||
*/
|
||||
static DataBuf readRawProfile(const DataBuf& text);
|
||||
|
||||
/*!
|
||||
@brief Wrapper around zlib to uncompress a PNG chunk content.
|
||||
*/
|
||||
static void zlibUncompress(const byte* compressedText,
|
||||
unsigned int compressedTextSize,
|
||||
DataBuf& arr);
|
||||
|
||||
/* TODO : code backported from digiKam. Not yet adapted and used.
|
||||
|
||||
static DataBuf writeRawProfile(const DataBuf& text);
|
||||
|
||||
static size_t concatenateString(char* destination,
|
||||
const char* source,
|
||||
const size_t length);
|
||||
|
||||
static size_t copyString(char* destination,
|
||||
const char* source,
|
||||
const size_t length);
|
||||
|
||||
static long formatString(char* string,
|
||||
const size_t length,
|
||||
const char* format,
|
||||
...);
|
||||
|
||||
static long formatStringList(char *string,
|
||||
const size_t length,
|
||||
const char *format,
|
||||
va_list operands);*/
|
||||
//@}
|
||||
|
||||
}; // class PngChunk
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
@author Andreas Huggel (ahu)
|
||||
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
|
||||
@author Gilles Caulier (gc)
|
||||
<a href="mailto:caulier dot gilles at kdemail dot net">caulier dot gilles at kdemail dot net</a>
|
||||
<a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
|
||||
@date 12-Jun-06, gc: submitted
|
||||
*/
|
||||
#ifndef PNGIMAGE_HPP_
|
||||
|
||||
906
src/properties.cpp
Normal file
906
src/properties.cpp
Normal file
@ -0,0 +1,906 @@
|
||||
// ***************************************************************** -*- C++ -*-
|
||||
/*
|
||||
* Copyright (C) 2007 Andreas Huggel <ahuggel@gmx.net>
|
||||
*
|
||||
* This program is part of the Exiv2 distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
/*
|
||||
File: properties.cpp
|
||||
Version: $Rev$
|
||||
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
|
||||
History: 13-July-07, ahu: created
|
||||
*/
|
||||
// *****************************************************************************
|
||||
#include "rcsid.hpp"
|
||||
EXIV2_RCSID("@(#) $Id$")
|
||||
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
#include "properties.hpp"
|
||||
#include "tags.hpp"
|
||||
#include "error.hpp"
|
||||
#include "types.hpp"
|
||||
#include "value.hpp"
|
||||
#include "metadatum.hpp"
|
||||
#include "i18n.h" // NLS support.
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
// *****************************************************************************
|
||||
// class member definitions
|
||||
namespace Exiv2 {
|
||||
|
||||
extern const XmpPropertyInfo xmpDcInfo[];
|
||||
extern const XmpPropertyInfo xmpXmpInfo[];
|
||||
extern const XmpPropertyInfo xmpXmpRightsInfo[];
|
||||
extern const XmpPropertyInfo xmpXmpMMInfo[];
|
||||
extern const XmpPropertyInfo xmpXmpBJInfo[];
|
||||
extern const XmpPropertyInfo xmpXmpTPgInfo[];
|
||||
extern const XmpPropertyInfo xmpPhotoshopInfo[];
|
||||
extern const XmpPropertyInfo xmpXmpDMInfo[];
|
||||
extern const XmpPropertyInfo xmpPdfInfo[];
|
||||
extern const XmpPropertyInfo xmpTiffInfo[];
|
||||
extern const XmpPropertyInfo xmpExifInfo[];
|
||||
|
||||
extern const XmpNsInfo xmpNsInfo[] = {
|
||||
// Schemas
|
||||
{ "http://purl.org/dc/elements/1.1/", "dc", xmpDcInfo, "Dublin Core schema" },
|
||||
{ "http://ns.adobe.com/xap/1.0/", "xmp", xmpXmpInfo, "XMP Basic schema" },
|
||||
{ "http://ns.adobe.com/xap/1.0/rights/", "xmpRights", xmpXmpRightsInfo, "XMP Rights Management schema" },
|
||||
{ "http://ns.adobe.com/xap/1.0/mm/", "xmpMM", xmpXmpMMInfo, "XMP Media Management schema" },
|
||||
{ "http://ns.adobe.com/xap/1.0/bj/", "xmpBJ", xmpXmpBJInfo, "XMP Basic Job Ticket schema" },
|
||||
{ "http://ns.adobe.com/xap/1.0/t/pg/", "xmpTPg", xmpXmpTPgInfo, "XMP Paged-Text schema" },
|
||||
{ "http://ns.adobe.com/xmp/1.0/DynamicMedia/", "xmpDM", xmpXmpDMInfo, "XMP Dynamic Media schema" },
|
||||
{ "http://ns.adobe.com/pdf/1.3/", "pdf", xmpPdfInfo, "Adobe PDF schema" },
|
||||
{ "http://ns.adobe.com/photoshop/1.0/", "photoshop", xmpPhotoshopInfo, "Adobe photoshop schema" },
|
||||
{ "http://ns.adobe.com/camera-raw-settings/1.0/", "crs", 0, "Camera Raw schema" },
|
||||
{ "http://ns.adobe.com/tiff/1.0/", "tiff", xmpTiffInfo, "Exif Schema for TIFF Properties" },
|
||||
{ "http://ns.adobe.com/exif/1.0/", "exif", xmpExifInfo, "Exif schema for Exif-specific Properties" },
|
||||
{ "http://ns.adobe.com/exif/1.0/aux/", "aux", 0, "Exif schema for Additional Exif Properties" },
|
||||
{ "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/", "iptc" /*Iptc4xmpCore*/, 0, "IPTC Core schema" },
|
||||
|
||||
// Structures
|
||||
{ "http://ns.adobe.com/xap/1.0/g/", "xapG", 0, "Colorant structure" },
|
||||
{ "http://ns.adobe.com/xap/1.0/sType/Dimensions#", "stDim", 0, "Dimensions structure" },
|
||||
{ "http://ns.adobe.com/xap/1.0/sType/Font#", "stFnt", 0, "Font structure" },
|
||||
{ "http://ns.adobe.com/xap/1.0/g/img/", "xapGImg", 0, "Thumbnail structure" },
|
||||
{ "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#", "stEvt", 0, "Resource Event structure" },
|
||||
{ "http://ns.adobe.com/xap/1.0/sType/ResourceRef#", "stRef", 0, "ResourceRef structure" },
|
||||
{ "http://ns.adobe.com/xap/1.0/sType/Version#", "stVer", 0, "Version structure" },
|
||||
{ "http://ns.adobe.com/xap/1.0/sType/Job#", "stJob", 0, "Basic Job/Workflow structure" },
|
||||
|
||||
// Qualifiers
|
||||
{ "http://ns.adobe.com/xmp/Identifier/qual/1.0/", "xmpidq", 0, "Qualifier for xmp:Identifier" }
|
||||
};
|
||||
|
||||
extern const XmpPropertyInfo xmpDcInfo[] = {
|
||||
{ "contributor", "contributor", "bag ProperName", xmpText, xmpExternal, "Contributors to the resource (other than the authors)." },
|
||||
{ "coverage", "coverage", "Text", xmpText, xmpExternal, "The extent or scope of the resource." },
|
||||
{ "creator", "creator", "seq ProperName", xmpText, xmpExternal, "The authors of the resource (listed in order of precedence, if significant)." },
|
||||
{ "date", "date", "seq Date", xmpText, xmpExternal, "Date(s) that something interesting happened to the resource." },
|
||||
{ "description", "description", "Lang Alt", xmpText, xmpExternal, "A textual description of the content of the resource. Multiple values may be "
|
||||
"present for different languages." },
|
||||
{ "format", "format", "MIMEType", xmpText, xmpInternal, "The file format used when saving the resource. Tools and applications should set "
|
||||
"this property to the save format of the data. It may include appropriate qualifiers." },
|
||||
{ "identifier", "identifier", "Text", xmpText, xmpExternal, "Unique identifier of the resource." },
|
||||
{ "language", "language", "bag Locale", xmpText, xmpInternal, "An unordered array specifying the languages used in the resource." },
|
||||
{ "publisher", "publisher", "bag ProperName", xmpText, xmpExternal, "Publishers." },
|
||||
{ "relation", "relation", "bag Text", xmpText, xmpInternal, "Relationships to other documents." },
|
||||
{ "rights", "rights", "Lang Alt", xmpText, xmpExternal, "Informal rights statement, selected by language." },
|
||||
{ "source", "source", "Text", xmpText, xmpExternal, "Unique identifier of the work from which this resource was derived." },
|
||||
{ "subject", "subject", "bag Text", xmpText, xmpExternal, "An unordered array of descriptive phrases or keywords that specify the topic of the "
|
||||
"content of the resource." },
|
||||
{ "title", "title", "Lang Alt", xmpText, xmpExternal, "The title of the document, or the name given to the resource. Typically, it will be "
|
||||
"a name by which the resource is formally known." },
|
||||
{ "type", "type", "bag open Choice", xmpText, xmpExternal, "A document type; for example, novel, poem, or working paper." },
|
||||
// End of list marker
|
||||
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
|
||||
};
|
||||
|
||||
extern const XmpPropertyInfo xmpXmpInfo[] = {
|
||||
{ "Advisory", "Advisory", "bag XPath", xmpText, xmpExternal, "An unordered array specifying properties that were edited outside the authoring "
|
||||
"application. Each item should contain a single namespace and XPath separated by "
|
||||
"one ASCII space (U+0020)." },
|
||||
{ "BaseURL", "BaseURL", "URL", xmpText, xmpInternal, "The base URL for relative URLs in the document content. If this document contains "
|
||||
"Internet links, and those links are relative, they are relative to this base URL. "
|
||||
"This property provides a standard way for embedded relative URLs to be interpreted "
|
||||
"by tools. Web authoring tools should set the value based on their notion of where "
|
||||
"URLs will be interpreted." },
|
||||
{ "CreateDate", "CreateDate", "Date", xmpText, xmpInternal, "The date and time the resource was originally created." },
|
||||
{ "CreatorTool", "CreatorTool", "AgentName", xmpText, xmpInternal, "The name of the first known tool used to create the resource. If history is "
|
||||
"present in the metadata, this value should be equivalent to that of "
|
||||
"xmpMM:History's softwareAgent property." },
|
||||
{ "Identifier", "Identifier", "bag Text", xmpText, xmpExternal, "An unordered array of text strings that unambiguously identify the resource within "
|
||||
"a given context. An array item may be qualified with xmpidq:Scheme to denote the "
|
||||
"formal identification system to which that identifier conforms. Note: The "
|
||||
"dc:identifier property is not used because it lacks a defined scheme qualifier and "
|
||||
"has been defined in the XMP Specification as a simple (single-valued) property." },
|
||||
{ "Label", "Label", "Text", xmpText, xmpExternal, "A word or short phrase that identifies a document as a member of a user-defined "
|
||||
"collection. Used to organize documents in a file browser." },
|
||||
{ "MetadataDate", "MetadataDate", "Date", xmpText, xmpInternal, "The date and time that any metadata for this resource was last changed. It should "
|
||||
"be the same as or more recent than xmp:ModifyDate." },
|
||||
{ "ModifyDate", "ModifyDate", "Date", xmpText, xmpInternal, "The date and time the resource was last modified. Note: The value of this property "
|
||||
"is not necessarily the same as the file's system modification date because it is "
|
||||
"set before the file is saved." },
|
||||
{ "Nickname", "Nickname", "Text", xmpText, xmpExternal, "A short informal name for the resource." },
|
||||
{ "Rating", "Rating", "Closed Choice of Integer", signedLong, xmpExternal, "A number that indicates a document's status relative to other documents, "
|
||||
"used to organize documents in a file browser. Values are user-defined within an "
|
||||
"application-defined range." },
|
||||
{ "Thumbnails", "Thumbnails", "alt Thumbnail", undefined, xmpInternal, "An alternative array of thumbnail images for a file, which can differ in "
|
||||
"characteristics such as size or image encoding." },
|
||||
// End of list marker
|
||||
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
|
||||
};
|
||||
|
||||
extern const XmpPropertyInfo xmpXmpRightsInfo[] = {
|
||||
{ "Certificate", "Certificate", "URL", xmpText, xmpExternal, "Online rights management certificate." },
|
||||
{ "Marked", "Marked", "Boolean", xmpText, xmpExternal, "Indicates that this is a rights-managed resource." },
|
||||
{ "Owner", "Owner", "bag ProperName", xmpText, xmpExternal, "An unordered array specifying the legal owner(s) of a resource." },
|
||||
{ "UsageTerms", "UsageTerms", "Lang Alt", xmpText, xmpExternal, "Text instructions on how a resource can be legally used." },
|
||||
{ "WebStatement", "WebStatement", "URL", xmpText, xmpExternal, "The location of a web page describing the owner and/or rights statement for this resource." },
|
||||
// End of list marker
|
||||
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
|
||||
};
|
||||
|
||||
extern const XmpPropertyInfo xmpXmpMMInfo[] = {
|
||||
{ "DerivedFrom", "DerivedFrom", "ResourceRef", xmpText, xmpInternal, "A reference to the original document from which this one is derived. It is a "
|
||||
"minimal reference; missing components can be assumed to be unchanged. For example, "
|
||||
"a new version might only need to specify the instance ID and version number of the "
|
||||
"previous version, or a rendition might only need to specify the instance ID and "
|
||||
"rendition class of the original." },
|
||||
{ "DocumentID", "DocumentID", "URI", xmpText, xmpInternal, "The common identifier for all versions and renditions of a document. It should be "
|
||||
"based on a UUID; see Document and Instance IDs below." },
|
||||
{ "History", "History", "seq ResourceEvent", xmpText, xmpInternal, "An ordered array of high-level user actions that resulted in this resource. It is "
|
||||
"intended to give human readers a general indication of the steps taken to make the "
|
||||
"changes from the previous version to this one. The list should be at an abstract "
|
||||
"level; it is not intended to be an exhaustive keystroke or other detailed history." },
|
||||
{ "InstanceID", "InstanceID", "URI", xmpText, xmpInternal, "An identifier for a specific incarnation of a document, updated each time a file "
|
||||
"is saved. It should be based on a UUID; see Document and Instance IDs below." },
|
||||
{ "ManagedFrom", "ManagedFrom", "ResourceRef", xmpText, xmpInternal, "A reference to the document as it was prior to becoming managed. It is set when a "
|
||||
"managed document is introduced to an asset management system that does not "
|
||||
"currently own it. It may or may not include references to different management systems." },
|
||||
{ "Manager", "Manager", "AgentName", xmpText, xmpInternal, "The name of the asset management system that manages this resource. Along with "
|
||||
"xmpMM: ManagerVariant, it tells applications which asset management system to "
|
||||
"contact concerning this document." },
|
||||
{ "ManageTo", "ManageTo", "URI", xmpText, xmpInternal, "A URI identifying the managed resource to the asset management system; the presence "
|
||||
"of this property is the formal indication that this resource is managed. The form "
|
||||
"and content of this URI is private to the asset management system." },
|
||||
{ "ManageUI", "ManageUI", "URI", xmpText, xmpInternal, "A URI that can be used to access information about the managed resource through a "
|
||||
"web browser. It might require a custom browser plug- in." },
|
||||
{ "ManagerVariant", "ManagerVariant", "Text", xmpText, xmpInternal, "Specifies a particular variant of the asset management system. The format of this "
|
||||
"property is private to the specific asset management system." },
|
||||
{ "RenditionClass", "RenditionClass", "RenditionClass", xmpText, xmpInternal, "The rendition class name for this resource. This property should be absent or set "
|
||||
"to default for a document version that is not a derived rendition." },
|
||||
{ "RenditionParams", "RenditionParams", "Text", xmpText, xmpInternal, "Can be used to provide additional rendition parameters that are too complex or "
|
||||
"verbose to encode in xmpMM: RenditionClass." },
|
||||
{ "VersionID", "VersionID", "Text", xmpText, xmpInternal, "The document version identifier for this resource. Each version of a document gets "
|
||||
"a new identifier, usually simply by incrementing integers 1, 2, 3 . . . and so on. "
|
||||
"Media management systems can have other conventions or support branching which "
|
||||
"requires a more complex scheme." },
|
||||
{ "Versions", "Versions", "seq Version", xmpText, xmpInternal, "The version history associated with this resource. Entry [1] is the oldest known "
|
||||
"version for this document, entry [last()] is the most recent version. Typically, a "
|
||||
"media management system would fill in the version information in the metadata on "
|
||||
"check-in. It is not guaranteed that a complete history versions from the first to "
|
||||
"this one will be present in the xmpMM:Versions property. Interior version information "
|
||||
"can be compressed or eliminated and the version history can be truncated at some point." },
|
||||
{ "LastURL", "LastURL", "URL", xmpText, xmpInternal, "Deprecated for privacy protection." },
|
||||
{ "RenditionOf", "RenditionOf", "ResourceRef", xmpText, xmpInternal, "Deprecated in favor of xmpMM:DerivedFrom. A reference to the document of which this is "
|
||||
"a rendition." },
|
||||
{ "SaveID", "SaveID", "Integer", signedLong, xmpInternal, "Deprecated. Previously used only to support the xmpMM:LastURL property." },
|
||||
// End of list marker
|
||||
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
|
||||
};
|
||||
|
||||
extern const XmpPropertyInfo xmpXmpBJInfo[] = {
|
||||
{ "JobRef", "JobRef", "bag Job", xmpText, xmpExternal, "References an external job management file for a job process in which the document is being used. Use of job "
|
||||
"names is under user control. Typical use would be to identify all documents that are part of a particular job or contract. "
|
||||
"There are multiple values because there can be more than one job using a particular document at any time, and it can "
|
||||
"also be useful to keep historical information about what jobs a document was part of previously." },
|
||||
// End of list marker
|
||||
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
|
||||
};
|
||||
|
||||
extern const XmpPropertyInfo xmpXmpTPgInfo[] = {
|
||||
{ "MaxPageSize", "MaxPageSize", "Dimensions", xmpText, xmpInternal, "The size of the largest page in the document (including any in contained documents)." },
|
||||
{ "NPages", "NPages", "Integer", unsignedLong, xmpInternal, "The number of pages in the document (including any in contained documents)." },
|
||||
{ "Fonts", "Fonts", "bag Font", xmpText, xmpInternal, "An unordered array of fonts that are used in the document (including any in contained documents)." },
|
||||
{ "Colorants", "Colorants", "seq Colorant", xmpText, xmpInternal, "An ordered array of colorants (swatches) that are used in the document (including any in contained documents)." },
|
||||
{ "PlateNames", "PlateNames", "seq Text", xmpText, xmpInternal, "An ordered array of plate names that are needed to print the document (including any in contained documents)." },
|
||||
// End of list marker
|
||||
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
|
||||
};
|
||||
|
||||
extern const XmpPropertyInfo xmpXmpDMInfo[] = {
|
||||
{ "projectRef", "projectRef", "ProjectLink", xmpText, xmpInternal, "A reference to the project that created this file." },
|
||||
{ "videoFrameRate", "videoFrameRate", "open Choice of Text", xmpText, xmpInternal, "The video frame rate. One of: 24, NTSC, PAL." },
|
||||
{ "videoFrameSize", "videoFrameSize", "Dimensions", xmpText, xmpInternal, "The frame size. For example: w:720, h: 480, unit:pixels" },
|
||||
{ "videoPixelAspectRatio", "videoPixelAspectRatio", "Rational", unsignedRational, xmpInternal, "The aspect ratio, expressed as ht/wd. For example: \"648/720\" = 0.9" },
|
||||
{ "videoPixelDepth", "videoPixelDepth", "closed Choice of Text", xmpText, xmpInternal, "The size in bits of each color component of a pixel. Standard Windows 32-bit "
|
||||
"pixels have 8 bits per component. One of: 8Int, 16Int, 32Int, 32Float." },
|
||||
{ "videoColorSpace", "videoColorSpace", "closed Choice of Text", xmpText, xmpInternal, "The color space. One of: sRGB (used by Photoshop), CCIR-601 (used for NTSC), "
|
||||
"CCIR-709 (used for HD)." },
|
||||
{ "videoAlphaMode", "videoAlphaMode", "closed Choice of Text", xmpText, xmpExternal, "The alpha mode. One of: straight, pre-multiplied." },
|
||||
{ "videoAlphaPremultipleColor", "videoAlphaPremultipleColor", "Colorant", xmpText, xmpExternal, "A color in CMYK or RGB to be used as the pre-multiple color when "
|
||||
"alpha mode is pre-multiplied." },
|
||||
{ "videoAlphaUnityIsTransparent", "videoAlphaUnityIsTransparent", "Boolean", xmpText, xmpInternal, "When true, unity is clear, when false, it is opaque." },
|
||||
{ "videoCompressor", "videoCompressor", "Text", xmpText, xmpInternal, "Video compression used. For example, jpeg." },
|
||||
{ "videoFieldOrder", "videoFieldOrder", "closed Choice of Text", xmpText, xmpInternal, "The field order for video. One of: Upper, Lower, Progressive." },
|
||||
{ "pullDown", "pullDown", "closed Choice of Text", xmpText, xmpInternal, "The sampling phase of film to be converted to video (pull-down). One of: "
|
||||
"WSSWW, SSWWW, SWWWS, WWWSS, WWSSW, WSSWW_24p, SSWWW_24p, SWWWS_24p, WWWSS_24p, WWSSW_24p." },
|
||||
{ "audioSampleRate", "audioSampleRate", "Integer", unsignedLong, xmpInternal, "The audio sample rate. Can be any value, but commonly 32000, 41100, or 48000." },
|
||||
{ "audioSampleType", "audioSampleType", "closed Choice of Text", xmpText, xmpInternal, "The audio sample type. One of: 8Int, 16Int, 32Int, 32Float." },
|
||||
{ "audioChannelType", "audioChannelType", "closed Choice of Text", xmpText, xmpInternal, "The audio channel type. One of: Mono, Stereo, 5.1, 7.1." },
|
||||
{ "audioCompressor", "audioCompressor", "Text", xmpText, xmpInternal, "The audio compression used. For example, MP3." },
|
||||
{ "speakerPlacement", "speakerPlacement", "Text", xmpText, xmpExternal, "A description of the speaker angles from center front in degrees. For example: "
|
||||
"\"Left = -30, Right = 30, Center = 0, LFE = 45, Left Surround = -110, Right Surround = 110\"" },
|
||||
{ "fileDataRate", "fileDataRate", "Rational", unsignedRational, xmpInternal, "The file data rate in megabytes per second. For example: \"36/10\" = 3.6 MB/sec" },
|
||||
{ "tapeName", "tapeName", "Text", xmpText, xmpExternal, "The name of the tape from which the clip was captured, as set during the capture process." },
|
||||
{ "altTapeName", "altTapeName", "Text", xmpText, xmpExternal, "An alternative tape name, set via the project window or timecode dialog in Premiere. "
|
||||
"If an alternative name has been set and has not been reverted, that name is displayed." },
|
||||
{ "startTimecode", "startTimecode", "Timecode", xmpText, xmpInternal, "The timecode of the first frame of video in the file, as obtained from the device control." },
|
||||
{ "altTimecode", "altTimecode", "Timecode", xmpText, xmpExternal, "A timecode set by the user. When specified, it is used instead of the startTimecode." },
|
||||
{ "duration", "duration", "Time", xmpText, xmpInternal, "The duration of the media file." },
|
||||
{ "scene", "scene", "Text", xmpText, xmpExternal, "The name of the scene." },
|
||||
{ "shotName", "shotName", "Text", xmpText, xmpExternal, "The name of the shot or take." },
|
||||
{ "shotDate", "shotDate", "Date", xmpText, xmpExternal, "The date and time when the video was shot." },
|
||||
{ "shotLocation", "shotLocation", "Text", xmpText, xmpExternal, "The name of the location where the video was shot. For example: \"Oktoberfest, Munich Germany\" "
|
||||
"For more accurate positioning, use the EXIF GPS values." },
|
||||
{ "logComment", "logComment", "Text", xmpText, xmpExternal, "User's log comments." },
|
||||
{ "markers", "markers", "seq Marker", xmpText, xmpInternal, "An ordered list of markers" },
|
||||
{ "contributedMedia", "contributedMedia", "bag Media", xmpText, xmpInternal, "An unordered list of all media used to create this media." },
|
||||
{ "absPeakAudioFilePath", "absPeakAudioFilePath", "URI", xmpText, xmpInternal, "The absolute path to the file's peak audio file. If empty, no peak file exists." },
|
||||
{ "relativePeakAudioFilePath", "relativePeakAudioFilePath", "URI", xmpText, xmpInternal, "The relative path to the file's peak audio file. If empty, no peak file exists." },
|
||||
{ "videoModDate", "videoModDate", "Date", xmpText, xmpInternal, "The date and time when the video was last modified." },
|
||||
{ "audioModDate", "audioModDate", "Date", xmpText, xmpInternal, "The date and time when the audio was last modified." },
|
||||
{ "metadataModDate", "metadataModDate", "Date", xmpText, xmpInternal, "The date and time when the metadata was last modified." },
|
||||
{ "artist", "artist", "Text", xmpText, xmpExternal, "The name of the artist or artists." },
|
||||
{ "album", "album", "Text", xmpText, xmpExternal, "The name of the album." },
|
||||
{ "trackNumber", "trackNumber", "Integer", unsignedShort, xmpExternal, "A numeric value indicating the order of the audio file within its original recording." },
|
||||
{ "genre", "genre", "Text", xmpText, xmpExternal, "The name of the genre." },
|
||||
{ "copyright", "copyright", "Text", xmpText, xmpExternal, "The copyright information." },
|
||||
{ "releaseDate", "releaseDate", "Date", xmpText, xmpExternal, "The date the title was released." },
|
||||
{ "composer", "composer", "Text", xmpText, xmpExternal, "The composer's name." },
|
||||
{ "engineer", "engineer", "Text", xmpText, xmpExternal, "The engineer's name." },
|
||||
{ "tempo", "tempo", "Real", xmpText, xmpInternal, "The audio's tempo." },
|
||||
{ "instrument", "instrument", "Text", xmpText, xmpExternal, "The musical instrument." },
|
||||
{ "introTime", "introTime", "Time", xmpText, xmpInternal, "The duration of lead time for queuing music." },
|
||||
{ "outCue", "outCue", "Time", xmpText, xmpInternal, "The time at which to fade out." },
|
||||
{ "relativeTimestamp", "relativeTimestamp", "Time", xmpText, xmpInternal, "The start time of the media inside the audio project." },
|
||||
{ "loop", "loop", "Boolean", xmpText, xmpInternal, "When true, the clip can be looped seemlessly." },
|
||||
{ "numberOfBeats", "numberOfBeats", "Real", xmpText, xmpInternal, "The number of beats." },
|
||||
{ "key", "key", "closed Choice of Text", xmpText, xmpInternal, "The audio's musical key. One of: C, C#, D, D#, E, F, F#, G, G#, A, A#, B." },
|
||||
{ "stretchMode", "stretchMode", "closed Choice of Text", xmpText, xmpInternal, "The audio stretch mode. One of: Fixed length, Time-Scale, Resample, Beat Splice, Hybrid." },
|
||||
{ "timeScaleParams", "timeScaleParams", "timeScaleStretch", xmpText, xmpInternal, "Additional parameters for Time-Scale stretch mode." },
|
||||
{ "resampleParams", "resampleParams", "resampleStretch", xmpText, xmpInternal, "Additional parameters for Resample stretch mode." },
|
||||
{ "beatSpliceParams", "beatSpliceParams", "beatSpliceStretch", xmpText, xmpInternal, "Additional parameters for Beat Splice stretch mode." },
|
||||
{ "timeSignature", "timeSignature", "closed Choice of Text", xmpText, xmpInternal, "The time signature of the music. One of: 2/4, 3/4, 4/4, 5/4, 7/4, 6/8, 9/8, 12/8, other." },
|
||||
{ "scaleType", "scaleType", "closed Choice of Text", xmpText, xmpInternal, "The musical scale used in the music. One of: Major, Minor, Both, Neither. "
|
||||
"Neither is most often used for instruments with no associated scale, such as drums." },
|
||||
// End of list marker
|
||||
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
|
||||
};
|
||||
|
||||
extern const XmpPropertyInfo xmpPdfInfo[] = {
|
||||
{ "Keywords", "Keywords", "Text", xmpText, xmpExternal, "Keywords." },
|
||||
{ "PDFVersion", "PDFVersion", "Text", xmpText, xmpInternal, "The PDF file version (for example: 1.0, 1.3, and so on)." },
|
||||
{ "Producer", "Producer", "AgentName", xmpText, xmpInternal, "The name of the tool that created the PDF document." },
|
||||
// End of list marker
|
||||
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
|
||||
};
|
||||
|
||||
extern const XmpPropertyInfo xmpPhotoshopInfo[] = {
|
||||
{ "AuthorsPosition", "AuthorsPosition", "Text", xmpText, xmpExternal, "By-line title." },
|
||||
{ "CaptionWriter", "CaptionWriter", "ProperName", xmpText, xmpExternal, "Writer/editor." },
|
||||
{ "Category", "Category", "Text", xmpText, xmpExternal, "Category. Limited to 3 7-bit ASCII characters." },
|
||||
{ "City", "City", "Text", xmpText, xmpExternal, "City." },
|
||||
{ "Country", "Country", "Text", xmpText, xmpExternal, "Country/primary location." },
|
||||
{ "Credit", "Credit", "Text", xmpText, xmpExternal, "Credit." },
|
||||
{ "DateCreated", "DateCreated", "Date", xmpText, xmpExternal, "The date the intellectual content of the document was created (rather than the creation "
|
||||
"date of the physical representation), following IIM conventions. For example, a photo "
|
||||
"taken during the American Civil War would have a creation date during that epoch "
|
||||
"(1861-1865) rather than the date the photo was digitized for archiving." },
|
||||
{ "Headline", "Headline", "Text", xmpText, xmpExternal, "Headline." },
|
||||
{ "Instructions", "Instructions", "Text", xmpText, xmpExternal, "Special instructions." },
|
||||
{ "Source", "Source", "Text", xmpText, xmpExternal, "Source." },
|
||||
{ "State", "State", "Text", xmpText, xmpExternal, "Province/state." },
|
||||
{ "SupplementalCategories", "SupplementalCategories", "bag Text", xmpText, xmpExternal, "Supplemental category." },
|
||||
{ "TransmissionReference", "TransmissionReference", "Text", xmpText, xmpExternal, "Original transmission reference." },
|
||||
{ "Urgency", "Urgency", "Integer", xmpText, xmpExternal, "Urgency. Valid range is 1-8." },
|
||||
// End of list marker
|
||||
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
|
||||
};
|
||||
|
||||
extern const XmpPropertyInfo xmpTiffInfo[] = {
|
||||
{ "ImageWidth", "ImageWidth", "Integer", unsignedLong, xmpInternal, "TIFF tag 256, 0x100. Image width in pixels." },
|
||||
{ "ImageLength", "ImageLength", "Integer", unsignedLong, xmpInternal, "TIFF tag 257, 0x101. Image height in pixels." },
|
||||
{ "BitsPerSample", "BitsPerSample", "seq Integer", unsignedShort, xmpInternal, "TIFF tag 258, 0x102. Number of bits per component in each channel." },
|
||||
{ "Compression", "Compression", "Closed Choice of Integer", unsignedShort, xmpInternal, "TIFF tag 259, 0x103. Compression scheme: 1 = uncompressed; 6 = JPEG." },
|
||||
{ "PhotometricInterpretation", "PhotometricInterpretation", "Closed Choice of Integer", unsignedShort, xmpInternal, "TIFF tag 262, 0x106. Pixel Composition: 2 = RGB; 6 = YCbCr." },
|
||||
{ "Orientation", "Orientation", "Closed Choice of Integer", unsignedShort, xmpInternal, "TIFF tag 274, 0x112. Orientation:"
|
||||
"1 = 0th row at top, 0th column at left "
|
||||
"2 = 0th row at top, 0th column at right "
|
||||
"3 = 0th row at bottom, 0th column at right "
|
||||
"4 = 0th row at bottom, 0th column at left "
|
||||
"5 = 0th row at left, 0th column at top "
|
||||
"6 = 0th row at right, 0th column at top "
|
||||
"7 = 0th row at right, 0th column at bottom "
|
||||
"8 = 0th row at left, 0th column at bottom" },
|
||||
{ "SamplesPerPixel", "SamplesPerPixel", "Integer", unsignedShort, xmpInternal, "TIFF tag 277, 0x115. Number of components per pixel." },
|
||||
{ "PlanarConfiguration", "PlanarConfiguration", "Closed Choice of Integer", unsignedShort, xmpInternal, "TIFF tag 284, 0x11C. Data layout:1 = chunky; 2 = planar." },
|
||||
{ "YCbCrSubSampling", "YCbCrSubSampling", "Closed Choice of seq Integer", unsignedShort, xmpInternal, "TIFF tag 530, 0x212. Sampling ratio of chrominance "
|
||||
"components: [2, 1] = YCbCr4:2:2; [2, 2] = YCbCr4:2:0" },
|
||||
{ "YCbCrPositioning", "YCbCrPositioning", "Closed Choice of Integer", unsignedShort, xmpInternal, "TIFF tag 531, 0x213. Position of chrominance vs. "
|
||||
"luminance components: 1 = centered; 2 = co-sited." },
|
||||
{ "XResolution", "XResolution", "Rational", unsignedRational, xmpInternal, "TIFF tag 282, 0x11A. Horizontal resolution in pixels per unit." },
|
||||
{ "YResolution", "YResolution", "Rational", unsignedRational, xmpInternal, "TIFF tag 283, 0x11B. Vertical resolution in pixels per unit." },
|
||||
{ "ResolutionUnit", "ResolutionUnit", "Closed Choice of Integer", unsignedShort, xmpInternal, "TIFF tag 296, 0x128. Unit used for XResolution and "
|
||||
"YResolution. Value is one of: 2 = inches; 3 = centimeters." },
|
||||
{ "TransferFunction", "TransferFunction", "seq Integer", unsignedShort, xmpInternal, "TIFF tag 301, 0x12D. Transfer function for image "
|
||||
"described in tabular style with 3 * 256 entries." },
|
||||
{ "WhitePoint", "WhitePoint", "seq Rational", unsignedRational, xmpInternal, "TIFF tag 318, 0x13E. Chromaticity of white point." },
|
||||
{ "PrimaryChromaticities", "PrimaryChromaticities", "seq Rational", unsignedRational, xmpInternal, "TIFF tag 319, 0x13F. Chromaticity of the three primary colors." },
|
||||
{ "YCbCrCoefficients", "YCbCrCoefficients", "seq Rational", unsignedRational, xmpInternal, "TIFF tag 529, 0x211. Matrix coefficients for RGB to YCbCr transformation." },
|
||||
{ "ReferenceBlackWhite", "ReferenceBlackWhite", "seq Rational", unsignedRational, xmpInternal, "TIFF tag 532, 0x214. Reference black and white point values." },
|
||||
{ "DateTime", "DateTime", "Date", date, xmpInternal, "TIFF tag 306, 0x132 (primary) and EXIF tag 37520, "
|
||||
"0x9290 (subseconds). Date and time of image creation "
|
||||
"(no time zone in EXIF), stored in ISO 8601 format, not "
|
||||
"the original EXIF format. This property includes the "
|
||||
"value for the EXIF SubSecTime attribute. "
|
||||
"NOTE: This property is stored in XMP as xmp:ModifyDate." },
|
||||
{ "ImageDescription", "ImageDescription", "Lang Alt", xmpText, xmpExternal, "TIFF tag 270, 0x10E. Description of the image. Note: This property is stored in XMP as dc:description." },
|
||||
{ "Make", "Make", "ProperName", xmpText, xmpInternal, "TIFF tag 271, 0x10F. Manufacturer of recording equipment." },
|
||||
{ "Model", "Model", "ProperName", xmpText, xmpInternal, "TIFF tag 272, 0x110. Model name or number of equipment." },
|
||||
{ "Software", "Software", "AgentName", xmpText, xmpInternal, "TIFF tag 305, 0x131. Software or firmware used to generate image. "
|
||||
"Note: This property is stored in XMP as xmp:CreatorTool. " },
|
||||
{ "Artist", "Artist", "ProperName", xmpText, xmpExternal, "TIFF tag 315, 0x13B. Camera owner, photographer or image creator. "
|
||||
"Note: This property is stored in XMP as the first item in the dc:creator array." },
|
||||
{ "Copyright", "Copyright", "Lang Alt", xmpText, xmpExternal, "TIFF tag 33432, 0x8298. Copyright information. "
|
||||
"Note: This property is stored in XMP as dc:rights." },
|
||||
// End of list marker
|
||||
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
|
||||
};
|
||||
|
||||
//! exif:ColorSpace
|
||||
extern const TagDetails xmpExifColorSpace[] = {
|
||||
{ 1, N_("sRGB") },
|
||||
{ 65535, N_("uncalibrated") }
|
||||
};
|
||||
|
||||
//! exif:ComponentsConfiguration
|
||||
extern const TagDetails xmpExifComponentsConfiguration[] = {
|
||||
{ 0, "does not exist" },
|
||||
{ 1, "Y" },
|
||||
{ 2, "Cb" },
|
||||
{ 3, "Cr" },
|
||||
{ 4, "R" },
|
||||
{ 5, "G" },
|
||||
{ 6, "B" }
|
||||
};
|
||||
|
||||
//! exif:ExposureProgram
|
||||
extern const TagDetails xmpExifExposureProgram[] = {
|
||||
{ 0, N_("not defined") },
|
||||
{ 1, N_("Manual") },
|
||||
{ 2, N_("Normal program") },
|
||||
{ 3, N_("Aperture priority") },
|
||||
{ 4, N_("Shutter priority") },
|
||||
{ 5, N_("Creative program") },
|
||||
{ 6, N_("Action program") },
|
||||
{ 7, N_("Portrait mode") },
|
||||
{ 8, N_("Landscape mode") }
|
||||
};
|
||||
|
||||
//! exif:MeteringMode
|
||||
extern const TagDetails xmpExifMeteringMode[] = {
|
||||
{ 0, N_("unknown") },
|
||||
{ 1, N_("Average") },
|
||||
{ 2, N_("CenterWeightedAverage") },
|
||||
{ 3, N_("Spot") },
|
||||
{ 4, N_("MultiSpot") },
|
||||
{ 5, N_("Pattern") },
|
||||
{ 6, N_("Partial") },
|
||||
{ 255, N_("other") }
|
||||
};
|
||||
|
||||
//! exif:LightSource
|
||||
extern const TagDetails xmpExifLightSource[] = {
|
||||
{ 0, N_("unknown") },
|
||||
{ 1, N_("Daylight") },
|
||||
{ 2, N_("Fluorescent") },
|
||||
{ 3, N_("Tungsten") },
|
||||
{ 4, N_("Flash") },
|
||||
{ 9, N_("Fine weather") },
|
||||
{ 10, N_("Cloudy weather") },
|
||||
{ 11, N_("Shade") },
|
||||
{ 12, N_("Daylight fluorescent (D 5700 - 7100K)") },
|
||||
{ 13, N_("Day white fluorescent (N 4600 - 5400K)") },
|
||||
{ 14, N_("Cool white fluorescent (W 3900 - 4500K)") },
|
||||
{ 15, N_("White fluorescent (WW 3200 - 3700K)") },
|
||||
{ 17, N_("Standard light A") },
|
||||
{ 18, N_("Standard light B") },
|
||||
{ 19, N_("Standard light C") },
|
||||
{ 20, N_("D55") },
|
||||
{ 21, N_("D65") },
|
||||
{ 22, N_("D75") },
|
||||
{ 23, N_("D50") },
|
||||
{ 24, N_("ISO studio tungsten") },
|
||||
{ 255, N_("other") }
|
||||
};
|
||||
|
||||
//! exif:FocalPlaneResolutionUnit
|
||||
extern const TagDetails xmpExifFocalPlaneResolutionUnit[] = {
|
||||
{ 2, N_("inches") },
|
||||
{ 3, N_("centimeters") }
|
||||
};
|
||||
|
||||
//! exif:SensingMethod
|
||||
extern const TagDetails xmpExifSensingMethod[] = {
|
||||
{ 1, N_("Not defined") },
|
||||
{ 2, N_("One-chip color area sensor") },
|
||||
{ 3, N_("Two-chip color area sensor") },
|
||||
{ 4, N_("Three-chip color area sensor") },
|
||||
{ 5, N_("Color sequential area sensor") },
|
||||
{ 7, N_("Trilinear sensor") },
|
||||
{ 8, N_("Color sequential linear sensor") }
|
||||
};
|
||||
|
||||
//! exif:FileSource
|
||||
extern const TagDetails xmpExifFileSource[] = {
|
||||
{ 3, N_("DSC") }
|
||||
};
|
||||
|
||||
//! exif:SceneType
|
||||
extern const TagDetails xmpExifSceneType[] = {
|
||||
{ 1, N_("directly photographed image") }
|
||||
};
|
||||
|
||||
//! exif:CustomRendered
|
||||
extern const TagDetails xmpExifCustomRendered[] = {
|
||||
{ 0, N_("Normal process") },
|
||||
{ 1, N_("Custom process") }
|
||||
};
|
||||
|
||||
//! exif:ExposureMode
|
||||
extern const TagDetails xmpExifExposureMode[] = {
|
||||
{ 0, N_("Auto exposure") },
|
||||
{ 1, N_("Manual exposure") },
|
||||
{ 2, N_("Auto bracket") }
|
||||
};
|
||||
|
||||
//! exif:WhiteBalance
|
||||
extern const TagDetails xmpExifWhiteBalance[] = {
|
||||
{ 0, N_("Auto white balance") },
|
||||
{ 1, N_("Manual white balance") }
|
||||
};
|
||||
|
||||
//! exif:SceneCaptureType
|
||||
extern const TagDetails xmpExifSceneCaptureType[] = {
|
||||
{ 0, N_("Standard") },
|
||||
{ 1, N_("Landscape") },
|
||||
{ 2, N_("Portrait") },
|
||||
{ 3, N_("Night scene") }
|
||||
};
|
||||
|
||||
//! exif:GainControl
|
||||
extern const TagDetails xmpExifGainControl[] = {
|
||||
{ 0, N_("None") },
|
||||
{ 1, N_("Low gain up") },
|
||||
{ 2, N_("High gain up") },
|
||||
{ 3, N_("Low gain down") },
|
||||
{ 4, N_("High gain down") }
|
||||
};
|
||||
|
||||
//! exif:Contrast, exif:Sharpness
|
||||
extern const TagDetails xmpExifNormalSoftHard[] = {
|
||||
{ 0, N_("Normal") },
|
||||
{ 1, N_("Soft") },
|
||||
{ 2, N_("Hard") }
|
||||
};
|
||||
|
||||
//! exif:Saturation
|
||||
extern const TagDetails xmpExifSaturation[] = {
|
||||
{ 0, N_("Normal") },
|
||||
{ 1, N_("Low saturation") },
|
||||
{ 2, N_("High saturation") }
|
||||
};
|
||||
|
||||
//! exif:SubjectDistanceRange
|
||||
extern const TagDetails xmpExifSubjectDistanceRange[] = {
|
||||
{ 0, N_("Unknown") },
|
||||
{ 1, N_("Macro") },
|
||||
{ 2, N_("Close view") },
|
||||
{ 3, N_("Distant view") }
|
||||
};
|
||||
|
||||
//! exif:GPSAltitudeRef
|
||||
extern const TagDetails xmpExifGPSAltitudeRef[] = {
|
||||
{ 0, N_("Above sea level") },
|
||||
{ 1, N_("Below sea level") }
|
||||
};
|
||||
|
||||
//! exif:GPSStatus
|
||||
extern const TagDetails xmpExifGPSStatus[] = {
|
||||
{ 'A', N_("measurement in progress") },
|
||||
{ 'V', N_("measurement is interoperability") }
|
||||
};
|
||||
|
||||
//! exif:GPSMeasureMode
|
||||
extern const TagDetails xmpExifGPSMeasureMode[] = {
|
||||
{ 2, N_("two-dimensional measurement") },
|
||||
{ 3, N_("three-dimensional measurement") }
|
||||
};
|
||||
|
||||
//! exif:GPSSpeedRef
|
||||
extern const TagDetails xmpExifGPSSpeedRef[] = {
|
||||
{ 'K', N_("kilometers per hour") },
|
||||
{ 'M', N_("miles per hour") },
|
||||
{ 'N', N_("knots") }
|
||||
};
|
||||
|
||||
//! exif:GPSTrackRef, exif:GPSImgDirectionRef, exif:GPSDestBearingRef
|
||||
extern const TagDetails xmpExifGPSDirection[] = {
|
||||
{ 'T', N_("true direction") },
|
||||
{ 'M', N_("magnetic direction") }
|
||||
};
|
||||
|
||||
//! exif:GPSDestDistanceRef
|
||||
extern const TagDetails xmpExifGPSDestDistanceRef[] = {
|
||||
{ 'K', N_("kilometers") },
|
||||
{ 'M', N_("miles") },
|
||||
{ 'N', N_("knots") }
|
||||
};
|
||||
|
||||
//! exif:GPSDifferential
|
||||
extern const TagDetails xmpExifGPSDifferential[] = {
|
||||
{ 0, N_("Without correction") },
|
||||
{ 1, N_("Correction applied") }
|
||||
};
|
||||
|
||||
extern const XmpPropertyInfo xmpExifInfo[] = {
|
||||
{ "ExifVersion", "ExifVersion", "Closed Choice of Text", xmpText, xmpInternal, "EXIF tag 36864, 0x9000. EXIF version number." },
|
||||
{ "FlashpixVersion", "FlashpixVersion", "Closed Choice of Text", xmpText, xmpInternal, "EXIF tag 40960, 0xA000. Version of FlashPix." },
|
||||
{ "ColorSpace", "ColorSpace", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 40961, 0xA001. Color space information" },
|
||||
{ "ComponentsConfiguration", "ComponentsConfiguration", "Closed Choice of seq Integer", unsignedShort, xmpInternal, "EXIF tag 37121, 0x9101. Configuration of components in data: 4 5 6 0 (if RGB compressed data), 1 2 3 0 (other cases)." },
|
||||
{ "CompressedBitsPerPixel", "CompressedBitsPerPixel", "Rational", unsignedRational, xmpInternal, "EXIF tag 37122, 0x9102. Compression mode used for a compressed image is indicated in unit bits per pixel." },
|
||||
{ "PixelXDimension", "PixelXDimension", "Integer", unsignedLong, xmpInternal, "EXIF tag 40962, 0xA002. Valid image width, in pixels." },
|
||||
{ "PixelYDimension", "PixelYDimension", "Integer", unsignedLong, xmpInternal, "EXIF tag 40963, 0xA003. Valid image height, in pixels." },
|
||||
{ "UserComment", "UserComment", "Lang Alt", xmpText, xmpExternal, "EXIF tag 37510, 0x9286. Comments from user." },
|
||||
{ "RelatedSoundFile", "RelatedSoundFile", "Text", xmpText, xmpInternal, "EXIF tag 40964, 0xA004. An \"8.3\" file name for the related sound file." },
|
||||
{ "DateTimeOriginal", "DateTimeOriginal", "Date", xmpText, xmpInternal, "EXIF tags 36867, 0x9003 (primary) and 37521, 0x9291 (subseconds). Date and time when original image was generated, in ISO 8601 format. Includes the EXIF SubSecTimeOriginal data." },
|
||||
{ "DateTimeDigitized", "DateTimeDigitized", "Date", xmpText, xmpInternal, "EXIF tag 36868, 0x9004 (primary) and 37522, 0x9292 (subseconds). Date and time when "
|
||||
"image was stored as digital data, can be the same as DateTimeOriginal if originally "
|
||||
"stored in digital form. Stored in ISO 8601 format. Includes the EXIF SubSecTimeDigitized data." },
|
||||
{ "ExposureTime", "ExposureTime", "Rational", unsignedRational, xmpInternal, "EXIF tag 33434, 0x829A. Exposure time in seconds." },
|
||||
{ "FNumber", "FNumber", "Rational", unsignedRational, xmpInternal, "EXIF tag 33437, 0x829D. F number." },
|
||||
{ "ExposureProgram", "ExposureProgram", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 34850, 0x8822. Class of program used for exposure." },
|
||||
{ "SpectralSensitivity", "SpectralSensitivity", "Text", xmpText, xmpInternal, "EXIF tag 34852, 0x8824. Spectral sensitivity of each channel." },
|
||||
{ "ISOSpeedRatings", "ISOSpeedRatings", "seq Integer", unsignedShort, xmpInternal, "EXIF tag 34855, 0x8827. ISO Speed and ISO Latitude of the input device as specified in ISO 12232." },
|
||||
{ "OECF", "OECF", "OECF/SFR", xmpText, xmpInternal, "EXIF tag 34856, 0x8828. Opto-Electoric Conversion Function as specified in ISO 14524." },
|
||||
{ "ShutterSpeedValue", "ShutterSpeedValue", "Rational", signedRational, xmpInternal, "EXIF tag 37377, 0x9201. Shutter speed, unit is APEX. See Annex C of the EXIF specification." },
|
||||
{ "ApertureValue", "ApertureValue", "Rational", unsignedRational, xmpInternal, "EXIF tag 37378, 0x9202. Lens aperture, unit is APEX." },
|
||||
{ "BrightnessValue", "BrightnessValue", "Rational", signedRational, xmpInternal, "EXIF tag 37379, 0x9203. Brightness, unit is APEX." },
|
||||
{ "ExposureBiasValue", "ExposureBiasValue", "Rational", signedRational, xmpInternal, "EXIF tag 37380, 0x9204. Exposure bias, unit is APEX." },
|
||||
{ "MaxApertureValue", "MaxApertureValue", "Rational", unsignedRational, xmpInternal, "EXIF tag 37381, 0x9205. Smallest F number of lens, in APEX." },
|
||||
{ "SubjectDistance", "SubjectDistance", "Rational", unsignedRational, xmpInternal, "EXIF tag 37382, 0x9206. Distance to subject, in meters." },
|
||||
{ "MeteringMode", "MeteringMode", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 37383, 0x9207. Metering mode." },
|
||||
{ "LightSource", "LightSource", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 37384, 0x9208. Light source." },
|
||||
{ "Flash", "Flash", "Flash", xmpText, xmpInternal, "EXIF tag 37385, 0x9209. Strobe light (flash) source data." },
|
||||
{ "FocalLength", "FocalLength", "Rational", unsignedRational, xmpInternal, "EXIF tag 37386, 0x920A. Focal length of the lens, in millimeters." },
|
||||
{ "SubjectArea", "SubjectArea", "seq Integer", unsignedShort, xmpInternal, "EXIF tag 37396, 0x9214. The location and area of the main subject in the overall scene." },
|
||||
{ "FlashEnergy", "FlashEnergy", "Rational", unsignedRational, xmpInternal, "EXIF tag 41483, 0xA20B. Strobe energy during image capture." },
|
||||
{ "SpatialFrequencyResponse", "SpatialFrequencyResponse", "OECF/SFR", xmpText, xmpInternal, "EXIF tag 41484, 0xA20C. Input device spatial frequency table and SFR values as specified in ISO 12233." },
|
||||
{ "FocalPlaneXResolution", "FocalPlaneXResolution", "Rational", unsignedRational, xmpInternal, "EXIF tag 41486, 0xA20E. Horizontal focal resolution, measured pixels per unit." },
|
||||
{ "FocalPlaneYResolution", "FocalPlaneYResolution", "Rational", unsignedRational, xmpInternal, "EXIF tag 41487, 0xA20F. Vertical focal resolution, measured in pixels per unit." },
|
||||
{ "FocalPlaneResolutionUnit", "FocalPlaneResolutionUnit", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41488, 0xA210. Unit used for FocalPlaneXResolution and FocalPlaneYResolution." },
|
||||
{ "SubjectLocation", "SubjectLocation", "seq Integer", unsignedShort, xmpInternal, "EXIF tag 41492, 0xA214. Location of the main subject of the scene. The first value is the "
|
||||
"horizontal pixel and the second value is the vertical pixel at which the main subject appears." },
|
||||
{ "ExposureIndex", "ExposureIndex", "Rational", unsignedRational, xmpInternal, "EXIF tag 41493, 0xA215. Exposure index of input device." },
|
||||
{ "SensingMethod", "SensingMethod", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41495, 0xA217. Image sensor type on input device." },
|
||||
{ "FileSource", "FileSource", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41728, 0xA300. Indicates image source." },
|
||||
{ "SceneType", "SceneType", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41729, 0xA301. Indicates the type of scene." },
|
||||
{ "CFAPattern", "CFAPattern", "CFAPattern", xmpText, xmpInternal, "EXIF tag 41730, 0xA302. Color filter array geometric pattern of the image sense." },
|
||||
{ "CustomRendered", "CustomRendered", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41985, 0xA401. Indicates the use of special processing on image data." },
|
||||
{ "ExposureMode", "ExposureMode", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41986, 0xA402. Indicates the exposure mode set when the image was shot." },
|
||||
{ "WhiteBalance", "WhiteBalance", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41987, 0xA403. Indicates the white balance mode set when the image was shot." },
|
||||
{ "DigitalZoomRatio", "DigitalZoomRatio", "Rational", unsignedRational, xmpInternal, "EXIF tag 41988, 0xA404. Indicates the digital zoom ratio when the image was shot." },
|
||||
{ "FocalLengthIn35mmFilm", "FocalLengthIn35mmFilm", "Integer", unsignedShort, xmpInternal, "EXIF tag 41989, 0xA405. Indicates the equivalent focal length assuming a 35mm film "
|
||||
"camera, in mm. A value of 0 means the focal length is unknown. Note that this tag differs from the FocalLength tag." },
|
||||
{ "SceneCaptureType", "SceneCaptureType", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41990, 0xA406. Indicates the type of scene that was shot." },
|
||||
{ "GainControl", "GainControl", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41991, 0xA407. Indicates the degree of overall image gain adjustment." },
|
||||
{ "Contrast", "Contrast", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41992, 0xA408. Indicates the direction of contrast processing applied by the camera." },
|
||||
{ "Saturation", "Saturation", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41993, 0xA409. Indicates the direction of saturation processing applied by the camera." },
|
||||
{ "Sharpness", "Sharpness", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41994, 0xA40A. Indicates the direction of sharpness processing applied by the camera." },
|
||||
{ "DeviceSettingDescription", "DeviceSettingDescription", "DeviceSettings", xmpText, xmpInternal, "EXIF tag 41995, 0xA40B. Indicates information on the picture-taking conditions of a particular camera model." },
|
||||
{ "SubjectDistanceRange", "SubjectDistanceRange", "Closed Choice of Integer", unsignedShort, xmpInternal, "EXIF tag 41996, 0xA40C. Indicates the distance to the subject." },
|
||||
{ "ImageUniqueID", "ImageUniqueID", "Text", xmpText, xmpInternal, "EXIF tag 42016, 0xA420. An identifier assigned uniquely to each image. It is recorded as a 32 "
|
||||
"character ASCII string, equivalent to hexadecimal notation and 128-bit fixed length." },
|
||||
{ "GPSVersionID", "GPSVersionID", "Text", xmpText, xmpInternal, "GPS tag 0, 0x00. A decimal encoding of each of the four EXIF bytes with period separators. The current value is \"2.0.0.0\"." },
|
||||
{ "GPSLatitude", "GPSLatitude", "GPSCoordinate", xmpText, xmpInternal, "GPS tag 2, 0x02 (position) and 1, 0x01 (North/South). Indicates latitude." },
|
||||
{ "GPSLongitude", "GPSLongitude", "GPSCoordinate", xmpText, xmpInternal, "GPS tag 4, 0x04 (position) and 3, 0x03 (East/West). Indicates longitude." },
|
||||
{ "GPSAltitudeRef", "GPSAltitudeRef", "Closed Choice of Integer", unsignedByte, xmpInternal, "GPS tag 5, 0x5. Indicates whether the altitude is above or below sea level." },
|
||||
{ "GPSAltitude", "GPSAltitude", "Rational", unsignedRational, xmpInternal, "GPS tag 6, 0x06. Indicates altitude in meters." },
|
||||
{ "GPSTimeStamp", "GPSTimeStamp", "Date", xmpText, xmpInternal, "GPS tag 29 (date), 0x1D, and, and GPS tag 7 (time), 0x07. Time stamp of GPS data, in Coordinated Universal Time. "
|
||||
"Note: The GPSDateStamp tag is new in EXIF 2.2. The GPS timestamp in EXIF 2.1 does not include a date. If not present, "
|
||||
"the date component for the XMP should be taken from exif:DateTimeOriginal, or if that is "
|
||||
"also lacking from exif:DateTimeDigitized. If no date is available, do not write exif:GPSTimeStamp to XMP." },
|
||||
{ "GPSSatellites", "GPSSatellites", "Text", xmpText, xmpInternal, "GPS tag 8, 0x08. Satellite information, format is unspecified." },
|
||||
{ "GPSStatus", "GPSStatus", "Closed Choice of Text", xmpText, xmpInternal, "GPS tag 9, 0x09. Status of GPS receiver at image creation time." },
|
||||
{ "GPSMeasureMode", "GPSMeasureMode", "Text", xmpText, xmpInternal, "GPS tag 10, 0x0A. GPS measurement mode, Text type." },
|
||||
{ "GPSDOP", "GPSDOP", "Rational", unsignedRational, xmpInternal, "GPS tag 11, 0x0B. Degree of precision for GPS data." },
|
||||
{ "GPSSpeedRef", "GPSSpeedRef", "Closed Choice of Text", xmpText, xmpInternal, "GPS tag 12, 0x0C. Units used to speed measurement." },
|
||||
{ "GPSSpeed", "GPSSpeed", "Rational", unsignedRational, xmpInternal, "GPS tag 13, 0x0D. Speed of GPS receiver movement." },
|
||||
{ "GPSTrackRef", "GPSTrackRef", "Closed Choice of Text", xmpText, xmpInternal, "GPS tag 14, 0x0E. Reference for movement direction." },
|
||||
{ "GPSTrack", "GPSTrack", "Rational", unsignedRational, xmpInternal, "GPS tag 15, 0x0F. Direction of GPS movement, values range from 0 to 359.99." },
|
||||
{ "GPSImgDirectionRef", "GPSImgDirectionRef", "Closed Choice of Text", xmpText, xmpInternal, "GPS tag 16, 0x10. Reference for movement direction." },
|
||||
{ "GPSImgDirection", "GPSImgDirection", "Rational", unsignedRational, xmpInternal, "GPS tag 17, 0x11. Direction of image when captured, values range from 0 to 359.99." },
|
||||
{ "GPSMapDatum", "GPSMapDatum", "Text", xmpText, xmpInternal, "GPS tag 18, 0x12. Geodetic survey data." },
|
||||
{ "GPSDestLatitude", "GPSDestLatitude", "GPSCoordinate", xmpText, xmpInternal, "GPS tag 20, 0x14 (position) and 19, 0x13 (North/South). Indicates destination latitude." },
|
||||
{ "GPSDestLongitude", "GPSDestLongitude", "GPSCoordinate", xmpText, xmpInternal, "GPS tag 22, 0x16 (position) and 21, 0x15 (East/West). Indicates destination longitude." },
|
||||
{ "GPSDestBearingRef", "GPSDestBearingRef", "Closed Choice of Text", xmpText, xmpInternal, "GPS tag 23, 0x17. Reference for movement direction." },
|
||||
{ "GPSDestBearing", "GPSDestBearing", "Rational", unsignedRational, xmpInternal, "GPS tag 24, 0x18. Destination bearing, values from 0 to 359.99." },
|
||||
{ "GPSDestDistanceRef", "GPSDestDistanceRef", "Closed Choice of Text", xmpText, xmpInternal, "GPS tag 25, 0x19. Units used for speed measurement." },
|
||||
{ "GPSDestDistance", "GPSDestDistance", "Rational", unsignedRational, xmpInternal, "GPS tag 26, 0x1A. Distance to destination." },
|
||||
{ "GPSProcessingMethod", "GPSProcessingMethod", "Text", xmpText, xmpInternal, "GPS tag 27, 0x1B. A character string recording the name of the method used for location finding." },
|
||||
{ "GPSAreaInformation", "GPSAreaInformation", "Text", xmpText, xmpInternal, "GPS tag 28, 0x1C. A character string recording the name of the GPS area." },
|
||||
{ "GPSDifferential", "GPSDifferential", "Closed Choice of Integer", unsignedShort, xmpInternal, "GPS tag 30, 0x1E. Indicates whether differential correction is applied to the GPS receiver." },
|
||||
// End of list marker
|
||||
{ 0, 0, 0, invalidTypeId, xmpInternal, 0 }
|
||||
};
|
||||
|
||||
XmpNsInfo::Ns::Ns(const std::string& ns)
|
||||
: ns_(ns)
|
||||
{
|
||||
}
|
||||
|
||||
XmpNsInfo::Prefix::Prefix(const std::string& prefix)
|
||||
: prefix_(prefix)
|
||||
{
|
||||
}
|
||||
|
||||
bool XmpNsInfo::operator==(const XmpNsInfo::Ns& ns) const
|
||||
{
|
||||
std::string n(ns_);
|
||||
return n == ns.ns_;
|
||||
}
|
||||
|
||||
bool XmpNsInfo::operator==(const XmpNsInfo::Prefix& prefix) const
|
||||
{
|
||||
std::string p(prefix_);
|
||||
return p == prefix.prefix_;
|
||||
}
|
||||
|
||||
bool XmpPropertyInfo::operator==(const std::string& name) const
|
||||
{
|
||||
std::string n(name_);
|
||||
return n == name;
|
||||
}
|
||||
|
||||
const char* XmpProperties::propertyTitle(const XmpKey& key)
|
||||
{
|
||||
return propertyInfo(key)->title_;
|
||||
}
|
||||
|
||||
const char* XmpProperties::propertyDesc(const XmpKey& key)
|
||||
{
|
||||
return propertyInfo(key)->desc_;
|
||||
}
|
||||
|
||||
TypeId XmpProperties::propertyType(const XmpKey& key)
|
||||
{
|
||||
return propertyInfo(key)->typeId_;
|
||||
}
|
||||
|
||||
const XmpPropertyInfo* XmpProperties::propertyInfo(const XmpKey& key)
|
||||
{
|
||||
const XmpPropertyInfo* pl = propertyList(key.groupName());
|
||||
if (!pl) throw Error(36, key.groupName());
|
||||
const XmpPropertyInfo* pi = 0;
|
||||
for (int i = 0; pl[i].name_ != 0; ++i) {
|
||||
if (std::string(pl[i].name_) == key.tagName()) {
|
||||
pi = pl + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pi) throw Error(38, key.groupName(), key.tagName());
|
||||
return pi;
|
||||
}
|
||||
|
||||
const char* XmpProperties::ns(const std::string& prefix)
|
||||
{
|
||||
return nsInfo(prefix)->ns_;
|
||||
}
|
||||
|
||||
const char* XmpProperties::nsDesc(const std::string& prefix)
|
||||
{
|
||||
return nsInfo(prefix)->desc_;
|
||||
}
|
||||
|
||||
const XmpPropertyInfo* XmpProperties::propertyList(const std::string& prefix)
|
||||
{
|
||||
return nsInfo(prefix)->xmpPropertyInfo_;
|
||||
}
|
||||
|
||||
const XmpNsInfo* XmpProperties::nsInfo(const std::string& prefix)
|
||||
{
|
||||
const XmpNsInfo* xn = find(xmpNsInfo, XmpNsInfo::Prefix(prefix));
|
||||
if (!xn) throw Error(35, prefix);
|
||||
return xn;
|
||||
}
|
||||
|
||||
const char* XmpProperties::prefix(const std::string& ns)
|
||||
{
|
||||
const XmpNsInfo* xn = find(xmpNsInfo, XmpNsInfo::Ns(ns));
|
||||
return xn ? xn->prefix_ : 0;
|
||||
}
|
||||
|
||||
void XmpProperties::printProperties(std::ostream& os, const std::string& prefix)
|
||||
{
|
||||
const XmpPropertyInfo* pl = propertyList(prefix);
|
||||
if (pl) {
|
||||
const int ck = sizeof(pl) / sizeof(pl[0]);
|
||||
for (int k = 0; k < ck; ++k) {
|
||||
os << pl[k];
|
||||
}
|
||||
}
|
||||
|
||||
} // XmpProperties::printProperties
|
||||
|
||||
//! @cond IGNORE
|
||||
|
||||
//! Internal Pimpl structure with private members and data of class XmpKey.
|
||||
struct XmpKey::Impl {
|
||||
Impl(); //!< Default constructor
|
||||
Impl(const std::string& prefix, const std::string& property); //!< Constructor
|
||||
|
||||
/*!
|
||||
@brief Parse and convert the \em key string into property and prefix.
|
||||
Updates data members if the string can be decomposed, or throws
|
||||
\em Error.
|
||||
|
||||
@throw Error if the key cannot be decomposed.
|
||||
*/
|
||||
void decomposeKey(const XmpKey* self, const std::string& key);
|
||||
|
||||
// DATA
|
||||
static const char* familyName_; //!< "Xmp"
|
||||
|
||||
std::string prefix_; //!< Prefix
|
||||
std::string property_; //!< Property name
|
||||
};
|
||||
//! @endcond
|
||||
|
||||
XmpKey::Impl::Impl()
|
||||
{
|
||||
}
|
||||
|
||||
XmpKey::Impl::Impl(const std::string& prefix, const std::string& property)
|
||||
: prefix_(prefix), property_(property)
|
||||
{
|
||||
}
|
||||
|
||||
const char* XmpKey::Impl::familyName_ = "Xmp";
|
||||
|
||||
XmpKey::XmpKey(const std::string& key)
|
||||
: p_(new Impl)
|
||||
{
|
||||
p_->decomposeKey(this, key);
|
||||
}
|
||||
|
||||
XmpKey::XmpKey(const std::string& prefix, const std::string& property)
|
||||
: p_(new Impl(prefix, property))
|
||||
{
|
||||
// Validate prefix and property, throws
|
||||
XmpProperties::propertyInfo(*this);
|
||||
}
|
||||
|
||||
XmpKey::~XmpKey()
|
||||
{
|
||||
delete p_;
|
||||
}
|
||||
|
||||
XmpKey::XmpKey(const XmpKey& rhs)
|
||||
: Key(rhs), p_(new Impl(*rhs.p_))
|
||||
{
|
||||
}
|
||||
|
||||
XmpKey& XmpKey::operator=(const XmpKey& rhs)
|
||||
{
|
||||
if (this == &rhs) return *this;
|
||||
*p_ = *rhs.p_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
XmpKey::AutoPtr XmpKey::clone() const
|
||||
{
|
||||
return AutoPtr(clone_());
|
||||
}
|
||||
|
||||
XmpKey* XmpKey::clone_() const
|
||||
{
|
||||
return new XmpKey(*this);
|
||||
}
|
||||
|
||||
std::string XmpKey::key() const
|
||||
{
|
||||
return std::string(p_->familyName_) + "." + p_->prefix_ + "." + p_->property_;
|
||||
}
|
||||
|
||||
const char* XmpKey::familyName() const
|
||||
{
|
||||
return p_->familyName_;
|
||||
}
|
||||
|
||||
std::string XmpKey::groupName() const
|
||||
{
|
||||
return p_->prefix_;
|
||||
}
|
||||
|
||||
std::string XmpKey::tagName() const
|
||||
{
|
||||
return p_->property_;
|
||||
}
|
||||
|
||||
std::string XmpKey::tagLabel() const
|
||||
{
|
||||
return XmpProperties::propertyTitle(*this);
|
||||
}
|
||||
|
||||
const char* XmpKey::ns() const
|
||||
{
|
||||
return XmpProperties::ns(p_->prefix_);
|
||||
}
|
||||
|
||||
void XmpKey::Impl::decomposeKey(const XmpKey* self, const std::string& key)
|
||||
{
|
||||
// Get the family name, prefix and property name parts of the key
|
||||
std::string::size_type pos1 = key.find('.');
|
||||
if (pos1 == std::string::npos) throw Error(6, key);
|
||||
std::string familyName = key.substr(0, pos1);
|
||||
if (familyName != std::string(familyName_)) {
|
||||
throw Error(6, key);
|
||||
}
|
||||
std::string::size_type pos0 = pos1 + 1;
|
||||
pos1 = key.find('.', pos0);
|
||||
if (pos1 == std::string::npos) throw Error(6, key);
|
||||
std::string prefix = key.substr(pos0, pos1 - pos0);
|
||||
if (prefix == "") throw Error(6, key);
|
||||
std::string property = key.substr(pos1 + 1);
|
||||
if (property == "") throw Error(6, key);
|
||||
|
||||
property_ = property;
|
||||
prefix_ = prefix;
|
||||
|
||||
// Validate prefix and property
|
||||
XmpProperties::propertyInfo(*self);
|
||||
|
||||
} // XmpKey::Impl::decomposeKey
|
||||
|
||||
// *************************************************************************
|
||||
// free functions
|
||||
std::ostream& operator<<(std::ostream& os, const XmpPropertyInfo& property)
|
||||
{
|
||||
return os << property.name_ << ",\t"
|
||||
<< property.title_ << ",\t"
|
||||
<< property.xmpValueType_ << ",\t"
|
||||
<< TypeInfo::typeName(property.typeId_) << ",\t"
|
||||
<< ( property.xmpCategory_ == xmpExternal ? "External" : "Internal" ) << ",\t"
|
||||
<< property.desc_ << "\n";
|
||||
}
|
||||
|
||||
} // namespace Exiv2
|
||||
262
src/properties.hpp
Normal file
262
src/properties.hpp
Normal file
@ -0,0 +1,262 @@
|
||||
// ***************************************************************** -*- C++ -*-
|
||||
/*
|
||||
* Copyright (C) 2007 Andreas Huggel <ahuggel@gmx.net>
|
||||
*
|
||||
* This program is part of the Exiv2 distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
/*!
|
||||
@file properties.hpp
|
||||
@brief XMP property and type information.<BR>References:<BR>
|
||||
<a href="http://www.adobe.com/devnet/xmp/pdfs/xmp_specification.pdf">XMP Specification</a> from Adobe
|
||||
<I>(Property descriptions copied from the XMP specification with the permission of Adobe)</I>
|
||||
@version $Rev$
|
||||
@author Andreas Huggel (ahu)
|
||||
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
|
||||
@date 13-Jul-07, ahu: created
|
||||
*/
|
||||
#ifndef PROPERTIES_HPP_
|
||||
#define PROPERTIES_HPP_
|
||||
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
#include "types.hpp"
|
||||
#include "metadatum.hpp"
|
||||
|
||||
// + standard includes
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
|
||||
// *****************************************************************************
|
||||
// namespace extensions
|
||||
namespace Exiv2 {
|
||||
|
||||
// *****************************************************************************
|
||||
// class declarations
|
||||
class XmpKey;
|
||||
|
||||
// *****************************************************************************
|
||||
// class definitions
|
||||
|
||||
//! Category of an XMP property
|
||||
enum XmpCategory { xmpInternal, xmpExternal };
|
||||
|
||||
//! Information about one XMP property.
|
||||
struct XmpPropertyInfo {
|
||||
//! Comparison operator for name
|
||||
bool operator==(const std::string& name) const;
|
||||
|
||||
const char* name_; //!< Property name
|
||||
const char* title_; //!< Property title or label
|
||||
const char* xmpValueType_; //!< XMP value type (for info only)
|
||||
TypeId typeId_; //!< Exiv2 default type for the property
|
||||
XmpCategory xmpCategory_; //!< Category (internal or external)
|
||||
const char* desc_; //!< Property description
|
||||
};
|
||||
|
||||
//! Structure mapping XMP namespaces and (preferred) prefixes.
|
||||
struct XmpNsInfo {
|
||||
//! For comparison with prefix
|
||||
struct Prefix {
|
||||
//! Constructor.
|
||||
Prefix(const std::string& prefix);
|
||||
//! The prefix string.
|
||||
std::string prefix_;
|
||||
};
|
||||
//! For comparison with namespace
|
||||
struct Ns {
|
||||
//! Constructor.
|
||||
Ns(const std::string& ns);
|
||||
//! The namespace string
|
||||
std::string ns_;
|
||||
};
|
||||
//! Comparison operator for namespace
|
||||
bool operator==(const Ns& ns) const;
|
||||
//! Comparison operator for prefix
|
||||
bool operator==(const Prefix& prefix) const;
|
||||
|
||||
const char* ns_; //!< Namespace
|
||||
const char* prefix_; //!< (Preferred) prefix
|
||||
const XmpPropertyInfo* xmpPropertyInfo_; //!< List of known properties
|
||||
const char* desc_; //!< Brief description of the namespace
|
||||
};
|
||||
|
||||
//! Container for XMP property information. Implemented as a static class.
|
||||
class XmpProperties {
|
||||
//! Prevent construction: not implemented.
|
||||
XmpProperties();
|
||||
//! Prevent copy-construction: not implemented.
|
||||
XmpProperties(const XmpProperties& rhs);
|
||||
//! Prevent assignment: not implemented.
|
||||
XmpProperties& operator=(const XmpProperties& rhs);
|
||||
|
||||
public:
|
||||
/*!
|
||||
@brief Return the title (label) of the property.
|
||||
@param key The property key
|
||||
@return The title (label) of the property
|
||||
@throw Error if the key is invalid.
|
||||
*/
|
||||
static const char* propertyTitle(const XmpKey& key);
|
||||
/*!
|
||||
@brief Return the description of the property.
|
||||
@param key The property key
|
||||
@return The description of the property
|
||||
@throw Error if the key is invalid.
|
||||
*/
|
||||
static const char* propertyDesc(const XmpKey& key);
|
||||
/*!
|
||||
@brief Return the type for property \em key
|
||||
@param key The property key
|
||||
@return The type of the property
|
||||
@throw Error if the key is invalid.
|
||||
*/
|
||||
static TypeId propertyType(const XmpKey& key);
|
||||
/*!
|
||||
@brief Return information for the property for key.
|
||||
Always returns a valid pointer.
|
||||
@param key The property key
|
||||
@return a pointer to the property information
|
||||
@throw Error if the key is invalid.
|
||||
*/
|
||||
static const XmpPropertyInfo* propertyInfo(const XmpKey& key);
|
||||
/*!
|
||||
@brief Return the namespace name for the schema associated
|
||||
with \em prefix.
|
||||
@param prefix Prefix
|
||||
@return the namespace name
|
||||
@throw Error if no namespace is registered with \em prefix.
|
||||
*/
|
||||
static const char* ns(const std::string& prefix);
|
||||
/*!
|
||||
@brief Return the namespace description for the schema associated
|
||||
with \em prefix.
|
||||
@param prefix Prefix
|
||||
@return the namespace description
|
||||
@throw Error if no namespace is registered with \em prefix.
|
||||
*/
|
||||
static const char* nsDesc(const std::string& prefix);
|
||||
/*!
|
||||
@brief Return read-only list of built-in properties for \em prefix.
|
||||
@param prefix Prefix
|
||||
@return Pointer to the built-in properties for prefix, may be 0 if
|
||||
none is configured in the namespace info.
|
||||
@throw Error if no namespace is registered with \em prefix.
|
||||
*/
|
||||
static const XmpPropertyInfo* propertyList(const std::string& prefix);
|
||||
/*!
|
||||
@brief Return information about a schema namespace for \em prefix.
|
||||
Always returns a valid pointer.
|
||||
@param prefix The prefix
|
||||
@return a pointer to the related information
|
||||
@throw Error if no namespace is registered with \em prefix.
|
||||
*/
|
||||
static const XmpNsInfo* nsInfo(const std::string& prefix);
|
||||
/*!
|
||||
@brief Return the (preferred) prefix for schema namespace \em ns
|
||||
@param ns Schema namespace
|
||||
@return the prefix or 0 if namespace \em ns is not registered.
|
||||
*/
|
||||
static const char* prefix(const std::string& ns);
|
||||
//! Print a list of properties of a schema namespace to output stream \em os.
|
||||
static void printProperties(std::ostream& os, const std::string& prefix);
|
||||
|
||||
}; // class XmpProperties
|
||||
|
||||
/*!
|
||||
@brief Concrete keys for XMP metadata.
|
||||
*/
|
||||
class XmpKey : public Key {
|
||||
public:
|
||||
//! Shortcut for an %XmpKey auto pointer.
|
||||
typedef std::auto_ptr<XmpKey> AutoPtr;
|
||||
|
||||
//! @name Creators
|
||||
//@{
|
||||
/*!
|
||||
@brief Constructor to create an XMP key from a key string.
|
||||
|
||||
@param key The key string.
|
||||
@throw Error if the first part of the key is not '<b>Xmp</b>' or
|
||||
the remaining parts of the key cannot be parsed and
|
||||
converted to a known schema prefix and property name.
|
||||
*/
|
||||
explicit XmpKey(const std::string& key);
|
||||
/*!
|
||||
@brief Constructor to create an XMP key from a schema prefix
|
||||
and a property name.
|
||||
|
||||
@param prefix Schema prefix name
|
||||
@param property Property name
|
||||
|
||||
@throw Error if the schema prefix or the property name are not
|
||||
known.
|
||||
*/
|
||||
XmpKey(const std::string& prefix, const std::string& property);
|
||||
//! Copy constructor.
|
||||
XmpKey(const XmpKey& rhs);
|
||||
//! Virtual destructor.
|
||||
virtual ~XmpKey();
|
||||
//@}
|
||||
|
||||
//! @name Manipulators
|
||||
//@{
|
||||
//! Assignment operator.
|
||||
XmpKey& operator=(const XmpKey& rhs);
|
||||
//@}
|
||||
|
||||
//! @name Accessors
|
||||
//@{
|
||||
virtual std::string key() const;
|
||||
virtual const char* familyName() const;
|
||||
/*!
|
||||
@brief Return the name of the group (the second part of the key).
|
||||
For XMP keys, the group name is the schema prefix name.
|
||||
*/
|
||||
virtual std::string groupName() const;
|
||||
virtual std::string tagName() const;
|
||||
virtual std::string tagLabel() const;
|
||||
//! Properties don't have a tag number. Return 0.
|
||||
virtual uint16_t tag() const { return 0; }
|
||||
|
||||
AutoPtr clone() const;
|
||||
|
||||
// Todo: Should this be removed? What about tagLabel then?
|
||||
//! Return the schema namespace for the prefix of the key
|
||||
const char* ns() const;
|
||||
//@}
|
||||
|
||||
private:
|
||||
//! Internal virtual copy constructor.
|
||||
virtual XmpKey* clone_() const;
|
||||
|
||||
private:
|
||||
// Pimpl idiom
|
||||
struct Impl;
|
||||
Impl* p_;
|
||||
|
||||
}; // class XmpKey
|
||||
|
||||
// *****************************************************************************
|
||||
// free functions
|
||||
|
||||
//! Output operator for property info
|
||||
std::ostream& operator<<(std::ostream& os, const XmpPropertyInfo& propertyInfo);
|
||||
|
||||
} // namespace Exiv2
|
||||
|
||||
#endif // #ifndef PROPERTIES_HPP_
|
||||
@ -1620,7 +1620,8 @@ namespace Exiv2 {
|
||||
int32_t denominator;
|
||||
char c;
|
||||
is >> nominator >> c >> denominator;
|
||||
if (is && c == '/') r = std::make_pair(nominator, denominator);
|
||||
if (c != '/') is.setstate(std::ios::failbit);
|
||||
if (is) r = std::make_pair(nominator, denominator);
|
||||
return is;
|
||||
}
|
||||
|
||||
@ -1633,9 +1634,10 @@ namespace Exiv2 {
|
||||
{
|
||||
uint32_t nominator;
|
||||
uint32_t denominator;
|
||||
char c;
|
||||
char c('\0');
|
||||
is >> nominator >> c >> denominator;
|
||||
if (is && c == '/') r = std::make_pair(nominator, denominator);
|
||||
if (c != '/') is.setstate(std::ios::failbit);
|
||||
if (is) r = std::make_pair(nominator, denominator);
|
||||
return is;
|
||||
}
|
||||
|
||||
|
||||
@ -216,8 +216,21 @@ namespace Exiv2 {
|
||||
long size = 0;
|
||||
getObjData(pData, size, 0x02bc, Group::ifd0, object);
|
||||
if (pData) {
|
||||
pImage_->xmpPacket().assign(
|
||||
std::string(reinterpret_cast<const char*>(pData), size));
|
||||
std::string& xmpPacket = pImage_->xmpPacket();
|
||||
xmpPacket.assign(reinterpret_cast<const char*>(pData), size);
|
||||
std::string::size_type idx = xmpPacket.find_first_of('<');
|
||||
if (idx != std::string::npos && idx > 0) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: Removing " << idx << " characters "
|
||||
<< "from the beginning of the XMP packet\n";
|
||||
#endif
|
||||
xmpPacket = xmpPacket.substr(idx);
|
||||
}
|
||||
if (XmpParser::decode(pImage_->xmpData(), xmpPacket)) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: Failed to decode XMP metadata.\n";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // TiffMetadataDecoder::decodeXmp
|
||||
|
||||
|
||||
@ -72,6 +72,7 @@ namespace Exiv2 {
|
||||
TypeInfoTable(time, "Time", 11),
|
||||
TypeInfoTable(comment, "Comment", 1),
|
||||
TypeInfoTable(directory, "Directory", 1),
|
||||
TypeInfoTable(xmpText, "XmpText", 1),
|
||||
// End of list marker
|
||||
TypeInfoTable(lastTypeId, "(Unknown)", 0)
|
||||
};
|
||||
|
||||
@ -90,7 +90,7 @@ namespace Exiv2 {
|
||||
enum ByteOrder { invalidByteOrder, littleEndian, bigEndian };
|
||||
|
||||
//! An identifier for each type of metadata
|
||||
enum MetadataId { mdExif=1, mdIptc=2, mdComment=4 };
|
||||
enum MetadataId { mdExif=1, mdIptc=2, mdComment=4, mdXmp=8 };
|
||||
|
||||
//! An identifier for each mode of metadata support
|
||||
enum AccessMode { amNone=0, amRead=1, amWrite=2, amReadWrite=3 };
|
||||
@ -102,6 +102,7 @@ namespace Exiv2 {
|
||||
string, date, time,
|
||||
comment,
|
||||
directory,
|
||||
xmpText,
|
||||
lastTypeId };
|
||||
|
||||
// Todo: decentralize IfdId, so that new ids can be defined elsewhere
|
||||
@ -388,6 +389,16 @@ namespace Exiv2 {
|
||||
return os.str();
|
||||
}
|
||||
|
||||
//! Utility function to convert a string to a value of type T.
|
||||
template<typename T>
|
||||
T stringTo(const std::string& s, bool& ok)
|
||||
{
|
||||
std::istringstream is(s);
|
||||
T tmp;
|
||||
ok = is >> tmp ? true : false;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Return the greatest common denominator of n and m.
|
||||
(implementation from Boost rational.hpp)
|
||||
|
||||
156
src/value.cpp
156
src/value.cpp
@ -44,6 +44,7 @@ EXIV2_RCSID("@(#) $Id$")
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <cstdlib>
|
||||
#include <ctype.h>
|
||||
|
||||
// *****************************************************************************
|
||||
// class member definitions
|
||||
@ -105,6 +106,9 @@ namespace Exiv2 {
|
||||
case comment:
|
||||
value = AutoPtr(new CommentValue);
|
||||
break;
|
||||
case xmpText:
|
||||
value = AutoPtr(new XmpTextValue);
|
||||
break;
|
||||
default:
|
||||
value = AutoPtr(new DataValue(typeId));
|
||||
break;
|
||||
@ -124,6 +128,11 @@ namespace Exiv2 {
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string Value::toString(long /*n*/) const
|
||||
{
|
||||
return toString();
|
||||
}
|
||||
|
||||
DataValue& DataValue::operator=(const DataValue& rhs)
|
||||
{
|
||||
if (this == &rhs) return *this;
|
||||
@ -144,7 +153,9 @@ namespace Exiv2 {
|
||||
std::istringstream is(buf);
|
||||
int tmp;
|
||||
value_.clear();
|
||||
while (is >> tmp) {
|
||||
while (!(is.eof())) {
|
||||
is >> tmp;
|
||||
if (is.fail()) return 1;
|
||||
value_.push_back(static_cast<byte>(tmp));
|
||||
}
|
||||
return 0;
|
||||
@ -177,6 +188,13 @@ namespace Exiv2 {
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string DataValue::toString(long n) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << static_cast<int>(value_[n]);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
StringValueBase& StringValueBase::operator=(const StringValueBase& rhs)
|
||||
{
|
||||
if (this == &rhs) return *this;
|
||||
@ -369,6 +387,115 @@ namespace Exiv2 {
|
||||
return new CommentValue(*this);
|
||||
}
|
||||
|
||||
XmpTextValue& XmpTextValue::operator=(const XmpTextValue& rhs)
|
||||
{
|
||||
if (this == &rhs) return *this;
|
||||
Value::operator=(rhs);
|
||||
value_ = rhs.value_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int XmpTextValue::read(const std::string& buf)
|
||||
{
|
||||
std::string::size_type start = 0;
|
||||
bool escaped = false;
|
||||
|
||||
for (std::string::size_type i = 0; i < buf.size(); ++i) {
|
||||
if (start == 0) {
|
||||
// skip whitespace leading to the quote
|
||||
if (isspace(buf[i])) continue;
|
||||
// first character after that must be a quote
|
||||
if (buf[i] == quoteChar[0]) {
|
||||
start = i + 1;
|
||||
continue;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
// look for the first unescaped quote
|
||||
if (buf[i] == escapeChar[0] && !escaped) {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
if (buf[i] == quoteChar[0] && !escaped) {
|
||||
value_.push_back(buf.substr(start, i - start));
|
||||
// remove escape characters
|
||||
unescapeText(value_.back());
|
||||
start = 0;
|
||||
continue;
|
||||
}
|
||||
escaped = false;
|
||||
}
|
||||
// check for premature end of string
|
||||
if (escaped || start != 0) return 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int XmpTextValue::read(const byte* buf,
|
||||
long len,
|
||||
ByteOrder /*byteOrder*/)
|
||||
{
|
||||
std::string s(reinterpret_cast<const char*>(buf), len);
|
||||
return read(s);
|
||||
}
|
||||
|
||||
long XmpTextValue::copy(byte* buf,
|
||||
ByteOrder /*byteOrder*/) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
write(os);
|
||||
std::string s = os.str();
|
||||
memcpy(buf, &s[0], s.size());
|
||||
return s.size();
|
||||
}
|
||||
|
||||
long XmpTextValue::size() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
write(os);
|
||||
return os.str().size();
|
||||
}
|
||||
|
||||
std::ostream& XmpTextValue::write(std::ostream& os) const
|
||||
{
|
||||
for (std::vector<std::string>::const_iterator i = value_.begin();
|
||||
i != value_.end(); ++i) {
|
||||
if (i != value_.begin()) os << " ";
|
||||
std::string s(*i);
|
||||
quoteText(s);
|
||||
os << s;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string XmpTextValue::toString(long n) const
|
||||
{
|
||||
return value_[n];
|
||||
}
|
||||
|
||||
long XmpTextValue::toLong(long n) const
|
||||
{
|
||||
bool ok;
|
||||
return stringTo<long>(value_[n], ok);
|
||||
}
|
||||
|
||||
float XmpTextValue::toFloat(long n) const
|
||||
{
|
||||
bool ok;
|
||||
return stringTo<float>(value_[n], ok);
|
||||
}
|
||||
|
||||
Rational XmpTextValue::toRational(long n) const
|
||||
{
|
||||
bool ok;
|
||||
return stringTo<Rational>(value_[n], ok);
|
||||
}
|
||||
|
||||
XmpTextValue* XmpTextValue::clone_() const
|
||||
{
|
||||
return new XmpTextValue(*this);
|
||||
}
|
||||
|
||||
DateValue::DateValue(int year, int month, int day)
|
||||
: Value(date)
|
||||
{
|
||||
@ -636,4 +763,31 @@ namespace Exiv2 {
|
||||
return result;
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// free functions
|
||||
|
||||
const char quoteChar[] = "\"";
|
||||
const char escapeChar[] = "\\";
|
||||
|
||||
void quoteText(std::string& text)
|
||||
{
|
||||
for (std::string::iterator i = text.begin(); i != text.end(); ++i) {
|
||||
if (*i == escapeChar[0] || *i == quoteChar[0]) {
|
||||
i = text.insert(i, escapeChar[0]);
|
||||
if (++i == text.end()) break;
|
||||
}
|
||||
}
|
||||
text = quoteChar + text + quoteChar;
|
||||
} // quoteText
|
||||
|
||||
void unescapeText(std::string& text)
|
||||
{
|
||||
for (std::string::iterator i = text.begin(); i != text.end(); ++i) {
|
||||
if (*i == escapeChar[0]) {
|
||||
i = text.erase(i); // returns next pos, i.e., skips the escaped char
|
||||
if (i == text.end()) break;
|
||||
}
|
||||
}
|
||||
} // unescapeText
|
||||
|
||||
} // namespace Exiv2
|
||||
|
||||
186
src/value.hpp
186
src/value.hpp
@ -117,11 +117,6 @@ namespace Exiv2 {
|
||||
//@{
|
||||
//! Return the type identifier (Exif data format type).
|
||||
TypeId typeId() const { return type_; }
|
||||
/*!
|
||||
@brief Return the value as a string. Implemented in terms of
|
||||
write(std::ostream& os) const of the concrete class.
|
||||
*/
|
||||
std::string toString() const;
|
||||
/*!
|
||||
@brief Return an auto-pointer to a copy of itself (deep copy).
|
||||
The caller owns this copy and the auto-pointer ensures that
|
||||
@ -151,25 +146,37 @@ namespace Exiv2 {
|
||||
*/
|
||||
virtual std::ostream& write(std::ostream& os) const =0;
|
||||
/*!
|
||||
@brief Convert the n-th component of the value to a long. The
|
||||
behaviour of this method may be undefined if there is no
|
||||
n-th component.
|
||||
@brief Return the value as a string. Implemented in terms of
|
||||
write(std::ostream& os) const of the concrete class.
|
||||
*/
|
||||
std::string toString() const;
|
||||
/*!
|
||||
@brief Return the <EM>n</EM>-th component of the value as a string.
|
||||
The default implementation returns toString(). The behaviour
|
||||
of this method may be undefined if there is no <EM>n</EM>-th
|
||||
component.
|
||||
*/
|
||||
virtual std::string toString(long n) const;
|
||||
/*!
|
||||
@brief Convert the <EM>n</EM>-th component of the value to a long.
|
||||
The behaviour of this method may be undefined if there is no
|
||||
<EM>n</EM>-th component.
|
||||
|
||||
@return The converted value.
|
||||
*/
|
||||
virtual long toLong(long n =0) const =0;
|
||||
/*!
|
||||
@brief Convert the n-th component of the value to a float. The
|
||||
behaviour of this method may be undefined if there is no
|
||||
n-th component.
|
||||
@brief Convert the <EM>n</EM>-th component of the value to a float.
|
||||
The behaviour of this method may be undefined if there is no
|
||||
<EM>n</EM>-th component.
|
||||
|
||||
@return The converted value.
|
||||
*/
|
||||
virtual float toFloat(long n =0) const =0;
|
||||
/*!
|
||||
@brief Convert the n-th component of the value to a Rational. The
|
||||
behaviour of this method may be undefined if there is no
|
||||
n-th component.
|
||||
@brief Convert the <EM>n</EM>-th component of the value to a Rational.
|
||||
The behaviour of this method may be undefined if there is no
|
||||
<EM>n</EM>-th component.
|
||||
|
||||
@return The converted value.
|
||||
*/
|
||||
@ -212,6 +219,7 @@ namespace Exiv2 {
|
||||
<TR><TD class="indexkey">date</TD><TD class="indexvalue">%DateValue</TD></TR>
|
||||
<TR><TD class="indexkey">time</TD><TD class="indexvalue">%TimeValue</TD></TR>
|
||||
<TR><TD class="indexkey">comment</TD><TD class="indexvalue">%CommentValue</TD></TR>
|
||||
<TR><TD class="indexkey">xmpText</TD><TD class="indexvalue">%XmpTextValue</TD></TR>
|
||||
<TR><TD class="indexkey"><EM>default:</EM></TD><TD class="indexvalue">%DataValue(typeId)</TD></TR>
|
||||
</TABLE>
|
||||
|
||||
@ -304,6 +312,12 @@ namespace Exiv2 {
|
||||
virtual long count() const { return size(); }
|
||||
virtual long size() const;
|
||||
virtual std::ostream& write(std::ostream& os) const;
|
||||
/*!
|
||||
@brief Return the <EM>n</EM>-th component of the value as a string.
|
||||
The behaviour of this method may be undefined if there is no
|
||||
<EM>n</EM>-th component.
|
||||
*/
|
||||
virtual std::string toString(long n) const;
|
||||
virtual long toLong(long n =0) const { return value_[n]; }
|
||||
virtual float toFloat(long n =0) const { return value_[n]; }
|
||||
virtual Rational toRational(long n =0) const
|
||||
@ -602,6 +616,103 @@ namespace Exiv2 {
|
||||
|
||||
}; // class CommentValue
|
||||
|
||||
/*!
|
||||
@brief %Value type suitable for XMP Text properties and arrays thereof.
|
||||
|
||||
Uses a vector of std::string to store the text(s).
|
||||
*/
|
||||
class XmpTextValue : public Value {
|
||||
public:
|
||||
//! Shortcut for a %XmpTextValue auto pointer.
|
||||
typedef std::auto_ptr<XmpTextValue> AutoPtr;
|
||||
|
||||
//! @name Creators
|
||||
//@{
|
||||
//! Constructor for subclasses
|
||||
XmpTextValue()
|
||||
: Value(xmpText) {}
|
||||
//! Constructor for subclasses
|
||||
XmpTextValue(const std::string& buf)
|
||||
: Value(xmpText) { read(buf); }
|
||||
//! Copy constructor
|
||||
XmpTextValue(const XmpTextValue& rhs)
|
||||
: Value(rhs), value_(rhs.value_) {}
|
||||
|
||||
//! Virtual destructor.
|
||||
virtual ~XmpTextValue() {}
|
||||
//@}
|
||||
|
||||
//! @name Manipulators
|
||||
//@{
|
||||
//! Assignment operator.
|
||||
XmpTextValue& operator=(const XmpTextValue& rhs);
|
||||
/*!
|
||||
@brief Read a text property value or array items from \em buf.
|
||||
|
||||
Expects a list of quoted strings, one for each array item. The
|
||||
quoted strings may be separated by whitespace. Double quotes in
|
||||
the strings must be escaped with a backslash character: \\".
|
||||
Examples: ""\\"foo\\", he said"" or ""foo" "bar"".
|
||||
*/
|
||||
virtual int read(const std::string& buf);
|
||||
/*!
|
||||
@brief Read the value from a character buffer.
|
||||
|
||||
Uses read(const std::string& buf)
|
||||
|
||||
@note The byte order is required by the interface but not used by this
|
||||
method, so just use the default.
|
||||
|
||||
@param buf Pointer to the data buffer to read from
|
||||
@param len Number of bytes in the data buffer
|
||||
@param byteOrder Byte order. Not needed.
|
||||
|
||||
@return 0 if successful.
|
||||
*/
|
||||
virtual int read(const byte* buf,
|
||||
long len,
|
||||
ByteOrder byteOrder =invalidByteOrder);
|
||||
//@}
|
||||
|
||||
//! @name Accessors
|
||||
//@{
|
||||
AutoPtr clone() const { return AutoPtr(clone_()); }
|
||||
/*!
|
||||
@brief Write value to a character data buffer.
|
||||
|
||||
The user must ensure that the buffer has enough memory. Otherwise
|
||||
the call results in undefined behaviour.
|
||||
|
||||
@note The byte order is required by the interface but not used by this
|
||||
method, so just use the default.
|
||||
|
||||
@param buf Data buffer to write to.
|
||||
@param byteOrder Byte order. Not used.
|
||||
@return Number of characters written.
|
||||
*/
|
||||
virtual long copy(byte* buf, ByteOrder byteOrder =invalidByteOrder) const;
|
||||
virtual long count() const { return static_cast<long>(value_.size()); }
|
||||
virtual long size() const;
|
||||
/*!
|
||||
@brief Return the <EM>n</EM>-th component of the value as a string.
|
||||
The behaviour of this method may be undefined if there is no
|
||||
<EM>n</EM>-th component.
|
||||
*/
|
||||
virtual std::string toString(long n) const;
|
||||
virtual long toLong(long n =0) const;
|
||||
virtual float toFloat(long n =0) const;
|
||||
virtual Rational toRational(long n =0) const;
|
||||
virtual std::ostream& write(std::ostream& os) const;
|
||||
//@}
|
||||
|
||||
protected:
|
||||
//! Internal virtual copy constructor.
|
||||
virtual XmpTextValue* clone_() const;
|
||||
// DATA
|
||||
std::vector<std::string> value_; //!< Stores the string values.
|
||||
|
||||
}; // class XmpTextValue
|
||||
|
||||
/*!
|
||||
@brief %Value for simple ISO 8601 dates
|
||||
|
||||
@ -685,13 +796,13 @@ namespace Exiv2 {
|
||||
virtual const Date& getDate() const { return date_; }
|
||||
virtual long count() const { return size(); }
|
||||
virtual long size() const;
|
||||
/*!
|
||||
@brief Write the value to an output stream. .
|
||||
*/
|
||||
virtual std::ostream& write(std::ostream& os) const;
|
||||
//! Return the value as a UNIX calender time converted to long.
|
||||
virtual long toLong(long n =0) const;
|
||||
//! Return the value as a UNIX calender time converted to float.
|
||||
virtual float toFloat(long n =0) const
|
||||
{ return static_cast<float>(toLong(n)); }
|
||||
//! Return the value as a UNIX calender time converted to Rational.
|
||||
virtual Rational toRational(long n =0) const
|
||||
{ return Rational(toLong(n), 1); }
|
||||
//@}
|
||||
@ -795,11 +906,13 @@ namespace Exiv2 {
|
||||
virtual const Time& getTime() const { return time_; }
|
||||
virtual long count() const { return size(); }
|
||||
virtual long size() const;
|
||||
//! Write the value to an output stream. .
|
||||
virtual std::ostream& write(std::ostream& os) const;
|
||||
//! Returns number of seconds in the day in UTC.
|
||||
virtual long toLong(long n =0) const;
|
||||
//! Returns number of seconds in the day in UTC converted to float.
|
||||
virtual float toFloat(long n =0) const
|
||||
{ return static_cast<float>(toLong(n)); }
|
||||
//! Returns number of seconds in the day in UTC converted to Rational.
|
||||
virtual Rational toRational(long n =0) const
|
||||
{ return Rational(toLong(n), 1); }
|
||||
//@}
|
||||
@ -841,6 +954,7 @@ namespace Exiv2 {
|
||||
Time time_;
|
||||
|
||||
}; // class TimeValue
|
||||
|
||||
//! Template to determine the TypeId for a type T
|
||||
template<typename T> TypeId getType();
|
||||
|
||||
@ -858,7 +972,7 @@ namespace Exiv2 {
|
||||
template<> inline TypeId getType<Rational>() { return signedRational; }
|
||||
|
||||
// No default implementation: let the compiler/linker complain
|
||||
// template<typename T> inline TypeId getType() { return invalid; }
|
||||
// template<typename T> inline TypeId getType() { return invalid; }
|
||||
|
||||
/*!
|
||||
@brief Template for a %Value of a basic type. This is used for unsigned
|
||||
@ -910,6 +1024,13 @@ namespace Exiv2 {
|
||||
virtual long count() const { return static_cast<long>(value_.size()); }
|
||||
virtual long size() const;
|
||||
virtual std::ostream& write(std::ostream& os) const;
|
||||
/*!
|
||||
@brief Return the <EM>n</EM>-th component of the value as a string.
|
||||
The behaviour of this method may be undefined if there is no
|
||||
<EM>n</EM>-th
|
||||
component.
|
||||
*/
|
||||
virtual std::string toString(long n) const;
|
||||
virtual long toLong(long n =0) const;
|
||||
virtual float toFloat(long n =0) const;
|
||||
virtual Rational toRational(long n =0) const;
|
||||
@ -963,8 +1084,21 @@ namespace Exiv2 {
|
||||
typedef ValueType<Rational> RationalValue;
|
||||
|
||||
// *****************************************************************************
|
||||
// template and inline definitions
|
||||
// free functions, template and inline definitions
|
||||
|
||||
//! Double-quote character
|
||||
extern const char quoteChar[];
|
||||
//! Escape character
|
||||
extern const char escapeChar[];
|
||||
/*!
|
||||
@brief Quote a string with double-quotes, escape quotes and escape
|
||||
characters in the string.
|
||||
*/
|
||||
void quoteText(std::string& text);
|
||||
/*!
|
||||
@brief Remove escape characters from an unquoted string.
|
||||
*/
|
||||
void unescapeText(std::string& text);
|
||||
/*!
|
||||
@brief Read a value of type T from the data buffer.
|
||||
|
||||
@ -1150,7 +1284,9 @@ namespace Exiv2 {
|
||||
std::istringstream is(buf);
|
||||
T tmp;
|
||||
value_.clear();
|
||||
while (is >> tmp) {
|
||||
while (!(is.eof())) {
|
||||
is >> tmp;
|
||||
if (is.fail()) return 1;
|
||||
value_.push_back(tmp);
|
||||
}
|
||||
return 0;
|
||||
@ -1190,6 +1326,13 @@ namespace Exiv2 {
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::string ValueType<T>::toString(long n) const
|
||||
{
|
||||
return Exiv2::toString(value_[n]);
|
||||
}
|
||||
|
||||
// Default implementation
|
||||
template<typename T>
|
||||
inline long ValueType<T>::toLong(long n) const
|
||||
@ -1264,7 +1407,6 @@ namespace Exiv2 {
|
||||
sizeDataArea_ = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Exiv2
|
||||
|
||||
#endif // #ifndef VALUE_HPP_
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
/*!
|
||||
@brief %Exiv2 MINOR version number of the library used at compile-time.
|
||||
*/
|
||||
#define EXIV2_MINOR_VERSION (15)
|
||||
#define EXIV2_MINOR_VERSION (16)
|
||||
/*!
|
||||
@brief %Exiv2 PATCH version number of the library used at compile-time.
|
||||
*/
|
||||
|
||||
558
src/xmp.cpp
Normal file
558
src/xmp.cpp
Normal file
@ -0,0 +1,558 @@
|
||||
// ***************************************************************** -*- C++ -*-
|
||||
/*
|
||||
* Copyright (C) 2007 Andreas Huggel <ahuggel@gmx.net>
|
||||
*
|
||||
* This program is part of the Exiv2 distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
/*
|
||||
File: xmp.cpp
|
||||
Version: $Rev$
|
||||
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
|
||||
History: 13-July-07, ahu: created
|
||||
*/
|
||||
// *****************************************************************************
|
||||
#include "rcsid.hpp"
|
||||
EXIV2_RCSID("@(#) $Id$")
|
||||
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
#include "xmp.hpp"
|
||||
#include "types.hpp"
|
||||
#include "error.hpp"
|
||||
#include "value.hpp"
|
||||
#include "properties.hpp"
|
||||
|
||||
// + standard includes
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
// Adobe XMP Toolkit
|
||||
#ifdef EXV_HAVE_XMP_TOOLKIT
|
||||
# define TXMP_STRING_TYPE std::string
|
||||
# include <XMP.hpp>
|
||||
# include <XMP.incl_cpp>
|
||||
#endif // EXV_HAVE_XMP_TOOLKIT
|
||||
|
||||
// *****************************************************************************
|
||||
namespace {
|
||||
//! Unary predicate that matches an Xmpdatum by key
|
||||
class FindXmpdatum {
|
||||
public:
|
||||
//! Constructor, initializes the object with key
|
||||
FindXmpdatum(const Exiv2::XmpKey& key)
|
||||
: key_(key.key()) {}
|
||||
/*!
|
||||
@brief Returns true if prefix and property of the argument
|
||||
Xmpdatum are equal to that of the object.
|
||||
*/
|
||||
bool operator()(const Exiv2::Xmpdatum& xmpdatum) const
|
||||
{ return key_ == xmpdatum.key(); }
|
||||
|
||||
private:
|
||||
std::string key_;
|
||||
|
||||
}; // class FindXmpdatum
|
||||
|
||||
//! Make an XMP key from a schema namespace and property path
|
||||
Exiv2::XmpKey::AutoPtr makeXmpKey(const std::string& schemaNs,
|
||||
const std::string& propPath);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// class member definitions
|
||||
namespace Exiv2 {
|
||||
|
||||
//! @cond IGNORE
|
||||
|
||||
//! Internal Pimpl structure with private members and data of class Xmpdatum.
|
||||
struct Xmpdatum::Impl {
|
||||
Impl(const XmpKey& key, const Value* pValue); //!< Constructor
|
||||
Impl(const Impl& rhs); //!< Copy constructor
|
||||
Impl& operator=(const Impl& rhs); //!< Assignment
|
||||
|
||||
// DATA
|
||||
XmpKey::AutoPtr key_; //!< Key
|
||||
Value::AutoPtr value_; //!< Value
|
||||
};
|
||||
//! @endcond
|
||||
|
||||
Xmpdatum::Impl::Impl(const XmpKey& key, const Value* pValue)
|
||||
: key_(key.clone())
|
||||
{
|
||||
if (pValue) value_ = pValue->clone();
|
||||
}
|
||||
|
||||
Xmpdatum::Impl::Impl(const Impl& rhs)
|
||||
{
|
||||
if (rhs.key_.get() != 0) key_ = rhs.key_->clone(); // deep copy
|
||||
if (rhs.value_.get() != 0) value_ = rhs.value_->clone(); // deep copy
|
||||
}
|
||||
|
||||
Xmpdatum::Impl::Impl& Xmpdatum::Impl::operator=(const Impl& rhs)
|
||||
{
|
||||
if (this == &rhs) return *this;
|
||||
key_.reset();
|
||||
if (rhs.key_.get() != 0) key_ = rhs.key_->clone(); // deep copy
|
||||
value_.reset();
|
||||
if (rhs.value_.get() != 0) value_ = rhs.value_->clone(); // deep copy
|
||||
return *this;
|
||||
}
|
||||
|
||||
Xmpdatum::Xmpdatum(const XmpKey& key, const Value* pValue)
|
||||
: p_(new Impl(key, pValue))
|
||||
{
|
||||
}
|
||||
|
||||
Xmpdatum::Xmpdatum(const Xmpdatum& rhs)
|
||||
: Metadatum(rhs), p_(new Impl(*rhs.p_))
|
||||
{
|
||||
}
|
||||
|
||||
Xmpdatum& Xmpdatum::operator=(const Xmpdatum& rhs)
|
||||
{
|
||||
if (this == &rhs) return *this;
|
||||
Metadatum::operator=(rhs);
|
||||
*p_ = *rhs.p_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Xmpdatum::~Xmpdatum()
|
||||
{
|
||||
delete p_;
|
||||
}
|
||||
|
||||
std::string Xmpdatum::key() const
|
||||
{
|
||||
return p_->key_.get() == 0 ? "" : p_->key_->key();
|
||||
}
|
||||
|
||||
std::string Xmpdatum::groupName() const
|
||||
{
|
||||
return p_->key_.get() == 0 ? "" : p_->key_->groupName();
|
||||
}
|
||||
|
||||
std::string Xmpdatum::tagName() const
|
||||
{
|
||||
return p_->key_.get() == 0 ? "" : p_->key_->tagName();
|
||||
}
|
||||
|
||||
std::string Xmpdatum::tagLabel() const
|
||||
{
|
||||
return p_->key_.get() == 0 ? "" : p_->key_->tagLabel();
|
||||
}
|
||||
|
||||
uint16_t Xmpdatum::tag() const
|
||||
{
|
||||
return p_->key_.get() == 0 ? 0 : p_->key_->tag();
|
||||
}
|
||||
|
||||
TypeId Xmpdatum::typeId() const
|
||||
{
|
||||
return p_->value_.get() == 0 ? invalidTypeId : p_->value_->typeId();
|
||||
}
|
||||
|
||||
const char* Xmpdatum::typeName() const
|
||||
{
|
||||
return TypeInfo::typeName(typeId());
|
||||
}
|
||||
|
||||
long Xmpdatum::count() const
|
||||
{
|
||||
return p_->value_.get() == 0 ? 0 : p_->value_->count();
|
||||
}
|
||||
|
||||
long Xmpdatum::size() const
|
||||
{
|
||||
return p_->value_.get() == 0 ? 0 : p_->value_->size();
|
||||
}
|
||||
|
||||
std::string Xmpdatum::toString() const
|
||||
{
|
||||
return p_->value_.get() == 0 ? "" : p_->value_->toString();
|
||||
}
|
||||
|
||||
std::string Xmpdatum::toString(long n) const
|
||||
{
|
||||
return p_->value_.get() == 0 ? "" : p_->value_->toString(n);
|
||||
}
|
||||
|
||||
long Xmpdatum::toLong(long n) const
|
||||
{
|
||||
return p_->value_.get() == 0 ? -1 : p_->value_->toLong(n);
|
||||
}
|
||||
|
||||
float Xmpdatum::toFloat(long n) const
|
||||
{
|
||||
return p_->value_.get() == 0 ? -1 : p_->value_->toFloat(n);
|
||||
}
|
||||
|
||||
Rational Xmpdatum::toRational(long n) const
|
||||
{
|
||||
return p_->value_.get() == 0 ? Rational(-1, 1) : p_->value_->toRational(n);
|
||||
}
|
||||
|
||||
Value::AutoPtr Xmpdatum::getValue() const
|
||||
{
|
||||
return p_->value_.get() == 0 ? Value::AutoPtr(0) : p_->value_->clone();
|
||||
}
|
||||
|
||||
const Value& Xmpdatum::value() const
|
||||
{
|
||||
if (p_->value_.get() == 0) throw Error(8);
|
||||
return *p_->value_;
|
||||
}
|
||||
|
||||
long Xmpdatum::copy(byte* /*buf*/, ByteOrder /*byteOrder*/) const
|
||||
{
|
||||
throw Error(34, "Xmpdatum::copy");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Xmpdatum& Xmpdatum::operator=(const uint16_t& value)
|
||||
{
|
||||
UShortValue::AutoPtr v(new UShortValue);
|
||||
v->value_.push_back(value);
|
||||
p_->value_ = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Xmpdatum& Xmpdatum::operator=(const std::string& value)
|
||||
{
|
||||
setValue(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Xmpdatum& Xmpdatum::operator=(const Value& value)
|
||||
{
|
||||
setValue(&value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Xmpdatum::setValue(const Value* pValue)
|
||||
{
|
||||
p_->value_.reset();
|
||||
if (pValue) p_->value_ = pValue->clone();
|
||||
}
|
||||
|
||||
void Xmpdatum::setValue(const std::string& value)
|
||||
{
|
||||
// Todo: What's the correct default? Adjust doc
|
||||
if (p_->value_.get() == 0) {
|
||||
assert(0 != p_->key_.get());
|
||||
TypeId type = XmpProperties::propertyType(*p_->key_.get());
|
||||
p_->value_ = Value::create(type);
|
||||
}
|
||||
p_->value_->read(value);
|
||||
}
|
||||
|
||||
Xmpdatum& XmpData::operator[](const std::string& key)
|
||||
{
|
||||
XmpKey xmpKey(key);
|
||||
iterator pos = findKey(xmpKey);
|
||||
if (pos == end()) {
|
||||
add(Xmpdatum(xmpKey));
|
||||
pos = findKey(xmpKey);
|
||||
}
|
||||
return *pos;
|
||||
}
|
||||
|
||||
int XmpData::add(const XmpKey& key, Value* value)
|
||||
{
|
||||
return add(Xmpdatum(key, value));
|
||||
}
|
||||
|
||||
int XmpData::add(const Xmpdatum& xmpDatum)
|
||||
{
|
||||
xmpMetadata_.push_back(xmpDatum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
XmpData::const_iterator XmpData::findKey(const XmpKey& key) const
|
||||
{
|
||||
return std::find_if(xmpMetadata_.begin(), xmpMetadata_.end(),
|
||||
FindXmpdatum(key));
|
||||
}
|
||||
|
||||
XmpData::iterator XmpData::findKey(const XmpKey& key)
|
||||
{
|
||||
return std::find_if(xmpMetadata_.begin(), xmpMetadata_.end(),
|
||||
FindXmpdatum(key));
|
||||
}
|
||||
|
||||
void XmpData::clear()
|
||||
{
|
||||
xmpMetadata_.clear();
|
||||
}
|
||||
|
||||
void XmpData::sortByKey()
|
||||
{
|
||||
std::sort(xmpMetadata_.begin(), xmpMetadata_.end(), cmpMetadataByKey);
|
||||
}
|
||||
|
||||
XmpData::const_iterator XmpData::begin() const
|
||||
{
|
||||
return xmpMetadata_.begin();
|
||||
}
|
||||
|
||||
XmpData::const_iterator XmpData::end() const
|
||||
{
|
||||
return xmpMetadata_.end();
|
||||
}
|
||||
|
||||
bool XmpData::empty() const
|
||||
{
|
||||
return count() == 0;
|
||||
}
|
||||
|
||||
long XmpData::count() const
|
||||
{
|
||||
return static_cast<long>(xmpMetadata_.size());
|
||||
}
|
||||
|
||||
XmpData::iterator XmpData::begin()
|
||||
{
|
||||
return xmpMetadata_.begin();
|
||||
}
|
||||
|
||||
XmpData::iterator XmpData::end()
|
||||
{
|
||||
return xmpMetadata_.end();
|
||||
}
|
||||
|
||||
XmpData::iterator XmpData::erase(XmpData::iterator pos)
|
||||
{
|
||||
return xmpMetadata_.erase(pos);
|
||||
}
|
||||
|
||||
bool XmpParser::initialized_ = false;
|
||||
|
||||
#ifdef EXV_HAVE_XMP_TOOLKIT
|
||||
int XmpParser::decode( XmpData& xmpData,
|
||||
const std::string& xmpPacket)
|
||||
{ try {
|
||||
xmpData.clear();
|
||||
|
||||
if (!initialized_) {
|
||||
initialized_ = true;
|
||||
if (!SXMPMeta::Initialize()) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "XMP Toolkit initialization failed.\n";
|
||||
#endif
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
SXMPMeta meta(xmpPacket.data(), xmpPacket.size());
|
||||
SXMPIterator iter(meta);
|
||||
std::string schemaNs, propPath, propValue;
|
||||
XMP_OptionBits opt;
|
||||
while (iter.Next(&schemaNs, &propPath, &propValue, &opt)) {
|
||||
if (XMP_NodeIsSchema(opt)) continue;
|
||||
|
||||
XmpKey::AutoPtr key = makeXmpKey(schemaNs, propPath);
|
||||
if (key.get() == 0) continue;
|
||||
|
||||
// Create an Exiv2 value and read the property value
|
||||
Value::AutoPtr val = Value::create(XmpProperties::propertyType(*key.get()));
|
||||
if (XMP_PropIsSimple(opt)) {
|
||||
if (val->typeId() != xmpText) {
|
||||
int ret = val->read(propValue);
|
||||
if (ret != 0) val = Value::create(xmpText);
|
||||
}
|
||||
if (val->typeId() == xmpText) {
|
||||
std::string pv = propValue;
|
||||
// Todo: Do not use read() for XmpTextValues
|
||||
quoteText(pv);
|
||||
val->read(pv);
|
||||
}
|
||||
}
|
||||
else if (XMP_PropIsArray(opt)) {
|
||||
XMP_Index itemIdx = 1;
|
||||
std::string itemValue, arrayValue;
|
||||
XMP_OptionBits itemOpt;
|
||||
while (meta.GetArrayItem(schemaNs.c_str(), propPath.c_str(),
|
||||
itemIdx, &itemValue, &itemOpt)) {
|
||||
if (val->typeId() == xmpText) quoteText(itemValue);
|
||||
if (itemIdx > 1) arrayValue += " ";
|
||||
arrayValue += itemValue;
|
||||
++itemIdx;
|
||||
}
|
||||
iter.Skip(kXMP_IterSkipSubtree);
|
||||
// Todo: Do not use read() for XmpTextValues
|
||||
val->read(arrayValue);
|
||||
}
|
||||
else {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: XMP property " << key->key()
|
||||
<< " has unsupported property type; skipping property.\n";
|
||||
#endif
|
||||
iter.Skip(kXMP_IterSkipSubtree);
|
||||
continue;
|
||||
}
|
||||
|
||||
xmpData.add(*key.get(), val.get());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (const XMP_Error& e) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << Error(39, e.GetID(), e.GetErrMsg()) << "\n";
|
||||
#endif
|
||||
xmpData.clear();
|
||||
return 3;
|
||||
}} // XmpParser::decode
|
||||
#else
|
||||
int XmpParser::decode( XmpData& /*xmpData*/,
|
||||
const std::string& xmpPacket)
|
||||
{
|
||||
if (!xmpPacket.empty()) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: XMP toolkit support not compiled in.\n";
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
} // XmpParser::decode
|
||||
#endif // !EXV_HAVE_XMP_TOOLKIT
|
||||
|
||||
#ifdef EXV_HAVE_XMP_TOOLKIT
|
||||
int XmpParser::encode( std::string& xmpPacket,
|
||||
const XmpData& xmpData)
|
||||
{ try {
|
||||
xmpPacket.clear();
|
||||
|
||||
if (!initialized_) {
|
||||
initialized_ = true;
|
||||
if (!SXMPMeta::Initialize()) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "XMP Toolkit initialization failed.\n";
|
||||
#endif
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
SXMPMeta meta;
|
||||
|
||||
for (XmpData::const_iterator i = xmpData.begin(); i != xmpData.end(); ++i) {
|
||||
|
||||
const char* ns = XmpProperties::ns(i->groupName());
|
||||
|
||||
// Todo: Make sure the namespace is registered with XMP-SDK
|
||||
|
||||
// Todo: Requires a separate indicator for array
|
||||
// (or value type in general => reuse typeId?)
|
||||
|
||||
if (i->count() == 1) {
|
||||
// simple property
|
||||
//-ahu
|
||||
std::cerr << i->key() << "\n";
|
||||
meta.SetProperty(ns, i->tagName().c_str(), i->toString(0).c_str());
|
||||
|
||||
}
|
||||
if (i->count() > 1) {
|
||||
// array or sequence
|
||||
for (int k = 0; k < i->count(); ++k) {
|
||||
|
||||
// Todo: Need indicator if array is ordered
|
||||
|
||||
//-ahu
|
||||
std::cerr << i->key() << " count = " << i->count() << "\n";
|
||||
|
||||
meta.AppendArrayItem(ns, i->tagName().c_str(),
|
||||
kXMP_PropArrayIsOrdered,
|
||||
i->toString(k).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
meta.SerializeToBuffer(&xmpPacket, kXMP_UseCompactFormat);
|
||||
return 0;
|
||||
}
|
||||
catch (const XMP_Error& e) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << Error(39, e.GetID(), e.GetErrMsg()) << "\n";
|
||||
#endif
|
||||
xmpPacket.clear();
|
||||
return 3;
|
||||
}} // XmpParser::decode
|
||||
#else
|
||||
void XmpParser::encode( std::string& /*xmpPacket*/,
|
||||
const XmpData& xmpData)
|
||||
{
|
||||
if (!xmpData.empty()) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: XMP toolkit support not compiled in.\n";
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
} // XmpParser::encode
|
||||
#endif // !EXV_HAVE_XMP_TOOLKIT
|
||||
|
||||
// *************************************************************************
|
||||
// free functions
|
||||
std::ostream& operator<<(std::ostream& os, const Xmpdatum& md)
|
||||
{
|
||||
return os << md.value();
|
||||
}
|
||||
|
||||
} // namespace Exiv2
|
||||
|
||||
// *****************************************************************************
|
||||
// local definitions
|
||||
namespace {
|
||||
Exiv2::XmpKey::AutoPtr makeXmpKey(const std::string& schemaNs,
|
||||
const std::string& propPath)
|
||||
{
|
||||
std::string property;
|
||||
std::string::size_type idx = propPath.find(':');
|
||||
if (idx != std::string::npos) {
|
||||
// Don't worry about out_of_range, XMP parser takes care of this
|
||||
property = propPath.substr(idx + 1);
|
||||
}
|
||||
else {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: Failed to determine property name from path "
|
||||
<< propPath << ", namespace " << schemaNs
|
||||
<< "; skipping property.\n";
|
||||
#endif
|
||||
return Exiv2::XmpKey::AutoPtr();
|
||||
}
|
||||
const char* prefix = Exiv2::XmpProperties::prefix(schemaNs);
|
||||
if (prefix == 0) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
// Todo: Print warning only for the first property in each ns
|
||||
std::cerr << "Warning: Unknown schema namespace "
|
||||
<< schemaNs << "; skipping property "
|
||||
<< property << ".\n";
|
||||
#endif
|
||||
return Exiv2::XmpKey::AutoPtr();
|
||||
}
|
||||
Exiv2::XmpKey::AutoPtr key;
|
||||
// Todo: Avoid the try/catch block
|
||||
try {
|
||||
key = Exiv2::XmpKey::AutoPtr(new Exiv2::XmpKey(prefix, property));
|
||||
}
|
||||
catch (const Exiv2::AnyError& e) {
|
||||
// This should only happen for unknown property names
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
std::cerr << "Warning: " << e << "; skipping property.\n";
|
||||
#endif
|
||||
}
|
||||
return key;
|
||||
} // makeXmpKey
|
||||
|
||||
}
|
||||
286
src/xmp.hpp
Normal file
286
src/xmp.hpp
Normal file
@ -0,0 +1,286 @@
|
||||
// ***************************************************************** -*- C++ -*-
|
||||
/*
|
||||
* Copyright (C) 2004-2007 Andreas Huggel <ahuggel@gmx.net>
|
||||
*
|
||||
* This program is part of the Exiv2 distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
/*!
|
||||
@file xmp.hpp
|
||||
@brief Encoding and decoding of XMP data
|
||||
@version $Rev$
|
||||
@author Andreas Huggel (ahu)
|
||||
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
|
||||
@date 13-Jul-07, ahu: created
|
||||
*/
|
||||
#ifndef XMP_HPP_
|
||||
#define XMP_HPP_
|
||||
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
#include "metadatum.hpp"
|
||||
#include "types.hpp"
|
||||
#include "value.hpp"
|
||||
#include "properties.hpp"
|
||||
|
||||
// + standard includes
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// *****************************************************************************
|
||||
// namespace extensions
|
||||
namespace Exiv2 {
|
||||
|
||||
// *****************************************************************************
|
||||
// class definitions
|
||||
|
||||
/*!
|
||||
@brief Information related to an XMP property. An XMP metadatum consists
|
||||
of an XmpKey and a Value and provides methods to manipulate these.
|
||||
*/
|
||||
class Xmpdatum : public Metadatum {
|
||||
public:
|
||||
//! @name Creators
|
||||
//@{
|
||||
/*!
|
||||
@brief Constructor for new tags created by an application. The
|
||||
%Xmpdatum is created from a key / value pair. %Xmpdatum
|
||||
copies (clones) the value if one is provided. Alternatively, a
|
||||
program can create an 'empty' %Xmpdatum with only a key and
|
||||
set the value using setValue().
|
||||
|
||||
@param key The key of the %Xmpdatum.
|
||||
@param pValue Pointer to a %Xmpdatum value.
|
||||
@throw Error if the key cannot be parsed and converted
|
||||
to a known schema namespace prefix and property name.
|
||||
*/
|
||||
explicit Xmpdatum(const XmpKey& key,
|
||||
const Value* pValue =0);
|
||||
//! Copy constructor
|
||||
Xmpdatum(const Xmpdatum& rhs);
|
||||
//! Destructor
|
||||
virtual ~Xmpdatum();
|
||||
//@}
|
||||
|
||||
//! @name Manipulators
|
||||
//@{
|
||||
//! Assignment operator
|
||||
Xmpdatum& operator=(const Xmpdatum& rhs);
|
||||
/*!
|
||||
@brief Assign \em value to the %Xmpdatum. The type of the new Value
|
||||
is set to UShortValue.
|
||||
*/
|
||||
Xmpdatum& operator=(const uint16_t& value);
|
||||
/*!
|
||||
@brief Assign \em value to the %Xmpdatum.
|
||||
Calls setValue(const std::string&).
|
||||
*/
|
||||
Xmpdatum& operator=(const std::string& value);
|
||||
/*!
|
||||
@brief Assign \em value to the %Xmpdatum.
|
||||
Calls setValue(const Value*).
|
||||
*/
|
||||
Xmpdatum& operator=(const Value& value);
|
||||
void setValue(const Value* pValue);
|
||||
/*!
|
||||
@brief Set the value to the string \em value. Uses Value::read(const
|
||||
std::string&). If the %Xmpdatum does not have a Value yet,
|
||||
then a %Value of the correct type for this %Xmpdatum is
|
||||
created.
|
||||
*/
|
||||
void setValue(const std::string& value);
|
||||
//@}
|
||||
|
||||
//! @name Accessors
|
||||
//@{
|
||||
//! Not implemented. Calling this method will raise an exception.
|
||||
long copy(byte* buf, ByteOrder byteOrder) const;
|
||||
/*!
|
||||
@brief Return the key of the Xmpdatum. The key is of the form
|
||||
'<b>Xmp</b>.prefix.property'. Note however that the
|
||||
key is not necessarily unique, i.e., an XmpData object may
|
||||
contain multiple metadata with the same key.
|
||||
*/
|
||||
std::string key() const;
|
||||
//! Return the (preferred) schema namespace prefix.
|
||||
std::string groupName() const;
|
||||
//! Return the property name.
|
||||
std::string tagName() const;
|
||||
std::string tagLabel() const;
|
||||
//! Properties don't have a tag number. Return 0.
|
||||
uint16_t tag() const;
|
||||
TypeId typeId() const;
|
||||
const char* typeName() const;
|
||||
// Todo: Remove this method from the baseclass
|
||||
//! The Exif typeSize doesn't make sense here. Return 0.
|
||||
long typeSize() const { return 0; }
|
||||
long count() const;
|
||||
long size() const;
|
||||
std::string toString() const;
|
||||
std::string toString(long n) const;
|
||||
long toLong(long n =0) const;
|
||||
float toFloat(long n =0) const;
|
||||
Rational toRational(long n =0) const;
|
||||
Value::AutoPtr getValue() const;
|
||||
const Value& value() const;
|
||||
//@}
|
||||
|
||||
private:
|
||||
// Pimpl idiom
|
||||
struct Impl;
|
||||
Impl* p_;
|
||||
|
||||
}; // class Xmpdatum
|
||||
|
||||
/*!
|
||||
@brief Output operator for Xmpdatum types, printing the interpreted
|
||||
tag value.
|
||||
*/
|
||||
std::ostream& operator<<(std::ostream& os, const Xmpdatum& md);
|
||||
|
||||
//! Container type to hold all metadata
|
||||
typedef std::vector<Xmpdatum> XmpMetadata;
|
||||
|
||||
/*!
|
||||
@brief A container for XMP data. This is a top-level class of
|
||||
the %Exiv2 library.
|
||||
|
||||
Provide high-level access to the XMP data of an image:
|
||||
- read XMP information from an XML block
|
||||
- access metadata through keys and standard C++ iterators
|
||||
- add, modify and delete metadata
|
||||
- serialize XMP data to an XML block
|
||||
*/
|
||||
class XmpData {
|
||||
public:
|
||||
//! XmpMetadata iterator type
|
||||
typedef XmpMetadata::iterator iterator;
|
||||
//! XmpMetadata const iterator type
|
||||
typedef XmpMetadata::const_iterator const_iterator;
|
||||
|
||||
//! @name Manipulators
|
||||
//@{
|
||||
/*!
|
||||
@brief Returns a reference to the %Xmpdatum that is associated with a
|
||||
particular \em key. If %XmpData does not already contain such
|
||||
an %Xmpdatum, operator[] adds object \em Xmpdatum(key).
|
||||
|
||||
@note Since operator[] might insert a new element, it can't be a const
|
||||
member function.
|
||||
*/
|
||||
Xmpdatum& operator[](const std::string& key);
|
||||
/*!
|
||||
@brief Add an %Xmpdatum from the supplied key and value pair. This
|
||||
method copies (clones) the value.
|
||||
@return 0 if successful.
|
||||
*/
|
||||
int add(const XmpKey& key, Value* value);
|
||||
/*!
|
||||
@brief Add a copy of the Xmpdatum to the XMP metadata.
|
||||
@return 0 if successful.
|
||||
*/
|
||||
int add(const Xmpdatum& xmpdatum);
|
||||
/*!
|
||||
@brief Delete the Xmpdatum at iterator position pos, return the
|
||||
position of the next Xmpdatum.
|
||||
|
||||
@note Iterators into the metadata, including pos, are potentially
|
||||
invalidated by this call.
|
||||
*/
|
||||
iterator erase(iterator pos);
|
||||
//! Delete all Xmpdatum instances resulting in an empty container.
|
||||
void clear();
|
||||
//! Sort metadata by key
|
||||
void sortByKey();
|
||||
//! Begin of the metadata
|
||||
iterator begin();
|
||||
//! End of the metadata
|
||||
iterator end();
|
||||
/*!
|
||||
@brief Find the first Xmpdatum with the given key, return an iterator
|
||||
to it.
|
||||
*/
|
||||
iterator findKey(const XmpKey& key);
|
||||
//@}
|
||||
|
||||
//! @name Accessors
|
||||
//@{
|
||||
//! Begin of the metadata
|
||||
const_iterator begin() const;
|
||||
//! End of the metadata
|
||||
const_iterator end() const;
|
||||
/*!
|
||||
@brief Find the first Xmpdatum with the given key, return a const
|
||||
iterator to it.
|
||||
*/
|
||||
const_iterator findKey(const XmpKey& key) const;
|
||||
//! Return true if there is no XMP metadata
|
||||
bool empty() const;
|
||||
//! Get the number of metadata entries
|
||||
long count() const;
|
||||
//@}
|
||||
|
||||
private:
|
||||
// DATA
|
||||
XmpMetadata xmpMetadata_;
|
||||
}; // class XmpData
|
||||
|
||||
/*!
|
||||
@brief Stateless parser class for XMP packets. Images use this
|
||||
class to parse and serialize XMP packets. The parser uses
|
||||
the XMP toolkit to do the job.
|
||||
*/
|
||||
class XmpParser {
|
||||
public:
|
||||
/*!
|
||||
@brief Decode XMP metadata from an XMP packet \em xmpPacket into
|
||||
\em xmpData. The format of the XMP packet must follow the
|
||||
XMP specification. This method clears any previous contents
|
||||
of \em xmpData.
|
||||
|
||||
@param xmpData Container for the decoded XMP properties
|
||||
@param xmpPacket The raw XMP packet to decode
|
||||
@return 0 if successful;<BR>
|
||||
1 if XMP support has not been compiled-in;<BR>
|
||||
2 if the XMP toolkit failed to initialize;<BR>
|
||||
3 if the XMP toolkit failed and raised an XMP_Error
|
||||
*/
|
||||
static int decode( XmpData& xmpData,
|
||||
const std::string& xmpPacket);
|
||||
/*!
|
||||
@brief Encode (serialize) XMP metadata from \em xmpData into a
|
||||
string xmpPacket. The XMP packet returned in the string
|
||||
follows the XMP specification. This method clears any
|
||||
previous contents of \em xmpPacket.
|
||||
|
||||
@param xmpPacket Reference to a string to hold the encoded XMP
|
||||
packet.
|
||||
@param xmpData XMP properties to encode.
|
||||
@return 0 if successful;<BR>
|
||||
1 if XMP support has not been compiled-in;<BR>
|
||||
2 if the XMP toolkit failed to initialize;<BR>
|
||||
3 if the XMP toolkit failed and raised an XMP_Error
|
||||
*/
|
||||
static int encode( std::string& xmpPacket,
|
||||
const XmpData& xmpData);
|
||||
private:
|
||||
static bool initialized_; //! Indicates if the XMP Toolkit has been initialized
|
||||
|
||||
}; // class XmpParser
|
||||
|
||||
} // namespace Exiv2
|
||||
|
||||
#endif // #ifndef XMP_HPP_
|
||||
@ -1,35 +0,0 @@
|
||||
// ***************************************************************** -*- C++ -*-
|
||||
// xmpdump.cpp, $Rev$
|
||||
// Sample program to dump the XMP packet of an image
|
||||
|
||||
#include "image.hpp"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
int main(int argc, char* const argv[])
|
||||
try {
|
||||
|
||||
if (argc != 2) {
|
||||
std::cout << "Usage: " << argv[0] << " file\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]);
|
||||
assert(image.get() != 0);
|
||||
image->readMetadata();
|
||||
|
||||
const std::string& xmpPacket = image->xmpPacket();
|
||||
if (xmpPacket.empty()) {
|
||||
std::string error(argv[1]);
|
||||
error += ": No XMP packet found in the file";
|
||||
throw Exiv2::Error(1, error);
|
||||
}
|
||||
std::cout << xmpPacket << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exiv2::AnyError& e) {
|
||||
std::cout << "Caught Exiv2 exception '" << e << "'\n";
|
||||
return -1;
|
||||
}
|
||||
51
src/xmpparse.cpp
Normal file
51
src/xmpparse.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
// ***************************************************************** -*- C++ -*-
|
||||
// xmpparse.cpp, $Rev$
|
||||
// Read an XMP packet from a file, parse it and print all (known) properties.
|
||||
|
||||
#include "basicio.hpp"
|
||||
#include "xmp.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
int main(int argc, char* const argv[])
|
||||
try {
|
||||
if (argc != 2) {
|
||||
std::cout << "Usage: " << argv[0] << " file\n";
|
||||
return 1;
|
||||
}
|
||||
Exiv2::DataBuf buf = Exiv2::readFile(argv[1]);
|
||||
std::string xmpPacket;
|
||||
xmpPacket.assign(reinterpret_cast<char*>(buf.pData_), buf.size_);
|
||||
Exiv2::XmpData xmpData;
|
||||
if (0 != Exiv2::XmpParser::decode(xmpData, xmpPacket)) {
|
||||
std::string error(argv[1]);
|
||||
error += ": Failed to parse file contents (XMP packet)";
|
||||
throw Exiv2::Error(1, error);
|
||||
}
|
||||
if (xmpData.empty()) {
|
||||
std::string error(argv[1]);
|
||||
error += ": No XMP properties found in the XMP packet";
|
||||
throw Exiv2::Error(1, error);
|
||||
}
|
||||
for (Exiv2::XmpData::const_iterator md = xmpData.begin();
|
||||
md != xmpData.end(); ++md) {
|
||||
std::cout << std::setfill(' ') << std::left
|
||||
<< std::setw(44)
|
||||
<< md->key() << " "
|
||||
<< std::setw(9) << std::setfill(' ') << std::left
|
||||
<< md->typeName() << " "
|
||||
<< std::dec << std::setw(3)
|
||||
<< std::setfill(' ') << std::right
|
||||
<< md->count() << " "
|
||||
<< std::dec << md->value()
|
||||
<< std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
catch (Exiv2::AnyError& e) {
|
||||
std::cout << "Caught Exiv2 exception '" << e << "'\n";
|
||||
return -1;
|
||||
}
|
||||
68
src/xmpparser-test.cpp
Normal file
68
src/xmpparser-test.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
// ***************************************************************** -*- C++ -*-
|
||||
// xmpparser-test.cpp, $Rev$
|
||||
// Read an XMP packet from a file, parse and re-serialize it.
|
||||
|
||||
#include "basicio.hpp"
|
||||
#include "xmp.hpp"
|
||||
#include "error.hpp"
|
||||
#include "futils.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
int main(int argc, char* const argv[])
|
||||
try {
|
||||
if (argc != 2) {
|
||||
std::cout << "Usage: " << argv[0] << " file\n";
|
||||
return 1;
|
||||
}
|
||||
std::string filename(argv[1]);
|
||||
Exiv2::DataBuf buf = Exiv2::readFile(filename);
|
||||
std::string xmpPacket;
|
||||
xmpPacket.assign(reinterpret_cast<char*>(buf.pData_), buf.size_);
|
||||
Exiv2::XmpData xmpData;
|
||||
if (0 != Exiv2::XmpParser::decode(xmpData, xmpPacket)) {
|
||||
std::string error(argv[1]);
|
||||
error += ": Failed to parse file contents (XMP packet)";
|
||||
throw Exiv2::Error(1, error);
|
||||
}
|
||||
if (xmpData.empty()) {
|
||||
std::string error(argv[1]);
|
||||
error += ": No XMP properties found in the XMP packet";
|
||||
throw Exiv2::Error(1, error);
|
||||
}
|
||||
for (Exiv2::XmpData::const_iterator md = xmpData.begin();
|
||||
md != xmpData.end(); ++md) {
|
||||
std::cout << std::setfill(' ') << std::left
|
||||
<< std::setw(44)
|
||||
<< md->key() << " "
|
||||
<< std::setw(9) << std::setfill(' ') << std::left
|
||||
<< md->typeName() << " "
|
||||
<< std::dec << std::setw(3)
|
||||
<< std::setfill(' ') << std::right
|
||||
<< md->count() << " "
|
||||
<< std::dec << md->value()
|
||||
<< std::endl;
|
||||
}
|
||||
std::cerr << "-----------------------------------------------\n";
|
||||
if (0 != Exiv2::XmpParser::encode(xmpPacket, xmpData)) {
|
||||
std::string error(argv[1]);
|
||||
error += ": Failed to encode the XMP data";
|
||||
throw Exiv2::Error(1, error);
|
||||
}
|
||||
filename += "-new";
|
||||
Exiv2::FileIo file(filename);
|
||||
if (file.open("wb") != 0) {
|
||||
throw Exiv2::Error(10, filename, "wb", Exiv2::strError());
|
||||
}
|
||||
if (file.write(reinterpret_cast<const Exiv2::byte*>(xmpPacket.data()), xmpPacket.size()) == 0) {
|
||||
throw Exiv2::Error(2, filename, Exiv2::strError(), "FileIo::write");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exiv2::AnyError& e) {
|
||||
std::cout << "Caught Exiv2 exception '" << e << "'\n";
|
||||
return -1;
|
||||
}
|
||||
@ -3,7 +3,7 @@ tmp/
|
||||
|
||||
Exiv2 version ------------------------------------------------------------
|
||||
../../src/exiv2
|
||||
exiv2 0.15
|
||||
exiv2 0.16
|
||||
Copyright (C) 2004-2007 Andreas Huggel.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
@ -33,12 +33,12 @@ Actions:
|
||||
rm | delete Delete image metadata from the files.
|
||||
in | insert Insert metadata from corresponding *.exv files.
|
||||
Use option -S to change the suffix of the input files.
|
||||
ex | extract Extract metadata to *.exv and thumbnail image files.
|
||||
ex | extract Extract metadata to *.exv, *.xmp and thumbnail image files.
|
||||
mv | rename Rename files and/or set file timestamps according to the
|
||||
Exif create timestamp. The filename format can be set with
|
||||
-r format, timestamp options are controlled with -t and -T.
|
||||
mo | modify Apply commands to modify (add, set, delete) the Exif and
|
||||
Iptc metadata of image files or set the Jpeg comment.
|
||||
IPTC metadata of image files or set the JPEG comment.
|
||||
Requires option -c, -m or -M.
|
||||
fi | fixiso Copy ISO setting from the Nikon Makernote to the regular
|
||||
Exif tag.
|
||||
@ -62,8 +62,9 @@ Options:
|
||||
t : interpreted (translated) Exif data (shortcut for -Pkyct)
|
||||
v : plain Exif data values (shortcut for -Pxgnycv)
|
||||
h : hexdump of the Exif data (shortcut for -Pxgnycsh)
|
||||
i : Iptc data values
|
||||
c : Jpeg comment
|
||||
i : IPTC data values
|
||||
x : XMP properties
|
||||
c : JPEG comment
|
||||
-P cols Print columns for the Exif taglist ('print' action). Valid are:
|
||||
x : print a column with the tag value
|
||||
g : group name
|
||||
@ -80,20 +81,24 @@ Options:
|
||||
a : all supported metadata (the default)
|
||||
e : Exif section
|
||||
t : Exif thumbnail only
|
||||
i : Iptc data
|
||||
c : Jpeg comment
|
||||
i : IPTC data
|
||||
x : XMP packet
|
||||
c : JPEG comment
|
||||
-i tgt Insert target(s) for the 'insert' action. Possible targets are
|
||||
the same as those for the -d option. Only Jpeg thumbnails can
|
||||
be inserted, they need to be named <file>-thumb.jpg
|
||||
the same as those for the -d option, plus:
|
||||
X : Insert XMP packet from <file>.xmp
|
||||
Only JPEG thumbnails can be inserted, they need to be named
|
||||
<file>-thumb.jpg
|
||||
-e tgt Extract target(s) for the 'extract' action. Possible targets
|
||||
are the same as those for the -d option.
|
||||
are the same as those for the -i option, plus:
|
||||
X : Extract XMP packet to <file>.xmp
|
||||
-r fmt Filename format for the 'rename' action. The format string
|
||||
follows strftime(3). The following keywords are supported:
|
||||
:basename: - original filename without extension
|
||||
:dirname: - name of the directory holding the original file
|
||||
:parentname: - name of parent directory
|
||||
Default filename format is %Y%m%d_%H%M%S.
|
||||
-c txt Jpeg comment string to set in the image.
|
||||
-c txt JPEG comment string to set in the image.
|
||||
-m file Command file for the modify action. The format for commands is
|
||||
set|add|del <key> [[<type>] <value>].
|
||||
-M cmd Command line for the modify action. The format for the
|
||||
@ -1955,7 +1960,7 @@ File 6/15: 20030925_201850.jpg
|
||||
Writing Exif data from 20030925_201850.jpg to ./20030925_201850.exv
|
||||
File 7/15: 20001026_044550.jpg
|
||||
Writing Exif data from 20001026_044550.jpg to ./20001026_044550.exv
|
||||
Writing Jpeg comment from 20001026_044550.jpg to ./20001026_044550.exv
|
||||
Writing JPEG comment from 20001026_044550.jpg to ./20001026_044550.exv
|
||||
File 8/15: 20030926_111535.jpg
|
||||
Writing Exif data from 20030926_111535.jpg to ./20030926_111535.exv
|
||||
File 9/15: 20040316_075137.jpg
|
||||
@ -4866,7 +4871,7 @@ File 6/15: 20030925_201850.jpg
|
||||
Erasing Exif data from the file
|
||||
File 7/15: 20001026_044550.jpg
|
||||
Erasing Exif data from the file
|
||||
Erasing Jpeg comment from the file
|
||||
Erasing JPEG comment from the file
|
||||
File 8/15: 20030926_111535.jpg
|
||||
Erasing Exif data from the file
|
||||
File 9/15: 20040316_075137.jpg
|
||||
@ -4929,7 +4934,7 @@ File 6/15: 20030925_201850.jpg
|
||||
Writing Exif data from ./20030925_201850.exv to 20030925_201850.jpg
|
||||
File 7/15: 20001026_044550.jpg
|
||||
Writing Exif data from ./20001026_044550.exv to 20001026_044550.jpg
|
||||
Writing Jpeg comment from ./20001026_044550.exv to 20001026_044550.jpg
|
||||
Writing JPEG comment from ./20001026_044550.exv to 20001026_044550.jpg
|
||||
File 8/15: 20030926_111535.jpg
|
||||
Writing Exif data from ./20030926_111535.exv to 20030926_111535.jpg
|
||||
File 9/15: 20040316_075137.jpg
|
||||
|
||||
Loading…
Reference in New Issue
Block a user