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).
This commit is contained in:
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}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iotest", "iotest\iotest.vcproj", "{5D43ECB3-681D-4732-9395-AB81CD283F6C}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{831EF580-92C8-4CA8-B0CE-3D906280A54D} = {831EF580-92C8-4CA8-B0CE-3D906280A54D}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iptceasy", "iptceasy\iptceasy.vcproj", "{D8B36F3A-34BB-4540-A731-EEABF1DC2E05}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{831EF580-92C8-4CA8-B0CE-3D906280A54D} = {831EF580-92C8-4CA8-B0CE-3D906280A54D}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfiguration) = preSolution
|
||||
Debug = Debug
|
||||
@ -139,6 +149,14 @@ Global
|
||||
{94A7505B-3A53-40F0-95A2-2ECB1CEC7C57}.Debug.Build.0 = Debug|Win32
|
||||
{94A7505B-3A53-40F0-95A2-2ECB1CEC7C57}.Release.ActiveCfg = Release|Win32
|
||||
{94A7505B-3A53-40F0-95A2-2ECB1CEC7C57}.Release.Build.0 = Release|Win32
|
||||
{5D43ECB3-681D-4732-9395-AB81CD283F6C}.Debug.ActiveCfg = Debug|Win32
|
||||
{5D43ECB3-681D-4732-9395-AB81CD283F6C}.Debug.Build.0 = Debug|Win32
|
||||
{5D43ECB3-681D-4732-9395-AB81CD283F6C}.Release.ActiveCfg = Release|Win32
|
||||
{5D43ECB3-681D-4732-9395-AB81CD283F6C}.Release.Build.0 = Release|Win32
|
||||
{D8B36F3A-34BB-4540-A731-EEABF1DC2E05}.Debug.ActiveCfg = Debug|Win32
|
||||
{D8B36F3A-34BB-4540-A731-EEABF1DC2E05}.Debug.Build.0 = Debug|Win32
|
||||
{D8B36F3A-34BB-4540-A731-EEABF1DC2E05}.Release.ActiveCfg = Release|Win32
|
||||
{D8B36F3A-34BB-4540-A731-EEABF1DC2E05}.Release.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
EndGlobalSection
|
||||
|
||||
@ -1,351 +1,363 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.10"
|
||||
Name="exiv2lib"
|
||||
ProjectGUID="{831EF580-92C8-4CA8-B0CE-3D906280A54D}"
|
||||
Keyword="Win32Proj">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="Debug"
|
||||
IntermediateDirectory="Debug"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\src;..\.."
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
||||
MinimalRebuild="TRUE"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="5"
|
||||
BufferSecurityCheck="TRUE"
|
||||
RuntimeTypeInfo="TRUE"
|
||||
UsePrecompiledHeader="0"
|
||||
BrowseInformation="1"
|
||||
WarningLevel="3"
|
||||
WarnAsError="TRUE"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="4"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/exiv2.lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="Release"
|
||||
IntermediateDirectory="Release"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories="..\..\src;..\.."
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
RuntimeLibrary="4"
|
||||
RuntimeTypeInfo="TRUE"
|
||||
UsePrecompiledHeader="0"
|
||||
BrowseInformation="1"
|
||||
WarningLevel="3"
|
||||
WarnAsError="TRUE"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="3"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/exiv2.lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<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\canonmn.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\datasets.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\exif.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\fujimn.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\ifd.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\image.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\iptc.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\makernote.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\metadatum.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\mn.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\nikonmn.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\sigmamn.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\tags.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\types.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\value.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
|
||||
<File
|
||||
RelativePath="..\..\src\canonmn.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\config.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\datasets.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\error.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\exif.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\fujimn.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\ifd.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\image.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\iptc.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\makernote.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\metadatum.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\nikonmn.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\rcsid.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\sigmamn.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\tags.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\types.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\value.hpp">
|
||||
</File>
|
||||
</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>
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.10"
|
||||
Name="exiv2lib"
|
||||
ProjectGUID="{831EF580-92C8-4CA8-B0CE-3D906280A54D}"
|
||||
Keyword="Win32Proj">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="Debug"
|
||||
IntermediateDirectory="Debug"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\src;..\.."
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
|
||||
MinimalRebuild="TRUE"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="5"
|
||||
BufferSecurityCheck="TRUE"
|
||||
RuntimeTypeInfo="TRUE"
|
||||
UsePrecompiledHeader="0"
|
||||
BrowseInformation="1"
|
||||
WarningLevel="3"
|
||||
WarnAsError="TRUE"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="4"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/exiv2.lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="Release"
|
||||
IntermediateDirectory="Release"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories="..\..\src;..\.."
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
|
||||
RuntimeLibrary="4"
|
||||
RuntimeTypeInfo="TRUE"
|
||||
UsePrecompiledHeader="0"
|
||||
BrowseInformation="1"
|
||||
WarningLevel="3"
|
||||
WarnAsError="TRUE"
|
||||
Detect64BitPortabilityProblems="TRUE"
|
||||
DebugInformationFormat="3"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
OutputFile="$(OutDir)/exiv2.lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<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\basicio.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\canonmn.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\datasets.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\exif.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\fujimn.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\ifd.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\image.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\iptc.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\jpgimage.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\makernote.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\metadatum.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\mn.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\nikonmn.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\sigmamn.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\tags.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\types.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\value.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
|
||||
<File
|
||||
RelativePath="..\..\src\basicio.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\canonmn.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\config.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\datasets.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\error.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\exif.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\fujimn.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\ifd.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\image.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\iptc.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\jpgimage.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\makernote.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\metadatum.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\nikonmn.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\rcsid.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\sigmamn.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\tags.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\types.hpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\value.hpp">
|
||||
</File>
|
||||
</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>
|
||||
|
||||
@ -28,61 +28,60 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "exivsimple.h"
|
||||
#include "image.hpp"
|
||||
#include "exif.hpp"
|
||||
#include "iptc.hpp"
|
||||
#include <cassert>
|
||||
|
||||
struct ExivImage
|
||||
struct ImageWrapper
|
||||
{
|
||||
Exiv2::IptcData iptcData;
|
||||
Exiv2::ExifData exifData;
|
||||
std::string fileName;
|
||||
Exiv2::Image::AutoPtr image;
|
||||
};
|
||||
|
||||
// Returns 0 if failed.
|
||||
EXIVSIMPLE_API HIMAGE OpenImage(const char *file)
|
||||
{
|
||||
assert(file);
|
||||
ImageWrapper *imgWrap = new ImageWrapper;
|
||||
|
||||
ExivImage *image = new ExivImage;
|
||||
image->fileName = file;
|
||||
|
||||
// rc of 3 just means no iptc or exif data (not a real error)
|
||||
int rc = image->iptcData.read( file );
|
||||
if (rc==0 || rc==3)
|
||||
rc = image->exifData.read( file );
|
||||
|
||||
if (rc!=0 && rc!=3) {
|
||||
delete image;
|
||||
image = 0;
|
||||
// See if file exists. Sorry for very bad error handling
|
||||
if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(file)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (HIMAGE)image;
|
||||
imgWrap->image = Exiv2::ImageFactory::open(file);
|
||||
if (imgWrap->image.get() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Load existing metadata
|
||||
if (imgWrap->image->readMetadata()) {
|
||||
delete imgWrap;
|
||||
imgWrap = 0;
|
||||
}
|
||||
|
||||
return (HIMAGE)imgWrap;
|
||||
}
|
||||
|
||||
EXIVSIMPLE_API void FreeImage(HIMAGE img)
|
||||
{
|
||||
if (img) {
|
||||
ExivImage *image = (ExivImage*)img;
|
||||
delete image;
|
||||
ImageWrapper *imgWrap = (ImageWrapper*)img;
|
||||
delete imgWrap;
|
||||
}
|
||||
}
|
||||
|
||||
EXIVSIMPLE_API int SaveImage(HIMAGE img)
|
||||
{
|
||||
assert(img);
|
||||
ExivImage *image = (ExivImage*)img;
|
||||
|
||||
int rc = image->iptcData.write(image->fileName);
|
||||
if (rc==0)
|
||||
rc = image->exifData.write(image->fileName);
|
||||
|
||||
return rc;
|
||||
ImageWrapper *imgWrap = (ImageWrapper*)img;
|
||||
return imgWrap->image->writeMetadata();
|
||||
}
|
||||
|
||||
// This is weird because iptc and exif have not been "unified". Once
|
||||
// the API is turned inside out, this DLL should not have to know
|
||||
// they are unified, this DLL should not have to know
|
||||
// about either... just generic images, keys, values, etc.
|
||||
//
|
||||
// buffsize should be the total size of *buff (including space for null)
|
||||
// Note that if there is more than one entry (for some IPTC datasets) this
|
||||
// returns the first one found. Currently no way to get the others.
|
||||
@ -90,14 +89,18 @@ EXIVSIMPLE_API int ReadMeta(HIMAGE img, const char *key, char *buff, int buffsiz
|
||||
{
|
||||
assert(img && key && buff);
|
||||
if (img==0 || key==0 || buff==0 || buffsize==0) return -1;
|
||||
ExivImage *image = (ExivImage*)img;
|
||||
ImageWrapper *imgWrap = (ImageWrapper*)img;
|
||||
int rc = 2;
|
||||
|
||||
Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
|
||||
Exiv2::ExifData &exifData = imgWrap->image->exifData();
|
||||
|
||||
try {
|
||||
// First try iptc
|
||||
Exiv2::IptcKey iptcKey(key);
|
||||
rc = 1;
|
||||
Exiv2::IptcData::const_iterator iter = image->iptcData.findKey(iptcKey);
|
||||
if (iter != image->iptcData.end()) {
|
||||
Exiv2::IptcData::const_iterator iter = iptcData.findKey(iptcKey);
|
||||
if (iter != iptcData.end()) {
|
||||
strncpy(buff, iter->value().toString().c_str(), buffsize);
|
||||
buff[buffsize-1] = 0;
|
||||
rc = 0;
|
||||
@ -111,8 +114,8 @@ EXIVSIMPLE_API int ReadMeta(HIMAGE img, const char *key, char *buff, int buffsiz
|
||||
try {
|
||||
Exiv2::ExifKey exifKey(key);
|
||||
rc = 1;
|
||||
Exiv2::ExifData::const_iterator iter = image->exifData.findKey(exifKey);
|
||||
if (iter != image->exifData.end()) {
|
||||
Exiv2::ExifData::const_iterator iter = exifData.findKey(exifKey);
|
||||
if (iter != exifData.end()) {
|
||||
strncpy(buff, iter->value().toString().c_str(), buffsize);
|
||||
buff[buffsize-1] = 0;
|
||||
rc = 0;
|
||||
@ -132,9 +135,12 @@ EXIVSIMPLE_API int ModifyMeta(HIMAGE img, const char *key, const char *val, DllT
|
||||
{
|
||||
assert(img && key && val);
|
||||
if (img==0 || key==0 || val==0) return -1;
|
||||
ExivImage *image = (ExivImage*)img;
|
||||
ImageWrapper *imgWrap = (ImageWrapper*)img;
|
||||
int rc = 2;
|
||||
|
||||
Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
|
||||
Exiv2::ExifData &exifData = imgWrap->image->exifData();
|
||||
|
||||
std::string data(val);
|
||||
// if data starts and ends with quotes, remove them
|
||||
if (data.at(0) == '\"' && data.at(data.size()-1) == '\"') {
|
||||
@ -150,13 +156,13 @@ EXIVSIMPLE_API int ModifyMeta(HIMAGE img, const char *key, const char *val, DllT
|
||||
Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
|
||||
value->read(data);
|
||||
|
||||
Exiv2::IptcData::iterator iter = image->iptcData.findKey(iptcKey);
|
||||
if (iter != image->iptcData.end()) {
|
||||
Exiv2::IptcData::iterator iter = iptcData.findKey(iptcKey);
|
||||
if (iter != iptcData.end()) {
|
||||
iter->setValue(value.get());
|
||||
rc = 0;
|
||||
}
|
||||
else {
|
||||
rc = image->iptcData.add(iptcKey, value.get());
|
||||
rc = iptcData.add(iptcKey, value.get());
|
||||
}
|
||||
}
|
||||
catch(const Exiv2::Error&) {
|
||||
@ -174,13 +180,13 @@ EXIVSIMPLE_API int ModifyMeta(HIMAGE img, const char *key, const char *val, DllT
|
||||
Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
|
||||
value->read(data);
|
||||
|
||||
Exiv2::ExifData::iterator iter = image->exifData.findKey(exifKey);
|
||||
if (iter != image->exifData.end()) {
|
||||
Exiv2::ExifData::iterator iter = exifData.findKey(exifKey);
|
||||
if (iter != exifData.end()) {
|
||||
iter->setValue(value.get());
|
||||
rc = 0;
|
||||
}
|
||||
else {
|
||||
image->exifData.add(exifKey, value.get());
|
||||
exifData.add(exifKey, value.get());
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
@ -198,9 +204,12 @@ EXIVSIMPLE_API int AddMeta(HIMAGE img, const char *key, const char *val, DllType
|
||||
{
|
||||
assert(img && key && val);
|
||||
if (img==0 || key==0 || val==0) return -1;
|
||||
ExivImage *image = (ExivImage*)img;
|
||||
ImageWrapper *imgWrap = (ImageWrapper*)img;
|
||||
int rc = 2;
|
||||
|
||||
Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
|
||||
Exiv2::ExifData &exifData = imgWrap->image->exifData();
|
||||
|
||||
std::string data(val);
|
||||
// if data starts and ends with quotes, remove them
|
||||
if (data.at(0) == '\"' && data.at(data.size()-1) == '\"') {
|
||||
@ -216,7 +225,7 @@ EXIVSIMPLE_API int AddMeta(HIMAGE img, const char *key, const char *val, DllType
|
||||
Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
|
||||
value->read(data);
|
||||
|
||||
rc = image->iptcData.add(iptcKey, value.get());
|
||||
rc = iptcData.add(iptcKey, value.get());
|
||||
}
|
||||
catch(const Exiv2::Error&) {
|
||||
}
|
||||
@ -233,7 +242,7 @@ EXIVSIMPLE_API int AddMeta(HIMAGE img, const char *key, const char *val, DllType
|
||||
Exiv2::Value::AutoPtr value = Exiv2::Value::create((Exiv2::TypeId)type);
|
||||
value->read(data);
|
||||
|
||||
image->exifData.add(exifKey, value.get());
|
||||
exifData.add(exifKey, value.get());
|
||||
rc = 0;
|
||||
}
|
||||
catch(const Exiv2::Error&) {
|
||||
@ -249,15 +258,18 @@ EXIVSIMPLE_API int RemoveMeta(HIMAGE img, const char *key)
|
||||
{
|
||||
assert(img && key);
|
||||
if (img==0 || key==0) return -1;
|
||||
ExivImage *image = (ExivImage*)img;
|
||||
ImageWrapper *imgWrap = (ImageWrapper*)img;
|
||||
int rc = 2;
|
||||
|
||||
Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
|
||||
Exiv2::ExifData &exifData = imgWrap->image->exifData();
|
||||
|
||||
try {
|
||||
Exiv2::IptcKey iptcKey(key);
|
||||
rc = 1;
|
||||
Exiv2::IptcData::iterator iter = image->iptcData.findKey(iptcKey);
|
||||
if (iter != image->iptcData.end()) {
|
||||
image->iptcData.erase(iter);
|
||||
Exiv2::IptcData::iterator iter = iptcData.findKey(iptcKey);
|
||||
if (iter != iptcData.end()) {
|
||||
iptcData.erase(iter);
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
@ -269,9 +281,9 @@ EXIVSIMPLE_API int RemoveMeta(HIMAGE img, const char *key)
|
||||
try {
|
||||
Exiv2::ExifKey exifKey(key);
|
||||
rc = 1;
|
||||
Exiv2::ExifData::iterator iter = image->exifData.findKey(exifKey);
|
||||
if (iter != image->exifData.end()) {
|
||||
image->exifData.erase(iter);
|
||||
Exiv2::ExifData::iterator iter = exifData.findKey(exifKey);
|
||||
if (iter != exifData.end()) {
|
||||
exifData.erase(iter);
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
@ -286,17 +298,20 @@ EXIVSIMPLE_API int EnumMeta(HIMAGE img, METAENUMPROC proc, void *user)
|
||||
{
|
||||
assert(img && proc);
|
||||
if (img==0 || proc==0) return -1;
|
||||
ExivImage *image = (ExivImage*)img;
|
||||
ImageWrapper *imgWrap = (ImageWrapper*)img;
|
||||
bool more = true;
|
||||
|
||||
Exiv2::IptcData::const_iterator iend = image->iptcData.end();
|
||||
for (Exiv2::IptcData::const_iterator i = image->iptcData.begin();
|
||||
Exiv2::IptcData &iptcData = imgWrap->image->iptcData();
|
||||
Exiv2::ExifData &exifData = imgWrap->image->exifData();
|
||||
|
||||
Exiv2::IptcData::const_iterator iend = iptcData.end();
|
||||
for (Exiv2::IptcData::const_iterator i = iptcData.begin();
|
||||
i != iend && more; ++i) {
|
||||
more = proc(i->key().c_str(), i->value().toString().c_str(), user);
|
||||
}
|
||||
|
||||
Exiv2::ExifData::const_iterator eend = image->exifData.end();
|
||||
for (Exiv2::ExifData::const_iterator e = image->exifData.begin();
|
||||
Exiv2::ExifData::const_iterator eend = exifData.end();
|
||||
for (Exiv2::ExifData::const_iterator e = exifData.begin();
|
||||
e != eend && more; ++e) {
|
||||
more = proc(e->key().c_str(), e->value().toString().c_str(), user);
|
||||
}
|
||||
|
||||
141
msvc/iotest/iotest.vcproj
Normal file
141
msvc/iotest/iotest.vcproj
Normal file
@ -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=""..\..\src";"..\..""
|
||||
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=""..\..\src";"..\..""
|
||||
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>
|
||||
144
msvc/iptceasy/iptceasy.vcproj
Normal file
144
msvc/iptceasy/iptceasy.vcproj
Normal file
@ -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=""..\..\src";"..\..""
|
||||
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=""..\..\src";"..\..""
|
||||
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
|
||||
|
||||
# Add library C++ source files to this list
|
||||
CCSRC = canonmn.cpp datasets.cpp exif.cpp fujimn.cpp ifd.cpp image.cpp iptc.cpp \
|
||||
CCSRC = canonmn.cpp datasets.cpp exif.cpp fujimn.cpp ifd.cpp basicio.cpp iptc.cpp \
|
||||
makernote.cpp metadatum.cpp nikonmn.cpp sigmamn.cpp tags.cpp types.cpp \
|
||||
value.cpp
|
||||
image.cpp jpgimage.cpp value.cpp
|
||||
|
||||
# Add source files of simple applications to this list
|
||||
BINSRC = addmoddel.cpp exifcomment.cpp exifprint.cpp ifd-test.cpp iptcprint.cpp \
|
||||
iptctest.cpp key-test.cpp makernote-test.cpp taglist.cpp write-test.cpp \
|
||||
write2-test.cpp dataarea-test.cpp iptceasy.cpp
|
||||
iotest.cpp write2-test.cpp dataarea-test.cpp iptceasy.cpp
|
||||
|
||||
# State the main source file of the Exiv2 application here
|
||||
EXIV2MAIN = exiv2.cpp
|
||||
|
||||
269
src/actions.cpp
269
src/actions.cpp
@ -39,6 +39,7 @@ EXIV2_RCSID("@(#) $Id$");
|
||||
#endif
|
||||
|
||||
#include "actions.hpp"
|
||||
#include "image.hpp"
|
||||
#include "exiv2.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "types.hpp"
|
||||
@ -167,13 +168,24 @@ namespace Action {
|
||||
|
||||
int Print::printSummary()
|
||||
{
|
||||
Exiv2::ExifData exifData;
|
||||
int rc = exifData.read(path_);
|
||||
if (!Util::fileExists(path_, true)) {
|
||||
std::cerr << path_
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
|
||||
if (image.get() == 0) {
|
||||
std::cerr << path_
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
return -2;
|
||||
}
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
|
||||
return rc;
|
||||
}
|
||||
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
align_ = 16;
|
||||
|
||||
// Filename
|
||||
@ -267,7 +279,7 @@ namespace Action {
|
||||
// Subject distance
|
||||
std::cout << std::setw(align_) << std::setfill(' ') << std::left
|
||||
<< "Subject distance" << ": ";
|
||||
if (0 == printTag(exifData, "Exif.Photo.SubjectDistance")) {
|
||||
if (0 == printTag(exifData, "Exif.Photo.SubjectDistance")) {
|
||||
md = exifData.findKey(
|
||||
Exiv2::ExifKey("Exif.Canon.CameraSettings2"));
|
||||
if (md != exifData.end() && md->count() >= 19) {
|
||||
@ -446,13 +458,24 @@ namespace Action {
|
||||
|
||||
int Print::printInterpreted()
|
||||
{
|
||||
Exiv2::ExifData exifData;
|
||||
int rc = exifData.read(path_);
|
||||
if (!Util::fileExists(path_, true)) {
|
||||
std::cerr << path_
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
|
||||
if (image.get() == 0) {
|
||||
std::cerr << path_
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
return -2;
|
||||
}
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
|
||||
return rc;
|
||||
}
|
||||
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
Exiv2::ExifData::const_iterator md;
|
||||
for (md = exifData.begin(); md != exifData.end(); ++md) {
|
||||
std::cout << "0x" << std::setw(4) << std::setfill('0') << std::right
|
||||
@ -469,13 +492,24 @@ namespace Action {
|
||||
|
||||
int Print::printValues()
|
||||
{
|
||||
Exiv2::ExifData exifData;
|
||||
int rc = exifData.read(path_);
|
||||
if (!Util::fileExists(path_, true)) {
|
||||
std::cerr << path_
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
|
||||
if (image.get() == 0) {
|
||||
std::cerr << path_
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
return -2;
|
||||
}
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
|
||||
return rc;
|
||||
}
|
||||
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
Exiv2::ExifData::const_iterator end = exifData.end();
|
||||
Exiv2::ExifData::const_iterator md;
|
||||
for (md = exifData.begin(); md != end; ++md) {
|
||||
@ -499,13 +533,24 @@ namespace Action {
|
||||
|
||||
int Print::printIptc()
|
||||
{
|
||||
Exiv2::IptcData iptcData;
|
||||
int rc = iptcData.read(path_);
|
||||
if (!Util::fileExists(path_, true)) {
|
||||
std::cerr << path_
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
|
||||
if (image.get() == 0) {
|
||||
std::cerr << path_
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
return -2;
|
||||
}
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::IptcData::strError(rc, path_) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
|
||||
return rc;
|
||||
}
|
||||
|
||||
Exiv2::IptcData &iptcData = image->iptcData();
|
||||
Exiv2::IptcData::const_iterator end = iptcData.end();
|
||||
Exiv2::IptcData::const_iterator md;
|
||||
for (md = iptcData.begin(); md != end; ++md) {
|
||||
@ -529,13 +574,24 @@ namespace Action {
|
||||
|
||||
int Print::printHexdump()
|
||||
{
|
||||
Exiv2::ExifData exifData;
|
||||
int rc = exifData.read(path_);
|
||||
if (!Util::fileExists(path_, true)) {
|
||||
std::cerr << path_
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
|
||||
if (image.get() == 0) {
|
||||
std::cerr << path_
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
return -2;
|
||||
}
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
|
||||
return rc;
|
||||
}
|
||||
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
Exiv2::ExifData::const_iterator md;
|
||||
for (md = exifData.begin(); md != exifData.end(); ++md) {
|
||||
std::cout << std::setw(4) << std::setfill(' ') << std::left
|
||||
@ -567,7 +623,7 @@ namespace Action {
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::instance().open(path_);
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
|
||||
if (image.get() == 0) {
|
||||
std::cerr << path_
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
@ -598,12 +654,24 @@ namespace Action {
|
||||
|
||||
int Rename::run(const std::string& path)
|
||||
try {
|
||||
Exiv2::ExifData exifData;
|
||||
int rc = exifData.read(path);
|
||||
if (!Util::fileExists(path, true)) {
|
||||
std::cerr << path
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path);
|
||||
if (image.get() == 0) {
|
||||
std::cerr << path
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
return -2;
|
||||
}
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path) << "\n";
|
||||
return rc;
|
||||
}
|
||||
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
Exiv2::ExifKey key("Exif.Photo.DateTimeOriginal");
|
||||
Exiv2::ExifData::iterator md = exifData.findKey(key);
|
||||
if (md == exifData.end()) {
|
||||
@ -683,8 +751,13 @@ namespace Action {
|
||||
try {
|
||||
path_ = path;
|
||||
|
||||
if (!Util::fileExists(path_, true)) {
|
||||
std::cerr << path_
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image
|
||||
= Exiv2::ImageFactory::instance().open(path_);
|
||||
= Exiv2::ImageFactory::open(path_);
|
||||
if (image.get() == 0) {
|
||||
std::cerr << path_
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
@ -710,7 +783,7 @@ namespace Action {
|
||||
if (0 == rc) {
|
||||
rc = image->writeMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@ -725,33 +798,22 @@ namespace Action {
|
||||
|
||||
int Erase::eraseThumbnail(Exiv2::Image* image) const
|
||||
{
|
||||
if (image->sizeExifData() == 0) {
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
std::string thumbExt = exifData.thumbnailExtension();
|
||||
if (thumbExt.empty()) {
|
||||
return 0;
|
||||
}
|
||||
int rc = 0;
|
||||
Exiv2::ExifData exifData;
|
||||
rc = exifData.read(image->exifData(), image->sizeExifData());
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
|
||||
long delta = exifData.eraseThumbnail();
|
||||
if (Params::instance().verbose_) {
|
||||
std::cout << "Erasing " << delta
|
||||
<< " Bytes of thumbnail data" << std::endl;
|
||||
}
|
||||
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
|
||||
{
|
||||
if (Params::instance().verbose_ && image->sizeExifData() > 0) {
|
||||
if (Params::instance().verbose_ && image->exifData().count() > 0) {
|
||||
std::cout << "Erasing Exif data from the file" << std::endl;
|
||||
}
|
||||
image->clearExifData();
|
||||
@ -760,7 +822,7 @@ namespace Action {
|
||||
|
||||
int Erase::eraseIptcData(Exiv2::Image* image) const
|
||||
{
|
||||
if (Params::instance().verbose_ && image->sizeIptcData() > 0) {
|
||||
if (Params::instance().verbose_ && image->iptcData().count() > 0) {
|
||||
std::cout << "Erasing Iptc data from the file" << std::endl;
|
||||
}
|
||||
image->clearIptcData();
|
||||
@ -816,12 +878,24 @@ namespace Action {
|
||||
|
||||
int Extract::writeThumbnail() const
|
||||
{
|
||||
Exiv2::ExifData exifData;
|
||||
int rc = exifData.read(path_);
|
||||
if (!Util::fileExists(path_, true)) {
|
||||
std::cerr << path_
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_);
|
||||
if (image.get() == 0) {
|
||||
std::cerr << path_
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
return -2;
|
||||
}
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path_) << "\n";
|
||||
return rc;
|
||||
}
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
|
||||
std::string thumb = Util::dirname(path_) + SEPERATOR_STR
|
||||
+ Util::basename(path_, true) + "-thumb";
|
||||
std::string thumbExt = exifData.thumbnailExtension();
|
||||
@ -898,14 +972,26 @@ namespace Action {
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::ExifData exifData;
|
||||
int rc = exifData.read(path);
|
||||
if (!Util::fileExists(path, true)) {
|
||||
std::cerr << path
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path);
|
||||
if (image.get() == 0) {
|
||||
std::cerr << path
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
return -2;
|
||||
}
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path) << "\n";
|
||||
return rc;
|
||||
}
|
||||
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
exifData.setJpegThumbnail(thumbPath);
|
||||
return exifData.write(path);
|
||||
return image->writeMetadata();
|
||||
|
||||
} // Insert::insertThumbnail
|
||||
|
||||
@ -926,10 +1012,17 @@ namespace Action {
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read both exif and iptc metadata (ignore return code)
|
||||
exifData_.read(path);
|
||||
iptcData_.read(path);
|
||||
image_ = Exiv2::ImageFactory::open(path);
|
||||
if (image_.get() == 0) {
|
||||
std::cerr << path
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
return -2;
|
||||
}
|
||||
int rc = image_->readMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::Image::strError(rc, path) << "\n";
|
||||
return rc;
|
||||
}
|
||||
|
||||
// loop through command table and apply each command
|
||||
ModifyCmds& modifyCmds = Params::instance().modifyCmds_;
|
||||
@ -953,13 +1046,9 @@ namespace Action {
|
||||
}
|
||||
|
||||
// Save both exif and iptc metadata
|
||||
int rc = exifData_.write(path);
|
||||
rc = image_->writeMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
|
||||
}
|
||||
rc = iptcData_.write(path);
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::IptcData::strError(rc, path) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path) << "\n";
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -981,10 +1070,10 @@ namespace Action {
|
||||
Exiv2::Value::AutoPtr value = Exiv2::Value::create(modifyCmd.typeId_);
|
||||
value->read(modifyCmd.value_);
|
||||
if (modifyCmd.metadataId_ == exif) {
|
||||
exifData_.add(Exiv2::ExifKey(modifyCmd.key_), value.get());
|
||||
image_->exifData().add(Exiv2::ExifKey(modifyCmd.key_), value.get());
|
||||
}
|
||||
if (modifyCmd.metadataId_ == iptc) {
|
||||
iptcData_.add(Exiv2::IptcKey(modifyCmd.key_), value.get());
|
||||
image_->iptcData().add(Exiv2::IptcKey(modifyCmd.key_), value.get());
|
||||
}
|
||||
}
|
||||
|
||||
@ -996,12 +1085,15 @@ namespace Action {
|
||||
<< Exiv2::TypeInfo::typeName(modifyCmd.typeId_)
|
||||
<< ")" << std::endl;
|
||||
}
|
||||
|
||||
Exiv2::ExifData &exifData = image_->exifData();
|
||||
Exiv2::IptcData &iptcData = image_->iptcData();
|
||||
Exiv2::Metadatum* metadatum = 0;
|
||||
if (modifyCmd.metadataId_ == exif) {
|
||||
metadatum = &exifData_[modifyCmd.key_];
|
||||
metadatum = &exifData[modifyCmd.key_];
|
||||
}
|
||||
if (modifyCmd.metadataId_ == iptc) {
|
||||
metadatum = &iptcData_[modifyCmd.key_];
|
||||
metadatum = &iptcData[modifyCmd.key_];
|
||||
}
|
||||
assert(metadatum);
|
||||
Exiv2::Value::AutoPtr value = metadatum->getValue();
|
||||
@ -1020,15 +1112,18 @@ namespace Action {
|
||||
if (Params::instance().verbose_) {
|
||||
std::cout << "Del " << modifyCmd.key_ << std::endl;
|
||||
}
|
||||
|
||||
Exiv2::ExifData &exifData = image_->exifData();
|
||||
Exiv2::IptcData &iptcData = image_->iptcData();
|
||||
if (modifyCmd.metadataId_ == exif) {
|
||||
Exiv2::ExifData::iterator pos =
|
||||
exifData_.findKey(Exiv2::ExifKey(modifyCmd.key_));
|
||||
if (pos != exifData_.end()) exifData_.erase(pos);
|
||||
exifData.findKey(Exiv2::ExifKey(modifyCmd.key_));
|
||||
if (pos != exifData.end()) exifData.erase(pos);
|
||||
}
|
||||
if (modifyCmd.metadataId_ == iptc) {
|
||||
Exiv2::IptcData::iterator pos =
|
||||
iptcData_.findKey(Exiv2::IptcKey(modifyCmd.key_));
|
||||
if (pos != iptcData_.end()) iptcData_.erase(pos);
|
||||
iptcData.findKey(Exiv2::IptcKey(modifyCmd.key_));
|
||||
if (pos != iptcData.end()) iptcData.erase(pos);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1046,19 +1141,31 @@ namespace Action {
|
||||
try {
|
||||
adjustment_ = Params::instance().adjustment_;
|
||||
|
||||
Exiv2::ExifData exifData;
|
||||
int rc = exifData.read(path);
|
||||
if (!Util::fileExists(path, true)) {
|
||||
std::cerr << path
|
||||
<< ": Failed to open the file\n";
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path);
|
||||
if (image.get() == 0) {
|
||||
std::cerr << path
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
return -2;
|
||||
}
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path) << "\n";
|
||||
return rc;
|
||||
}
|
||||
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
rc = adjustDateTime(exifData, "Exif.Image.DateTime", path);
|
||||
rc += adjustDateTime(exifData, "Exif.Photo.DateTimeOriginal", path);
|
||||
rc += adjustDateTime(exifData, "Exif.Photo.DateTimeDigitized", path);
|
||||
if (rc) return 1;
|
||||
rc = exifData.write(path);
|
||||
rc = image->writeMetadata();
|
||||
if (rc) {
|
||||
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
|
||||
std::cerr << Exiv2::Image::strError(rc, path) << "\n";
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -1182,7 +1289,7 @@ namespace {
|
||||
return -1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr sourceImage
|
||||
= Exiv2::ImageFactory::instance().open(source);
|
||||
= Exiv2::ImageFactory::open(source);
|
||||
if (sourceImage.get() == 0) {
|
||||
std::cerr << source
|
||||
<< ": The file contains data of an unknown image type\n";
|
||||
@ -1195,7 +1302,7 @@ namespace {
|
||||
return 1;
|
||||
}
|
||||
Exiv2::Image::AutoPtr targetImage
|
||||
= Exiv2::ImageFactory::instance().open(target);
|
||||
= Exiv2::ImageFactory::open(target);
|
||||
if (preserve && targetImage.get() != 0) {
|
||||
if (targetImage->readMetadata()) {
|
||||
std::cerr << target
|
||||
@ -1205,7 +1312,7 @@ namespace {
|
||||
}
|
||||
if (targetImage.get() == 0) {
|
||||
targetImage
|
||||
= Exiv2::ImageFactory::instance().create(Exiv2::Image::exv, target);
|
||||
= Exiv2::ImageFactory::create(Exiv2::Image::exv, target);
|
||||
}
|
||||
if (targetImage.get() == 0) {
|
||||
std::cerr << target
|
||||
@ -1213,22 +1320,20 @@ namespace {
|
||||
return 2;
|
||||
}
|
||||
if ( Params::instance().target_ & Params::ctExif
|
||||
&& sourceImage->sizeExifData() > 0) {
|
||||
&& sourceImage->exifData().count() > 0) {
|
||||
if (Params::instance().verbose_) {
|
||||
std::cout << "Writing Exif data from " << source
|
||||
<< " to " << target << std::endl;
|
||||
}
|
||||
targetImage->setExifData(sourceImage->exifData(),
|
||||
sourceImage->sizeExifData());
|
||||
targetImage->setExifData(sourceImage->exifData());
|
||||
}
|
||||
if ( Params::instance().target_ & Params::ctIptc
|
||||
&& sourceImage->sizeIptcData() > 0) {
|
||||
&& sourceImage->iptcData().count() > 0) {
|
||||
if (Params::instance().verbose_) {
|
||||
std::cout << "Writing Iptc data from " << source
|
||||
<< " to " << target << std::endl;
|
||||
}
|
||||
targetImage->setIptcData(sourceImage->iptcData(),
|
||||
sourceImage->sizeIptcData());
|
||||
targetImage->setIptcData(sourceImage->iptcData());
|
||||
}
|
||||
if ( Params::instance().target_ & Params::ctComment
|
||||
&& !sourceImage->comment().empty()) {
|
||||
|
||||
@ -303,9 +303,12 @@ namespace Action {
|
||||
virtual int run(const std::string& path);
|
||||
typedef std::auto_ptr<Modify> AutoPtr;
|
||||
AutoPtr clone() const;
|
||||
Modify() {}
|
||||
|
||||
private:
|
||||
virtual Modify* clone_() const;
|
||||
//! Copy contructor needed because of AutoPtr memeber
|
||||
Modify(const Modify& src) {}
|
||||
|
||||
//! Add a metadatum according to \em modifyCmd
|
||||
void addMetadatum(const ModifyCmd& modifyCmd);
|
||||
@ -314,8 +317,7 @@ namespace Action {
|
||||
//! Delete a metadatum according to \em modifyCmd
|
||||
void delMetadatum(const ModifyCmd& modifyCmd);
|
||||
|
||||
Exiv2::ExifData exifData_; //!< Exif metadata
|
||||
Exiv2::IptcData iptcData_; //!< Iptc metadata
|
||||
Exiv2::Image::AutoPtr image_; //!< Image to modify
|
||||
}; // class Modify
|
||||
|
||||
} // namespace Action
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// addmoddel.cpp, $Rev$
|
||||
// Sample program showing how to add, modify and delete Exif metadata.
|
||||
|
||||
#include "image.hpp"
|
||||
#include "exif.hpp"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
@ -14,7 +15,9 @@ try {
|
||||
}
|
||||
std::string file(argv[1]);
|
||||
|
||||
// Container for all metadata
|
||||
// Container for exif metadata. This is an example of creating
|
||||
// exif metadata from scratch. If you want to add, modify, delete
|
||||
// metadata that exists in an image, start with ImageFactory::open
|
||||
Exiv2::ExifData exifData;
|
||||
|
||||
// *************************************************************************
|
||||
@ -90,10 +93,18 @@ try {
|
||||
std::cout << "Deleted key \"" << key << "\"\n";
|
||||
|
||||
// *************************************************************************
|
||||
// Finally, write the remaining Exif data to an image file
|
||||
int rc = exifData.write(file);
|
||||
// Finally, write the remaining Exif data to the image file
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
|
||||
if (image.get() == 0) {
|
||||
std::string error(file);
|
||||
error += " : Could not read file or unknown image type";
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
image->setExifData(exifData);
|
||||
int rc = image->writeMetadata();
|
||||
if (rc) {
|
||||
std::string error = Exiv2::ExifData::strError(rc, file);
|
||||
std::string error = Exiv2::Image::strError(rc, file);
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
|
||||
427
src/basicio.cpp
Normal file
427
src/basicio.cpp
Normal file
@ -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
|
||||
605
src/basicio.hpp
Normal file
605
src/basicio.hpp
Normal file
@ -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)
|
||||
{
|
||||
int rc = ed.writeExifData(file);
|
||||
Image::AutoPtr image = ImageFactory::create(Image::exv, file);
|
||||
assert(image.get() != 0);
|
||||
|
||||
image->setExifData(ed);
|
||||
int rc = image->writeMetadata();
|
||||
if (rc) {
|
||||
std::string error = Exiv2::ExifData::strError(rc, file);
|
||||
std::string error = Exiv2::Image::strError(rc, file);
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
void print(const std::string& file)
|
||||
{
|
||||
Exiv2::ExifData ed;
|
||||
int rc = ed.read(file);
|
||||
Image::AutoPtr image = ImageFactory::open(file);
|
||||
assert(image.get() != 0);
|
||||
|
||||
// Load existing metadata
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::string error = Exiv2::ExifData::strError(rc, file);
|
||||
std::string error = Exiv2::Image::strError(rc, file);
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
Exiv2::ExifData &ed = image->exifData();
|
||||
Exiv2::ExifData::const_iterator end = ed.end();
|
||||
for (Exiv2::ExifData::const_iterator i = ed.begin(); i != end; ++i) {
|
||||
std::cout << std::setw(35) << std::setfill(' ') << std::left
|
||||
@ -109,29 +117,28 @@ void print(const std::string& file)
|
||||
<< i->count() << " "
|
||||
<< std::dec << i->value()
|
||||
<< "\n";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int read(const std::string& path)
|
||||
{
|
||||
Image::AutoPtr image = ImageFactory::instance().open(path);
|
||||
Image::AutoPtr image = ImageFactory::open(path);
|
||||
assert(image.get() != 0);
|
||||
|
||||
int rc = image->readMetadata();
|
||||
if (rc) return rc;
|
||||
if (image->sizeExifData() > 0) {
|
||||
const byte *pData = image->exifData();
|
||||
long size = image->sizeExifData();
|
||||
if (image->exifData().count() > 0) {
|
||||
DataBuf exifData = image->exifData().copy();
|
||||
long size = exifData.size_;
|
||||
|
||||
// Read the TIFF header
|
||||
TiffHeader tiffHeader;
|
||||
rc = tiffHeader.read(pData);
|
||||
rc = tiffHeader.read(exifData.pData_);
|
||||
if (rc) return rc;
|
||||
|
||||
// Read IFD0
|
||||
Ifd ifd0(ifd0Id);
|
||||
rc = ifd0.read(pData + tiffHeader.offset(),
|
||||
rc = ifd0.read(exifData.pData_ + tiffHeader.offset(),
|
||||
size - tiffHeader.offset(),
|
||||
tiffHeader.byteOrder(),
|
||||
tiffHeader.offset());
|
||||
@ -143,7 +150,7 @@ int read(const std::string& path)
|
||||
|
||||
Value::AutoPtr v = Value::create(TypeId(i->type()));
|
||||
v->read(i->data(), i->count() * i->typeSize(), tiffHeader.byteOrder());
|
||||
v->setDataArea(pData + v->toLong(), 32);
|
||||
v->setDataArea(exifData.pData_ + v->toLong(), 32);
|
||||
|
||||
std::cout << "Value of tag 0x8298: " << std::hex;
|
||||
v->write(std::cout);
|
||||
@ -162,7 +169,7 @@ int read(const std::string& path)
|
||||
|
||||
v = Value::create(TypeId(i->type()));
|
||||
v->read(i->data(), i->count() * i->typeSize(), tiffHeader.byteOrder());
|
||||
v->setDataArea(pData + v->toLong(), 16);
|
||||
v->setDataArea(exifData.pData_ + v->toLong(), 16);
|
||||
|
||||
std::cout << "Value of tag 0x013b: ";
|
||||
v->write(std::cout);
|
||||
|
||||
79
src/exif.cpp
79
src/exif.cpp
@ -36,11 +36,12 @@ EXIV2_RCSID("@(#) $Id$");
|
||||
// included header files
|
||||
#include "exif.hpp"
|
||||
#include "types.hpp"
|
||||
#include "basicio.hpp"
|
||||
#include "error.hpp"
|
||||
#include "value.hpp"
|
||||
#include "ifd.hpp"
|
||||
#include "tags.hpp"
|
||||
#include "image.hpp"
|
||||
#include "jpgimage.hpp"
|
||||
#include "makernote.hpp"
|
||||
|
||||
// + standard includes
|
||||
@ -355,28 +356,7 @@ namespace Exiv2 {
|
||||
return *pos;
|
||||
}
|
||||
|
||||
int ExifData::read(const std::string& path)
|
||||
{
|
||||
if (!fileExists(path, true)) return -1;
|
||||
Image::AutoPtr image = ImageFactory::instance().open(path);
|
||||
if (image.get() == 0) {
|
||||
// We don't know this type of file
|
||||
return -2;
|
||||
}
|
||||
|
||||
int rc = image->readMetadata();
|
||||
if (rc == 0) {
|
||||
if (image->sizeExifData() > 0) {
|
||||
rc = read(image->exifData(), image->sizeExifData());
|
||||
}
|
||||
else {
|
||||
rc = 3;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ExifData::read(const byte* buf, long len)
|
||||
int ExifData::load(const byte* buf, long len)
|
||||
{
|
||||
// Copy the data buffer
|
||||
delete[] pData_;
|
||||
@ -476,38 +456,6 @@ namespace Exiv2 {
|
||||
return ret;
|
||||
} // ExifData::read
|
||||
|
||||
int ExifData::erase(const std::string& path) const
|
||||
{
|
||||
if (!fileExists(path, true)) return -1;
|
||||
Image::AutoPtr image = ImageFactory::instance().open(path);
|
||||
if (image.get() == 0) return -2;
|
||||
|
||||
// Read all metadata then erase only Exif data
|
||||
int rc = image->readMetadata();
|
||||
if (rc == 0) {
|
||||
image->clearExifData();
|
||||
rc = image->writeMetadata();
|
||||
}
|
||||
return rc;
|
||||
} // ExifData::erase
|
||||
|
||||
int ExifData::write(const std::string& path)
|
||||
{
|
||||
// Remove the Exif section from the file if there is no metadata
|
||||
if (count() == 0) return erase(path);
|
||||
|
||||
if (!fileExists(path, true)) return -1;
|
||||
Image::AutoPtr image = ImageFactory::instance().open(path);
|
||||
if (image.get() == 0) return -2;
|
||||
DataBuf buf(copy());
|
||||
// Read all metadata to preserve non-Exif data
|
||||
int rc = image->readMetadata();
|
||||
if (rc == 0) {
|
||||
image->setExifData(buf.pData_, buf.size_);
|
||||
rc = image->writeMetadata();
|
||||
}
|
||||
return rc;
|
||||
} // ExifData::write
|
||||
|
||||
DataBuf ExifData::copy()
|
||||
{
|
||||
@ -660,15 +608,6 @@ namespace Exiv2 {
|
||||
return buf;
|
||||
} // ExifData::copyFromMetadata
|
||||
|
||||
int ExifData::writeExifData(const std::string& path)
|
||||
{
|
||||
DataBuf buf(copy());
|
||||
ExvImage exvImage(path, true);
|
||||
if (!exvImage.good()) return -1;
|
||||
exvImage.setExifData(buf.pData_, buf.size_);
|
||||
return exvImage.writeMetadata();
|
||||
} // ExifData::writeExifData
|
||||
|
||||
void ExifData::add(Entries::const_iterator begin,
|
||||
Entries::const_iterator end,
|
||||
ByteOrder byteOrder)
|
||||
@ -846,11 +785,11 @@ namespace Exiv2 {
|
||||
if (thumbnail.get() == 0) return 8;
|
||||
|
||||
std::string name = path + thumbnail->extension();
|
||||
FileCloser file(fopen(name.c_str(), "wb"));
|
||||
if (!file.fp_) return -1;
|
||||
FileIo file(name);
|
||||
if (file.open("wb") != 0) return -1;
|
||||
|
||||
DataBuf buf(thumbnail->copy(*this));
|
||||
if (fwrite(buf.pData_, 1, buf.size_, file.fp_) != (size_t)buf.size_) {
|
||||
if (file.write(buf.pData_, buf.size_) != buf.size_) {
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
@ -1197,14 +1136,14 @@ namespace {
|
||||
|
||||
Exiv2::DataBuf readFile(const std::string& path)
|
||||
{
|
||||
Exiv2::FileCloser file(fopen(path.c_str(), "rb"));
|
||||
if (!file.fp_)
|
||||
Exiv2::FileIo file(path);
|
||||
if (file.open("rb") != 0)
|
||||
throw Exiv2::Error("Couldn't open input file");
|
||||
struct stat st;
|
||||
if (0 != stat(path.c_str(), &st))
|
||||
throw Exiv2::Error("Couldn't stat input file");
|
||||
Exiv2::DataBuf buf(st.st_size);
|
||||
long len = (long)fread(buf.pData_, 1, buf.size_, file.fp_);
|
||||
long len = file.read(buf.pData_, buf.size_);
|
||||
if (len != buf.size_)
|
||||
throw Exiv2::Error("Couldn't read input file");
|
||||
return buf;
|
||||
|
||||
59
src/exif.hpp
59
src/exif.hpp
@ -480,9 +480,6 @@ namespace Exiv2 {
|
||||
- extract and delete Exif thumbnail (JPEG and TIFF thumbnails)
|
||||
*/
|
||||
class ExifData {
|
||||
//! @name Not implemented
|
||||
//@{
|
||||
//@}
|
||||
public:
|
||||
//! ExifMetadata iterator type
|
||||
typedef ExifMetadata::iterator iterator;
|
||||
@ -504,43 +501,13 @@ namespace Exiv2 {
|
||||
//! Assignment operator (Todo: assign image data also)
|
||||
ExifData& operator=(const ExifData& rhs);
|
||||
/*!
|
||||
@brief Read the Exif data from file \em path.
|
||||
@param path Path to the file
|
||||
@return 0 if successful;<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
|
||||
@brief Load the Exif data from a byte buffer. The data buffer
|
||||
must start with the TIFF header.
|
||||
@param buf Pointer to the data buffer to read from
|
||||
@param len Number of bytes in the data buffer
|
||||
@return 0 if successful.
|
||||
*/
|
||||
int read(const byte* buf, long len);
|
||||
/*!
|
||||
@brief Write the Exif data to file \em path. If an Exif data section
|
||||
already exists in the file, it is replaced. If there is no
|
||||
metadata and no thumbnail to write, the Exif data section is
|
||||
deleted from the file. Otherwise, an Exif data section is
|
||||
created. See copy(byte* buf) for further details.
|
||||
|
||||
@return 0 if successful.
|
||||
*/
|
||||
int write(const std::string& path);
|
||||
/*!
|
||||
@brief Write the Exif data to a binary file. By convention, the
|
||||
filename extension should be ".exv". This file format contains
|
||||
the Exif data as it is found in a JPEG file, starting with the
|
||||
APP1 marker 0xffe1, the size of the data and the string
|
||||
"Exif\0\0". Exv files can be read with
|
||||
int read(const std::string& path) just like image Exif data.
|
||||
*/
|
||||
int writeExifData(const std::string& path);
|
||||
int load(const byte* buf, long len);
|
||||
/*!
|
||||
@brief Write the Exif data to a data buffer, which is returned. The
|
||||
caller owns this copy and %DataBuf ensures that it will be
|
||||
@ -603,6 +570,11 @@ namespace Exiv2 {
|
||||
by this call.
|
||||
*/
|
||||
iterator erase(iterator pos);
|
||||
/*!
|
||||
@brief Delete all Exifdatum instances resulting in an empty container.
|
||||
Note that this also removes thumbnails.
|
||||
*/
|
||||
void clear() { eraseThumbnail(); exifMetadata_.clear(); }
|
||||
//! Sort metadata by key
|
||||
void sortByKey();
|
||||
//! Sort metadata by tag
|
||||
@ -709,12 +681,6 @@ namespace Exiv2 {
|
||||
|
||||
//! @name Accessors
|
||||
//@{
|
||||
/*!
|
||||
@brief Erase the Exif data section from file \em path.
|
||||
@param path Path to the file.
|
||||
@return 0 if successful.
|
||||
*/
|
||||
int erase(const std::string& path) const;
|
||||
//! Begin of the metadata
|
||||
const_iterator begin() const { return exifMetadata_.begin(); }
|
||||
//! End of the metadata
|
||||
@ -780,14 +746,13 @@ namespace Exiv2 {
|
||||
|
||||
/*!
|
||||
@brief Convert the return code \em rc from \n
|
||||
int read(const std::string& path); \n
|
||||
int write(const std::string& path); \n
|
||||
int writeExifData(const std::string& path); \n
|
||||
int writeThumbnail(const std::string& path) const; and \n
|
||||
int erase(const std::string& path) const \n
|
||||
int read(const byte* buf, long len), \n
|
||||
into an error message.
|
||||
@param rc Error code.
|
||||
@param path %Image file or other identifying string.
|
||||
@return String containing error message.
|
||||
|
||||
Todo: Implement global handling of error messages
|
||||
Todo: Implement global handling of error messages
|
||||
*/
|
||||
static std::string strError(int rc, const std::string& path);
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
*/
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
#include "image.hpp"
|
||||
#include "exif.hpp"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
@ -24,13 +25,22 @@ try {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Exiv2::ExifData exifData;
|
||||
int rc = exifData.read(argv[1]);
|
||||
if (rc) {
|
||||
std::string error = Exiv2::ExifData::strError(rc, argv[1]);
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]);
|
||||
if (image.get() == 0) {
|
||||
std::string error(argv[1]);
|
||||
error += " : Could not read file or unknown image type";
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
// Load existing metadata
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::string error = Exiv2::Image::strError(rc, argv[1]);
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
|
||||
/*
|
||||
There are two pitfalls that we need to consider when setting the Exif user
|
||||
comment (Exif.Photo.UserComment) of an image:
|
||||
@ -76,13 +86,13 @@ try {
|
||||
// output operator to print the formatted value
|
||||
std::cout << "Writing user comment '" << *pos << "' back to the image\n";
|
||||
|
||||
rc = exifData.write(argv[1]);
|
||||
rc = image->writeMetadata();
|
||||
if (rc) {
|
||||
std::string error = Exiv2::ExifData::strError(rc, argv[1]);
|
||||
std::string error = Exiv2::Image::strError(rc, argv[1]);
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
return rc;
|
||||
return rc;
|
||||
}
|
||||
catch (Exiv2::Error& e) {
|
||||
std::cout << "Caught Exiv2 exception '" << e << "'\n";
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// exifprint.cpp, $Rev$
|
||||
// Sample program to print the Exif metadata of an image
|
||||
|
||||
#include "image.hpp"
|
||||
#include "exif.hpp"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
@ -14,13 +15,21 @@ try {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Exiv2::ExifData exifData;
|
||||
int rc = exifData.read(argv[1]);
|
||||
if (rc) {
|
||||
std::string error = Exiv2::ExifData::strError(rc, argv[1]);
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]);
|
||||
if (image.get() == 0) {
|
||||
std::string error(argv[1]);
|
||||
error += " : Could not read file or unknown image type";
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
// Load existing metadata
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::string error = Exiv2::Image::strError(rc, argv[1]);
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
Exiv2::ExifData::const_iterator end = exifData.end();
|
||||
for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
|
||||
std::cout << std::setw(53) << std::setfill(' ') << std::left
|
||||
|
||||
771
src/image.cpp
771
src/image.cpp
@ -26,6 +26,7 @@
|
||||
History: 26-Jan-04, ahu: created
|
||||
11-Feb-04, ahu: isolated as a component
|
||||
19-Jul-04, brad: revamped to be more flexible and support Iptc
|
||||
15-Jan-05, brad: inside-out design changes
|
||||
*/
|
||||
// *****************************************************************************
|
||||
#include "rcsid.hpp"
|
||||
@ -42,9 +43,11 @@ EXIV2_RCSID("@(#) $Id$");
|
||||
#endif
|
||||
|
||||
#include "image.hpp"
|
||||
#include "types.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
// Ensure registration with factory
|
||||
#include "jpgimage.hpp"
|
||||
|
||||
// + standard includes
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
@ -55,71 +58,54 @@ EXIV2_RCSID("@(#) $Id$");
|
||||
#ifdef _MSC_VER
|
||||
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
#ifdef HAVE_PROCESS_H
|
||||
# include <process.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h> // for getpid, stat
|
||||
# include <unistd.h> // stat
|
||||
#endif
|
||||
|
||||
// *****************************************************************************
|
||||
// class member definitions
|
||||
namespace Exiv2 {
|
||||
|
||||
// Local functions. These could be static private functions on Image
|
||||
// subclasses but then ImageFactory needs to be made a friend.
|
||||
/*!
|
||||
@brief Create a new ExvImage instance and return an auto-pointer to it.
|
||||
Caller owns the returned object and the auto-pointer ensures that
|
||||
it will be deleted.
|
||||
*/
|
||||
Image::AutoPtr newExvInstance(const std::string& path, bool create);
|
||||
//! Check if the file ifp is an EXV file.
|
||||
bool isExvType(FILE* ifp, bool advance);
|
||||
/*!
|
||||
@brief Create a new JpegImage instance and return an auto-pointer to it.
|
||||
Caller owns the returned object and the auto-pointer ensures that
|
||||
it will be deleted.
|
||||
*/
|
||||
Image::AutoPtr newJpegInstance(const std::string& path, bool create);
|
||||
//! Check if the file ifp is a JPEG image.
|
||||
bool isJpegType(FILE* ifp, bool advance);
|
||||
ImageFactory::Registry* ImageFactory::registry_ = 0;
|
||||
|
||||
ImageFactory* ImageFactory::pInstance_ = 0;
|
||||
|
||||
ImageFactory& ImageFactory::instance()
|
||||
void ImageFactory::init()
|
||||
{
|
||||
if (0 == pInstance_) {
|
||||
pInstance_ = new ImageFactory;
|
||||
if (0 == registry_) {
|
||||
registry_ = new Registry;
|
||||
}
|
||||
return *pInstance_;
|
||||
} // ImageFactory::instance
|
||||
}
|
||||
|
||||
void ImageFactory::registerImage(Image::Type type,
|
||||
NewInstanceFct newInst, IsThisTypeFct isType)
|
||||
{
|
||||
init();
|
||||
assert (newInst && isType);
|
||||
registry_[type] = ImageFcts(newInst, isType);
|
||||
} // ImageFactory::registerImage
|
||||
(*registry_)[type] = ImageFcts(newInst, isType);
|
||||
}
|
||||
|
||||
ImageFactory::ImageFactory()
|
||||
Image::Type ImageFactory::getType(const std::string& path)
|
||||
{
|
||||
// Register a prototype of each known image
|
||||
registerImage(Image::jpeg, newJpegInstance, isJpegType);
|
||||
registerImage(Image::exv, newExvInstance, isExvType);
|
||||
} // ImageFactory c'tor
|
||||
FileIo fileIo(path);
|
||||
return getType(fileIo);
|
||||
}
|
||||
|
||||
Image::Type ImageFactory::getType(const std::string& path) const
|
||||
Image::Type ImageFactory::getType(const byte* data, long size)
|
||||
{
|
||||
FileCloser closer(fopen(path.c_str(), "rb"));
|
||||
if (!closer.fp_) return Image::none;
|
||||
MemIo memIo(data, size);
|
||||
return getType(memIo);
|
||||
}
|
||||
|
||||
Image::Type ImageFactory::getType(BasicIo& io)
|
||||
{
|
||||
IoCloser closer(io);
|
||||
if (io.open() != 0) return Image::none;
|
||||
|
||||
Image::Type type = Image::none;
|
||||
Registry::const_iterator b = registry_.begin();
|
||||
Registry::const_iterator e = registry_.end();
|
||||
Registry::const_iterator b = registry_->begin();
|
||||
Registry::const_iterator e = registry_->end();
|
||||
for (Registry::const_iterator i = b; i != e; ++i)
|
||||
{
|
||||
if (i->second.isThisType(closer.fp_, false)) {
|
||||
if (i->second.isThisType(io, false)) {
|
||||
type = i->first;
|
||||
break;
|
||||
}
|
||||
@ -127,657 +113,96 @@ namespace Exiv2 {
|
||||
return type;
|
||||
} // ImageFactory::getType
|
||||
|
||||
Image::AutoPtr ImageFactory::open(const std::string& path) const
|
||||
Image::AutoPtr ImageFactory::open(const std::string& path)
|
||||
{
|
||||
BasicIo::AutoPtr io(new FileIo(path));
|
||||
return open(io);
|
||||
}
|
||||
|
||||
Image::AutoPtr ImageFactory::open(const byte* data, long size)
|
||||
{
|
||||
BasicIo::AutoPtr io(new MemIo(data, size));
|
||||
return open(io);
|
||||
}
|
||||
|
||||
Image::AutoPtr ImageFactory::open(BasicIo::AutoPtr io)
|
||||
{
|
||||
Image::AutoPtr image;
|
||||
FileCloser closer(fopen(path.c_str(), "rb"));
|
||||
if (!closer.fp_) return image;
|
||||
IoCloser closer(*io);
|
||||
if (io->open() != 0) return image;
|
||||
|
||||
Registry::const_iterator b = registry_.begin();
|
||||
Registry::const_iterator e = registry_.end();
|
||||
Registry::const_iterator b = registry_->begin();
|
||||
Registry::const_iterator e = registry_->end();
|
||||
for (Registry::const_iterator i = b; i != e; ++i)
|
||||
{
|
||||
if (i->second.isThisType(closer.fp_, false)) {
|
||||
image = i->second.newInstance(path, false);
|
||||
if (i->second.isThisType(*io, false)) {
|
||||
image = i->second.newInstance(io, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return image;
|
||||
} // ImageFactory::open
|
||||
|
||||
|
||||
Image::AutoPtr ImageFactory::create(Image::Type type,
|
||||
const std::string& path) const
|
||||
const std::string& path)
|
||||
{
|
||||
Registry::const_iterator i = registry_.find(type);
|
||||
if (i != registry_.end()) {
|
||||
return i->second.newInstance(path, true);
|
||||
FileIo *fileIo = new FileIo(path);
|
||||
BasicIo::AutoPtr io(fileIo);
|
||||
// Create or overwrite the file, then close it
|
||||
if (fileIo->open("w+b") != 0) return Image::AutoPtr();
|
||||
fileIo->close();
|
||||
return create(type, io);
|
||||
}
|
||||
|
||||
Image::AutoPtr ImageFactory::create(Image::Type type)
|
||||
{
|
||||
BasicIo::AutoPtr io(new MemIo);
|
||||
return create(type, io);
|
||||
}
|
||||
|
||||
|
||||
Image::AutoPtr ImageFactory::create(Image::Type type,
|
||||
BasicIo::AutoPtr io)
|
||||
{
|
||||
// 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
|
||||
|
||||
|
||||
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)
|
||||
std::string Image::strError(int rc, const std::string& path)
|
||||
{
|
||||
if (create) {
|
||||
FILE* fp = fopen(path.c_str(), "w+b");
|
||||
if (fp) {
|
||||
initFile(fp, initData, dataSize);
|
||||
fclose(fp);
|
||||
}
|
||||
std::string error = path + ": ";
|
||||
switch (rc) {
|
||||
case -1:
|
||||
error += "Failed to open the file";
|
||||
break;
|
||||
case -2:
|
||||
error += "The file contains data of an unknown image type";
|
||||
break;
|
||||
case -3:
|
||||
error += "Couldn't open temporary file";
|
||||
break;
|
||||
case -4:
|
||||
error += "Renaming temporary file failed";
|
||||
break;
|
||||
case 1:
|
||||
error += "Couldn't read from the input file";
|
||||
break;
|
||||
case 2:
|
||||
error += "This does not look like a JPEG image";
|
||||
break;
|
||||
default:
|
||||
error += "Accessing image data failed, rc = " + toString(rc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int JpegBase::initFile(FILE* fp, const byte initData[], size_t dataSize)
|
||||
{
|
||||
if (!fp || ferror(fp)) return 4;
|
||||
if (fwrite(initData, 1, dataSize, fp) != dataSize) {
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
JpegBase::~JpegBase()
|
||||
{
|
||||
delete[] pExifData_;
|
||||
delete[] pIptcData_;
|
||||
}
|
||||
|
||||
bool JpegBase::good() const
|
||||
{
|
||||
FileCloser closer(fopen(path_.c_str(), "rb"));
|
||||
if (closer.fp_ == 0 ) return false;
|
||||
return isThisType(closer.fp_, false);
|
||||
}
|
||||
|
||||
void JpegBase::clearMetadata()
|
||||
{
|
||||
clearIptcData();
|
||||
clearExifData();
|
||||
clearComment();
|
||||
}
|
||||
|
||||
void JpegBase::clearIptcData()
|
||||
{
|
||||
delete[] pIptcData_;
|
||||
pIptcData_ = 0;
|
||||
sizeIptcData_ = 0;
|
||||
}
|
||||
|
||||
void JpegBase::clearExifData()
|
||||
{
|
||||
delete[] pExifData_;
|
||||
pExifData_ = 0;
|
||||
sizeExifData_ = 0;
|
||||
}
|
||||
|
||||
void JpegBase::clearComment()
|
||||
{
|
||||
comment_.erase();
|
||||
}
|
||||
|
||||
void JpegBase::setExifData(const byte* buf, long size)
|
||||
{
|
||||
if (size > 0xfffd) throw Error("Exif data too large");
|
||||
clearExifData();
|
||||
if (size) {
|
||||
sizeExifData_ = size;
|
||||
pExifData_ = new byte[size];
|
||||
memcpy(pExifData_, buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
void JpegBase::setIptcData(const byte* buf, long size)
|
||||
{
|
||||
clearIptcData();
|
||||
if (size) {
|
||||
sizeIptcData_ = size;
|
||||
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
|
||||
return error;
|
||||
} // Image::strError
|
||||
|
||||
|
||||
// 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
|
||||
if ( fwrite(record+sizeOldData, 1, sizeEnd, ofp)
|
||||
!= (size_t)sizeEnd) return 4;
|
||||
if (ferror(ofp)) return 4;
|
||||
}
|
||||
}
|
||||
if (marker == eoi_) {
|
||||
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;
|
||||
}
|
||||
|
||||
bool isJpegType(FILE* ifp, bool advance)
|
||||
{
|
||||
bool result = true;
|
||||
byte tmpBuf[2];
|
||||
fread(tmpBuf, 1, 2, ifp);
|
||||
if (ferror(ifp) || feof(ifp)) return false;
|
||||
|
||||
if (0xff!=tmpBuf[0] || JpegImage::soi_!=tmpBuf[1]) {
|
||||
result = false;
|
||||
}
|
||||
if (!advance || !result ) fseek(ifp, -2, SEEK_CUR);
|
||||
return result;
|
||||
}
|
||||
|
||||
const char ExvImage::exiv2Id_[] = "Exiv2";
|
||||
const byte ExvImage::blank_[] = { 0xff,0x01,'E','x','i','v','2',0xff,0xd9 };
|
||||
|
||||
ExvImage::ExvImage(const std::string& path, bool create)
|
||||
: JpegBase(path, create, blank_, sizeof(blank_))
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return isExvType(ifp, advance);
|
||||
}
|
||||
|
||||
Image::AutoPtr newExvInstance(const std::string& path, bool create)
|
||||
{
|
||||
Image::AutoPtr image;
|
||||
if (create) {
|
||||
image = Image::AutoPtr(new ExvImage(path, true));
|
||||
}
|
||||
else {
|
||||
image = Image::AutoPtr(new ExvImage(path, false));
|
||||
}
|
||||
if (!image->good()) image.reset();
|
||||
return image;
|
||||
}
|
||||
|
||||
bool isExvType(FILE* ifp, bool advance)
|
||||
{
|
||||
bool result = true;
|
||||
byte tmpBuf[7];
|
||||
fread(tmpBuf, 1, 7, ifp);
|
||||
if (ferror(ifp) || feof(ifp)) return false;
|
||||
|
||||
if (0xff!=tmpBuf[0] || 0x01!=tmpBuf[1] ||
|
||||
memcmp(tmpBuf + 2, ExvImage::exiv2Id_, 5) != 0) {
|
||||
result = false;
|
||||
}
|
||||
if (!advance || !result ) fseek(ifp, -7, SEEK_CUR);
|
||||
return result;
|
||||
}
|
||||
|
||||
TiffHeader::TiffHeader(ByteOrder byteOrder)
|
||||
: byteOrder_(byteOrder), tag_(0x002a), offset_(0x00000008)
|
||||
|
||||
618
src/image.hpp
618
src/image.hpp
@ -29,6 +29,7 @@
|
||||
@date 09-Jan-04, ahu: created<BR>
|
||||
11-Feb-04, ahu: isolated as a component<BR>
|
||||
19-Jul-04, brad: revamped to be more flexible and support Iptc
|
||||
15-Jan-05, brad: inside-out design changes
|
||||
*/
|
||||
#ifndef IMAGE_HPP_
|
||||
#define IMAGE_HPP_
|
||||
@ -36,29 +37,39 @@
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
#include "types.hpp"
|
||||
#include "basicio.hpp"
|
||||
|
||||
// + standard includes
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
// *****************************************************************************
|
||||
// namespace extensions
|
||||
namespace Exiv2 {
|
||||
|
||||
|
||||
// *****************************************************************************
|
||||
// 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 {
|
||||
public:
|
||||
//! Image auto_ptr type
|
||||
typedef std::auto_ptr<Image> AutoPtr;
|
||||
|
||||
//! Supported image formats
|
||||
enum Type { none, jpeg, exv };
|
||||
|
||||
//! Image auto_ptr type
|
||||
typedef std::auto_ptr<Image> AutoPtr;
|
||||
|
||||
//! @name Creators
|
||||
//@{
|
||||
@ -69,61 +80,67 @@ namespace Exiv2 {
|
||||
//! @name Manipulators
|
||||
//@{
|
||||
/*!
|
||||
@brief Read metadata from assigned image file into internal
|
||||
buffers.
|
||||
@brief Read metadata from assigned image. Before this method
|
||||
is called, the various metadata types (Iptc, Exif) will be empty.
|
||||
@return 0 if successful.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
virtual int writeMetadata() =0;
|
||||
/*!
|
||||
@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.
|
||||
@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
|
||||
*/
|
||||
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
|
||||
from the actual file until writeMetadata is called.
|
||||
@brief Erase any buffered Exif data. Exif data is not removed from
|
||||
the actual image until the writeMetadata() method is called.
|
||||
*/
|
||||
virtual void clearExifData() =0;
|
||||
/*!
|
||||
@brief Set the Iptc data. The data is copied into an internal data
|
||||
buffer and is not written until writeMetadata is called.
|
||||
@param buf Pointer to the new Iptc data.
|
||||
@param size Size in bytes of new Iptc data.
|
||||
@brief Assign new iptc data. The new iptc data is not written
|
||||
to the image until the writeMetadata() method is called.
|
||||
@param iptcData An IptcData instance holding iptc data to be copied
|
||||
*/
|
||||
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
|
||||
from the actual file until writeMetadata is called.
|
||||
@brief Erase any buffered Iptc data. Iptc data is not removed from
|
||||
the actual image until the writeMetadata() method is called.
|
||||
*/
|
||||
virtual void clearIptcData() =0;
|
||||
/*!
|
||||
@brief Set the image comment. The data is copied into an internal data
|
||||
buffer and is not written until writeMetadata is called.
|
||||
@brief Set the image comment. The new comment is not written
|
||||
to the image until the writeMetadata() method is called.
|
||||
@param comment String containing comment.
|
||||
*/
|
||||
virtual void setComment(const std::string& comment) =0;
|
||||
/*!
|
||||
@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;
|
||||
/*!
|
||||
@brief Copy all existing metadata from source %Image. The data is
|
||||
copied into internal buffers and is not written until
|
||||
writeMetadata is called.
|
||||
@brief Copy all existing metadata from source Image. The data is
|
||||
copied into internal buffers and is not written to the image
|
||||
until the writeMetadata() method is called.
|
||||
@param image Metadata source. All metadata types are copied.
|
||||
*/
|
||||
virtual void setMetadata(const Image& image) =0;
|
||||
/*!
|
||||
@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;
|
||||
//@}
|
||||
@ -131,31 +148,91 @@ namespace Exiv2 {
|
||||
//! @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.
|
||||
@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;
|
||||
//! 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
|
||||
attempt to write to this buffer.
|
||||
@brief Returns an ExifData instance containing currently buffered
|
||||
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 byte* exifData() const =0;
|
||||
//! Return the size of the Iptc data in bytes.
|
||||
virtual long sizeIptcData() const =0;
|
||||
virtual const ExifData& exifData() const =0;
|
||||
/*!
|
||||
@brief Return a read-only pointer to an Iptc data buffer. Do not
|
||||
attempt to write to this buffer.
|
||||
@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* iptcData() const =0;
|
||||
virtual ExifData& exifData() =0;
|
||||
/*!
|
||||
@brief Returns an IptcData instance containing currently buffered
|
||||
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 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.
|
||||
*/
|
||||
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:
|
||||
//! @name Creators
|
||||
//@{
|
||||
@ -173,17 +250,15 @@ namespace Exiv2 {
|
||||
}; // class Image
|
||||
|
||||
//! Type for function pointer that creates new Image instances
|
||||
typedef Image::AutoPtr (*NewInstanceFct)(const std::string& path,
|
||||
bool create);
|
||||
typedef Image::AutoPtr (*NewInstanceFct)(BasicIo::AutoPtr io, bool create);
|
||||
//! 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
|
||||
implemented as a singleton, which can be accessed only through the static
|
||||
member function instance().
|
||||
The factory is implemented as a singleton, which can be accessed
|
||||
through static member functions.
|
||||
*/
|
||||
class ImageFactory {
|
||||
public:
|
||||
@ -192,16 +267,16 @@ namespace Exiv2 {
|
||||
/*!
|
||||
@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
|
||||
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 newInst Function pointer for creating image instances.
|
||||
@param isType Function pointer to test for matching image types.
|
||||
*/
|
||||
void registerImage(Image::Type type,
|
||||
static void registerImage(Image::Type type,
|
||||
NewInstanceFct newInst,
|
||||
IsThisTypeFct isType);
|
||||
//@}
|
||||
@ -209,41 +284,101 @@ namespace Exiv2 {
|
||||
//! @name Accessors
|
||||
//@{
|
||||
/*!
|
||||
@brief Create an %Image of the appropriate type by opening the
|
||||
specified file. File type is derived from the contents of the
|
||||
file.
|
||||
@brief Create an Image subclass of the appropriate type by reading
|
||||
the specified file. %Image type is derived from the 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.
|
||||
determine the image type. File extension is ignored.
|
||||
@return An auto-pointer that owns an Image instance whose type
|
||||
matches that of the file. If no image type could be determined,
|
||||
the pointer is 0.
|
||||
*/
|
||||
Image::AutoPtr open(const std::string& path) const;
|
||||
static Image::AutoPtr open(const std::string& path);
|
||||
/*!
|
||||
@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.
|
||||
@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.
|
||||
*/
|
||||
Image::AutoPtr create(Image::Type type, const std::string& path) const;
|
||||
static Image::AutoPtr open(const byte* data, long size);
|
||||
/*!
|
||||
@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 of Image::none if the type is not recognized.
|
||||
@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.
|
||||
*/
|
||||
Image::Type getType(const std::string& path) const;
|
||||
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:
|
||||
//! @name Creators
|
||||
@ -252,6 +387,8 @@ namespace Exiv2 {
|
||||
ImageFactory();
|
||||
//! Prevent copy construction: not implemented.
|
||||
ImageFactory(const ImageFactory& rhs);
|
||||
//! Creates the private static instance
|
||||
static void init();
|
||||
//@}
|
||||
|
||||
//! Struct for storing image function pointers.
|
||||
@ -265,331 +402,12 @@ namespace Exiv2 {
|
||||
};
|
||||
|
||||
// DATA
|
||||
//! Pointer to the one and only instance of this class.
|
||||
static ImageFactory* pInstance_;
|
||||
//! Type used to store Image creation functions
|
||||
typedef std::map<Image::Type, ImageFcts> Registry;
|
||||
//! List of image types and corresponding creation functions.
|
||||
Registry registry_;
|
||||
|
||||
static Registry* registry_;
|
||||
}; // 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.
|
||||
class TiffHeader {
|
||||
|
||||
217
src/iotest.cpp
Normal file
217
src/iotest.cpp
Normal file
@ -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;
|
||||
}
|
||||
|
||||
71
src/iptc.cpp
71
src/iptc.cpp
@ -38,7 +38,7 @@ EXIV2_RCSID("@(#) $Id$");
|
||||
#include "error.hpp"
|
||||
#include "value.hpp"
|
||||
#include "datasets.hpp"
|
||||
#include "image.hpp"
|
||||
#include "jpgimage.hpp"
|
||||
|
||||
// + standard includes
|
||||
#include <iostream>
|
||||
@ -128,28 +128,7 @@ namespace Exiv2 {
|
||||
return *pos;
|
||||
}
|
||||
|
||||
int IptcData::read(const std::string& path)
|
||||
{
|
||||
if (!fileExists(path, true)) return -1;
|
||||
Image::AutoPtr image = ImageFactory::instance().open(path);
|
||||
if (image.get() == 0) {
|
||||
// We don't know this type of file
|
||||
return -2;
|
||||
}
|
||||
|
||||
int rc = image->readMetadata();
|
||||
if (rc == 0) {
|
||||
if (image->sizeIptcData() > 0) {
|
||||
rc = read(image->iptcData(), image->sizeIptcData());
|
||||
}
|
||||
else {
|
||||
rc = 3;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int IptcData::read(const byte* buf, long len)
|
||||
int IptcData::load(const byte* buf, long len)
|
||||
{
|
||||
const byte* pRead = buf;
|
||||
iptcMetadata_.clear();
|
||||
@ -201,41 +180,6 @@ namespace Exiv2 {
|
||||
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 buf(size());
|
||||
@ -268,7 +212,7 @@ namespace Exiv2 {
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
} // IptcData::updateBuffer
|
||||
|
||||
long IptcData::size() const
|
||||
{
|
||||
@ -288,15 +232,6 @@ namespace Exiv2 {
|
||||
return newSize;
|
||||
} // 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)
|
||||
{
|
||||
return add(Iptcdatum(key, value));
|
||||
|
||||
64
src/iptc.hpp
64
src/iptc.hpp
@ -290,48 +290,14 @@ namespace Exiv2 {
|
||||
//! @name Manipulators
|
||||
//@{
|
||||
/*!
|
||||
@brief Read the Iptc data from file path.
|
||||
@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
|
||||
@brief Load the Iptc data from a byte buffer. The format must follow
|
||||
the IPTC IIM4 standard.
|
||||
@param buf Pointer to the data buffer to read from
|
||||
@param len Number of bytes in the data buffer
|
||||
@return 0 if successful;<BR>
|
||||
5 if Iptc data is invalid or corrupt;<BR>
|
||||
*/
|
||||
int read(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);
|
||||
int load(const byte* buf, long len);
|
||||
/*!
|
||||
@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
|
||||
@ -370,6 +336,10 @@ namespace Exiv2 {
|
||||
by this call.
|
||||
*/
|
||||
iterator erase(iterator pos);
|
||||
/*!
|
||||
@brief Delete all Iptcdatum instances resulting in an empty container.
|
||||
*/
|
||||
void clear() { iptcMetadata_.clear(); }
|
||||
//! Sort metadata by key
|
||||
void sortByKey();
|
||||
//! Sort metadata by tag (aka dataset)
|
||||
@ -396,15 +366,6 @@ namespace Exiv2 {
|
||||
|
||||
//! @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
|
||||
const_iterator begin() const { return iptcMetadata_.begin(); }
|
||||
//! End of the metadata
|
||||
@ -432,15 +393,14 @@ namespace Exiv2 {
|
||||
//@}
|
||||
|
||||
/*!
|
||||
@brief Convert the return code from
|
||||
int read(const std::string& path),
|
||||
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
|
||||
@brief Convert the return code from \n
|
||||
int read(const byte* buf, long len), \n
|
||||
into an error message.
|
||||
@param rc Error code.
|
||||
@param path %Image file or other identifying string.
|
||||
@return String containing error message.
|
||||
|
||||
Todo: Implement global handling of error messages
|
||||
Todo: Implement global handling of error messages
|
||||
*/
|
||||
static std::string strError(int rc, const std::string& path);
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
// The quickest way to access, set or modify Iptc metadata.
|
||||
|
||||
#include "iptc.hpp"
|
||||
#include "image.hpp"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
@ -31,7 +32,24 @@ try {
|
||||
|
||||
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) {
|
||||
std::cout << "Caught Exiv2 exception '" << e << "'\n";
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// iptcprint.cpp, $Rev$
|
||||
// Sample program to print the Iptc metadata of an image
|
||||
|
||||
#include "image.hpp"
|
||||
#include "iptc.hpp"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
@ -14,13 +15,21 @@ try {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Exiv2::IptcData iptcData;
|
||||
int rc = iptcData.read(argv[1]);
|
||||
if (rc) {
|
||||
std::string error = Exiv2::IptcData::strError(rc, argv[1]);
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]);
|
||||
if (image.get() == 0) {
|
||||
std::string error(argv[1]);
|
||||
error += " : Could not read file or unknown image type";
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
// Load existing metadata
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::string error = Exiv2::Image::strError(rc, argv[1]);
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
Exiv2::IptcData &iptcData = image->iptcData();
|
||||
Exiv2::IptcData::iterator end = iptcData.end();
|
||||
for (Exiv2::IptcData::iterator md = iptcData.begin(); md != end; ++md) {
|
||||
std::cout << std::setw(36) << std::setfill(' ') << std::left
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
*/
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
#include "image.hpp"
|
||||
#include "iptc.hpp"
|
||||
#include "datasets.hpp"
|
||||
#include "value.hpp"
|
||||
@ -18,12 +19,10 @@
|
||||
|
||||
using namespace Exiv2;
|
||||
|
||||
bool processLine(const std::string& line, int num);
|
||||
void processAdd(const std::string& line, int num);
|
||||
void processRemove(const std::string& line, int num);
|
||||
void processModify(const std::string& line, int num);
|
||||
|
||||
IptcData g_iptcData;
|
||||
bool processLine(const std::string& line, int num, IptcData &iptcData);
|
||||
void processAdd(const std::string& line, int num, IptcData &iptcData);
|
||||
void processRemove(const std::string& line, int num, IptcData &iptcData);
|
||||
void processModify(const std::string& line, int num, IptcData &iptcData);
|
||||
|
||||
// *****************************************************************************
|
||||
// Main
|
||||
@ -37,22 +36,30 @@ int main(int argc, char* const argv[])
|
||||
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) {
|
||||
std::string error = IptcData::strError(rc, argv[1]);
|
||||
std::string error = Image::strError(rc, argv[1]);
|
||||
throw Error(error);
|
||||
}
|
||||
|
||||
// Process commands
|
||||
std::string line;
|
||||
int num = 0;
|
||||
std::getline(std::cin, line);
|
||||
while (line.length() && processLine(line, ++num)) {
|
||||
while (line.length() && processLine(line, ++num, image->iptcData())) {
|
||||
std::getline(std::cin, line);
|
||||
}
|
||||
|
||||
rc = g_iptcData.write(argv[1]);
|
||||
|
||||
// Save any changes
|
||||
rc = image->writeMetadata();
|
||||
if (rc) {
|
||||
std::string error = IptcData::strError(rc, argv[1]);
|
||||
std::string error = Image::strError(rc, argv[1]);
|
||||
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)) {
|
||||
case 'a':
|
||||
case 'A':
|
||||
processAdd(line, num);
|
||||
processAdd(line, num, iptcData);
|
||||
break;
|
||||
case 'r':
|
||||
case 'R':
|
||||
processRemove(line, num);
|
||||
processRemove(line, num, iptcData);
|
||||
break;
|
||||
case 'm':
|
||||
case 'M':
|
||||
processModify(line, num);
|
||||
processModify(line, num, iptcData);
|
||||
break;
|
||||
case 'q':
|
||||
case 'Q':
|
||||
@ -90,7 +97,7 @@ bool processLine(const std::string& line, int num )
|
||||
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 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->read(data);
|
||||
|
||||
int rc = g_iptcData.add(iptcKey, value.get());
|
||||
int rc = iptcData.add(iptcKey, value.get());
|
||||
if (rc) {
|
||||
std::string error = IptcData::strError(rc, "Input file");
|
||||
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);
|
||||
|
||||
@ -137,13 +144,13 @@ void processRemove(const std::string& line, int num)
|
||||
const std::string key( line.substr(keyStart) );
|
||||
IptcKey iptcKey(key);
|
||||
|
||||
IptcData::iterator iter = g_iptcData.findKey(iptcKey);
|
||||
if (iter != g_iptcData.end()) {
|
||||
g_iptcData.erase(iter);
|
||||
IptcData::iterator iter = iptcData.findKey(iptcKey);
|
||||
if (iter != iptcData.end()) {
|
||||
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 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->read(data);
|
||||
|
||||
IptcData::iterator iter = g_iptcData.findKey(iptcKey);
|
||||
if (iter != g_iptcData.end()) {
|
||||
IptcData::iterator iter = iptcData.findKey(iptcKey);
|
||||
if (iter != iptcData.end()) {
|
||||
iter->setValue(value.get());
|
||||
}
|
||||
else {
|
||||
int rc = g_iptcData.add(iptcKey, value.get());
|
||||
int rc = iptcData.add(iptcKey, value.get());
|
||||
if (rc) {
|
||||
std::string error = IptcData::strError(rc, "Input file");
|
||||
throw Error(error);
|
||||
|
||||
670
src/jpgimage.cpp
Normal file
670
src/jpgimage.cpp
Normal file
@ -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
|
||||
418
src/jpgimage.hpp
Normal file
418
src/jpgimage.hpp
Normal file
@ -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
|
||||
#include "image.hpp"
|
||||
#include "iptc.hpp"
|
||||
#include "exif.hpp"
|
||||
#include "types.hpp"
|
||||
#include "metacopy.hpp"
|
||||
@ -50,8 +52,18 @@ try {
|
||||
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::ImageFactory::instance().open(params.read_);
|
||||
= Exiv2::ImageFactory::open(memIo);
|
||||
if (readImg.get() == 0) {
|
||||
std::cerr << params.progname() <<
|
||||
": Could not read file (" << params.read_ << ")\n";
|
||||
@ -64,7 +76,7 @@ try {
|
||||
}
|
||||
|
||||
Exiv2::Image::AutoPtr writeImg
|
||||
= Exiv2::ImageFactory::instance().open(params.write_);
|
||||
= Exiv2::ImageFactory::open(params.write_);
|
||||
if (writeImg.get() == 0) {
|
||||
std::cerr << params.progname() <<
|
||||
": Could not read file (" << params.write_ << ")\n";
|
||||
@ -79,10 +91,10 @@ try {
|
||||
}
|
||||
}
|
||||
if (params.iptc_) {
|
||||
writeImg->setIptcData(readImg->iptcData(), readImg->sizeIptcData());
|
||||
writeImg->setIptcData(readImg->iptcData());
|
||||
}
|
||||
if (params.exif_) {
|
||||
writeImg->setExifData(readImg->exifData(), readImg->sizeExifData());
|
||||
writeImg->setExifData(readImg->exifData());
|
||||
}
|
||||
if (params.comment_) {
|
||||
writeImg->setComment(readImg->comment());
|
||||
|
||||
@ -203,39 +203,6 @@ namespace Exiv2 {
|
||||
long size_;
|
||||
}; // 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
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
#include "image.hpp"
|
||||
#include "exif.hpp"
|
||||
#include "makernote.hpp"
|
||||
|
||||
@ -160,15 +161,24 @@ void testCase(const std::string& file1,
|
||||
const std::string& value)
|
||||
{
|
||||
ExifKey ek(key);
|
||||
ExifData ed1;
|
||||
|
||||
std::cerr << "---> Reading file " << file1 << "\n";
|
||||
int rc = ed1.read(file1);
|
||||
if (rc) {
|
||||
std::string error = ExifData::strError(rc, file1.c_str());
|
||||
throw Error(error);
|
||||
//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";
|
||||
int rc = image1->readMetadata();
|
||||
if (rc) {
|
||||
std::string error = Exiv2::Image::strError(rc, file1);
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
Exiv2::ExifData &ed1 = image1->exifData();
|
||||
std::cerr << "---> Modifying Exif data\n";
|
||||
Exiv2::ExifData::iterator pos = ed1.findKey(ek);
|
||||
if (pos == ed1.end()) {
|
||||
@ -176,27 +186,35 @@ void testCase(const std::string& file1,
|
||||
}
|
||||
pos->setValue(value);
|
||||
|
||||
std::cerr << "---> Writing Exif data to file " << file2 << "\n";
|
||||
rc = ed1.write(file2);
|
||||
if (rc) {
|
||||
std::string error = ExifData::strError(rc, file2.c_str());
|
||||
throw Error(error);
|
||||
// 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);
|
||||
}
|
||||
|
||||
ExifData ed2;
|
||||
image2->setExifData(image1->exifData());
|
||||
|
||||
std::cerr << "---> Writing Exif data to file " << file2 << "\n";
|
||||
rc = image2->writeMetadata();
|
||||
if (rc) {
|
||||
std::string error = Image::strError(rc, file2.c_str());
|
||||
throw Error(error);
|
||||
}
|
||||
|
||||
std::cerr << "---> Reading file " << file2 << "\n";
|
||||
rc = ed2.read(file2);
|
||||
rc = image2->readMetadata();
|
||||
if (rc) {
|
||||
std::string error = ExifData::strError(rc, file2.c_str());
|
||||
throw Error(error);
|
||||
std::string error = Exiv2::Image::strError(rc, file2);
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
Exiv2::ExifData &ed2 = image2->exifData();
|
||||
exifPrint(ed2);
|
||||
|
||||
std::cerr << "---> Writing Exif thumbnail to file " << thumb << ".*\n";
|
||||
ed2.writeThumbnail(thumb);
|
||||
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
*/
|
||||
// *****************************************************************************
|
||||
// included header files
|
||||
#include "image.hpp"
|
||||
#include "exif.hpp"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
@ -150,22 +151,37 @@ catch (Exiv2::Error& e) {
|
||||
|
||||
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) {
|
||||
std::string error = Exiv2::ExifData::strError(rc, file);
|
||||
std::string error = Exiv2::Image::strError(rc, file);
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
void print(const std::string& file)
|
||||
{
|
||||
Exiv2::ExifData ed;
|
||||
int rc = ed.read(file);
|
||||
if (rc) {
|
||||
std::string error = Exiv2::ExifData::strError(rc, 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);
|
||||
}
|
||||
|
||||
int rc = image->readMetadata();
|
||||
if (rc) {
|
||||
std::string error = Exiv2::Image::strError(rc, file);
|
||||
throw Exiv2::Error(error);
|
||||
}
|
||||
|
||||
Exiv2::ExifData &ed = image->exifData();
|
||||
Exiv2::ExifData::const_iterator end = ed.end();
|
||||
for (Exiv2::ExifData::const_iterator i = ed.begin(); i != end; ++i) {
|
||||
std::cout << std::setw(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
|
||||
|
||||
eraseTest()
|
||||
|
||||
59
test/iotest.sh
Normal file
59
test/iotest.sh
Normal file
@ -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
|
||||
|
||||
printTest()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user