diff --git a/msvc/exiv2.sln b/msvc/exiv2.sln
index a51fb452..886b0bcb 100644
--- a/msvc/exiv2.sln
+++ b/msvc/exiv2.sln
@@ -73,6 +73,16 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "write-test", "write-test\wr
{831EF580-92C8-4CA8-B0CE-3D906280A54D} = {831EF580-92C8-4CA8-B0CE-3D906280A54D}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iotest", "iotest\iotest.vcproj", "{5D43ECB3-681D-4732-9395-AB81CD283F6C}"
+ ProjectSection(ProjectDependencies) = postProject
+ {831EF580-92C8-4CA8-B0CE-3D906280A54D} = {831EF580-92C8-4CA8-B0CE-3D906280A54D}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iptceasy", "iptceasy\iptceasy.vcproj", "{D8B36F3A-34BB-4540-A731-EEABF1DC2E05}"
+ ProjectSection(ProjectDependencies) = postProject
+ {831EF580-92C8-4CA8-B0CE-3D906280A54D} = {831EF580-92C8-4CA8-B0CE-3D906280A54D}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
@@ -139,6 +149,14 @@ Global
{94A7505B-3A53-40F0-95A2-2ECB1CEC7C57}.Debug.Build.0 = Debug|Win32
{94A7505B-3A53-40F0-95A2-2ECB1CEC7C57}.Release.ActiveCfg = Release|Win32
{94A7505B-3A53-40F0-95A2-2ECB1CEC7C57}.Release.Build.0 = Release|Win32
+ {5D43ECB3-681D-4732-9395-AB81CD283F6C}.Debug.ActiveCfg = Debug|Win32
+ {5D43ECB3-681D-4732-9395-AB81CD283F6C}.Debug.Build.0 = Debug|Win32
+ {5D43ECB3-681D-4732-9395-AB81CD283F6C}.Release.ActiveCfg = Release|Win32
+ {5D43ECB3-681D-4732-9395-AB81CD283F6C}.Release.Build.0 = Release|Win32
+ {D8B36F3A-34BB-4540-A731-EEABF1DC2E05}.Debug.ActiveCfg = Debug|Win32
+ {D8B36F3A-34BB-4540-A731-EEABF1DC2E05}.Debug.Build.0 = Debug|Win32
+ {D8B36F3A-34BB-4540-A731-EEABF1DC2E05}.Release.ActiveCfg = Release|Win32
+ {D8B36F3A-34BB-4540-A731-EEABF1DC2E05}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
diff --git a/msvc/exiv2lib/exiv2lib.vcproj b/msvc/exiv2lib/exiv2lib.vcproj
index 95c55bd7..4a32f0cd 100644
--- a/msvc/exiv2lib/exiv2lib.vcproj
+++ b/msvc/exiv2lib/exiv2lib.vcproj
@@ -1,351 +1,363 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/msvc/exivsimple/exivsimple.cpp b/msvc/exivsimple/exivsimple.cpp
index f000115d..0f3ab695 100644
--- a/msvc/exivsimple/exivsimple.cpp
+++ b/msvc/exivsimple/exivsimple.cpp
@@ -28,61 +28,60 @@
#include "stdafx.h"
#include "exivsimple.h"
+#include "image.hpp"
#include "exif.hpp"
#include "iptc.hpp"
#include
-struct ExivImage
+struct ImageWrapper
{
- Exiv2::IptcData iptcData;
- Exiv2::ExifData exifData;
- std::string fileName;
+ Exiv2::Image::AutoPtr image;
};
// Returns 0 if failed.
EXIVSIMPLE_API HIMAGE OpenImage(const char *file)
{
assert(file);
+ ImageWrapper *imgWrap = new ImageWrapper;
- ExivImage *image = new ExivImage;
- image->fileName = file;
+ // See if file exists. Sorry for very bad error handling
+ if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(file)) {
+ return 0;
+ }
- // rc of 3 just means no iptc or exif data (not a real error)
- int rc = image->iptcData.read( file );
- if (rc==0 || rc==3)
- rc = image->exifData.read( file );
+ imgWrap->image = Exiv2::ImageFactory::open(file);
+ if (imgWrap->image.get() == 0) {
+ return 0;
+ }
- if (rc!=0 && rc!=3) {
- delete image;
- image = 0;
+ // Load existing metadata
+ if (imgWrap->image->readMetadata()) {
+ delete imgWrap;
+ imgWrap = 0;
}
- return (HIMAGE)image;
+ return (HIMAGE)imgWrap;
}
EXIVSIMPLE_API void FreeImage(HIMAGE img)
{
if (img) {
- ExivImage *image = (ExivImage*)img;
- delete image;
+ ImageWrapper *imgWrap = (ImageWrapper*)img;
+ delete imgWrap;
}
}
EXIVSIMPLE_API int SaveImage(HIMAGE img)
{
assert(img);
- ExivImage *image = (ExivImage*)img;
-
- int rc = image->iptcData.write(image->fileName);
- if (rc==0)
- rc = image->exifData.write(image->fileName);
-
- return rc;
+ ImageWrapper *imgWrap = (ImageWrapper*)img;
+ return imgWrap->image->writeMetadata();
}
// This is weird because iptc and exif have not been "unified". Once
-// the API is turned inside out, this DLL should not have to know
+// they are unified, this DLL should not have to know
// about either... just generic images, keys, values, etc.
+//
// buffsize should be the total size of *buff (including space for null)
// Note that if there is more than one entry (for some IPTC datasets) this
// returns the first one found. Currently no way to get the others.
@@ -90,14 +89,18 @@ EXIVSIMPLE_API int ReadMeta(HIMAGE img, const char *key, char *buff, int buffsiz
{
assert(img && key && buff);
if (img==0 || key==0 || buff==0 || buffsize==0) return -1;
- ExivImage *image = (ExivImage*)img;
+ ImageWrapper *imgWrap = (ImageWrapper*)img;
int rc = 2;
+ Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
+ Exiv2::ExifData &exifData = imgWrap->image->exifData();
+
try {
+ // First try iptc
Exiv2::IptcKey iptcKey(key);
rc = 1;
- Exiv2::IptcData::const_iterator iter = image->iptcData.findKey(iptcKey);
- if (iter != image->iptcData.end()) {
+ Exiv2::IptcData::const_iterator iter = iptcData.findKey(iptcKey);
+ if (iter != iptcData.end()) {
strncpy(buff, iter->value().toString().c_str(), buffsize);
buff[buffsize-1] = 0;
rc = 0;
@@ -111,8 +114,8 @@ EXIVSIMPLE_API int ReadMeta(HIMAGE img, const char *key, char *buff, int buffsiz
try {
Exiv2::ExifKey exifKey(key);
rc = 1;
- Exiv2::ExifData::const_iterator iter = image->exifData.findKey(exifKey);
- if (iter != image->exifData.end()) {
+ Exiv2::ExifData::const_iterator iter = exifData.findKey(exifKey);
+ if (iter != exifData.end()) {
strncpy(buff, iter->value().toString().c_str(), buffsize);
buff[buffsize-1] = 0;
rc = 0;
@@ -132,9 +135,12 @@ EXIVSIMPLE_API int ModifyMeta(HIMAGE img, const char *key, const char *val, DllT
{
assert(img && key && val);
if (img==0 || key==0 || val==0) return -1;
- ExivImage *image = (ExivImage*)img;
+ ImageWrapper *imgWrap = (ImageWrapper*)img;
int rc = 2;
+ Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
+ Exiv2::ExifData &exifData = imgWrap->image->exifData();
+
std::string data(val);
// if data starts and ends with quotes, remove them
if (data.at(0) == '\"' && data.at(data.size()-1) == '\"') {
@@ -150,13 +156,13 @@ EXIVSIMPLE_API int ModifyMeta(HIMAGE img, const char *key, const char *val, DllT
Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
value->read(data);
- Exiv2::IptcData::iterator iter = image->iptcData.findKey(iptcKey);
- if (iter != image->iptcData.end()) {
+ Exiv2::IptcData::iterator iter = iptcData.findKey(iptcKey);
+ if (iter != iptcData.end()) {
iter->setValue(value.get());
rc = 0;
}
else {
- rc = image->iptcData.add(iptcKey, value.get());
+ rc = iptcData.add(iptcKey, value.get());
}
}
catch(const Exiv2::Error&) {
@@ -174,13 +180,13 @@ EXIVSIMPLE_API int ModifyMeta(HIMAGE img, const char *key, const char *val, DllT
Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
value->read(data);
- Exiv2::ExifData::iterator iter = image->exifData.findKey(exifKey);
- if (iter != image->exifData.end()) {
+ Exiv2::ExifData::iterator iter = exifData.findKey(exifKey);
+ if (iter != exifData.end()) {
iter->setValue(value.get());
rc = 0;
}
else {
- image->exifData.add(exifKey, value.get());
+ exifData.add(exifKey, value.get());
rc = 0;
}
}
@@ -198,9 +204,12 @@ EXIVSIMPLE_API int AddMeta(HIMAGE img, const char *key, const char *val, DllType
{
assert(img && key && val);
if (img==0 || key==0 || val==0) return -1;
- ExivImage *image = (ExivImage*)img;
+ ImageWrapper *imgWrap = (ImageWrapper*)img;
int rc = 2;
+ Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
+ Exiv2::ExifData &exifData = imgWrap->image->exifData();
+
std::string data(val);
// if data starts and ends with quotes, remove them
if (data.at(0) == '\"' && data.at(data.size()-1) == '\"') {
@@ -216,7 +225,7 @@ EXIVSIMPLE_API int AddMeta(HIMAGE img, const char *key, const char *val, DllType
Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
value->read(data);
- rc = image->iptcData.add(iptcKey, value.get());
+ rc = iptcData.add(iptcKey, value.get());
}
catch(const Exiv2::Error&) {
}
@@ -233,7 +242,7 @@ EXIVSIMPLE_API int AddMeta(HIMAGE img, const char *key, const char *val, DllType
Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
value->read(data);
- image->exifData.add(exifKey, value.get());
+ exifData.add(exifKey, value.get());
rc = 0;
}
catch(const Exiv2::Error&) {
@@ -249,15 +258,18 @@ EXIVSIMPLE_API int RemoveMeta(HIMAGE img, const char *key)
{
assert(img && key);
if (img==0 || key==0) return -1;
- ExivImage *image = (ExivImage*)img;
+ ImageWrapper *imgWrap = (ImageWrapper*)img;
int rc = 2;
+ Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
+ Exiv2::ExifData &exifData = imgWrap->image->exifData();
+
try {
Exiv2::IptcKey iptcKey(key);
rc = 1;
- Exiv2::IptcData::iterator iter = image->iptcData.findKey(iptcKey);
- if (iter != image->iptcData.end()) {
- image->iptcData.erase(iter);
+ Exiv2::IptcData::iterator iter = iptcData.findKey(iptcKey);
+ if (iter != iptcData.end()) {
+ iptcData.erase(iter);
rc = 0;
}
}
@@ -269,9 +281,9 @@ EXIVSIMPLE_API int RemoveMeta(HIMAGE img, const char *key)
try {
Exiv2::ExifKey exifKey(key);
rc = 1;
- Exiv2::ExifData::iterator iter = image->exifData.findKey(exifKey);
- if (iter != image->exifData.end()) {
- image->exifData.erase(iter);
+ Exiv2::ExifData::iterator iter = exifData.findKey(exifKey);
+ if (iter != exifData.end()) {
+ exifData.erase(iter);
rc = 0;
}
}
@@ -286,17 +298,20 @@ EXIVSIMPLE_API int EnumMeta(HIMAGE img, METAENUMPROC proc, void *user)
{
assert(img && proc);
if (img==0 || proc==0) return -1;
- ExivImage *image = (ExivImage*)img;
+ ImageWrapper *imgWrap = (ImageWrapper*)img;
bool more = true;
- Exiv2::IptcData::const_iterator iend = image->iptcData.end();
- for (Exiv2::IptcData::const_iterator i = image->iptcData.begin();
+ Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
+ Exiv2::ExifData &exifData = imgWrap->image->exifData();
+
+ Exiv2::IptcData::const_iterator iend = iptcData.end();
+ for (Exiv2::IptcData::const_iterator i = iptcData.begin();
i != iend && more; ++i) {
more = proc(i->key().c_str(), i->value().toString().c_str(), user);
}
- Exiv2::ExifData::const_iterator eend = image->exifData.end();
- for (Exiv2::ExifData::const_iterator e = image->exifData.begin();
+ Exiv2::ExifData::const_iterator eend = exifData.end();
+ for (Exiv2::ExifData::const_iterator e = exifData.begin();
e != eend && more; ++e) {
more = proc(e->key().c_str(), e->value().toString().c_str(), user);
}
diff --git a/msvc/iotest/iotest.vcproj b/msvc/iotest/iotest.vcproj
new file mode 100644
index 00000000..568a0f54
--- /dev/null
+++ b/msvc/iotest/iotest.vcproj
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/msvc/iptceasy/iptceasy.vcproj b/msvc/iptceasy/iptceasy.vcproj
new file mode 100644
index 00000000..366778ae
--- /dev/null
+++ b/msvc/iptceasy/iptceasy.vcproj
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Makefile b/src/Makefile
index 4992beae..508c7e44 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -51,14 +51,14 @@ include $(top_srcdir)/config.mk
CCHDR = rcsid.hpp error.hpp
# Add library C++ source files to this list
-CCSRC = canonmn.cpp datasets.cpp exif.cpp fujimn.cpp ifd.cpp image.cpp iptc.cpp \
+CCSRC = canonmn.cpp datasets.cpp exif.cpp fujimn.cpp ifd.cpp basicio.cpp iptc.cpp \
makernote.cpp metadatum.cpp nikonmn.cpp sigmamn.cpp tags.cpp types.cpp \
- value.cpp
+ image.cpp jpgimage.cpp value.cpp
# Add source files of simple applications to this list
BINSRC = addmoddel.cpp exifcomment.cpp exifprint.cpp ifd-test.cpp iptcprint.cpp \
iptctest.cpp key-test.cpp makernote-test.cpp taglist.cpp write-test.cpp \
- write2-test.cpp dataarea-test.cpp iptceasy.cpp
+ iotest.cpp write2-test.cpp dataarea-test.cpp iptceasy.cpp
# State the main source file of the Exiv2 application here
EXIV2MAIN = exiv2.cpp
diff --git a/src/actions.cpp b/src/actions.cpp
index e2cd7af1..337497bc 100644
--- a/src/actions.cpp
+++ b/src/actions.cpp
@@ -39,6 +39,7 @@ EXIV2_RCSID("@(#) $Id$");
#endif
#include "actions.hpp"
+#include "image.hpp"
#include "exiv2.hpp"
#include "utils.hpp"
#include "types.hpp"
@@ -167,13 +168,24 @@ namespace Action {
int Print::printSummary()
{
- Exiv2::ExifData exifData;
- int rc = exifData.read(path_);
+ if (!Util::fileExists(path_, true)) {
+ std::cerr << path_
+ << ": Failed to open the file\n";
+ return -1;
+ }
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
+ if (image.get() == 0) {
+ std::cerr << path_
+ << ": The file contains data of an unknown image type\n";
+ return -2;
+ }
+ int rc = image->readMetadata();
if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc;
}
+ Exiv2::ExifData &exifData = image->exifData();
align_ = 16;
// Filename
@@ -267,7 +279,7 @@ namespace Action {
// Subject distance
std::cout << std::setw(align_) << std::setfill(' ') << std::left
<< "Subject distance" << ": ";
- if (0 == printTag(exifData, "Exif.Photo.SubjectDistance")) {
+ if (0 == printTag(exifData, "Exif.Photo.SubjectDistance")) {
md = exifData.findKey(
Exiv2::ExifKey("Exif.Canon.CameraSettings2"));
if (md != exifData.end() && md->count() >= 19) {
@@ -446,13 +458,24 @@ namespace Action {
int Print::printInterpreted()
{
- Exiv2::ExifData exifData;
- int rc = exifData.read(path_);
+ if (!Util::fileExists(path_, true)) {
+ std::cerr << path_
+ << ": Failed to open the file\n";
+ return -1;
+ }
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
+ if (image.get() == 0) {
+ std::cerr << path_
+ << ": The file contains data of an unknown image type\n";
+ return -2;
+ }
+ int rc = image->readMetadata();
if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc;
}
+ Exiv2::ExifData &exifData = image->exifData();
Exiv2::ExifData::const_iterator md;
for (md = exifData.begin(); md != exifData.end(); ++md) {
std::cout << "0x" << std::setw(4) << std::setfill('0') << std::right
@@ -469,13 +492,24 @@ namespace Action {
int Print::printValues()
{
- Exiv2::ExifData exifData;
- int rc = exifData.read(path_);
+ if (!Util::fileExists(path_, true)) {
+ std::cerr << path_
+ << ": Failed to open the file\n";
+ return -1;
+ }
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
+ if (image.get() == 0) {
+ std::cerr << path_
+ << ": The file contains data of an unknown image type\n";
+ return -2;
+ }
+ int rc = image->readMetadata();
if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc;
}
+ Exiv2::ExifData &exifData = image->exifData();
Exiv2::ExifData::const_iterator end = exifData.end();
Exiv2::ExifData::const_iterator md;
for (md = exifData.begin(); md != end; ++md) {
@@ -499,13 +533,24 @@ namespace Action {
int Print::printIptc()
{
- Exiv2::IptcData iptcData;
- int rc = iptcData.read(path_);
+ if (!Util::fileExists(path_, true)) {
+ std::cerr << path_
+ << ": Failed to open the file\n";
+ return -1;
+ }
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
+ if (image.get() == 0) {
+ std::cerr << path_
+ << ": The file contains data of an unknown image type\n";
+ return -2;
+ }
+ int rc = image->readMetadata();
if (rc) {
- std::cerr << Exiv2::IptcData::strError(rc, path_) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc;
}
+ Exiv2::IptcData &iptcData = image->iptcData();
Exiv2::IptcData::const_iterator end = iptcData.end();
Exiv2::IptcData::const_iterator md;
for (md = iptcData.begin(); md != end; ++md) {
@@ -529,13 +574,24 @@ namespace Action {
int Print::printHexdump()
{
- Exiv2::ExifData exifData;
- int rc = exifData.read(path_);
+ if (!Util::fileExists(path_, true)) {
+ std::cerr << path_
+ << ": Failed to open the file\n";
+ return -1;
+ }
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
+ if (image.get() == 0) {
+ std::cerr << path_
+ << ": The file contains data of an unknown image type\n";
+ return -2;
+ }
+ int rc = image->readMetadata();
if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc;
}
+ Exiv2::ExifData &exifData = image->exifData();
Exiv2::ExifData::const_iterator md;
for (md = exifData.begin(); md != exifData.end(); ++md) {
std::cout << std::setw(4) << std::setfill(' ') << std::left
@@ -567,7 +623,7 @@ namespace Action {
<< ": Failed to open the file\n";
return -1;
}
- Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::instance().open(path_);
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
if (image.get() == 0) {
std::cerr << path_
<< ": The file contains data of an unknown image type\n";
@@ -598,12 +654,24 @@ namespace Action {
int Rename::run(const std::string& path)
try {
- Exiv2::ExifData exifData;
- int rc = exifData.read(path);
+ if (!Util::fileExists(path, true)) {
+ std::cerr << path
+ << ": Failed to open the file\n";
+ return -1;
+ }
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path);
+ if (image.get() == 0) {
+ std::cerr << path
+ << ": The file contains data of an unknown image type\n";
+ return -2;
+ }
+ int rc = image->readMetadata();
if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path) << "\n";
return rc;
}
+
+ Exiv2::ExifData &exifData = image->exifData();
Exiv2::ExifKey key("Exif.Photo.DateTimeOriginal");
Exiv2::ExifData::iterator md = exifData.findKey(key);
if (md == exifData.end()) {
@@ -683,8 +751,13 @@ namespace Action {
try {
path_ = path;
+ if (!Util::fileExists(path_, true)) {
+ std::cerr << path_
+ << ": Failed to open the file\n";
+ return -1;
+ }
Exiv2::Image::AutoPtr image
- = Exiv2::ImageFactory::instance().open(path_);
+ = Exiv2::ImageFactory::open(path_);
if (image.get() == 0) {
std::cerr << path_
<< ": The file contains data of an unknown image type\n";
@@ -710,7 +783,7 @@ namespace Action {
if (0 == rc) {
rc = image->writeMetadata();
if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
}
}
@@ -725,33 +798,22 @@ namespace Action {
int Erase::eraseThumbnail(Exiv2::Image* image) const
{
- if (image->sizeExifData() == 0) {
+ Exiv2::ExifData &exifData = image->exifData();
+ std::string thumbExt = exifData.thumbnailExtension();
+ if (thumbExt.empty()) {
return 0;
}
- int rc = 0;
- Exiv2::ExifData exifData;
- rc = exifData.read(image->exifData(), image->sizeExifData());
- if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
- }
- if (0 == rc) {
- std::string thumbExt = exifData.thumbnailExtension();
- if (!thumbExt.empty()) {
- long delta = exifData.eraseThumbnail();
- if (Params::instance().verbose_) {
- std::cout << "Erasing " << delta
- << " Bytes of thumbnail data" << std::endl;
- }
- Exiv2::DataBuf buf(exifData.copy());
- image->setExifData(buf.pData_, buf.size_);
- }
+ long delta = exifData.eraseThumbnail();
+ if (Params::instance().verbose_) {
+ std::cout << "Erasing " << delta
+ << " Bytes of thumbnail data" << std::endl;
}
- return rc;
+ return 0;
}
int Erase::eraseExifData(Exiv2::Image* image) const
{
- if (Params::instance().verbose_ && image->sizeExifData() > 0) {
+ if (Params::instance().verbose_ && image->exifData().count() > 0) {
std::cout << "Erasing Exif data from the file" << std::endl;
}
image->clearExifData();
@@ -760,7 +822,7 @@ namespace Action {
int Erase::eraseIptcData(Exiv2::Image* image) const
{
- if (Params::instance().verbose_ && image->sizeIptcData() > 0) {
+ if (Params::instance().verbose_ && image->iptcData().count() > 0) {
std::cout << "Erasing Iptc data from the file" << std::endl;
}
image->clearIptcData();
@@ -816,12 +878,24 @@ namespace Action {
int Extract::writeThumbnail() const
{
- Exiv2::ExifData exifData;
- int rc = exifData.read(path_);
+ if (!Util::fileExists(path_, true)) {
+ std::cerr << path_
+ << ": Failed to open the file\n";
+ return -1;
+ }
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
+ if (image.get() == 0) {
+ std::cerr << path_
+ << ": The file contains data of an unknown image type\n";
+ return -2;
+ }
+ int rc = image->readMetadata();
if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc;
}
+ Exiv2::ExifData &exifData = image->exifData();
+
std::string thumb = Util::dirname(path_) + SEPERATOR_STR
+ Util::basename(path_, true) + "-thumb";
std::string thumbExt = exifData.thumbnailExtension();
@@ -898,14 +972,26 @@ namespace Action {
<< ": Failed to open the file\n";
return -1;
}
- Exiv2::ExifData exifData;
- int rc = exifData.read(path);
+ if (!Util::fileExists(path, true)) {
+ std::cerr << path
+ << ": Failed to open the file\n";
+ return -1;
+ }
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path);
+ if (image.get() == 0) {
+ std::cerr << path
+ << ": The file contains data of an unknown image type\n";
+ return -2;
+ }
+ int rc = image->readMetadata();
if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path) << "\n";
return rc;
}
+
+ Exiv2::ExifData &exifData = image->exifData();
exifData.setJpegThumbnail(thumbPath);
- return exifData.write(path);
+ return image->writeMetadata();
} // Insert::insertThumbnail
@@ -926,10 +1012,17 @@ namespace Action {
<< ": Failed to open the file\n";
return -1;
}
-
- // Read both exif and iptc metadata (ignore return code)
- exifData_.read(path);
- iptcData_.read(path);
+ image_ = Exiv2::ImageFactory::open(path);
+ if (image_.get() == 0) {
+ std::cerr << path
+ << ": The file contains data of an unknown image type\n";
+ return -2;
+ }
+ int rc = image_->readMetadata();
+ if (rc) {
+ std::cerr << Exiv2::Image::strError(rc, path) << "\n";
+ return rc;
+ }
// loop through command table and apply each command
ModifyCmds& modifyCmds = Params::instance().modifyCmds_;
@@ -953,13 +1046,9 @@ namespace Action {
}
// Save both exif and iptc metadata
- int rc = exifData_.write(path);
- if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
- }
- rc = iptcData_.write(path);
+ rc = image_->writeMetadata();
if (rc) {
- std::cerr << Exiv2::IptcData::strError(rc, path) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path) << "\n";
}
return rc;
}
@@ -981,10 +1070,10 @@ namespace Action {
Exiv2::Value::AutoPtr value = Exiv2::Value::create(modifyCmd.typeId_);
value->read(modifyCmd.value_);
if (modifyCmd.metadataId_ == exif) {
- exifData_.add(Exiv2::ExifKey(modifyCmd.key_), value.get());
+ image_->exifData().add(Exiv2::ExifKey(modifyCmd.key_), value.get());
}
if (modifyCmd.metadataId_ == iptc) {
- iptcData_.add(Exiv2::IptcKey(modifyCmd.key_), value.get());
+ image_->iptcData().add(Exiv2::IptcKey(modifyCmd.key_), value.get());
}
}
@@ -996,12 +1085,15 @@ namespace Action {
<< Exiv2::TypeInfo::typeName(modifyCmd.typeId_)
<< ")" << std::endl;
}
+
+ Exiv2::ExifData &exifData = image_->exifData();
+ Exiv2::IptcData &iptcData = image_->iptcData();
Exiv2::Metadatum* metadatum = 0;
if (modifyCmd.metadataId_ == exif) {
- metadatum = &exifData_[modifyCmd.key_];
+ metadatum = &exifData[modifyCmd.key_];
}
if (modifyCmd.metadataId_ == iptc) {
- metadatum = &iptcData_[modifyCmd.key_];
+ metadatum = &iptcData[modifyCmd.key_];
}
assert(metadatum);
Exiv2::Value::AutoPtr value = metadatum->getValue();
@@ -1020,15 +1112,18 @@ namespace Action {
if (Params::instance().verbose_) {
std::cout << "Del " << modifyCmd.key_ << std::endl;
}
+
+ Exiv2::ExifData &exifData = image_->exifData();
+ Exiv2::IptcData &iptcData = image_->iptcData();
if (modifyCmd.metadataId_ == exif) {
Exiv2::ExifData::iterator pos =
- exifData_.findKey(Exiv2::ExifKey(modifyCmd.key_));
- if (pos != exifData_.end()) exifData_.erase(pos);
+ exifData.findKey(Exiv2::ExifKey(modifyCmd.key_));
+ if (pos != exifData.end()) exifData.erase(pos);
}
if (modifyCmd.metadataId_ == iptc) {
Exiv2::IptcData::iterator pos =
- iptcData_.findKey(Exiv2::IptcKey(modifyCmd.key_));
- if (pos != iptcData_.end()) iptcData_.erase(pos);
+ iptcData.findKey(Exiv2::IptcKey(modifyCmd.key_));
+ if (pos != iptcData.end()) iptcData.erase(pos);
}
}
@@ -1046,19 +1141,31 @@ namespace Action {
try {
adjustment_ = Params::instance().adjustment_;
- Exiv2::ExifData exifData;
- int rc = exifData.read(path);
+ if (!Util::fileExists(path, true)) {
+ std::cerr << path
+ << ": Failed to open the file\n";
+ return -1;
+ }
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path);
+ if (image.get() == 0) {
+ std::cerr << path
+ << ": The file contains data of an unknown image type\n";
+ return -2;
+ }
+ int rc = image->readMetadata();
if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path) << "\n";
return rc;
}
+
+ Exiv2::ExifData &exifData = image->exifData();
rc = adjustDateTime(exifData, "Exif.Image.DateTime", path);
rc += adjustDateTime(exifData, "Exif.Photo.DateTimeOriginal", path);
rc += adjustDateTime(exifData, "Exif.Photo.DateTimeDigitized", path);
if (rc) return 1;
- rc = exifData.write(path);
+ rc = image->writeMetadata();
if (rc) {
- std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
+ std::cerr << Exiv2::Image::strError(rc, path) << "\n";
}
return rc;
}
@@ -1182,7 +1289,7 @@ namespace {
return -1;
}
Exiv2::Image::AutoPtr sourceImage
- = Exiv2::ImageFactory::instance().open(source);
+ = Exiv2::ImageFactory::open(source);
if (sourceImage.get() == 0) {
std::cerr << source
<< ": The file contains data of an unknown image type\n";
@@ -1195,7 +1302,7 @@ namespace {
return 1;
}
Exiv2::Image::AutoPtr targetImage
- = Exiv2::ImageFactory::instance().open(target);
+ = Exiv2::ImageFactory::open(target);
if (preserve && targetImage.get() != 0) {
if (targetImage->readMetadata()) {
std::cerr << target
@@ -1205,7 +1312,7 @@ namespace {
}
if (targetImage.get() == 0) {
targetImage
- = Exiv2::ImageFactory::instance().create(Exiv2::Image::exv, target);
+ = Exiv2::ImageFactory::create(Exiv2::Image::exv, target);
}
if (targetImage.get() == 0) {
std::cerr << target
@@ -1213,22 +1320,20 @@ namespace {
return 2;
}
if ( Params::instance().target_ & Params::ctExif
- && sourceImage->sizeExifData() > 0) {
+ && sourceImage->exifData().count() > 0) {
if (Params::instance().verbose_) {
std::cout << "Writing Exif data from " << source
<< " to " << target << std::endl;
}
- targetImage->setExifData(sourceImage->exifData(),
- sourceImage->sizeExifData());
+ targetImage->setExifData(sourceImage->exifData());
}
if ( Params::instance().target_ & Params::ctIptc
- && sourceImage->sizeIptcData() > 0) {
+ && sourceImage->iptcData().count() > 0) {
if (Params::instance().verbose_) {
std::cout << "Writing Iptc data from " << source
<< " to " << target << std::endl;
}
- targetImage->setIptcData(sourceImage->iptcData(),
- sourceImage->sizeIptcData());
+ targetImage->setIptcData(sourceImage->iptcData());
}
if ( Params::instance().target_ & Params::ctComment
&& !sourceImage->comment().empty()) {
diff --git a/src/actions.hpp b/src/actions.hpp
index ad4e279e..f19a9cf2 100644
--- a/src/actions.hpp
+++ b/src/actions.hpp
@@ -303,9 +303,12 @@ namespace Action {
virtual int run(const std::string& path);
typedef std::auto_ptr AutoPtr;
AutoPtr clone() const;
+ Modify() {}
private:
virtual Modify* clone_() const;
+ //! Copy contructor needed because of AutoPtr memeber
+ Modify(const Modify& src) {}
//! Add a metadatum according to \em modifyCmd
void addMetadatum(const ModifyCmd& modifyCmd);
@@ -314,8 +317,7 @@ namespace Action {
//! Delete a metadatum according to \em modifyCmd
void delMetadatum(const ModifyCmd& modifyCmd);
- Exiv2::ExifData exifData_; //!< Exif metadata
- Exiv2::IptcData iptcData_; //!< Iptc metadata
+ Exiv2::Image::AutoPtr image_; //!< Image to modify
}; // class Modify
} // namespace Action
diff --git a/src/addmoddel.cpp b/src/addmoddel.cpp
index 68036d41..aef33d3c 100644
--- a/src/addmoddel.cpp
+++ b/src/addmoddel.cpp
@@ -2,6 +2,7 @@
// addmoddel.cpp, $Rev$
// Sample program showing how to add, modify and delete Exif metadata.
+#include "image.hpp"
#include "exif.hpp"
#include
#include
@@ -14,7 +15,9 @@ try {
}
std::string file(argv[1]);
- // Container for all metadata
+ // Container for exif metadata. This is an example of creating
+ // exif metadata from scratch. If you want to add, modify, delete
+ // metadata that exists in an image, start with ImageFactory::open
Exiv2::ExifData exifData;
// *************************************************************************
@@ -90,10 +93,18 @@ try {
std::cout << "Deleted key \"" << key << "\"\n";
// *************************************************************************
- // Finally, write the remaining Exif data to an image file
- int rc = exifData.write(file);
+ // Finally, write the remaining Exif data to the image file
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
+ if (image.get() == 0) {
+ std::string error(file);
+ error += " : Could not read file or unknown image type";
+ throw Exiv2::Error(error);
+ }
+
+ image->setExifData(exifData);
+ int rc = image->writeMetadata();
if (rc) {
- std::string error = Exiv2::ExifData::strError(rc, file);
+ std::string error = Exiv2::Image::strError(rc, file);
throw Exiv2::Error(error);
}
diff --git a/src/basicio.cpp b/src/basicio.cpp
new file mode 100644
index 00000000..bf43123c
--- /dev/null
+++ b/src/basicio.cpp
@@ -0,0 +1,427 @@
+// ***************************************************************** -*- C++ -*-
+/*
+ * Copyright (C) 2004 Andreas Huggel
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+/*
+ File: basicio.cpp
+ Version: $Rev$
+ Author(s): Brad Schick (brad)
+ History: 04-Dec-04, brad: created
+ */
+// *****************************************************************************
+#include "rcsid.hpp"
+EXIV2_RCSID("@(#) $Id$");
+
+// Define DEBUG_MAKERNOTE to output debug information to std::cerr
+#undef DEBUG_MAKERNOTE
+
+// *****************************************************************************
+// included header files
+#include "basicio.hpp"
+#include "types.hpp"
+
+// + standard includes
+#include
+#include // for stat()
+#include // for stat()
+#ifdef HAVE_PROCESS_H
+# include
+#endif
+#ifdef HAVE_UNISTD_H
+# include // for getpid, stat
+#endif
+
+// *****************************************************************************
+// class member definitions
+namespace Exiv2 {
+
+ FileIo::FileIo(const std::string& path) :
+ path_(path), fp_(0), opMode_(opSeek)
+ {
+ }
+
+ FileIo::~FileIo()
+ {
+ close();
+ }
+
+ BasicIo::AutoPtr FileIo::temporary() const
+ {
+ BasicIo::AutoPtr basicIo;
+
+ struct stat buf;
+ int ret = stat(path_.c_str(), &buf);
+
+ // If file is > 1MB then use a file, otherwise use memory buffer
+ if (buf.st_size > 1048576 || ret != 0) {
+ pid_t pid = getpid();
+ std::string tmpname = path_ + toString(pid);
+ FileIo *fileIo = new FileIo(tmpname);
+ if (fileIo->open("w+b") != 0 ) {
+ delete fileIo;
+ }
+ else {
+ basicIo.reset(fileIo);
+ }
+ }
+ else {
+ basicIo.reset(new MemIo);
+ }
+
+ return basicIo;
+ }
+
+ long FileIo::write(const byte* data, long wcount )
+ {
+ assert(fp_ != 0);
+
+ // ANSI C requires a flush or seek when switching
+ // between read and write modes.
+ if (opMode_ == opRead) {
+ // on msvcrt fflush does not do the job
+ fseek(fp_, 0, SEEK_CUR);
+ }
+ opMode_ = opWrite;
+ return (long)fwrite(data, 1, wcount, fp_);
+ }
+
+ long FileIo::write(BasicIo& src)
+ {
+ assert(fp_ != 0);
+ if (static_cast(this)==&src) return 0;
+ if (!src.isopen()) return 0;
+
+ // ANSI C requires a flush or seek when switching
+ // between read and write modes.
+ if (opMode_ == opRead) {
+ // on msvcrt fflush does not do the job
+ fseek(fp_, 0, SEEK_CUR);
+ }
+ opMode_ = opWrite;
+
+ byte buf[4096];
+ long readCount = 0;
+ long writeCount = 0;
+ long writeTotal = 0;
+ while ((readCount = src.read(buf, sizeof(buf)))) {
+ writeTotal += writeCount = (long)fwrite(buf, 1, readCount, fp_);
+ if (writeCount != readCount) {
+ // try to reset back to where write stopped
+ src.seek(writeCount-readCount, BasicIo::cur);
+ break;
+ }
+ }
+
+ return writeTotal;
+ }
+
+ int FileIo::transfer(BasicIo& src)
+ {
+ const bool wasOpen = (fp_ != 0);
+ const std::string lastMode(openMode_);
+
+ FileIo *fileIo = dynamic_cast(&src);
+ if (fileIo) {
+ // Optimization if this is another instance of FileIo
+ close();
+ fileIo->close();
+ // MSVCRT rename that does not overwrite existing files
+ if (remove(path_.c_str()) != 0) return -4;
+ if (rename(fileIo->path_.c_str(), path_.c_str()) == -1) return -4;
+ remove(fileIo->path_.c_str());
+ }
+ else{
+ // Generic handling, reopen both to reset to start
+ open("w+b");
+ if (src.open() !=0) return 1;
+ write(src);
+ src.close();
+ }
+
+ if (wasOpen) open(lastMode);
+ else close();
+
+ return error() || src.error();
+ }
+
+ int FileIo::putb(byte data)
+ {
+ assert(fp_ != 0);
+ if (opMode_ == opRead) {
+ // on msvcrt fflush does not do the job
+ fseek(fp_, 0, SEEK_CUR);
+ }
+ opMode_ = opWrite;
+ return putc(data, fp_);
+ }
+
+ int FileIo::seek(long offset, Position pos)
+ {
+ assert(fp_ != 0);
+ int fileSeek;
+ if (pos == BasicIo::cur) {
+ fileSeek = SEEK_CUR;
+ }
+ else if (pos == BasicIo::beg) {
+ fileSeek = SEEK_SET;
+ }
+ else {
+ assert(pos == BasicIo::end);
+ fileSeek = SEEK_END;
+ }
+
+ opMode_ = opSeek;
+ return fseek(fp_, offset, fileSeek);
+ }
+
+ long FileIo::tell() const
+ {
+ assert(fp_ != 0);
+ return ftell(fp_);
+ }
+
+ int FileIo::open()
+ {
+ // Default open is in read-write binary mode
+ return open("r+b");
+ }
+
+ int FileIo::open(const std::string& mode)
+ {
+ if (fp_ != 0) {
+ fclose(fp_);
+ }
+
+ openMode_ = mode;
+ opMode_ = opSeek;
+ fp_ = fopen(path_.c_str(), mode.c_str());
+ if (!fp_) return 1;
+ return 0;
+ }
+
+ bool FileIo::isopen() const
+ {
+ return fp_ != 0;
+ }
+
+ int FileIo::close()
+ {
+ if (fp_ != 0) {
+ fclose(fp_);
+ fp_= 0;
+ }
+ return 0;
+ }
+
+ DataBuf FileIo::read(long rcount)
+ {
+ assert(fp_ != 0);
+ DataBuf buf(rcount);
+ long readCount = read(buf.pData_, buf.size_);
+ buf.size_ = readCount;
+ return buf;
+ }
+
+ long FileIo::read(byte* buf, long rcount)
+ {
+ assert(fp_ != 0);
+
+ if (opMode_ == opWrite) {
+ // on msvcrt fflush does not do the job
+ fseek(fp_, 0, SEEK_CUR);
+ }
+ opMode_ = opRead;
+ return (long)fread(buf, 1, rcount, fp_);
+ }
+
+ int FileIo::getb()
+ {
+ assert(fp_ != 0);
+
+ if (opMode_ == opWrite) {
+ // on msvcrt fflush does not do the job
+ fseek(fp_, 0, SEEK_CUR);
+ }
+ opMode_ = opRead;
+ return getc(fp_);
+ }
+
+ int FileIo::error() const
+ {
+ return fp_ != 0 ? ferror(fp_) : 0;
+ }
+
+ bool FileIo::eof() const
+ {
+ assert(fp_ != 0);
+ return feof(fp_) != 0;
+ }
+
+
+ MemIo::MemIo(const byte* data, long size)
+ {
+ // If copying data is too slow it might be worth
+ // creating a readonly MemIo variant
+ data_.reserve(size);
+ data_.assign(data, data+size);
+ idx_ = 0;
+ }
+
+ BasicIo::AutoPtr MemIo::temporary() const
+ {
+ return BasicIo::AutoPtr(new MemIo);
+ }
+
+ void MemIo::checkSize(long wcount)
+ {
+ ByteVector::size_type need = wcount + idx_;
+ if (need > data_.size()) {
+ data_.resize(need);
+ }
+ }
+
+ long MemIo::write(const byte* data, long wcount )
+ {
+ checkSize(wcount);
+ // According to Josuttis 6.2.3 this is safe
+ memcpy(&data_[idx_], data, wcount);
+ idx_ += wcount;
+ return wcount;
+ }
+
+ int MemIo::transfer(BasicIo& src)
+ {
+ MemIo *memIo = dynamic_cast(&src);
+ if (memIo) {
+ // Optimization if this is another instance of MemIo
+ data_.swap(memIo->data_);
+ idx_ = 0;
+ }
+ else{
+ // Generic reopen to reset position to start
+ data_.clear();
+ idx_ = 0;
+ if (src.open() != 0) return 1;
+ write(src);
+ src.close();
+ }
+ return error() || src.error();
+ }
+
+ long MemIo::write(BasicIo& src)
+ {
+ if (static_cast(this)==&src) return 0;
+ if (!src.isopen()) return 0;
+
+ byte buf[4096];
+ long readCount = 0;
+ long writeTotal = 0;
+ while ((readCount = src.read(buf, sizeof(buf)))) {
+ write(buf, readCount);
+ writeTotal += readCount;
+ }
+
+ return writeTotal;
+ }
+
+ int MemIo::putb(byte data)
+ {
+ checkSize(1);
+ data_[idx_++] = data;
+ return data;
+ }
+
+ int MemIo::seek(long offset, Position pos)
+ {
+ ByteVector::size_type newIdx;
+
+ if (pos == BasicIo::cur ) {
+ newIdx = idx_ + offset;
+ }
+ else if (pos == BasicIo::beg) {
+ newIdx = offset;
+ }
+ else {
+ assert(pos == BasicIo::end);
+ newIdx = data_.size() + offset;
+ }
+
+ if (newIdx < 0 || newIdx > data_.size()) return 1;
+ idx_ = newIdx;
+ return 0;
+ }
+
+ long MemIo::tell() const
+ {
+ return (long)idx_;
+ }
+
+ int MemIo::open()
+ {
+ idx_ = 0;
+ return 0;
+ }
+
+ bool MemIo::isopen() const
+ {
+ return true;
+ }
+
+ int MemIo::close()
+ {
+ return 0;
+ }
+
+ DataBuf MemIo::read(long rcount)
+ {
+ DataBuf buf(rcount);
+ long readCount = read(buf.pData_, buf.size_);
+ buf.size_ = readCount;
+ return buf;
+ }
+
+ long MemIo::read(byte* buf, long rcount)
+ {
+ long avail = (long)(data_.size() - idx_);
+ long allow = std::min(rcount, avail);
+
+ // According to Josuttis 6.2.3 this is safe
+ memcpy(buf, &data_[idx_], allow);
+ idx_ += allow;
+ return allow;
+ }
+
+ int MemIo::getb()
+ {
+ if (idx_ == data_.size())
+ return EOF;
+ return data_[idx_++];
+ }
+
+ int MemIo::error() const
+ {
+ return 0;
+ }
+
+ bool MemIo::eof() const
+ {
+ return idx_ == data_.size();
+ }
+
+} // namespace Exiv2
diff --git a/src/basicio.hpp b/src/basicio.hpp
new file mode 100644
index 00000000..47385764
--- /dev/null
+++ b/src/basicio.hpp
@@ -0,0 +1,605 @@
+// ***************************************************************** -*- C++ -*-
+/*
+ * Copyright (C) 2004 Andreas Huggel
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+/*!
+ @file basicio.hpp
+ @brief Simple binary IO abstraction
+ @version $Rev$
+ @author Brad Schick (brad)
+ brad@robotbattle.com
+ @date 04-Dec-04, brad: created
+ */
+#ifndef BASICIO_HPP_
+#define BASICIO_HPP_
+
+// *****************************************************************************
+// included header files
+#include "types.hpp"
+#include "error.hpp"
+
+// + standard includes
+#include
+#include
+#include
+
+// *****************************************************************************
+// namespace extensions
+namespace Exiv2 {
+
+// *****************************************************************************
+// class definitions
+
+ /*!
+ @brief An interface for simple binary IO.
+
+ Designed to have semantics
+ and names similar to those of C style FILE* operations. Subclasses
+ should all behave the same so that they can be interchanged.
+ */
+ class BasicIo
+ {
+ public:
+ //! BasicIo auto_ptr type
+ typedef std::auto_ptr AutoPtr;
+
+ //! Seek starting positions
+ enum Position { beg, cur, end };
+
+ //! @name Creators
+ //@{
+ //! Destructor
+ virtual ~BasicIo() {}
+ //@}
+
+ //! @name Manipulators
+ //@{
+ /*!
+ @brief Open the IO source using the default access mode. The
+ default mode should allow for reading and writing.
+
+ This method can also be used to "reopen" an IO source which will
+ flush any unwritten data and reset the IO position to the start.
+ Subclasses may provide custom methods to allow for
+ opening IO sources differently.
+
+ @return 0 if successful;
+ Nonzero if failure;
+ */
+ virtual int open() = 0;
+ /*!
+ @brief Close the IO source. After closing a BasicIo instance can not
+ be read or written. Closing flushes any unwritten data. It is
+ safe to call close on a closed instance.
+ @return 0 if successful;
+ Nonzero if failure;
+ */
+ virtual int close() = 0;
+ /*!
+ @brief Write data to the IO source. Current IO position is advanced
+ by the number of bytes written.
+ @param data Pointer to data. Data must be at least \em wcount
+ bytes long
+ @param wcount Number of bytes to be written.
+ @return Number of bytes written to IO source successfully;
+ 0 if failure;
+ */
+ virtual long write(const byte* data, long wcount) = 0;
+ /*!
+ @brief Write data that is read from another BasicIo instance to
+ the IO source. Current IO position is advanced by the number
+ of bytes written.
+ @param src Reference to another BasicIo instance. Reading start
+ at the source's current IO position
+ @return Number of bytes written to IO source successfully;
+ 0 if failure;
+ */
+ virtual long write(BasicIo& src) = 0;
+ /*!
+ @brief Write one byte to the IO source. Current IO position is
+ advanced by one byte.
+ @param data The single byte to be written.
+ @return The value of the byte written if successful;
+ EOF if failure;
+ */
+ virtual int putb(byte data) = 0;
+ /*!
+ @brief Read data from the IO source. Reading starts at the current
+ IO position and the position is advanced by the number of bytes
+ read.
+ @param rcount Maximum number of bytes to read. Fewer bytes may be
+ read if \em rcount bytes are not available.
+ @return DataBuf instance containing the bytes read. Use the
+ DataBuf::size_ member to find the number of bytes read.
+ DataBuf::size_ will be 0 on failure.
+ */
+ virtual DataBuf read(long rcount) = 0;
+ /*!
+ @brief Read data from the IO source. Reading starts at the current
+ IO position and the position is advanced by the number of bytes
+ read.
+ @param buf Pointer to a block of memory into which the read data
+ is stored. The memory block must be at least \em rcount bytes
+ long.
+ @param rcount Maximum number of bytes to read. Fewer bytes may be
+ read if \em rcount bytes are not available.
+ @return Number of bytes read from IO source successfully;
+ 0 if failure;
+ */
+ virtual long read(byte *buf, long rcount) = 0;
+ /*!
+ @brief Read one byte from the IO source. Current IO position is
+ advanced by one byte.
+ @return The byte read from the IO source if successful;
+ EOF if failure;
+ */
+ virtual int getb() = 0;
+ /*!
+ @brief Remove all data from this object's IO source and then transfer
+ data from the \em src BasicIo object into this object.
+
+ The source object is invalidated by this operation and should not be
+ used after this method returns. This method exists primarily to
+ be used with the BasicIo::temporary() method.
+
+ @param src Reference to another BasicIo instance. The entire contents
+ of src are transferred to this object. The \em src object is
+ invalidated by the method.
+ @return 0 if successful;
+ Nonzero if failure;
+ */
+ virtual int transfer(BasicIo& src) = 0;
+ /*!
+ @brief Move the current IO position.
+ @param offset Number of bytes to move the position relative
+ to the starting position specified by \em pos
+ @param pos Position from which the seek should start
+ @return 0 if successful;
+ Nonzero if failure;
+ */
+ virtual int seek(long offset, Position pos) = 0;
+ //@}
+
+ //! @name Accessors
+ //@{
+ /*!
+ @brief Get the current IO position.
+ @return Offset from the start of IO if successful;
+ -l if failure;
+ */
+ virtual long tell() const = 0;
+ //!Returns true if the IO source is open, otherwise false.
+ virtual bool isopen() const = 0;
+ //!Returns 0 if the IO source is in a valid state, otherwise nonzero.
+ virtual int error() const = 0;
+ //!Returns true if the IO position has reach the end, otherwise false.
+ virtual bool eof() const = 0;
+ /*!
+ @brief Returns a temporary data storage location. This is often
+ needed to rewrite an IO source.
+
+ For example, data may be read from the original IO source, modified
+ in some way, and then saved to the temporary instance. After the
+ operation is complete, the BasicIo::transfer method can be used to
+ replace the original IO source with the modified version. Subclasses
+ are free to return any class that derives from BasicIo.
+
+ @return An instance of BasicIo on success;
+ Null pointer on failure;
+ */
+ virtual BasicIo::AutoPtr temporary() const = 0;
+ //@}
+
+ protected:
+ //! @name Creators
+ //@{
+ //! Default Constructor
+ BasicIo() {}
+ //@}
+ }; // class BasicIo
+
+ /*!
+ @brief Utility class that closes a BasicIo instance upon destruction.
+ Meant to be used as a stack variable in functions that need to
+ ensure BasicIo instances get closed. Useful when functions return
+ errors from many locations.
+ */
+ class IoCloser {
+ public:
+ //! @name Creators
+ //@{
+ //! Constructor, takes a BasicIo reference
+ IoCloser(BasicIo &bio) : bio_(bio) {}
+ //! Destructor, closes the BasicIo reference
+ ~IoCloser() { close(); }
+ //@}
+
+ //! @name Manipulators
+ //@{
+ //! Close the BasicIo if it is open
+ void close() { if (bio_.isopen()) bio_.close(); }
+ //@}
+
+ // DATA
+ //! The BasicIo reference
+ BasicIo &bio_;
+ private:
+ // Not implemented
+ //! Copy constructor
+ IoCloser(const IoCloser&);
+ //! Assignment operator
+ IoCloser& operator=(const IoCloser&);
+ }; // class IoCloser
+
+
+ /*!
+ @brief Provides binary file IO by implementing the BasicIo
+ interface.
+ */
+ class FileIo : public BasicIo
+ {
+ public:
+ //! @name Creators
+ //@{
+ /*!
+ @brief Constructor that accepts the file path on which IO will be
+ performed. The constructor does not open the file, and
+ therefore never failes.
+ @param path The full path of a file
+ */
+ FileIo(const std::string& path);
+ //! Destructor. Flushes and closes an open file.
+ virtual ~FileIo();
+ //@}
+
+ //! @name Manipulators
+ //@{
+ /*!
+ @brief Open the file using using the specified mode.
+
+ This method can also be used to "reopen" a file which will flush any
+ unwritten data and reset the IO position to the start. Although
+ files can be opened in binary or text mode, this class has
+ only been tested carefully in binary mode.
+
+ @param mode Specified that type of access allowed on the file.
+ Valid values match those of the C fopen command exactly.
+ @return 0 if successful;
+ Nonzero if failure;
+ */
+ int open( const std::string& mode);
+ /*!
+ @brief Open the file using using the default access mode of "r+b".
+ This method can also be used to "reopen" a file which will flush
+ any unwritten data and reset the IO position to the start.
+ @return 0 if successful;
+ Nonzero if failure;
+ */
+ virtual int open();
+ /*!
+ @brief Flush and unwritten data and close the file . It is
+ safe to call close on an already closed instance.
+ @return 0 if successful;
+ Nonzero if failure;
+ */
+ virtual int close();
+ /*!
+ @brief Write data to the file. The file position is advanced
+ by the number of bytes written.
+ @param data Pointer to data. Data must be at least \em wcount
+ bytes long
+ @param wcount Number of bytes to be written.
+ @return Number of bytes written to the file successfully;
+ 0 if failure;
+ */
+ virtual long write(const byte* data, long wcount);
+ /*!
+ @brief Write data that is read from another BasicIo instance to
+ the file. The file position is advanced by the number
+ of bytes written.
+ @param src Reference to another BasicIo instance. Reading start
+ at the source's current IO position
+ @return Number of bytes written to the file successfully;
+ 0 if failure;
+ */
+ virtual long write(BasicIo& src);
+ /*!
+ @brief Write one byte to the file. The file position is
+ advanced by one byte.
+ @param data The single byte to be written.
+ @return The value of the byte written if successful;
+ EOF if failure;
+ */
+ virtual int putb(byte data);
+ /*!
+ @brief Read data from the file. Reading starts at the current
+ file position and the position is advanced by the number of
+ bytes read.
+ @param rcount Maximum number of bytes to read. Fewer bytes may be
+ read if \em rcount bytes are not available.
+ @return DataBuf instance containing the bytes read. Use the
+ DataBuf::size_ member to find the number of bytes read.
+ DataBuf::size_ will be 0 on failure.
+ */
+ virtual DataBuf read(long rcount);
+ /*!
+ @brief Read data from the file. Reading starts at the current
+ file position and the position is advanced by the number of
+ bytes read.
+ @param buf Pointer to a block of memory into which the read data
+ is stored. The memory block must be at least \em rcount bytes
+ long.
+ @param rcount Maximum number of bytes to read. Fewer bytes may be
+ read if \em rcount bytes are not available.
+ @return Number of bytes read from the file successfully;
+ 0 if failure;
+ */
+ virtual long read(byte *buf, long rcount);
+ /*!
+ @brief Read one byte from the file. The file position is
+ advanced by one byte.
+ @return The byte read from the file if successful;
+ EOF if failure;
+ */
+ virtual int getb();
+ /*!
+ @brief Remove the contents of the file and then transfer data from
+ the \em src BasicIo object into the empty file.
+
+ This method is optimized to simply rename the source file if the
+ source object is another FileIo instance. The source BasicIo object
+ is invalidated by this operation and should not be used after this
+ method returns. This method exists primarily to be used with
+ the BasicIo::temporary() method.
+
+ @param src Reference to another BasicIo instance. The entire contents
+ of src are transferred to this object. The \em src object is
+ invalidated by the method.
+ @return 0 if successful;
+ Nonzero if failure;
+ */
+ virtual int transfer(BasicIo& src);
+ /*!
+ @brief Move the current file position.
+ @param offset Number of bytes to move the file position
+ relative to the starting position specified by \em pos
+ @param pos Position from which the seek should start
+ @return 0 if successful;
+ Nonzero if failure;
+ */
+ virtual int seek(long offset, Position pos);
+ //@}
+
+ //! @name Accessors
+ //@{
+ /*!
+ @brief Get the current file position.
+ @return Offset from the start of the file if successful;
+ -l if failure;
+ */
+ virtual long tell() const;
+ //!Returns true if the file is open, otherwise false.
+ virtual bool isopen() const;
+ //!Returns 0 if the file is in a valid state, otherwise nonzero.
+ virtual int error() const;
+ //!Returns true if the file position has reach the end, otherwise false.
+ virtual bool eof() const;
+ /*!
+ @brief Returns a temporary data storage location. The actual type
+ returned depends upon the size of the file represented a FileIo
+ object. For small files, a MemIo is returned while for large files
+ a FileIo is returned. Callers should not rely on this behavior,
+ however, since it may change.
+ @return An instance of BasicIo on success;
+ Null pointer on failure;
+ */
+ virtual BasicIo::AutoPtr temporary() const;
+ //@}
+
+ private:
+ // NOT IMPLEMENTED
+ //! Copy constructor
+ FileIo(FileIo& rhs);
+ //! Assignment operator
+ FileIo& operator=(const FileIo& rhs);
+
+ // Enumeration
+ enum OpMode { opRead, opWrite, opSeek };
+
+ // DATA
+ std::string path_;
+ std::string openMode_;
+ FILE *fp_;
+ OpMode opMode_;
+ }; // class FileIo
+
+ /*!
+ @brief Provides binary IO on blocks of memory by implementing the
+ BasicIo interface. The current implementation makes a copy of
+ any data passed to its constructors. If writes are performed, the
+ changed data can be retrieved using the read methods (since the
+ data used in construction is never modified).
+
+ @note If read only usage of this class is common, it might be worth
+ creating a specialized readonly class or changing this one to
+ have a readonly mode.
+ */
+ class MemIo : public BasicIo
+ {
+ public:
+ //! @name Creators
+ //@{
+ //! Default constructor that results in an empty object
+ MemIo() { idx_ = 0; }
+ /*!
+ @brief Constructor that accepts a block of memory to be copied.
+ IO operations are performed on the copied memory.
+ @param data Pointer to data. Data must be at least \em size
+ bytes long
+ @param size Number of bytes to copy.
+ */
+ MemIo(const byte* data, long size);
+ //! Destructor. Releases all managed memory
+ virtual ~MemIo() {}
+ //@}
+
+ //! @name Manipulators
+ //@{
+ /*!
+ @brief Memory IO is always open for reading and writing. This method
+ therefore only resets the IO position to the start.
+ @return 0
+ */
+ virtual int open();
+ /*!
+ @brief Does nothing on MemIo objects.
+ @return 0
+ */
+ virtual int close();
+ /*!
+ @brief Write data to the memory block. If needed, the size of the
+ internal memory block is expanded. The IO position is advanced
+ by the number of bytes written.
+ @param data Pointer to data. Data must be at least \em wcount
+ bytes long
+ @param wcount Number of bytes to be written.
+ @return Number of bytes written to the memory block successfully;
+ 0 if failure;
+ */
+ virtual long write(const byte* data, long wcount);
+ /*!
+ @brief Write data that is read from another BasicIo instance to
+ the memory block. If needed, the size of the internal memory
+ block is expanded. The IO position is advanced by the number
+ of bytes written.
+ @param src Reference to another BasicIo instance. Reading start
+ at the source's current IO position
+ @return Number of bytes written to the memory block successfully;
+ 0 if failure;
+ */
+ virtual long write(BasicIo& src);
+ /*!
+ @brief Write one byte to the memory block. The IO position is
+ advanced by one byte.
+ @param data The single byte to be written.
+ @return The value of the byte written if successful;
+ EOF if failure;
+ */
+ virtual int putb(byte data);
+ /*!
+ @brief Read data from the memory block. Reading starts at the current
+ IO position and the position is advanced by the number of
+ bytes read.
+ @param rcount Maximum number of bytes to read. Fewer bytes may be
+ read if \em rcount bytes are not available.
+ @return DataBuf instance containing the bytes read. Use the
+ DataBuf::size_ member to find the number of bytes read.
+ DataBuf::size_ will be 0 on failure.
+ */
+ virtual DataBuf read(long rcount);
+ /*!
+ @brief Read data from the memory block. Reading starts at the current
+ IO position and the position is advanced by the number of
+ bytes read.
+ @param buf Pointer to a block of memory into which the read data
+ is stored. The memory block must be at least \em rcount bytes
+ long.
+ @param rcount Maximum number of bytes to read. Fewer bytes may be
+ read if \em rcount bytes are not available.
+ @return Number of bytes read from the memory block successfully;
+ 0 if failure;
+ */
+ virtual long read(byte *buf, long rcount);
+ /*!
+ @brief Read one byte from the memory block. The IO position is
+ advanced by one byte.
+ @return The byte read from the memory block if successful;
+ EOF if failure;
+ */
+ virtual int getb();
+ /*!
+ @brief Clear the memory clock and then transfer data from
+ the \em src BasicIo object into a new block of memory.
+
+ This method is optimized to simply swap memory block if the source
+ object is another MemIo instance. The source BasicIo instance
+ is invalidated by this operation and should not be used after this
+ method returns. This method exists primarily to be used with
+ the BasicIo::temporary() method.
+
+ @param src Reference to another BasicIo instance. The entire contents
+ of src are transferred to this object. The \em src object is
+ invalidated by the method.
+ @return 0 if successful;
+ Nonzero if failure;
+ */
+ virtual int transfer(BasicIo& src);
+ /*!
+ @brief Move the current IO position.
+ @param offset Number of bytes to move the IO position
+ relative to the starting position specified by \em pos
+ @param pos Position from which the seek should start
+ @return 0 if successful;
+ Nonzero if failure;
+ */
+ virtual int seek(long offset, Position pos);
+ //@}
+
+ //! @name Accessors
+ //@{
+ /*!
+ @brief Get the current IO position.
+ @return Offset from the start of the memory block
+ */
+ virtual long tell() const;
+ //!Always returns true
+ virtual bool isopen() const;
+ //!Always returns 0
+ virtual int error() const;
+ //!Returns true if the IO position has reach the end, otherwise false.
+ virtual bool eof() const;
+ /*!
+ @brief Returns a temporary data storage location. Currently returns
+ an empty MemIo object, but callers should not rely on this
+ behavior since it may change.
+ @return An instance of BasicIo on success;
+ Null pointer on failure;
+ */
+ virtual BasicIo::AutoPtr temporary() const;
+ //@}
+ private:
+ // NOT IMPLEMENTED
+ //! Copy constructor
+ MemIo(MemIo& rhs);
+ //! Assignment operator
+ MemIo& operator=(const MemIo& rhs);
+
+ // Typedefs
+ typedef std::vector ByteVector;
+
+ // DATA
+ ByteVector data_;
+ ByteVector::size_type idx_;
+
+ //METHODS
+ void checkSize(long wcount);
+ }; // class MemIo
+} // namespace Exiv2
+
+#endif // #ifndef BASICIO_HPP_
diff --git a/src/dataarea-test.cpp b/src/dataarea-test.cpp
index df46f560..23529789 100644
--- a/src/dataarea-test.cpp
+++ b/src/dataarea-test.cpp
@@ -78,22 +78,30 @@ catch (Exiv2::Error& e) {
void write(const std::string& file, Exiv2::ExifData& ed)
{
- int rc = ed.writeExifData(file);
+ Image::AutoPtr image = ImageFactory::create(Image::exv, file);
+ assert(image.get() != 0);
+
+ image->setExifData(ed);
+ int rc = image->writeMetadata();
if (rc) {
- std::string error = Exiv2::ExifData::strError(rc, file);
+ std::string error = Exiv2::Image::strError(rc, file);
throw Exiv2::Error(error);
}
}
void print(const std::string& file)
{
- Exiv2::ExifData ed;
- int rc = ed.read(file);
+ Image::AutoPtr image = ImageFactory::open(file);
+ assert(image.get() != 0);
+
+ // Load existing metadata
+ int rc = image->readMetadata();
if (rc) {
- std::string error = Exiv2::ExifData::strError(rc, file);
+ std::string error = Exiv2::Image::strError(rc, file);
throw Exiv2::Error(error);
}
+ Exiv2::ExifData &ed = image->exifData();
Exiv2::ExifData::const_iterator end = ed.end();
for (Exiv2::ExifData::const_iterator i = ed.begin(); i != end; ++i) {
std::cout << std::setw(35) << std::setfill(' ') << std::left
@@ -109,29 +117,28 @@ void print(const std::string& file)
<< i->count() << " "
<< std::dec << i->value()
<< "\n";
-
}
}
int read(const std::string& path)
{
- Image::AutoPtr image = ImageFactory::instance().open(path);
+ Image::AutoPtr image = ImageFactory::open(path);
assert(image.get() != 0);
int rc = image->readMetadata();
if (rc) return rc;
- if (image->sizeExifData() > 0) {
- const byte *pData = image->exifData();
- long size = image->sizeExifData();
+ if (image->exifData().count() > 0) {
+ DataBuf exifData = image->exifData().copy();
+ long size = exifData.size_;
// Read the TIFF header
TiffHeader tiffHeader;
- rc = tiffHeader.read(pData);
+ rc = tiffHeader.read(exifData.pData_);
if (rc) return rc;
// Read IFD0
Ifd ifd0(ifd0Id);
- rc = ifd0.read(pData + tiffHeader.offset(),
+ rc = ifd0.read(exifData.pData_ + tiffHeader.offset(),
size - tiffHeader.offset(),
tiffHeader.byteOrder(),
tiffHeader.offset());
@@ -143,7 +150,7 @@ int read(const std::string& path)
Value::AutoPtr v = Value::create(TypeId(i->type()));
v->read(i->data(), i->count() * i->typeSize(), tiffHeader.byteOrder());
- v->setDataArea(pData + v->toLong(), 32);
+ v->setDataArea(exifData.pData_ + v->toLong(), 32);
std::cout << "Value of tag 0x8298: " << std::hex;
v->write(std::cout);
@@ -162,7 +169,7 @@ int read(const std::string& path)
v = Value::create(TypeId(i->type()));
v->read(i->data(), i->count() * i->typeSize(), tiffHeader.byteOrder());
- v->setDataArea(pData + v->toLong(), 16);
+ v->setDataArea(exifData.pData_ + v->toLong(), 16);
std::cout << "Value of tag 0x013b: ";
v->write(std::cout);
diff --git a/src/exif.cpp b/src/exif.cpp
index 0424036b..712f696c 100644
--- a/src/exif.cpp
+++ b/src/exif.cpp
@@ -36,11 +36,12 @@ EXIV2_RCSID("@(#) $Id$");
// included header files
#include "exif.hpp"
#include "types.hpp"
+#include "basicio.hpp"
#include "error.hpp"
#include "value.hpp"
#include "ifd.hpp"
#include "tags.hpp"
-#include "image.hpp"
+#include "jpgimage.hpp"
#include "makernote.hpp"
// + standard includes
@@ -355,28 +356,7 @@ namespace Exiv2 {
return *pos;
}
- int ExifData::read(const std::string& path)
- {
- if (!fileExists(path, true)) return -1;
- Image::AutoPtr image = ImageFactory::instance().open(path);
- if (image.get() == 0) {
- // We don't know this type of file
- return -2;
- }
-
- int rc = image->readMetadata();
- if (rc == 0) {
- if (image->sizeExifData() > 0) {
- rc = read(image->exifData(), image->sizeExifData());
- }
- else {
- rc = 3;
- }
- }
- return rc;
- }
-
- int ExifData::read(const byte* buf, long len)
+ int ExifData::load(const byte* buf, long len)
{
// Copy the data buffer
delete[] pData_;
@@ -476,38 +456,6 @@ namespace Exiv2 {
return ret;
} // ExifData::read
- int ExifData::erase(const std::string& path) const
- {
- if (!fileExists(path, true)) return -1;
- Image::AutoPtr image = ImageFactory::instance().open(path);
- if (image.get() == 0) return -2;
-
- // Read all metadata then erase only Exif data
- int rc = image->readMetadata();
- if (rc == 0) {
- image->clearExifData();
- rc = image->writeMetadata();
- }
- return rc;
- } // ExifData::erase
-
- int ExifData::write(const std::string& path)
- {
- // Remove the Exif section from the file if there is no metadata
- if (count() == 0) return erase(path);
-
- if (!fileExists(path, true)) return -1;
- Image::AutoPtr image = ImageFactory::instance().open(path);
- if (image.get() == 0) return -2;
- DataBuf buf(copy());
- // Read all metadata to preserve non-Exif data
- int rc = image->readMetadata();
- if (rc == 0) {
- image->setExifData(buf.pData_, buf.size_);
- rc = image->writeMetadata();
- }
- return rc;
- } // ExifData::write
DataBuf ExifData::copy()
{
@@ -660,15 +608,6 @@ namespace Exiv2 {
return buf;
} // ExifData::copyFromMetadata
- int ExifData::writeExifData(const std::string& path)
- {
- DataBuf buf(copy());
- ExvImage exvImage(path, true);
- if (!exvImage.good()) return -1;
- exvImage.setExifData(buf.pData_, buf.size_);
- return exvImage.writeMetadata();
- } // ExifData::writeExifData
-
void ExifData::add(Entries::const_iterator begin,
Entries::const_iterator end,
ByteOrder byteOrder)
@@ -846,11 +785,11 @@ namespace Exiv2 {
if (thumbnail.get() == 0) return 8;
std::string name = path + thumbnail->extension();
- FileCloser file(fopen(name.c_str(), "wb"));
- if (!file.fp_) return -1;
+ FileIo file(name);
+ if (file.open("wb") != 0) return -1;
DataBuf buf(thumbnail->copy(*this));
- if (fwrite(buf.pData_, 1, buf.size_, file.fp_) != (size_t)buf.size_) {
+ if (file.write(buf.pData_, buf.size_) != buf.size_) {
return 4;
}
return 0;
@@ -1197,14 +1136,14 @@ namespace {
Exiv2::DataBuf readFile(const std::string& path)
{
- Exiv2::FileCloser file(fopen(path.c_str(), "rb"));
- if (!file.fp_)
+ Exiv2::FileIo file(path);
+ if (file.open("rb") != 0)
throw Exiv2::Error("Couldn't open input file");
struct stat st;
if (0 != stat(path.c_str(), &st))
throw Exiv2::Error("Couldn't stat input file");
Exiv2::DataBuf buf(st.st_size);
- long len = (long)fread(buf.pData_, 1, buf.size_, file.fp_);
+ long len = file.read(buf.pData_, buf.size_);
if (len != buf.size_)
throw Exiv2::Error("Couldn't read input file");
return buf;
diff --git a/src/exif.hpp b/src/exif.hpp
index ec632dd0..6fcea025 100644
--- a/src/exif.hpp
+++ b/src/exif.hpp
@@ -480,9 +480,6 @@ namespace Exiv2 {
- extract and delete Exif thumbnail (JPEG and TIFF thumbnails)
*/
class ExifData {
- //! @name Not implemented
- //@{
- //@}
public:
//! ExifMetadata iterator type
typedef ExifMetadata::iterator iterator;
@@ -504,43 +501,13 @@ namespace Exiv2 {
//! Assignment operator (Todo: assign image data also)
ExifData& operator=(const ExifData& rhs);
/*!
- @brief Read the Exif data from file \em path.
- @param path Path to the file
- @return 0 if successful;
- 3 if the file contains no Exif data;
- the return code of Image::readMetadata()
- if the call to this function fails
- the return code of read(const char* buf, long len)
- if the call to this function fails
- */
- int read(const std::string& path);
- /*!
- @brief Read the Exif data from a byte buffer. The data buffer
+ @brief Load the Exif data from a byte buffer. The data buffer
must start with the TIFF header.
@param buf Pointer to the data buffer to read from
@param len Number of bytes in the data buffer
@return 0 if successful.
*/
- int read(const byte* buf, long len);
- /*!
- @brief Write the Exif data to file \em path. If an Exif data section
- already exists in the file, it is replaced. If there is no
- metadata and no thumbnail to write, the Exif data section is
- deleted from the file. Otherwise, an Exif data section is
- created. See copy(byte* buf) for further details.
-
- @return 0 if successful.
- */
- int write(const std::string& path);
- /*!
- @brief Write the Exif data to a binary file. By convention, the
- filename extension should be ".exv". This file format contains
- the Exif data as it is found in a JPEG file, starting with the
- APP1 marker 0xffe1, the size of the data and the string
- "Exif\0\0". Exv files can be read with
- int read(const std::string& path) just like image Exif data.
- */
- int writeExifData(const std::string& path);
+ int load(const byte* buf, long len);
/*!
@brief Write the Exif data to a data buffer, which is returned. The
caller owns this copy and %DataBuf ensures that it will be
@@ -603,6 +570,11 @@ namespace Exiv2 {
by this call.
*/
iterator erase(iterator pos);
+ /*!
+ @brief Delete all Exifdatum instances resulting in an empty container.
+ Note that this also removes thumbnails.
+ */
+ void clear() { eraseThumbnail(); exifMetadata_.clear(); }
//! Sort metadata by key
void sortByKey();
//! Sort metadata by tag
@@ -709,12 +681,6 @@ namespace Exiv2 {
//! @name Accessors
//@{
- /*!
- @brief Erase the Exif data section from file \em path.
- @param path Path to the file.
- @return 0 if successful.
- */
- int erase(const std::string& path) const;
//! Begin of the metadata
const_iterator begin() const { return exifMetadata_.begin(); }
//! End of the metadata
@@ -780,14 +746,13 @@ namespace Exiv2 {
/*!
@brief Convert the return code \em rc from \n
- int read(const std::string& path); \n
- int write(const std::string& path); \n
- int writeExifData(const std::string& path); \n
- int writeThumbnail(const std::string& path) const; and \n
- int erase(const std::string& path) const \n
+ int read(const byte* buf, long len), \n
into an error message.
+ @param rc Error code.
+ @param path %Image file or other identifying string.
+ @return String containing error message.
- Todo: Implement global handling of error messages
+ Todo: Implement global handling of error messages
*/
static std::string strError(int rc, const std::string& path);
diff --git a/src/exifcomment.cpp b/src/exifcomment.cpp
index 2bf8862f..a3562ac2 100644
--- a/src/exifcomment.cpp
+++ b/src/exifcomment.cpp
@@ -9,6 +9,7 @@
*/
// *****************************************************************************
// included header files
+#include "image.hpp"
#include "exif.hpp"
#include
#include
@@ -24,13 +25,22 @@ try {
return 1;
}
- Exiv2::ExifData exifData;
- int rc = exifData.read(argv[1]);
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]);
+ if (image.get() == 0) {
+ std::string error(argv[1]);
+ error += " : Could not read file or unknown image type";
+ throw Exiv2::Error(error);
+ }
+
+ // Load existing metadata
+ int rc = image->readMetadata();
if (rc) {
- std::string error = Exiv2::ExifData::strError(rc, argv[1]);
+ std::string error = Exiv2::Image::strError(rc, argv[1]);
throw Exiv2::Error(error);
}
+ Exiv2::ExifData &exifData = image->exifData();
+
/*
There are two pitfalls that we need to consider when setting the Exif user
comment (Exif.Photo.UserComment) of an image:
@@ -76,13 +86,13 @@ try {
// output operator to print the formatted value
std::cout << "Writing user comment '" << *pos << "' back to the image\n";
- rc = exifData.write(argv[1]);
+ rc = image->writeMetadata();
if (rc) {
- std::string error = Exiv2::ExifData::strError(rc, argv[1]);
+ std::string error = Exiv2::Image::strError(rc, argv[1]);
throw Exiv2::Error(error);
}
- return rc;
+ return rc;
}
catch (Exiv2::Error& e) {
std::cout << "Caught Exiv2 exception '" << e << "'\n";
diff --git a/src/exifprint.cpp b/src/exifprint.cpp
index 034392db..3a0809f1 100644
--- a/src/exifprint.cpp
+++ b/src/exifprint.cpp
@@ -2,6 +2,7 @@
// exifprint.cpp, $Rev$
// Sample program to print the Exif metadata of an image
+#include "image.hpp"
#include "exif.hpp"
#include
#include
@@ -14,13 +15,21 @@ try {
return 1;
}
- Exiv2::ExifData exifData;
- int rc = exifData.read(argv[1]);
+ Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]);
+ if (image.get() == 0) {
+ std::string error(argv[1]);
+ error += " : Could not read file or unknown image type";
+ throw Exiv2::Error(error);
+ }
+
+ // Load existing metadata
+ int rc = image->readMetadata();
if (rc) {
- std::string error = Exiv2::ExifData::strError(rc, argv[1]);
+ std::string error = Exiv2::Image::strError(rc, argv[1]);
throw Exiv2::Error(error);
}
+ Exiv2::ExifData &exifData = image->exifData();
Exiv2::ExifData::const_iterator end = exifData.end();
for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
std::cout << std::setw(53) << std::setfill(' ') << std::left
diff --git a/src/image.cpp b/src/image.cpp
index c369ac83..084847e5 100644
--- a/src/image.cpp
+++ b/src/image.cpp
@@ -26,6 +26,7 @@
History: 26-Jan-04, ahu: created
11-Feb-04, ahu: isolated as a component
19-Jul-04, brad: revamped to be more flexible and support Iptc
+ 15-Jan-05, brad: inside-out design changes
*/
// *****************************************************************************
#include "rcsid.hpp"
@@ -42,9 +43,11 @@ EXIV2_RCSID("@(#) $Id$");
#endif
#include "image.hpp"
-#include "types.hpp"
#include "error.hpp"
+// Ensure registration with factory
+#include "jpgimage.hpp"
+
// + standard includes
#include
#include
@@ -55,71 +58,54 @@ EXIV2_RCSID("@(#) $Id$");
#ifdef _MSC_VER
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
-#ifdef HAVE_PROCESS_H
-# include
-#endif
#ifdef HAVE_UNISTD_H
-# include // for getpid, stat
+# include // stat
#endif
// *****************************************************************************
// class member definitions
namespace Exiv2 {
- // Local functions. These could be static private functions on Image
- // subclasses but then ImageFactory needs to be made a friend.
- /*!
- @brief Create a new ExvImage instance and return an auto-pointer to it.
- Caller owns the returned object and the auto-pointer ensures that
- it will be deleted.
- */
- Image::AutoPtr newExvInstance(const std::string& path, bool create);
- //! Check if the file ifp is an EXV file.
- bool isExvType(FILE* ifp, bool advance);
- /*!
- @brief Create a new JpegImage instance and return an auto-pointer to it.
- Caller owns the returned object and the auto-pointer ensures that
- it will be deleted.
- */
- Image::AutoPtr newJpegInstance(const std::string& path, bool create);
- //! Check if the file ifp is a JPEG image.
- bool isJpegType(FILE* ifp, bool advance);
-
- ImageFactory* ImageFactory::pInstance_ = 0;
+ ImageFactory::Registry* ImageFactory::registry_ = 0;
- ImageFactory& ImageFactory::instance()
+ void ImageFactory::init()
{
- if (0 == pInstance_) {
- pInstance_ = new ImageFactory;
+ if (0 == registry_) {
+ registry_ = new Registry;
}
- return *pInstance_;
- } // ImageFactory::instance
+ }
void ImageFactory::registerImage(Image::Type type,
NewInstanceFct newInst, IsThisTypeFct isType)
{
+ init();
assert (newInst && isType);
- registry_[type] = ImageFcts(newInst, isType);
- } // ImageFactory::registerImage
+ (*registry_)[type] = ImageFcts(newInst, isType);
+ }
- ImageFactory::ImageFactory()
+ Image::Type ImageFactory::getType(const std::string& path)
{
- // Register a prototype of each known image
- registerImage(Image::jpeg, newJpegInstance, isJpegType);
- registerImage(Image::exv, newExvInstance, isExvType);
- } // ImageFactory c'tor
+ FileIo fileIo(path);
+ return getType(fileIo);
+ }
- Image::Type ImageFactory::getType(const std::string& path) const
+ Image::Type ImageFactory::getType(const byte* data, long size)
{
- FileCloser closer(fopen(path.c_str(), "rb"));
- if (!closer.fp_) return Image::none;
+ MemIo memIo(data, size);
+ return getType(memIo);
+ }
+ Image::Type ImageFactory::getType(BasicIo& io)
+ {
+ IoCloser closer(io);
+ if (io.open() != 0) return Image::none;
+
Image::Type type = Image::none;
- Registry::const_iterator b = registry_.begin();
- Registry::const_iterator e = registry_.end();
+ Registry::const_iterator b = registry_->begin();
+ Registry::const_iterator e = registry_->end();
for (Registry::const_iterator i = b; i != e; ++i)
{
- if (i->second.isThisType(closer.fp_, false)) {
+ if (i->second.isThisType(io, false)) {
type = i->first;
break;
}
@@ -127,657 +113,96 @@ namespace Exiv2 {
return type;
} // ImageFactory::getType
- Image::AutoPtr ImageFactory::open(const std::string& path) const
- {
- Image::AutoPtr image;
- FileCloser closer(fopen(path.c_str(), "rb"));
- if (!closer.fp_) return image;
-
- Registry::const_iterator b = registry_.begin();
- Registry::const_iterator e = registry_.end();
- for (Registry::const_iterator i = b; i != e; ++i)
- {
- if (i->second.isThisType(closer.fp_, false)) {
- image = i->second.newInstance(path, false);
- break;
- }
- }
- return image;
- } // ImageFactory::open
-
- Image::AutoPtr ImageFactory::create(Image::Type type,
- const std::string& path) const
- {
- Registry::const_iterator i = registry_.find(type);
- if (i != registry_.end()) {
- return i->second.newInstance(path, true);
- }
- return Image::AutoPtr();
- } // ImageFactory::create
-
-
- const byte JpegBase::sos_ = 0xda;
- const byte JpegBase::eoi_ = 0xd9;
- const byte JpegBase::app0_ = 0xe0;
- const byte JpegBase::app1_ = 0xe1;
- const byte JpegBase::app13_ = 0xed;
- const byte JpegBase::com_ = 0xfe;
- const uint16_t JpegBase::iptc_ = 0x0404;
- const char JpegBase::exifId_[] = "Exif\0\0";
- const char JpegBase::jfifId_[] = "JFIF\0";
- const char JpegBase::ps3Id_[] = "Photoshop 3.0\0";
- const char JpegBase::bimId_[] = "8BIM";
-
- JpegBase::JpegBase(const std::string& path, bool create,
- const byte initData[], size_t dataSize)
- : path_(path), sizeExifData_(0), pExifData_(0),
- sizeIptcData_(0), pIptcData_(0)
- {
- if (create) {
- FILE* fp = fopen(path.c_str(), "w+b");
- if (fp) {
- initFile(fp, initData, dataSize);
- fclose(fp);
- }
- }
- }
-
- int JpegBase::initFile(FILE* fp, const byte initData[], size_t dataSize)
- {
- if (!fp || ferror(fp)) return 4;
- if (fwrite(initData, 1, dataSize, fp) != dataSize) {
- return 4;
- }
- return 0;
- }
-
- JpegBase::~JpegBase()
- {
- delete[] pExifData_;
- delete[] pIptcData_;
- }
-
- bool JpegBase::good() const
- {
- FileCloser closer(fopen(path_.c_str(), "rb"));
- if (closer.fp_ == 0 ) return false;
- return isThisType(closer.fp_, false);
- }
-
- void JpegBase::clearMetadata()
- {
- clearIptcData();
- clearExifData();
- clearComment();
- }
-
- void JpegBase::clearIptcData()
- {
- delete[] pIptcData_;
- pIptcData_ = 0;
- sizeIptcData_ = 0;
- }
-
- void JpegBase::clearExifData()
- {
- delete[] pExifData_;
- pExifData_ = 0;
- sizeExifData_ = 0;
- }
-
- void JpegBase::clearComment()
- {
- comment_.erase();
- }
-
- void JpegBase::setExifData(const byte* buf, long size)
+ Image::AutoPtr ImageFactory::open(const std::string& path)
{
- if (size > 0xfffd) throw Error("Exif data too large");
- clearExifData();
- if (size) {
- sizeExifData_ = size;
- pExifData_ = new byte[size];
- memcpy(pExifData_, buf, size);
- }
+ BasicIo::AutoPtr io(new FileIo(path));
+ return open(io);
}
- void JpegBase::setIptcData(const byte* buf, long size)
+ Image::AutoPtr ImageFactory::open(const byte* data, long size)
{
- clearIptcData();
- if (size) {
- sizeIptcData_ = size;
- pIptcData_ = new byte[size];
- memcpy(pIptcData_, buf, size);
- }
+ BasicIo::AutoPtr io(new MemIo(data, size));
+ return open(io);
}
- void JpegBase::setComment(const std::string& comment)
- {
- comment_ = comment;
- }
-
- void JpegBase::setMetadata(const Image& image)
+ Image::AutoPtr ImageFactory::open(BasicIo::AutoPtr io)
{
- setIptcData(image.iptcData(), image.sizeIptcData());
- setExifData(image.exifData(), image.sizeExifData());
- setComment(image.comment());
- }
-
- int JpegBase::advanceToMarker(FILE *fp) const
- {
- int c = -1;
- // Skips potential padding between markers
- while ((c=fgetc(fp)) != 0xff) {
- if (c == EOF) return -1;
- }
-
- // Markers can start with any number of 0xff
- while ((c=fgetc(fp)) == 0xff) {
- if (c == EOF) return -1;
- }
- return c;
- }
-
- int JpegBase::readMetadata()
- {
- FileCloser closer(fopen(path_.c_str(), "rb"));
- if (!closer.fp_) return 1;
-
- // Ensure that this is the correct image type
- if (!isThisType(closer.fp_, true)) {
- if (ferror(closer.fp_) || feof(closer.fp_)) return 1;
- return 2;
- }
- clearMetadata();
- int search = 3;
- const long bufMinSize = 16;
- long bufRead = 0;
- DataBuf buf(bufMinSize);
-
- // Read section marker
- int marker = advanceToMarker(closer.fp_);
- if (marker < 0) return 2;
-
- while (marker != sos_ && marker != eoi_ && search > 0) {
- // Read size and signature (ok if this hits EOF)
- bufRead = (long)fread(buf.pData_, 1, bufMinSize, closer.fp_);
- if (ferror(closer.fp_)) return 1;
- uint16_t size = getUShort(buf.pData_, bigEndian);
-
- if (marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) {
- if (size < 8) return 2;
- // Seek to begining and read the Exif data
- fseek(closer.fp_, 8-bufRead, SEEK_CUR);
- long sizeExifData = size - 8;
- pExifData_ = new byte[sizeExifData];
- fread(pExifData_, 1, sizeExifData, closer.fp_);
- if (ferror(closer.fp_) || feof(closer.fp_)) {
- delete[] pExifData_;
- pExifData_ = 0;
- return 1;
- }
- // Set the size and offset of the Exif data buffer
- sizeExifData_ = sizeExifData;
- --search;
- }
- else if (marker == app13_ && memcmp(buf.pData_ + 2, ps3Id_, 14) == 0) {
- if (size < 16) return 2;
- // Read the rest of the APP13 segment
- // needed if bufMinSize!=16: fseek(closer.fp_, 16-bufRead, SEEK_CUR);
- DataBuf psData(size - 16);
- fread(psData.pData_, 1, psData.size_, closer.fp_);
- if (ferror(closer.fp_) || feof(closer.fp_)) return 1;
- const byte *record = 0;
- uint16_t sizeIptc = 0;
- uint16_t sizeHdr = 0;
- // Find actual Iptc data within the APP13 segment
- if (!locateIptcData(psData.pData_, psData.size_, &record,
- &sizeHdr, &sizeIptc)) {
- assert(sizeIptc);
- sizeIptcData_ = sizeIptc;
- pIptcData_ = new byte[sizeIptc];
- memcpy( pIptcData_, record + sizeHdr, sizeIptc );
- }
- --search;
- }
- else if (marker == com_ && comment_.empty())
- {
- if (size < 2) return 2;
- // Jpegs can have multiple comments, but for now only read
- // the first one (most jpegs only have one anyway). Comments
- // are simple single byte ISO-8859-1 strings.
- fseek(closer.fp_, 2-bufRead, SEEK_CUR);
- buf.alloc(size-2);
- fread(buf.pData_, 1, size-2, closer.fp_);
- if (ferror(closer.fp_) || feof(closer.fp_)) return 1;
- comment_.assign(reinterpret_cast(buf.pData_), size-2);
- while ( comment_.length()
- && comment_.at(comment_.length()-1) == '\0') {
- comment_.erase(comment_.length()-1);
- }
- --search;
- }
- else {
- if (size < 2) return 2;
- // Skip the remainder of the unknown segment
- if (fseek(closer.fp_, size-bufRead, SEEK_CUR)) return 2;
- }
- // Read the beginning of the next segment
- marker = advanceToMarker(closer.fp_);
- if (marker < 0) return 2;
- }
- return 0;
- } // JpegBase::readMetadata
-
-
- // Operates on raw data (rather than file streams) to simplify reuse
- int JpegBase::locateIptcData(const byte *pPsData,
- long sizePsData,
- const byte **record,
- uint16_t *const sizeHdr,
- uint16_t *const sizeIptc) const
- {
- assert(record);
- assert(sizeHdr);
- assert(sizeIptc);
- // Used for error checking
- long position = 0;
-
- // Data should follow Photoshop format, if not exit
- while (position <= (sizePsData - 14) &&
- memcmp(pPsData + position, bimId_, 4)==0) {
- const byte *hrd = pPsData + position;
- position += 4;
- uint16_t type = getUShort(pPsData+ position, bigEndian);
- position += 2;
-
- // Pascal string is padded to have an even size (including size byte)
- byte psSize = pPsData[position] + 1;
- psSize += (psSize & 1);
- position += psSize;
- if (position >= sizePsData) return -2;
-
- // Data is also padded to be even
- long dataSize = getULong(pPsData + position, bigEndian);
- position += 4;
- if (dataSize > sizePsData - position) return -2;
-
- if (type == iptc_) {
- *sizeIptc = static_cast(dataSize);
- *sizeHdr = psSize + 10;
- *record = hrd;
- return 0;
- }
- position += dataSize + (dataSize & 1);
- }
- return 3;
- } // JpegBase::locateIptcData
-
- int JpegBase::writeMetadata()
- {
- FileCloser reader(fopen(path_.c_str(), "rb"));
- if (!reader.fp_) return 1;
-
- // Write the output to a temporary file
- pid_t pid = getpid();
- std::string tmpname = path_ + toString(pid);
- FileCloser writer(fopen(tmpname.c_str(), "wb"));
- if (!writer.fp_) return -3;
-
- int rc = doWriteMetadata(reader.fp_, writer.fp_);
- writer.close();
- reader.close();
- if (rc == 0) {
- // Workaround for MSVCRT rename that does not overwrite existing files
- if (remove(path_.c_str()) != 0) rc = -4;
- }
- if (rc == 0) {
- // rename temporary file
- if (rename(tmpname.c_str(), path_.c_str()) == -1) rc = -4;
- }
- if (rc != 0) {
- // remove temporary file
- remove(tmpname.c_str());
- }
- return rc;
- } // JpegBase::writeMetadata
-
- int JpegBase::doWriteMetadata(FILE *ifp, FILE* ofp) const
- {
- if (!ifp) return 1;
- if (!ofp) return 4;
-
- // Ensure that this is the correct image type
- if (!isThisType(ifp, true)) {
- if (ferror(ifp) || feof(ifp)) return 1;
- return 2;
- }
-
- const long bufMinSize = 16;
- long bufRead = 0;
- DataBuf buf(bufMinSize);
- const long seek = ftell(ifp);
- int count = 0;
- int search = 0;
- int insertPos = 0;
- int skipApp1Exif = -1;
- int skipApp13Ps3 = -1;
- int skipCom = -1;
- DataBuf psData;
-
- // Write image header
- if (writeHeader(ofp)) return 4;
-
- // Read section marker
- int marker = advanceToMarker(ifp);
- if (marker < 0) return 2;
-
- // First find segments of interest. Normally app0 is first and we want
- // to insert after it. But if app0 comes after com, app1 and app13 then
- // don't bother.
- while (marker != sos_ && marker != eoi_ && search < 3) {
- // Read size and signature (ok if this hits EOF)
- bufRead = (long)fread(buf.pData_, 1, bufMinSize, ifp);
- if (ferror(ifp)) return 1;
- uint16_t size = getUShort(buf.pData_, bigEndian);
-
- if (marker == app0_) {
- if (size < 2) return 2;
- insertPos = count + 1;
- if (fseek(ifp, size-bufRead, SEEK_CUR)) return 2;
- }
- else if (marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) {
- if (size < 8) return 2;
- skipApp1Exif = count;
- ++search;
- if (fseek(ifp, size-bufRead, SEEK_CUR)) return 2;
- }
- else if (marker == app13_ && memcmp(buf.pData_ + 2, ps3Id_, 14) == 0) {
- if (size < 16) return 2;
- skipApp13Ps3 = count;
- ++search;
- // needed if bufMinSize!=16: fseek(ifp, 16-bufRead, SEEK_CUR);
- psData.alloc(size - 16);
- // Load PS data now to allow reinsertion at any point
- fread(psData.pData_, 1, psData.size_, ifp);
- if (ferror(ifp) || feof(ifp)) return 1;
- }
- else if (marker == com_ && skipCom == -1) {
- if (size < 2) return 2;
- // Jpegs can have multiple comments, but for now only handle
- // the first one (most jpegs only have one anyway).
- skipCom = count;
- ++search;
- if (fseek(ifp, size-bufRead, SEEK_CUR)) return 2;
- }
- else {
- if (size < 2) return 2;
- if (fseek(ifp, size-bufRead, SEEK_CUR)) return 2;
- }
- marker = advanceToMarker(ifp);
- if (marker < 0) return 2;
- ++count;
- }
-
- if (pExifData_) ++search;
- if (pIptcData_) ++search;
- if (!comment_.empty()) ++search;
-
- fseek(ifp, seek, SEEK_SET);
- count = 0;
- marker = advanceToMarker(ifp);
- if (marker < 0) return 2;
-
- // To simplify this a bit, new segments are inserts at either the start
- // or right after app0. This is standard in most jpegs, but has the
- // potential to change segment ordering (which is allowed).
- // Segments are erased if there is no assigned metadata.
- while (marker != sos_ && search > 0) {
- // Read size and signature (ok if this hits EOF)
- bufRead = (long)fread(buf.pData_, 1, bufMinSize, ifp);
- if (ferror(ifp)) return 1;
- // Careful, this can be a meaningless number for empty
- // images with only an eoi_ marker
- uint16_t size = getUShort(buf.pData_, bigEndian);
-
- if (insertPos == count) {
- byte tmpBuf[18];
- if (!comment_.empty()) {
- // Write COM marker, size of comment, and string
- tmpBuf[0] = 0xff;
- tmpBuf[1] = com_;
- us2Data(tmpBuf + 2,
- static_cast(comment_.length()+3), bigEndian);
- if (fwrite(tmpBuf, 1, 4, ofp) != 4) return 4;
- if ( fwrite(comment_.data(), 1, comment_.length(), ofp)
- != comment_.length()) return 4;
- if (fputc(0, ofp)==EOF) return 4;
- if (ferror(ofp)) return 4;
- --search;
- }
- if (pExifData_) {
- // Write APP1 marker, size of APP1 field, Exif id and Exif data
- tmpBuf[0] = 0xff;
- tmpBuf[1] = app1_;
- us2Data(tmpBuf + 2,
- static_cast(sizeExifData_+8),
- bigEndian);
- memcpy(tmpBuf + 4, exifId_, 6);
- if (fwrite(tmpBuf, 1, 10, ofp) != 10) return 4;
- if ( fwrite(pExifData_, 1, sizeExifData_, ofp)
- != (size_t)sizeExifData_) return 4;
- if (ferror(ofp)) return 4;
- --search;
- }
-
- const byte *record = psData.pData_;
- uint16_t sizeIptc = 0;
- uint16_t sizeHdr = 0;
- // Safe to call with zero psData.size_
- locateIptcData(psData.pData_, psData.size_, &record, &sizeHdr, &sizeIptc);
-
- // Data is rounded to be even
- const int sizeOldData = sizeHdr + sizeIptc + (sizeIptc & 1);
- if (psData.size_ > sizeOldData || pIptcData_) {
- // write app13 marker, new size, and ps3Id
- tmpBuf[0] = 0xff;
- tmpBuf[1] = app13_;
- const int sizeNewData = sizeIptcData_ ?
- sizeIptcData_+(sizeIptcData_&1)+12 : 0;
- us2Data(tmpBuf + 2,
- static_cast(psData.size_-sizeOldData+sizeNewData+16),
- bigEndian);
- memcpy(tmpBuf + 4, ps3Id_, 14);
- if (fwrite(tmpBuf, 1, 18, ofp) != 18) return 4;
- if (ferror(ofp)) return 4;
-
- const long sizeFront = (long)(record - psData.pData_);
- const long sizeEnd = psData.size_ - sizeFront - sizeOldData;
- // write data before old record.
- if (fwrite(psData.pData_, 1, sizeFront, ofp) != (size_t)sizeFront) return 4;
+ Image::AutoPtr image;
+ IoCloser closer(*io);
+ if (io->open() != 0) return image;
- // write new iptc record if we have it
- if (pIptcData_) {
- memcpy(tmpBuf, bimId_, 4);
- us2Data(tmpBuf+4, iptc_, bigEndian);
- tmpBuf[6] = 0;
- tmpBuf[7] = 0;
- ul2Data(tmpBuf + 8, sizeIptcData_, bigEndian);
- if (fwrite(tmpBuf, 1, 12, ofp) != 12) return 4;
- if ( fwrite(pIptcData_, 1, sizeIptcData_ , ofp)
- != (size_t)sizeIptcData_) return 4;
- // data is padded to be even (but not included in size)
- if (sizeIptcData_ & 1) {
- if (fputc(0, ofp)==EOF) return 4;
- }
- if (ferror(ofp)) return 4;
- --search;
- }
-
- // write existing stuff after record
- if ( fwrite(record+sizeOldData, 1, sizeEnd, ofp)
- != (size_t)sizeEnd) return 4;
- if (ferror(ofp)) return 4;
- }
- }
- if (marker == eoi_) {
+ Registry::const_iterator b = registry_->begin();
+ Registry::const_iterator e = registry_->end();
+ for (Registry::const_iterator i = b; i != e; ++i)
+ {
+ if (i->second.isThisType(*io, false)) {
+ image = i->second.newInstance(io, false);
break;
}
- else if (skipApp1Exif==count || skipApp13Ps3==count || skipCom==count) {
- --search;
- fseek(ifp, size-bufRead, SEEK_CUR);
- }
- else {
- if (size < 2) return 2;
- buf.alloc(size+2);
- fseek(ifp, -bufRead-2, SEEK_CUR);
- fread(buf.pData_, 1, size+2, ifp);
- if (ferror(ifp) || feof(ifp)) return 1;
- if (fwrite(buf.pData_, 1, size+2, ofp) != (size_t)size+2) return 4;
- if (ferror(ofp)) return 4;
- }
-
- // Next marker
- marker = advanceToMarker(ifp);
- if (marker < 0) return 2;
- ++count;
- }
-
- // Copy rest of the stream
- fseek(ifp, -2, SEEK_CUR);
- fflush( ofp );
- buf.alloc(4096);
- size_t readSize = 0;
- while ((readSize=fread(buf.pData_, 1, buf.size_, ifp))) {
- if (fwrite(buf.pData_, 1, readSize, ofp) != readSize) return 4;
- }
- if (ferror(ofp)) return 4;
-
- return 0;
- }// JpegBase::doWriteMetadata
-
-
- const byte JpegImage::soi_ = 0xd8;
- const byte JpegImage::blank_[] = {
- 0xFF,0xD8,0xFF,0xDB,0x00,0x84,0x00,0x10,0x0B,0x0B,0x0B,0x0C,0x0B,0x10,0x0C,0x0C,
- 0x10,0x17,0x0F,0x0D,0x0F,0x17,0x1B,0x14,0x10,0x10,0x14,0x1B,0x1F,0x17,0x17,0x17,
- 0x17,0x17,0x1F,0x1E,0x17,0x1A,0x1A,0x1A,0x1A,0x17,0x1E,0x1E,0x23,0x25,0x27,0x25,
- 0x23,0x1E,0x2F,0x2F,0x33,0x33,0x2F,0x2F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
- 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x01,0x11,0x0F,0x0F,0x11,0x13,0x11,0x15,0x12,
- 0x12,0x15,0x14,0x11,0x14,0x11,0x14,0x1A,0x14,0x16,0x16,0x14,0x1A,0x26,0x1A,0x1A,
- 0x1C,0x1A,0x1A,0x26,0x30,0x23,0x1E,0x1E,0x1E,0x1E,0x23,0x30,0x2B,0x2E,0x27,0x27,
- 0x27,0x2E,0x2B,0x35,0x35,0x30,0x30,0x35,0x35,0x40,0x40,0x3F,0x40,0x40,0x40,0x40,
- 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xFF,0xC0,0x00,0x11,0x08,0x00,0x01,0x00,
- 0x01,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11,0x01,0xFF,0xC4,0x00,0x4B,0x00,
- 0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x07,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xDA,0x00,0x0C,0x03,0x01,0x00,0x02,
- 0x11,0x03,0x11,0x00,0x3F,0x00,0xA0,0x00,0x0F,0xFF,0xD9 };
-
- JpegImage::JpegImage(const std::string& path, bool create)
- : JpegBase(path, create, blank_, sizeof(blank_))
- {
- }
-
- int JpegImage::writeHeader(FILE* ofp) const
- {
- // Jpeg header
- byte tmpBuf[2];
- tmpBuf[0] = 0xff;
- tmpBuf[1] = soi_;
- if (fwrite(tmpBuf, 1, 2, ofp) != 2) return 4;
- if (ferror(ofp)) return 4;
- return 0;
- }
-
- bool JpegImage::isThisType(FILE* ifp, bool advance) const
- {
- return isJpegType(ifp, advance);
- }
-
- Image::AutoPtr newJpegInstance(const std::string& path, bool create)
- {
- Image::AutoPtr image;
- if (create) {
- image = Image::AutoPtr(new JpegImage(path, true));
- }
- else {
- image = Image::AutoPtr(new JpegImage(path, false));
- }
- if (!image->good()) {
- image.reset();
}
return image;
- }
+ } // ImageFactory::open
- bool isJpegType(FILE* ifp, bool advance)
- {
- bool result = true;
- byte tmpBuf[2];
- fread(tmpBuf, 1, 2, ifp);
- if (ferror(ifp) || feof(ifp)) return false;
- if (0xff!=tmpBuf[0] || JpegImage::soi_!=tmpBuf[1]) {
- result = false;
- }
- if (!advance || !result ) fseek(ifp, -2, SEEK_CUR);
- return result;
+ Image::AutoPtr ImageFactory::create(Image::Type type,
+ const std::string& path)
+ {
+ FileIo *fileIo = new FileIo(path);
+ BasicIo::AutoPtr io(fileIo);
+ // Create or overwrite the file, then close it
+ if (fileIo->open("w+b") != 0) return Image::AutoPtr();
+ fileIo->close();
+ return create(type, io);
}
-
- const char ExvImage::exiv2Id_[] = "Exiv2";
- const byte ExvImage::blank_[] = { 0xff,0x01,'E','x','i','v','2',0xff,0xd9 };
- ExvImage::ExvImage(const std::string& path, bool create)
- : JpegBase(path, create, blank_, sizeof(blank_))
+ Image::AutoPtr ImageFactory::create(Image::Type type)
{
+ BasicIo::AutoPtr io(new MemIo);
+ return create(type, io);
}
- int ExvImage::writeHeader(FILE* ofp) const
- {
- // Exv header
- byte tmpBuf[7];
- tmpBuf[0] = 0xff;
- tmpBuf[1] = 0x01;
- memcpy(tmpBuf + 2, exiv2Id_, 5);
- if (fwrite(tmpBuf, 1, 7, ofp) != 7) return 4;
- if (ferror(ofp)) return 4;
- return 0;
- }
- bool ExvImage::isThisType(FILE* ifp, bool advance) const
+ Image::AutoPtr ImageFactory::create(Image::Type type,
+ BasicIo::AutoPtr io)
{
- return isExvType(ifp, advance);
- }
+ // BasicIo instance does not need to be open
+ Registry::const_iterator i = registry_->find(type);
+ if (i != registry_->end()) {
+ return i->second.newInstance(io, true);
+ }
+ return Image::AutoPtr();
+ } // ImageFactory::create
- Image::AutoPtr newExvInstance(const std::string& path, bool create)
+ std::string Image::strError(int rc, const std::string& path)
{
- Image::AutoPtr image;
- if (create) {
- image = Image::AutoPtr(new ExvImage(path, true));
- }
- else {
- image = Image::AutoPtr(new ExvImage(path, false));
+ std::string error = path + ": ";
+ switch (rc) {
+ case -1:
+ error += "Failed to open the file";
+ break;
+ case -2:
+ error += "The file contains data of an unknown image type";
+ break;
+ case -3:
+ error += "Couldn't open temporary file";
+ break;
+ case -4:
+ error += "Renaming temporary file failed";
+ break;
+ case 1:
+ error += "Couldn't read from the input file";
+ break;
+ case 2:
+ error += "This does not look like a JPEG image";
+ break;
+ default:
+ error += "Accessing image data failed, rc = " + toString(rc);
+ break;
}
- if (!image->good()) image.reset();
- return image;
- }
+ return error;
+ } // Image::strError
- bool isExvType(FILE* ifp, bool advance)
- {
- bool result = true;
- byte tmpBuf[7];
- fread(tmpBuf, 1, 7, ifp);
- if (ferror(ifp) || feof(ifp)) return false;
- if (0xff!=tmpBuf[0] || 0x01!=tmpBuf[1] ||
- memcmp(tmpBuf + 2, ExvImage::exiv2Id_, 5) != 0) {
- result = false;
- }
- if (!advance || !result ) fseek(ifp, -7, SEEK_CUR);
- return result;
- }
TiffHeader::TiffHeader(ByteOrder byteOrder)
: byteOrder_(byteOrder), tag_(0x002a), offset_(0x00000008)
diff --git a/src/image.hpp b/src/image.hpp
index 8bb1f278..d8f11412 100644
--- a/src/image.hpp
+++ b/src/image.hpp
@@ -29,6 +29,7 @@
@date 09-Jan-04, ahu: created
11-Feb-04, ahu: isolated as a component
19-Jul-04, brad: revamped to be more flexible and support Iptc
+ 15-Jan-05, brad: inside-out design changes
*/
#ifndef IMAGE_HPP_
#define IMAGE_HPP_
@@ -36,29 +37,39 @@
// *****************************************************************************
// included header files
#include "types.hpp"
+#include "basicio.hpp"
// + standard includes
#include
#include