#include "gdcmFileAnonymizer.h"
#include "gdcmReader.h"
#include <fstream>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#ifdef _MSC_VER
#pragma warning (disable : 4068 )
#endif
#if defined(GCC_VERSION) && GCC_VERSION < 50101
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
namespace gdcm
{
enum Action
{
EMPTY,
REMOVE,
REPLACE
};
struct PositionEmpty
{
std::streampos BeginPos;
std::streampos EndPos;
Action action;
bool IsTagFound; DataElement DE;
bool operator() (const PositionEmpty & i, const PositionEmpty & j)
{
if( i.BeginPos == j.BeginPos )
{
return i.DE.GetTag() < j.DE.GetTag();
}
return (int)i.BeginPos < (int)j.BeginPos;
}
};
class FileAnonymizerInternals
{
public:
std::string InputFilename;
std::string OutputFilename;
std::set<Tag> EmptyTags;
std::set<Tag> RemoveTags;
std::map<Tag, std::string> ReplaceTags;
TransferSyntax TS;
std::vector<PositionEmpty> PositionEmptyArray;
};
FileAnonymizer::FileAnonymizer()
{
Internals = new FileAnonymizerInternals;
}
FileAnonymizer::~FileAnonymizer()
{
delete Internals;
}
void FileAnonymizer::Empty( Tag const &t )
{
if( t.GetGroup() >= 0x0008 )
{
Internals->EmptyTags.insert( t );
}
}
void FileAnonymizer::Remove( Tag const &t )
{
if( t.GetGroup() >= 0x0008 )
{
Internals->RemoveTags.insert( t );
}
}
void FileAnonymizer::Replace( Tag const &t, const char *value )
{
if( value && t.GetGroup() >= 0x0008 )
{
Internals->ReplaceTags.insert(
std::make_pair( t, value) );
}
}
void FileAnonymizer::Replace( Tag const &t, const char *value, VL const & vl )
{
if( value && t.GetGroup() >= 0x0008 )
{
Internals->ReplaceTags.insert(
std::make_pair( t, std::string(value, vl) ) );
}
}
void FileAnonymizer::SetInputFileName(const char *filename_native)
{
if( filename_native )
Internals->InputFilename = filename_native;
}
void FileAnonymizer::SetOutputFileName(const char *filename_native)
{
if( filename_native )
Internals->OutputFilename = filename_native;
}
static inline bool file_exist(const char *filename)
{
std::ifstream infile(filename, std::ios::binary);
return infile.good();
}
bool FileAnonymizer::ComputeReplaceTagPosition()
{
gdcm_assert( !Internals->InputFilename.empty() );
const char *filename = Internals->InputFilename.c_str();
gdcm_assert( filename );
const bool inplace = file_exist(Internals->OutputFilename.c_str());
std::map<Tag, std::string>::reverse_iterator rit = Internals->ReplaceTags.rbegin();
for ( ; rit != Internals->ReplaceTags.rend(); rit++ )
{
PositionEmpty pe;
std::set<Tag> removeme;
const Tag & t = rit->first;
const std::string & valuereplace = rit->second;
removeme.insert( t );
std::ifstream is( filename, std::ios::binary );
Reader reader;
reader.SetStream( is );
if( !reader.ReadSelectedTags( removeme ) )
{
return false;
}
pe.EndPos = pe.BeginPos = is.tellg();
pe.action = REPLACE;
pe.IsTagFound = false;
const File & f = reader.GetFile();
const DataSet &ds = f.GetDataSet();
const TransferSyntax &ts = f.GetHeader().GetDataSetTransferSyntax();
Internals->TS = ts;
pe.DE.SetTag( t );
if( ds.FindDataElement( t ) )
{
const DataElement &de = ds.GetDataElement( t );
pe.IsTagFound = true;
pe.DE.SetVL( de.GetVL() ); pe.DE.SetVR( de.GetVR() );
gdcm_assert( pe.DE.GetVL().IsUndefined() == de.GetVL().IsUndefined() );
gdcm_assert( pe.DE.GetVR() == de.GetVR() );
gdcm_assert( pe.DE.GetTag() == de.GetTag() );
if( de.GetVL().IsUndefined() )
{
gdcmErrorMacro( "Replacing a SQ is not supported. Use Remove() or Empty()" );
return false;
}
else
{
if( inplace && valuereplace.size() != de.GetVL() )
{
gdcmErrorMacro( "inplace mode requires same length attribute" ); return false;
}
gdcm_assert( !de.GetVL().IsUndefined() );
pe.BeginPos -= de.GetVL();
pe.BeginPos -= 2 * de.GetVR().GetLength(); pe.BeginPos -= 4; gdcm_assert( (int)pe.EndPos ==
(int)pe.BeginPos + (int)de.GetVL() + 2 * de.GetVR().GetLength() + 4 );
}
pe.DE.SetByteValue( valuereplace.c_str(), (uint32_t)valuereplace.size() );
gdcm_assert( pe.DE.GetVL() == valuereplace.size() );
}
else
{
if( inplace )
{
gdcmErrorMacro( "inplace mode requires existing tag (cannot insert). Tag: " << t );
return false;
}
pe.DE.SetVR( VR::UN );
pe.DE.SetByteValue( valuereplace.c_str(), (uint32_t)valuereplace.size() );
gdcm_assert( pe.DE.GetVL() == valuereplace.size() );
}
Internals->PositionEmptyArray.push_back( pe );
is.close();
}
return true;
}
bool FileAnonymizer::ComputeRemoveTagPosition()
{
gdcm_assert( !Internals->InputFilename.empty() );
const char *filename = Internals->InputFilename.c_str();
gdcm_assert( filename );
const bool inplace = file_exist(Internals->OutputFilename.c_str());
if( inplace && !Internals->RemoveTags.empty())
{
gdcmErrorMacro( "inplace mode requires existing tag (cannot remove)" );
return false;
}
std::set<Tag>::reverse_iterator rit = Internals->RemoveTags.rbegin();
for ( ; rit != Internals->RemoveTags.rend(); rit++ )
{
PositionEmpty pe;
std::set<Tag> removeme;
const Tag & t = *rit;
removeme.insert( t );
std::ifstream is( filename, std::ios::binary );
Reader reader;
reader.SetStream( is );
if( !reader.ReadSelectedTags( removeme ) )
{
return false;
}
pe.EndPos = pe.BeginPos = is.tellg();
pe.action = REMOVE;
pe.IsTagFound = false;
const File & f = reader.GetFile();
const DataSet &ds = f.GetDataSet();
const TransferSyntax &ts = f.GetHeader().GetDataSetTransferSyntax();
Internals->TS = ts;
pe.DE.SetTag( t );
if( ds.FindDataElement( t ) )
{
const DataElement &de = ds.GetDataElement( t );
pe.IsTagFound = true;
pe.DE.SetVL( de.GetVL() ); pe.DE.SetVR( de.GetVR() );
gdcm_assert( pe.DE.GetVL().IsUndefined() == de.GetVL().IsUndefined() );
gdcm_assert( pe.DE.GetVR() == de.GetVR() );
gdcm_assert( pe.DE.GetTag() == de.GetTag() );
if( de.GetVL().IsUndefined() )
{
VL vl;
if( ts.GetNegociatedType() == TransferSyntax::Implicit )
{
vl = de.GetLength<ImplicitDataElement>();
}
else
{
vl = de.GetLength<ExplicitDataElement>();
}
gdcm_assert( pe.BeginPos > vl );
pe.BeginPos -= vl;
}
else
{
gdcm_assert( !de.GetVL().IsUndefined() );
pe.BeginPos -= de.GetVL();
pe.BeginPos -= 2 * de.GetVR().GetLength(); pe.BeginPos -= 4; gdcm_assert( (int)pe.EndPos ==
(int)pe.BeginPos + (int)de.GetVL() + 2 * de.GetVR().GetLength() + 4 );
}
Internals->PositionEmptyArray.push_back( pe );
}
else
{
}
is.close();
}
return true;
}
bool FileAnonymizer::ComputeEmptyTagPosition()
{
gdcm_assert( !Internals->InputFilename.empty() );
const char *filename = Internals->InputFilename.c_str();
gdcm_assert( filename );
const bool inplace = file_exist(Internals->OutputFilename.c_str());
if( inplace && !Internals->EmptyTags.empty())
{
gdcmErrorMacro( "inplace mode requires existing tag (cannot empty)" );
return false;
}
std::set<Tag>::reverse_iterator rit = Internals->EmptyTags.rbegin();
for ( ; rit != Internals->EmptyTags.rend(); rit++ )
{
PositionEmpty pe;
std::set<Tag> removeme;
const Tag & t = *rit;
removeme.insert( t );
std::ifstream is( filename, std::ios::binary );
Reader reader;
reader.SetStream( is );
if( !reader.ReadSelectedTags( removeme ) )
{
return false;
}
pe.EndPos = pe.BeginPos = is.tellg();
pe.action = EMPTY;
pe.IsTagFound = false;
const File & f = reader.GetFile();
const DataSet &ds = f.GetDataSet();
const TransferSyntax &ts = f.GetHeader().GetDataSetTransferSyntax();
Internals->TS = ts;
pe.DE.SetTag( t );
if( ds.FindDataElement( t ) )
{
const DataElement &de = ds.GetDataElement( t );
pe.IsTagFound = true;
pe.DE.SetVL( de.GetVL() ); pe.DE.SetVR( de.GetVR() );
gdcm_assert( pe.DE.GetVL().IsUndefined() == de.GetVL().IsUndefined() );
gdcm_assert( pe.DE.GetVR() == de.GetVR() );
gdcm_assert( pe.DE.GetTag() == de.GetTag() );
if( de.GetVL().IsUndefined() )
{
VL vl;
if( ts.GetNegociatedType() == TransferSyntax::Implicit )
{
vl = de.GetLength<ImplicitDataElement>();
}
else
{
vl = de.GetLength<ExplicitDataElement>();
}
gdcm_assert( pe.BeginPos > vl );
pe.BeginPos -= vl;
pe.BeginPos += 4; if( ts.GetNegociatedType() == TransferSyntax::Implicit )
{
pe.BeginPos += 0;
}
else
{
pe.BeginPos += de.GetVR().GetLength();
}
}
else
{
gdcm_assert( !de.GetVL().IsUndefined() );
pe.BeginPos -= de.GetVL();
if( ts.GetNegociatedType() == TransferSyntax::Implicit )
{
pe.BeginPos -= 4;
}
else
{
pe.BeginPos -= de.GetVR().GetLength();
gdcm_assert( (int)pe.EndPos ==
(int)pe.BeginPos + (int)de.GetVL() + de.GetVR().GetLength() );
}
}
}
else
{
pe.DE.SetVR( VR::UN );
pe.DE.SetVL( 0 );
}
Internals->PositionEmptyArray.push_back( pe );
is.close();
}
return true;
}
bool FileAnonymizer::Write()
{
if( Internals->OutputFilename.empty() ) return false;
const char *outfilename = Internals->OutputFilename.c_str();
if( Internals->InputFilename.empty() ) return false;
const char *filename = Internals->InputFilename.c_str();
Internals->PositionEmptyArray.clear();
if( !ComputeRemoveTagPosition()
|| !ComputeEmptyTagPosition()
|| !ComputeReplaceTagPosition() )
{
return false;
}
PositionEmpty pe_sort = {};
std::sort (Internals->PositionEmptyArray.begin(),
Internals->PositionEmptyArray.end(), pe_sort);
std::ios::openmode om;
const bool inplace = file_exist(outfilename);
if( inplace )
{
om = std::ofstream::in | std::ofstream::out | std::ios::binary;
}
else
{
om = std::ofstream::out | std::ios::binary;
}
std::fstream of( outfilename, om );
std::ifstream is( filename, std::ios::binary );
std::streampos prev = 0;
const TransferSyntax &ts = Internals->TS;
std::vector<PositionEmpty>::const_iterator it =
Internals->PositionEmptyArray.begin();
for( ; it != Internals->PositionEmptyArray.end(); ++it )
{
const PositionEmpty & pe = *it;
Action action = pe.action;
if( pe.IsTagFound )
{
const DataElement & de = pe.DE;
int vrlen = de.GetVR().GetLength();
if( ts.GetNegociatedType() == TransferSyntax::Implicit )
{
vrlen = 4;
}
std::streampos end = pe.BeginPos;
for( int i = (int)prev; i < end; ++i)
{
of.put( (char)is.get() );
}
if( action == EMPTY )
{
gdcm_assert( !inplace );
for( int i = 0; i < vrlen; ++i)
{
of.put( 0 );
}
}
else if( action == REPLACE )
{
if( ts.GetSwapCode() == SwapCode::BigEndian )
{
if( ts.GetNegociatedType() == TransferSyntax::Implicit )
{
gdcmErrorMacro( "Cannot write Virtual Big Endian" );
return true;
}
else
{
pe.DE.Write<ExplicitDataElement,SwapperDoOp>( of );
}
}
else
{
if( ts.GetNegociatedType() == TransferSyntax::Implicit )
{
pe.DE.Write<ImplicitDataElement,SwapperNoOp>( of );
}
else
{
pe.DE.Write<ExplicitDataElement,SwapperNoOp>( of );
}
}
}
gdcm_assert( is.good() );
is.seekg( pe.EndPos );
gdcm_assert( is.good() );
prev = is.tellg();
gdcm_assert( prev == pe.EndPos );
}
else
{
std::streampos end = pe.BeginPos;
for( int i = (int)prev; i < end; ++i)
{
of.put( (char)is.get() );
}
if( ts.GetSwapCode() == SwapCode::BigEndian )
{
if( ts.GetNegociatedType() == TransferSyntax::Implicit )
{
gdcmErrorMacro( "Cannot write Virtual Big Endian" );
return true;
}
else
{
pe.DE.Write<ExplicitDataElement,SwapperDoOp>( of );
}
}
else
{
if( ts.GetNegociatedType() == TransferSyntax::Implicit )
{
pe.DE.Write<ImplicitDataElement,SwapperNoOp>( of );
}
else
{
pe.DE.Write<ExplicitDataElement,SwapperNoOp>( of );
}
}
prev = is.tellg();
}
}
of << is.rdbuf();
of.close();
is.close();
#if 0#endif
return true;
}
}