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).
parent
cbfe3eead2
commit
0cab366ec2
@ -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>
|
@ -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>
|
@ -0,0 +1,427 @@
|
|||||||
|
// ***************************************************************** -*- C++ -*-
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
|
||||||
|
*
|
||||||
|
* This program is part of the Exiv2 distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
File: basicio.cpp
|
||||||
|
Version: $Rev$
|
||||||
|
Author(s): Brad Schick (brad) <brad@robotbattle.com>
|
||||||
|
History: 04-Dec-04, brad: created
|
||||||
|
*/
|
||||||
|
// *****************************************************************************
|
||||||
|
#include "rcsid.hpp"
|
||||||
|
EXIV2_RCSID("@(#) $Id$");
|
||||||
|
|
||||||
|
// Define DEBUG_MAKERNOTE to output debug information to std::cerr
|
||||||
|
#undef DEBUG_MAKERNOTE
|
||||||
|
|
||||||
|
// *****************************************************************************
|
||||||
|
// included header files
|
||||||
|
#include "basicio.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
// + standard includes
|
||||||
|
#include <cassert>
|
||||||
|
#include <sys/types.h> // for stat()
|
||||||
|
#include <sys/stat.h> // for stat()
|
||||||
|
#ifdef HAVE_PROCESS_H
|
||||||
|
# include <process.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
# include <unistd.h> // for getpid, stat
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// *****************************************************************************
|
||||||
|
// class member definitions
|
||||||
|
namespace Exiv2 {
|
||||||
|
|
||||||
|
FileIo::FileIo(const std::string& path) :
|
||||||
|
path_(path), fp_(0), opMode_(opSeek)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FileIo::~FileIo()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicIo::AutoPtr FileIo::temporary() const
|
||||||
|
{
|
||||||
|
BasicIo::AutoPtr basicIo;
|
||||||
|
|
||||||
|
struct stat buf;
|
||||||
|
int ret = stat(path_.c_str(), &buf);
|
||||||
|
|
||||||
|
// If file is > 1MB then use a file, otherwise use memory buffer
|
||||||
|
if (buf.st_size > 1048576 || ret != 0) {
|
||||||
|
pid_t pid = getpid();
|
||||||
|
std::string tmpname = path_ + toString(pid);
|
||||||
|
FileIo *fileIo = new FileIo(tmpname);
|
||||||
|
if (fileIo->open("w+b") != 0 ) {
|
||||||
|
delete fileIo;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
basicIo.reset(fileIo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
basicIo.reset(new MemIo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return basicIo;
|
||||||
|
}
|
||||||
|
|
||||||
|
long FileIo::write(const byte* data, long wcount )
|
||||||
|
{
|
||||||
|
assert(fp_ != 0);
|
||||||
|
|
||||||
|
// ANSI C requires a flush or seek when switching
|
||||||
|
// between read and write modes.
|
||||||
|
if (opMode_ == opRead) {
|
||||||
|
// on msvcrt fflush does not do the job
|
||||||
|
fseek(fp_, 0, SEEK_CUR);
|
||||||
|
}
|
||||||
|
opMode_ = opWrite;
|
||||||
|
return (long)fwrite(data, 1, wcount, fp_);
|
||||||
|
}
|
||||||
|
|
||||||
|
long FileIo::write(BasicIo& src)
|
||||||
|
{
|
||||||
|
assert(fp_ != 0);
|
||||||
|
if (static_cast<BasicIo*>(this)==&src) return 0;
|
||||||
|
if (!src.isopen()) return 0;
|
||||||
|
|
||||||
|
// ANSI C requires a flush or seek when switching
|
||||||
|
// between read and write modes.
|
||||||
|
if (opMode_ == opRead) {
|
||||||
|
// on msvcrt fflush does not do the job
|
||||||
|
fseek(fp_, 0, SEEK_CUR);
|
||||||
|
}
|
||||||
|
opMode_ = opWrite;
|
||||||
|
|
||||||
|
byte buf[4096];
|
||||||
|
long readCount = 0;
|
||||||
|
long writeCount = 0;
|
||||||
|
long writeTotal = 0;
|
||||||
|
while ((readCount = src.read(buf, sizeof(buf)))) {
|
||||||
|
writeTotal += writeCount = (long)fwrite(buf, 1, readCount, fp_);
|
||||||
|
if (writeCount != readCount) {
|
||||||
|
// try to reset back to where write stopped
|
||||||
|
src.seek(writeCount-readCount, BasicIo::cur);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileIo::transfer(BasicIo& src)
|
||||||
|
{
|
||||||
|
const bool wasOpen = (fp_ != 0);
|
||||||
|
const std::string lastMode(openMode_);
|
||||||
|
|
||||||
|
FileIo *fileIo = dynamic_cast<FileIo*>(&src);
|
||||||
|
if (fileIo) {
|
||||||
|
// Optimization if this is another instance of FileIo
|
||||||
|
close();
|
||||||
|
fileIo->close();
|
||||||
|
// MSVCRT rename that does not overwrite existing files
|
||||||
|
if (remove(path_.c_str()) != 0) return -4;
|
||||||
|
if (rename(fileIo->path_.c_str(), path_.c_str()) == -1) return -4;
|
||||||
|
remove(fileIo->path_.c_str());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// Generic handling, reopen both to reset to start
|
||||||
|
open("w+b");
|
||||||
|
if (src.open() !=0) return 1;
|
||||||
|
write(src);
|
||||||
|
src.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasOpen) open(lastMode);
|
||||||
|
else close();
|
||||||
|
|
||||||
|
return error() || src.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileIo::putb(byte data)
|
||||||
|
{
|
||||||
|
assert(fp_ != 0);
|
||||||
|
if (opMode_ == opRead) {
|
||||||
|
// on msvcrt fflush does not do the job
|
||||||
|
fseek(fp_, 0, SEEK_CUR);
|
||||||
|
}
|
||||||
|
opMode_ = opWrite;
|
||||||
|
return putc(data, fp_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileIo::seek(long offset, Position pos)
|
||||||
|
{
|
||||||
|
assert(fp_ != 0);
|
||||||
|
int fileSeek;
|
||||||
|
if (pos == BasicIo::cur) {
|
||||||
|
fileSeek = SEEK_CUR;
|
||||||
|
}
|
||||||
|
else if (pos == BasicIo::beg) {
|
||||||
|
fileSeek = SEEK_SET;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(pos == BasicIo::end);
|
||||||
|
fileSeek = SEEK_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
opMode_ = opSeek;
|
||||||
|
return fseek(fp_, offset, fileSeek);
|
||||||
|
}
|
||||||
|
|
||||||
|
long FileIo::tell() const
|
||||||
|
{
|
||||||
|
assert(fp_ != 0);
|
||||||
|
return ftell(fp_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileIo::open()
|
||||||
|
{
|
||||||
|
// Default open is in read-write binary mode
|
||||||
|
return open("r+b");
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileIo::open(const std::string& mode)
|
||||||
|
{
|
||||||
|
if (fp_ != 0) {
|
||||||
|
fclose(fp_);
|
||||||
|
}
|
||||||
|
|
||||||
|
openMode_ = mode;
|
||||||
|
opMode_ = opSeek;
|
||||||
|
fp_ = fopen(path_.c_str(), mode.c_str());
|
||||||
|
if (!fp_) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIo::isopen() const
|
||||||
|
{
|
||||||
|
return fp_ != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileIo::close()
|
||||||
|
{
|
||||||
|
if (fp_ != 0) {
|
||||||
|
fclose(fp_);
|
||||||
|
fp_= 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuf FileIo::read(long rcount)
|
||||||
|
{
|
||||||
|
assert(fp_ != 0);
|
||||||
|
DataBuf buf(rcount);
|
||||||
|
long readCount = read(buf.pData_, buf.size_);
|
||||||
|
buf.size_ = readCount;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
long FileIo::read(byte* buf, long rcount)
|
||||||
|
{
|
||||||
|
assert(fp_ != 0);
|
||||||
|
|
||||||
|
if (opMode_ == opWrite) {
|
||||||
|
// on msvcrt fflush does not do the job
|
||||||
|
fseek(fp_, 0, SEEK_CUR);
|
||||||
|
}
|
||||||
|
opMode_ = opRead;
|
||||||
|
return (long)fread(buf, 1, rcount, fp_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileIo::getb()
|
||||||
|
{
|
||||||
|
assert(fp_ != 0);
|
||||||
|
|
||||||
|
if (opMode_ == opWrite) {
|
||||||
|
// on msvcrt fflush does not do the job
|
||||||
|
fseek(fp_, 0, SEEK_CUR);
|
||||||
|
}
|
||||||
|
opMode_ = opRead;
|
||||||
|
return getc(fp_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileIo::error() const
|
||||||
|
{
|
||||||
|
return fp_ != 0 ? ferror(fp_) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIo::eof() const
|
||||||
|
{
|
||||||
|
assert(fp_ != 0);
|
||||||
|
return feof(fp_) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MemIo::MemIo(const byte* data, long size)
|
||||||
|
{
|
||||||
|
// If copying data is too slow it might be worth
|
||||||
|
// creating a readonly MemIo variant
|
||||||
|
data_.reserve(size);
|
||||||
|
data_.assign(data, data+size);
|
||||||
|
idx_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicIo::AutoPtr MemIo::temporary() const
|
||||||
|
{
|
||||||
|
return BasicIo::AutoPtr(new MemIo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemIo::checkSize(long wcount)
|
||||||
|
{
|
||||||
|
ByteVector::size_type need = wcount + idx_;
|
||||||
|
if (need > data_.size()) {
|
||||||
|
data_.resize(need);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long MemIo::write(const byte* data, long wcount )
|
||||||
|
{
|
||||||
|
checkSize(wcount);
|
||||||
|
// According to Josuttis 6.2.3 this is safe
|
||||||
|
memcpy(&data_[idx_], data, wcount);
|
||||||
|
idx_ += wcount;
|
||||||
|
return wcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MemIo::transfer(BasicIo& src)
|
||||||
|
{
|
||||||
|
MemIo *memIo = dynamic_cast<MemIo*>(&src);
|
||||||
|
if (memIo) {
|
||||||
|
// Optimization if this is another instance of MemIo
|
||||||
|
data_.swap(memIo->data_);
|
||||||
|
idx_ = 0;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// Generic reopen to reset position to start
|
||||||
|
data_.clear();
|
||||||
|
idx_ = 0;
|
||||||
|
if (src.open() != 0) return 1;
|
||||||
|
write(src);
|
||||||
|
src.close();
|
||||||
|
}
|
||||||
|
return error() || src.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
long MemIo::write(BasicIo& src)
|
||||||
|
{
|
||||||
|
if (static_cast<BasicIo*>(this)==&src) return 0;
|
||||||
|
if (!src.isopen()) return 0;
|
||||||
|
|
||||||
|
byte buf[4096];
|
||||||
|
long readCount = 0;
|
||||||
|
long writeTotal = 0;
|
||||||
|
while ((readCount = src.read(buf, sizeof(buf)))) {
|
||||||
|
write(buf, readCount);
|
||||||
|
writeTotal += readCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MemIo::putb(byte data)
|
||||||
|
{
|
||||||
|
checkSize(1);
|
||||||
|
data_[idx_++] = data;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MemIo::seek(long offset, Position pos)
|
||||||
|
{
|
||||||
|
ByteVector::size_type newIdx;
|
||||||
|
|
||||||
|
if (pos == BasicIo::cur ) {
|
||||||
|
newIdx = idx_ + offset;
|
||||||
|
}
|
||||||
|
else if (pos == BasicIo::beg) {
|
||||||
|
newIdx = offset;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(pos == BasicIo::end);
|
||||||
|
newIdx = data_.size() + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newIdx < 0 || newIdx > data_.size()) return 1;
|
||||||
|
idx_ = newIdx;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long MemIo::tell() const
|
||||||
|
{
|
||||||
|
return (long)idx_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MemIo::open()
|
||||||
|
{
|
||||||
|
idx_ = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemIo::isopen() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MemIo::close()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuf MemIo::read(long rcount)
|
||||||
|
{
|
||||||
|
DataBuf buf(rcount);
|
||||||
|
long readCount = read(buf.pData_, buf.size_);
|
||||||
|
buf.size_ = readCount;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
long MemIo::read(byte* buf, long rcount)
|
||||||
|
{
|
||||||
|
long avail = (long)(data_.size() - idx_);
|
||||||
|
long allow = std::min(rcount, avail);
|
||||||
|
|
||||||
|
// According to Josuttis 6.2.3 this is safe
|
||||||
|
memcpy(buf, &data_[idx_], allow);
|
||||||
|
idx_ += allow;
|
||||||
|
return allow;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MemIo::getb()
|
||||||
|
{
|
||||||
|
if (idx_ == data_.size())
|
||||||
|
return EOF;
|
||||||
|
return data_[idx_++];
|
||||||
|
}
|
||||||
|
|
||||||
|
int MemIo::error() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemIo::eof() const
|
||||||
|
{
|
||||||
|
return idx_ == data_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Exiv2
|
@ -0,0 +1,605 @@
|
|||||||
|
// ***************************************************************** -*- C++ -*-
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
|
||||||
|
*
|
||||||
|
* This program is part of the Exiv2 distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
/*!
|
||||||
|
@file basicio.hpp
|
||||||
|
@brief Simple binary IO abstraction
|
||||||
|
@version $Rev$
|
||||||
|
@author Brad Schick (brad)
|
||||||
|
<a href="mailto:brad@robotbattle.com">brad@robotbattle.com</a>
|
||||||
|
@date 04-Dec-04, brad: created
|
||||||
|
*/
|
||||||
|
#ifndef BASICIO_HPP_
|
||||||
|
#define BASICIO_HPP_
|
||||||
|
|
||||||
|
// *****************************************************************************
|
||||||
|
// included header files
|
||||||
|
#include "types.hpp"
|
||||||
|
#include "error.hpp"
|
||||||
|
|
||||||
|
// + standard includes
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
// *****************************************************************************
|
||||||
|
// namespace extensions
|
||||||
|
namespace Exiv2 {
|
||||||
|
|
||||||
|
// *****************************************************************************
|
||||||
|
// class definitions
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief An interface for simple binary IO.
|
||||||
|
|
||||||
|
Designed to have semantics
|
||||||
|
and names similar to those of C style FILE* operations. Subclasses
|
||||||
|
should all behave the same so that they can be interchanged.
|
||||||
|
*/
|
||||||
|
class BasicIo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! BasicIo auto_ptr type
|
||||||
|
typedef std::auto_ptr<BasicIo> AutoPtr;
|
||||||
|
|
||||||
|
//! Seek starting positions
|
||||||
|
enum Position { beg, cur, end };
|
||||||
|
|
||||||
|
//! @name Creators
|
||||||
|
//@{
|
||||||
|
//! Destructor
|
||||||
|
virtual ~BasicIo() {}
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! @name Manipulators
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Open the IO source using the default access mode. The
|
||||||
|
default mode should allow for reading and writing.
|
||||||
|
|
||||||
|
This method can also be used to "reopen" an IO source which will
|
||||||
|
flush any unwritten data and reset the IO position to the start.
|
||||||
|
Subclasses may provide custom methods to allow for
|
||||||
|
opening IO sources differently.
|
||||||
|
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
Nonzero if failure;
|
||||||
|
*/
|
||||||
|
virtual int open() = 0;
|
||||||
|
/*!
|
||||||
|
@brief Close the IO source. After closing a BasicIo instance can not
|
||||||
|
be read or written. Closing flushes any unwritten data. It is
|
||||||
|
safe to call close on a closed instance.
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
Nonzero if failure;
|
||||||
|
*/
|
||||||
|
virtual int close() = 0;
|
||||||
|
/*!
|
||||||
|
@brief Write data to the IO source. Current IO position is advanced
|
||||||
|
by the number of bytes written.
|
||||||
|
@param data Pointer to data. Data must be at least \em wcount
|
||||||
|
bytes long
|
||||||
|
@param wcount Number of bytes to be written.
|
||||||
|
@return Number of bytes written to IO source successfully;<BR>
|
||||||
|
0 if failure;
|
||||||
|
*/
|
||||||
|
virtual long write(const byte* data, long wcount) = 0;
|
||||||
|
/*!
|
||||||
|
@brief Write data that is read from another BasicIo instance to
|
||||||
|
the IO source. Current IO position is advanced by the number
|
||||||
|
of bytes written.
|
||||||
|
@param src Reference to another BasicIo instance. Reading start
|
||||||
|
at the source's current IO position
|
||||||
|
@return Number of bytes written to IO source successfully;<BR>
|
||||||
|
0 if failure;
|
||||||
|
*/
|
||||||
|
virtual long write(BasicIo& src) = 0;
|
||||||
|
/*!
|
||||||
|
@brief Write one byte to the IO source. Current IO position is
|
||||||
|
advanced by one byte.
|
||||||
|
@param data The single byte to be written.
|
||||||
|
@return The value of the byte written if successful;<BR>
|
||||||
|
EOF if failure;
|
||||||
|
*/
|
||||||
|
virtual int putb(byte data) = 0;
|
||||||
|
/*!
|
||||||
|
@brief Read data from the IO source. Reading starts at the current
|
||||||
|
IO position and the position is advanced by the number of bytes
|
||||||
|
read.
|
||||||
|
@param rcount Maximum number of bytes to read. Fewer bytes may be
|
||||||
|
read if \em rcount bytes are not available.
|
||||||
|
@return DataBuf instance containing the bytes read. Use the
|
||||||
|
DataBuf::size_ member to find the number of bytes read.
|
||||||
|
DataBuf::size_ will be 0 on failure.
|
||||||
|
*/
|
||||||
|
virtual DataBuf read(long rcount) = 0;
|
||||||
|
/*!
|
||||||
|
@brief Read data from the IO source. Reading starts at the current
|
||||||
|
IO position and the position is advanced by the number of bytes
|
||||||
|
read.
|
||||||
|
@param buf Pointer to a block of memory into which the read data
|
||||||
|
is stored. The memory block must be at least \em rcount bytes
|
||||||
|
long.
|
||||||
|
@param rcount Maximum number of bytes to read. Fewer bytes may be
|
||||||
|
read if \em rcount bytes are not available.
|
||||||
|
@return Number of bytes read from IO source successfully;<BR>
|
||||||
|
0 if failure;
|
||||||
|
*/
|
||||||
|
virtual long read(byte *buf, long rcount) = 0;
|
||||||
|
/*!
|
||||||
|
@brief Read one byte from the IO source. Current IO position is
|
||||||
|
advanced by one byte.
|
||||||
|
@return The byte read from the IO source if successful;<BR>
|
||||||
|
EOF if failure;
|
||||||
|
*/
|
||||||
|
virtual int getb() = 0;
|
||||||
|
/*!
|
||||||
|
@brief Remove all data from this object's IO source and then transfer
|
||||||
|
data from the \em src BasicIo object into this object.
|
||||||
|
|
||||||
|
The source object is invalidated by this operation and should not be
|
||||||
|
used after this method returns. This method exists primarily to
|
||||||
|
be used with the BasicIo::temporary() method.
|
||||||
|
|
||||||
|
@param src Reference to another BasicIo instance. The entire contents
|
||||||
|
of src are transferred to this object. The \em src object is
|
||||||
|
invalidated by the method.
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
Nonzero if failure;
|
||||||
|
*/
|
||||||
|
virtual int transfer(BasicIo& src) = 0;
|
||||||
|
/*!
|
||||||
|
@brief Move the current IO position.
|
||||||
|
@param offset Number of bytes to move the position relative
|
||||||
|
to the starting position specified by \em pos
|
||||||
|
@param pos Position from which the seek should start
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
Nonzero if failure;
|
||||||
|
*/
|
||||||
|
virtual int seek(long offset, Position pos) = 0;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! @name Accessors
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Get the current IO position.
|
||||||
|
@return Offset from the start of IO if successful;<BR>
|
||||||
|
-l if failure;
|
||||||
|
*/
|
||||||
|
virtual long tell() const = 0;
|
||||||
|
//!Returns true if the IO source is open, otherwise false.
|
||||||
|
virtual bool isopen() const = 0;
|
||||||
|
//!Returns 0 if the IO source is in a valid state, otherwise nonzero.
|
||||||
|
virtual int error() const = 0;
|
||||||
|
//!Returns true if the IO position has reach the end, otherwise false.
|
||||||
|
virtual bool eof() const = 0;
|
||||||
|
/*!
|
||||||
|
@brief Returns a temporary data storage location. This is often
|
||||||
|
needed to rewrite an IO source.
|
||||||
|
|
||||||
|
For example, data may be read from the original IO source, modified
|
||||||
|
in some way, and then saved to the temporary instance. After the
|
||||||
|
operation is complete, the BasicIo::transfer method can be used to
|
||||||
|
replace the original IO source with the modified version. Subclasses
|
||||||
|
are free to return any class that derives from BasicIo.
|
||||||
|
|
||||||
|
@return An instance of BasicIo on success;<BR>
|
||||||
|
Null pointer on failure;
|
||||||
|
*/
|
||||||
|
virtual BasicIo::AutoPtr temporary() const = 0;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//! @name Creators
|
||||||
|
//@{
|
||||||
|
//! Default Constructor
|
||||||
|
BasicIo() {}
|
||||||
|
//@}
|
||||||
|
}; // class BasicIo
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Utility class that closes a BasicIo instance upon destruction.
|
||||||
|
Meant to be used as a stack variable in functions that need to
|
||||||
|
ensure BasicIo instances get closed. Useful when functions return
|
||||||
|
errors from many locations.
|
||||||
|
*/
|
||||||
|
class IoCloser {
|
||||||
|
public:
|
||||||
|
//! @name Creators
|
||||||
|
//@{
|
||||||
|
//! Constructor, takes a BasicIo reference
|
||||||
|
IoCloser(BasicIo &bio) : bio_(bio) {}
|
||||||
|
//! Destructor, closes the BasicIo reference
|
||||||
|
~IoCloser() { close(); }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! @name Manipulators
|
||||||
|
//@{
|
||||||
|
//! Close the BasicIo if it is open
|
||||||
|
void close() { if (bio_.isopen()) bio_.close(); }
|
||||||
|
//@}
|
||||||
|
|
||||||
|
// DATA
|
||||||
|
//! The BasicIo reference
|
||||||
|
BasicIo &bio_;
|
||||||
|
private:
|
||||||
|
// Not implemented
|
||||||
|
//! Copy constructor
|
||||||
|
IoCloser(const IoCloser&);
|
||||||
|
//! Assignment operator
|
||||||
|
IoCloser& operator=(const IoCloser&);
|
||||||
|
}; // class IoCloser
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Provides binary file IO by implementing the BasicIo
|
||||||
|
interface.
|
||||||
|
*/
|
||||||
|
class FileIo : public BasicIo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! @name Creators
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Constructor that accepts the file path on which IO will be
|
||||||
|
performed. The constructor does not open the file, and
|
||||||
|
therefore never failes.
|
||||||
|
@param path The full path of a file
|
||||||
|
*/
|
||||||
|
FileIo(const std::string& path);
|
||||||
|
//! Destructor. Flushes and closes an open file.
|
||||||
|
virtual ~FileIo();
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! @name Manipulators
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Open the file using using the specified mode.
|
||||||
|
|
||||||
|
This method can also be used to "reopen" a file which will flush any
|
||||||
|
unwritten data and reset the IO position to the start. Although
|
||||||
|
files can be opened in binary or text mode, this class has
|
||||||
|
only been tested carefully in binary mode.
|
||||||
|
|
||||||
|
@param mode Specified that type of access allowed on the file.
|
||||||
|
Valid values match those of the C fopen command exactly.
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
Nonzero if failure;
|
||||||
|
*/
|
||||||
|
int open( const std::string& mode);
|
||||||
|
/*!
|
||||||
|
@brief Open the file using using the default access mode of "r+b".
|
||||||
|
This method can also be used to "reopen" a file which will flush
|
||||||
|
any unwritten data and reset the IO position to the start.
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
Nonzero if failure;
|
||||||
|
*/
|
||||||
|
virtual int open();
|
||||||
|
/*!
|
||||||
|
@brief Flush and unwritten data and close the file . It is
|
||||||
|
safe to call close on an already closed instance.
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
Nonzero if failure;
|
||||||
|
*/
|
||||||
|
virtual int close();
|
||||||
|
/*!
|
||||||
|
@brief Write data to the file. The file position is advanced
|
||||||
|
by the number of bytes written.
|
||||||
|
@param data Pointer to data. Data must be at least \em wcount
|
||||||
|
bytes long
|
||||||
|
@param wcount Number of bytes to be written.
|
||||||
|
@return Number of bytes written to the file successfully;<BR>
|
||||||
|
0 if failure;
|
||||||
|
*/
|
||||||
|
virtual long write(const byte* data, long wcount);
|
||||||
|
/*!
|
||||||
|
@brief Write data that is read from another BasicIo instance to
|
||||||
|
the file. The file position is advanced by the number
|
||||||
|
of bytes written.
|
||||||
|
@param src Reference to another BasicIo instance. Reading start
|
||||||
|
at the source's current IO position
|
||||||
|
@return Number of bytes written to the file successfully;<BR>
|
||||||
|
0 if failure;
|
||||||
|
*/
|
||||||
|
virtual long write(BasicIo& src);
|
||||||
|
/*!
|
||||||
|
@brief Write one byte to the file. The file position is
|
||||||
|
advanced by one byte.
|
||||||
|
@param data The single byte to be written.
|
||||||
|
@return The value of the byte written if successful;<BR>
|
||||||
|
EOF if failure;
|
||||||
|
*/
|
||||||
|
virtual int putb(byte data);
|
||||||
|
/*!
|
||||||
|
@brief Read data from the file. Reading starts at the current
|
||||||
|
file position and the position is advanced by the number of
|
||||||
|
bytes read.
|
||||||
|
@param rcount Maximum number of bytes to read. Fewer bytes may be
|
||||||
|
read if \em rcount bytes are not available.
|
||||||
|
@return DataBuf instance containing the bytes read. Use the
|
||||||
|
DataBuf::size_ member to find the number of bytes read.
|
||||||
|
DataBuf::size_ will be 0 on failure.
|
||||||
|
*/
|
||||||
|
virtual DataBuf read(long rcount);
|
||||||
|
/*!
|
||||||
|
@brief Read data from the file. Reading starts at the current
|
||||||
|
file position and the position is advanced by the number of
|
||||||
|
bytes read.
|
||||||
|
@param buf Pointer to a block of memory into which the read data
|
||||||
|
is stored. The memory block must be at least \em rcount bytes
|
||||||
|
long.
|
||||||
|
@param rcount Maximum number of bytes to read. Fewer bytes may be
|
||||||
|
read if \em rcount bytes are not available.
|
||||||
|
@return Number of bytes read from the file successfully;<BR>
|
||||||
|
0 if failure;
|
||||||
|
*/
|
||||||
|
virtual long read(byte *buf, long rcount);
|
||||||
|
/*!
|
||||||
|
@brief Read one byte from the file. The file position is
|
||||||
|
advanced by one byte.
|
||||||
|
@return The byte read from the file if successful;<BR>
|
||||||
|
EOF if failure;
|
||||||
|
*/
|
||||||
|
virtual int getb();
|
||||||
|
/*!
|
||||||
|
@brief Remove the contents of the file and then transfer data from
|
||||||
|
the \em src BasicIo object into the empty file.
|
||||||
|
|
||||||
|
This method is optimized to simply rename the source file if the
|
||||||
|
source object is another FileIo instance. The source BasicIo object
|
||||||
|
is invalidated by this operation and should not be used after this
|
||||||
|
method returns. This method exists primarily to be used with
|
||||||
|
the BasicIo::temporary() method.
|
||||||
|
|
||||||
|
@param src Reference to another BasicIo instance. The entire contents
|
||||||
|
of src are transferred to this object. The \em src object is
|
||||||
|
invalidated by the method.
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
Nonzero if failure;
|
||||||
|
*/
|
||||||
|
virtual int transfer(BasicIo& src);
|
||||||
|
/*!
|
||||||
|
@brief Move the current file position.
|
||||||
|
@param offset Number of bytes to move the file position
|
||||||
|
relative to the starting position specified by \em pos
|
||||||
|
@param pos Position from which the seek should start
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
Nonzero if failure;
|
||||||
|
*/
|
||||||
|
virtual int seek(long offset, Position pos);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! @name Accessors
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Get the current file position.
|
||||||
|
@return Offset from the start of the file if successful;<BR>
|
||||||
|
-l if failure;
|
||||||
|
*/
|
||||||
|
virtual long tell() const;
|
||||||
|
//!Returns true if the file is open, otherwise false.
|
||||||
|
virtual bool isopen() const;
|
||||||
|
//!Returns 0 if the file is in a valid state, otherwise nonzero.
|
||||||
|
virtual int error() const;
|
||||||
|
//!Returns true if the file position has reach the end, otherwise false.
|
||||||
|
virtual bool eof() const;
|
||||||
|
/*!
|
||||||
|
@brief Returns a temporary data storage location. The actual type
|
||||||
|
returned depends upon the size of the file represented a FileIo
|
||||||
|
object. For small files, a MemIo is returned while for large files
|
||||||
|
a FileIo is returned. Callers should not rely on this behavior,
|
||||||
|
however, since it may change.
|
||||||
|
@return An instance of BasicIo on success;<BR>
|
||||||
|
Null pointer on failure;
|
||||||
|
*/
|
||||||
|
virtual BasicIo::AutoPtr temporary() const;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// NOT IMPLEMENTED
|
||||||
|
//! Copy constructor
|
||||||
|
FileIo(FileIo& rhs);
|
||||||
|
//! Assignment operator
|
||||||
|
FileIo& operator=(const FileIo& rhs);
|
||||||
|
|
||||||
|
// Enumeration
|
||||||
|
enum OpMode { opRead, opWrite, opSeek };
|
||||||
|
|
||||||
|
// DATA
|
||||||
|
std::string path_;
|
||||||
|
std::string openMode_;
|
||||||
|
FILE *fp_;
|
||||||
|
OpMode opMode_;
|
||||||
|
}; // class FileIo
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Provides binary IO on blocks of memory by implementing the
|
||||||
|
BasicIo interface. The current implementation makes a copy of
|
||||||
|
any data passed to its constructors. If writes are performed, the
|
||||||
|
changed data can be retrieved using the read methods (since the
|
||||||
|
data used in construction is never modified).
|
||||||
|
|
||||||
|
@note If read only usage of this class is common, it might be worth
|
||||||
|
creating a specialized readonly class or changing this one to
|
||||||
|
have a readonly mode.
|
||||||
|
*/
|
||||||
|
class MemIo : public BasicIo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! @name Creators
|
||||||
|
//@{
|
||||||
|
//! Default constructor that results in an empty object
|
||||||
|
MemIo() { idx_ = 0; }
|
||||||
|
/*!
|
||||||
|
@brief Constructor that accepts a block of memory to be copied.
|
||||||
|
IO operations are performed on the copied memory.
|
||||||
|
@param data Pointer to data. Data must be at least \em size
|
||||||
|
bytes long
|
||||||
|
@param size Number of bytes to copy.
|
||||||
|
*/
|
||||||
|
MemIo(const byte* data, long size);
|
||||||
|
//! Destructor. Releases all managed memory
|
||||||
|
virtual ~MemIo() {}
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! @name Manipulators
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Memory IO is always open for reading and writing. This method
|
||||||
|
therefore only resets the IO position to the start.
|
||||||
|
@return 0
|
||||||
|
*/
|
||||||
|
virtual int open();
|
||||||
|
/*!
|
||||||
|
@brief Does nothing on MemIo objects.
|
||||||
|
@return 0
|
||||||
|
*/
|
||||||
|
virtual int close();
|
||||||
|
/*!
|
||||||
|
@brief Write data to the memory block. If needed, the size of the
|
||||||
|
internal memory block is expanded. The IO position is advanced
|
||||||
|
by the number of bytes written.
|
||||||
|
@param data Pointer to data. Data must be at least \em wcount
|
||||||
|
bytes long
|
||||||
|
@param wcount Number of bytes to be written.
|
||||||
|
@return Number of bytes written to the memory block successfully;<BR>
|
||||||
|
0 if failure;
|
||||||
|
*/
|
||||||
|
virtual long write(const byte* data, long wcount);
|
||||||
|
/*!
|
||||||
|
@brief Write data that is read from another BasicIo instance to
|
||||||
|
the memory block. If needed, the size of the internal memory
|
||||||
|
block is expanded. The IO position is advanced by the number
|
||||||
|
of bytes written.
|
||||||
|
@param src Reference to another BasicIo instance. Reading start
|
||||||
|
at the source's current IO position
|
||||||
|
@return Number of bytes written to the memory block successfully;<BR>
|
||||||
|
0 if failure;
|
||||||
|
*/
|
||||||
|
virtual long write(BasicIo& src);
|
||||||
|
/*!
|
||||||
|
@brief Write one byte to the memory block. The IO position is
|
||||||
|
advanced by one byte.
|
||||||
|
@param data The single byte to be written.
|
||||||
|
@return The value of the byte written if successful;<BR>
|
||||||
|
EOF if failure;
|
||||||
|
*/
|
||||||
|
virtual int putb(byte data);
|
||||||
|
/*!
|
||||||
|
@brief Read data from the memory block. Reading starts at the current
|
||||||
|
IO position and the position is advanced by the number of
|
||||||
|
bytes read.
|
||||||
|
@param rcount Maximum number of bytes to read. Fewer bytes may be
|
||||||
|
read if \em rcount bytes are not available.
|
||||||
|
@return DataBuf instance containing the bytes read. Use the
|
||||||
|
DataBuf::size_ member to find the number of bytes read.
|
||||||
|
DataBuf::size_ will be 0 on failure.
|
||||||
|
*/
|
||||||
|
virtual DataBuf read(long rcount);
|
||||||
|
/*!
|
||||||
|
@brief Read data from the memory block. Reading starts at the current
|
||||||
|
IO position and the position is advanced by the number of
|
||||||
|
bytes read.
|
||||||
|
@param buf Pointer to a block of memory into which the read data
|
||||||
|
is stored. The memory block must be at least \em rcount bytes
|
||||||
|
long.
|
||||||
|
@param rcount Maximum number of bytes to read. Fewer bytes may be
|
||||||
|
read if \em rcount bytes are not available.
|
||||||
|
@return Number of bytes read from the memory block successfully;<BR>
|
||||||
|
0 if failure;
|
||||||
|
*/
|
||||||
|
virtual long read(byte *buf, long rcount);
|
||||||
|
/*!
|
||||||
|
@brief Read one byte from the memory block. The IO position is
|
||||||
|
advanced by one byte.
|
||||||
|
@return The byte read from the memory block if successful;<BR>
|
||||||
|
EOF if failure;
|
||||||
|
*/
|
||||||
|
virtual int getb();
|
||||||
|
/*!
|
||||||
|
@brief Clear the memory clock and then transfer data from
|
||||||
|
the \em src BasicIo object into a new block of memory.
|
||||||
|
|
||||||
|
This method is optimized to simply swap memory block if the source
|
||||||
|
object is another MemIo instance. The source BasicIo instance
|
||||||
|
is invalidated by this operation and should not be used after this
|
||||||
|
method returns. This method exists primarily to be used with
|
||||||
|
the BasicIo::temporary() method.
|
||||||
|
|
||||||
|
@param src Reference to another BasicIo instance. The entire contents
|
||||||
|
of src are transferred to this object. The \em src object is
|
||||||
|
invalidated by the method.
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
Nonzero if failure;
|
||||||
|
*/
|
||||||
|
virtual int transfer(BasicIo& src);
|
||||||
|
/*!
|
||||||
|
@brief Move the current IO position.
|
||||||
|
@param offset Number of bytes to move the IO position
|
||||||
|
relative to the starting position specified by \em pos
|
||||||
|
@param pos Position from which the seek should start
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
Nonzero if failure;
|
||||||
|
*/
|
||||||
|
virtual int seek(long offset, Position pos);
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! @name Accessors
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Get the current IO position.
|
||||||
|
@return Offset from the start of the memory block
|
||||||
|
*/
|
||||||
|
virtual long tell() const;
|
||||||
|
//!Always returns true
|
||||||
|
virtual bool isopen() const;
|
||||||
|
//!Always returns 0
|
||||||
|
virtual int error() const;
|
||||||
|
//!Returns true if the IO position has reach the end, otherwise false.
|
||||||
|
virtual bool eof() const;
|
||||||
|
/*!
|
||||||
|
@brief Returns a temporary data storage location. Currently returns
|
||||||
|
an empty MemIo object, but callers should not rely on this
|
||||||
|
behavior since it may change.
|
||||||
|
@return An instance of BasicIo on success;<BR>
|
||||||
|
Null pointer on failure;
|
||||||
|
*/
|
||||||
|
virtual BasicIo::AutoPtr temporary() const;
|
||||||
|
//@}
|
||||||
|
private:
|
||||||
|
// NOT IMPLEMENTED
|
||||||
|
//! Copy constructor
|
||||||
|
MemIo(MemIo& rhs);
|
||||||
|
//! Assignment operator
|
||||||
|
MemIo& operator=(const MemIo& rhs);
|
||||||
|
|
||||||
|
// Typedefs
|
||||||
|
typedef std::vector<byte> ByteVector;
|
||||||
|
|
||||||
|
// DATA
|
||||||
|
ByteVector data_;
|
||||||
|
ByteVector::size_type idx_;
|
||||||
|
|
||||||
|
//METHODS
|
||||||
|
void checkSize(long wcount);
|
||||||
|
}; // class MemIo
|
||||||
|
} // namespace Exiv2
|
||||||
|
|
||||||
|
#endif // #ifndef BASICIO_HPP_
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,670 @@
|
|||||||
|
// ***************************************************************** -*- C++ -*-
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
|
||||||
|
*
|
||||||
|
* This program is part of the Exiv2 distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
File: jpgimage.cpp
|
||||||
|
Version: $Rev$
|
||||||
|
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
|
||||||
|
Brad Schick (brad) <brad@robotbattle.com>
|
||||||
|
History: 15-Jan-05, brad: split out from image.cpp
|
||||||
|
|
||||||
|
*/
|
||||||
|
// *****************************************************************************
|
||||||
|
#include "rcsid.hpp"
|
||||||
|
EXIV2_RCSID("@(#) $Id$");
|
||||||
|
|
||||||
|
// *****************************************************************************
|
||||||
|
// included header files
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#else
|
||||||
|
# ifdef _MSC_VER
|
||||||
|
# include <config_win32.h>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "jpgimage.hpp"
|
||||||
|
#include "error.hpp"
|
||||||
|
|
||||||
|
// + standard includes
|
||||||
|
#include <cstring>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
// *****************************************************************************
|
||||||
|
// class member definitions
|
||||||
|
namespace Exiv2 {
|
||||||
|
|
||||||
|
// Local functions. These could be static private functions on Image
|
||||||
|
// subclasses but then ImageFactory needs to be made a friend.
|
||||||
|
/*!
|
||||||
|
@brief Create a new ExvImage instance and return an auto-pointer to it.
|
||||||
|
Caller owns the returned object and the auto-pointer ensures that
|
||||||
|
it will be deleted.
|
||||||
|
*/
|
||||||
|
Image::AutoPtr newExvInstance(BasicIo::AutoPtr io, bool create);
|
||||||
|
//! Check if the file iIo is an EXV file
|
||||||
|
bool isExvType(BasicIo& iIo, bool advance);
|
||||||
|
/*!
|
||||||
|
@brief Create a new JpegImage instance and return an auto-pointer to it.
|
||||||
|
Caller owns the returned object and the auto-pointer ensures that
|
||||||
|
it will be deleted.
|
||||||
|
*/
|
||||||
|
Image::AutoPtr newJpegInstance(BasicIo::AutoPtr io, bool create);
|
||||||
|
//! Check if the file iIo is a JPEG image.
|
||||||
|
bool isJpegType(BasicIo& iIo, bool advance);
|
||||||
|
|
||||||
|
const byte JpegBase::sos_ = 0xda;
|
||||||
|
const byte JpegBase::eoi_ = 0xd9;
|
||||||
|
const byte JpegBase::app0_ = 0xe0;
|
||||||
|
const byte JpegBase::app1_ = 0xe1;
|
||||||
|
const byte JpegBase::app13_ = 0xed;
|
||||||
|
const byte JpegBase::com_ = 0xfe;
|
||||||
|
const uint16_t JpegBase::iptc_ = 0x0404;
|
||||||
|
const char JpegBase::exifId_[] = "Exif\0\0";
|
||||||
|
const char JpegBase::jfifId_[] = "JFIF\0";
|
||||||
|
const char JpegBase::ps3Id_[] = "Photoshop 3.0\0";
|
||||||
|
const char JpegBase::bimId_[] = "8BIM";
|
||||||
|
|
||||||
|
JpegBase::JpegBase(BasicIo::AutoPtr io, bool create,
|
||||||
|
const byte initData[], long dataSize)
|
||||||
|
: io_(io)
|
||||||
|
{
|
||||||
|
if (create) {
|
||||||
|
initImage(initData, dataSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int JpegBase::initImage(const byte initData[], long dataSize)
|
||||||
|
{
|
||||||
|
IoCloser closer(*io_);
|
||||||
|
if (io_->open() != 0) return 4;
|
||||||
|
if (io_->write(initData, dataSize) != dataSize) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JpegBase::good() const
|
||||||
|
{
|
||||||
|
IoCloser closer(*io_);
|
||||||
|
if (io_->open() != 0) return false;
|
||||||
|
return isThisType(*io_, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JpegBase::clearMetadata()
|
||||||
|
{
|
||||||
|
clearIptcData();
|
||||||
|
clearExifData();
|
||||||
|
clearComment();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JpegBase::clearIptcData()
|
||||||
|
{
|
||||||
|
iptcData_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JpegBase::clearExifData()
|
||||||
|
{
|
||||||
|
exifData_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JpegBase::clearComment()
|
||||||
|
{
|
||||||
|
comment_.erase();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JpegBase::setExifData(const ExifData& exifData)
|
||||||
|
{
|
||||||
|
exifData_ = exifData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JpegBase::setIptcData(const IptcData& iptcData)
|
||||||
|
{
|
||||||
|
iptcData_ = iptcData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JpegBase::setComment(const std::string& comment)
|
||||||
|
{
|
||||||
|
comment_ = comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JpegBase::setMetadata(const Image& image)
|
||||||
|
{
|
||||||
|
setIptcData(image.iptcData());
|
||||||
|
setExifData(image.exifData());
|
||||||
|
setComment(image.comment());
|
||||||
|
}
|
||||||
|
|
||||||
|
int JpegBase::advanceToMarker() const
|
||||||
|
{
|
||||||
|
int c = -1;
|
||||||
|
// Skips potential padding between markers
|
||||||
|
while ((c=io_->getb()) != 0xff) {
|
||||||
|
if (c == EOF) return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Markers can start with any number of 0xff
|
||||||
|
while ((c=io_->getb()) == 0xff) {
|
||||||
|
if (c == EOF) return -1;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JpegBase::readMetadata()
|
||||||
|
{
|
||||||
|
IoCloser closer(*io_);
|
||||||
|
if (io_->open() != 0) return 1;
|
||||||
|
|
||||||
|
// Ensure that this is the correct image type
|
||||||
|
if (!isThisType(*io_, true)) {
|
||||||
|
if (io_->error() || io_->eof()) return 1;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
clearMetadata();
|
||||||
|
int search = 3;
|
||||||
|
const long bufMinSize = 16;
|
||||||
|
long bufRead = 0;
|
||||||
|
DataBuf buf(bufMinSize);
|
||||||
|
|
||||||
|
// Read section marker
|
||||||
|
int marker = advanceToMarker();
|
||||||
|
if (marker < 0) return 2;
|
||||||
|
|
||||||
|
while (marker != sos_ && marker != eoi_ && search > 0) {
|
||||||
|
// Read size and signature (ok if this hits EOF)
|
||||||
|
bufRead = io_->read(buf.pData_, bufMinSize);
|
||||||
|
if (io_->error()) return 1;
|
||||||
|
uint16_t size = getUShort(buf.pData_, bigEndian);
|
||||||
|
|
||||||
|
if (marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) {
|
||||||
|
if (size < 8) return 2;
|
||||||
|
// Seek to begining and read the Exif data
|
||||||
|
io_->seek(8-bufRead, BasicIo::cur);
|
||||||
|
long sizeExifData = size - 8;
|
||||||
|
DataBuf rawExif(sizeExifData);
|
||||||
|
io_->read(rawExif.pData_, sizeExifData);
|
||||||
|
if (io_->error() || io_->eof()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (exifData_.load(rawExif.pData_, sizeExifData)) return 2;
|
||||||
|
--search;
|
||||||
|
}
|
||||||
|
else if (marker == app13_ && memcmp(buf.pData_ + 2, ps3Id_, 14) == 0) {
|
||||||
|
if (size < 16) return 2;
|
||||||
|
// Read the rest of the APP13 segment
|
||||||
|
// needed if bufMinSize!=16: io_->seek(16-bufRead, BasicIo::cur);
|
||||||
|
DataBuf psData(size - 16);
|
||||||
|
io_->read(psData.pData_, psData.size_);
|
||||||
|
if (io_->error() || io_->eof()) return 1;
|
||||||
|
const byte *record = 0;
|
||||||
|
uint16_t sizeIptc = 0;
|
||||||
|
uint16_t sizeHdr = 0;
|
||||||
|
// Find actual Iptc data within the APP13 segment
|
||||||
|
if (!locateIptcData(psData.pData_, psData.size_, &record,
|
||||||
|
&sizeHdr, &sizeIptc)) {
|
||||||
|
assert(sizeIptc);
|
||||||
|
if (iptcData_.load(record + sizeHdr, sizeIptc)) return 2;
|
||||||
|
}
|
||||||
|
--search;
|
||||||
|
}
|
||||||
|
else if (marker == com_ && comment_.empty())
|
||||||
|
{
|
||||||
|
if (size < 2) return 2;
|
||||||
|
// Jpegs can have multiple comments, but for now only read
|
||||||
|
// the first one (most jpegs only have one anyway). Comments
|
||||||
|
// are simple single byte ISO-8859-1 strings.
|
||||||
|
io_->seek(2-bufRead, BasicIo::cur);
|
||||||
|
buf.alloc(size-2);
|
||||||
|
io_->read(buf.pData_, size-2);
|
||||||
|
if (io_->error() || io_->eof()) return 1;
|
||||||
|
comment_.assign(reinterpret_cast<char*>(buf.pData_), size-2);
|
||||||
|
while ( comment_.length()
|
||||||
|
&& comment_.at(comment_.length()-1) == '\0') {
|
||||||
|
comment_.erase(comment_.length()-1);
|
||||||
|
}
|
||||||
|
--search;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (size < 2) return 2;
|
||||||
|
// Skip the remainder of the unknown segment
|
||||||
|
if (io_->seek(size-bufRead, BasicIo::cur)) return 2;
|
||||||
|
}
|
||||||
|
// Read the beginning of the next segment
|
||||||
|
marker = advanceToMarker();
|
||||||
|
if (marker < 0) return 2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} // JpegBase::readMetadata
|
||||||
|
|
||||||
|
|
||||||
|
// Operates on raw data (rather than file streams) to simplify reuse
|
||||||
|
int JpegBase::locateIptcData(const byte *pPsData,
|
||||||
|
long sizePsData,
|
||||||
|
const byte **record,
|
||||||
|
uint16_t *const sizeHdr,
|
||||||
|
uint16_t *const sizeIptc) const
|
||||||
|
{
|
||||||
|
assert(record);
|
||||||
|
assert(sizeHdr);
|
||||||
|
assert(sizeIptc);
|
||||||
|
// Used for error checking
|
||||||
|
long position = 0;
|
||||||
|
|
||||||
|
// Data should follow Photoshop format, if not exit
|
||||||
|
while (position <= (sizePsData - 14) &&
|
||||||
|
memcmp(pPsData + position, bimId_, 4)==0) {
|
||||||
|
const byte *hrd = pPsData + position;
|
||||||
|
position += 4;
|
||||||
|
uint16_t type = getUShort(pPsData+ position, bigEndian);
|
||||||
|
position += 2;
|
||||||
|
|
||||||
|
// Pascal string is padded to have an even size (including size byte)
|
||||||
|
byte psSize = pPsData[position] + 1;
|
||||||
|
psSize += (psSize & 1);
|
||||||
|
position += psSize;
|
||||||
|
if (position >= sizePsData) return -2;
|
||||||
|
|
||||||
|
// Data is also padded to be even
|
||||||
|
long dataSize = getULong(pPsData + position, bigEndian);
|
||||||
|
position += 4;
|
||||||
|
if (dataSize > sizePsData - position) return -2;
|
||||||
|
|
||||||
|
if (type == iptc_) {
|
||||||
|
*sizeIptc = static_cast<uint16_t>(dataSize);
|
||||||
|
*sizeHdr = psSize + 10;
|
||||||
|
*record = hrd;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
position += dataSize + (dataSize & 1);
|
||||||
|
}
|
||||||
|
return 3;
|
||||||
|
} // JpegBase::locateIptcData
|
||||||
|
|
||||||
|
int JpegBase::writeMetadata()
|
||||||
|
{
|
||||||
|
IoCloser closer(*io_);
|
||||||
|
if (io_->open() != 0) return 1;
|
||||||
|
BasicIo::AutoPtr tempIo(io_->temporary());
|
||||||
|
if (!tempIo.get()) return -3;
|
||||||
|
|
||||||
|
int rc = doWriteMetadata(*tempIo);
|
||||||
|
io_->close();
|
||||||
|
if( rc == 0 ) {
|
||||||
|
if (io_->transfer(*tempIo) != 0) return -3;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
} // JpegBase::writeMetadata
|
||||||
|
|
||||||
|
int JpegBase::doWriteMetadata(BasicIo& outIo)
|
||||||
|
{
|
||||||
|
if (!io_->isopen()) return 1;
|
||||||
|
if (!outIo.isopen()) return 4;
|
||||||
|
|
||||||
|
// Ensure that this is the correct image type
|
||||||
|
if (!isThisType(*io_, true)) {
|
||||||
|
if (io_->error() || io_->eof()) return 1;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const long bufMinSize = 16;
|
||||||
|
long bufRead = 0;
|
||||||
|
DataBuf buf(bufMinSize);
|
||||||
|
const long seek = io_->tell();
|
||||||
|
int count = 0;
|
||||||
|
int search = 0;
|
||||||
|
int insertPos = 0;
|
||||||
|
int skipApp1Exif = -1;
|
||||||
|
int skipApp13Ps3 = -1;
|
||||||
|
int skipCom = -1;
|
||||||
|
DataBuf psData;
|
||||||
|
|
||||||
|
// Write image header
|
||||||
|
if (writeHeader(outIo)) return 4;
|
||||||
|
|
||||||
|
// Read section marker
|
||||||
|
int marker = advanceToMarker();
|
||||||
|
if (marker < 0) return 2;
|
||||||
|
|
||||||
|
// First find segments of interest. Normally app0 is first and we want
|
||||||
|
// to insert after it. But if app0 comes after com, app1 and app13 then
|
||||||
|
// don't bother.
|
||||||
|
while (marker != sos_ && marker != eoi_ && search < 3) {
|
||||||
|
// Read size and signature (ok if this hits EOF)
|
||||||
|
bufRead = io_->read(buf.pData_, bufMinSize);
|
||||||
|
if (io_->error()) return 1;
|
||||||
|
uint16_t size = getUShort(buf.pData_, bigEndian);
|
||||||
|
|
||||||
|
if (marker == app0_) {
|
||||||
|
if (size < 2) return 2;
|
||||||
|
insertPos = count + 1;
|
||||||
|
if (io_->seek(size-bufRead, BasicIo::cur)) return 2;
|
||||||
|
}
|
||||||
|
else if (marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) {
|
||||||
|
if (size < 8) return 2;
|
||||||
|
skipApp1Exif = count;
|
||||||
|
++search;
|
||||||
|
if (io_->seek(size-bufRead, BasicIo::cur)) return 2;
|
||||||
|
}
|
||||||
|
else if (marker == app13_ && memcmp(buf.pData_ + 2, ps3Id_, 14) == 0) {
|
||||||
|
if (size < 16) return 2;
|
||||||
|
skipApp13Ps3 = count;
|
||||||
|
++search;
|
||||||
|
// needed if bufMinSize!=16: io_->seek(16-bufRead, BasicIo::cur);
|
||||||
|
psData.alloc(size - 16);
|
||||||
|
// Load PS data now to allow reinsertion at any point
|
||||||
|
io_->read(psData.pData_, psData.size_);
|
||||||
|
if (io_->error() || io_->eof()) return 1;
|
||||||
|
}
|
||||||
|
else if (marker == com_ && skipCom == -1) {
|
||||||
|
if (size < 2) return 2;
|
||||||
|
// Jpegs can have multiple comments, but for now only handle
|
||||||
|
// the first one (most jpegs only have one anyway).
|
||||||
|
skipCom = count;
|
||||||
|
++search;
|
||||||
|
if (io_->seek(size-bufRead, BasicIo::cur)) return 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (size < 2) return 2;
|
||||||
|
if (io_->seek(size-bufRead, BasicIo::cur)) return 2;
|
||||||
|
}
|
||||||
|
marker = advanceToMarker();
|
||||||
|
if (marker < 0) return 2;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exifData_.count() > 0) ++search;
|
||||||
|
if (iptcData_.count() > 0) ++search;
|
||||||
|
if (!comment_.empty()) ++search;
|
||||||
|
|
||||||
|
io_->seek(seek, BasicIo::beg);
|
||||||
|
count = 0;
|
||||||
|
marker = advanceToMarker();
|
||||||
|
if (marker < 0) return 2;
|
||||||
|
|
||||||
|
// To simplify this a bit, new segments are inserts at either the start
|
||||||
|
// or right after app0. This is standard in most jpegs, but has the
|
||||||
|
// potential to change segment ordering (which is allowed).
|
||||||
|
// Segments are erased if there is no assigned metadata.
|
||||||
|
while (marker != sos_ && search > 0) {
|
||||||
|
// Read size and signature (ok if this hits EOF)
|
||||||
|
bufRead = io_->read(buf.pData_, bufMinSize);
|
||||||
|
if (io_->error()) return 1;
|
||||||
|
// Careful, this can be a meaningless number for empty
|
||||||
|
// images with only an eoi_ marker
|
||||||
|
uint16_t size = getUShort(buf.pData_, bigEndian);
|
||||||
|
|
||||||
|
if (insertPos == count) {
|
||||||
|
byte tmpBuf[18];
|
||||||
|
if (!comment_.empty()) {
|
||||||
|
// Write COM marker, size of comment, and string
|
||||||
|
tmpBuf[0] = 0xff;
|
||||||
|
tmpBuf[1] = com_;
|
||||||
|
us2Data(tmpBuf + 2,
|
||||||
|
static_cast<uint16_t>(comment_.length()+3), bigEndian);
|
||||||
|
if (outIo.write(tmpBuf, 4) != 4) return 4;
|
||||||
|
if (outIo.write((byte*)comment_.data(), (long)comment_.length())
|
||||||
|
!= (long)comment_.length()) return 4;
|
||||||
|
if (outIo.putb(0)==EOF) return 4;
|
||||||
|
if (outIo.error()) return 4;
|
||||||
|
--search;
|
||||||
|
}
|
||||||
|
if (exifData_.count() > 0) {
|
||||||
|
// Write APP1 marker, size of APP1 field, Exif id and Exif data
|
||||||
|
DataBuf rawExif(exifData_.copy());
|
||||||
|
tmpBuf[0] = 0xff;
|
||||||
|
tmpBuf[1] = app1_;
|
||||||
|
us2Data(tmpBuf + 2,
|
||||||
|
static_cast<uint16_t>(rawExif.size_+8),
|
||||||
|
bigEndian);
|
||||||
|
memcpy(tmpBuf + 4, exifId_, 6);
|
||||||
|
if (outIo.write(tmpBuf, 10) != 10) return 4;
|
||||||
|
if (outIo.write(rawExif.pData_, rawExif.size_)
|
||||||
|
!= rawExif.size_) return 4;
|
||||||
|
if (outIo.error()) return 4;
|
||||||
|
--search;
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte *record = psData.pData_;
|
||||||
|
uint16_t sizeIptc = 0;
|
||||||
|
uint16_t sizeHdr = 0;
|
||||||
|
// Safe to call with zero psData.size_
|
||||||
|
locateIptcData(psData.pData_, psData.size_, &record, &sizeHdr, &sizeIptc);
|
||||||
|
|
||||||
|
// Data is rounded to be even
|
||||||
|
const int sizeOldData = sizeHdr + sizeIptc + (sizeIptc & 1);
|
||||||
|
if (psData.size_ > sizeOldData || iptcData_.count() > 0) {
|
||||||
|
// rawIptc may have size of zero.
|
||||||
|
DataBuf rawIptc(iptcData_.copy());
|
||||||
|
// write app13 marker, new size, and ps3Id
|
||||||
|
tmpBuf[0] = 0xff;
|
||||||
|
tmpBuf[1] = app13_;
|
||||||
|
const int sizeNewData = rawIptc.size_ ?
|
||||||
|
rawIptc.size_ + (rawIptc.size_ & 1) + 12 : 0;
|
||||||
|
us2Data(tmpBuf + 2,
|
||||||
|
static_cast<uint16_t>(psData.size_-sizeOldData+sizeNewData+16),
|
||||||
|
bigEndian);
|
||||||
|
memcpy(tmpBuf + 4, ps3Id_, 14);
|
||||||
|
if (outIo.write(tmpBuf, 18) != 18) return 4;
|
||||||
|
if (outIo.error()) return 4;
|
||||||
|
|
||||||
|
const long sizeFront = (long)(record - psData.pData_);
|
||||||
|
const long sizeEnd = psData.size_ - sizeFront - sizeOldData;
|
||||||
|
// write data before old record.
|
||||||
|
if (outIo.write(psData.pData_, sizeFront) != sizeFront) return 4;
|
||||||
|
|
||||||
|
// write new iptc record if we have it
|
||||||
|
if (iptcData_.count() > 0) {
|
||||||
|
memcpy(tmpBuf, bimId_, 4);
|
||||||
|
us2Data(tmpBuf+4, iptc_, bigEndian);
|
||||||
|
tmpBuf[6] = 0;
|
||||||
|
tmpBuf[7] = 0;
|
||||||
|
ul2Data(tmpBuf + 8, rawIptc.size_, bigEndian);
|
||||||
|
if (outIo.write(tmpBuf, 12) != 12) return 4;
|
||||||
|
if (outIo.write(rawIptc.pData_, rawIptc.size_)
|
||||||
|
!= rawIptc.size_) return 4;
|
||||||
|
// data is padded to be even (but not included in size)
|
||||||
|
if (rawIptc.size_ & 1) {
|
||||||
|
if (outIo.putb(0)==EOF) return 4;
|
||||||
|
}
|
||||||
|
if (outIo.error()) return 4;
|
||||||
|
--search;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write existing stuff after record
|
||||||
|
if (outIo.write(record+sizeOldData, sizeEnd)
|
||||||
|
!= sizeEnd) return 4;
|
||||||
|
if (outIo.error()) return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (marker == eoi_) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (skipApp1Exif==count || skipApp13Ps3==count || skipCom==count) {
|
||||||
|
--search;
|
||||||
|
io_->seek(size-bufRead, BasicIo::cur);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (size < 2) return 2;
|
||||||
|
buf.alloc(size+2);
|
||||||
|
io_->seek(-bufRead-2, BasicIo::cur);
|
||||||
|
io_->read(buf.pData_, size+2);
|
||||||
|
if (io_->error() || io_->eof()) return 1;
|
||||||
|
if (outIo.write(buf.pData_, size+2) != size+2) return 4;
|
||||||
|
if (outIo.error()) return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next marker
|
||||||
|
marker = advanceToMarker();
|
||||||
|
if (marker < 0) return 2;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy rest of the Io
|
||||||
|
io_->seek(-2, BasicIo::cur);
|
||||||
|
buf.alloc(4096);
|
||||||
|
long readSize = 0;
|
||||||
|
while ((readSize=io_->read(buf.pData_, buf.size_))) {
|
||||||
|
if (outIo.write(buf.pData_, readSize) != readSize) return 4;
|
||||||
|
}
|
||||||
|
if (outIo.error()) return 4;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}// JpegBase::doWriteMetadata
|
||||||
|
|
||||||
|
|
||||||
|
const byte JpegImage::soi_ = 0xd8;
|
||||||
|
const byte JpegImage::blank_[] = {
|
||||||
|
0xFF,0xD8,0xFF,0xDB,0x00,0x84,0x00,0x10,0x0B,0x0B,0x0B,0x0C,0x0B,0x10,0x0C,0x0C,
|
||||||
|
0x10,0x17,0x0F,0x0D,0x0F,0x17,0x1B,0x14,0x10,0x10,0x14,0x1B,0x1F,0x17,0x17,0x17,
|
||||||
|
0x17,0x17,0x1F,0x1E,0x17,0x1A,0x1A,0x1A,0x1A,0x17,0x1E,0x1E,0x23,0x25,0x27,0x25,
|
||||||
|
0x23,0x1E,0x2F,0x2F,0x33,0x33,0x2F,0x2F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
|
||||||
|
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x01,0x11,0x0F,0x0F,0x11,0x13,0x11,0x15,0x12,
|
||||||
|
0x12,0x15,0x14,0x11,0x14,0x11,0x14,0x1A,0x14,0x16,0x16,0x14,0x1A,0x26,0x1A,0x1A,
|
||||||
|
0x1C,0x1A,0x1A,0x26,0x30,0x23,0x1E,0x1E,0x1E,0x1E,0x23,0x30,0x2B,0x2E,0x27,0x27,
|
||||||
|
0x27,0x2E,0x2B,0x35,0x35,0x30,0x30,0x35,0x35,0x40,0x40,0x3F,0x40,0x40,0x40,0x40,
|
||||||
|
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xFF,0xC0,0x00,0x11,0x08,0x00,0x01,0x00,
|
||||||
|
0x01,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11,0x01,0xFF,0xC4,0x00,0x4B,0x00,
|
||||||
|
0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x07,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xDA,0x00,0x0C,0x03,0x01,0x00,0x02,
|
||||||
|
0x11,0x03,0x11,0x00,0x3F,0x00,0xA0,0x00,0x0F,0xFF,0xD9 };
|
||||||
|
|
||||||
|
JpegImage::JpegImage(BasicIo::AutoPtr io, bool create)
|
||||||
|
: JpegBase(io, create, blank_, sizeof(blank_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//! @cond IGNORE
|
||||||
|
JpegImage::JpegRegister::JpegRegister()
|
||||||
|
{
|
||||||
|
ImageFactory::registerImage(
|
||||||
|
Image::jpeg, newJpegInstance, isJpegType);
|
||||||
|
}
|
||||||
|
//! @endcond
|
||||||
|
|
||||||
|
int JpegImage::writeHeader(BasicIo& outIo) const
|
||||||
|
{
|
||||||
|
// Jpeg header
|
||||||
|
byte tmpBuf[2];
|
||||||
|
tmpBuf[0] = 0xff;
|
||||||
|
tmpBuf[1] = soi_;
|
||||||
|
if (outIo.write(tmpBuf, 2) != 2) return 4;
|
||||||
|
if (outIo.error()) return 4;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JpegImage::isThisType(BasicIo& iIo, bool advance) const
|
||||||
|
{
|
||||||
|
return isJpegType(iIo, advance);
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::AutoPtr newJpegInstance(BasicIo::AutoPtr io, bool create)
|
||||||
|
{
|
||||||
|
Image::AutoPtr image;
|
||||||
|
if (create) {
|
||||||
|
image = Image::AutoPtr(new JpegImage(io, true));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
image = Image::AutoPtr(new JpegImage(io, false));
|
||||||
|
}
|
||||||
|
if (!image->good()) {
|
||||||
|
image.reset();
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isJpegType(BasicIo& iIo, bool advance)
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
byte tmpBuf[2];
|
||||||
|
iIo.read(tmpBuf, 2);
|
||||||
|
if (iIo.error() || iIo.eof()) return false;
|
||||||
|
|
||||||
|
if (0xff!=tmpBuf[0] || JpegImage::soi_!=tmpBuf[1]) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
if (!advance || !result ) iIo.seek(-2, BasicIo::cur);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char ExvImage::exiv2Id_[] = "Exiv2";
|
||||||
|
const byte ExvImage::blank_[] = { 0xff,0x01,'E','x','i','v','2',0xff,0xd9 };
|
||||||
|
|
||||||
|
ExvImage::ExvImage(BasicIo::AutoPtr io, bool create)
|
||||||
|
: JpegBase(io, create, blank_, sizeof(blank_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//! @cond IGNORE
|
||||||
|
ExvImage::ExvRegister::ExvRegister()
|
||||||
|
{
|
||||||
|
ImageFactory::registerImage(
|
||||||
|
Image::exv, newExvInstance, isExvType);
|
||||||
|
}
|
||||||
|
//! @endcond
|
||||||
|
|
||||||
|
int ExvImage::writeHeader(BasicIo& outIo) const
|
||||||
|
{
|
||||||
|
// Exv header
|
||||||
|
byte tmpBuf[7];
|
||||||
|
tmpBuf[0] = 0xff;
|
||||||
|
tmpBuf[1] = 0x01;
|
||||||
|
memcpy(tmpBuf + 2, exiv2Id_, 5);
|
||||||
|
if (outIo.write(tmpBuf, 7) != 7) return 4;
|
||||||
|
if (outIo.error()) return 4;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExvImage::isThisType(BasicIo& iIo, bool advance) const
|
||||||
|
{
|
||||||
|
return isExvType(iIo, advance);
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::AutoPtr newExvInstance(BasicIo::AutoPtr io, bool create)
|
||||||
|
{
|
||||||
|
Image::AutoPtr image;
|
||||||
|
if (create) {
|
||||||
|
image = Image::AutoPtr(new ExvImage(io, true));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
image = Image::AutoPtr(new ExvImage(io, false));
|
||||||
|
}
|
||||||
|
if (!image->good()) image.reset();
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isExvType(BasicIo& iIo, bool advance)
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
byte tmpBuf[7];
|
||||||
|
iIo.read(tmpBuf, 7);
|
||||||
|
if (iIo.error() || iIo.eof()) return false;
|
||||||
|
|
||||||
|
if (0xff!=tmpBuf[0] || 0x01!=tmpBuf[1] ||
|
||||||
|
memcmp(tmpBuf + 2, ExvImage::exiv2Id_, 5) != 0) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
if (!advance || !result ) iIo.seek(-7, BasicIo::cur);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Exiv2
|
@ -0,0 +1,418 @@
|
|||||||
|
// ***************************************************************** -*- C++ -*-
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2004 Andreas Huggel <ahuggel@gmx.net>
|
||||||
|
*
|
||||||
|
* This program is part of the Exiv2 distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
/*!
|
||||||
|
@file jpgimage.hpp
|
||||||
|
@brief Class JpegImage to access JPEG images
|
||||||
|
@version $Rev$
|
||||||
|
@author Andreas Huggel (ahu)
|
||||||
|
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
|
||||||
|
@author Brad Schick (brad)
|
||||||
|
<a href="mailto:brad@robotbattle.com">brad@robotbattle.com</a>
|
||||||
|
@date 15-Jan-05, brad: split out from image.cpp
|
||||||
|
*/
|
||||||
|
#ifndef JPGIMAGE_HPP_
|
||||||
|
#define JPGIMAGE_HPP_
|
||||||
|
|
||||||
|
// *****************************************************************************
|
||||||
|
// included header files
|
||||||
|
#include "types.hpp"
|
||||||
|
#include "image.hpp"
|
||||||
|
#include "basicio.hpp"
|
||||||
|
#include "exif.hpp"
|
||||||
|
#include "iptc.hpp"
|
||||||
|
|
||||||
|
// + standard includes
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// *****************************************************************************
|
||||||
|
// namespace extensions
|
||||||
|
namespace Exiv2 {
|
||||||
|
|
||||||
|
|
||||||
|
// *****************************************************************************
|
||||||
|
// class definitions
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Abstract helper base class to access JPEG images.
|
||||||
|
*/
|
||||||
|
class JpegBase : public Image {
|
||||||
|
public:
|
||||||
|
//! @name Creators
|
||||||
|
//@{
|
||||||
|
//! Virtual destructor.
|
||||||
|
virtual ~JpegBase() {}
|
||||||
|
//@}
|
||||||
|
//! @name Manipulators
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Read all metadata from the image. Before this method
|
||||||
|
is called, the various metadata types (Iptc, Exif) will be empty.
|
||||||
|
|
||||||
|
This method returns success even when no metadata is found in
|
||||||
|
the image. Callers must therefore check the size of indivdual
|
||||||
|
metadata types before accessing the data.
|
||||||
|
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
1 if reading from the file failed
|
||||||
|
(could be caused by invalid image);<BR>
|
||||||
|
2 if the file does not contain a valid image;<BR>
|
||||||
|
*/
|
||||||
|
int readMetadata();
|
||||||
|
/*!
|
||||||
|
@brief Write metadata back to the image.
|
||||||
|
|
||||||
|
All existing metadata sections in the image are either created,
|
||||||
|
replaced, or erased. If values for a given metadata type have been
|
||||||
|
assigned, a section for that metadata type will either be created or
|
||||||
|
replaced. If no values have been assigned to a given metadata type,
|
||||||
|
any exists section for that metadata type will be removed from the
|
||||||
|
image.
|
||||||
|
|
||||||
|
@return 0 if successful;<br>
|
||||||
|
1 if reading from the file failed;<BR>
|
||||||
|
2 if the file does not contain a valid image;<BR>
|
||||||
|
4 if the temporary output file can not be written to;<BR>
|
||||||
|
-1 if the newly created file could not be reopened;<BR>
|
||||||
|
-3 if the temporary output file can not be opened;<BR>
|
||||||
|
-4 if renaming the temporary file fails;<br>
|
||||||
|
*/
|
||||||
|
int writeMetadata();
|
||||||
|
/*!
|
||||||
|
@brief Assign new exif data. The new exif data is not written
|
||||||
|
to the image until the writeMetadata() method is called.
|
||||||
|
@param exifData An ExifData instance holding exif data to be copied
|
||||||
|
|
||||||
|
@throw Error ("Exif data too large") if the exif data is larger than
|
||||||
|
65535 bytes (the maximum size of JPEG APP segments)
|
||||||
|
*/
|
||||||
|
void setExifData(const ExifData& exifData);
|
||||||
|
void clearExifData();
|
||||||
|
void setIptcData(const IptcData& iptcData);
|
||||||
|
void clearIptcData();
|
||||||
|
void setComment(const std::string& comment);
|
||||||
|
void clearComment();
|
||||||
|
void setMetadata(const Image& image);
|
||||||
|
void clearMetadata();
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! @name Accessors
|
||||||
|
//@{
|
||||||
|
bool good() const;
|
||||||
|
const ExifData& exifData() const { return exifData_; }
|
||||||
|
ExifData& exifData() { return exifData_; }
|
||||||
|
const IptcData& iptcData() const { return iptcData_; }
|
||||||
|
IptcData& iptcData() { return iptcData_; }
|
||||||
|
std::string comment() const { return comment_; }
|
||||||
|
BasicIo& io() const { return *io_; }
|
||||||
|
//@}
|
||||||
|
protected:
|
||||||
|
//! @name Creators
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Constructor that can either open an existing image or create
|
||||||
|
a new image from scratch. If a new image is to be created, any
|
||||||
|
existing data is overwritten.
|
||||||
|
@param io An auto-pointer that owns a BasicIo instance used for
|
||||||
|
reading and writing image metadata. \b Important: The constructor
|
||||||
|
takes ownership of the passed in BasicIo instance through the
|
||||||
|
auto-pointer. Callers should not continue to use the BasicIo
|
||||||
|
instance after it is passed to this method. Use the Image::io()
|
||||||
|
method to get a temporary reference.
|
||||||
|
@param create Specifies if an existing image should be read (false)
|
||||||
|
or if a new image should be created (true).
|
||||||
|
@param initData Data to initialize newly created images. Only used
|
||||||
|
when \em create is true. Should contain data for the smallest
|
||||||
|
valid image of the calling subclass.
|
||||||
|
@param dataSize Size of initData in bytes.
|
||||||
|
*/
|
||||||
|
JpegBase(BasicIo::AutoPtr io, bool create,
|
||||||
|
const byte initData[], long dataSize);
|
||||||
|
//@}
|
||||||
|
//! @name Manipulators
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Writes the image header (aka signature) to the BasicIo instance.
|
||||||
|
@param oIo BasicIo instance that the header is written to.
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
4 if the output file can not be written to;<BR>
|
||||||
|
*/
|
||||||
|
virtual int writeHeader(BasicIo& oIo) const =0;
|
||||||
|
//@}
|
||||||
|
//! @name Accessors
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Determine if the content of the BasicIo instance is of the
|
||||||
|
type supported by this class.
|
||||||
|
|
||||||
|
The advance flag determines if the read position in the stream is
|
||||||
|
moved (see below). This applies only if the type matches and the
|
||||||
|
function returns true. If the type does not match, the stream
|
||||||
|
position is not changed. However, if reading from the stream fails,
|
||||||
|
the stream position is undefined. Consult the stream state to obtain
|
||||||
|
more information in this case.
|
||||||
|
|
||||||
|
@param iIo BasicIo instance to read from.
|
||||||
|
@param advance Flag indicating whether the position of the io
|
||||||
|
should be advanced by the number of characters read to
|
||||||
|
analyse the data (true) or left at its original
|
||||||
|
position (false). This applies only if the type matches.
|
||||||
|
@return true if the data matches the type of this class;<BR>
|
||||||
|
false if the data does not match;<BR>
|
||||||
|
*/
|
||||||
|
virtual bool isThisType(BasicIo& iIo, bool advance) const =0;
|
||||||
|
//@}
|
||||||
|
|
||||||
|
// Constant Data
|
||||||
|
static const byte sos_; //!< JPEG SOS marker
|
||||||
|
static const byte eoi_; //!< JPEG EOI marker
|
||||||
|
static const byte app0_; //!< JPEG APP0 marker
|
||||||
|
static const byte app1_; //!< JPEG APP1 marker
|
||||||
|
static const byte app13_; //!< JPEG APP13 marker
|
||||||
|
static const byte com_; //!< JPEG Comment marker
|
||||||
|
static const char exifId_[]; //!< Exif identifier
|
||||||
|
static const char jfifId_[]; //!< JFIF identifier
|
||||||
|
static const char ps3Id_[]; //!< Photoshop marker
|
||||||
|
static const char bimId_[]; //!< Photoshop marker
|
||||||
|
static const uint16_t iptc_; //!< Photoshop Iptc marker
|
||||||
|
|
||||||
|
private:
|
||||||
|
// DATA
|
||||||
|
BasicIo::AutoPtr io_; //!< Image data io pointer
|
||||||
|
ExifData exifData_; //!< Exif data container
|
||||||
|
IptcData iptcData_; //!< Iptc data container
|
||||||
|
std::string comment_; //!< JPEG comment
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
/*!
|
||||||
|
@brief Advances associated io instance to one byte past the next
|
||||||
|
Jpeg marker and returns the marker. This method should be called
|
||||||
|
when the BasicIo instance is positioned one byte past the end of a
|
||||||
|
Jpeg segment.
|
||||||
|
@return the next Jpeg segment marker if successful;<BR>
|
||||||
|
-1 if a maker was not found before EOF;<BR>
|
||||||
|
*/
|
||||||
|
int advanceToMarker() const;
|
||||||
|
/*!
|
||||||
|
@brief Locates Photoshop formated Iptc data in a memory buffer.
|
||||||
|
Operates on raw data to simplify reuse.
|
||||||
|
@param pPsData Pointer to buffer containing entire payload of
|
||||||
|
Photoshop formated APP13 Jpeg segment.
|
||||||
|
@param sizePsData Size in bytes of pPsData.
|
||||||
|
@param record Output value that is set to the start of the Iptc
|
||||||
|
data block within pPsData (may not be null).
|
||||||
|
@param sizeHdr Output value that is set to the size of the header
|
||||||
|
within the Iptc data block pointed to by record (may not
|
||||||
|
be null).
|
||||||
|
@param sizeIptc Output value that is set to the size of the actual
|
||||||
|
Iptc data within the Iptc data block pointed to by record
|
||||||
|
(may not be null).
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
3 if no Iptc data was found in pPsData;<BR>
|
||||||
|
-2 if the pPsData buffer does not contain valid data;<BR>
|
||||||
|
*/
|
||||||
|
int locateIptcData(const byte *pPsData,
|
||||||
|
long sizePsData,
|
||||||
|
const byte **record,
|
||||||
|
uint16_t *const sizeHdr,
|
||||||
|
uint16_t *const sizeIptc) const;
|
||||||
|
/*!
|
||||||
|
@brief Initialize the image with the provided data.
|
||||||
|
@param initData Data to be written to the associated BasicIo
|
||||||
|
@param dataSize Size in bytes of data to be written
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
4 if the output file can not be written to;<BR>
|
||||||
|
*/
|
||||||
|
int initImage(const byte initData[], long dataSize);
|
||||||
|
/*!
|
||||||
|
@brief Provides the main implementation of writeMetadata() by
|
||||||
|
writing all buffered metadata to the provided BasicIo.
|
||||||
|
@param oIo BasicIo instance to write to (a temporary location).
|
||||||
|
@return 0 if successful;<br>
|
||||||
|
1 if reading from input file failed;<BR>
|
||||||
|
2 if the input file does not contain a valid image;<BR>
|
||||||
|
4 if the output file can not be written to;<BR>
|
||||||
|
*/
|
||||||
|
int doWriteMetadata(BasicIo& oIo);
|
||||||
|
|
||||||
|
// NOT Implemented
|
||||||
|
//! Default constructor.
|
||||||
|
JpegBase();
|
||||||
|
//! Copy constructor
|
||||||
|
JpegBase(const JpegBase& rhs);
|
||||||
|
//! Assignment operator
|
||||||
|
JpegBase& operator=(const JpegBase& rhs);
|
||||||
|
}; // class JpegBase
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Class to access JPEG images
|
||||||
|
*/
|
||||||
|
class JpegImage : public JpegBase {
|
||||||
|
friend bool isJpegType(BasicIo& iIo, bool advance);
|
||||||
|
public:
|
||||||
|
//! @name Creators
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Constructor that can either open an existing Jpeg image or create
|
||||||
|
a new image from scratch. If a new image is to be created, any
|
||||||
|
existing data is overwritten. Since the constructor can not return
|
||||||
|
a result, callers should check the good() method after object
|
||||||
|
construction to determine success or failure.
|
||||||
|
@param io An auto-pointer that owns a BasicIo instance used for
|
||||||
|
reading and writing image metadata. \b Important: The constructor
|
||||||
|
takes ownership of the passed in BasicIo instance through the
|
||||||
|
auto-pointer. Callers should not continue to use the BasicIo
|
||||||
|
instance after it is passed to this method. Use the Image::io()
|
||||||
|
method to get a temporary reference.
|
||||||
|
@param create Specifies if an existing image should be read (false)
|
||||||
|
or if a new file should be created (true).
|
||||||
|
*/
|
||||||
|
JpegImage(BasicIo::AutoPtr io, bool create);
|
||||||
|
//! Destructor
|
||||||
|
~JpegImage() {}
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! @cond IGNORE
|
||||||
|
// Public only so that we can create a static instance
|
||||||
|
struct JpegRegister{
|
||||||
|
JpegRegister();
|
||||||
|
};
|
||||||
|
//! @endcond
|
||||||
|
protected:
|
||||||
|
//! @name Accessors
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Determine if the content of the BasicIo instance is a Jpeg image.
|
||||||
|
See base class for more details.
|
||||||
|
@param iIo BasicIo instance to read from.
|
||||||
|
@param advance Flag indicating whether the position of the io
|
||||||
|
should be advanced by the number of characters read to
|
||||||
|
analyse the data (true) or left at its original
|
||||||
|
position (false). This applies only if the type matches.
|
||||||
|
@return true if the data matches a Jpeg image;<BR>
|
||||||
|
false if the data does not match;<BR>
|
||||||
|
*/
|
||||||
|
bool isThisType(BasicIo& iIo, bool advance) const;
|
||||||
|
//@}
|
||||||
|
//! @name Manipulators
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Writes a Jpeg header (aka signature) to the BasicIo instance.
|
||||||
|
@param oIo BasicIo instance that the header is written to.
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
2 if the input image is invalid or can not be read;<BR>
|
||||||
|
4 if the temporary image can not be written to;<BR>
|
||||||
|
-3 other temporary errors;<BR>
|
||||||
|
*/
|
||||||
|
int writeHeader(BasicIo& oIo) const;
|
||||||
|
//@}
|
||||||
|
private:
|
||||||
|
// Constant data
|
||||||
|
static const byte soi_; // SOI marker
|
||||||
|
static const byte blank_[]; // Minimal Jpeg image
|
||||||
|
|
||||||
|
// NOT Implemented
|
||||||
|
//! Default constructor
|
||||||
|
JpegImage();
|
||||||
|
//! Copy constructor
|
||||||
|
JpegImage(const JpegImage& rhs);
|
||||||
|
//! Assignment operator
|
||||||
|
JpegImage& operator=(const JpegImage& rhs);
|
||||||
|
}; // class JpegImage
|
||||||
|
|
||||||
|
static JpegImage::JpegRegister jpegReg;
|
||||||
|
|
||||||
|
//! Helper class to access %Exiv2 files
|
||||||
|
class ExvImage : public JpegBase {
|
||||||
|
friend bool isExvType(BasicIo& iIo, bool advance);
|
||||||
|
public:
|
||||||
|
//! @name Creators
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Constructor that can either open an existing Exv image or create
|
||||||
|
a new image from scratch. If a new image is to be created, any
|
||||||
|
existing data is overwritten. Since the constructor can not return
|
||||||
|
a result, callers should check the good() method after object
|
||||||
|
construction to determine success or failure.
|
||||||
|
@param io An auto-pointer that owns a BasicIo instance used for
|
||||||
|
reading and writing image metadata. \b Important: The constructor
|
||||||
|
takes ownership of the passed in BasicIo instance through the
|
||||||
|
auto-pointer. Callers should not continue to use the BasicIo
|
||||||
|
instance after it is passed to this method. Use the Image::io()
|
||||||
|
method to get a temporary reference.
|
||||||
|
@param create Specifies if an existing image should be read (false)
|
||||||
|
or if a new file should be created (true).
|
||||||
|
*/
|
||||||
|
ExvImage(BasicIo::AutoPtr io, bool create);
|
||||||
|
//! Destructor
|
||||||
|
~ExvImage() {}
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//! @cond IGNORE
|
||||||
|
// Public only so that we can create a static instance
|
||||||
|
struct ExvRegister{
|
||||||
|
ExvRegister();
|
||||||
|
};
|
||||||
|
//! @endcond
|
||||||
|
protected:
|
||||||
|
//! @name Accessors
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Determine if the content of the BasicIo instance is an Exv
|
||||||
|
image. See base class for more details.
|
||||||
|
@param iIo BasicIo instance to read from.
|
||||||
|
@param advance Flag indicating whether the position of the io
|
||||||
|
should be advanced by the number of characters read to
|
||||||
|
analyse the data (true) or left at its original
|
||||||
|
position (false). This applies only if the type matches.
|
||||||
|
@return true if the data matches a Jpeg image;<BR>
|
||||||
|
false if the data does not match;<BR>
|
||||||
|
*/
|
||||||
|
virtual bool isThisType(BasicIo& iIo, bool advance) const;
|
||||||
|
//@}
|
||||||
|
//! @name Manipulators
|
||||||
|
//@{
|
||||||
|
/*!
|
||||||
|
@brief Writes an Exv header (aka signature) to the BasicIo instance.
|
||||||
|
@param oIo BasicIo instance that the header is written to.
|
||||||
|
@return 0 if successful;<BR>
|
||||||
|
4 if the output file can not be written to;<BR>
|
||||||
|
*/
|
||||||
|
int writeHeader(BasicIo& oIo) const;
|
||||||
|
//@}
|
||||||
|
private:
|
||||||
|
// Constant data
|
||||||
|
static const char exiv2Id_[]; // Exv identifier
|
||||||
|
static const byte blank_[]; // Minimal exiv file
|
||||||
|
|
||||||
|
// NOT Implemented
|
||||||
|
//! Default constructor
|
||||||
|
ExvImage();
|
||||||
|
//! Copy constructor
|
||||||
|
ExvImage(const ExvImage& rhs);
|
||||||
|
//! Assignment operator
|
||||||
|
ExvImage& operator=(const ExvImage& rhs);
|
||||||
|
}; // class ExvImage
|
||||||
|
|
||||||
|
static ExvImage::ExvRegister exvReg;
|
||||||
|
} // namespace Exiv2
|
||||||
|
|
||||||
|
|
||||||
|
#endif // #ifndef JPGIMAGE_HPP_
|
@ -1 +0,0 @@
|
|||||||
Caught Exiv2 exception 'temp: No Iptc data found in the 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
|
Loading…
Reference in New Issue