Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

gdcmDocument.cxx

Go to the documentation of this file.
00001 /*=========================================================================
00002                                                                                 
00003   Program:   gdcm
00004   Module:    $RCSfile: gdcmDocument.cxx,v $
00005   Language:  C++
00006   Date:      $Date: 2005/02/09 21:37:45 $
00007   Version:   $Revision: 1.226 $
00008                                                                                 
00009   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
00010   l'Image). All rights reserved. See Doc/License.txt or
00011   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
00012                                                                                 
00013      This software is distributed WITHOUT ANY WARRANTY; without even
00014      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
00015      PURPOSE.  See the above copyright notices for more information.
00016                                                                                 
00017 =========================================================================*/
00018 
00019 #include "gdcmDocument.h"
00020 #include "gdcmValEntry.h"
00021 #include "gdcmBinEntry.h"
00022 #include "gdcmSeqEntry.h"
00023 #include "gdcmGlobal.h"
00024 #include "gdcmUtil.h"
00025 #include "gdcmDebug.h"
00026 #include "gdcmTS.h"
00027 #include "gdcmDictSet.h"
00028 #include "gdcmDocEntrySet.h"
00029 #include "gdcmSQItem.h"
00030 
00031 #include <vector>
00032 #include <iomanip>
00033 #include <fstream>
00034 
00035 // For nthos:
00036 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__) 
00037    #include <winsock.h>
00038 #endif
00039 
00040 #ifdef CMAKE_HAVE_NETINET_IN_H
00041    #include <netinet/in.h>
00042 #endif
00043 
00044 namespace gdcm 
00045 {
00046 //-----------------------------------------------------------------------------
00047 // Refer to Document::CheckSwap()
00048 //const unsigned int Document::HEADER_LENGTH_TO_READ = 256;
00049 
00050 // Refer to Document::SetMaxSizeLoadEntry()
00051 const unsigned int Document::MAX_SIZE_LOAD_ELEMENT_VALUE = 0xfff; // 4096
00052 const unsigned int Document::MAX_SIZE_PRINT_ELEMENT_VALUE = 0x7fffffff;
00053 
00054 //-----------------------------------------------------------------------------
00055 // Constructor / Destructor
00056 // Constructors and destructors are protected to avoid user to invoke directly
00061 Document::Document( std::string const &filename ) 
00062          :ElementSet(-1)
00063 {
00064    SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE); 
00065    Filename = filename;
00066    Initialize();
00067 
00068    Fp = 0;
00069    if ( !OpenFile() )
00070    {
00071       return;
00072    }
00073 
00074    Group0002Parsed = false;
00075 
00076    gdcmWarningMacro( "Starting parsing of file: " << Filename.c_str());
00077 
00078    Fp->seekg(0, std::ios::end);
00079    long lgt = Fp->tellg();
00080            
00081    Fp->seekg(0, std::ios::beg);
00082 
00083    CheckSwap();
00084    long beg = Fp->tellg();
00085    lgt -= beg;
00086    
00087    ParseDES( this, beg, lgt, false); // Loading is done during parsing
00088 
00089    Fp->seekg( 0,  std::ios::beg);
00090    
00091    // Load 'non string' values
00092       
00093    std::string PhotometricInterpretation = GetEntryValue(0x0028,0x0004);   
00094    if( PhotometricInterpretation == "PALETTE COLOR " )
00095    {
00096       LoadEntryBinArea(0x0028,0x1200);  // gray LUT   
00112       LoadEntryBinArea(0x0028,0x1201);  // R    LUT
00113       LoadEntryBinArea(0x0028,0x1202);  // G    LUT
00114       LoadEntryBinArea(0x0028,0x1203);  // B    LUT
00115       
00116       // Segmented Red   Palette Color LUT Data
00117       LoadEntryBinArea(0x0028,0x1221);
00118       // Segmented Green Palette Color LUT Data
00119       LoadEntryBinArea(0x0028,0x1222);
00120       // Segmented Blue  Palette Color LUT Data
00121       LoadEntryBinArea(0x0028,0x1223);
00122    } 
00123    //FIXME later : how to use it?
00124    LoadEntryBinArea(0x0028,0x3006);  //LUT Data (CTX dependent) 
00125 
00126    CloseFile(); 
00127   
00128    // ----------------------------
00129    // Specific code to allow gdcm to read ACR-LibIDO formated images
00130    // Note: ACR-LibIDO is an extension of the ACR standard that was
00131    //       used at CREATIS. For the time being (say a couple years)
00132    //       we keep this kludge to allow a smooth move to gdcm for
00133    //       CREATIS developpers (sorry folks).
00134    //
00135    // if recognition code tells us we deal with a LibIDO image
00136    // we switch lineNumber and columnNumber
00137    //
00138    std::string RecCode;
00139    RecCode = GetEntryValue(0x0008, 0x0010); // recognition code (RET)
00140    if (RecCode == "ACRNEMA_LIBIDO_1.1" ||
00141        RecCode == "CANRME_AILIBOD1_1." )  // for brain-damaged softwares
00142                                           // with "little-endian strings"
00143    {
00144          Filetype = ACR_LIBIDO; 
00145          std::string rows    = GetEntryValue(0x0028, 0x0010);
00146          std::string columns = GetEntryValue(0x0028, 0x0011);
00147          SetValEntry(columns, 0x0028, 0x0010);
00148          SetValEntry(rows   , 0x0028, 0x0011);
00149    }
00150    // --- End of ACR-LibIDO kludge --- 
00151 }
00152 
00157 Document::Document() 
00158          :ElementSet(-1)
00159 {
00160    Fp = 0;
00161 
00162    SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE);
00163    Initialize();
00164    SwapCode = 1234;
00165    Filetype = ExplicitVR;
00166    Group0002Parsed = false;
00167 }
00168 
00172 Document::~Document ()
00173 {
00174    RefPubDict = NULL;
00175    RefShaDict = NULL;
00176 }
00177 
00178 //-----------------------------------------------------------------------------
00179 // Public
00183 Dict *Document::GetPubDict()
00184 {
00185    return RefPubDict;
00186 }
00187 
00191 Dict *Document::GetShaDict()
00192 {
00193    return RefShaDict;
00194 }
00195 
00200 bool Document::SetShaDict(Dict *dict)
00201 {
00202    RefShaDict = dict;
00203    return !RefShaDict;
00204 }
00205 
00210 bool Document::SetShaDict(DictKey const &dictName)
00211 {
00212    RefShaDict = Global::GetDicts()->GetDict(dictName);
00213    return !RefShaDict;
00214 }
00215 
00224 bool Document::IsReadable()
00225 {
00226    if( Filetype == Unknown)
00227    {
00228       gdcmWarningMacro( "Wrong filetype");
00229       return false;
00230    }
00231 
00232    if ( IsEmpty() )
00233    { 
00234       gdcmWarningMacro( "No tag in internal hash table.");
00235       return false;
00236    }
00237 
00238    return true;
00239 }
00240 
00245 bool Document::IsDicomV3()
00246 {
00247    // Checking if Transfer Syntax exists is enough
00248    // Anyway, it's to late check if the 'Preamble' was found ...
00249    // And ... would it be a rich idea to check ?
00250    // (some 'no Preamble' DICOM images exist !)
00251    return GetDocEntry(0x0002, 0x0010) != NULL;
00252 }
00253 
00259 bool Document::IsPapyrus()
00260 {
00261    // check for Papyrus private Sequence
00262    DocEntry *e = GetDocEntry(0x0041, 0x1050);
00263    if ( !e )
00264       return false;
00265    // check if it's actually a Sequence
00266    if ( !dynamic_cast<SeqEntry*>(e) )
00267       return  false;
00268    return true;
00269 }
00270 
00276 FileType Document::GetFileType()
00277 {
00278    return Filetype;
00279 }
00280 
00287 std::string Document::GetTransferSyntax()
00288 {
00289    DocEntry *entry = GetDocEntry(0x0002, 0x0010);
00290    if ( !entry )
00291    {
00292       return GDCM_UNKNOWN;
00293    }
00294 
00295    // The entry might be present but not loaded (parsing and loading
00296    // happen at different stages): try loading and proceed with check...
00297    LoadDocEntrySafe(entry);
00298    if (ValEntry *valEntry = dynamic_cast< ValEntry* >(entry) )
00299    {
00300       std::string transfer = valEntry->GetValue();
00301       // The actual transfer (as read from disk) might be padded. We
00302       // first need to remove the potential padding. We can make the
00303       // weak assumption that padding was not executed with digits...
00304       if  ( transfer.length() == 0 )
00305       {
00306          // for brain damaged headers
00307          return GDCM_UNKNOWN;
00308       }
00309       while ( !isdigit((unsigned char)transfer[transfer.length()-1]) )
00310       {
00311          transfer.erase(transfer.length()-1, 1);
00312       }
00313       return transfer;
00314    }
00315    return GDCM_UNKNOWN;
00316 }
00317 
00322 std::string Document::GetTransferSyntaxName()
00323 {
00324    // use the TS (TS : Transfer Syntax)
00325    std::string transferSyntax = GetEntryValue(0x0002,0x0010);
00326 
00327    if ( (transferSyntax.find(GDCM_NOTLOADED) < transferSyntax.length()) )
00328    {
00329       gdcmErrorMacro( "Transfer Syntax not loaded. " << std::endl
00330                << "Better you increase MAX_SIZE_LOAD_ELEMENT_VALUE" );
00331       return "Uncompressed ACR-NEMA";
00332    }
00333    if ( transferSyntax == GDCM_UNFOUND )
00334    {
00335       gdcmWarningMacro( "Unfound Transfer Syntax (0002,0010)");
00336       return "Uncompressed ACR-NEMA";
00337    }
00338 
00339    // we do it only when we need it
00340    const TSKey &tsName = Global::GetTS()->GetValue( transferSyntax );
00341 
00342    // Global::GetTS() is a global static you shall never try to delete it!
00343    return tsName;
00344 }
00345 //
00346 // --------------- Swap Code ------------------
00351 uint16_t Document::SwapShort(uint16_t a)
00352 {
00353    if ( SwapCode == 4321 || SwapCode == 2143 )
00354    {
00355       a = ((( a << 8 ) & 0xff00 ) | (( a >> 8 ) & 0x00ff ) );
00356    }
00357    return a;
00358 }
00359 
00365 uint32_t Document::SwapLong(uint32_t a)
00366 {
00367    switch (SwapCode)
00368    {
00369       case 1234 :
00370          break;
00371       case 4321 :
00372          a=( ((a<<24) & 0xff000000) | ((a<<8)  & 0x00ff0000) | 
00373              ((a>>8)  & 0x0000ff00) | ((a>>24) & 0x000000ff) );
00374          break;   
00375       case 3412 :
00376          a=( ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
00377          break;  
00378       case 2143 :
00379          a=( ((a<< 8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff)  );
00380       break;
00381       default :
00382          gdcmErrorMacro( "Unset swap code:" << SwapCode );
00383          a = 0;
00384    }
00385    return a;
00386 } 
00387 
00388 //
00389 // -----------------File I/O ---------------
00395 std::ifstream *Document::OpenFile()
00396 {
00397    HasDCMPreamble = false;
00398    if (Filename.length() == 0) 
00399    {
00400       return 0;
00401    }
00402 
00403    if(Fp)
00404    {
00405       gdcmWarningMacro( "File already open: " << Filename.c_str());
00406       CloseFile();
00407    }
00408 
00409    Fp = new std::ifstream(Filename.c_str(), std::ios::in | std::ios::binary);
00410    if( ! *Fp )
00411    {
00412       gdcmDebugMacro( "Cannot open file: " << Filename.c_str());
00413       delete Fp;
00414       Fp = 0;
00415       return 0;
00416    }
00417  
00418    uint16_t zero = 0;
00419    Fp->read((char*)&zero, (size_t)2);
00420    if( Fp->eof() )
00421    {
00422       CloseFile();
00423       return 0;
00424    }
00425  
00426    //ACR -- or DICOM with no Preamble; may start with a Shadow Group --
00427    if( 
00428        zero == 0x0001 || zero == 0x0100 || zero == 0x0002 || zero == 0x0200 ||
00429        zero == 0x0003 || zero == 0x0300 || zero == 0x0004 || zero == 0x0400 ||
00430        zero == 0x0005 || zero == 0x0500 || zero == 0x0006 || zero == 0x0600 ||
00431        zero == 0x0007 || zero == 0x0700 || zero == 0x0008 || zero == 0x0800 )
00432    {
00433       std::string msg 
00434          = Util::Format("ACR/DICOM with no preamble: (%04x)\n", zero);
00435       gdcmWarningMacro( msg.c_str() );
00436       return Fp;
00437    }
00438  
00439    //DICOM
00440    Fp->seekg(126L, std::ios::cur);
00441    char dicm[4] = {' ',' ',' ',' '};
00442    Fp->read(dicm,  (size_t)4);
00443    if( Fp->eof() )
00444    {
00445       CloseFile();
00446       return 0;
00447    }
00448    if( memcmp(dicm, "DICM", 4) == 0 )
00449    {
00450       HasDCMPreamble = true;
00451       return Fp;
00452    }
00453  
00454    CloseFile();
00455    gdcmWarningMacro( "Not DICOM/ACR (missing preamble)" << Filename.c_str());
00456  
00457    return 0;
00458 }
00459 
00464 bool Document::CloseFile()
00465 {
00466    if( Fp )
00467    {
00468       Fp->close();
00469       delete Fp;
00470       Fp = 0;
00471    }
00472    return true; //FIXME how do we detect a non-closed ifstream ?
00473 }
00474 
00482 void Document::WriteContent(std::ofstream *fp, FileType filetype)
00483 {
00484    // \TODO move the following lines (and a lot of others, to be written)
00485    // to a future function CheckAndCorrectHeader  
00486 
00487    // (necessary if user wants to write a DICOM V3 file
00488    // starting from an ACR-NEMA (V2) Header
00489 
00490    if ( filetype == ImplicitVR || filetype == ExplicitVR )
00491    {
00492       // writing Dicom File Preamble
00493       char filePreamble[128];
00494       memset(filePreamble, 0, 128);
00495       fp->write(filePreamble, 128);
00496       fp->write("DICM", 4);
00497    }
00498 
00499    /*
00500     * \todo rewrite later, if really usefull
00501     *       - 'Group Length' element is optional in DICOM
00502     *       - but un-updated odd groups lengthes can causes pb
00503     *         (xmedcon breaker)
00504     *
00505     * if ( (filetype == ImplicitVR) || (filetype == ExplicitVR) )
00506     *    UpdateGroupLength(false,filetype);
00507     * if ( filetype == ACR)
00508     *    UpdateGroupLength(true,ACR);
00509     */
00510 
00511    ElementSet::WriteContent(fp, filetype); // This one is recursive
00512 }
00513 
00514 // -----------------------------------------
00515 // Content entries 
00522 void Document::LoadEntryBinArea(uint16_t group, uint16_t elem)
00523 {
00524    // Search the corresponding DocEntry
00525    DocEntry *docElement = GetDocEntry(group, elem);
00526    if ( !docElement )
00527       return;
00528 
00529    BinEntry *binElement = dynamic_cast<BinEntry *>(docElement);
00530    if( !binElement )
00531       return;
00532 
00533    LoadEntryBinArea(binElement);
00534 }
00535 
00541 void Document::LoadEntryBinArea(BinEntry *elem) 
00542 {
00543    if(elem->GetBinArea())
00544       return;
00545 
00546    bool openFile = !Fp;
00547    if(openFile)
00548       OpenFile();
00549 
00550    size_t o =(size_t)elem->GetOffset();
00551    Fp->seekg(o, std::ios::beg);
00552 
00553    size_t l = elem->GetLength();
00554    uint8_t *a = new uint8_t[l];
00555    if( !a )
00556    {
00557       gdcmWarningMacro( "Cannot allocate BinEntry content");
00558       return;
00559    }
00560 
00562    Fp->read((char*)a, l);
00563    if( Fp->fail() || Fp->eof())
00564    {
00565       delete[] a;
00566       return;
00567    }
00568 
00569    elem->SetBinArea(a);
00570 
00571    if(openFile)
00572       CloseFile();
00573 }
00574 
00581 void Document::LoadDocEntrySafe(DocEntry *entry)
00582 {
00583    if(Fp)
00584    {
00585       long PositionOnEntry = Fp->tellg();
00586       LoadDocEntry(entry);
00587       Fp->seekg(PositionOnEntry, std::ios::beg);
00588    }
00589 }
00590 
00598 bool Document::operator<(Document &document)
00599 {
00600    // Patient Name
00601    std::string s1 = GetEntryValue(0x0010,0x0010);
00602    std::string s2 = document.GetEntryValue(0x0010,0x0010);
00603    if(s1 < s2)
00604    {
00605       return true;
00606    }
00607    else if( s1 > s2 )
00608    {
00609       return false;
00610    }
00611    else
00612    {
00613       // Patient ID
00614       s1 = GetEntryValue(0x0010,0x0020);
00615       s2 = document.GetEntryValue(0x0010,0x0020);
00616       if ( s1 < s2 )
00617       {
00618          return true;
00619       }
00620       else if ( s1 > s2 )
00621       {
00622          return false;
00623       }
00624       else
00625       {
00626          // Study Instance UID
00627          s1 = GetEntryValue(0x0020,0x000d);
00628          s2 = document.GetEntryValue(0x0020,0x000d);
00629          if ( s1 < s2 )
00630          {
00631             return true;
00632          }
00633          else if( s1 > s2 )
00634          {
00635             return false;
00636          }
00637          else
00638          {
00639             // Serie Instance UID
00640             s1 = GetEntryValue(0x0020,0x000e);
00641             s2 = document.GetEntryValue(0x0020,0x000e);    
00642             if ( s1 < s2 )
00643             {
00644                return true;
00645             }
00646             else if( s1 > s2 )
00647             {
00648                return false;
00649             }
00650          }
00651       }
00652    }
00653    return false;
00654 }
00655 
00656 //-----------------------------------------------------------------------------
00657 // Protected
00663 uint16_t Document::ReadInt16()
00664    throw( FormatError )
00665 {
00666    uint16_t g;
00667    Fp->read ((char*)&g, (size_t)2);
00668    if ( Fp->fail() )
00669    {
00670       throw FormatError( "Document::ReadInt16()", " file error." );
00671    }
00672    if( Fp->eof() )
00673    {
00674       throw FormatError( "Document::ReadInt16()", "EOF." );
00675    }
00676    g = SwapShort(g); 
00677    return g;
00678 }
00679 
00685 uint32_t Document::ReadInt32()
00686    throw( FormatError )
00687 {
00688    uint32_t g;
00689    Fp->read ((char*)&g, (size_t)4);
00690    if ( Fp->fail() )
00691    {
00692       throw FormatError( "Document::ReadInt32()", " file error." );
00693    }
00694    if( Fp->eof() )
00695    {
00696       throw FormatError( "Document::ReadInt32()", "EOF." );
00697    }
00698    g = SwapLong(g);
00699    return g;
00700 }
00701 
00707 void Document::SkipBytes(uint32_t nBytes)
00708 {
00709    //FIXME don't dump the returned value
00710    Fp->seekg((long)nBytes, std::ios::cur);
00711 }
00712 
00717 int Document::ComputeGroup0002Length( FileType filetype ) 
00718 {
00719    uint16_t gr;
00720    std::string vr;
00721    
00722    int groupLength = 0;
00723    bool found0002 = false;   
00724   
00725    // for each zero-level Tag in the DCM Header
00726    DocEntry *entry = GetFirstEntry();
00727    while( entry )
00728    {
00729       gr = entry->GetGroup();
00730 
00731       if( gr == 0x0002 )
00732       {
00733          found0002 = true;
00734 
00735          if( entry->GetElement() != 0x0000 )
00736          {
00737             vr = entry->GetVR();
00738  
00739             if( filetype == ExplicitVR )
00740             {
00741                if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) 
00742                {
00743                   // explicit VR AND OB, OW, SQ : 4 more bytes
00744                   groupLength +=  4;
00745                }
00746             }
00747             groupLength += 2 + 2 + 4 + entry->GetLength();   
00748          }
00749       }
00750       else if (found0002 )
00751          break;
00752 
00753       entry = GetNextEntry();
00754    }
00755    return groupLength; 
00756 }
00757 
00758 //-----------------------------------------------------------------------------
00759 // Private
00764 void Document::Initialize() 
00765 {
00766    RefPubDict = Global::GetDicts()->GetDefaultPubDict();
00767    RefShaDict = NULL;
00768    Filetype   = Unknown;
00769 }
00770 
00775 void Document::ParseDES(DocEntrySet *set, long offset, 
00776                         long l_max, bool delim_mode)
00777 {
00778    DocEntry *newDocEntry = 0;
00779    ValEntry *newValEntry;
00780    BinEntry *newBinEntry;
00781    SeqEntry *newSeqEntry;
00782    VRKey vr;
00783    bool used = false;
00784 
00785    while (true)
00786    {
00787       if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max)
00788       {
00789          break;
00790       }
00791 
00792       used = true;
00793       newDocEntry = ReadNextDocEntry( );
00794 
00795       if ( !newDocEntry )
00796       {
00797          break;
00798       }
00799 
00800       vr = newDocEntry->GetVR();
00801       newValEntry = dynamic_cast<ValEntry*>(newDocEntry);
00802       newBinEntry = dynamic_cast<BinEntry*>(newDocEntry);
00803       newSeqEntry = dynamic_cast<SeqEntry*>(newDocEntry);
00804 
00805       if ( newValEntry || newBinEntry )
00806       {
00807          if ( newBinEntry )
00808          {
00809             if ( Filetype == ExplicitVR && 
00810                  !Global::GetVR()->IsVROfBinaryRepresentable(vr) )
00811             { 
00813                 gdcmWarningMacro( std::hex << newDocEntry->GetGroup() 
00814                                   << "|" << newDocEntry->GetElement()
00815                                   << " : Neither Valentry, nor BinEntry." 
00816                                   "Probably unknown VR.");
00817             }
00818 
00820             // When "this" is a Document the Key is simply of the
00821             // form ( group, elem )...
00822             if ( dynamic_cast< Document* > ( set ) )
00823             {
00824                newBinEntry->SetKey( newBinEntry->GetKey() );
00825             }
00826             // but when "this" is a SQItem, we are inserting this new
00827             // valEntry in a sequence item, and the key has the
00828             // generalized form (refer to \ref BaseTagKey):
00829             if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) )
00830             {
00831                newBinEntry->SetKey(  parentSQItem->GetBaseTagKey()
00832                                    + newBinEntry->GetKey() );
00833             }
00834 
00835             LoadDocEntry( newBinEntry );
00836             if( !set->AddEntry( newBinEntry ) )
00837             {
00838               //Expect big troubles if here
00839               //delete newBinEntry;
00840               used=false;
00841             }
00842          }
00843          else
00844          {
00846             // When "set" is a Document, then we are at the top of the
00847             // hierarchy and the Key is simply of the form ( group, elem )...
00848             if ( dynamic_cast< Document* > ( set ) )
00849             {
00850                newValEntry->SetKey( newValEntry->GetKey() );
00851             }
00852             // ...but when "set" is a SQItem, we are inserting this new
00853             // valEntry in a sequence item. Hence the key has the
00854             // generalized form (refer to \ref BaseTagKey):
00855             if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) )
00856             {
00857                newValEntry->SetKey(  parentSQItem->GetBaseTagKey()
00858                                    + newValEntry->GetKey() );
00859             }
00860              
00861             LoadDocEntry( newValEntry );
00862             bool delimitor=newValEntry->IsItemDelimitor();
00863             if( !set->AddEntry( newValEntry ) )
00864             {
00865               // If here expect big troubles
00866               //delete newValEntry; //otherwise mem leak
00867               used=false;
00868             }
00869 
00870             if (delimitor)
00871             {
00872                if(!used)
00873                   delete newDocEntry;
00874                break;
00875             }
00876             if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max)
00877             {
00878                if(!used)
00879                   delete newDocEntry;
00880                break;
00881             }
00882          }
00883 
00884          // Just to make sure we are at the beginning of next entry.
00885          SkipToNextDocEntry(newDocEntry);
00886       }
00887       else
00888       {
00889          // VR = "SQ"
00890          unsigned long l = newDocEntry->GetReadLength();            
00891          if ( l != 0 ) // don't mess the delim_mode for zero-length sequence
00892          {
00893             if ( l == 0xffffffff )
00894             {
00895               delim_mode = true;
00896             }
00897             else
00898             {
00899               delim_mode = false;
00900             }
00901          }
00902          // no other way to create it ...
00903          newSeqEntry->SetDelimitorMode( delim_mode );
00904 
00905          // At the top of the hierarchy, stands a Document. When "set"
00906          // is a Document, then we are building the first depth level.
00907          // Hence the SeqEntry we are building simply has a depth
00908          // level of one:
00909          if (/*Document *dummy =*/ dynamic_cast< Document* > ( set ) )
00910          {
00911             //(void)dummy;
00912             newSeqEntry->SetDepthLevel( 1 );
00913             newSeqEntry->SetKey( newSeqEntry->GetKey() );
00914          }
00915          // But when "set" is already a SQItem, we are building a nested
00916          // sequence, and hence the depth level of the new SeqEntry
00917          // we are building, is one level deeper:
00918          if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) )
00919          {
00920             newSeqEntry->SetDepthLevel( parentSQItem->GetDepthLevel() + 1 );
00921             newSeqEntry->SetKey(  parentSQItem->GetBaseTagKey()
00922                                 + newSeqEntry->GetKey() );
00923          }
00924 
00925          if ( l != 0 )
00926          {  // Don't try to parse zero-length sequences
00927             ParseSQ( newSeqEntry, 
00928                      newDocEntry->GetOffset(),
00929                      l, delim_mode);
00930          }
00931          if( !set->AddEntry( newSeqEntry ) )
00932          {
00933             used = false;
00934          }
00935          if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max)
00936          {
00937             if( !used )
00938                delete newDocEntry;
00939             break;
00940          }
00941       }
00942 
00943       if( !used )
00944          delete newDocEntry;
00945    }
00946 }
00947 
00952 void Document::ParseSQ( SeqEntry *seqEntry,
00953                         long offset, long l_max, bool delim_mode)
00954 {
00955    int SQItemNumber = 0;
00956    bool dlm_mod;
00957    long offsetStartCurrentSQItem = offset;
00958 
00959    while (true)
00960    {
00961       // the first time, we read the fff0,e000 of the first SQItem
00962       DocEntry *newDocEntry = ReadNextDocEntry();
00963 
00964       if ( !newDocEntry )
00965       {
00966          // FIXME Should warn user
00967          break;
00968       }
00969       if( delim_mode )
00970       {
00971          if ( newDocEntry->IsSequenceDelimitor() )
00972          {
00973             seqEntry->SetDelimitationItem( newDocEntry ); 
00974             break;
00975          }
00976       }
00977       if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max)
00978       {
00979          delete newDocEntry;
00980          break;
00981       }
00982       // create the current SQItem
00983       SQItem *itemSQ = new SQItem( seqEntry->GetDepthLevel() );
00984       std::ostringstream newBase;
00985       newBase << seqEntry->GetKey()
00986               << "/"
00987               << SQItemNumber
00988               << "#";
00989       itemSQ->SetBaseTagKey( newBase.str() );
00990       unsigned int l = newDocEntry->GetReadLength();
00991       
00992       if ( l == 0xffffffff )
00993       {
00994          dlm_mod = true;
00995       }
00996       else
00997       {
00998          dlm_mod = false;
00999       }
01000       // FIXME, TODO
01001       // when we're here, element fffe,e000 is already passed.
01002       // it's lost for the SQItem we're going to process !!
01003 
01004       //ParseDES(itemSQ, newDocEntry->GetOffset(), l, dlm_mod);
01005       //delete newDocEntry; // FIXME well ... it's too late to use it !
01006 
01007       // Let's try :------------
01008       // remove fff0,e000, created out of the SQItem
01009       delete newDocEntry;
01010       Fp->seekg(offsetStartCurrentSQItem, std::ios::beg);
01011       // fill up the current SQItem, starting at the beginning of fff0,e000
01012       ParseDES(itemSQ, offsetStartCurrentSQItem, l+8, dlm_mod);
01013       offsetStartCurrentSQItem = Fp->tellg();
01014       // end try -----------------
01015  
01016       seqEntry->AddSQItem( itemSQ, SQItemNumber ); 
01017       SQItemNumber++;
01018       if ( !delim_mode && ((long)(Fp->tellg())-offset ) >= l_max )
01019       {
01020          break;
01021       }
01022    }
01023 }
01024 
01030 void Document::LoadDocEntry(DocEntry *entry)
01031 {
01032    uint16_t group  = entry->GetGroup();
01033    std::string  vr = entry->GetVR();
01034    uint32_t length = entry->GetLength();
01035 
01036    Fp->seekg((long)entry->GetOffset(), std::ios::beg);
01037 
01038    // A SeQuence "contains" a set of Elements.  
01039    //          (fffe e000) tells us an Element is beginning
01040    //          (fffe e00d) tells us an Element just ended
01041    //          (fffe e0dd) tells us the current SeQuence just ended
01042    if( group == 0xfffe )
01043    {
01044       // NO more value field for SQ !
01045       return;
01046    }
01047 
01048    // When the length is zero things are easy:
01049    if ( length == 0 )
01050    {
01051       ((ValEntry *)entry)->SetValue("");
01052       return;
01053    }
01054 
01055    // The elements whose length is bigger than the specified upper bound
01056    // are not loaded. Instead we leave a short notice of the offset of
01057    // the element content and it's length.
01058 
01059    std::ostringstream s;
01060    if (length > MaxSizeLoadEntry)
01061    {
01062       if (BinEntry *binEntryPtr = dynamic_cast< BinEntry* >(entry) )
01063       {  
01064          //s << "gdcm::NotLoaded (BinEntry)";
01065          s << GDCM_NOTLOADED;
01066          s << " Address:" << (long)entry->GetOffset();
01067          s << " Length:"  << entry->GetLength();
01068          s << " x(" << std::hex << entry->GetLength() << ")";
01069          binEntryPtr->SetValue(s.str());
01070       }
01071       // Be carefull : a BinEntry IS_A ValEntry ... 
01072       else if (ValEntry *valEntryPtr = dynamic_cast< ValEntry* >(entry) )
01073       {
01074         // s << "gdcm::NotLoaded. (ValEntry)";
01075          s << GDCM_NOTLOADED;  
01076          s << " Address:" << (long)entry->GetOffset();
01077          s << " Length:"  << entry->GetLength();
01078          s << " x(" << std::hex << entry->GetLength() << ")";
01079          valEntryPtr->SetValue(s.str());
01080       }
01081       else
01082       {
01083          // fusible
01084          gdcmErrorMacro( "MaxSizeLoadEntry exceeded, neither a BinEntry "
01085                       << "nor a ValEntry ?! Should never print that !" );
01086       }
01087 
01088       // to be sure we are at the end of the value ...
01089       Fp->seekg((long)entry->GetOffset()+(long)entry->GetLength(),
01090                 std::ios::beg);
01091       return;
01092    }
01093 
01094    // When we find a BinEntry not very much can be done :
01095    if (BinEntry *binEntryPtr = dynamic_cast< BinEntry* >(entry) )
01096    {
01097       s << GDCM_BINLOADED;
01098       binEntryPtr->SetValue(s.str());
01099       LoadEntryBinArea(binEntryPtr); // last one, not to erase length !
01100       return;
01101    }
01102 
01104    if ( IsDocEntryAnInteger(entry) )
01105    {   
01106       uint32_t NewInt;
01107       int nbInt;
01108       // When short integer(s) are expected, read and convert the following 
01109       // n *two characters properly i.e. consider them as short integers as
01110       // opposed to strings.
01111       // Elements with Value Multiplicity > 1
01112       // contain a set of integers (not a single one)       
01113       if (vr == "US" || vr == "SS")
01114       {
01115          nbInt = length / 2;
01116          NewInt = ReadInt16();
01117          s << NewInt;
01118          if (nbInt > 1)
01119          {
01120             for (int i=1; i < nbInt; i++)
01121             {
01122                s << '\\';
01123                NewInt = ReadInt16();
01124                s << NewInt;
01125             }
01126          }
01127       }
01128       // See above comment on multiple integers (mutatis mutandis).
01129       else if (vr == "UL" || vr == "SL")
01130       {
01131          nbInt = length / 4;
01132          NewInt = ReadInt32();
01133          s << NewInt;
01134          if (nbInt > 1)
01135          {
01136             for (int i=1; i < nbInt; i++)
01137             {
01138                s << '\\';
01139                NewInt = ReadInt32();
01140                s << NewInt;
01141             }
01142          }
01143       }
01144 #ifdef GDCM_NO_ANSI_STRING_STREAM
01145       s << std::ends; // to avoid oddities on Solaris
01146 #endif //GDCM_NO_ANSI_STRING_STREAM
01147 
01148       ((ValEntry *)entry)->SetValue(s.str());
01149       return;
01150    }
01151    
01152   // FIXME: We need an additional byte for storing \0 that is not on disk
01153    char *str = new char[length+1];
01154    Fp->read(str, (size_t)length);
01155    str[length] = '\0'; //this is only useful when length is odd
01156    // Special DicomString call to properly handle \0 and even length
01157    std::string newValue;
01158    if( length % 2 )
01159    {
01160       newValue = Util::DicomString(str, length+1);
01161       gdcmWarningMacro("Warning: bad length: " << length <<
01162                        ",For string :" <<  newValue.c_str()); 
01163       // Since we change the length of string update it length
01164       //entry->SetReadLength(length+1);
01165    }
01166    else
01167    {
01168       newValue = Util::DicomString(str, length);
01169    }
01170    delete[] str;
01171 
01172    if ( ValEntry *valEntry = dynamic_cast<ValEntry* >(entry) )
01173    {
01174       if ( Fp->fail() || Fp->eof())
01175       {
01176          gdcmWarningMacro("Unread element value");
01177          valEntry->SetValue(GDCM_UNREAD);
01178          return;
01179       }
01180 
01181       if( vr == "UI" )
01182       {
01183          // Because of correspondance with the VR dic
01184          valEntry->SetValue(newValue);
01185       }
01186       else
01187       {
01188          valEntry->SetValue(newValue);
01189       }
01190    }
01191    else
01192    {
01193       gdcmErrorMacro( "Should have a ValEntry, here !");
01194    }
01195 }
01196 
01201 void Document::FindDocEntryLength( DocEntry *entry )
01202    throw ( FormatError )
01203 {
01204    std::string  vr  = entry->GetVR();
01205    uint16_t length16;       
01206    
01207    if ( Filetype == ExplicitVR && !entry->IsImplicitVR() ) 
01208    {
01209       if ( vr == "OB" || vr == "OW" || vr == "SQ" || vr == "UN" ) 
01210       {
01211          // The following reserved two bytes (see PS 3.5-2003, section
01212          // "7.1.2 Data element structure with explicit vr", p 27) must be
01213          // skipped before proceeding on reading the length on 4 bytes.
01214          Fp->seekg( 2L, std::ios::cur);
01215          uint32_t length32 = ReadInt32();
01216 
01217          if ( (vr == "OB" || vr == "OW") && length32 == 0xffffffff ) 
01218          {
01219             uint32_t lengthOB;
01220             try 
01221             {
01222                lengthOB = FindDocEntryLengthOBOrOW();
01223             }
01224             catch ( FormatUnexpected )
01225             {
01226                // Computing the length failed (this happens with broken
01227                // files like gdcm-JPEG-LossLess3a.dcm). We still have a
01228                // chance to get the pixels by deciding the element goes
01229                // until the end of the file. Hence we artificially fix the
01230                // the length and proceed.
01231                long currentPosition = Fp->tellg();
01232                Fp->seekg(0L,std::ios::end);
01233 
01234                long lengthUntilEOF = (long)(Fp->tellg())-currentPosition;
01235                Fp->seekg(currentPosition, std::ios::beg);
01236 
01237                entry->SetReadLength(lengthUntilEOF);
01238                entry->SetLength(lengthUntilEOF);
01239                return;
01240             }
01241             entry->SetReadLength(lengthOB);
01242             entry->SetLength(lengthOB);
01243             return;
01244          }
01245          FixDocEntryFoundLength(entry, length32); 
01246          return;
01247       }
01248 
01249       // Length is encoded on 2 bytes.
01250       length16 = ReadInt16();
01251 
01252       // FIXME : This heuristic supposes that the first group following
01253       //         group 0002 *has* and element 0000.
01254       // BUT ... Element 0000 is optionnal :-(
01255 
01256 
01257    // Fixed using : HandleOutOfGroup0002()
01258    //              (first hereafter strategy ...)
01259       
01260       // We can tell the current file is encoded in big endian (like
01261       // Data/US-RGB-8-epicard) when we find the "Transfer Syntax" tag
01262       // and it's value is the one of the encoding of a big endian file.
01263       // In order to deal with such big endian encoded files, we have
01264       // (at least) two strategies:
01265       // * when we load the "Transfer Syntax" tag with value of big endian
01266       //   encoding, we raise the proper flags. Then we wait for the end
01267       //   of the META group (0x0002) among which is "Transfer Syntax",
01268       //   before switching the swap code to big endian. We have to postpone
01269       //   the switching of the swap code since the META group is fully encoded
01270       //   in little endian, and big endian coding only starts at the next
01271       //   group. The corresponding code can be hard to analyse and adds
01272       //   many additional unnecessary tests for regular tags.
01273       // * the second strategy consists in waiting for trouble, that shall
01274       //   appear when we find the first group with big endian encoding. This
01275       //   is easy to detect since the length of a "Group Length" tag (the
01276       //   ones with zero as element number) has to be of 4 (0x0004). When we
01277       //   encounter 1024 (0x0400) chances are the encoding changed and we
01278       //   found a group with big endian encoding.
01279       //---> Unfortunately, element 0000 is optional.
01280       //---> This will not work when missing!
01281       // We shall use this second strategy. In order to make sure that we
01282       // can interpret the presence of an apparently big endian encoded
01283       // length of a "Group Length" without committing a big mistake, we
01284       // add an additional check: we look in the already parsed elements
01285       // for the presence of a "Transfer Syntax" whose value has to be "big
01286       // endian encoding". When this is the case, chances are we have got our
01287       // hands on a big endian encoded file: we switch the swap code to
01288       // big endian and proceed...
01289 
01290 //      if ( element  == 0x0000 && length16 == 0x0400 ) 
01291 //      {
01292 //         std::string ts = GetTransferSyntax();
01293 //         if ( Global::GetTS()->GetSpecialTransferSyntax(ts) 
01294 //                != TS::ExplicitVRBigEndian ) 
01295 //         {
01296 //            throw FormatError( "Document::FindDocEntryLength()",
01297 //                               " not explicit VR." );
01298 //           return;
01299 //        }
01300 //        length16 = 4;
01301 //        SwitchByteSwapCode();
01302 //
01303 //         // Restore the unproperly loaded values i.e. the group, the element
01304 //         // and the dictionary entry depending on them.
01305 //         uint16_t correctGroup = SwapShort( entry->GetGroup() );
01306 //         uint16_t correctElem  = SwapShort( entry->GetElement() );
01307 //         DictEntry *newTag = GetDictEntry( correctGroup, correctElem );
01308 //         if ( !newTag )
01309 //         {
01310 //            // This correct tag is not in the dictionary. Create a new one.
01311 //            newTag = NewVirtualDictEntry(correctGroup, correctElem);
01312 //         }
01313 //         // FIXME this can create a memory leaks on the old entry that be
01314 //         // left unreferenced.
01315 //         entry->SetDictEntry( newTag );
01316 //      }
01317   
01318       // 0xffff means that we deal with 'No Length' Sequence 
01319       //        or 'No Length' SQItem
01320       if ( length16 == 0xffff) 
01321       {           
01322          length16 = 0;
01323       }
01324       FixDocEntryFoundLength( entry, (uint32_t)length16 );
01325       return;
01326    }
01327    else
01328    {
01329       // Either implicit VR or a non DICOM conformal (see note below) explicit
01330       // VR that ommited the VR of (at least) this element. Farts happen.
01331       // [Note: according to the part 5, PS 3.5-2001, section 7.1 p25
01332       // on Data elements "Implicit and Explicit VR Data Elements shall
01333       // not coexist in a Data Set and Data Sets nested within it".]
01334       // Length is on 4 bytes.
01335 
01336      // Well ... group 0002 is always coded in 'Explicit VR Litle Endian'
01337      // even if Transfer Syntax is 'Implicit VR ...' 
01338       
01339       FixDocEntryFoundLength( entry, ReadInt32() );
01340       return;
01341    }
01342 }
01343 
01349 uint32_t Document::FindDocEntryLengthOBOrOW()
01350    throw( FormatUnexpected )
01351 {
01352    // See PS 3.5-2001, section A.4 p. 49 on encapsulation of encoded pixel data.
01353    long positionOnEntry = Fp->tellg();
01354    bool foundSequenceDelimiter = false;
01355    uint32_t totalLength = 0;
01356 
01357    while ( !foundSequenceDelimiter )
01358    {
01359       uint16_t group;
01360       uint16_t elem;
01361       try
01362       {
01363          group = ReadInt16();
01364          elem  = ReadInt16();   
01365       }
01366       catch ( FormatError )
01367       {
01368          throw FormatError("Unexpected end of file encountered during ",
01369                            "Document::FindDocEntryLengthOBOrOW()");
01370       }
01371       // We have to decount the group and element we just read
01372       totalLength += 4;     
01373       if ( group != 0xfffe || ( ( elem != 0xe0dd ) && ( elem != 0xe000 ) ) )
01374       {
01375          long filePosition = Fp->tellg();
01376          gdcmWarningMacro( "Neither an Item tag nor a Sequence delimiter tag on :" 
01377            << std::hex << group << " , " << elem 
01378            << ") -before- position x(" << filePosition << ")" );
01379   
01380          Fp->seekg(positionOnEntry, std::ios::beg);
01381          throw FormatUnexpected( "Neither an Item tag nor a Sequence delimiter tag.");
01382       }
01383       if ( elem == 0xe0dd )
01384       {
01385          foundSequenceDelimiter = true;
01386       }
01387       uint32_t itemLength = ReadInt32();
01388       // We add 4 bytes since we just read the ItemLength with ReadInt32
01389       totalLength += itemLength + 4;
01390       SkipBytes(itemLength);
01391       
01392       if ( foundSequenceDelimiter )
01393       {
01394          break;
01395       }
01396    }
01397    Fp->seekg( positionOnEntry, std::ios::beg);
01398    return totalLength;
01399 }
01400 
01405 std::string Document::FindDocEntryVR()
01406 {
01407    if ( Filetype != ExplicitVR )
01408       return GDCM_UNKNOWN;
01409 
01410    long positionOnEntry = Fp->tellg();
01411    // Warning: we believe this is explicit VR (Value Representation) because
01412    // we used a heuristic that found "UL" in the first tag. Alas this
01413    // doesn't guarantee that all the tags will be in explicit VR. In some
01414    // cases (see e-film filtered files) one finds implicit VR tags mixed
01415    // within an explicit VR file. Hence we make sure the present tag
01416    // is in explicit VR and try to fix things if it happens not to be
01417    // the case.
01418 
01419    char vr[3];
01420    Fp->read (vr, (size_t)2);
01421    vr[2] = 0;
01422 
01423    if( !CheckDocEntryVR(vr) )
01424    {
01425       Fp->seekg(positionOnEntry, std::ios::beg);
01426       return GDCM_UNKNOWN;
01427    }
01428    return vr;
01429 }
01430 
01439 bool Document::CheckDocEntryVR(VRKey vr)
01440 {
01441    if ( !Global::GetVR()->IsValidVR(vr) )
01442       return false;
01443 
01444    return true; 
01445 }
01446 
01454 std::string Document::GetDocEntryValue(DocEntry *entry)
01455 {
01456    if ( IsDocEntryAnInteger(entry) && entry->IsImplicitVR() )
01457    {
01458       std::string val = ((ValEntry *)entry)->GetValue();
01459       std::string vr  = entry->GetVR();
01460       uint32_t length = entry->GetLength();
01461       std::ostringstream s;
01462       int nbInt;
01463 
01464       // When short integer(s) are expected, read and convert the following 
01465       // n * 2 bytes properly i.e. as a multivaluated strings
01466       // (each single value is separated fromthe next one by '\'
01467       // as usual for standard multivaluated filels
01468       // Elements with Value Multiplicity > 1
01469       // contain a set of short integers (not a single one) 
01470    
01471       if( vr == "US" || vr == "SS" )
01472       {
01473          uint16_t newInt16;
01474 
01475          nbInt = length / 2;
01476          for (int i=0; i < nbInt; i++) 
01477          {
01478             if( i != 0 )
01479             {
01480                s << '\\';
01481             }
01482             newInt16 = ( val[2*i+0] & 0xFF ) + ( ( val[2*i+1] & 0xFF ) << 8);
01483             newInt16 = SwapShort( newInt16 );
01484             s << newInt16;
01485          }
01486       }
01487 
01488       // When integer(s) are expected, read and convert the following 
01489       // n * 4 bytes properly i.e. as a multivaluated strings
01490       // (each single value is separated fromthe next one by '\'
01491       // as usual for standard multivaluated filels
01492       // Elements with Value Multiplicity > 1
01493       // contain a set of integers (not a single one) 
01494       else if( vr == "UL" || vr == "SL" )
01495       {
01496          uint32_t newInt32;
01497 
01498          nbInt = length / 4;
01499          for (int i=0; i < nbInt; i++) 
01500          {
01501             if( i != 0)
01502             {
01503                s << '\\';
01504             }
01505             newInt32 = ( val[4*i+0] & 0xFF )
01506                     + (( val[4*i+1] & 0xFF ) <<  8 )
01507                     + (( val[4*i+2] & 0xFF ) << 16 )
01508                     + (( val[4*i+3] & 0xFF ) << 24 );
01509             newInt32 = SwapLong( newInt32 );
01510             s << newInt32;
01511          }
01512       }
01513 #ifdef GDCM_NO_ANSI_STRING_STREAM
01514       s << std::ends; // to avoid oddities on Solaris
01515 #endif //GDCM_NO_ANSI_STRING_STREAM
01516       return s.str();
01517    }
01518    return ((ValEntry *)entry)->GetValue();
01519 }
01520 
01529 std::string Document::GetDocEntryUnvalue(DocEntry *entry)
01530 {
01531    if ( IsDocEntryAnInteger(entry) && entry->IsImplicitVR() )
01532    {
01533       std::string vr = entry->GetVR();
01534       std::vector<std::string> tokens;
01535       std::ostringstream s;
01536 
01537       if ( vr == "US" || vr == "SS" ) 
01538       {
01539          uint16_t newInt16;
01540 
01541          tokens.erase( tokens.begin(), tokens.end()); // clean any previous value
01542          Util::Tokenize (((ValEntry *)entry)->GetValue(), tokens, "\\");
01543          for (unsigned int i=0; i<tokens.size(); i++) 
01544          {
01545             newInt16 = atoi(tokens[i].c_str());
01546             s << (  newInt16        & 0xFF ) 
01547               << (( newInt16 >> 8 ) & 0xFF );
01548          }
01549          tokens.clear();
01550       }
01551       if ( vr == "UL" || vr == "SL")
01552       {
01553          uint32_t newInt32;
01554 
01555          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
01556          Util::Tokenize (((ValEntry *)entry)->GetValue(), tokens, "\\");
01557          for (unsigned int i=0; i<tokens.size();i++) 
01558          {
01559             newInt32 = atoi(tokens[i].c_str());
01560             s << (char)(  newInt32         & 0xFF ) 
01561               << (char)(( newInt32 >>  8 ) & 0xFF )
01562               << (char)(( newInt32 >> 16 ) & 0xFF )
01563               << (char)(( newInt32 >> 24 ) & 0xFF );
01564          }
01565          tokens.clear();
01566       }
01567 
01568 #ifdef GDCM_NO_ANSI_STRING_STREAM
01569       s << std::ends; // to avoid oddities on Solaris
01570 #endif //GDCM_NO_ANSI_STRING_STREAM
01571       return s.str();
01572    }
01573 
01574    return ((ValEntry *)entry)->GetValue();
01575 }
01576 
01582 void Document::SkipDocEntry(DocEntry *entry) 
01583 {
01584    SkipBytes(entry->GetLength());
01585 }
01586 
01592 void Document::SkipToNextDocEntry(DocEntry *currentDocEntry) 
01593 {
01594    Fp->seekg((long)(currentDocEntry->GetOffset()),     std::ios::beg);
01595    if (currentDocEntry->GetGroup() != 0xfffe)  // for fffe pb
01596       Fp->seekg( (long)(currentDocEntry->GetReadLength()),std::ios::cur);
01597 }
01598 
01606 void Document::FixDocEntryFoundLength(DocEntry *entry,
01607                                       uint32_t foundLength)
01608 {
01609    entry->SetReadLength( foundLength ); // will be updated only if a bug is found        
01610    if ( foundLength == 0xffffffff)
01611    {
01612       foundLength = 0;
01613    }
01614    
01615    uint16_t gr   = entry->GetGroup();
01616    uint16_t elem = entry->GetElement(); 
01617      
01618    if ( foundLength % 2)
01619    {
01620       gdcmWarningMacro( "Warning : Tag with uneven length " << foundLength 
01621         <<  " in x(" << std::hex << gr << "," << elem <<")");
01622    }
01623       
01625    // Allthough not recent many such GE corrupted images are still present
01626    // on Creatis hard disks. Hence this fix shall remain when such images
01627    // are no longer in use (we are talking a few years, here)...
01628    // Note: XMedCom probably uses such a trick since it is able to read
01629    //       those pesky GE images ...
01630    if ( foundLength == 13)
01631    {
01632       // Only happens for this length !
01633       if ( gr != 0x0008 || ( elem != 0x0070 && elem != 0x0080 ) )
01634       {
01635          foundLength = 10;
01636          entry->SetReadLength(10); 
01637       }
01638    }
01639 
01641    // Occurence of such images is quite low (unless one leaves close to a
01642    // 'Leonardo' source. Hence, one might consider commenting out the
01643    // following fix on efficiency reasons.
01644    else if ( gr   == 0x0009 && ( elem == 0x1113 || elem == 0x1114 ) )
01645    {
01646       foundLength = 4;
01647       entry->SetReadLength(4); 
01648    } 
01649  
01650    else if ( entry->GetVR() == "SQ" )
01651    {
01652       foundLength = 0;      // ReadLength is unchanged 
01653    } 
01654     
01656    // "fffe|xxxx" which is just a marker. Delimiters length should not be
01657    // taken into account.
01658    else if( gr == 0xfffe )
01659    {    
01660      // According to the norm, fffe|0000 shouldn't exist. BUT the Philips
01661      // image gdcmData/gdcm-MR-PHILIPS-16-Multi-Seq.dcm happens to
01662      // causes extra troubles...
01663      if( entry->GetElement() != 0x0000 )
01664      {
01665         foundLength = 0;
01666      }
01667    }            
01668    entry->SetLength(foundLength);
01669 }
01670 
01677 bool Document::IsDocEntryAnInteger(DocEntry *entry)
01678 {
01679    uint16_t elem          = entry->GetElement();
01680    uint16_t group         = entry->GetGroup();
01681    const std::string &vr  = entry->GetVR();
01682    uint32_t length        = entry->GetLength();
01683 
01684    // When we have some semantics on the element we just read, and if we
01685    // a priori know we are dealing with an integer, then we shall be
01686    // able to swap it's element value properly.
01687    if ( elem == 0 )  // This is the group length of the group
01688    {  
01689       if ( length == 4 )
01690       {
01691          return true;
01692       }
01693       else 
01694       {
01695          // Allthough this should never happen, still some images have a
01696          // corrupted group length [e.g. have a glance at offset x(8336) of
01697          // gdcmData/gdcm-MR-PHILIPS-16-Multi-Seq.dcm].
01698          // Since for dicom compliant and well behaved headers, the present
01699          // test is useless (and might even look a bit paranoid), when we
01700          // encounter such an ill-formed image, we simply display a warning
01701          // message and proceed on parsing (while crossing fingers).
01702          long filePosition = Fp->tellg();
01703          gdcmWarningMacro( "Erroneous Group Length element length  on : (" 
01704            << std::hex << group << " , " << elem
01705            << ") -before- position x(" << filePosition << ")"
01706            << "lgt : " << length );
01707       }
01708    }
01709 
01710    if ( vr == "UL" || vr == "US" || vr == "SL" || vr == "SS" )
01711    {
01712       return true;
01713    }   
01714    return false;
01715 }
01716 
01725 bool Document::CheckSwap()
01726 {
01727    // The only guaranted way of finding the swap code is to find a
01728    // group tag since we know it's length has to be of four bytes i.e.
01729    // 0x00000004. Finding the swap code in then straigthforward. Trouble
01730    // occurs when we can't find such group...
01731    
01732    uint32_t  x = 4;  // x : for ntohs
01733    bool net2host; // true when HostByteOrder is the same as NetworkByteOrder
01734    uint32_t  s32;
01735    uint16_t  s16;
01736        
01737    char deb[256];
01738     
01739    // First, compare HostByteOrder and NetworkByteOrder in order to
01740    // determine if we shall need to swap bytes (i.e. the Endian type).
01741    if ( x == ntohs(x) )
01742    {
01743       net2host = true;
01744    }
01745    else
01746    {
01747       net2host = false;
01748    }
01749          
01750    // The easiest case is the one of a 'true' DICOM header, we just have
01751    // to look for the string "DICM" inside the file preamble.
01752    Fp->read(deb, 256);
01753    
01754    char *entCur = deb + 128;
01755    if( memcmp(entCur, "DICM", (size_t)4) == 0 )
01756    {
01757       gdcmWarningMacro( "Looks like DICOM Version3 (preamble + DCM)" );
01758       
01759       // Group 0002 should always be VR, and the first element 0000
01760       // Let's be carefull (so many wrong headers ...)
01761       // and determine the value representation (VR) : 
01762       // Let's skip to the first element (0002,0000) and check there if we find
01763       // "UL"  - or "OB" if the 1st one is (0002,0001) -,
01764       // in which case we (almost) know it is explicit VR.
01765       // WARNING: if it happens to be implicit VR then what we will read
01766       // is the length of the group. If this ascii representation of this
01767       // length happens to be "UL" then we shall believe it is explicit VR.
01768       // We need to skip :
01769       // * the 128 bytes of File Preamble (often padded with zeroes),
01770       // * the 4 bytes of "DICM" string,
01771       // * the 4 bytes of the first tag (0002, 0000),or (0002, 0001)
01772       // i.e. a total of  136 bytes.
01773       entCur = deb + 136;
01774      
01775       // group 0x0002 *is always* Explicit VR Sometimes ,
01776       // even if elem 0002,0010 (Transfer Syntax) tells us the file is
01777       // *Implicit* VR  (see former 'gdcmData/icone.dcm')
01778       
01779       if( memcmp(entCur, "UL", (size_t)2) == 0 ||
01780           memcmp(entCur, "OB", (size_t)2) == 0 ||
01781           memcmp(entCur, "UI", (size_t)2) == 0 ||
01782           memcmp(entCur, "CS", (size_t)2) == 0 )  // CS, to remove later
01783                                                   // when Write DCM *adds*
01784       // FIXME
01785       // Use Document::dicom_vr to test all the possibilities
01786       // instead of just checking for UL, OB and UI !? group 0000 
01787       {
01788          Filetype = ExplicitVR;
01789          gdcmWarningMacro( "Group 0002 : Explicit Value Representation");
01790       } 
01791       else 
01792       {
01793          Filetype = ImplicitVR;
01794          gdcmWarningMacro( "Group 0002 :Not an explicit Value Representation;"
01795                         << "Looks like a bugged Header!");
01796       }
01797       
01798       if ( net2host )
01799       {
01800          SwapCode = 4321;
01801          gdcmWarningMacro( "HostByteOrder != NetworkByteOrder");
01802       }
01803       else 
01804       {
01805          SwapCode = 1234;
01806          gdcmWarningMacro( "HostByteOrder = NetworkByteOrder");
01807       }
01808       
01809       // Position the file position indicator at first tag 
01810       // (i.e. after the file preamble and the "DICM" string).
01811       Fp->seekg(0, std::ios::beg);
01812       Fp->seekg ( 132L, std::ios::beg);
01813       return true;
01814    } // End of DicomV3
01815 
01816    // Alas, this is not a DicomV3 file and whatever happens there is no file
01817    // preamble. We can reset the file position indicator to where the data
01818    // is (i.e. the beginning of the file).
01819    gdcmWarningMacro( "Not a DICOM Version3 file");
01820    Fp->seekg(0, std::ios::beg);
01821 
01822    // Our next best chance would be to be considering a 'clean' ACR/NEMA file.
01823    // By clean we mean that the length of the first tag is written down.
01824    // If this is the case and since the length of the first group HAS to be
01825    // four (bytes), then determining the proper swap code is straightforward.
01826 
01827    entCur = deb + 4;
01828    // We assume the array of char we are considering contains the binary
01829    // representation of a 32 bits integer. Hence the following dirty
01830    // trick :
01831    s32 = *((uint32_t *)(entCur));
01832 
01833    switch( s32 )
01834    {
01835       case 0x00040000 :
01836          SwapCode = 3412;
01837          Filetype = ACR;
01838          return true;
01839       case 0x04000000 :
01840          SwapCode = 4321;
01841          Filetype = ACR;
01842          return true;
01843       case 0x00000400 :
01844          SwapCode = 2143;
01845          Filetype = ACR;
01846          return true;
01847       case 0x00000004 :
01848          SwapCode = 1234;
01849          Filetype = ACR;
01850          return true;
01851       default :
01852          // We are out of luck. It is not a DicomV3 nor a 'clean' ACR/NEMA file.
01853          // It is time for despaired wild guesses. 
01854          // So, let's check if this file wouldn't happen to be 'dirty' ACR/NEMA,
01855          //  i.e. the 'group length' element is not present :     
01856          
01857          //  check the supposed-to-be 'group number'
01858          //  in ( 0x0001 .. 0x0008 )
01859          //  to determine ' SwapCode' value .
01860          //  Only 0 or 4321 will be possible 
01861          //  (no oportunity to check for the formerly well known
01862          //  ACR-NEMA 'Bad Big Endian' or 'Bad Little Endian' 
01863          //  if unsuccessfull (i.e. neither 0x0002 nor 0x0200 etc -3, 4, ..., 8-) 
01864          //  the file IS NOT ACR-NEMA nor DICOM V3
01865          //  Find a trick to tell it the caller...
01866       
01867          s16 = *((uint16_t *)(deb));
01868       
01869          switch ( s16 )
01870          {
01871             case 0x0001 :
01872             case 0x0002 :
01873             case 0x0003 :
01874             case 0x0004 :
01875             case 0x0005 :
01876             case 0x0006 :
01877             case 0x0007 :
01878             case 0x0008 :
01879                SwapCode = 1234;
01880                Filetype = ACR;
01881                return true;
01882             case 0x0100 :
01883             case 0x0200 :
01884             case 0x0300 :
01885             case 0x0400 :
01886             case 0x0500 :
01887             case 0x0600 :
01888             case 0x0700 :
01889             case 0x0800 :
01890                SwapCode = 4321;
01891                Filetype = ACR;
01892                return true;
01893             default :
01894                gdcmWarningMacro( "ACR/NEMA unfound swap info (Really hopeless !)");
01895                Filetype = Unknown;
01896                return false;
01897          }
01898    }
01899 }
01900 
01904 void Document::SwitchByteSwapCode() 
01905 {
01906    gdcmWarningMacro( "Switching Byte Swap code from "<< SwapCode);
01907    if ( SwapCode == 1234 ) 
01908    {
01909       SwapCode = 4321;
01910    }
01911    else if ( SwapCode == 4321 ) 
01912    {
01913       SwapCode = 1234;
01914    }
01915    else if ( SwapCode == 3412 ) 
01916    {
01917       SwapCode = 2143;
01918    }
01919    else if ( SwapCode == 2143 )
01920    {
01921       SwapCode = 3412;
01922    }
01923 }
01924 
01929 void Document::SetMaxSizeLoadEntry(long newSize) 
01930 {
01931    if ( newSize < 0 )
01932    {
01933       return;
01934    }
01935    if ((uint32_t)newSize >= (uint32_t)0xffffffff )
01936    {
01937       MaxSizeLoadEntry = 0xffffffff;
01938       return;
01939    }
01940    MaxSizeLoadEntry = newSize;
01941 }
01942 
01948 void Document::SetMaxSizePrintEntry(long newSize) 
01949 {
01950    if ( newSize < 0 )
01951    {
01952       return;
01953    }
01954    if ((uint32_t)newSize >= (uint32_t)0xffffffff )
01955    {
01956       MaxSizePrintEntry = 0xffffffff;
01957       return;
01958    }
01959    MaxSizePrintEntry = newSize;
01960 }
01961 
01962 
01970 DocEntry *Document::ReadNextDocEntry()
01971 {
01972    uint16_t group;
01973    uint16_t elem;
01974 
01975    try
01976    {
01977       group = ReadInt16();
01978       elem  = ReadInt16();
01979    }
01980    catch ( FormatError e )
01981    {
01982       // We reached the EOF (or an error occured) therefore 
01983       // header parsing has to be considered as finished.
01984       return 0;
01985    }
01986 
01987    // Sometimes file contains groups of tags with reversed endianess.
01988    HandleBrokenEndian(group, elem);
01989 
01990    // In 'true DICOM' files Group 0002 is always little endian
01991    if ( HasDCMPreamble )
01992       HandleOutOfGroup0002(group, elem);
01993  
01994    std::string vr = FindDocEntryVR();
01995    std::string realVR = vr;
01996 
01997    if( vr == GDCM_UNKNOWN)
01998    {
01999       DictEntry *dictEntry = GetDictEntry(group,elem);
02000       if( dictEntry )
02001          realVR = dictEntry->GetVR();
02002    }
02003 
02004    DocEntry *newEntry;
02005    if( Global::GetVR()->IsVROfSequence(realVR) )
02006       newEntry = NewSeqEntry(group, elem);
02007    else if( Global::GetVR()->IsVROfStringRepresentable(realVR) )
02008       newEntry = NewValEntry(group, elem,vr);
02009    else
02010       newEntry = NewBinEntry(group, elem,vr);
02011 
02012    if( vr == GDCM_UNKNOWN )
02013    {
02014       if( Filetype == ExplicitVR )
02015       {
02016          // We thought this was explicit VR, but we end up with an
02017          // implicit VR tag. Let's backtrack.
02018          if ( newEntry->GetGroup() != 0xfffe )
02019          { 
02020             std::string msg;
02021             msg = Util::Format("Entry (%04x,%04x) should be Explicit VR\n", 
02022                           newEntry->GetGroup(), newEntry->GetElement());
02023             gdcmWarningMacro( msg.c_str() );
02024           }
02025       }
02026       newEntry->SetImplicitVR();
02027    }
02028 
02029    try
02030    {
02031       FindDocEntryLength(newEntry);
02032    }
02033    catch ( FormatError e )
02034    {
02035       // Call it quits
02036       //std::cout << e;
02037       delete newEntry;
02038       return 0;
02039    }
02040 
02041    newEntry->SetOffset(Fp->tellg());  
02042 
02043    return newEntry;
02044 }
02045 
02052 void Document::HandleBrokenEndian(uint16_t &group, uint16_t &elem)
02053 {
02054    // Endian reversion. Some files contain groups of tags with reversed endianess.
02055    static int reversedEndian = 0;
02056    // try to fix endian switching in the middle of headers
02057    if ((group == 0xfeff) && (elem == 0x00e0))
02058    {
02059      // start endian swap mark for group found
02060      reversedEndian++;
02061      SwitchByteSwapCode();
02062      // fix the tag
02063      group = 0xfffe;
02064      elem  = 0xe000;
02065    } 
02066    else if (group == 0xfffe && elem == 0xe00d && reversedEndian) 
02067    {
02068      // end of reversed endian group
02069      reversedEndian--;
02070      SwitchByteSwapCode();
02071    }
02072 }
02073 
02079 void Document::HandleOutOfGroup0002(uint16_t &group, uint16_t &elem)
02080 {
02081    // Endian reversion. Some files contain groups of tags with reversed endianess.
02082    if ( !Group0002Parsed && group != 0x0002)
02083    {
02084       Group0002Parsed = true;
02085       // we just came out of group 0002
02086       // if Transfer syntax is Big Endian we have to change CheckSwap
02087 
02088       std::string ts = GetTransferSyntax();
02089       if ( !Global::GetTS()->IsTransferSyntax(ts) )
02090       {
02091          gdcmWarningMacro("True DICOM File, with NO Tansfer Syntax: " << ts );
02092          return;
02093       }
02094 
02095       // Group 0002 is always 'Explicit ...' enven when Transfer Syntax says 'Implicit ..." 
02096 
02097       if ( Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRLittleEndian )
02098          {
02099             Filetype = ImplicitVR;
02100          }
02101        
02102       // FIXME Strangely, this works with 
02103       //'Implicit VR Transfer Syntax (GE Private)
02104       if ( Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRBigEndian )
02105       {
02106          gdcmWarningMacro("Transfer Syntax Name = [" 
02107                         << GetTransferSyntaxName() << "]" );
02108          SwitchByteSwapCode();
02109          group = SwapShort(group);
02110          elem  = SwapShort(elem);
02111       }
02112    }
02113 }
02114 
02115 //-----------------------------------------------------------------------------
02116 // Print
02117 
02118 //-----------------------------------------------------------------------------
02119 } // end namespace gdcm

Generated on Thu Feb 10 22:17:58 2005 for gdcm by doxygen 1.3.6