#include "gdcmFileMetaInformation.h"
#include "gdcmAttribute.h"
#include "gdcmVR.h"
#include "gdcmExplicitDataElement.h"
#include "gdcmImplicitDataElement.h"
#include "gdcmByteValue.h"
#include "gdcmSwapper.h"
#include "gdcmException.h"
#include "gdcmTagToType.h"
#include "gdcmTag.h"
namespace gdcm_ns
{
const char FileMetaInformation::GDCM_FILE_META_INFORMATION_VERSION[] = "\0\1";
const char FileMetaInformation::GDCM_IMPLEMENTATION_CLASS_UID[] = "1.2.826.0.1.3680043.2.1143.107.104.103.115." GDCM_VERSION;
const char FileMetaInformation::GDCM_IMPLEMENTATION_VERSION_NAME[] = "GDCM " GDCM_VERSION;
const char FileMetaInformation::GDCM_SOURCE_APPLICATION_ENTITY_TITLE[] = "GDCM";
std::string FileMetaInformation::ImplementationClassUID = GetGDCMImplementationClassUID();
std::string FileMetaInformation::ImplementationVersionName = GetGDCMImplementationVersionName();
std::string FileMetaInformation::SourceApplicationEntityTitle = GetGDCMSourceApplicationEntityTitle();
const char * FileMetaInformation::GetFileMetaInformationVersion()
{
return GDCM_FILE_META_INFORMATION_VERSION;
}
const char * FileMetaInformation::GetGDCMImplementationClassUID()
{
return GDCM_IMPLEMENTATION_CLASS_UID;
}
const char * FileMetaInformation::GetGDCMImplementationVersionName()
{
return GDCM_IMPLEMENTATION_VERSION_NAME;
}
const char * FileMetaInformation::GetGDCMSourceApplicationEntityTitle()
{
return GDCM_SOURCE_APPLICATION_ENTITY_TITLE;
}
FileMetaInformation::FileMetaInformation():DataSetTS(TransferSyntax::TS_END),MetaInformationTS(TransferSyntax::Unknown),DataSetMS(MediaStorage::MS_END) {}
FileMetaInformation::~FileMetaInformation() = default;
void FileMetaInformation::SetImplementationClassUID(const char * imp)
{
if( imp )
{
ImplementationClassUID = imp;
}
}
void FileMetaInformation::AppendImplementationClassUID(const char * imp)
{
if( imp )
{
std::string tmp = GetGDCMImplementationClassUID();
tmp += ".";
tmp += imp;
if( tmp.size() <= 64 ) { ImplementationClassUID = tmp; }
}
}
void FileMetaInformation::SetImplementationVersionName(const char * version)
{
if( version )
{
gdcmAssertAlwaysMacro( strlen(version) <= 16 );
ImplementationVersionName = version;
}
}
void FileMetaInformation::SetSourceApplicationEntityTitle(const char * title)
{
if( title )
{
AEComp ae( title );
SourceApplicationEntityTitle = ae.Truncate();
}
}
const char *FileMetaInformation::GetImplementationClassUID()
{
return ImplementationClassUID.c_str();
}
const char *FileMetaInformation::GetImplementationVersionName()
{
return ImplementationVersionName.c_str();
}
const char *FileMetaInformation::GetSourceApplicationEntityTitle()
{
return SourceApplicationEntityTitle.c_str();
}
void FileMetaInformation::FillFromDataSet(DataSet const &ds)
{
DataElement xde;
if( !FindDataElement( Tag(0x0002, 0x0001) ) )
{
xde.SetTag( Tag(0x0002, 0x0001) );
xde.SetVR( VR::OB );
const char *version = FileMetaInformation::GetFileMetaInformationVersion();
xde.SetByteValue( version, 2 );
Insert( xde );
}
else
{
const DataElement &de = GetDataElement( Tag(0x0002,0x0001) );
const ByteValue *bv = de.GetByteValue();
if( bv->GetLength() != 2
|| memcmp( bv->GetPointer(), FileMetaInformation::GetFileMetaInformationVersion(), 2 ) != 0 )
{
xde.SetTag( Tag(0x0002, 0x0001) );
xde.SetVR( VR::OB );
const char *version = FileMetaInformation::GetFileMetaInformationVersion();
xde.SetByteValue( version, 2 );
Replace( xde );
}
}
if( !FindDataElement( Tag(0x0002, 0x0002) ) || GetDataElement( Tag(0x0002,0x0002) ).IsEmpty() )
{
if( !ds.FindDataElement( Tag(0x0008, 0x0016) ) || ds.GetDataElement( Tag(0x0008,0x0016) ).IsEmpty() )
{
MediaStorage ms;
ms.SetFromModality(ds);
const char *msstr = ms.GetString();
if( msstr )
{
VL::Type strlenMsstr = (VL::Type) strlen(msstr);
xde.SetByteValue( msstr, strlenMsstr );
xde.SetTag( Tag(0x0002, 0x0002) );
{
xde.SetVR( VR::UI );
}
Insert( xde );
}
else
{
gdcmErrorMacro( "Could not find MediaStorage" );
}
}
else
{
const DataElement& msclass = ds.GetDataElement( Tag(0x0008, 0x0016) );
xde = msclass;
xde.SetTag( Tag(0x0002, 0x0002) );
if( msclass.GetVR() == VR::UN || msclass.GetVR() == VR::INVALID )
{
xde.SetVR( VR::UI );
}
Insert( xde );
}
}
else {
bool dicomdir = ds.FindDataElement( Tag(0x0004, 0x1220) ); (void)dicomdir;
{
if( !ds.FindDataElement( Tag(0x0008, 0x0016) ) )
{
gdcmWarningMacro( "Missing SOPClassUID in DataSet but found in FileMeta" );
}
else
{
const DataElement& sopclass = ds.GetDataElement( Tag(0x0008, 0x0016) );
DataElement mssopclass = GetDataElement( Tag(0x0002, 0x0002) );
gdcm_assert( !mssopclass.IsEmpty() );
const ByteValue *bv = sopclass.GetByteValue();
if( bv )
{
mssopclass.SetByteValue( bv->GetPointer(), bv->GetLength() );
}
else
{
throw gdcm::Exception( "SOP Class is empty sorry" );
}
Replace( mssopclass );
}
}
}
const DataElement &dummy = GetDataElement(Tag(0x0002,0x0003)); (void)dummy;
if( !FindDataElement( Tag(0x0002, 0x0003) ) || GetDataElement( Tag(0x0002,0x0003) ).IsEmpty() )
{
if( ds.FindDataElement( Tag(0x0008, 0x0018) ) )
{
const DataElement& msinst = ds.GetDataElement( Tag(0x0008, 0x0018) );
if( msinst.IsEmpty() )
{
xde = msinst;
}
else
{
xde = msinst;
}
xde.SetTag( Tag(0x0002, 0x0003) );
if( msinst.GetVR() == VR::UN || msinst.GetVR() == VR::INVALID )
{
xde.SetVR( VR::UI );
}
Replace( xde );
}
else
{
throw gdcm::Exception( "No 2,3 and 8,18 element sorry" );
}
}
else {
bool dirrecsq = ds.FindDataElement( Tag(0x0004, 0x1220) ); MediaStorage ms;
ms.SetFromHeader( *this );
bool dicomdir = (ms == MediaStorage::MediaStorageDirectoryStorage && dirrecsq);
if( !dicomdir )
{
if( !ds.FindDataElement( Tag(0x0008, 0x0018) ) || ds.GetDataElement( Tag(0x0008, 0x0018) ).IsEmpty() )
{
throw gdcm::Exception( "No 8,18 element sorry" );
}
const DataElement& sopinst = ds.GetDataElement( Tag(0x0008, 0x0018) );
gdcm_assert( !GetDataElement( Tag(0x0002, 0x0003) ).IsEmpty() );
DataElement mssopinst = GetDataElement( Tag(0x0002, 0x0003) );
const ByteValue *bv = sopinst.GetByteValue();
gdcm_assert( bv );
mssopinst.SetByteValue( bv->GetPointer(), bv->GetLength() );
Replace( mssopinst );
}
}
if( FindDataElement( Tag(0x0002, 0x0010) ) && !GetDataElement( Tag(0x0002,0x0010) ).IsEmpty() )
{
DataElement tsuid = GetDataElement( Tag(0x0002, 0x0010) );
const char * datasetts = DataSetTS.GetString();
const ByteValue * bv = tsuid.GetByteValue();
gdcm_assert( bv );
std::string currentts( bv->GetPointer(), bv->GetPointer() + bv->GetLength() );
if( strlen(currentts.c_str()) != strlen(datasetts)
|| strcmp( currentts.c_str(), datasetts ) != 0 )
{
xde = tsuid;
VL::Type strlenDatasetts = (VL::Type) strlen(datasetts);
xde.SetByteValue( datasetts, strlenDatasetts );
Replace( xde );
}
if( tsuid.GetVR() != VR::UI )
{
xde = tsuid;
xde.SetVR( VR::UI );
Replace( xde );
}
}
else
{
if( DataSetTS == TransferSyntax::TS_END )
{
throw gdcm::Exception( "No TransferSyntax specified." );
}
const char* str = TransferSyntax::GetTSString(DataSetTS);
VL::Type strlenStr = (VL::Type) strlen(str);
xde.SetByteValue( str, strlenStr );
xde.SetVR( VR::UI );
xde.SetTag( Tag(0x0002,0x0010) );
Insert( xde );
}
if( !FindDataElement( Tag(0x0002, 0x0012) ) )
{
xde.SetTag( Tag(0x0002, 0x0012) );
xde.SetVR( VR::UI );
const char *implementation = FileMetaInformation::GetImplementationClassUID();
VL::Type strlenImplementation = (VL::Type) strlen(implementation);
xde.SetByteValue( implementation, strlenImplementation );
Insert( xde );
}
else
{
}
if( !FindDataElement( Tag(0x0002, 0x0013) ) )
{
xde.SetTag( Tag(0x0002, 0x0013) );
xde.SetVR( VR::SH );
SHComp version = FileMetaInformation::GetImplementationVersionName();
VL::Type strlenVersion = (VL::Type)strlen(version);
xde.SetByteValue( version, strlenVersion );
Insert( xde );
}
if( !FindDataElement( Tag(0x0002, 0x0016) ) )
{
xde.SetTag( Tag(0x0002, 0x0016) );
xde.SetVR( VR::AE );
const char *title = FileMetaInformation::GetSourceApplicationEntityTitle();
VL::Type strlenTitle = (VL::Type)strlen(title);
xde.SetByteValue( title, strlenTitle );
Insert( xde );
}
Attribute<0x0002, 0x0000> filemetagrouplength;
Remove( filemetagrouplength.GetTag() );
unsigned int glen = GetLength<ExplicitDataElement>();
gdcm_assert( (glen % 2) == 0 );
filemetagrouplength.SetValue( glen );
Insert( filemetagrouplength.GetAsDataElement() );
gdcm_assert( !IsEmpty() );
}
template <typename TSwap>
bool ReadExplicitDataElement(std::istream &is, ExplicitDataElement &de)
{
std::streampos start = is.tellg();
Tag t;
if( !t.template Read<TSwap>(is) )
{
gdcm_assert(0 && "Should not happen" );
return false;
}
if( t.GetGroup() != 0x0002 )
{
std::streampos currentpos = is.tellg();
gdcm_assert( (start - currentpos) <= 0);
gdcm_assert( (int)(start - currentpos) == -4 );
is.seekg( (start - currentpos), std::ios::cur );
return false;
}
VR vr;
if( !vr.Read(is) )
{
is.seekg( start, std::ios::beg );
return false;
}
VL vl;
if( vr & VR::VL32 )
{
if( !vl.template Read<TSwap>(is) )
{
gdcm_assert(0 && "Should not happen");
return false;
}
}
else
{
vl.template Read16<TSwap>(is);
}
ByteValue *bv = nullptr;
if( vr == VR::SQ )
{
gdcm_assert(0 && "Should not happen");
return false;
}
else if( vl.IsUndefined() )
{
gdcm_assert(0 && "Should not happen");
return false;
}
else
{
bv = new ByteValue;
}
bv->SetLength(vl); if( !bv->template Read<TSwap>(is) )
{
gdcm_assert(0 && "Should not happen");
return false;
}
gdcm_assert( bv->GetLength() == vl );
de.SetTag(t);
de.SetVR(vr);
de.SetVL(vl);
de.SetValue(*bv);
return true;
}
template <typename TSwap>
bool ReadImplicitDataElement(std::istream &is, ImplicitDataElement &de)
{
std::streampos start = is.tellg();
Tag t;
if( !t.template Read<TSwap>(is) )
{
gdcm_assert(0 && "Should not happen");
return false;
}
if( t.GetGroup() != 0x0002 )
{
gdcmDebugMacro( "Done reading File Meta Information" );
is.seekg( start, std::ios::beg );
return false;
}
VL vl;
if( !vl.template Read<TSwap>(is) )
{
gdcm_assert(0 && "Should not happen");
return false;
}
ByteValue *bv = nullptr;
if( vl.IsUndefined() )
{
gdcm_assert(0 && "Should not happen");
return false;
}
else
{
bv = new ByteValue;
}
bv->SetLength(vl); if( !bv->template Read<TSwap>(is) )
{
gdcm_assert(0 && "Should not happen");
return false;
}
de.SetTag(t);
de.SetVL(vl);
de.SetValue(*bv);
return true;
}
std::istream &FileMetaInformation::Read(std::istream &is)
{
std::streampos start = is.tellg();
ExplicitDataElement xde;
Tag gl;
gl.Read<SwapperNoOp>(is);
if( gl.GetGroup() != 0x2 ) throw Exception( "INVALID" );
if( gl.GetElement() != 0x0 ) throw Exception( "INVALID" );
VR vr;
vr.Read(is);
if( vr == VR::INVALID ) throw Exception( "INVALID" );
if( vr != VR::UL ) throw Exception( "INVALID" );
is.seekg(-6,std::ios::cur);
xde.Read<SwapperNoOp>(is);
Insert( xde );
try {
while( ReadExplicitDataElement<SwapperNoOp>(is, xde ) )
{
Insert( xde );
}
}
catch( std::exception & ex )
{
(void)ex;
is.seekg(start,std::ios::beg);
ReadExplicitDataElement<SwapperNoOp>(is, xde );
Attribute<0x0002, 0x0000> filemetagrouplength;
filemetagrouplength.SetFromDataElement( xde );
const unsigned int glen = filemetagrouplength.GetValue();
unsigned int cur_len = 0;
while( cur_len < glen && ReadExplicitDataElement<SwapperNoOp>(is, xde ) )
{
Insert( xde );
cur_len += xde.GetLength();
}
}
ComputeDataSetTransferSyntax();
return is;
}
std::istream &FileMetaInformation::ReadCompat(std::istream &is)
{
gdcm_assert( is.good() );
if( !IsEmpty() )
{
throw Exception( "Serious bug" );
}
Tag t;
if( !t.Read<SwapperNoOp>(is) )
{
throw Exception( "Cannot read very first tag" );
}
if( t.GetGroup() == 0x0002 )
{
return ReadCompatInternal<SwapperNoOp>(is);
}
else if( t.GetGroup() == 0x0008 ) {
char vr_str[3];
is.read(vr_str, 2);
vr_str[2] = '\0';
VR::VRType vr = VR::GetVRType(vr_str);
if( vr != VR::VR_END )
{
DataSetTS = TransferSyntax::ExplicitVRLittleEndian;
}
else
{
DataSetTS = TransferSyntax::ImplicitVRLittleEndian;
}
is.seekg(-6, std::ios::cur); }
else if( t.GetGroup() == 0x0800 ) {
char vr_str[3];
is.read(vr_str, 2);
vr_str[2] = '\0';
VR::VRType vr = VR::GetVRType(vr_str);
if( vr != VR::VR_END )
{
DataSetTS = TransferSyntax::ExplicitVRBigEndian;
}
else
{
DataSetTS = TransferSyntax::ImplicitVRBigEndianACRNEMA;
}
is.seekg(-6, std::ios::cur); }
else if( t.GetElement() == 0x0010 ) {
char vr_str[3];
is.read(vr_str, 2);
vr_str[2] = '\0';
VR::VRType vr = VR::GetVRType(vr_str);
if( vr != VR::VR_END )
{
DataSetTS = TransferSyntax::ExplicitVRLittleEndian;
}
else
{
DataSetTS = TransferSyntax::ImplicitVRLittleEndian;
}
is.seekg(-6, std::ios::cur); }
else
{
char vr_str[3];
VR::VRType vr = VR::VR_END;
if( is.read(vr_str, 2) )
{
vr_str[2] = '\0';
vr = VR::GetVRType(vr_str);
}
else
{
throw Exception( "Impossible: cannot read 2bytes for VR" );
}
is.seekg(-6, std::ios::cur); if( vr != VR::VR_END )
{
if( t.GetGroup() > 0xff || t.GetElement() > 0xff )
DataSetTS = TransferSyntax::ExplicitVRBigEndian;
else
DataSetTS = TransferSyntax::ExplicitVRLittleEndian;
}
else
{
DataElement null( Tag(0x0,0x0), 0);
ImplicitDataElement ide;
ide.ReadPreValue<SwapperNoOp>(is);
if( ide.GetTag() == null.GetTag() && ide.GetVL() == 4 )
{
ide.ReadValue<SwapperNoOp>(is);
ReadCompat(is); gdcm_assert( DataSetTS == TransferSyntax::ImplicitVRLittleEndian );
is.seekg(-12, std::ios::cur); return is;
}
throw Exception( "Cannot find DICOM type. Giving up." );
}
}
return is;
}
#define ADDVRIMPLICIT( element ) \
case element: \
de.SetVR( (VR::VRType)TagToType<0x0002,element>::VRType ); \
break
bool AddVRToDataElement(DataElement &de)
{
switch(de.GetTag().GetElement())
{
ADDVRIMPLICIT(0x0000);
ADDVRIMPLICIT(0x0001);
ADDVRIMPLICIT(0x0002);
ADDVRIMPLICIT(0x0003);
ADDVRIMPLICIT(0x0010);
ADDVRIMPLICIT(0x0012);
ADDVRIMPLICIT(0x0013);
ADDVRIMPLICIT(0x0016);
ADDVRIMPLICIT(0x0100);
ADDVRIMPLICIT(0x0102);
default:
return false;
}
return true;
}
template <typename TSwap>
std::istream &FileMetaInformation::ReadCompatInternal(std::istream &is)
{
{
char vr_str0[2];
is.read(vr_str0, 2);
if( VR::IsValid(vr_str0) )
{
MetaInformationTS = TransferSyntax::Explicit;
is.seekg(-6, std::ios::cur); ExplicitDataElement xde;
while( ReadExplicitDataElement<SwapperNoOp>(is, xde ) )
{
if( xde.GetVR() == VR::UN )
{
gdcmWarningMacro( "VR::UN found in file Meta header. "
"VR::UN will be replaced with proper VR for tag: " << xde.GetTag() );
AddVRToDataElement(xde);
}
Insert( xde );
}
ComputeDataSetTransferSyntax();
}
else
{
MetaInformationTS = TransferSyntax::Implicit;
gdcmWarningMacro( "File Meta Information is implicit. VR will be explicitly added" );
is.seekg(-6, std::ios::cur); ImplicitDataElement ide;
while( ReadImplicitDataElement<SwapperNoOp>(is, ide ) )
{
if( AddVRToDataElement(ide) )
{
Insert(ide);
}
else
{
gdcmWarningMacro( "Unknown element found in Meta Header: " << ide.GetTag() );
}
}
try {
ComputeDataSetTransferSyntax();
} catch( gdcm::Exception & ) {
gdcmWarningMacro( "Meta Header is bogus. Guessing DataSet TS." );
Tag t;
if( !t.Read<SwapperNoOp>(is) )
{
throw Exception( "Cannot read very first tag" );
}
char vr_str[3];
is.read(vr_str, 2);
vr_str[2] = '\0';
VR::VRType vr = VR::GetVRType(vr_str);
if( vr != VR::VR_END )
{
DataSetTS = TransferSyntax::ExplicitVRLittleEndian;
}
else
{
DataSetTS = TransferSyntax::ImplicitVRLittleEndian;
}
is.seekg(-6, std::ios::cur);
}
}
}
return is;
}
void FileMetaInformation::ComputeDataSetTransferSyntax()
{
const Tag t(0x0002,0x0010);
const DataElement &de = GetDataElement(t);
std::string ts;
const ByteValue *bv = de.GetByteValue();
if( !bv )
{
throw Exception( "NULL Transfer syntax" );
}
ts = std::string(bv->GetPointer(), bv->GetLength());
TransferSyntax tst(TransferSyntax::GetTSType(ts.c_str()));
if( tst == TransferSyntax::TS_END )
{
throw Exception( "Unknown Transfer syntax" );
}
DataSetTS = tst;
DataSetTS.IsValid();
}
void FileMetaInformation::SetDataSetTransferSyntax(const TransferSyntax &ts)
{
DataSetTS = ts;
}
std::string FileMetaInformation::GetMediaStorageAsString() const
{
const Tag t(0x0002,0x0002);
if( !FindDataElement( t ) )
{
gdcmDebugMacro( "File Meta information is present but does not"
" contains " << t );
return "";
}
const DataElement &de = GetDataElement(t);
std::string ts;
{
const ByteValue *bv = de.GetByteValue();
gdcm_assert( bv );
if( bv->GetPointer() && bv->GetLength() )
{
ts = std::string(bv->GetPointer(), bv->GetLength());
}
}
if( !ts.empty() )
{
char &last = ts[ts.size()-1];
if( last == ' ' )
{
gdcmWarningMacro( "Media Storage Class UID: " << ts << " contained a trailing space character" );
last = '\0';
}
}
return ts;
}
MediaStorage FileMetaInformation::GetMediaStorage() const
{
const std::string &ts = GetMediaStorageAsString();
if( ts.empty() ) return MediaStorage::MS_END;
MediaStorage ms = MediaStorage::GetMSType(ts.c_str());
if( ms == MediaStorage::MS_END )
{
gdcmWarningMacro( "Media Storage Class UID: " << ts << " is unknown" );
}
return ms;
}
void FileMetaInformation::Default()
{
}
std::ostream &FileMetaInformation::Write(std::ostream &os) const
{
P.Write(os);
{
this->DataSet::Write<ExplicitDataElement,SwapperNoOp>(os);
}
#if 0#endif
return os;
}
}