Merges all changes from the insideout branch back into trunk. This includes the IO abstraction code, split-up of image.cpp, and inside-out design change (#402, #403, and #404).

v0.27.3
brad 21 years ago
parent cbfe3eead2
commit 0cab366ec2

@ -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} {831EF580-92C8-4CA8-B0CE-3D906280A54D} = {831EF580-92C8-4CA8-B0CE-3D906280A54D}
EndProjectSection EndProjectSection
EndProject 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 Global
GlobalSection(SolutionConfiguration) = preSolution GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug Debug = Debug
@ -139,6 +149,14 @@ Global
{94A7505B-3A53-40F0-95A2-2ECB1CEC7C57}.Debug.Build.0 = Debug|Win32 {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.ActiveCfg = Release|Win32
{94A7505B-3A53-40F0-95A2-2ECB1CEC7C57}.Release.Build.0 = 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 EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection EndGlobalSection

@ -106,6 +106,9 @@
Name="Source Files" Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
<File
RelativePath="..\..\src\basicio.cpp">
</File>
<File <File
RelativePath="..\..\src\canonmn.cpp"> RelativePath="..\..\src\canonmn.cpp">
<FileConfiguration <FileConfiguration
@ -187,6 +190,9 @@
<File <File
RelativePath="..\..\src\iptc.cpp"> RelativePath="..\..\src\iptc.cpp">
</File> </File>
<File
RelativePath="..\..\src\jpgimage.cpp">
</File>
<File <File
RelativePath="..\..\src\makernote.cpp"> RelativePath="..\..\src\makernote.cpp">
<FileConfiguration <FileConfiguration
@ -288,6 +294,9 @@
Name="Header Files" Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd" Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
<File
RelativePath="..\..\src\basicio.hpp">
</File>
<File <File
RelativePath="..\..\src\canonmn.hpp"> RelativePath="..\..\src\canonmn.hpp">
</File> </File>
@ -315,6 +324,9 @@
<File <File
RelativePath="..\..\src\iptc.hpp"> RelativePath="..\..\src\iptc.hpp">
</File> </File>
<File
RelativePath="..\..\src\jpgimage.hpp">
</File>
<File <File
RelativePath="..\..\src\makernote.hpp"> RelativePath="..\..\src\makernote.hpp">
</File> </File>

@ -28,61 +28,60 @@
#include "stdafx.h" #include "stdafx.h"
#include "exivsimple.h" #include "exivsimple.h"
#include "image.hpp"
#include "exif.hpp" #include "exif.hpp"
#include "iptc.hpp" #include "iptc.hpp"
#include <cassert> #include <cassert>
struct ExivImage struct ImageWrapper
{ {
Exiv2::IptcData iptcData; Exiv2::Image::AutoPtr image;
Exiv2::ExifData exifData;
std::string fileName;
}; };
// Returns 0 if failed. // Returns 0 if failed.
EXIVSIMPLE_API HIMAGE OpenImage(const char *file) EXIVSIMPLE_API HIMAGE OpenImage(const char *file)
{ {
assert(file); assert(file);
ImageWrapper *imgWrap = new ImageWrapper;
ExivImage *image = new ExivImage; // See if file exists. Sorry for very bad error handling
image->fileName = file; if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(file)) {
return 0;
}
// rc of 3 just means no iptc or exif data (not a real error) imgWrap->image = Exiv2::ImageFactory::open(file);
int rc = image->iptcData.read( file ); if (imgWrap->image.get() == 0) {
if (rc==0 || rc==3) return 0;
rc = image->exifData.read( file ); }
if (rc!=0 && rc!=3) { // Load existing metadata
delete image; if (imgWrap->image->readMetadata()) {
image = 0; delete imgWrap;
imgWrap = 0;
} }
return (HIMAGE)image; return (HIMAGE)imgWrap;
} }
EXIVSIMPLE_API void FreeImage(HIMAGE img) EXIVSIMPLE_API void FreeImage(HIMAGE img)
{ {
if (img) { if (img) {
ExivImage *image = (ExivImage*)img; ImageWrapper *imgWrap = (ImageWrapper*)img;
delete image; delete imgWrap;
} }
} }
EXIVSIMPLE_API int SaveImage(HIMAGE img) EXIVSIMPLE_API int SaveImage(HIMAGE img)
{ {
assert(img); assert(img);
ExivImage *image = (ExivImage*)img; ImageWrapper *imgWrap = (ImageWrapper*)img;
return imgWrap->image->writeMetadata();
int rc = image->iptcData.write(image->fileName);
if (rc==0)
rc = image->exifData.write(image->fileName);
return rc;
} }
// This is weird because iptc and exif have not been "unified". Once // 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. // about either... just generic images, keys, values, etc.
//
// buffsize should be the total size of *buff (including space for null) // 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 // 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. // 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); assert(img && key && buff);
if (img==0 || key==0 || buff==0 || buffsize==0) return -1; if (img==0 || key==0 || buff==0 || buffsize==0) return -1;
ExivImage *image = (ExivImage*)img; ImageWrapper *imgWrap = (ImageWrapper*)img;
int rc = 2; int rc = 2;
Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
Exiv2::ExifData &exifData = imgWrap->image->exifData();
try { try {
// First try iptc
Exiv2::IptcKey iptcKey(key); Exiv2::IptcKey iptcKey(key);
rc = 1; rc = 1;
Exiv2::IptcData::const_iterator iter = image->iptcData.findKey(iptcKey); Exiv2::IptcData::const_iterator iter = iptcData.findKey(iptcKey);
if (iter != image->iptcData.end()) { if (iter != iptcData.end()) {
strncpy(buff, iter->value().toString().c_str(), buffsize); strncpy(buff, iter->value().toString().c_str(), buffsize);
buff[buffsize-1] = 0; buff[buffsize-1] = 0;
rc = 0; rc = 0;
@ -111,8 +114,8 @@ EXIVSIMPLE_API int ReadMeta(HIMAGE img, const char *key, char *buff, int buffsiz
try { try {
Exiv2::ExifKey exifKey(key); Exiv2::ExifKey exifKey(key);
rc = 1; rc = 1;
Exiv2::ExifData::const_iterator iter = image->exifData.findKey(exifKey); Exiv2::ExifData::const_iterator iter = exifData.findKey(exifKey);
if (iter != image->exifData.end()) { if (iter != exifData.end()) {
strncpy(buff, iter->value().toString().c_str(), buffsize); strncpy(buff, iter->value().toString().c_str(), buffsize);
buff[buffsize-1] = 0; buff[buffsize-1] = 0;
rc = 0; rc = 0;
@ -132,9 +135,12 @@ EXIVSIMPLE_API int ModifyMeta(HIMAGE img, const char *key, const char *val, DllT
{ {
assert(img && key && val); assert(img && key && val);
if (img==0 || key==0 || val==0) return -1; if (img==0 || key==0 || val==0) return -1;
ExivImage *image = (ExivImage*)img; ImageWrapper *imgWrap = (ImageWrapper*)img;
int rc = 2; int rc = 2;
Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
Exiv2::ExifData &exifData = imgWrap->image->exifData();
std::string data(val); std::string data(val);
// if data starts and ends with quotes, remove them // if data starts and ends with quotes, remove them
if (data.at(0) == '\"' && data.at(data.size()-1) == '\"') { 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); Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
value->read(data); value->read(data);
Exiv2::IptcData::iterator iter = image->iptcData.findKey(iptcKey); Exiv2::IptcData::iterator iter = iptcData.findKey(iptcKey);
if (iter != image->iptcData.end()) { if (iter != iptcData.end()) {
iter->setValue(value.get()); iter->setValue(value.get());
rc = 0; rc = 0;
} }
else { else {
rc = image->iptcData.add(iptcKey, value.get()); rc = iptcData.add(iptcKey, value.get());
} }
} }
catch(const Exiv2::Error&) { 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); Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
value->read(data); value->read(data);
Exiv2::ExifData::iterator iter = image->exifData.findKey(exifKey); Exiv2::ExifData::iterator iter = exifData.findKey(exifKey);
if (iter != image->exifData.end()) { if (iter != exifData.end()) {
iter->setValue(value.get()); iter->setValue(value.get());
rc = 0; rc = 0;
} }
else { else {
image->exifData.add(exifKey, value.get()); exifData.add(exifKey, value.get());
rc = 0; rc = 0;
} }
} }
@ -198,9 +204,12 @@ EXIVSIMPLE_API int AddMeta(HIMAGE img, const char *key, const char *val, DllType
{ {
assert(img && key && val); assert(img && key && val);
if (img==0 || key==0 || val==0) return -1; if (img==0 || key==0 || val==0) return -1;
ExivImage *image = (ExivImage*)img; ImageWrapper *imgWrap = (ImageWrapper*)img;
int rc = 2; int rc = 2;
Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
Exiv2::ExifData &exifData = imgWrap->image->exifData();
std::string data(val); std::string data(val);
// if data starts and ends with quotes, remove them // if data starts and ends with quotes, remove them
if (data.at(0) == '\"' && data.at(data.size()-1) == '\"') { 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); Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
value->read(data); value->read(data);
rc = image->iptcData.add(iptcKey, value.get()); rc = iptcData.add(iptcKey, value.get());
} }
catch(const Exiv2::Error&) { 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); Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
value->read(data); value->read(data);
image->exifData.add(exifKey, value.get()); exifData.add(exifKey, value.get());
rc = 0; rc = 0;
} }
catch(const Exiv2::Error&) { catch(const Exiv2::Error&) {
@ -249,15 +258,18 @@ EXIVSIMPLE_API int RemoveMeta(HIMAGE img, const char *key)
{ {
assert(img && key); assert(img && key);
if (img==0 || key==0) return -1; if (img==0 || key==0) return -1;
ExivImage *image = (ExivImage*)img; ImageWrapper *imgWrap = (ImageWrapper*)img;
int rc = 2; int rc = 2;
Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
Exiv2::ExifData &exifData = imgWrap->image->exifData();
try { try {
Exiv2::IptcKey iptcKey(key); Exiv2::IptcKey iptcKey(key);
rc = 1; rc = 1;
Exiv2::IptcData::iterator iter = image->iptcData.findKey(iptcKey); Exiv2::IptcData::iterator iter = iptcData.findKey(iptcKey);
if (iter != image->iptcData.end()) { if (iter != iptcData.end()) {
image->iptcData.erase(iter); iptcData.erase(iter);
rc = 0; rc = 0;
} }
} }
@ -269,9 +281,9 @@ EXIVSIMPLE_API int RemoveMeta(HIMAGE img, const char *key)
try { try {
Exiv2::ExifKey exifKey(key); Exiv2::ExifKey exifKey(key);
rc = 1; rc = 1;
Exiv2::ExifData::iterator iter = image->exifData.findKey(exifKey); Exiv2::ExifData::iterator iter = exifData.findKey(exifKey);
if (iter != image->exifData.end()) { if (iter != exifData.end()) {
image->exifData.erase(iter); exifData.erase(iter);
rc = 0; rc = 0;
} }
} }
@ -286,17 +298,20 @@ EXIVSIMPLE_API int EnumMeta(HIMAGE img, METAENUMPROC proc, void *user)
{ {
assert(img && proc); assert(img && proc);
if (img==0 || proc==0) return -1; if (img==0 || proc==0) return -1;
ExivImage *image = (ExivImage*)img; ImageWrapper *imgWrap = (ImageWrapper*)img;
bool more = true; bool more = true;
Exiv2::IptcData::const_iterator iend = image->iptcData.end(); Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
for (Exiv2::IptcData::const_iterator i = image->iptcData.begin(); 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) { i != iend && more; ++i) {
more = proc(i->key().c_str(), i->value().toString().c_str(), user); more = proc(i->key().c_str(), i->value().toString().c_str(), user);
} }
Exiv2::ExifData::const_iterator eend = image->exifData.end(); Exiv2::ExifData::const_iterator eend = exifData.end();
for (Exiv2::ExifData::const_iterator e = image->exifData.begin(); for (Exiv2::ExifData::const_iterator e = exifData.begin();
e != eend && more; ++e) { e != eend && more; ++e) {
more = proc(e->key().c_str(), e->value().toString().c_str(), user); more = proc(e->key().c_str(), e->value().toString().c_str(), user);
} }

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="iotest"
ProjectGUID="{5D43ECB3-681D-4732-9395-AB81CD283F6C}"
Keyword="Win32Proj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="&quot;..\..\src&quot;;&quot;..\..&quot;"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
BufferSecurityCheck="TRUE"
RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="0"
WarningLevel="3"
WarnAsError="TRUE"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="4"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="..\exiv2lib\Debug\exiv2.lib"
OutputFile="$(OutDir)/iotest.exe"
LinkIncremental="2"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/iotest.pdb"
SubSystem="1"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="&quot;..\..\src&quot;;&quot;..\..&quot;"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="4"
RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="0"
WarningLevel="3"
WarnAsError="TRUE"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="..\exiv2lib\Release\exiv2.lib"
OutputFile="$(OutDir)/iotest.exe"
LinkIncremental="1"
GenerateDebugInformation="TRUE"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
<File
RelativePath="..\..\src\iotest.cpp">
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="iptceasy"
ProjectGUID="{D8B36F3A-34BB-4540-A731-EEABF1DC2E05}"
Keyword="Win32Proj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="&quot;..\..\src&quot;;&quot;..\..&quot;"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
BufferSecurityCheck="TRUE"
RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="0"
WarningLevel="3"
WarnAsError="TRUE"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="4"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="..\exiv2lib\Debug\exiv2.lib"
OutputFile="$(OutDir)/iptceasy.exe"
LinkIncremental="2"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/iptceasy.pdb"
SubSystem="1"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="&quot;..\..\src&quot;;&quot;..\..&quot;"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="4"
RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="0"
WarningLevel="3"
WarnAsError="TRUE"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="..\exiv2lib\Release\exiv2.lib"
OutputFile="$(OutDir)/iptceasy.exe"
LinkIncremental="1"
GenerateDebugInformation="TRUE"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
<File
RelativePath="..\..\src\iptceasy.cpp">
</File>
<File
RelativePath="..\..\src\mn.cpp">
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

@ -51,14 +51,14 @@ include $(top_srcdir)/config.mk
CCHDR = rcsid.hpp error.hpp CCHDR = rcsid.hpp error.hpp
# Add library C++ source files to this list # 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 \ 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 # Add source files of simple applications to this list
BINSRC = addmoddel.cpp exifcomment.cpp exifprint.cpp ifd-test.cpp iptcprint.cpp \ 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 \ 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 # State the main source file of the Exiv2 application here
EXIV2MAIN = exiv2.cpp EXIV2MAIN = exiv2.cpp

@ -39,6 +39,7 @@ EXIV2_RCSID("@(#) $Id$");
#endif #endif
#include "actions.hpp" #include "actions.hpp"
#include "image.hpp"
#include "exiv2.hpp" #include "exiv2.hpp"
#include "utils.hpp" #include "utils.hpp"
#include "types.hpp" #include "types.hpp"
@ -167,13 +168,24 @@ namespace Action {
int Print::printSummary() int Print::printSummary()
{ {
Exiv2::ExifData exifData; if (!Util::fileExists(path_, true)) {
int rc = exifData.read(path_); 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) { if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n"; std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc; return rc;
} }
Exiv2::ExifData &exifData = image->exifData();
align_ = 16; align_ = 16;
// Filename // Filename
@ -267,7 +279,7 @@ namespace Action {
// Subject distance // Subject distance
std::cout << std::setw(align_) << std::setfill(' ') << std::left std::cout << std::setw(align_) << std::setfill(' ') << std::left
<< "Subject distance" << ": "; << "Subject distance" << ": ";
if (0 == printTag(exifData, "Exif.Photo.SubjectDistance")) { if (0 == printTag(exifData, "Exif.Photo.SubjectDistance")) {
md = exifData.findKey( md = exifData.findKey(
Exiv2::ExifKey("Exif.Canon.CameraSettings2")); Exiv2::ExifKey("Exif.Canon.CameraSettings2"));
if (md != exifData.end() && md->count() >= 19) { if (md != exifData.end() && md->count() >= 19) {
@ -446,13 +458,24 @@ namespace Action {
int Print::printInterpreted() int Print::printInterpreted()
{ {
Exiv2::ExifData exifData; if (!Util::fileExists(path_, true)) {
int rc = exifData.read(path_); 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) { if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n"; std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc; return rc;
} }
Exiv2::ExifData &exifData = image->exifData();
Exiv2::ExifData::const_iterator md; Exiv2::ExifData::const_iterator md;
for (md = exifData.begin(); md != exifData.end(); ++md) { for (md = exifData.begin(); md != exifData.end(); ++md) {
std::cout << "0x" << std::setw(4) << std::setfill('0') << std::right std::cout << "0x" << std::setw(4) << std::setfill('0') << std::right
@ -469,13 +492,24 @@ namespace Action {
int Print::printValues() int Print::printValues()
{ {
Exiv2::ExifData exifData; if (!Util::fileExists(path_, true)) {
int rc = exifData.read(path_); 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) { if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n"; std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc; return rc;
} }
Exiv2::ExifData &exifData = image->exifData();
Exiv2::ExifData::const_iterator end = exifData.end(); Exiv2::ExifData::const_iterator end = exifData.end();
Exiv2::ExifData::const_iterator md; Exiv2::ExifData::const_iterator md;
for (md = exifData.begin(); md != end; ++md) { for (md = exifData.begin(); md != end; ++md) {
@ -499,13 +533,24 @@ namespace Action {
int Print::printIptc() int Print::printIptc()
{ {
Exiv2::IptcData iptcData; if (!Util::fileExists(path_, true)) {
int rc = iptcData.read(path_); 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) { if (rc) {
std::cerr << Exiv2::IptcData::strError(rc, path_) << "\n"; std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc; return rc;
} }
Exiv2::IptcData &iptcData = image->iptcData();
Exiv2::IptcData::const_iterator end = iptcData.end(); Exiv2::IptcData::const_iterator end = iptcData.end();
Exiv2::IptcData::const_iterator md; Exiv2::IptcData::const_iterator md;
for (md = iptcData.begin(); md != end; ++md) { for (md = iptcData.begin(); md != end; ++md) {
@ -529,13 +574,24 @@ namespace Action {
int Print::printHexdump() int Print::printHexdump()
{ {
Exiv2::ExifData exifData; if (!Util::fileExists(path_, true)) {
int rc = exifData.read(path_); 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) { if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n"; std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc; return rc;
} }
Exiv2::ExifData &exifData = image->exifData();
Exiv2::ExifData::const_iterator md; Exiv2::ExifData::const_iterator md;
for (md = exifData.begin(); md != exifData.end(); ++md) { for (md = exifData.begin(); md != exifData.end(); ++md) {
std::cout << std::setw(4) << std::setfill(' ') << std::left std::cout << std::setw(4) << std::setfill(' ') << std::left
@ -567,7 +623,7 @@ namespace Action {
<< ": Failed to open the file\n"; << ": Failed to open the file\n";
return -1; return -1;
} }
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::instance().open(path_); Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
if (image.get() == 0) { if (image.get() == 0) {
std::cerr << path_ std::cerr << path_
<< ": The file contains data of an unknown image type\n"; << ": The file contains data of an unknown image type\n";
@ -598,12 +654,24 @@ namespace Action {
int Rename::run(const std::string& path) int Rename::run(const std::string& path)
try { try {
Exiv2::ExifData exifData; if (!Util::fileExists(path, true)) {
int rc = exifData.read(path); 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) { if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n"; std::cerr << Exiv2::Image::strError(rc, path) << "\n";
return rc; return rc;
} }
Exiv2::ExifData &exifData = image->exifData();
Exiv2::ExifKey key("Exif.Photo.DateTimeOriginal"); Exiv2::ExifKey key("Exif.Photo.DateTimeOriginal");
Exiv2::ExifData::iterator md = exifData.findKey(key); Exiv2::ExifData::iterator md = exifData.findKey(key);
if (md == exifData.end()) { if (md == exifData.end()) {
@ -683,8 +751,13 @@ namespace Action {
try { try {
path_ = path; path_ = path;
if (!Util::fileExists(path_, true)) {
std::cerr << path_
<< ": Failed to open the file\n";
return -1;
}
Exiv2::Image::AutoPtr image Exiv2::Image::AutoPtr image
= Exiv2::ImageFactory::instance().open(path_); = Exiv2::ImageFactory::open(path_);
if (image.get() == 0) { if (image.get() == 0) {
std::cerr << path_ std::cerr << path_
<< ": The file contains data of an unknown image type\n"; << ": The file contains data of an unknown image type\n";
@ -710,7 +783,7 @@ namespace Action {
if (0 == rc) { if (0 == rc) {
rc = image->writeMetadata(); rc = image->writeMetadata();
if (rc) { 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 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; return 0;
} }
int rc = 0; long delta = exifData.eraseThumbnail();
Exiv2::ExifData exifData; if (Params::instance().verbose_) {
rc = exifData.read(image->exifData(), image->sizeExifData()); std::cout << "Erasing " << delta
if (rc) { << " Bytes of thumbnail data" << std::endl;
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_);
}
} }
return rc; return 0;
} }
int Erase::eraseExifData(Exiv2::Image* image) const 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; std::cout << "Erasing Exif data from the file" << std::endl;
} }
image->clearExifData(); image->clearExifData();
@ -760,7 +822,7 @@ namespace Action {
int Erase::eraseIptcData(Exiv2::Image* image) const 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; std::cout << "Erasing Iptc data from the file" << std::endl;
} }
image->clearIptcData(); image->clearIptcData();
@ -816,12 +878,24 @@ namespace Action {
int Extract::writeThumbnail() const int Extract::writeThumbnail() const
{ {
Exiv2::ExifData exifData; if (!Util::fileExists(path_, true)) {
int rc = exifData.read(path_); 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) { if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n"; std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
return rc; return rc;
} }
Exiv2::ExifData &exifData = image->exifData();
std::string thumb = Util::dirname(path_) + SEPERATOR_STR std::string thumb = Util::dirname(path_) + SEPERATOR_STR
+ Util::basename(path_, true) + "-thumb"; + Util::basename(path_, true) + "-thumb";
std::string thumbExt = exifData.thumbnailExtension(); std::string thumbExt = exifData.thumbnailExtension();
@ -898,14 +972,26 @@ namespace Action {
<< ": Failed to open the file\n"; << ": Failed to open the file\n";
return -1; return -1;
} }
Exiv2::ExifData exifData; if (!Util::fileExists(path, true)) {
int rc = exifData.read(path); 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) { if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n"; std::cerr << Exiv2::Image::strError(rc, path) << "\n";
return rc; return rc;
} }
Exiv2::ExifData &exifData = image->exifData();
exifData.setJpegThumbnail(thumbPath); exifData.setJpegThumbnail(thumbPath);
return exifData.write(path); return image->writeMetadata();
} // Insert::insertThumbnail } // Insert::insertThumbnail
@ -926,10 +1012,17 @@ namespace Action {
<< ": Failed to open the file\n"; << ": Failed to open the file\n";
return -1; return -1;
} }
image_ = Exiv2::ImageFactory::open(path);
// Read both exif and iptc metadata (ignore return code) if (image_.get() == 0) {
exifData_.read(path); std::cerr << path
iptcData_.read(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 // loop through command table and apply each command
ModifyCmds& modifyCmds = Params::instance().modifyCmds_; ModifyCmds& modifyCmds = Params::instance().modifyCmds_;
@ -953,13 +1046,9 @@ namespace Action {
} }
// Save both exif and iptc metadata // Save both exif and iptc metadata
int rc = exifData_.write(path); rc = image_->writeMetadata();
if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
}
rc = iptcData_.write(path);
if (rc) { if (rc) {
std::cerr << Exiv2::IptcData::strError(rc, path) << "\n"; std::cerr << Exiv2::Image::strError(rc, path) << "\n";
} }
return rc; return rc;
} }
@ -981,10 +1070,10 @@ namespace Action {
Exiv2::Value::AutoPtr value = Exiv2::Value::create(modifyCmd.typeId_); Exiv2::Value::AutoPtr value = Exiv2::Value::create(modifyCmd.typeId_);
value->read(modifyCmd.value_); value->read(modifyCmd.value_);
if (modifyCmd.metadataId_ == exif) { 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) { 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_) << Exiv2::TypeInfo::typeName(modifyCmd.typeId_)
<< ")" << std::endl; << ")" << std::endl;
} }
Exiv2::ExifData &exifData = image_->exifData();
Exiv2::IptcData &iptcData = image_->iptcData();
Exiv2::Metadatum* metadatum = 0; Exiv2::Metadatum* metadatum = 0;
if (modifyCmd.metadataId_ == exif) { if (modifyCmd.metadataId_ == exif) {
metadatum = &exifData_[modifyCmd.key_]; metadatum = &exifData[modifyCmd.key_];
} }
if (modifyCmd.metadataId_ == iptc) { if (modifyCmd.metadataId_ == iptc) {
metadatum = &iptcData_[modifyCmd.key_]; metadatum = &iptcData[modifyCmd.key_];
} }
assert(metadatum); assert(metadatum);
Exiv2::Value::AutoPtr value = metadatum->getValue(); Exiv2::Value::AutoPtr value = metadatum->getValue();
@ -1020,15 +1112,18 @@ namespace Action {
if (Params::instance().verbose_) { if (Params::instance().verbose_) {
std::cout << "Del " << modifyCmd.key_ << std::endl; std::cout << "Del " << modifyCmd.key_ << std::endl;
} }
Exiv2::ExifData &exifData = image_->exifData();
Exiv2::IptcData &iptcData = image_->iptcData();
if (modifyCmd.metadataId_ == exif) { if (modifyCmd.metadataId_ == exif) {
Exiv2::ExifData::iterator pos = Exiv2::ExifData::iterator pos =
exifData_.findKey(Exiv2::ExifKey(modifyCmd.key_)); exifData.findKey(Exiv2::ExifKey(modifyCmd.key_));
if (pos != exifData_.end()) exifData_.erase(pos); if (pos != exifData.end()) exifData.erase(pos);
} }
if (modifyCmd.metadataId_ == iptc) { if (modifyCmd.metadataId_ == iptc) {
Exiv2::IptcData::iterator pos = Exiv2::IptcData::iterator pos =
iptcData_.findKey(Exiv2::IptcKey(modifyCmd.key_)); iptcData.findKey(Exiv2::IptcKey(modifyCmd.key_));
if (pos != iptcData_.end()) iptcData_.erase(pos); if (pos != iptcData.end()) iptcData.erase(pos);
} }
} }
@ -1046,19 +1141,31 @@ namespace Action {
try { try {
adjustment_ = Params::instance().adjustment_; adjustment_ = Params::instance().adjustment_;
Exiv2::ExifData exifData; if (!Util::fileExists(path, true)) {
int rc = exifData.read(path); 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) { if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n"; std::cerr << Exiv2::Image::strError(rc, path) << "\n";
return rc; return rc;
} }
Exiv2::ExifData &exifData = image->exifData();
rc = adjustDateTime(exifData, "Exif.Image.DateTime", path); rc = adjustDateTime(exifData, "Exif.Image.DateTime", path);
rc += adjustDateTime(exifData, "Exif.Photo.DateTimeOriginal", path); rc += adjustDateTime(exifData, "Exif.Photo.DateTimeOriginal", path);
rc += adjustDateTime(exifData, "Exif.Photo.DateTimeDigitized", path); rc += adjustDateTime(exifData, "Exif.Photo.DateTimeDigitized", path);
if (rc) return 1; if (rc) return 1;
rc = exifData.write(path); rc = image->writeMetadata();
if (rc) { if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n"; std::cerr << Exiv2::Image::strError(rc, path) << "\n";
} }
return rc; return rc;
} }
@ -1182,7 +1289,7 @@ namespace {
return -1; return -1;
} }
Exiv2::Image::AutoPtr sourceImage Exiv2::Image::AutoPtr sourceImage
= Exiv2::ImageFactory::instance().open(source); = Exiv2::ImageFactory::open(source);
if (sourceImage.get() == 0) { if (sourceImage.get() == 0) {
std::cerr << source std::cerr << source
<< ": The file contains data of an unknown image type\n"; << ": The file contains data of an unknown image type\n";
@ -1195,7 +1302,7 @@ namespace {
return 1; return 1;
} }
Exiv2::Image::AutoPtr targetImage Exiv2::Image::AutoPtr targetImage
= Exiv2::ImageFactory::instance().open(target); = Exiv2::ImageFactory::open(target);
if (preserve && targetImage.get() != 0) { if (preserve && targetImage.get() != 0) {
if (targetImage->readMetadata()) { if (targetImage->readMetadata()) {
std::cerr << target std::cerr << target
@ -1205,7 +1312,7 @@ namespace {
} }
if (targetImage.get() == 0) { if (targetImage.get() == 0) {
targetImage targetImage
= Exiv2::ImageFactory::instance().create(Exiv2::Image::exv, target); = Exiv2::ImageFactory::create(Exiv2::Image::exv, target);
} }
if (targetImage.get() == 0) { if (targetImage.get() == 0) {
std::cerr << target std::cerr << target
@ -1213,22 +1320,20 @@ namespace {
return 2; return 2;
} }
if ( Params::instance().target_ & Params::ctExif if ( Params::instance().target_ & Params::ctExif
&& sourceImage->sizeExifData() > 0) { && sourceImage->exifData().count() > 0) {
if (Params::instance().verbose_) { if (Params::instance().verbose_) {
std::cout << "Writing Exif data from " << source std::cout << "Writing Exif data from " << source
<< " to " << target << std::endl; << " to " << target << std::endl;
} }
targetImage->setExifData(sourceImage->exifData(), targetImage->setExifData(sourceImage->exifData());
sourceImage->sizeExifData());
} }
if ( Params::instance().target_ & Params::ctIptc if ( Params::instance().target_ & Params::ctIptc
&& sourceImage->sizeIptcData() > 0) { && sourceImage->iptcData().count() > 0) {
if (Params::instance().verbose_) { if (Params::instance().verbose_) {
std::cout << "Writing Iptc data from " << source std::cout << "Writing Iptc data from " << source
<< " to " << target << std::endl; << " to " << target << std::endl;
} }
targetImage->setIptcData(sourceImage->iptcData(), targetImage->setIptcData(sourceImage->iptcData());
sourceImage->sizeIptcData());
} }
if ( Params::instance().target_ & Params::ctComment if ( Params::instance().target_ & Params::ctComment
&& !sourceImage->comment().empty()) { && !sourceImage->comment().empty()) {

@ -303,9 +303,12 @@ namespace Action {
virtual int run(const std::string& path); virtual int run(const std::string& path);
typedef std::auto_ptr<Modify> AutoPtr; typedef std::auto_ptr<Modify> AutoPtr;
AutoPtr clone() const; AutoPtr clone() const;
Modify() {}
private: private:
virtual Modify* clone_() const; virtual Modify* clone_() const;
//! Copy contructor needed because of AutoPtr memeber
Modify(const Modify& src) {}
//! Add a metadatum according to \em modifyCmd //! Add a metadatum according to \em modifyCmd
void addMetadatum(const ModifyCmd& modifyCmd); void addMetadatum(const ModifyCmd& modifyCmd);
@ -314,8 +317,7 @@ namespace Action {
//! Delete a metadatum according to \em modifyCmd //! Delete a metadatum according to \em modifyCmd
void delMetadatum(const ModifyCmd& modifyCmd); void delMetadatum(const ModifyCmd& modifyCmd);
Exiv2::ExifData exifData_; //!< Exif metadata Exiv2::Image::AutoPtr image_; //!< Image to modify
Exiv2::IptcData iptcData_; //!< Iptc metadata
}; // class Modify }; // class Modify
} // namespace Action } // namespace Action

@ -2,6 +2,7 @@
// addmoddel.cpp, $Rev$ // addmoddel.cpp, $Rev$
// Sample program showing how to add, modify and delete Exif metadata. // Sample program showing how to add, modify and delete Exif metadata.
#include "image.hpp"
#include "exif.hpp" #include "exif.hpp"
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
@ -14,7 +15,9 @@ try {
} }
std::string file(argv[1]); 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; Exiv2::ExifData exifData;
// ************************************************************************* // *************************************************************************
@ -90,10 +93,18 @@ try {
std::cout << "Deleted key \"" << key << "\"\n"; std::cout << "Deleted key \"" << key << "\"\n";
// ************************************************************************* // *************************************************************************
// Finally, write the remaining Exif data to an image file // Finally, write the remaining Exif data to the image file
int rc = exifData.write(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) { if (rc) {
std::string error = Exiv2::ExifData::strError(rc, file); std::string error = Exiv2::Image::strError(rc, file);
throw Exiv2::Error(error); throw Exiv2::Error(error);
} }

@ -0,0 +1,427 @@
// ***************************************************************** -*- C++ -*-
/*
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
*
* This program is part of the Exiv2 distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
File: basicio.cpp
Version: $Rev$
Author(s): Brad Schick (brad) <brad@robotbattle.com>
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 <cassert>
#include <sys/types.h> // for stat()
#include <sys/stat.h> // for stat()
#ifdef HAVE_PROCESS_H
# include <process.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h> // 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<BasicIo*>(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<FileIo*>(&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<MemIo*>(&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<BasicIo*>(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

@ -0,0 +1,605 @@
// ***************************************************************** -*- C++ -*-
/*
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
*
* This program is part of the Exiv2 distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*!
@file basicio.hpp
@brief Simple binary IO abstraction
@version $Rev$
@author Brad Schick (brad)
<a href="mailto:brad@robotbattle.com">brad@robotbattle.com</a>
@date 04-Dec-04, brad: created
*/
#ifndef BASICIO_HPP_
#define BASICIO_HPP_
// *****************************************************************************
// included header files
#include "types.hpp"
#include "error.hpp"
// + standard includes
#include <string>
#include <vector>
#include <cstdio>
// *****************************************************************************
// 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<BasicIo> 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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
-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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
-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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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;<BR>
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<byte> ByteVector;
// DATA
ByteVector data_;
ByteVector::size_type idx_;
//METHODS
void checkSize(long wcount);
}; // class MemIo
} // namespace Exiv2
#endif // #ifndef BASICIO_HPP_

@ -78,22 +78,30 @@ catch (Exiv2::Error& e) {
void write(const std::string& file, Exiv2::ExifData& ed) 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) { if (rc) {
std::string error = Exiv2::ExifData::strError(rc, file); std::string error = Exiv2::Image::strError(rc, file);
throw Exiv2::Error(error); throw Exiv2::Error(error);
} }
} }
void print(const std::string& file) void print(const std::string& file)
{ {
Exiv2::ExifData ed; Image::AutoPtr image = ImageFactory::open(file);
int rc = ed.read(file); assert(image.get() != 0);
// Load existing metadata
int rc = image->readMetadata();
if (rc) { if (rc) {
std::string error = Exiv2::ExifData::strError(rc, file); std::string error = Exiv2::Image::strError(rc, file);
throw Exiv2::Error(error); throw Exiv2::Error(error);
} }
Exiv2::ExifData &ed = image->exifData();
Exiv2::ExifData::const_iterator end = ed.end(); Exiv2::ExifData::const_iterator end = ed.end();
for (Exiv2::ExifData::const_iterator i = ed.begin(); i != end; ++i) { for (Exiv2::ExifData::const_iterator i = ed.begin(); i != end; ++i) {
std::cout << std::setw(35) << std::setfill(' ') << std::left std::cout << std::setw(35) << std::setfill(' ') << std::left
@ -109,29 +117,28 @@ void print(const std::string& file)
<< i->count() << " " << i->count() << " "
<< std::dec << i->value() << std::dec << i->value()
<< "\n"; << "\n";
} }
} }
int read(const std::string& path) int read(const std::string& path)
{ {
Image::AutoPtr image = ImageFactory::instance().open(path); Image::AutoPtr image = ImageFactory::open(path);
assert(image.get() != 0); assert(image.get() != 0);
int rc = image->readMetadata(); int rc = image->readMetadata();
if (rc) return rc; if (rc) return rc;
if (image->sizeExifData() > 0) { if (image->exifData().count() > 0) {
const byte *pData = image->exifData(); DataBuf exifData = image->exifData().copy();
long size = image->sizeExifData(); long size = exifData.size_;
// Read the TIFF header // Read the TIFF header
TiffHeader tiffHeader; TiffHeader tiffHeader;
rc = tiffHeader.read(pData); rc = tiffHeader.read(exifData.pData_);
if (rc) return rc; if (rc) return rc;
// Read IFD0 // Read IFD0
Ifd ifd0(ifd0Id); Ifd ifd0(ifd0Id);
rc = ifd0.read(pData + tiffHeader.offset(), rc = ifd0.read(exifData.pData_ + tiffHeader.offset(),
size - tiffHeader.offset(), size - tiffHeader.offset(),
tiffHeader.byteOrder(), tiffHeader.byteOrder(),
tiffHeader.offset()); tiffHeader.offset());
@ -143,7 +150,7 @@ int read(const std::string& path)
Value::AutoPtr v = Value::create(TypeId(i->type())); Value::AutoPtr v = Value::create(TypeId(i->type()));
v->read(i->data(), i->count() * i->typeSize(), tiffHeader.byteOrder()); 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; std::cout << "Value of tag 0x8298: " << std::hex;
v->write(std::cout); v->write(std::cout);
@ -162,7 +169,7 @@ int read(const std::string& path)
v = Value::create(TypeId(i->type())); v = Value::create(TypeId(i->type()));
v->read(i->data(), i->count() * i->typeSize(), tiffHeader.byteOrder()); 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: "; std::cout << "Value of tag 0x013b: ";
v->write(std::cout); v->write(std::cout);

@ -36,11 +36,12 @@ EXIV2_RCSID("@(#) $Id$");
// included header files // included header files
#include "exif.hpp" #include "exif.hpp"
#include "types.hpp" #include "types.hpp"
#include "basicio.hpp"
#include "error.hpp" #include "error.hpp"
#include "value.hpp" #include "value.hpp"
#include "ifd.hpp" #include "ifd.hpp"
#include "tags.hpp" #include "tags.hpp"
#include "image.hpp" #include "jpgimage.hpp"
#include "makernote.hpp" #include "makernote.hpp"
// + standard includes // + standard includes
@ -355,28 +356,7 @@ namespace Exiv2 {
return *pos; return *pos;
} }
int ExifData::read(const std::string& path) int ExifData::load(const byte* buf, long len)
{
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)
{ {
// Copy the data buffer // Copy the data buffer
delete[] pData_; delete[] pData_;
@ -476,38 +456,6 @@ namespace Exiv2 {
return ret; return ret;
} // ExifData::read } // 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() DataBuf ExifData::copy()
{ {
@ -660,15 +608,6 @@ namespace Exiv2 {
return buf; return buf;
} // ExifData::copyFromMetadata } // 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, void ExifData::add(Entries::const_iterator begin,
Entries::const_iterator end, Entries::const_iterator end,
ByteOrder byteOrder) ByteOrder byteOrder)
@ -846,11 +785,11 @@ namespace Exiv2 {
if (thumbnail.get() == 0) return 8; if (thumbnail.get() == 0) return 8;
std::string name = path + thumbnail->extension(); std::string name = path + thumbnail->extension();
FileCloser file(fopen(name.c_str(), "wb")); FileIo file(name);
if (!file.fp_) return -1; if (file.open("wb") != 0) return -1;
DataBuf buf(thumbnail->copy(*this)); 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 4;
} }
return 0; return 0;
@ -1197,14 +1136,14 @@ namespace {
Exiv2::DataBuf readFile(const std::string& path) Exiv2::DataBuf readFile(const std::string& path)
{ {
Exiv2::FileCloser file(fopen(path.c_str(), "rb")); Exiv2::FileIo file(path);
if (!file.fp_) if (file.open("rb") != 0)
throw Exiv2::Error("Couldn't open input file"); throw Exiv2::Error("Couldn't open input file");
struct stat st; struct stat st;
if (0 != stat(path.c_str(), &st)) if (0 != stat(path.c_str(), &st))
throw Exiv2::Error("Couldn't stat input file"); throw Exiv2::Error("Couldn't stat input file");
Exiv2::DataBuf buf(st.st_size); 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_) if (len != buf.size_)
throw Exiv2::Error("Couldn't read input file"); throw Exiv2::Error("Couldn't read input file");
return buf; return buf;

@ -480,9 +480,6 @@ namespace Exiv2 {
- extract and delete Exif thumbnail (JPEG and TIFF thumbnails) - extract and delete Exif thumbnail (JPEG and TIFF thumbnails)
*/ */
class ExifData { class ExifData {
//! @name Not implemented
//@{
//@}
public: public:
//! ExifMetadata iterator type //! ExifMetadata iterator type
typedef ExifMetadata::iterator iterator; typedef ExifMetadata::iterator iterator;
@ -504,43 +501,13 @@ namespace Exiv2 {
//! Assignment operator (Todo: assign image data also) //! Assignment operator (Todo: assign image data also)
ExifData& operator=(const ExifData& rhs); ExifData& operator=(const ExifData& rhs);
/*! /*!
@brief Read the Exif data from file \em path. @brief Load the Exif data from a byte buffer. The data buffer
@param path Path to the file
@return 0 if successful;<BR>
3 if the file contains no Exif data;<BR>
the return code of Image::readMetadata()
if the call to this function fails<BR>
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
must start with the TIFF header. must start with the TIFF header.
@param buf Pointer to the data buffer to read from @param buf Pointer to the data buffer to read from
@param len Number of bytes in the data buffer @param len Number of bytes in the data buffer
@return 0 if successful. @return 0 if successful.
*/ */
int read(const byte* buf, long len); int load(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);
/*! /*!
@brief Write the Exif data to a data buffer, which is returned. The @brief Write the Exif data to a data buffer, which is returned. The
caller owns this copy and %DataBuf ensures that it will be caller owns this copy and %DataBuf ensures that it will be
@ -603,6 +570,11 @@ namespace Exiv2 {
by this call. by this call.
*/ */
iterator erase(iterator pos); 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 //! Sort metadata by key
void sortByKey(); void sortByKey();
//! Sort metadata by tag //! Sort metadata by tag
@ -709,12 +681,6 @@ namespace Exiv2 {
//! @name Accessors //! @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 //! Begin of the metadata
const_iterator begin() const { return exifMetadata_.begin(); } const_iterator begin() const { return exifMetadata_.begin(); }
//! End of the metadata //! End of the metadata
@ -780,14 +746,13 @@ namespace Exiv2 {
/*! /*!
@brief Convert the return code \em rc from \n @brief Convert the return code \em rc from \n
int read(const std::string& path); \n int read(const byte* buf, long len), \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
into an error message. 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); static std::string strError(int rc, const std::string& path);

@ -9,6 +9,7 @@
*/ */
// ***************************************************************************** // *****************************************************************************
// included header files // included header files
#include "image.hpp"
#include "exif.hpp" #include "exif.hpp"
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
@ -24,13 +25,22 @@ try {
return 1; return 1;
} }
Exiv2::ExifData exifData; Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]);
int rc = exifData.read(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) { if (rc) {
std::string error = Exiv2::ExifData::strError(rc, argv[1]); std::string error = Exiv2::Image::strError(rc, argv[1]);
throw Exiv2::Error(error); throw Exiv2::Error(error);
} }
Exiv2::ExifData &exifData = image->exifData();
/* /*
There are two pitfalls that we need to consider when setting the Exif user There are two pitfalls that we need to consider when setting the Exif user
comment (Exif.Photo.UserComment) of an image: comment (Exif.Photo.UserComment) of an image:
@ -76,13 +86,13 @@ try {
// output operator to print the formatted value // output operator to print the formatted value
std::cout << "Writing user comment '" << *pos << "' back to the image\n"; std::cout << "Writing user comment '" << *pos << "' back to the image\n";
rc = exifData.write(argv[1]); rc = image->writeMetadata();
if (rc) { if (rc) {
std::string error = Exiv2::ExifData::strError(rc, argv[1]); std::string error = Exiv2::Image::strError(rc, argv[1]);
throw Exiv2::Error(error); throw Exiv2::Error(error);
} }
return rc; return rc;
} }
catch (Exiv2::Error& e) { catch (Exiv2::Error& e) {
std::cout << "Caught Exiv2 exception '" << e << "'\n"; std::cout << "Caught Exiv2 exception '" << e << "'\n";

@ -2,6 +2,7 @@
// exifprint.cpp, $Rev$ // exifprint.cpp, $Rev$
// Sample program to print the Exif metadata of an image // Sample program to print the Exif metadata of an image
#include "image.hpp"
#include "exif.hpp" #include "exif.hpp"
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
@ -14,13 +15,21 @@ try {
return 1; return 1;
} }
Exiv2::ExifData exifData; Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]);
int rc = exifData.read(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) { if (rc) {
std::string error = Exiv2::ExifData::strError(rc, argv[1]); std::string error = Exiv2::Image::strError(rc, argv[1]);
throw Exiv2::Error(error); throw Exiv2::Error(error);
} }
Exiv2::ExifData &exifData = image->exifData();
Exiv2::ExifData::const_iterator end = exifData.end(); Exiv2::ExifData::const_iterator end = exifData.end();
for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) { for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
std::cout << std::setw(53) << std::setfill(' ') << std::left std::cout << std::setw(53) << std::setfill(' ') << std::left

@ -26,6 +26,7 @@
History: 26-Jan-04, ahu: created History: 26-Jan-04, ahu: created
11-Feb-04, ahu: isolated as a component 11-Feb-04, ahu: isolated as a component
19-Jul-04, brad: revamped to be more flexible and support Iptc 19-Jul-04, brad: revamped to be more flexible and support Iptc
15-Jan-05, brad: inside-out design changes
*/ */
// ***************************************************************************** // *****************************************************************************
#include "rcsid.hpp" #include "rcsid.hpp"
@ -42,9 +43,11 @@ EXIV2_RCSID("@(#) $Id$");
#endif #endif
#include "image.hpp" #include "image.hpp"
#include "types.hpp"
#include "error.hpp" #include "error.hpp"
// Ensure registration with factory
#include "jpgimage.hpp"
// + standard includes // + standard includes
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
@ -55,71 +58,54 @@ EXIV2_RCSID("@(#) $Id$");
#ifdef _MSC_VER #ifdef _MSC_VER
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif #endif
#ifdef HAVE_PROCESS_H
# include <process.h>
#endif
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
# include <unistd.h> // for getpid, stat # include <unistd.h> // stat
#endif #endif
// ***************************************************************************** // *****************************************************************************
// class member definitions // class member definitions
namespace Exiv2 { namespace Exiv2 {
// Local functions. These could be static private functions on Image ImageFactory::Registry* ImageFactory::registry_ = 0;
// 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& ImageFactory::instance() void ImageFactory::init()
{ {
if (0 == pInstance_) { if (0 == registry_) {
pInstance_ = new ImageFactory; registry_ = new Registry;
} }
return *pInstance_; }
} // ImageFactory::instance
void ImageFactory::registerImage(Image::Type type, void ImageFactory::registerImage(Image::Type type,
NewInstanceFct newInst, IsThisTypeFct isType) NewInstanceFct newInst, IsThisTypeFct isType)
{ {
init();
assert (newInst && isType); assert (newInst && isType);
registry_[type] = ImageFcts(newInst, isType); (*registry_)[type] = ImageFcts(newInst, isType);
} // ImageFactory::registerImage }
Image::Type ImageFactory::getType(const std::string& path)
{
FileIo fileIo(path);
return getType(fileIo);
}
ImageFactory::ImageFactory() Image::Type ImageFactory::getType(const byte* data, long size)
{ {
// Register a prototype of each known image MemIo memIo(data, size);
registerImage(Image::jpeg, newJpegInstance, isJpegType); return getType(memIo);
registerImage(Image::exv, newExvInstance, isExvType); }
} // ImageFactory c'tor
Image::Type ImageFactory::getType(const std::string& path) const Image::Type ImageFactory::getType(BasicIo& io)
{ {
FileCloser closer(fopen(path.c_str(), "rb")); IoCloser closer(io);
if (!closer.fp_) return Image::none; if (io.open() != 0) return Image::none;
Image::Type type = Image::none; Image::Type type = Image::none;
Registry::const_iterator b = registry_.begin(); Registry::const_iterator b = registry_->begin();
Registry::const_iterator e = registry_.end(); Registry::const_iterator e = registry_->end();
for (Registry::const_iterator i = b; i != e; ++i) 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; type = i->first;
break; break;
} }
@ -127,657 +113,96 @@ namespace Exiv2 {
return type; return type;
} // ImageFactory::getType } // ImageFactory::getType
Image::AutoPtr ImageFactory::open(const std::string& path) const Image::AutoPtr ImageFactory::open(const std::string& path)
{
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(); BasicIo::AutoPtr io(new FileIo(path));
return open(io);
} }
void JpegBase::setExifData(const byte* buf, long size) Image::AutoPtr ImageFactory::open(const byte* data, long size)
{ {
if (size > 0xfffd) throw Error("Exif data too large"); BasicIo::AutoPtr io(new MemIo(data, size));
clearExifData(); return open(io);
if (size) {
sizeExifData_ = size;
pExifData_ = new byte[size];
memcpy(pExifData_, buf, size);
}
} }
void JpegBase::setIptcData(const byte* buf, long size) Image::AutoPtr ImageFactory::open(BasicIo::AutoPtr io)
{ {
clearIptcData(); Image::AutoPtr image;
if (size) { IoCloser closer(*io);
sizeIptcData_ = size; if (io->open() != 0) return image;
pIptcData_ = new byte[size];
memcpy(pIptcData_, buf, size);
}
}
void JpegBase::setComment(const std::string& comment)
{
comment_ = comment;
}
void JpegBase::setMetadata(const Image& image)
{
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<char*>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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;
// 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 Registry::const_iterator b = registry_->begin();
if ( fwrite(record+sizeOldData, 1, sizeEnd, ofp) Registry::const_iterator e = registry_->end();
!= (size_t)sizeEnd) return 4; for (Registry::const_iterator i = b; i != e; ++i)
if (ferror(ofp)) return 4; {
} if (i->second.isThisType(*io, false)) {
} image = i->second.newInstance(io, false);
if (marker == eoi_) {
break; 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; 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]) { Image::AutoPtr ImageFactory::create(Image::Type type,
result = false; const std::string& path)
} {
if (!advance || !result ) fseek(ifp, -2, SEEK_CUR); FileIo *fileIo = new FileIo(path);
return result; 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"; Image::AutoPtr ImageFactory::create(Image::Type type)
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_))
{ {
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; std::string error = path + ": ";
if (create) { switch (rc) {
image = Image::AutoPtr(new ExvImage(path, true)); case -1:
} error += "Failed to open the file";
else { break;
image = Image::AutoPtr(new ExvImage(path, false)); 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 error;
return image; } // 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) TiffHeader::TiffHeader(ByteOrder byteOrder)
: byteOrder_(byteOrder), tag_(0x002a), offset_(0x00000008) : byteOrder_(byteOrder), tag_(0x002a), offset_(0x00000008)

@ -29,6 +29,7 @@
@date 09-Jan-04, ahu: created<BR> @date 09-Jan-04, ahu: created<BR>
11-Feb-04, ahu: isolated as a component<BR> 11-Feb-04, ahu: isolated as a component<BR>
19-Jul-04, brad: revamped to be more flexible and support Iptc 19-Jul-04, brad: revamped to be more flexible and support Iptc
15-Jan-05, brad: inside-out design changes
*/ */
#ifndef IMAGE_HPP_ #ifndef IMAGE_HPP_
#define IMAGE_HPP_ #define IMAGE_HPP_
@ -36,30 +37,40 @@
// ***************************************************************************** // *****************************************************************************
// included header files // included header files
#include "types.hpp" #include "types.hpp"
#include "basicio.hpp"
// + standard includes // + standard includes
#include <string> #include <string>
#include <map> #include <map>
#include <memory>
// ***************************************************************************** // *****************************************************************************
// namespace extensions // namespace extensions
namespace Exiv2 { namespace Exiv2 {
// ***************************************************************************** // *****************************************************************************
// class definitions // class declarations
class ExifData;
class IptcData;
// *****************************************************************************
// class definitions
/*! /*!
@brief Abstract base class defining the interface for an image. @brief Abstract base class defining the interface for an image. This is
the top-level interface to the Exiv2 library.
Most client apps will obtain an Image instance by calling a static
ImageFactory method. The Image class can then be used to to
read, write, and save metadata.
*/ */
class Image { class Image {
public: public:
//! Image auto_ptr type
typedef std::auto_ptr<Image> AutoPtr;
//! Supported image formats //! Supported image formats
enum Type { none, jpeg, exv }; enum Type { none, jpeg, exv };
//! Image auto_ptr type
typedef std::auto_ptr<Image> AutoPtr;
//! @name Creators //! @name Creators
//@{ //@{
//! Virtual Destructor //! Virtual Destructor
@ -69,61 +80,67 @@ namespace Exiv2 {
//! @name Manipulators //! @name Manipulators
//@{ //@{
/*! /*!
@brief Read metadata from assigned image file into internal @brief Read metadata from assigned image. Before this method
buffers. is called, the various metadata types (Iptc, Exif) will be empty.
@return 0 if successful. @return 0 if successful.
*/ */
virtual int readMetadata() =0; virtual int readMetadata() =0;
/*! /*!
@brief Write metadata from internal buffers into to the image fle. @brief Write metadata back to the image.
All existing metadata sections in the image are either created,
replaced, or erased. If values for a given metadata type have been
assigned, a section for that metadata type will either be created or
replaced. If no values have been assigned to a given metadata type,
any exists section for that metadata type will be removed from the
image.
@return 0 if successful. @return 0 if successful.
*/ */
virtual int writeMetadata() =0; virtual int writeMetadata() =0;
/*! /*!
@brief Set the Exif data. The data is copied into an internal data @brief Assign new exif data. The new exif data is not written
buffer and is not written until writeMetadata is called. to the image until the writeMetadata() method is called.
@param buf Pointer to the new Exif data. @param exifData An ExifData instance holding exif data to be copied
@param size Size in bytes of new Exif data.
*/ */
virtual void setExifData(const byte* buf, long size) =0; virtual void setExifData(const ExifData& exifData) =0;
/*! /*!
@brief Erase any buffered Exif data. Exif data is not removed @brief Erase any buffered Exif data. Exif data is not removed from
from the actual file until writeMetadata is called. the actual image until the writeMetadata() method is called.
*/ */
virtual void clearExifData() =0; virtual void clearExifData() =0;
/*! /*!
@brief Set the Iptc data. The data is copied into an internal data @brief Assign new iptc data. The new iptc data is not written
buffer and is not written until writeMetadata is called. to the image until the writeMetadata() method is called.
@param buf Pointer to the new Iptc data. @param iptcData An IptcData instance holding iptc data to be copied
@param size Size in bytes of new Iptc data.
*/ */
virtual void setIptcData(const byte* buf, long size) =0; virtual void setIptcData(const IptcData& iptcData) =0;
/*! /*!
@brief Erase any buffered Iptc data. Iptc data is not removed @brief Erase any buffered Iptc data. Iptc data is not removed from
from the actual file until writeMetadata is called. the actual image until the writeMetadata() method is called.
*/ */
virtual void clearIptcData() =0; virtual void clearIptcData() =0;
/*! /*!
@brief Set the image comment. The data is copied into an internal data @brief Set the image comment. The new comment is not written
buffer and is not written until writeMetadata is called. to the image until the writeMetadata() method is called.
@param comment String containing comment. @param comment String containing comment.
*/ */
virtual void setComment(const std::string& comment) =0; virtual void setComment(const std::string& comment) =0;
/*! /*!
@brief Erase any buffered comment. Comment is not removed @brief Erase any buffered comment. Comment is not removed
from the actual file until writeMetadata is called. from the actual image until the writeMetadata() method is called.
*/ */
virtual void clearComment() =0; virtual void clearComment() =0;
/*! /*!
@brief Copy all existing metadata from source %Image. The data is @brief Copy all existing metadata from source Image. The data is
copied into internal buffers and is not written until copied into internal buffers and is not written to the image
writeMetadata is called. until the writeMetadata() method is called.
@param image Metadata source. All metadata types are copied. @param image Metadata source. All metadata types are copied.
*/ */
virtual void setMetadata(const Image& image) =0; virtual void setMetadata(const Image& image) =0;
/*! /*!
@brief Erase all buffered metadata. Metadata is not removed @brief Erase all buffered metadata. Metadata is not removed
from the actual file until writeMetadata is called. from the actual image until the writeMetadata() method is called.
*/ */
virtual void clearMetadata() =0; virtual void clearMetadata() =0;
//@} //@}
@ -131,31 +148,91 @@ namespace Exiv2 {
//! @name Accessors //! @name Accessors
//@{ //@{
/*! /*!
@brief Check if the %Image instance is valid. Use after object @brief Check if the Image instance is valid. Use after object
construction. construction.
@return true if the %Image is in a valid state. @return true if the Image is in a valid state.
*/ */
virtual bool good() const =0; virtual bool good() const =0;
//! Return the size of the Exif data in bytes.
virtual long sizeExifData() const =0;
/*! /*!
@brief Return a read-only pointer to an Exif data buffer. Do not @brief Returns an ExifData instance containing currently buffered
attempt to write to this buffer. exif data.
The exif data may have been read from the image by
a previous call to readMetadata() or added directly. The exif
data in the returned instance will be written to the image when
writeMetadata() is called.
@return read only ExifData instance containing exif values
*/
virtual const ExifData& exifData() const =0;
/*!
@brief Returns an ExifData instance containing currently buffered
exif data.
The contained exif data may have been read from the image by
a previous call to readMetadata() or added directly. The exif
data in the returned instance will be written to the image when
writeMetadata() is called.
@return modifiable ExifData instance containing exif values
*/ */
virtual const byte* exifData() const =0; virtual ExifData& exifData() =0;
//! Return the size of the Iptc data in bytes.
virtual long sizeIptcData() const =0;
/*! /*!
@brief Return a read-only pointer to an Iptc data buffer. Do not @brief Returns an IptcData instance containing currently buffered
attempt to write to this buffer. iptc data.
The contained iptc data may have been read from the image by
a previous call to readMetadata() or added directly. The iptc
data in the returned instance will be written to the image when
writeMetadata() is called.
@return modifiable IptcData instance containing iptc values
*/ */
virtual const byte* iptcData() const =0; virtual const IptcData& iptcData() const =0;
/*!
@brief Returns an ExifData instance containing currently buffered
exif data.
The contained iptc data may have been read from the image by
a previous call to readMetadata() or added directly. The iptc
data in the returned instance will be written to the image when
writeMetadata() is called.
@return modifiable IptcData instance containing iptc values
*/
virtual IptcData& iptcData() =0;
/*! /*!
@brief Return a copy of the image comment. May be an empty string. @brief Return a copy of the image comment. May be an empty string.
*/ */
virtual std::string comment() const =0; virtual std::string comment() const =0;
/*!
@brief Return a reference to the BasicIo instance being used for Io.
This refence is particularly useful to reading the results of
operations on a MemIo instance. For example after metadata has
been modified and the writeMetadata() method has been called,
this method can be used to get access to the modified image.
@return BasicIo instance that can be used to read or write image
data directly.
@note If the returned BasicIo is used to write to the image, the
Image class will not see those changes until the readMetadata()
method is called.
*/
virtual BasicIo& io() const = 0;
/*!
@brief Convert the return code \em rc from various methods
in this class to string messages.
@param rc Error code.
@param path %Image file or other identifying string.
@return String containing error message.
Todo: Implement global handling of error messages
*/
static std::string strError(int rc, const std::string& path);
//@} //@}
protected: protected:
//! @name Creators //! @name Creators
//@{ //@{
@ -173,17 +250,15 @@ namespace Exiv2 {
}; // class Image }; // class Image
//! Type for function pointer that creates new Image instances //! Type for function pointer that creates new Image instances
typedef Image::AutoPtr (*NewInstanceFct)(const std::string& path, typedef Image::AutoPtr (*NewInstanceFct)(BasicIo::AutoPtr io, bool create);
bool create);
//! Type for function pointer that checks image types //! Type for function pointer that checks image types
typedef bool (*IsThisTypeFct)(FILE* ifp, bool advance); typedef bool (*IsThisTypeFct)(BasicIo& iIo, bool advance);
/*! /*!
@brief Image factory. @brief Returns an Image instance of the specified type.
Creates an instance of the image of the requested type. The factory is The factory is implemented as a singleton, which can be accessed
implemented as a singleton, which can be accessed only through the static through static member functions.
member function instance().
*/ */
class ImageFactory { class ImageFactory {
public: public:
@ -192,16 +267,16 @@ namespace Exiv2 {
/*! /*!
@brief Register image type together with its function pointers. @brief Register image type together with its function pointers.
The image factory creates new images calling their associated The image factory creates new images by calling their associated
function pointer. Additional images can be added by registering function pointer. Additional images can be added by registering
new type and function pointers. If called for a type that already new type and function pointers. If called for a type that already
exists in the list, the corresponding prototype is replaced. exists in the list, the corresponding functions are replaced.
@param type Image type. @param type Image type.
@param newInst Function pointer for creating image instances. @param newInst Function pointer for creating image instances.
@param isType Function pointer to test for matching image types. @param isType Function pointer to test for matching image types.
*/ */
void registerImage(Image::Type type, static void registerImage(Image::Type type,
NewInstanceFct newInst, NewInstanceFct newInst,
IsThisTypeFct isType); IsThisTypeFct isType);
//@} //@}
@ -209,41 +284,101 @@ namespace Exiv2 {
//! @name Accessors //! @name Accessors
//@{ //@{
/*! /*!
@brief Create an %Image of the appropriate type by opening the @brief Create an Image subclass of the appropriate type by reading
specified file. File type is derived from the contents of the the specified file. %Image type is derived from the file
file. contents.
@param path %Image file. The contents of the file are tested to
determine the image type to open. File extension is ignored.
@return An auto-pointer that owns an %Image of the type derived from
the file. If no image type could be determined, the pointer is 0.
*/
Image::AutoPtr open(const std::string& path) const;
/*!
@brief Create an %Image of the requested type by creating a new
file. If the file already exists, it will be overwritten.
@param type Type of the image to be created.
@param path %Image file. The contents of the file are tested to
determine the image type to open. File extension is ignored.
@return An auto-pointer that owns an %Image of the requested type.
If the image type is not supported, the pointer is 0.
*/
Image::AutoPtr create(Image::Type type, const std::string& path) const;
/*!
@brief Returns the image type of the provided file.
@param path %Image file. The contents of the file are tested to @param path %Image file. The contents of the file are tested to
determine the image type. File extension is ignored. determine the image type. File extension is ignored.
@return %Image type of Image::none if the type is not recognized. @return An auto-pointer that owns an Image instance whose type
*/ matches that of the file. If no image type could be determined,
Image::Type getType(const std::string& path) const; the pointer is 0.
*/
static Image::AutoPtr open(const std::string& path);
/*!
@brief Create an Image subclass of the appropriate type by reading
the provided memory. %Image type is derived from the memory
contents.
@param data Pointer to a data buffer containing an image. The contents
of the memory are tested to determine the image type.
@param size Number of bytes pointed to by \em data.
@return An auto-pointer that owns an Image instance whose type
matches that of the data buffer. If no image type could be
determined, the pointer is 0.
*/
static Image::AutoPtr open(const byte* data, long size);
/*!
@brief Create an Image subclass of the appropriate type by reading
the provided BasicIo instance. %Image type is derived from the
data provided by \em io. The passed in \em io instance is
(re)opened by this method.
@param io An auto-pointer that owns a BasicIo instance that provides
image data. The contents of the image data are tested to determine
the type. \b Important: This method takes ownership of the passed
in BasicIo instance through the auto-pointer. Callers should not
continue to use the BasicIo instance after it is passed to this method.
Use theImage::io() method to get a temporary reference.
@return An auto-pointer that owns an Image instance whose type
matches that of the \em io data. If no image type could be
determined, the pointer is 0.
*/
static Image::AutoPtr open(BasicIo::AutoPtr io);
/*!
@brief Create an Image subclass of the requested type by creating a
new image file. If the file already exists, it will be overwritten.
@param type Type of the image to be created.
@param path %Image file to create. File extension is ignored.
@return An auto-pointer that owns an Image instance of the requested
type. If the image type is not supported, the pointer is 0.
*/
static Image::AutoPtr create(Image::Type type, const std::string& path);
/*!
@brief Create an Image subclass of the requested type by creating a
new image in memory.
@param type Type of the image to be created.
@return An auto-pointer that owns an Image instance of the requested
type. If the image type is not supported, the pointer is 0.
*/
static Image::AutoPtr create(Image::Type type);
/*!
@brief Create an Image subclass of the requested type by writing a
new image to a BasicIo instance. If the BasicIo instance already
contains data, it will be overwritten.
@param type Type of the image to be created.
@param io An auto-pointer that owns a BasicIo instance that will
be written to when creating a new image. \b Important: This
method takes ownership of the passed in BasicIo instance through
the auto-pointer. Callers should not continue to use the BasicIo
instance after it is passed to this method. Use theImage::io()
method to get a temporary reference.
@return An auto-pointer that owns an Image instance of the requested
type. If the image type is not supported, the pointer is 0.
*/
static Image::AutoPtr create(Image::Type type, BasicIo::AutoPtr io);
/*!
@brief Returns the image type of the provided file.
@param path %Image file. The contents of the file are tested to
determine the image type. File extension is ignored.
@return %Image type or Image::none if the type is not recognized.
*/
static Image::Type getType(const std::string& path);
/*!
@brief Returns the image type of the provided data buffer.
@param data Pointer to a data buffer containing an image. The contents
of the memory are tested to determine the image type.
@param size Number of bytes pointed to by \em data.
@return %Image type or Image::none if the type is not recognized.
*/
static Image::Type getType(const byte* data, long size);
/*!
@brief Returns the image type of data provided by a BasicIo instance.
The passed in \em io instance is (re)opened by this method.
@param io A BasicIo instance that provides image data. The contents
of the image data are tested to determine the type.
@return %Image type or Image::none if the type is not recognized.
*/
static Image::Type getType(BasicIo& io);
//@} //@}
/*!
@brief Get access to the image factory.
Clients access the image factory exclusively through
this method.
*/
static ImageFactory& instance();
private: private:
//! @name Creators //! @name Creators
@ -252,6 +387,8 @@ namespace Exiv2 {
ImageFactory(); ImageFactory();
//! Prevent copy construction: not implemented. //! Prevent copy construction: not implemented.
ImageFactory(const ImageFactory& rhs); ImageFactory(const ImageFactory& rhs);
//! Creates the private static instance
static void init();
//@} //@}
//! Struct for storing image function pointers. //! Struct for storing image function pointers.
@ -265,331 +402,12 @@ namespace Exiv2 {
}; };
// DATA // DATA
//! Pointer to the one and only instance of this class.
static ImageFactory* pInstance_;
//! Type used to store Image creation functions //! Type used to store Image creation functions
typedef std::map<Image::Type, ImageFcts> Registry; typedef std::map<Image::Type, ImageFcts> Registry;
//! List of image types and corresponding creation functions. //! List of image types and corresponding creation functions.
Registry registry_; static Registry* registry_;
}; // class ImageFactory }; // class ImageFactory
/*!
@brief Abstract helper base class to access JPEG images
*/
class JpegBase : public Image {
public:
//! @name Creators
//@{
//! Virtual destructor.
virtual ~JpegBase();
//@}
//! @name Manipulators
//@{
/*!
@brief Read all metadata from the file into the internal
data buffers. This method returns success even when
no metadata is found in the image. Callers must therefore
check the size of indivdual metadata types before
accessing the data.
@return 0 if successful;<BR>
1 if reading from the file failed
(could be caused by invalid image);<BR>
2 if the file does not contain a valid image;<BR>
*/
int readMetadata();
/*!
@brief Write all buffered metadata to associated file. All existing
metadata sections in the file are either replaced or erased.
If data for a given metadata type has not been assigned,
then that metadata type will be erased from the file.
@return 0 if successful;<br>
1 if reading from the file failed;<BR>
2 if the file does not contain a valid image;<BR>
4 if the temporary output file can not be written to;<BR>
-1 if the newly created file could not be reopened;<BR>
-3 if the temporary output file can not be opened;<BR>
-4 if renaming the temporary file fails;<br>
*/
int writeMetadata();
/*!
@brief Set the Exif data. The data is copied into an internal data
buffer and is not written until writeMetadata is called.
@param buf Pointer to the new Exif data.
@param size Size in bytes of new Exif data.
@throw Error ("Exif data too large") if the exif data is larger than
65535 bytes (the maximum size of JPEG APP segments)
*/
void setExifData(const byte* buf, long size);
void clearExifData();
void setIptcData(const byte* buf, long size);
void clearIptcData();
void setComment(const std::string& comment);
void clearComment();
void setMetadata(const Image& image);
void clearMetadata();
//@}
//! @name Accessors
//@{
bool good() const;
long sizeExifData() const { return sizeExifData_; }
const byte* exifData() const { return pExifData_; }
long sizeIptcData() const { return sizeIptcData_; }
const byte* iptcData() const { return pIptcData_; }
std::string comment() const { return comment_; }
//@}
protected:
//! @name Creators
//@{
/*!
@brief Constructor that can either open an existing image or create
a new image from scratch. If a new image is to be created, any
existing file is overwritten
@param path Full path to image file.
@param create Specifies if an existing file should be opened (false)
or if a new file should be created (true).
@param initData Data to initialize newly created files. Only used
when %create is true. Should contain the data for the smallest
valid image of the calling subclass.
@param dataSize Size of initData in bytes.
*/
JpegBase(const std::string& path, bool create,
const byte initData[], size_t dataSize);
//@}
//! @name Accessors
//@{
/*!
@brief Writes the image header (aka signature) to the file stream.
@param ofp File stream that the header is written to.
@return 0 if successful;<BR>
4 if the output file can not be written to;<BR>
*/
virtual int writeHeader(FILE* ofp) const =0;
/*!
@brief Determine if the content of the stream is of the type of this
class.
The advance flag determines if the read position in the stream is
moved (see below). This applies only if the type matches and the
function returns true. If the type does not match, the stream
position is not changed. However, if reading from the stream fails,
the stream position is undefined. Consult the stream state to obtain
more information in this case.
@param ifp Input file stream.
@param advance Flag indicating whether the read position in the stream
should be advanced by the number of characters read to
analyse the stream (true) or left at its original
position (false). This applies only if the type matches.
@return true if the stream data matches the type of this class;<BR>
false if the stream data does not match;<BR>
*/
virtual bool isThisType(FILE* ifp, bool advance) const =0;
//@}
// Constant Data
static const byte sos_; //!< JPEG SOS marker
static const byte eoi_; //!< JPEG EOI marker
static const byte app0_; //!< JPEG APP0 marker
static const byte app1_; //!< JPEG APP1 marker
static const byte app13_; //!< JPEG APP13 marker
static const byte com_; //!< JPEG Comment marker
static const char exifId_[]; //!< Exif identifier
static const char jfifId_[]; //!< JFIF identifier
static const char ps3Id_[]; //!< Photoshop marker
static const char bimId_[]; //!< Photoshop marker
static const uint16_t iptc_; //!< Photoshop Iptc marker
private:
// DATA
const std::string path_; //!< Image file name
long sizeExifData_; //!< Size of the Exif data buffer
byte* pExifData_; //!< Exif data buffer
long sizeIptcData_; //!< Size of the Iptc data buffer
byte* pIptcData_; //!< Iptc data buffer
std::string comment_; //!< JPEG comment
// METHODS
/*!
@brief Advances file stream to one byte past the next Jpeg marker
and returns the marker. This method should be called when the
file stream is positioned one byte past the end of a Jpeg segment.
@param fp File stream to advance
@return the next Jpeg segment marker if successful;<BR>
-1 if a maker was not found before EOF;<BR>
*/
int advanceToMarker(FILE *fp) const;
/*!
@brief Locates Photoshop formated Iptc data in a memory buffer.
Operates on raw data (rather than file streams) to simplify reuse.
@param pPsData Pointer to buffer containing entire payload of
Photoshop formated APP13 Jpeg segment.
@param sizePsData Size in bytes of pPsData.
@param record Output value that is set to the start of the Iptc
data block within pPsData (may not be null).
@param sizeHdr Output value that is set to the size of the header
within the Iptc data block pointed to by record (may not
be null).
@param sizeIptc Output value that is set to the size of the actual
Iptc data within the Iptc data block pointed to by record
(may not be null).
@return 0 if successful;<BR>
3 if no Iptc data was found in pPsData;<BR>
-2 if the pPsData buffer does not contain valid data;<BR>
*/
int locateIptcData(const byte *pPsData,
long sizePsData,
const byte **record,
uint16_t *const sizeHdr,
uint16_t *const sizeIptc) const;
/*!
@brief Write to the specified file stream with the provided data.
@param fp File stream to be written to (should be "w+b" mode)
@param initData Data to be written to the associated file
@param dataSize Size in bytes of data to be written
@return 0 if successful;<BR>
4 if the output file can not be written to;<BR>
*/
int initFile(FILE* fp, const byte initData[], size_t dataSize);
/*!
@brief Provides the main implementation of writeMetadata by
writing all buffered metadata to associated file.
@param ifp Input file stream. Non-metadata is copied to output file.
@param ofp Output file stream to write to (e.g., a temporary file).
@return 0 if successful;<br>
1 if reading from input file failed;<BR>
2 if the input file does not contain a valid image;<BR>
4 if the output file can not be written to;<BR>
*/
int doWriteMetadata(FILE *ifp, FILE* ofp) const;
// NOT Implemented
//! Default constructor.
JpegBase();
//! Copy constructor
JpegBase(const JpegBase& rhs);
//! Assignment operator
JpegBase& operator=(const JpegBase& rhs);
}; // class JpegBase
/*!
@brief Helper class to access JPEG images
*/
class JpegImage : public JpegBase {
friend bool isJpegType(FILE* ifp, bool advance);
public:
//! @name Creators
//@{
/*!
@brief Constructor that can either open an existing Jpeg image or create
a new image from scratch. If a new image is to be created, any
existing file is overwritten. Since the constructor can not return
a result, callers should check the %good method after object
construction to determine success or failure.
@param path Full path to image file.
@param create Specifies if an existing file should be opened (false)
or if a new file should be created (true).
*/
JpegImage(const std::string& path, bool create);
//! Destructor
~JpegImage() {}
//@}
protected:
//! @name Accessors
//@{
/*!
@brief Writes a Jpeg header (aka signature) to the file stream.
@param ofp File stream that the header is written to.
@return 0 if successful;<BR>
4 if the output file can not be written to;<BR>
*/
int writeHeader(FILE* ofp) const;
/*!
@brief Determine if the content of the file stream is a Jpeg image.
See base class for more details.
@param ifp Input file stream.
@param advance Flag indicating whether the read position in the stream
should be advanced by the number of characters read to
analyse the stream (true) or left at its original
position (false). This applies only if the type matches.
@return true if the file stream data matches a Jpeg image;<BR>
false if the stream data does not match;<BR>
*/
bool isThisType(FILE* ifp, bool advance) const;
//@}
private:
// Constant data
static const byte soi_; // SOI marker
static const byte blank_[]; // Minimal Jpeg image
// NOT Implemented
//! Default constructor
JpegImage();
//! Copy constructor
JpegImage(const JpegImage& rhs);
//! Assignment operator
JpegImage& operator=(const JpegImage& rhs);
}; // class JpegImage
//! Helper class to access %Exiv2 files
class ExvImage : public JpegBase {
friend bool isExvType(FILE* ifp, bool advance);
public:
//! @name Creators
//@{
/*!
@brief Constructor that can either open an existing Exv image or create
a new image from scratch. If a new image is to be created, any
existing file is overwritten. Since the constructor can not return
a result, callers should check the %good method after object
construction to determine success or failure.
@param path Full path to image file.
@param create Specifies if an existing file should be opened (false)
or if a new file should be created (true).
*/
ExvImage(const std::string& path, bool create);
//! Destructor
~ExvImage() {}
//@}
protected:
//! @name Accessors
//@{
/*!
@brief Writes an Exv header (aka signature) to the file stream.
@param ofp File stream that the header is written to.
@return 0 if successful;<BR>
4 if the output file can not be written to;<BR>
*/
int writeHeader(FILE* ofp) const;
/*!
@brief Determine if the content of the file stream is a Exv image.
See base class for more details.
@param ifp Input file stream.
@param advance Flag indicating whether the read position in the stream
should be advanced by the number of characters read to
analyse the stream (true) or left at its original
position (false). This applies only if the type matches.
@return true if the file stream data matches a Exv image;<BR>
false if the stream data does not match;<BR>
*/
virtual bool isThisType(FILE* ifp, bool advance) const;
//@}
private:
// Constant data
static const char exiv2Id_[]; // Exv identifier
static const byte blank_[]; // Minimal exiv file
// NOT Implemented
//! Default constructor
ExvImage();
//! Copy constructor
ExvImage(const ExvImage& rhs);
//! Assignment operator
ExvImage& operator=(const ExvImage& rhs);
}; // class ExvImage
//! Helper class modelling the TIFF header structure. //! Helper class modelling the TIFF header structure.
class TiffHeader { class TiffHeader {

@ -0,0 +1,217 @@
// ***************************************************************** -*- C++ -*-
/*
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
*
* This program is part of the Exiv2 distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
Abstract : Tester application for BasicIo functions. Tests MemIo primarily
since FileIo just sits atop of FILE* streams.
File : iotest.cpp
Version : $Rev$
Author(s): Brad Schick (brad) <brad@robotbattle.com>
History : 13-Jul-04, brad: created
*/
// *****************************************************************************
// included header files
#include "types.hpp"
#include "error.hpp"
#include "basicio.hpp"
#include <iostream>
using Exiv2::byte;
using Exiv2::BasicIo;
using Exiv2::MemIo;
using Exiv2::FileIo;
using Exiv2::IoCloser;
int WriteReadSeek(BasicIo &io);
// *****************************************************************************
// Main
int main(int argc, char* const argv[])
{
try {
if (argc != 4) {
std::cout << "Usage: " << argv[0] << " filein fileout1 fileout2\n";
std::cout << "fileouts are overwritten and should match filein exactly\n";
return 1;
}
FileIo fileIn(argv[1]);
if (fileIn.open() != 0) {
std::cerr << argv[0] <<
": Could not open input file (" << argv[1] << ")\n";
return 1;
}
FileIo fileOut1(argv[2]);
if (fileOut1.open("w+b") != 0) {
std::cerr << argv[0] <<
": Could not open output file 1 (" << argv[2] << ")\n";
return 1;
}
MemIo memIo1;
// Copy to output file through memIo
memIo1.write(fileIn);
memIo1.seek(0, BasicIo::beg);
fileOut1.write(memIo1);
// Read writereadseek test on MemIo
MemIo memIo2;
int rc = WriteReadSeek(memIo2);
if (rc != 0) return rc;
// Read writereadseek test on FileIo
// Create or overwrite the file, then close it
FileIo fileTest("iotest.txt");
if (fileTest.open("w+b") != 0) {
std::cerr << argv[0] <<
": Could not create test file iotest.txt\n";
return 1;
}
fileTest.close();
rc = WriteReadSeek(fileTest);
if (rc != 0) return rc;
// Another test of reading and writing
fileOut1.seek(0, BasicIo::beg);
memIo2.seek(0, BasicIo::beg);
FileIo fileOut2(argv[3]);
if (fileOut2.open("w+b") != 0) {
std::cerr << argv[0] <<
": Could not open output file 2 (" << argv[3] << ")\n";
return 1;
}
long readCount = 0;
byte buf[32];
while ((readCount=fileOut1.read(buf, sizeof(buf)))) {
if (memIo2.write(buf, readCount) != readCount) {
std::cerr << argv[0] <<
": MemIo bad write 2\n";
return 13;
}
if (fileOut2.write(buf, readCount) != readCount) {
std::cerr << argv[0] <<
": FileIo bad write 2\n";
return 14;
}
}
return 0;
}
catch (Exiv2::Error& e) {
std::cerr << "Caught Exiv2 exception '" << e << "'\n";
return 20;
}
}
int WriteReadSeek(BasicIo &io)
{
byte buf[4096];
const char tester1[] = "this is a little test of MemIo";
const char tester2[] = "Appending this on the end";
const char expect[] = "this is a little teAppending this on the end";
const long insert = 19;
const long len1 = (long)strlen(tester1) + 1;
const long len2 = (long)strlen(tester2) + 1;
IoCloser closer(io);
if (io.open() != 0) {
std::cerr << ": WRS could not open IO\n";
return 2;
}
if (io.write((byte*)tester1, len1) != len1) {
std::cerr << ": WRS initial write failed\n";
return 2;
}
io.seek(-len1, BasicIo::cur);
int c = EOF;
memset(buf, -1, sizeof(buf));
for (int i = 0; (c=io.getb()) != EOF; ++i) {
buf[i] = (byte)c;
}
// Make sure we got the null back
if(buf[len1-1] != 0) {
std::cerr << ": WRS missing null terminator 1\n";
return 3;
}
if (strcmp(tester1, (char*)buf) != 0 ) {
std::cerr << ": WRS strings don't match 1\n";
return 4;
}
io.seek(-2, BasicIo::end);
if (io.getb() != 'o') {
std::cerr << ": WRS bad getb o\n";
return 5;
}
io.seek(-2, BasicIo::cur);
if (io.getb() != 'I') {
std::cerr << ": WRS bad getb I\n";
return 6;
}
if (io.putb('O') != 'O') {
std::cerr << ": WRS bad putb\n";
return 7;
}
io.seek(-1, BasicIo::cur);
if (io.getb() != 'O') {
std::cerr << ": WRS bad getb O\n";
return 8;
}
io.seek(insert, BasicIo::beg);
if(io.write((byte*)tester2, len2) != len2) {
std::cerr << ": WRS bad write 1\n";
return 9;
}
// open should seek to beginning
io.open();
memset(buf, -1, sizeof(buf));
if (io.read(buf, sizeof(buf)) != insert + len2) {
std::cerr << ": WRS something went wrong\n";
return 10;
}
// Make sure we got the null back
if(buf[insert + len2 - 1] != 0) {
std::cerr << ": WRS missing null terminator 2\n";
return 11;
}
if (strcmp(expect, (char*)buf) != 0 ) {
std::cerr << ": WRS strings don't match 2\n";
return 12;
}
return 0;
}

@ -38,7 +38,7 @@ EXIV2_RCSID("@(#) $Id$");
#include "error.hpp" #include "error.hpp"
#include "value.hpp" #include "value.hpp"
#include "datasets.hpp" #include "datasets.hpp"
#include "image.hpp" #include "jpgimage.hpp"
// + standard includes // + standard includes
#include <iostream> #include <iostream>
@ -128,28 +128,7 @@ namespace Exiv2 {
return *pos; return *pos;
} }
int IptcData::read(const std::string& path) int IptcData::load(const byte* buf, long len)
{
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->sizeIptcData() > 0) {
rc = read(image->iptcData(), image->sizeIptcData());
}
else {
rc = 3;
}
}
return rc;
}
int IptcData::read(const byte* buf, long len)
{ {
const byte* pRead = buf; const byte* pRead = buf;
iptcMetadata_.clear(); iptcMetadata_.clear();
@ -201,41 +180,6 @@ namespace Exiv2 {
return 0; return 0;
} }
int IptcData::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 Iptc data
int rc = image->readMetadata();
if (rc == 0) {
image->clearIptcData();
rc = image->writeMetadata();
}
return rc;
} // IptcData::erase
int IptcData::write(const std::string& path)
{
// Remove the Iptc 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-Iptc data
int rc = image->readMetadata();
if (rc == 0) {
image->setIptcData(buf.pData_, buf.size_);
rc = image->writeMetadata();
}
return rc;
} // IptcData::write
DataBuf IptcData::copy() DataBuf IptcData::copy()
{ {
DataBuf buf(size()); DataBuf buf(size());
@ -268,7 +212,7 @@ namespace Exiv2 {
} }
return buf; return buf;
} } // IptcData::updateBuffer
long IptcData::size() const long IptcData::size() const
{ {
@ -288,15 +232,6 @@ namespace Exiv2 {
return newSize; return newSize;
} // IptcData::size } // IptcData::size
int IptcData::writeIptcData(const std::string& path)
{
DataBuf buf(copy());
ExvImage exvImage(path, true);
if (!exvImage.good()) return -1;
exvImage.setIptcData(buf.pData_, buf.size_);
return exvImage.writeMetadata();
} // IptcData::writeIptcData
int IptcData::add(const IptcKey& key, Value* value) int IptcData::add(const IptcKey& key, Value* value)
{ {
return add(Iptcdatum(key, value)); return add(Iptcdatum(key, value));

@ -290,48 +290,14 @@ namespace Exiv2 {
//! @name Manipulators //! @name Manipulators
//@{ //@{
/*! /*!
@brief Read the Iptc data from file path. @brief Load the Iptc data from a byte buffer. The format must follow
@param path Path to the file
@return 0 if successful;<BR>
3 if the file contains no Iptc data;<BR>
the return code of Image::readMetadata()
if the call to this function fails;<BR>
the return code of read(const char* buf, long len)
if the call to this function fails;<BR>
*/
int read(const std::string& path);
/*!
@brief Read the Iptc data from a byte buffer. The format must follow
the IPTC IIM4 standard. the IPTC IIM4 standard.
@param buf Pointer to the data buffer to read from @param buf Pointer to the data buffer to read from
@param len Number of bytes in the data buffer @param len Number of bytes in the data buffer
@return 0 if successful;<BR> @return 0 if successful;<BR>
5 if Iptc data is invalid or corrupt;<BR> 5 if Iptc data is invalid or corrupt;<BR>
*/ */
int read(const byte* buf, long len); int load(const byte* buf, long len);
/*!
@brief Write the Iptc data to file path. If an Iptc data section
already exists in the file, it is replaced. If there is no
metadata to write, the Iptc data section is
deleted from the file. Otherwise, an Iptc data section is
created.
@return 0 if successful;<BR>
-2 if the file contains an unknown image type;<BR>
the return code of Image::writeMetadata()
if the call to this function fails;<BR>
*/
int write(const std::string& path);
/*!
@brief Write the Iptc data to a binary file. By convention, the
filename extension should be ".exv". This file format contains
the Iptc data as it is found in a JPEG file. Exv files can
be read with int read(const std::string& path) just like
normal image files.
@return 0 if successful;<BR>
the return code of Image::writeMetadata()
if the call to this function fails;<BR>
*/
int writeIptcData(const std::string& path);
/*! /*!
@brief Write the Iptc data to a data buffer and return the data buffer. @brief Write the Iptc data to a data buffer and return the data buffer.
Caller owns this buffer. The copied data follows the IPTC IIM4 Caller owns this buffer. The copied data follows the IPTC IIM4
@ -370,6 +336,10 @@ namespace Exiv2 {
by this call. by this call.
*/ */
iterator erase(iterator pos); iterator erase(iterator pos);
/*!
@brief Delete all Iptcdatum instances resulting in an empty container.
*/
void clear() { iptcMetadata_.clear(); }
//! Sort metadata by key //! Sort metadata by key
void sortByKey(); void sortByKey();
//! Sort metadata by tag (aka dataset) //! Sort metadata by tag (aka dataset)
@ -396,15 +366,6 @@ namespace Exiv2 {
//! @name Accessors //! @name Accessors
//@{ //@{
/*!
@brief Erase the Iptc data from file path.
@param path Path to the file.
@return 0 if successful;<BR>
-2 if the file contains an unknown image type;<BR>
the return code of Image::writeMetadata()
if the call to this function fails;<BR>
*/
int erase(const std::string& path) const;
//! Begin of the metadata //! Begin of the metadata
const_iterator begin() const { return iptcMetadata_.begin(); } const_iterator begin() const { return iptcMetadata_.begin(); }
//! End of the metadata //! End of the metadata
@ -432,15 +393,14 @@ namespace Exiv2 {
//@} //@}
/*! /*!
@brief Convert the return code from @brief Convert the return code from \n
int read(const std::string& path), int read(const byte* buf, long len), \n
int read(const byte* buf, long len),
int write(const std::string& path),
int writeIptcData(const std::string& path),
int erase(const std::string& path) const
into an error message. 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); static std::string strError(int rc, const std::string& path);

@ -3,6 +3,7 @@
// The quickest way to access, set or modify Iptc metadata. // The quickest way to access, set or modify Iptc metadata.
#include "iptc.hpp" #include "iptc.hpp"
#include "image.hpp"
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
@ -31,7 +32,24 @@ try {
std::cout << "Time sent: " << iptcData["Iptc.Envelope.TimeSent"] << "\n"; std::cout << "Time sent: " << iptcData["Iptc.Envelope.TimeSent"] << "\n";
return iptcData.write(file); // Write IPTC data to file and exit // Open 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);
}
// Read existing metdata (so that exif will be preserved)
int rc = image->readMetadata();
if (rc) {
std::string error = Exiv2::Image::strError(rc, file);
throw Exiv2::Error(error);
}
// Replace Iptc data and write it back to the file
image->setIptcData(iptcData);
return image->writeMetadata();
} }
catch (Exiv2::Error& e) { catch (Exiv2::Error& e) {
std::cout << "Caught Exiv2 exception '" << e << "'\n"; std::cout << "Caught Exiv2 exception '" << e << "'\n";

@ -2,6 +2,7 @@
// iptcprint.cpp, $Rev$ // iptcprint.cpp, $Rev$
// Sample program to print the Iptc metadata of an image // Sample program to print the Iptc metadata of an image
#include "image.hpp"
#include "iptc.hpp" #include "iptc.hpp"
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
@ -14,13 +15,21 @@ try {
return 1; return 1;
} }
Exiv2::IptcData iptcData; Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]);
int rc = iptcData.read(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) { if (rc) {
std::string error = Exiv2::IptcData::strError(rc, argv[1]); std::string error = Exiv2::Image::strError(rc, argv[1]);
throw Exiv2::Error(error); throw Exiv2::Error(error);
} }
Exiv2::IptcData &iptcData = image->iptcData();
Exiv2::IptcData::iterator end = iptcData.end(); Exiv2::IptcData::iterator end = iptcData.end();
for (Exiv2::IptcData::iterator md = iptcData.begin(); md != end; ++md) { for (Exiv2::IptcData::iterator md = iptcData.begin(); md != end; ++md) {
std::cout << std::setw(36) << std::setfill(' ') << std::left std::cout << std::setw(36) << std::setfill(' ') << std::left

@ -10,6 +10,7 @@
*/ */
// ***************************************************************************** // *****************************************************************************
// included header files // included header files
#include "image.hpp"
#include "iptc.hpp" #include "iptc.hpp"
#include "datasets.hpp" #include "datasets.hpp"
#include "value.hpp" #include "value.hpp"
@ -18,12 +19,10 @@
using namespace Exiv2; using namespace Exiv2;
bool processLine(const std::string& line, int num); bool processLine(const std::string& line, int num, IptcData &iptcData);
void processAdd(const std::string& line, int num); void processAdd(const std::string& line, int num, IptcData &iptcData);
void processRemove(const std::string& line, int num); void processRemove(const std::string& line, int num, IptcData &iptcData);
void processModify(const std::string& line, int num); void processModify(const std::string& line, int num, IptcData &iptcData);
IptcData g_iptcData;
// ***************************************************************************** // *****************************************************************************
// Main // Main
@ -37,22 +36,30 @@ int main(int argc, char* const argv[])
return 1; return 1;
} }
int rc = g_iptcData.read(argv[1]); Image::AutoPtr image = ImageFactory::open(argv[1]);
if (image.get() == 0) {
throw Error("Could not read file");
}
// Load existing metadata
int rc = image->readMetadata();
if (rc) { if (rc) {
std::string error = IptcData::strError(rc, argv[1]); std::string error = Image::strError(rc, argv[1]);
throw Error(error); throw Error(error);
} }
// Process commands
std::string line; std::string line;
int num = 0; int num = 0;
std::getline(std::cin, line); std::getline(std::cin, line);
while (line.length() && processLine(line, ++num)) { while (line.length() && processLine(line, ++num, image->iptcData())) {
std::getline(std::cin, line); std::getline(std::cin, line);
} }
rc = g_iptcData.write(argv[1]); // Save any changes
rc = image->writeMetadata();
if (rc) { if (rc) {
std::string error = IptcData::strError(rc, argv[1]); std::string error = Image::strError(rc, argv[1]);
throw Error(error); throw Error(error);
} }
@ -64,20 +71,20 @@ int main(int argc, char* const argv[])
} }
} }
bool processLine(const std::string& line, int num ) bool processLine(const std::string& line, int num, IptcData &iptcData)
{ {
switch (line.at(0)) { switch (line.at(0)) {
case 'a': case 'a':
case 'A': case 'A':
processAdd(line, num); processAdd(line, num, iptcData);
break; break;
case 'r': case 'r':
case 'R': case 'R':
processRemove(line, num); processRemove(line, num, iptcData);
break; break;
case 'm': case 'm':
case 'M': case 'M':
processModify(line, num); processModify(line, num, iptcData);
break; break;
case 'q': case 'q':
case 'Q': case 'Q':
@ -90,7 +97,7 @@ bool processLine(const std::string& line, int num )
return true; return true;
} }
void processAdd(const std::string& line, int num) void processAdd(const std::string& line, int num, IptcData &iptcData)
{ {
std::string::size_type keyStart = line.find_first_not_of(" \t", 1); std::string::size_type keyStart = line.find_first_not_of(" \t", 1);
std::string::size_type keyEnd = line.find_first_of(" \t", keyStart+1); std::string::size_type keyEnd = line.find_first_of(" \t", keyStart+1);
@ -116,7 +123,7 @@ void processAdd(const std::string& line, int num)
Value::AutoPtr value = Value::create(type); Value::AutoPtr value = Value::create(type);
value->read(data); value->read(data);
int rc = g_iptcData.add(iptcKey, value.get()); int rc = iptcData.add(iptcKey, value.get());
if (rc) { if (rc) {
std::string error = IptcData::strError(rc, "Input file"); std::string error = IptcData::strError(rc, "Input file");
throw Error(error); throw Error(error);
@ -124,7 +131,7 @@ void processAdd(const std::string& line, int num)
} }
void processRemove(const std::string& line, int num) void processRemove(const std::string& line, int num, IptcData &iptcData)
{ {
std::string::size_type keyStart = line.find_first_not_of(" \t", 1); std::string::size_type keyStart = line.find_first_not_of(" \t", 1);
@ -137,13 +144,13 @@ void processRemove(const std::string& line, int num)
const std::string key( line.substr(keyStart) ); const std::string key( line.substr(keyStart) );
IptcKey iptcKey(key); IptcKey iptcKey(key);
IptcData::iterator iter = g_iptcData.findKey(iptcKey); IptcData::iterator iter = iptcData.findKey(iptcKey);
if (iter != g_iptcData.end()) { if (iter != iptcData.end()) {
g_iptcData.erase(iter); iptcData.erase(iter);
} }
} }
void processModify(const std::string& line, int num) void processModify(const std::string& line, int num, IptcData &iptcData)
{ {
std::string::size_type keyStart = line.find_first_not_of(" \t", 1); std::string::size_type keyStart = line.find_first_not_of(" \t", 1);
std::string::size_type keyEnd = line.find_first_of(" \t", keyStart+1); std::string::size_type keyEnd = line.find_first_of(" \t", keyStart+1);
@ -169,12 +176,12 @@ void processModify(const std::string& line, int num)
Value::AutoPtr value = Value::create(type); Value::AutoPtr value = Value::create(type);
value->read(data); value->read(data);
IptcData::iterator iter = g_iptcData.findKey(iptcKey); IptcData::iterator iter = iptcData.findKey(iptcKey);
if (iter != g_iptcData.end()) { if (iter != iptcData.end()) {
iter->setValue(value.get()); iter->setValue(value.get());
} }
else { else {
int rc = g_iptcData.add(iptcKey, value.get()); int rc = iptcData.add(iptcKey, value.get());
if (rc) { if (rc) {
std::string error = IptcData::strError(rc, "Input file"); std::string error = IptcData::strError(rc, "Input file");
throw Error(error); throw Error(error);

@ -0,0 +1,670 @@
// ***************************************************************** -*- C++ -*-
/*
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
*
* This program is part of the Exiv2 distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
File: jpgimage.cpp
Version: $Rev$
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
Brad Schick (brad) <brad@robotbattle.com>
History: 15-Jan-05, brad: split out from image.cpp
*/
// *****************************************************************************
#include "rcsid.hpp"
EXIV2_RCSID("@(#) $Id$");
// *****************************************************************************
// included header files
#ifdef HAVE_CONFIG_H
# include <config.h>
#else
# ifdef _MSC_VER
# include <config_win32.h>
# endif
#endif
#include "jpgimage.hpp"
#include "error.hpp"
// + standard includes
#include <cstring>
#include <cassert>
// *****************************************************************************
// 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(BasicIo::AutoPtr io, bool create);
//! Check if the file iIo is an EXV file
bool isExvType(BasicIo& iIo, 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(BasicIo::AutoPtr io, bool create);
//! Check if the file iIo is a JPEG image.
bool isJpegType(BasicIo& iIo, bool advance);
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(BasicIo::AutoPtr io, bool create,
const byte initData[], long dataSize)
: io_(io)
{
if (create) {
initImage(initData, dataSize);
}
}
int JpegBase::initImage(const byte initData[], long dataSize)
{
IoCloser closer(*io_);
if (io_->open() != 0) return 4;
if (io_->write(initData, dataSize) != dataSize) {
return 4;
}
return 0;
}
bool JpegBase::good() const
{
IoCloser closer(*io_);
if (io_->open() != 0) return false;
return isThisType(*io_, false);
}
void JpegBase::clearMetadata()
{
clearIptcData();
clearExifData();
clearComment();
}
void JpegBase::clearIptcData()
{
iptcData_.clear();
}
void JpegBase::clearExifData()
{
exifData_.clear();
}
void JpegBase::clearComment()
{
comment_.erase();
}
void JpegBase::setExifData(const ExifData& exifData)
{
exifData_ = exifData;
}
void JpegBase::setIptcData(const IptcData& iptcData)
{
iptcData_ = iptcData;
}
void JpegBase::setComment(const std::string& comment)
{
comment_ = comment;
}
void JpegBase::setMetadata(const Image& image)
{
setIptcData(image.iptcData());
setExifData(image.exifData());
setComment(image.comment());
}
int JpegBase::advanceToMarker() const
{
int c = -1;
// Skips potential padding between markers
while ((c=io_->getb()) != 0xff) {
if (c == EOF) return -1;
}
// Markers can start with any number of 0xff
while ((c=io_->getb()) == 0xff) {
if (c == EOF) return -1;
}
return c;
}
int JpegBase::readMetadata()
{
IoCloser closer(*io_);
if (io_->open() != 0) return 1;
// Ensure that this is the correct image type
if (!isThisType(*io_, true)) {
if (io_->error() || io_->eof()) return 1;
return 2;
}
clearMetadata();
int search = 3;
const long bufMinSize = 16;
long bufRead = 0;
DataBuf buf(bufMinSize);
// Read section marker
int marker = advanceToMarker();
if (marker < 0) return 2;
while (marker != sos_ && marker != eoi_ && search > 0) {
// Read size and signature (ok if this hits EOF)
bufRead = io_->read(buf.pData_, bufMinSize);
if (io_->error()) 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
io_->seek(8-bufRead, BasicIo::cur);
long sizeExifData = size - 8;
DataBuf rawExif(sizeExifData);
io_->read(rawExif.pData_, sizeExifData);
if (io_->error() || io_->eof()) {
return 1;
}
if (exifData_.load(rawExif.pData_, sizeExifData)) return 2;
--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: io_->seek(16-bufRead, BasicIo::cur);
DataBuf psData(size - 16);
io_->read(psData.pData_, psData.size_);
if (io_->error() || io_->eof()) 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);
if (iptcData_.load(record + sizeHdr, sizeIptc)) return 2;
}
--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.
io_->seek(2-bufRead, BasicIo::cur);
buf.alloc(size-2);
io_->read(buf.pData_, size-2);
if (io_->error() || io_->eof()) return 1;
comment_.assign(reinterpret_cast<char*>(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 (io_->seek(size-bufRead, BasicIo::cur)) return 2;
}
// Read the beginning of the next segment
marker = advanceToMarker();
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<uint16_t>(dataSize);
*sizeHdr = psSize + 10;
*record = hrd;
return 0;
}
position += dataSize + (dataSize & 1);
}
return 3;
} // JpegBase::locateIptcData
int JpegBase::writeMetadata()
{
IoCloser closer(*io_);
if (io_->open() != 0) return 1;
BasicIo::AutoPtr tempIo(io_->temporary());
if (!tempIo.get()) return -3;
int rc = doWriteMetadata(*tempIo);
io_->close();
if( rc == 0 ) {
if (io_->transfer(*tempIo) != 0) return -3;
}
return rc;
} // JpegBase::writeMetadata
int JpegBase::doWriteMetadata(BasicIo& outIo)
{
if (!io_->isopen()) return 1;
if (!outIo.isopen()) return 4;
// Ensure that this is the correct image type
if (!isThisType(*io_, true)) {
if (io_->error() || io_->eof()) return 1;
return 2;
}
const long bufMinSize = 16;
long bufRead = 0;
DataBuf buf(bufMinSize);
const long seek = io_->tell();
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(outIo)) return 4;
// Read section marker
int marker = advanceToMarker();
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 = io_->read(buf.pData_, bufMinSize);
if (io_->error()) return 1;
uint16_t size = getUShort(buf.pData_, bigEndian);
if (marker == app0_) {
if (size < 2) return 2;
insertPos = count + 1;
if (io_->seek(size-bufRead, BasicIo::cur)) return 2;
}
else if (marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) {
if (size < 8) return 2;
skipApp1Exif = count;
++search;
if (io_->seek(size-bufRead, BasicIo::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: io_->seek(16-bufRead, BasicIo::cur);
psData.alloc(size - 16);
// Load PS data now to allow reinsertion at any point
io_->read(psData.pData_, psData.size_);
if (io_->error() || io_->eof()) 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 (io_->seek(size-bufRead, BasicIo::cur)) return 2;
}
else {
if (size < 2) return 2;
if (io_->seek(size-bufRead, BasicIo::cur)) return 2;
}
marker = advanceToMarker();
if (marker < 0) return 2;
++count;
}
if (exifData_.count() > 0) ++search;
if (iptcData_.count() > 0) ++search;
if (!comment_.empty()) ++search;
io_->seek(seek, BasicIo::beg);
count = 0;
marker = advanceToMarker();
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 = io_->read(buf.pData_, bufMinSize);
if (io_->error()) 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<uint16_t>(comment_.length()+3), bigEndian);
if (outIo.write(tmpBuf, 4) != 4) return 4;
if (outIo.write((byte*)comment_.data(), (long)comment_.length())
!= (long)comment_.length()) return 4;
if (outIo.putb(0)==EOF) return 4;
if (outIo.error()) return 4;
--search;
}
if (exifData_.count() > 0) {
// Write APP1 marker, size of APP1 field, Exif id and Exif data
DataBuf rawExif(exifData_.copy());
tmpBuf[0] = 0xff;
tmpBuf[1] = app1_;
us2Data(tmpBuf + 2,
static_cast<uint16_t>(rawExif.size_+8),
bigEndian);
memcpy(tmpBuf + 4, exifId_, 6);
if (outIo.write(tmpBuf, 10) != 10) return 4;
if (outIo.write(rawExif.pData_, rawExif.size_)
!= rawExif.size_) return 4;
if (outIo.error()) 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 || iptcData_.count() > 0) {
// rawIptc may have size of zero.
DataBuf rawIptc(iptcData_.copy());
// write app13 marker, new size, and ps3Id
tmpBuf[0] = 0xff;
tmpBuf[1] = app13_;
const int sizeNewData = rawIptc.size_ ?
rawIptc.size_ + (rawIptc.size_ & 1) + 12 : 0;
us2Data(tmpBuf + 2,
static_cast<uint16_t>(psData.size_-sizeOldData+sizeNewData+16),
bigEndian);
memcpy(tmpBuf + 4, ps3Id_, 14);
if (outIo.write(tmpBuf, 18) != 18) return 4;
if (outIo.error()) return 4;
const long sizeFront = (long)(record - psData.pData_);
const long sizeEnd = psData.size_ - sizeFront - sizeOldData;
// write data before old record.
if (outIo.write(psData.pData_, sizeFront) != sizeFront) return 4;
// write new iptc record if we have it
if (iptcData_.count() > 0) {
memcpy(tmpBuf, bimId_, 4);
us2Data(tmpBuf+4, iptc_, bigEndian);
tmpBuf[6] = 0;
tmpBuf[7] = 0;
ul2Data(tmpBuf + 8, rawIptc.size_, bigEndian);
if (outIo.write(tmpBuf, 12) != 12) return 4;
if (outIo.write(rawIptc.pData_, rawIptc.size_)
!= rawIptc.size_) return 4;
// data is padded to be even (but not included in size)
if (rawIptc.size_ & 1) {
if (outIo.putb(0)==EOF) return 4;
}
if (outIo.error()) return 4;
--search;
}
// write existing stuff after record
if (outIo.write(record+sizeOldData, sizeEnd)
!= sizeEnd) return 4;
if (outIo.error()) return 4;
}
}
if (marker == eoi_) {
break;
}
else if (skipApp1Exif==count || skipApp13Ps3==count || skipCom==count) {
--search;
io_->seek(size-bufRead, BasicIo::cur);
}
else {
if (size < 2) return 2;
buf.alloc(size+2);
io_->seek(-bufRead-2, BasicIo::cur);
io_->read(buf.pData_, size+2);
if (io_->error() || io_->eof()) return 1;
if (outIo.write(buf.pData_, size+2) != size+2) return 4;
if (outIo.error()) return 4;
}
// Next marker
marker = advanceToMarker();
if (marker < 0) return 2;
++count;
}
// Copy rest of the Io
io_->seek(-2, BasicIo::cur);
buf.alloc(4096);
long readSize = 0;
while ((readSize=io_->read(buf.pData_, buf.size_))) {
if (outIo.write(buf.pData_, readSize) != readSize) return 4;
}
if (outIo.error()) 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(BasicIo::AutoPtr io, bool create)
: JpegBase(io, create, blank_, sizeof(blank_))
{
}
//! @cond IGNORE
JpegImage::JpegRegister::JpegRegister()
{
ImageFactory::registerImage(
Image::jpeg, newJpegInstance, isJpegType);
}
//! @endcond
int JpegImage::writeHeader(BasicIo& outIo) const
{
// Jpeg header
byte tmpBuf[2];
tmpBuf[0] = 0xff;
tmpBuf[1] = soi_;
if (outIo.write(tmpBuf, 2) != 2) return 4;
if (outIo.error()) return 4;
return 0;
}
bool JpegImage::isThisType(BasicIo& iIo, bool advance) const
{
return isJpegType(iIo, advance);
}
Image::AutoPtr newJpegInstance(BasicIo::AutoPtr io, bool create)
{
Image::AutoPtr image;
if (create) {
image = Image::AutoPtr(new JpegImage(io, true));
}
else {
image = Image::AutoPtr(new JpegImage(io, false));
}
if (!image->good()) {
image.reset();
}
return image;
}
bool isJpegType(BasicIo& iIo, bool advance)
{
bool result = true;
byte tmpBuf[2];
iIo.read(tmpBuf, 2);
if (iIo.error() || iIo.eof()) return false;
if (0xff!=tmpBuf[0] || JpegImage::soi_!=tmpBuf[1]) {
result = false;
}
if (!advance || !result ) iIo.seek(-2, BasicIo::cur);
return result;
}
const char ExvImage::exiv2Id_[] = "Exiv2";
const byte ExvImage::blank_[] = { 0xff,0x01,'E','x','i','v','2',0xff,0xd9 };
ExvImage::ExvImage(BasicIo::AutoPtr io, bool create)
: JpegBase(io, create, blank_, sizeof(blank_))
{
}
//! @cond IGNORE
ExvImage::ExvRegister::ExvRegister()
{
ImageFactory::registerImage(
Image::exv, newExvInstance, isExvType);
}
//! @endcond
int ExvImage::writeHeader(BasicIo& outIo) const
{
// Exv header
byte tmpBuf[7];
tmpBuf[0] = 0xff;
tmpBuf[1] = 0x01;
memcpy(tmpBuf + 2, exiv2Id_, 5);
if (outIo.write(tmpBuf, 7) != 7) return 4;
if (outIo.error()) return 4;
return 0;
}
bool ExvImage::isThisType(BasicIo& iIo, bool advance) const
{
return isExvType(iIo, advance);
}
Image::AutoPtr newExvInstance(BasicIo::AutoPtr io, bool create)
{
Image::AutoPtr image;
if (create) {
image = Image::AutoPtr(new ExvImage(io, true));
}
else {
image = Image::AutoPtr(new ExvImage(io, false));
}
if (!image->good()) image.reset();
return image;
}
bool isExvType(BasicIo& iIo, bool advance)
{
bool result = true;
byte tmpBuf[7];
iIo.read(tmpBuf, 7);
if (iIo.error() || iIo.eof()) return false;
if (0xff!=tmpBuf[0] || 0x01!=tmpBuf[1] ||
memcmp(tmpBuf + 2, ExvImage::exiv2Id_, 5) != 0) {
result = false;
}
if (!advance || !result ) iIo.seek(-7, BasicIo::cur);
return result;
}
} // namespace Exiv2

@ -0,0 +1,418 @@
// ***************************************************************** -*- C++ -*-
/*
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
*
* This program is part of the Exiv2 distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*!
@file jpgimage.hpp
@brief Class JpegImage to access JPEG images
@version $Rev$
@author Andreas Huggel (ahu)
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
@author Brad Schick (brad)
<a href="mailto:brad@robotbattle.com">brad@robotbattle.com</a>
@date 15-Jan-05, brad: split out from image.cpp
*/
#ifndef JPGIMAGE_HPP_
#define JPGIMAGE_HPP_
// *****************************************************************************
// included header files
#include "types.hpp"
#include "image.hpp"
#include "basicio.hpp"
#include "exif.hpp"
#include "iptc.hpp"
// + standard includes
#include <string>
// *****************************************************************************
// namespace extensions
namespace Exiv2 {
// *****************************************************************************
// class definitions
/*!
@brief Abstract helper base class to access JPEG images.
*/
class JpegBase : public Image {
public:
//! @name Creators
//@{
//! Virtual destructor.
virtual ~JpegBase() {}
//@}
//! @name Manipulators
//@{
/*!
@brief Read all metadata from the image. Before this method
is called, the various metadata types (Iptc, Exif) will be empty.
This method returns success even when no metadata is found in
the image. Callers must therefore check the size of indivdual
metadata types before accessing the data.
@return 0 if successful;<BR>
1 if reading from the file failed
(could be caused by invalid image);<BR>
2 if the file does not contain a valid image;<BR>
*/
int readMetadata();
/*!
@brief Write metadata back to the image.
All existing metadata sections in the image are either created,
replaced, or erased. If values for a given metadata type have been
assigned, a section for that metadata type will either be created or
replaced. If no values have been assigned to a given metadata type,
any exists section for that metadata type will be removed from the
image.
@return 0 if successful;<br>
1 if reading from the file failed;<BR>
2 if the file does not contain a valid image;<BR>
4 if the temporary output file can not be written to;<BR>
-1 if the newly created file could not be reopened;<BR>
-3 if the temporary output file can not be opened;<BR>
-4 if renaming the temporary file fails;<br>
*/
int writeMetadata();
/*!
@brief Assign new exif data. The new exif data is not written
to the image until the writeMetadata() method is called.
@param exifData An ExifData instance holding exif data to be copied
@throw Error ("Exif data too large") if the exif data is larger than
65535 bytes (the maximum size of JPEG APP segments)
*/
void setExifData(const ExifData& exifData);
void clearExifData();
void setIptcData(const IptcData& iptcData);
void clearIptcData();
void setComment(const std::string& comment);
void clearComment();
void setMetadata(const Image& image);
void clearMetadata();
//@}
//! @name Accessors
//@{
bool good() const;
const ExifData& exifData() const { return exifData_; }
ExifData& exifData() { return exifData_; }
const IptcData& iptcData() const { return iptcData_; }
IptcData& iptcData() { return iptcData_; }
std::string comment() const { return comment_; }
BasicIo& io() const { return *io_; }
//@}
protected:
//! @name Creators
//@{
/*!
@brief Constructor that can either open an existing image or create
a new image from scratch. If a new image is to be created, any
existing data is overwritten.
@param io An auto-pointer that owns a BasicIo instance used for
reading and writing image metadata. \b Important: The constructor
takes ownership of the passed in BasicIo instance through the
auto-pointer. Callers should not continue to use the BasicIo
instance after it is passed to this method. Use the Image::io()
method to get a temporary reference.
@param create Specifies if an existing image should be read (false)
or if a new image should be created (true).
@param initData Data to initialize newly created images. Only used
when \em create is true. Should contain data for the smallest
valid image of the calling subclass.
@param dataSize Size of initData in bytes.
*/
JpegBase(BasicIo::AutoPtr io, bool create,
const byte initData[], long dataSize);
//@}
//! @name Manipulators
//@{
/*!
@brief Writes the image header (aka signature) to the BasicIo instance.
@param oIo BasicIo instance that the header is written to.
@return 0 if successful;<BR>
4 if the output file can not be written to;<BR>
*/
virtual int writeHeader(BasicIo& oIo) const =0;
//@}
//! @name Accessors
//@{
/*!
@brief Determine if the content of the BasicIo instance is of the
type supported by this class.
The advance flag determines if the read position in the stream is
moved (see below). This applies only if the type matches and the
function returns true. If the type does not match, the stream
position is not changed. However, if reading from the stream fails,
the stream position is undefined. Consult the stream state to obtain
more information in this case.
@param iIo BasicIo instance to read from.
@param advance Flag indicating whether the position of the io
should be advanced by the number of characters read to
analyse the data (true) or left at its original
position (false). This applies only if the type matches.
@return true if the data matches the type of this class;<BR>
false if the data does not match;<BR>
*/
virtual bool isThisType(BasicIo& iIo, bool advance) const =0;
//@}
// Constant Data
static const byte sos_; //!< JPEG SOS marker
static const byte eoi_; //!< JPEG EOI marker
static const byte app0_; //!< JPEG APP0 marker
static const byte app1_; //!< JPEG APP1 marker
static const byte app13_; //!< JPEG APP13 marker
static const byte com_; //!< JPEG Comment marker
static const char exifId_[]; //!< Exif identifier
static const char jfifId_[]; //!< JFIF identifier
static const char ps3Id_[]; //!< Photoshop marker
static const char bimId_[]; //!< Photoshop marker
static const uint16_t iptc_; //!< Photoshop Iptc marker
private:
// DATA
BasicIo::AutoPtr io_; //!< Image data io pointer
ExifData exifData_; //!< Exif data container
IptcData iptcData_; //!< Iptc data container
std::string comment_; //!< JPEG comment
// METHODS
/*!
@brief Advances associated io instance to one byte past the next
Jpeg marker and returns the marker. This method should be called
when the BasicIo instance is positioned one byte past the end of a
Jpeg segment.
@return the next Jpeg segment marker if successful;<BR>
-1 if a maker was not found before EOF;<BR>
*/
int advanceToMarker() const;
/*!
@brief Locates Photoshop formated Iptc data in a memory buffer.
Operates on raw data to simplify reuse.
@param pPsData Pointer to buffer containing entire payload of
Photoshop formated APP13 Jpeg segment.
@param sizePsData Size in bytes of pPsData.
@param record Output value that is set to the start of the Iptc
data block within pPsData (may not be null).
@param sizeHdr Output value that is set to the size of the header
within the Iptc data block pointed to by record (may not
be null).
@param sizeIptc Output value that is set to the size of the actual
Iptc data within the Iptc data block pointed to by record
(may not be null).
@return 0 if successful;<BR>
3 if no Iptc data was found in pPsData;<BR>
-2 if the pPsData buffer does not contain valid data;<BR>
*/
int locateIptcData(const byte *pPsData,
long sizePsData,
const byte **record,
uint16_t *const sizeHdr,
uint16_t *const sizeIptc) const;
/*!
@brief Initialize the image with the provided data.
@param initData Data to be written to the associated BasicIo
@param dataSize Size in bytes of data to be written
@return 0 if successful;<BR>
4 if the output file can not be written to;<BR>
*/
int initImage(const byte initData[], long dataSize);
/*!
@brief Provides the main implementation of writeMetadata() by
writing all buffered metadata to the provided BasicIo.
@param oIo BasicIo instance to write to (a temporary location).
@return 0 if successful;<br>
1 if reading from input file failed;<BR>
2 if the input file does not contain a valid image;<BR>
4 if the output file can not be written to;<BR>
*/
int doWriteMetadata(BasicIo& oIo);
// NOT Implemented
//! Default constructor.
JpegBase();
//! Copy constructor
JpegBase(const JpegBase& rhs);
//! Assignment operator
JpegBase& operator=(const JpegBase& rhs);
}; // class JpegBase
/*!
@brief Class to access JPEG images
*/
class JpegImage : public JpegBase {
friend bool isJpegType(BasicIo& iIo, bool advance);
public:
//! @name Creators
//@{
/*!
@brief Constructor that can either open an existing Jpeg image or create
a new image from scratch. If a new image is to be created, any
existing data is overwritten. Since the constructor can not return
a result, callers should check the good() method after object
construction to determine success or failure.
@param io An auto-pointer that owns a BasicIo instance used for
reading and writing image metadata. \b Important: The constructor
takes ownership of the passed in BasicIo instance through the
auto-pointer. Callers should not continue to use the BasicIo
instance after it is passed to this method. Use the Image::io()
method to get a temporary reference.
@param create Specifies if an existing image should be read (false)
or if a new file should be created (true).
*/
JpegImage(BasicIo::AutoPtr io, bool create);
//! Destructor
~JpegImage() {}
//@}
//! @cond IGNORE
// Public only so that we can create a static instance
struct JpegRegister{
JpegRegister();
};
//! @endcond
protected:
//! @name Accessors
//@{
/*!
@brief Determine if the content of the BasicIo instance is a Jpeg image.
See base class for more details.
@param iIo BasicIo instance to read from.
@param advance Flag indicating whether the position of the io
should be advanced by the number of characters read to
analyse the data (true) or left at its original
position (false). This applies only if the type matches.
@return true if the data matches a Jpeg image;<BR>
false if the data does not match;<BR>
*/
bool isThisType(BasicIo& iIo, bool advance) const;
//@}
//! @name Manipulators
//@{
/*!
@brief Writes a Jpeg header (aka signature) to the BasicIo instance.
@param oIo BasicIo instance that the header is written to.
@return 0 if successful;<BR>
2 if the input image is invalid or can not be read;<BR>
4 if the temporary image can not be written to;<BR>
-3 other temporary errors;<BR>
*/
int writeHeader(BasicIo& oIo) const;
//@}
private:
// Constant data
static const byte soi_; // SOI marker
static const byte blank_[]; // Minimal Jpeg image
// NOT Implemented
//! Default constructor
JpegImage();
//! Copy constructor
JpegImage(const JpegImage& rhs);
//! Assignment operator
JpegImage& operator=(const JpegImage& rhs);
}; // class JpegImage
static JpegImage::JpegRegister jpegReg;
//! Helper class to access %Exiv2 files
class ExvImage : public JpegBase {
friend bool isExvType(BasicIo& iIo, bool advance);
public:
//! @name Creators
//@{
/*!
@brief Constructor that can either open an existing Exv image or create
a new image from scratch. If a new image is to be created, any
existing data is overwritten. Since the constructor can not return
a result, callers should check the good() method after object
construction to determine success or failure.
@param io An auto-pointer that owns a BasicIo instance used for
reading and writing image metadata. \b Important: The constructor
takes ownership of the passed in BasicIo instance through the
auto-pointer. Callers should not continue to use the BasicIo
instance after it is passed to this method. Use the Image::io()
method to get a temporary reference.
@param create Specifies if an existing image should be read (false)
or if a new file should be created (true).
*/
ExvImage(BasicIo::AutoPtr io, bool create);
//! Destructor
~ExvImage() {}
//@}
//! @cond IGNORE
// Public only so that we can create a static instance
struct ExvRegister{
ExvRegister();
};
//! @endcond
protected:
//! @name Accessors
//@{
/*!
@brief Determine if the content of the BasicIo instance is an Exv
image. See base class for more details.
@param iIo BasicIo instance to read from.
@param advance Flag indicating whether the position of the io
should be advanced by the number of characters read to
analyse the data (true) or left at its original
position (false). This applies only if the type matches.
@return true if the data matches a Jpeg image;<BR>
false if the data does not match;<BR>
*/
virtual bool isThisType(BasicIo& iIo, bool advance) const;
//@}
//! @name Manipulators
//@{
/*!
@brief Writes an Exv header (aka signature) to the BasicIo instance.
@param oIo BasicIo instance that the header is written to.
@return 0 if successful;<BR>
4 if the output file can not be written to;<BR>
*/
int writeHeader(BasicIo& oIo) const;
//@}
private:
// Constant data
static const char exiv2Id_[]; // Exv identifier
static const byte blank_[]; // Minimal exiv file
// NOT Implemented
//! Default constructor
ExvImage();
//! Copy constructor
ExvImage(const ExvImage& rhs);
//! Assignment operator
ExvImage& operator=(const ExvImage& rhs);
}; // class ExvImage
static ExvImage::ExvRegister exvReg;
} // namespace Exiv2
#endif // #ifndef JPGIMAGE_HPP_

@ -28,6 +28,8 @@
*/ */
// ***************************************************************************** // *****************************************************************************
// included header files // included header files
#include "image.hpp"
#include "iptc.hpp"
#include "exif.hpp" #include "exif.hpp"
#include "types.hpp" #include "types.hpp"
#include "metacopy.hpp" #include "metacopy.hpp"
@ -50,8 +52,18 @@ try {
return 2; return 2;
} }
// Use MemIo to increase test coverage.
Exiv2::BasicIo::AutoPtr fileIo(new Exiv2::FileIo(params.read_));
Exiv2::BasicIo::AutoPtr memIo(new Exiv2::MemIo);
if (memIo->transfer(*fileIo) != 0) {
std::cerr << params.progname() <<
": Could not read file (" << params.read_ << ")\n";
return 4;
}
Exiv2::Image::AutoPtr readImg Exiv2::Image::AutoPtr readImg
= Exiv2::ImageFactory::instance().open(params.read_); = Exiv2::ImageFactory::open(memIo);
if (readImg.get() == 0) { if (readImg.get() == 0) {
std::cerr << params.progname() << std::cerr << params.progname() <<
": Could not read file (" << params.read_ << ")\n"; ": Could not read file (" << params.read_ << ")\n";
@ -64,7 +76,7 @@ try {
} }
Exiv2::Image::AutoPtr writeImg Exiv2::Image::AutoPtr writeImg
= Exiv2::ImageFactory::instance().open(params.write_); = Exiv2::ImageFactory::open(params.write_);
if (writeImg.get() == 0) { if (writeImg.get() == 0) {
std::cerr << params.progname() << std::cerr << params.progname() <<
": Could not read file (" << params.write_ << ")\n"; ": Could not read file (" << params.write_ << ")\n";
@ -79,10 +91,10 @@ try {
} }
} }
if (params.iptc_) { if (params.iptc_) {
writeImg->setIptcData(readImg->iptcData(), readImg->sizeIptcData()); writeImg->setIptcData(readImg->iptcData());
} }
if (params.exif_) { if (params.exif_) {
writeImg->setExifData(readImg->exifData(), readImg->sizeExifData()); writeImg->setExifData(readImg->exifData());
} }
if (params.comment_) { if (params.comment_) {
writeImg->setComment(readImg->comment()); writeImg->setComment(readImg->comment());

@ -203,39 +203,6 @@ namespace Exiv2 {
long size_; long size_;
}; // class DataBuf }; // class DataBuf
/*!
@brief Utility class that closes a file stream pointer upon destruction.
Its primary use is to be a stack variable in functions that need to
ensure files get closed. Useful when functions return errors from
many locations.
*/
class FileCloser {
// Not implemented
//! Copy constructor
FileCloser(const FileCloser&);
//! Assignment operator
FileCloser& operator=(const FileCloser&);
public:
//! @name Creators
//@{
//! Default constructor
FileCloser() : fp_(0) {}
//! Constructor, takes a file stream pointer
FileCloser(FILE *fp) : fp_(fp) {}
//! Destructor, closes the file
~FileCloser() { close(); }
//@}
//! @name Manipulators
//@{
//! Close the file
void close() { if (fp_) fclose(fp_); fp_ = 0; }
//@}
// DATA
//! The file stream pointer
FILE *fp_;
}; // class FileCloser
// ***************************************************************************** // *****************************************************************************
// free functions // free functions

@ -15,6 +15,7 @@
*/ */
// ***************************************************************************** // *****************************************************************************
// included header files // included header files
#include "image.hpp"
#include "exif.hpp" #include "exif.hpp"
#include "makernote.hpp" #include "makernote.hpp"
@ -160,15 +161,24 @@ void testCase(const std::string& file1,
const std::string& value) const std::string& value)
{ {
ExifKey ek(key); ExifKey ek(key);
ExifData ed1;
//Open first image
Image::AutoPtr image1 = ImageFactory::open(file1);
if (image1.get() == 0) {
std::string error(file1);
error += " : Could not read file or unknown image type";
throw Exiv2::Error(error);
}
// Load existing metadata
std::cerr << "---> Reading file " << file1 << "\n"; std::cerr << "---> Reading file " << file1 << "\n";
int rc = ed1.read(file1); int rc = image1->readMetadata();
if (rc) { if (rc) {
std::string error = ExifData::strError(rc, file1.c_str()); std::string error = Exiv2::Image::strError(rc, file1);
throw Error(error); throw Exiv2::Error(error);
} }
Exiv2::ExifData &ed1 = image1->exifData();
std::cerr << "---> Modifying Exif data\n"; std::cerr << "---> Modifying Exif data\n";
Exiv2::ExifData::iterator pos = ed1.findKey(ek); Exiv2::ExifData::iterator pos = ed1.findKey(ek);
if (pos == ed1.end()) { if (pos == ed1.end()) {
@ -176,27 +186,35 @@ void testCase(const std::string& file1,
} }
pos->setValue(value); pos->setValue(value);
// Open second image
Image::AutoPtr image2 = ImageFactory::open(file2);
if (image2.get() == 0) {
std::string error(file2);
error += " : Could not read file or unknown image type";
throw Exiv2::Error(error);
}
image2->setExifData(image1->exifData());
std::cerr << "---> Writing Exif data to file " << file2 << "\n"; std::cerr << "---> Writing Exif data to file " << file2 << "\n";
rc = ed1.write(file2); rc = image2->writeMetadata();
if (rc) { if (rc) {
std::string error = ExifData::strError(rc, file2.c_str()); std::string error = Image::strError(rc, file2.c_str());
throw Error(error); throw Error(error);
} }
ExifData ed2;
std::cerr << "---> Reading file " << file2 << "\n"; std::cerr << "---> Reading file " << file2 << "\n";
rc = ed2.read(file2); rc = image2->readMetadata();
if (rc) { if (rc) {
std::string error = ExifData::strError(rc, file2.c_str()); std::string error = Exiv2::Image::strError(rc, file2);
throw Error(error); throw Exiv2::Error(error);
} }
Exiv2::ExifData &ed2 = image2->exifData();
exifPrint(ed2); exifPrint(ed2);
std::cerr << "---> Writing Exif thumbnail to file " << thumb << ".*\n"; std::cerr << "---> Writing Exif thumbnail to file " << thumb << ".*\n";
ed2.writeThumbnail(thumb); ed2.writeThumbnail(thumb);
} }
// ***************************************************************************** // *****************************************************************************

@ -10,6 +10,7 @@
*/ */
// ***************************************************************************** // *****************************************************************************
// included header files // included header files
#include "image.hpp"
#include "exif.hpp" #include "exif.hpp"
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
@ -150,22 +151,37 @@ catch (Exiv2::Error& e) {
void write(const std::string& file, Exiv2::ExifData& ed) void write(const std::string& file, Exiv2::ExifData& ed)
{ {
int rc = ed.write(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(ed);
int rc = image->writeMetadata();
if (rc) { if (rc) {
std::string error = Exiv2::ExifData::strError(rc, file); std::string error = Exiv2::Image::strError(rc, file);
throw Exiv2::Error(error); throw Exiv2::Error(error);
} }
} }
void print(const std::string& file) void print(const std::string& file)
{ {
Exiv2::ExifData ed; Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
int rc = ed.read(file); if (image.get() == 0) {
std::string error(file);
error += " : Could not read file or unknown image type";
throw Exiv2::Error(error);
}
int rc = image->readMetadata();
if (rc) { if (rc) {
std::string error = Exiv2::ExifData::strError(rc, file); std::string error = Exiv2::Image::strError(rc, file);
throw Exiv2::Error(error); throw Exiv2::Error(error);
} }
Exiv2::ExifData &ed = image->exifData();
Exiv2::ExifData::const_iterator end = ed.end(); Exiv2::ExifData::const_iterator end = ed.end();
for (Exiv2::ExifData::const_iterator i = ed.begin(); i != end; ++i) { for (Exiv2::ExifData::const_iterator i = ed.begin(); i != end; ++i) {
std::cout << std::setw(45) << std::setfill(' ') << std::left std::cout << std::setw(45) << std::setfill(' ') << std::left

@ -1 +0,0 @@
Caught Exiv2 exception 'temp: No Iptc data found in the file'

@ -1,4 +1,4 @@
#! /bin/sh #! /bin/bash
# Test driver for image file i/o # Test driver for image file i/o
eraseTest() eraseTest()

@ -0,0 +1,59 @@
#! /bin/bash
# Test driver for image file i/o
ioTest()
{
src=$datapath/$1
out1=${1}.1
out2=${1}.2
#run tests
$binpath/iotest $src $out1 $out2
if [ $? -ne 0 ]; then
let ++errors
return
fi
#check results
diffCheck $out1 $src
diffCheck $out2 $src
echo -n "."
}
# Make sure to pass the test file first and the known good file second
diffCheck()
{
test=$1
good=$2
#run diff and check results
diff -q --binary $test $good
if [ $? -ne 0 ]; then
let ++errors
else
rm $test
fi
}
# **********************************************************************
# main
LD_LIBRARY_PATH=../../src:$LD_LIBRARY_PATH
binpath="../../src"
datapath="../data"
test_files="table.jpg smiley2.jpg ext.dat"
let errors=0
cd ./tmp
echo
echo -n "Io tests"
for i in $test_files; do ioTest $i; done
echo -e "\n---------------------------------------------------------"
if [ $errors -eq 0 ]; then
echo 'All test cases passed'
else
echo $errors 'test case(s) failed!'
fi

@ -1,4 +1,4 @@
#! /bin/sh #! /bin/bash
# Test driver for Iptc metadata # Test driver for Iptc metadata
printTest() printTest()

Loading…
Cancel
Save