]> Creatis software - gdcm.git/blob - src/gdcmDocument.cxx
* src/*.cxx : first parss to normalize file organisation
[gdcm.git] / src / gdcmDocument.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDocument.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/02/01 10:29:55 $
7   Version:   $Revision: 1.218 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18
19 #include "gdcmDocument.h"
20 #include "gdcmValEntry.h"
21 #include "gdcmBinEntry.h"
22 #include "gdcmSeqEntry.h"
23 #include "gdcmGlobal.h"
24 #include "gdcmUtil.h"
25 #include "gdcmDebug.h"
26 #include "gdcmTS.h"
27 #include "gdcmDictSet.h"
28 #include "gdcmDocEntrySet.h"
29 #include "gdcmSQItem.h"
30
31 #include <vector>
32 #include <iomanip>
33
34 // For nthos:
35 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__) 
36    #include <winsock.h>
37 #endif
38
39 #ifdef CMAKE_HAVE_NETINET_IN_H
40    #include <netinet/in.h>
41 #endif
42
43 namespace gdcm 
44 {
45 //-----------------------------------------------------------------------------
46 // Refer to Document::CheckSwap()
47 //const unsigned int Document::HEADER_LENGTH_TO_READ = 256;
48
49 // Refer to Document::SetMaxSizeLoadEntry()
50 const unsigned int Document::MAX_SIZE_LOAD_ELEMENT_VALUE = 0xfff; // 4096
51 const unsigned int Document::MAX_SIZE_PRINT_ELEMENT_VALUE = 0x7fffffff;
52
53 //-----------------------------------------------------------------------------
54 // Constructor / Destructor
55 // Constructors and destructors are protected to avoid user to invoke directly
56 /**
57  * \brief   constructor  
58  * @param   filename 'Document' (File or DicomDir) to be opened for parsing
59  */
60 Document::Document( std::string const &filename ) : ElementSet(-1)
61 {
62    SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE); 
63    Filename = filename;
64    Initialize();
65
66    Fp = 0;
67    if ( !OpenFile() )
68    {
69       return;
70    }
71
72    Group0002Parsed = false;
73
74    gdcmVerboseMacro( "Starting parsing of file: " << Filename.c_str());
75   // Fp->seekg( 0,  std::ios::beg);
76    
77    Fp->seekg(0,  std::ios::end);
78    long lgt = Fp->tellg();
79            
80    Fp->seekg( 0,  std::ios::beg);
81
82    CheckSwap();
83    long beg = Fp->tellg();
84    lgt -= beg;
85    
86    ParseDES( this, beg, lgt, false); // Loading is done during parsing
87
88    Fp->seekg( 0,  std::ios::beg);
89    
90    // Load 'non string' values
91       
92    std::string PhotometricInterpretation = GetEntryValue(0x0028,0x0004);   
93    if( PhotometricInterpretation == "PALETTE COLOR " )
94    {
95       LoadEntryBinArea(0x0028,0x1200);  // gray LUT   
96       /// FIXME FIXME FIXME
97       /// The tags refered by the three following lines used to be CORRECTLY
98       /// defined as having an US Value Representation in the public
99       /// dictionary. BUT the semantics implied by the three following
100       /// lines state that the corresponding tag contents are in fact
101       /// the ones of a BinEntry.
102       /// In order to fix things "Quick and Dirty" the dictionary was
103       /// altered on PURPOSE but now contains a WRONG value.
104       /// In order to fix things and restore the dictionary to its
105       /// correct value, one needs to decided of the semantics by deciding
106       /// whether the following tags are either:
107       /// - multivaluated US, and hence loaded as ValEntry, but afterwards
108       ///   also used as BinEntry, which requires the proper conversion,
109       /// - OW, and hence loaded as BinEntry, but afterwards also used
110       ///   as ValEntry, which requires the proper conversion.
111       LoadEntryBinArea(0x0028,0x1201);  // R    LUT
112       LoadEntryBinArea(0x0028,0x1202);  // G    LUT
113       LoadEntryBinArea(0x0028,0x1203);  // B    LUT
114       
115       // Segmented Red   Palette Color LUT Data
116       LoadEntryBinArea(0x0028,0x1221);
117       // Segmented Green Palette Color LUT Data
118       LoadEntryBinArea(0x0028,0x1222);
119       // Segmented Blue  Palette Color LUT Data
120       LoadEntryBinArea(0x0028,0x1223);
121    } 
122    //FIXME later : how to use it?
123    LoadEntryBinArea(0x0028,0x3006);  //LUT Data (CTX dependent) 
124
125    CloseFile(); 
126   
127    // ----------------------------
128    // Specific code to allow gdcm to read ACR-LibIDO formated images
129    // Note: ACR-LibIDO is an extension of the ACR standard that was
130    //       used at CREATIS. For the time being (say a couple years)
131    //       we keep this kludge to allow a smooth move to gdcm for
132    //       CREATIS developpers (sorry folks).
133    //
134    // if recognition code tells us we deal with a LibIDO image
135    // we switch lineNumber and columnNumber
136    //
137    std::string RecCode;
138    RecCode = GetEntryValue(0x0008, 0x0010); // recognition code (RET)
139    if (RecCode == "ACRNEMA_LIBIDO_1.1" ||
140        RecCode == "CANRME_AILIBOD1_1." )  // for brain-damaged softwares
141                                           // with "little-endian strings"
142    {
143          Filetype = ACR_LIBIDO; 
144          std::string rows    = GetEntryValue(0x0028, 0x0010);
145          std::string columns = GetEntryValue(0x0028, 0x0011);
146          SetValEntry(columns, 0x0028, 0x0010);
147          SetValEntry(rows   , 0x0028, 0x0011);
148    }
149    // --- End of ACR-LibIDO kludge --- 
150 }
151
152 /**
153  * \brief This default constructor doesn't parse the file. You should
154  *        then invoke \ref Document::SetFileName and then the parsing.
155  */
156 Document::Document() : ElementSet(-1)
157 {
158    Fp = 0;
159
160    SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE);
161    Initialize();
162    SwapCode = 1234;
163    Filetype = ExplicitVR;
164    Group0002Parsed = false;
165 }
166
167 /**
168  * \brief   Canonical destructor.
169  */
170 Document::~Document ()
171 {
172    RefPubDict = NULL;
173    RefShaDict = NULL;
174 }
175
176 //-----------------------------------------------------------------------------
177 // Public
178 /**
179  * \brief   Get the public dictionary used
180  */
181 Dict *Document::GetPubDict()
182 {
183    return RefPubDict;
184 }
185
186 /**
187  * \brief   Get the shadow dictionary used
188  */
189 Dict *Document::GetShaDict()
190 {
191    return RefShaDict;
192 }
193
194 /**
195  * \brief   Set the shadow dictionary used
196  * @param   dict dictionary to use in shadow
197  */
198 bool Document::SetShaDict(Dict *dict)
199 {
200    RefShaDict = dict;
201    return !RefShaDict;
202 }
203
204 /**
205  * \brief   Set the shadow dictionary used
206  * @param   dictName name of the dictionary to use in shadow
207  */
208 bool Document::SetShaDict(DictKey const &dictName)
209 {
210    RefShaDict = Global::GetDicts()->GetDict(dictName);
211    return !RefShaDict;
212 }
213
214 /**
215  * \brief  This predicate, based on hopefully reasonable heuristics,
216  *         decides whether or not the current Document was properly parsed
217  *         and contains the mandatory information for being considered as
218  *         a well formed and usable Dicom/Acr File.
219  * @return true when Document is the one of a reasonable Dicom/Acr file,
220  *         false otherwise. 
221  */
222 bool Document::IsReadable()
223 {
224    if( Filetype == Unknown)
225    {
226       gdcmVerboseMacro( "Wrong filetype");
227       return false;
228    }
229
230    if ( IsEmpty() )
231    { 
232       gdcmVerboseMacro( "No tag in internal hash table.");
233       return false;
234    }
235
236    return true;
237 }
238
239 /**
240  * \brief   Predicate for dicom version 3 file.
241  * @return  True when the file is a dicom version 3.
242  */
243 bool Document::IsDicomV3()
244 {
245    // Checking if Transfer Syntax exists is enough
246    // Anyway, it's to late check if the 'Preamble' was found ...
247    // And ... would it be a rich idea to check ?
248    // (some 'no Preamble' DICOM images exist !)
249    return GetDocEntry(0x0002, 0x0010) != NULL;
250 }
251
252 /**
253  * \brief   Predicate for Papyrus file
254  *          Dedicated to whomsoever it may concern
255  * @return  True when the file is a Papyrus file.
256  */
257 bool Document::IsPapyrus()
258 {
259    // check for Papyrus private Sequence
260    DocEntry *e = GetDocEntry(0x0041, 0x1050);
261    if ( !e )
262       return false;
263    // check if it's actually a Sequence
264    if ( !dynamic_cast<SeqEntry*>(e) )
265       return  false;
266    return true;
267 }
268
269 /**
270  * \brief  returns the File Type 
271  *         (ACR, ACR_LIBIDO, ExplicitVR, ImplicitVR, Unknown)
272  * @return the FileType code
273  */
274 FileType Document::GetFileType()
275 {
276    return Filetype;
277 }
278
279 /**
280  * \brief   Accessor to the Transfer Syntax (when present) of the
281  *          current document (it internally handles reading the
282  *          value from disk when only parsing occured).
283  * @return  The encountered Transfer Syntax of the current document.
284  */
285 std::string Document::GetTransferSyntax()
286 {
287    DocEntry *entry = GetDocEntry(0x0002, 0x0010);
288    if ( !entry )
289    {
290       return GDCM_UNKNOWN;
291    }
292
293    // The entry might be present but not loaded (parsing and loading
294    // happen at different stages): try loading and proceed with check...
295    LoadDocEntrySafe(entry);
296    if (ValEntry *valEntry = dynamic_cast< ValEntry* >(entry) )
297    {
298       std::string transfer = valEntry->GetValue();
299       // The actual transfer (as read from disk) might be padded. We
300       // first need to remove the potential padding. We can make the
301       // weak assumption that padding was not executed with digits...
302       if  ( transfer.length() == 0 )
303       {
304          // for brain damaged headers
305          return GDCM_UNKNOWN;
306       }
307       while ( !isdigit((unsigned char)transfer[transfer.length()-1]) )
308       {
309          transfer.erase(transfer.length()-1, 1);
310       }
311       return transfer;
312    }
313    return GDCM_UNKNOWN;
314 }
315
316 /**
317  * \brief Accesses the info from 0002,0010 : Transfer Syntax and TS
318  * @return The full Transfer Syntax Name (as opposed to Transfer Syntax UID)
319  */
320 std::string Document::GetTransferSyntaxName()
321 {
322    // use the TS (TS : Transfer Syntax)
323    std::string transferSyntax = GetEntryValue(0x0002,0x0010);
324
325    if ( (transferSyntax.find(GDCM_NOTLOADED) < transferSyntax.length()) )
326    {
327       gdcmErrorMacro( "Transfer Syntax not loaded. " << std::endl
328                << "Better you increase MAX_SIZE_LOAD_ELEMENT_VALUE" );
329       return "Uncompressed ACR-NEMA";
330    }
331    if ( transferSyntax == GDCM_UNFOUND )
332    {
333       gdcmVerboseMacro( "Unfound Transfer Syntax (0002,0010)");
334       return "Uncompressed ACR-NEMA";
335    }
336
337    // we do it only when we need it
338    const TSKey &tsName = Global::GetTS()->GetValue( transferSyntax );
339
340    // Global::GetTS() is a global static you shall never try to delete it!
341    return tsName;
342 }
343 //
344 // --------------- Swap Code ------------------
345 /**
346  * \brief   Swaps the bytes so they agree with the processor order
347  * @return  The properly swaped 16 bits integer.
348  */
349 uint16_t Document::SwapShort(uint16_t a)
350 {
351    if ( SwapCode == 4321 || SwapCode == 2143 )
352    {
353       a = ((( a << 8 ) & 0x0ff00 ) | (( a >> 8 ) & 0x00ff ) );
354    }
355    return a;
356 }
357
358 /**
359  * \brief   Swaps back the bytes of 4-byte long integer accordingly to
360  *          processor order.
361  * @return  The properly swaped 32 bits integer.
362  */
363 uint32_t Document::SwapLong(uint32_t a)
364 {
365    switch (SwapCode)
366    {
367       case 1234 :
368          break;
369       case 4321 :
370          a=( ((a<<24) & 0xff000000) | ((a<<8)  & 0x00ff0000) | 
371              ((a>>8)  & 0x0000ff00) | ((a>>24) & 0x000000ff) );
372          break;   
373       case 3412 :
374          a=( ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
375          break;  
376       case 2143 :
377          a=( ((a<< 8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff)  );
378       break;
379       default :
380          gdcmErrorMacro( "Unset swap code:" << SwapCode );
381          a = 0;
382    }
383    return a;
384
385
386 //
387 // -----------------File I/O ---------------
388 /**
389  * \brief  Tries to open the file \ref Document::Filename and
390  *         checks the preamble when existing.
391  * @return The FILE pointer on success. 
392  */
393 std::ifstream *Document::OpenFile()
394 {
395    HasDCMPreamble = false;
396    if (Filename.length() == 0) 
397    {
398       return 0;
399    }
400
401    if(Fp)
402    {
403       gdcmVerboseMacro( "File already open: " << Filename.c_str());
404       CloseFile();
405    }
406
407    Fp = new std::ifstream(Filename.c_str(), std::ios::in | std::ios::binary);
408    if( ! *Fp )
409    {
410       gdcmDebugMacro( "Cannot open file: " << Filename.c_str());
411       delete Fp;
412       Fp = 0;
413       return 0;
414    }
415  
416    uint16_t zero;
417    Fp->read((char*)&zero, (size_t)2);
418    if( Fp->eof() )
419    {
420       CloseFile();
421       return 0;
422    }
423  
424    //ACR -- or DICOM with no Preamble; may start with a Shadow Group --
425    if( 
426        zero == 0x0001 || zero == 0x0100 || zero == 0x0002 || zero == 0x0200 ||
427        zero == 0x0003 || zero == 0x0300 || zero == 0x0004 || zero == 0x0400 ||
428        zero == 0x0005 || zero == 0x0500 || zero == 0x0006 || zero == 0x0600 ||
429        zero == 0x0007 || zero == 0x0700 || zero == 0x0008 || zero == 0x0800 )
430    {
431       std::string msg 
432          = Util::Format("ACR/DICOM with no preamble: (%04x)\n", zero);
433       gdcmVerboseMacro( msg.c_str() );
434       return Fp;
435    }
436  
437    //DICOM
438    Fp->seekg(126L, std::ios::cur);
439    char dicm[4];
440    Fp->read(dicm,  (size_t)4);
441    if( Fp->eof() )
442    {
443       CloseFile();
444       return 0;
445    }
446    if( memcmp(dicm, "DICM", 4) == 0 )
447    {
448       HasDCMPreamble = true;
449       return Fp;
450    }
451  
452    CloseFile();
453    gdcmVerboseMacro( "Not DICOM/ACR (missing preamble)" << Filename.c_str());
454  
455    return 0;
456 }
457
458 /**
459  * \brief closes the file  
460  * @return  TRUE if the close was successfull 
461  */
462 bool Document::CloseFile()
463 {
464    if( Fp )
465    {
466       Fp->close();
467       delete Fp;
468       Fp = 0;
469    }
470    return true; //FIXME how do we detect a non-closed ifstream ?
471 }
472
473 /**
474  * \brief Writes in a file all the Header Entries (Dicom Elements) 
475  * @param fp file pointer on an already open file (actually: Output File Stream)
476  * @param filetype Type of the File to be written 
477  *          (ACR-NEMA, ExplicitVR, ImplicitVR)
478  * @return Always true.
479  */
480 void Document::WriteContent(std::ofstream *fp, FileType filetype)
481 {
482    // \TODO move the following lines (and a lot of others, to be written)
483    // to a future function CheckAndCorrectHeader  
484
485    // (necessary if user wants to write a DICOM V3 file
486    // starting from an ACR-NEMA (V2) Header
487
488    if ( filetype == ImplicitVR || filetype == ExplicitVR )
489    {
490       // writing Dicom File Preamble
491       char filePreamble[128];
492       memset(filePreamble, 0, 128);
493       fp->write(filePreamble, 128);
494       fp->write("DICM", 4);
495    }
496
497 /*
498  * \todo rewrite later, if really usefull
499  *       - 'Group Length' element is optional in DICOM
500  *       - but un-updated odd groups lengthes can causes pb
501  *         (xmedcon breaker)
502  *
503  * if ( (filetype == ImplicitVR) || (filetype == ExplicitVR) )
504  *    UpdateGroupLength(false,filetype);
505  * if ( filetype == ACR)
506  *    UpdateGroupLength(true,ACR);
507  */
508  
509    ElementSet::WriteContent(fp, filetype); // This one is recursive
510 }
511
512 // -----------------------------------------
513 // Content entries 
514 /**
515  * \brief Loads (from disk) the element content 
516  *        when a string is not suitable
517  * @param group   group number of the Entry 
518  * @param elem  element number of the Entry
519  */
520 void Document::LoadEntryBinArea(uint16_t group, uint16_t elem)
521 {
522    // Search the corresponding DocEntry
523    DocEntry *docElement = GetDocEntry(group, elem);
524    if ( !docElement )
525       return;
526
527    BinEntry *binElement = dynamic_cast<BinEntry *>(docElement);
528    if( !binElement )
529       return;
530
531    LoadEntryBinArea(binElement);
532 }
533
534 /**
535  * \brief Loads (from disk) the element content 
536  *        when a string is not suitable
537  * @param elem  Entry whose binArea is going to be loaded
538  */
539 void Document::LoadEntryBinArea(BinEntry *elem) 
540 {
541    if(elem->GetBinArea())
542       return;
543
544    bool openFile = !Fp;
545    if(openFile)
546       OpenFile();
547
548    size_t o =(size_t)elem->GetOffset();
549    Fp->seekg(o, std::ios::beg);
550
551    size_t l = elem->GetLength();
552    uint8_t *a = new uint8_t[l];
553    if( !a )
554    {
555       gdcmVerboseMacro( "Cannot allocate BinEntry content");
556       return;
557    }
558
559    /// \todo check the result 
560    Fp->read((char*)a, l);
561    if( Fp->fail() || Fp->eof())
562    {
563       delete[] a;
564       return;
565    }
566
567    elem->SetBinArea(a);
568
569    if(openFile)
570       CloseFile();
571 }
572
573 /**
574  * \brief  Loads the element while preserving the current
575  *         underlying file position indicator as opposed to
576  *        LoadDocEntry that modifies it.
577  * @param entry   Header Entry whose value will be loaded. 
578  * @return  
579  */
580 void Document::LoadDocEntrySafe(DocEntry *entry)
581 {
582    if(Fp)
583    {
584       long PositionOnEntry = Fp->tellg();
585       LoadDocEntry(entry);
586       Fp->seekg(PositionOnEntry, std::ios::beg);
587    }
588 }
589
590 //-----------------------------------------------------------------------------
591 // Protected
592 /**
593  * \brief Reads a supposed to be 16 Bits integer
594  *       (swaps it depending on processor endianity) 
595  * @return read value
596  */
597 uint16_t Document::ReadInt16()
598    throw( FormatError )
599 {
600    uint16_t g;
601    Fp->read ((char*)&g, (size_t)2);
602    if ( Fp->fail() )
603    {
604       throw FormatError( "Document::ReadInt16()", " file error." );
605    }
606    if( Fp->eof() )
607    {
608       throw FormatError( "Document::ReadInt16()", "EOF." );
609    }
610    g = SwapShort(g); 
611    return g;
612 }
613
614 /**
615  * \brief  Reads a supposed to be 32 Bits integer
616  *         (swaps it depending on processor endianity)  
617  * @return read value
618  */
619 uint32_t Document::ReadInt32()
620    throw( FormatError )
621 {
622    uint32_t g;
623    Fp->read ((char*)&g, (size_t)4);
624    if ( Fp->fail() )
625    {
626       throw FormatError( "Document::ReadInt32()", " file error." );
627    }
628    if( Fp->eof() )
629    {
630       throw FormatError( "Document::ReadInt32()", "EOF." );
631    }
632    g = SwapLong(g);
633    return g;
634 }
635
636 /**
637  * \brief skips bytes inside the source file 
638  * \warning NOT end user intended method !
639  * @return 
640  */
641 void Document::SkipBytes(uint32_t nBytes)
642 {
643    //FIXME don't dump the returned value
644    Fp->seekg((long)nBytes, std::ios::cur);
645 }
646
647 /**
648  * \brief   Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader
649  * @param filetype Type of the File to be written 
650  */
651 int Document::ComputeGroup0002Length( FileType filetype ) 
652 {
653    uint16_t gr;
654    std::string vr;
655    
656    int groupLength = 0;
657    bool found0002 = false;   
658   
659    // for each zero-level Tag in the DCM Header
660    DocEntry *entry = GetFirstEntry();
661    while( entry )
662    {
663       gr = entry->GetGroup();
664
665       if( gr == 0x0002 )
666       {
667          found0002 = true;
668
669          if( entry->GetElement() != 0x0000 )
670          {
671             vr = entry->GetVR();
672  
673             if( filetype == ExplicitVR )
674             {
675                if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) 
676                {
677                   // explicit VR AND OB, OW, SQ : 4 more bytes
678                   groupLength +=  4;
679                }
680             }
681             groupLength += 2 + 2 + 4 + entry->GetLength();   
682          }
683       }
684       else if (found0002 )
685          break;
686
687       entry = GetNextEntry();
688    }
689    return groupLength; 
690 }
691
692 //-----------------------------------------------------------------------------
693 // Private
694 /**
695  * \brief   Parses a DocEntrySet (Zero-level DocEntries or SQ Item DocEntries)
696  * @return  length of the parsed set. 
697  */ 
698 void Document::ParseDES(DocEntrySet *set, long offset, 
699                         long l_max, bool delim_mode)
700 {
701    DocEntry *newDocEntry = 0;
702    ValEntry *newValEntry;
703    BinEntry *newBinEntry;
704    SeqEntry *newSeqEntry;
705    VRKey vr;
706    bool used = false;
707
708    while (true)
709    {
710       if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max)
711       {
712          break;
713       }
714
715       used = true;
716       newDocEntry = ReadNextDocEntry( );
717
718       if ( !newDocEntry )
719       {
720          break;
721       }
722
723       vr = newDocEntry->GetVR();
724       newValEntry = dynamic_cast<ValEntry*>(newDocEntry);
725       newBinEntry = dynamic_cast<BinEntry*>(newDocEntry);
726       newSeqEntry = dynamic_cast<SeqEntry*>(newDocEntry);
727
728       if ( newValEntry || newBinEntry )
729       {
730          if ( newBinEntry )
731          {
732             if ( Filetype == ExplicitVR && 
733                  !Global::GetVR()->IsVROfBinaryRepresentable(vr) )
734             { 
735                 ////// Neither ValEntry NOR BinEntry: should mean UNKOWN VR
736                 gdcmVerboseMacro( std::hex << newDocEntry->GetGroup() 
737                                   << "|" << newDocEntry->GetElement()
738                                   << " : Neither Valentry, nor BinEntry." 
739                                   "Probably unknown VR.");
740             }
741
742          //////////////////// BinEntry or UNKOWN VR:
743             // When "this" is a Document the Key is simply of the
744             // form ( group, elem )...
745             if ( dynamic_cast< Document* > ( set ) )
746             {
747                newBinEntry->SetKey( newBinEntry->GetKey() );
748             }
749             // but when "this" is a SQItem, we are inserting this new
750             // valEntry in a sequence item, and the key has the
751             // generalized form (refer to \ref BaseTagKey):
752             if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) )
753             {
754                newBinEntry->SetKey(  parentSQItem->GetBaseTagKey()
755                                    + newBinEntry->GetKey() );
756             }
757
758             LoadDocEntry( newBinEntry );
759             if( !set->AddEntry( newBinEntry ) )
760             {
761               //Expect big troubles if here
762               //delete newBinEntry;
763               used=false;
764             }
765          }
766          else
767          {
768          /////////////////////// ValEntry
769             // When "set" is a Document, then we are at the top of the
770             // hierarchy and the Key is simply of the form ( group, elem )...
771             if ( dynamic_cast< Document* > ( set ) )
772             {
773                newValEntry->SetKey( newValEntry->GetKey() );
774             }
775             // ...but when "set" is a SQItem, we are inserting this new
776             // valEntry in a sequence item. Hence the key has the
777             // generalized form (refer to \ref BaseTagKey):
778             if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) )
779             {
780                newValEntry->SetKey(  parentSQItem->GetBaseTagKey()
781                                    + newValEntry->GetKey() );
782             }
783              
784             LoadDocEntry( newValEntry );
785             bool delimitor=newValEntry->IsItemDelimitor();
786             if( !set->AddEntry( newValEntry ) )
787             {
788               // If here expect big troubles
789               //delete newValEntry; //otherwise mem leak
790               used=false;
791             }
792
793             if (delimitor)
794             {
795                if(!used)
796                   delete newDocEntry;
797                break;
798             }
799             if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max)
800             {
801                if(!used)
802                   delete newDocEntry;
803                break;
804             }
805          }
806
807          // Just to make sure we are at the beginning of next entry.
808          SkipToNextDocEntry(newDocEntry);
809       }
810       else
811       {
812          // VR = "SQ"
813          unsigned long l = newDocEntry->GetReadLength();            
814          if ( l != 0 ) // don't mess the delim_mode for zero-length sequence
815          {
816             if ( l == 0xffffffff )
817             {
818               delim_mode = true;
819             }
820             else
821             {
822               delim_mode = false;
823             }
824          }
825          // no other way to create it ...
826          newSeqEntry->SetDelimitorMode( delim_mode );
827
828          // At the top of the hierarchy, stands a Document. When "set"
829          // is a Document, then we are building the first depth level.
830          // Hence the SeqEntry we are building simply has a depth
831          // level of one:
832          if (/*Document *dummy =*/ dynamic_cast< Document* > ( set ) )
833          {
834             //(void)dummy;
835             newSeqEntry->SetDepthLevel( 1 );
836             newSeqEntry->SetKey( newSeqEntry->GetKey() );
837          }
838          // But when "set" is already a SQItem, we are building a nested
839          // sequence, and hence the depth level of the new SeqEntry
840          // we are building, is one level deeper:
841          if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) )
842          {
843             newSeqEntry->SetDepthLevel( parentSQItem->GetDepthLevel() + 1 );
844             newSeqEntry->SetKey(  parentSQItem->GetBaseTagKey()
845                                 + newSeqEntry->GetKey() );
846          }
847
848          if ( l != 0 )
849          {  // Don't try to parse zero-length sequences
850             ParseSQ( newSeqEntry, 
851                      newDocEntry->GetOffset(),
852                      l, delim_mode);
853          }
854          if( !set->AddEntry( newSeqEntry ) )
855          {
856             used = false;
857          }
858          if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max)
859          {
860             if( !used )
861                delete newDocEntry;
862             break;
863          }
864       }
865
866       if( !used )
867          delete newDocEntry;
868    }
869 }
870
871 /**
872  * \brief   Parses a Sequence ( SeqEntry after SeqEntry)
873  * @return  parsed length for this level
874  */ 
875 void Document::ParseSQ( SeqEntry *seqEntry,
876                         long offset, long l_max, bool delim_mode)
877 {
878    int SQItemNumber = 0;
879    bool dlm_mod;
880    long offsetStartCurrentSQItem = offset;
881
882    while (true)
883    {
884       // the first time, we read the fff0,e000 of the first SQItem
885       DocEntry *newDocEntry = ReadNextDocEntry();
886
887       if ( !newDocEntry )
888       {
889          // FIXME Should warn user
890          break;
891       }
892       if( delim_mode )
893       {
894          if ( newDocEntry->IsSequenceDelimitor() )
895          {
896             seqEntry->SetDelimitationItem( newDocEntry ); 
897             break;
898          }
899       }
900       if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max)
901       {
902          delete newDocEntry;
903          break;
904       }
905       // create the current SQItem
906       SQItem *itemSQ = new SQItem( seqEntry->GetDepthLevel() );
907       std::ostringstream newBase;
908       newBase << seqEntry->GetKey()
909               << "/"
910               << SQItemNumber
911               << "#";
912       itemSQ->SetBaseTagKey( newBase.str() );
913       unsigned int l = newDocEntry->GetReadLength();
914       
915       if ( l == 0xffffffff )
916       {
917          dlm_mod = true;
918       }
919       else
920       {
921          dlm_mod = false;
922       }
923       // FIXME, TODO
924       // when we're here, element fffe,e000 is already passed.
925       // it's lost for the SQItem we're going to process !!
926
927       //ParseDES(itemSQ, newDocEntry->GetOffset(), l, dlm_mod);
928       //delete newDocEntry; // FIXME well ... it's too late to use it !
929
930       // Let's try :------------
931       // remove fff0,e000, created out of the SQItem
932       delete newDocEntry;
933       Fp->seekg(offsetStartCurrentSQItem, std::ios::beg);
934       // fill up the current SQItem, starting at the beginning of fff0,e000
935       ParseDES(itemSQ, offsetStartCurrentSQItem, l+8, dlm_mod);
936       offsetStartCurrentSQItem = Fp->tellg();
937       // end try -----------------
938  
939       seqEntry->AddSQItem( itemSQ, SQItemNumber ); 
940       SQItemNumber++;
941       if ( !delim_mode && ((long)(Fp->tellg())-offset ) >= l_max )
942       {
943          break;
944       }
945    }
946 }
947
948 /**
949  * \brief   Loads the element content if its length doesn't exceed
950  *          the value specified with Document::SetMaxSizeLoadEntry()
951  * @param   entry Header Entry (Dicom Element) to be dealt with
952  */
953 void Document::LoadDocEntry(DocEntry *entry)
954 {
955    uint16_t group  = entry->GetGroup();
956    std::string  vr = entry->GetVR();
957    uint32_t length = entry->GetLength();
958
959    Fp->seekg((long)entry->GetOffset(), std::ios::beg);
960
961    // A SeQuence "contains" a set of Elements.  
962    //          (fffe e000) tells us an Element is beginning
963    //          (fffe e00d) tells us an Element just ended
964    //          (fffe e0dd) tells us the current SeQuence just ended
965    if( group == 0xfffe )
966    {
967       // NO more value field for SQ !
968       return;
969    }
970
971    // When the length is zero things are easy:
972    if ( length == 0 )
973    {
974       ((ValEntry *)entry)->SetValue("");
975       return;
976    }
977
978    // The elements whose length is bigger than the specified upper bound
979    // are not loaded. Instead we leave a short notice of the offset of
980    // the element content and it's length.
981
982    std::ostringstream s;
983    if (length > MaxSizeLoadEntry)
984    {
985       if (BinEntry *binEntryPtr = dynamic_cast< BinEntry* >(entry) )
986       {  
987          //s << "gdcm::NotLoaded (BinEntry)";
988          s << GDCM_NOTLOADED;
989          s << " Address:" << (long)entry->GetOffset();
990          s << " Length:"  << entry->GetLength();
991          s << " x(" << std::hex << entry->GetLength() << ")";
992          binEntryPtr->SetValue(s.str());
993       }
994       // Be carefull : a BinEntry IS_A ValEntry ... 
995       else if (ValEntry *valEntryPtr = dynamic_cast< ValEntry* >(entry) )
996       {
997         // s << "gdcm::NotLoaded. (ValEntry)";
998          s << GDCM_NOTLOADED;  
999          s << " Address:" << (long)entry->GetOffset();
1000          s << " Length:"  << entry->GetLength();
1001          s << " x(" << std::hex << entry->GetLength() << ")";
1002          valEntryPtr->SetValue(s.str());
1003       }
1004       else
1005       {
1006          // fusible
1007          gdcmErrorMacro( "MaxSizeLoadEntry exceeded, neither a BinEntry "
1008                       << "nor a ValEntry ?! Should never print that !" );
1009       }
1010
1011       // to be sure we are at the end of the value ...
1012       Fp->seekg((long)entry->GetOffset()+(long)entry->GetLength(),
1013                 std::ios::beg);
1014       return;
1015    }
1016
1017    // When we find a BinEntry not very much can be done :
1018    if (BinEntry *binEntryPtr = dynamic_cast< BinEntry* >(entry) )
1019    {
1020       s << GDCM_BINLOADED;
1021       binEntryPtr->SetValue(s.str());
1022       LoadEntryBinArea(binEntryPtr); // last one, not to erase length !
1023       return;
1024    }
1025
1026    /// \todo Any compacter code suggested (?)
1027    if ( IsDocEntryAnInteger(entry) )
1028    {   
1029       uint32_t NewInt;
1030       int nbInt;
1031       // When short integer(s) are expected, read and convert the following 
1032       // n *two characters properly i.e. consider them as short integers as
1033       // opposed to strings.
1034       // Elements with Value Multiplicity > 1
1035       // contain a set of integers (not a single one)       
1036       if (vr == "US" || vr == "SS")
1037       {
1038          nbInt = length / 2;
1039          NewInt = ReadInt16();
1040          s << NewInt;
1041          if (nbInt > 1)
1042          {
1043             for (int i=1; i < nbInt; i++)
1044             {
1045                s << '\\';
1046                NewInt = ReadInt16();
1047                s << NewInt;
1048             }
1049          }
1050       }
1051       // See above comment on multiple integers (mutatis mutandis).
1052       else if (vr == "UL" || vr == "SL")
1053       {
1054          nbInt = length / 4;
1055          NewInt = ReadInt32();
1056          s << NewInt;
1057          if (nbInt > 1)
1058          {
1059             for (int i=1; i < nbInt; i++)
1060             {
1061                s << '\\';
1062                NewInt = ReadInt32();
1063                s << NewInt;
1064             }
1065          }
1066       }
1067 #ifdef GDCM_NO_ANSI_STRING_STREAM
1068       s << std::ends; // to avoid oddities on Solaris
1069 #endif //GDCM_NO_ANSI_STRING_STREAM
1070
1071       ((ValEntry *)entry)->SetValue(s.str());
1072       return;
1073    }
1074    
1075   // FIXME: We need an additional byte for storing \0 that is not on disk
1076    char *str = new char[length+1];
1077    Fp->read(str, (size_t)length);
1078    str[length] = '\0'; //this is only useful when length is odd
1079    // Special DicomString call to properly handle \0 and even length
1080    std::string newValue;
1081    if( length % 2 )
1082    {
1083       newValue = Util::DicomString(str, length+1);
1084       gdcmVerboseMacro("Warning: bad length: " << length <<
1085                        ",For string :" <<  newValue.c_str()); 
1086       // Since we change the length of string update it length
1087       //entry->SetReadLength(length+1);
1088    }
1089    else
1090    {
1091       newValue = Util::DicomString(str, length);
1092    }
1093    delete[] str;
1094
1095    if ( ValEntry *valEntry = dynamic_cast<ValEntry* >(entry) )
1096    {
1097       if ( Fp->fail() || Fp->eof())
1098       {
1099          gdcmVerboseMacro("Unread element value");
1100          valEntry->SetValue(GDCM_UNREAD);
1101          return;
1102       }
1103
1104       if( vr == "UI" )
1105       {
1106          // Because of correspondance with the VR dic
1107          valEntry->SetValue(newValue);
1108       }
1109       else
1110       {
1111          valEntry->SetValue(newValue);
1112       }
1113    }
1114    else
1115    {
1116       gdcmErrorMacro( "Should have a ValEntry, here !");
1117    }
1118 }
1119
1120 /**
1121  * \brief  Find the value Length of the passed Header Entry
1122  * @param  entry Header Entry whose length of the value shall be loaded. 
1123  */
1124 void Document::FindDocEntryLength( DocEntry *entry )
1125    throw ( FormatError )
1126 {
1127    std::string  vr  = entry->GetVR();
1128    uint16_t length16;       
1129    
1130    if ( Filetype == ExplicitVR && !entry->IsImplicitVR() ) 
1131    {
1132       if ( vr == "OB" || vr == "OW" || vr == "SQ" || vr == "UN" ) 
1133       {
1134          // The following reserved two bytes (see PS 3.5-2003, section
1135          // "7.1.2 Data element structure with explicit vr", p 27) must be
1136          // skipped before proceeding on reading the length on 4 bytes.
1137          Fp->seekg( 2L, std::ios::cur);
1138          uint32_t length32 = ReadInt32();
1139
1140          if ( (vr == "OB" || vr == "OW") && length32 == 0xffffffff ) 
1141          {
1142             uint32_t lengthOB;
1143             try 
1144             {
1145                lengthOB = FindDocEntryLengthOBOrOW();
1146             }
1147             catch ( FormatUnexpected )
1148             {
1149                // Computing the length failed (this happens with broken
1150                // files like gdcm-JPEG-LossLess3a.dcm). We still have a
1151                // chance to get the pixels by deciding the element goes
1152                // until the end of the file. Hence we artificially fix the
1153                // the length and proceed.
1154                long currentPosition = Fp->tellg();
1155                Fp->seekg(0L,std::ios::end);
1156
1157                long lengthUntilEOF = (long)(Fp->tellg())-currentPosition;
1158                Fp->seekg(currentPosition, std::ios::beg);
1159
1160                entry->SetReadLength(lengthUntilEOF);
1161                entry->SetLength(lengthUntilEOF);
1162                return;
1163             }
1164             entry->SetReadLength(lengthOB);
1165             entry->SetLength(lengthOB);
1166             return;
1167          }
1168          FixDocEntryFoundLength(entry, length32); 
1169          return;
1170       }
1171
1172       // Length is encoded on 2 bytes.
1173       length16 = ReadInt16();
1174
1175       // FIXME : This heuristic supposes that the first group following
1176       //         group 0002 *has* and element 0000.
1177       // BUT ... Element 0000 is optionnal :-(
1178
1179
1180    // Fixed using : HandleOutOfGroup0002()
1181    //              (first hereafter strategy ...)
1182       
1183       // We can tell the current file is encoded in big endian (like
1184       // Data/US-RGB-8-epicard) when we find the "Transfer Syntax" tag
1185       // and it's value is the one of the encoding of a big endian file.
1186       // In order to deal with such big endian encoded files, we have
1187       // (at least) two strategies:
1188       // * when we load the "Transfer Syntax" tag with value of big endian
1189       //   encoding, we raise the proper flags. Then we wait for the end
1190       //   of the META group (0x0002) among which is "Transfer Syntax",
1191       //   before switching the swap code to big endian. We have to postpone
1192       //   the switching of the swap code since the META group is fully encoded
1193       //   in little endian, and big endian coding only starts at the next
1194       //   group. The corresponding code can be hard to analyse and adds
1195       //   many additional unnecessary tests for regular tags.
1196       // * the second strategy consists in waiting for trouble, that shall
1197       //   appear when we find the first group with big endian encoding. This
1198       //   is easy to detect since the length of a "Group Length" tag (the
1199       //   ones with zero as element number) has to be of 4 (0x0004). When we
1200       //   encounter 1024 (0x0400) chances are the encoding changed and we
1201       //   found a group with big endian encoding.
1202       //---> Unfortunately, element 0000 is optional.
1203       //---> This will not work when missing!
1204       // We shall use this second strategy. In order to make sure that we
1205       // can interpret the presence of an apparently big endian encoded
1206       // length of a "Group Length" without committing a big mistake, we
1207       // add an additional check: we look in the already parsed elements
1208       // for the presence of a "Transfer Syntax" whose value has to be "big
1209       // endian encoding". When this is the case, chances are we have got our
1210       // hands on a big endian encoded file: we switch the swap code to
1211       // big endian and proceed...
1212
1213 //      if ( element  == 0x0000 && length16 == 0x0400 ) 
1214 //      {
1215 //         std::string ts = GetTransferSyntax();
1216 //         if ( Global::GetTS()->GetSpecialTransferSyntax(ts) 
1217 //                != TS::ExplicitVRBigEndian ) 
1218 //         {
1219 //            throw FormatError( "Document::FindDocEntryLength()",
1220 //                               " not explicit VR." );
1221 //           return;
1222 //        }
1223 //        length16 = 4;
1224 //        SwitchByteSwapCode();
1225 //
1226 //         // Restore the unproperly loaded values i.e. the group, the element
1227 //         // and the dictionary entry depending on them.
1228 //         uint16_t correctGroup = SwapShort( entry->GetGroup() );
1229 //         uint16_t correctElem  = SwapShort( entry->GetElement() );
1230 //         DictEntry *newTag = GetDictEntry( correctGroup, correctElem );
1231 //         if ( !newTag )
1232 //         {
1233 //            // This correct tag is not in the dictionary. Create a new one.
1234 //            newTag = NewVirtualDictEntry(correctGroup, correctElem);
1235 //         }
1236 //         // FIXME this can create a memory leaks on the old entry that be
1237 //         // left unreferenced.
1238 //         entry->SetDictEntry( newTag );
1239 //      }
1240   
1241       // 0xffff means that we deal with 'No Length' Sequence 
1242       //        or 'No Length' SQItem
1243       if ( length16 == 0xffff) 
1244       {           
1245          length16 = 0;
1246       }
1247       FixDocEntryFoundLength( entry, (uint32_t)length16 );
1248       return;
1249    }
1250    else
1251    {
1252       // Either implicit VR or a non DICOM conformal (see note below) explicit
1253       // VR that ommited the VR of (at least) this element. Farts happen.
1254       // [Note: according to the part 5, PS 3.5-2001, section 7.1 p25
1255       // on Data elements "Implicit and Explicit VR Data Elements shall
1256       // not coexist in a Data Set and Data Sets nested within it".]
1257       // Length is on 4 bytes.
1258
1259      // Well ... group 0002 is always coded in 'Explicit VR Litle Endian'
1260      // even if Transfer Syntax is 'Implicit VR ...' 
1261       
1262       FixDocEntryFoundLength( entry, ReadInt32() );
1263       return;
1264    }
1265 }
1266
1267 /**
1268  * \brief  Find the Length till the next sequence delimiter
1269  * \warning NOT end user intended method !
1270  * @return 
1271  */
1272 uint32_t Document::FindDocEntryLengthOBOrOW()
1273    throw( FormatUnexpected )
1274 {
1275    // See PS 3.5-2001, section A.4 p. 49 on encapsulation of encoded pixel data.
1276    long positionOnEntry = Fp->tellg();
1277    bool foundSequenceDelimiter = false;
1278    uint32_t totalLength = 0;
1279
1280    while ( !foundSequenceDelimiter )
1281    {
1282       uint16_t group;
1283       uint16_t elem;
1284       try
1285       {
1286          group = ReadInt16();
1287          elem  = ReadInt16();   
1288       }
1289       catch ( FormatError )
1290       {
1291          throw FormatError("Unexpected end of file encountered during ",
1292                            "Document::FindDocEntryLengthOBOrOW()");
1293       }
1294       // We have to decount the group and element we just read
1295       totalLength += 4;     
1296       if ( group != 0xfffe || ( ( elem != 0xe0dd ) && ( elem != 0xe000 ) ) )
1297       {
1298          long filePosition = Fp->tellg();
1299          gdcmVerboseMacro( "Neither an Item tag nor a Sequence delimiter tag on :" 
1300            << std::hex << group << " , " << elem 
1301            << ") -before- position x(" << filePosition << ")" );
1302   
1303          Fp->seekg(positionOnEntry, std::ios::beg);
1304          throw FormatUnexpected( "Neither an Item tag nor a Sequence delimiter tag.");
1305       }
1306       if ( elem == 0xe0dd )
1307       {
1308          foundSequenceDelimiter = true;
1309       }
1310       uint32_t itemLength = ReadInt32();
1311       // We add 4 bytes since we just read the ItemLength with ReadInt32
1312       totalLength += itemLength + 4;
1313       SkipBytes(itemLength);
1314       
1315       if ( foundSequenceDelimiter )
1316       {
1317          break;
1318       }
1319    }
1320    Fp->seekg( positionOnEntry, std::ios::beg);
1321    return totalLength;
1322 }
1323
1324 /**
1325  * \brief     Find the Value Representation of the current Dicom Element.
1326  * @return    Value Representation of the current Entry
1327  */
1328 std::string Document::FindDocEntryVR()
1329 {
1330    if ( Filetype != ExplicitVR )
1331       return GDCM_UNKNOWN;
1332
1333    long positionOnEntry = Fp->tellg();
1334    // Warning: we believe this is explicit VR (Value Representation) because
1335    // we used a heuristic that found "UL" in the first tag. Alas this
1336    // doesn't guarantee that all the tags will be in explicit VR. In some
1337    // cases (see e-film filtered files) one finds implicit VR tags mixed
1338    // within an explicit VR file. Hence we make sure the present tag
1339    // is in explicit VR and try to fix things if it happens not to be
1340    // the case.
1341
1342    char vr[3];
1343    Fp->read (vr, (size_t)2);
1344    vr[2] = 0;
1345
1346    if( !CheckDocEntryVR(vr) )
1347    {
1348       Fp->seekg(positionOnEntry, std::ios::beg);
1349       return GDCM_UNKNOWN;
1350    }
1351    return vr;
1352 }
1353
1354 /**
1355  * \brief     Check the correspondance between the VR of the header entry
1356  *            and the taken VR. If they are different, the header entry is 
1357  *            updated with the new VR.
1358  * @param     vr    Dicom Value Representation
1359  * @return    false if the VR is incorrect of if the VR isn't referenced
1360  *            otherwise, it returns true
1361 */
1362 bool Document::CheckDocEntryVR(VRKey vr)
1363 {
1364    // CLEANME searching the dicom_vr at each occurence is expensive.
1365    // PostPone this test in an optional integrity check at the end
1366    // of parsing or only in debug mode.
1367    if ( !Global::GetVR()->IsValidVR(vr) )
1368       return false;
1369
1370    return true; 
1371 }
1372
1373 /**
1374  * \brief   Get the transformed value of the header entry. The VR value 
1375  *          is used to define the transformation to operate on the value
1376  * \warning NOT end user intended method !
1377  * @param   entry entry to tranform
1378  * @return  Transformed entry value
1379  */
1380 std::string Document::GetDocEntryValue(DocEntry *entry)
1381 {
1382    if ( IsDocEntryAnInteger(entry) && entry->IsImplicitVR() )
1383    {
1384       std::string val = ((ValEntry *)entry)->GetValue();
1385       std::string vr  = entry->GetVR();
1386       uint32_t length = entry->GetLength();
1387       std::ostringstream s;
1388       int nbInt;
1389
1390       // When short integer(s) are expected, read and convert the following 
1391       // n * 2 bytes properly i.e. as a multivaluated strings
1392       // (each single value is separated fromthe next one by '\'
1393       // as usual for standard multivaluated filels
1394       // Elements with Value Multiplicity > 1
1395       // contain a set of short integers (not a single one) 
1396    
1397       if( vr == "US" || vr == "SS" )
1398       {
1399          uint16_t newInt16;
1400
1401          nbInt = length / 2;
1402          for (int i=0; i < nbInt; i++) 
1403          {
1404             if( i != 0 )
1405             {
1406                s << '\\';
1407             }
1408             newInt16 = ( val[2*i+0] & 0xFF ) + ( ( val[2*i+1] & 0xFF ) << 8);
1409             newInt16 = SwapShort( newInt16 );
1410             s << newInt16;
1411          }
1412       }
1413
1414       // When integer(s) are expected, read and convert the following 
1415       // n * 4 bytes properly i.e. as a multivaluated strings
1416       // (each single value is separated fromthe next one by '\'
1417       // as usual for standard multivaluated filels
1418       // Elements with Value Multiplicity > 1
1419       // contain a set of integers (not a single one) 
1420       else if( vr == "UL" || vr == "SL" )
1421       {
1422          uint32_t newInt32;
1423
1424          nbInt = length / 4;
1425          for (int i=0; i < nbInt; i++) 
1426          {
1427             if( i != 0)
1428             {
1429                s << '\\';
1430             }
1431             newInt32 = ( val[4*i+0] & 0xFF )
1432                     + (( val[4*i+1] & 0xFF ) <<  8 )
1433                     + (( val[4*i+2] & 0xFF ) << 16 )
1434                     + (( val[4*i+3] & 0xFF ) << 24 );
1435             newInt32 = SwapLong( newInt32 );
1436             s << newInt32;
1437          }
1438       }
1439 #ifdef GDCM_NO_ANSI_STRING_STREAM
1440       s << std::ends; // to avoid oddities on Solaris
1441 #endif //GDCM_NO_ANSI_STRING_STREAM
1442       return s.str();
1443    }
1444    return ((ValEntry *)entry)->GetValue();
1445 }
1446
1447 /**
1448  * \brief   Get the reverse transformed value of the header entry. The VR 
1449  *          value is used to define the reverse transformation to operate on
1450  *          the value
1451  * \warning NOT end user intended method !
1452  * @param   entry Entry to reverse transform
1453  * @return  Reverse transformed entry value
1454  */
1455 std::string Document::GetDocEntryUnvalue(DocEntry *entry)
1456 {
1457    if ( IsDocEntryAnInteger(entry) && entry->IsImplicitVR() )
1458    {
1459       std::string vr = entry->GetVR();
1460       std::vector<std::string> tokens;
1461       std::ostringstream s;
1462
1463       if ( vr == "US" || vr == "SS" ) 
1464       {
1465          uint16_t newInt16;
1466
1467          tokens.erase( tokens.begin(), tokens.end()); // clean any previous value
1468          Util::Tokenize (((ValEntry *)entry)->GetValue(), tokens, "\\");
1469          for (unsigned int i=0; i<tokens.size(); i++) 
1470          {
1471             newInt16 = atoi(tokens[i].c_str());
1472             s << (  newInt16        & 0xFF ) 
1473               << (( newInt16 >> 8 ) & 0xFF );
1474          }
1475          tokens.clear();
1476       }
1477       if ( vr == "UL" || vr == "SL")
1478       {
1479          uint32_t newInt32;
1480
1481          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
1482          Util::Tokenize (((ValEntry *)entry)->GetValue(), tokens, "\\");
1483          for (unsigned int i=0; i<tokens.size();i++) 
1484          {
1485             newInt32 = atoi(tokens[i].c_str());
1486             s << (char)(  newInt32         & 0xFF ) 
1487               << (char)(( newInt32 >>  8 ) & 0xFF )
1488               << (char)(( newInt32 >> 16 ) & 0xFF )
1489               << (char)(( newInt32 >> 24 ) & 0xFF );
1490          }
1491          tokens.clear();
1492       }
1493
1494 #ifdef GDCM_NO_ANSI_STRING_STREAM
1495       s << std::ends; // to avoid oddities on Solaris
1496 #endif //GDCM_NO_ANSI_STRING_STREAM
1497       return s.str();
1498    }
1499
1500    return ((ValEntry *)entry)->GetValue();
1501 }
1502
1503 /**
1504  * \brief   Skip a given Header Entry 
1505  * \warning NOT end user intended method !
1506  * @param   entry entry to skip
1507  */
1508 void Document::SkipDocEntry(DocEntry *entry) 
1509 {
1510    SkipBytes(entry->GetLength());
1511 }
1512
1513 /**
1514  * \brief   Skips to the beginning of the next Header Entry 
1515  * \warning NOT end user intended method !
1516  * @param   currentDocEntry entry to skip
1517  */
1518 void Document::SkipToNextDocEntry(DocEntry *currentDocEntry) 
1519 {
1520    Fp->seekg((long)(currentDocEntry->GetOffset()),     std::ios::beg);
1521    if (currentDocEntry->GetGroup() != 0xfffe)  // for fffe pb
1522       Fp->seekg( (long)(currentDocEntry->GetReadLength()),std::ios::cur);
1523 }
1524
1525 /**
1526  * \brief   When the length of an element value is obviously wrong (because
1527  *          the parser went Jabberwocky) one can hope improving things by
1528  *          applying some heuristics.
1529  * @param   entry entry to check
1530  * @param   foundLength first assumption about length    
1531  */
1532 void Document::FixDocEntryFoundLength(DocEntry *entry,
1533                                       uint32_t foundLength)
1534 {
1535    entry->SetReadLength( foundLength ); // will be updated only if a bug is found        
1536    if ( foundLength == 0xffffffff)
1537    {
1538       foundLength = 0;
1539    }
1540    
1541    uint16_t gr   = entry->GetGroup();
1542    uint16_t elem = entry->GetElement(); 
1543      
1544    if ( foundLength % 2)
1545    {
1546       gdcmVerboseMacro( "Warning : Tag with uneven length " << foundLength 
1547         <<  " in x(" << std::hex << gr << "," << elem <<")");
1548    }
1549       
1550    //////// Fix for some naughty General Electric images.
1551    // Allthough not recent many such GE corrupted images are still present
1552    // on Creatis hard disks. Hence this fix shall remain when such images
1553    // are no longer in use (we are talking a few years, here)...
1554    // Note: XMedCom probably uses such a trick since it is able to read
1555    //       those pesky GE images ...
1556    if ( foundLength == 13)
1557    {
1558       // Only happens for this length !
1559       if ( gr != 0x0008 || ( elem != 0x0070 && elem != 0x0080 ) )
1560       {
1561          foundLength = 10;
1562          entry->SetReadLength(10); /// \todo a bug is to be fixed !?
1563       }
1564    }
1565
1566    //////// Fix for some brain-dead 'Leonardo' Siemens images.
1567    // Occurence of such images is quite low (unless one leaves close to a
1568    // 'Leonardo' source. Hence, one might consider commenting out the
1569    // following fix on efficiency reasons.
1570    else if ( gr   == 0x0009 && ( elem == 0x1113 || elem == 0x1114 ) )
1571    {
1572       foundLength = 4;
1573       entry->SetReadLength(4); /// \todo a bug is to be fixed !?
1574    } 
1575  
1576    else if ( entry->GetVR() == "SQ" )
1577    {
1578       foundLength = 0;      // ReadLength is unchanged 
1579    } 
1580     
1581    //////// We encountered a 'delimiter' element i.e. a tag of the form 
1582    // "fffe|xxxx" which is just a marker. Delimiters length should not be
1583    // taken into account.
1584    else if( gr == 0xfffe )
1585    {    
1586      // According to the norm, fffe|0000 shouldn't exist. BUT the Philips
1587      // image gdcmData/gdcm-MR-PHILIPS-16-Multi-Seq.dcm happens to
1588      // causes extra troubles...
1589      if( entry->GetElement() != 0x0000 )
1590      {
1591         foundLength = 0;
1592      }
1593    }            
1594    entry->SetLength(foundLength);
1595 }
1596
1597 /**
1598  * \brief   Apply some heuristics to predict whether the considered 
1599  *          element value contains/represents an integer or not.
1600  * @param   entry The element value on which to apply the predicate.
1601  * @return  The result of the heuristical predicate.
1602  */
1603 bool Document::IsDocEntryAnInteger(DocEntry *entry)
1604 {
1605    uint16_t elem    = entry->GetElement();
1606    uint16_t group   = entry->GetGroup();
1607    const std::string &vr  = entry->GetVR();
1608    uint32_t length  = entry->GetLength();
1609
1610    // When we have some semantics on the element we just read, and if we
1611    // a priori know we are dealing with an integer, then we shall be
1612    // able to swap it's element value properly.
1613    if ( elem == 0 )  // This is the group length of the group
1614    {  
1615       if ( length == 4 )
1616       {
1617          return true;
1618       }
1619       else 
1620       {
1621          // Allthough this should never happen, still some images have a
1622          // corrupted group length [e.g. have a glance at offset x(8336) of
1623          // gdcmData/gdcm-MR-PHILIPS-16-Multi-Seq.dcm].
1624          // Since for dicom compliant and well behaved headers, the present
1625          // test is useless (and might even look a bit paranoid), when we
1626          // encounter such an ill-formed image, we simply display a warning
1627          // message and proceed on parsing (while crossing fingers).
1628          long filePosition = Fp->tellg();
1629          gdcmVerboseMacro( "Erroneous Group Length element length  on : (" 
1630            << std::hex << group << " , " << elem
1631            << ") -before- position x(" << filePosition << ")"
1632            << "lgt : " << length );
1633       }
1634    }
1635
1636    if ( vr == "UL" || vr == "US" || vr == "SL" || vr == "SS" )
1637    {
1638       return true;
1639    }   
1640    return false;
1641 }
1642
1643 /**
1644  * \brief Loads all the needed Dictionaries
1645  * \warning NOT end user intended method !   
1646  */
1647 void Document::Initialize() 
1648 {
1649    RefPubDict = Global::GetDicts()->GetDefaultPubDict();
1650    RefShaDict = NULL;
1651    Filetype = Unknown;
1652 }
1653
1654 /**
1655  * \brief   Discover what the swap code is (among little endian, big endian,
1656  *          bad little endian, bad big endian).
1657  *          sw is set
1658  * @return false when we are absolutely sure 
1659  *               it's neither ACR-NEMA nor DICOM
1660  *         true  when we hope ours assuptions are OK
1661  */
1662 bool Document::CheckSwap()
1663 {
1664    // The only guaranted way of finding the swap code is to find a
1665    // group tag since we know it's length has to be of four bytes i.e.
1666    // 0x00000004. Finding the swap code in then straigthforward. Trouble
1667    // occurs when we can't find such group...
1668    
1669    uint32_t  x = 4;  // x : for ntohs
1670    bool net2host; // true when HostByteOrder is the same as NetworkByteOrder
1671    uint32_t  s32;
1672    uint16_t  s16;
1673        
1674    char deb[256];
1675     
1676    // First, compare HostByteOrder and NetworkByteOrder in order to
1677    // determine if we shall need to swap bytes (i.e. the Endian type).
1678    if ( x == ntohs(x) )
1679    {
1680       net2host = true;
1681    }
1682    else
1683    {
1684       net2host = false;
1685    }
1686          
1687    // The easiest case is the one of a 'true' DICOM header, we just have
1688    // to look for the string "DICM" inside the file preamble.
1689    Fp->read(deb, 256);
1690    
1691    char *entCur = deb + 128;
1692    if( memcmp(entCur, "DICM", (size_t)4) == 0 )
1693    {
1694       gdcmVerboseMacro( "Looks like DICOM Version3 (preamble + DCM)" );
1695       
1696       // Group 0002 should always be VR, and the first element 0000
1697       // Let's be carefull (so many wrong headers ...)
1698       // and determine the value representation (VR) : 
1699       // Let's skip to the first element (0002,0000) and check there if we find
1700       // "UL"  - or "OB" if the 1st one is (0002,0001) -,
1701       // in which case we (almost) know it is explicit VR.
1702       // WARNING: if it happens to be implicit VR then what we will read
1703       // is the length of the group. If this ascii representation of this
1704       // length happens to be "UL" then we shall believe it is explicit VR.
1705       // We need to skip :
1706       // * the 128 bytes of File Preamble (often padded with zeroes),
1707       // * the 4 bytes of "DICM" string,
1708       // * the 4 bytes of the first tag (0002, 0000),or (0002, 0001)
1709       // i.e. a total of  136 bytes.
1710       entCur = deb + 136;
1711      
1712       // group 0x0002 *is always* Explicit VR Sometimes ,
1713       // even if elem 0002,0010 (Transfer Syntax) tells us the file is
1714       // *Implicit* VR  (see former 'gdcmData/icone.dcm')
1715       
1716       if( memcmp(entCur, "UL", (size_t)2) == 0 ||
1717           memcmp(entCur, "OB", (size_t)2) == 0 ||
1718           memcmp(entCur, "UI", (size_t)2) == 0 ||
1719           memcmp(entCur, "CS", (size_t)2) == 0 )  // CS, to remove later
1720                                                   // when Write DCM *adds*
1721       // FIXME
1722       // Use Document::dicom_vr to test all the possibilities
1723       // instead of just checking for UL, OB and UI !? group 0000 
1724       {
1725          Filetype = ExplicitVR;
1726          gdcmVerboseMacro( "Group 0002 : Explicit Value Representation");
1727       } 
1728       else 
1729       {
1730          Filetype = ImplicitVR;
1731          gdcmVerboseMacro( "Group 0002 :Not an explicit Value Representation;"
1732                         << "Looks like a bugged Header!");
1733       }
1734       
1735       if ( net2host )
1736       {
1737          SwapCode = 4321;
1738          gdcmVerboseMacro( "HostByteOrder != NetworkByteOrder");
1739       }
1740       else 
1741       {
1742          SwapCode = 1234;
1743          gdcmVerboseMacro( "HostByteOrder = NetworkByteOrder");
1744       }
1745       
1746       // Position the file position indicator at first tag 
1747       // (i.e. after the file preamble and the "DICM" string).
1748       Fp->seekg(0, std::ios::beg);
1749       Fp->seekg ( 132L, std::ios::beg);
1750       return true;
1751    } // End of DicomV3
1752
1753    // Alas, this is not a DicomV3 file and whatever happens there is no file
1754    // preamble. We can reset the file position indicator to where the data
1755    // is (i.e. the beginning of the file).
1756    gdcmVerboseMacro( "Not a DICOM Version3 file");
1757    Fp->seekg(0, std::ios::beg);
1758
1759    // Our next best chance would be to be considering a 'clean' ACR/NEMA file.
1760    // By clean we mean that the length of the first tag is written down.
1761    // If this is the case and since the length of the first group HAS to be
1762    // four (bytes), then determining the proper swap code is straightforward.
1763
1764    entCur = deb + 4;
1765    // We assume the array of char we are considering contains the binary
1766    // representation of a 32 bits integer. Hence the following dirty
1767    // trick :
1768    s32 = *((uint32_t *)(entCur));
1769
1770    switch( s32 )
1771    {
1772       case 0x00040000 :
1773          SwapCode = 3412;
1774          Filetype = ACR;
1775          return true;
1776       case 0x04000000 :
1777          SwapCode = 4321;
1778          Filetype = ACR;
1779          return true;
1780       case 0x00000400 :
1781          SwapCode = 2143;
1782          Filetype = ACR;
1783          return true;
1784       case 0x00000004 :
1785          SwapCode = 1234;
1786          Filetype = ACR;
1787          return true;
1788       default :
1789          // We are out of luck. It is not a DicomV3 nor a 'clean' ACR/NEMA file.
1790          // It is time for despaired wild guesses. 
1791          // So, let's check if this file wouldn't happen to be 'dirty' ACR/NEMA,
1792          //  i.e. the 'group length' element is not present :     
1793          
1794          //  check the supposed-to-be 'group number'
1795          //  in ( 0x0001 .. 0x0008 )
1796          //  to determine ' SwapCode' value .
1797          //  Only 0 or 4321 will be possible 
1798          //  (no oportunity to check for the formerly well known
1799          //  ACR-NEMA 'Bad Big Endian' or 'Bad Little Endian' 
1800          //  if unsuccessfull (i.e. neither 0x0002 nor 0x0200 etc -3, 4, ..., 8-) 
1801          //  the file IS NOT ACR-NEMA nor DICOM V3
1802          //  Find a trick to tell it the caller...
1803       
1804          s16 = *((uint16_t *)(deb));
1805       
1806          switch ( s16 )
1807          {
1808             case 0x0001 :
1809             case 0x0002 :
1810             case 0x0003 :
1811             case 0x0004 :
1812             case 0x0005 :
1813             case 0x0006 :
1814             case 0x0007 :
1815             case 0x0008 :
1816                SwapCode = 1234;
1817                Filetype = ACR;
1818                return true;
1819             case 0x0100 :
1820             case 0x0200 :
1821             case 0x0300 :
1822             case 0x0400 :
1823             case 0x0500 :
1824             case 0x0600 :
1825             case 0x0700 :
1826             case 0x0800 :
1827                SwapCode = 4321;
1828                Filetype = ACR;
1829                return true;
1830             default :
1831                gdcmVerboseMacro( "ACR/NEMA unfound swap info (Really hopeless !)");
1832                Filetype = Unknown;
1833                return false;
1834          }
1835    }
1836 }
1837
1838 /**
1839  * \brief Change the Byte Swap code. 
1840  */
1841 void Document::SwitchByteSwapCode() 
1842 {
1843    gdcmVerboseMacro( "Switching Byte Swap code from "<< SwapCode);
1844    if ( SwapCode == 1234 ) 
1845    {
1846       SwapCode = 4321;
1847    }
1848    else if ( SwapCode == 4321 ) 
1849    {
1850       SwapCode = 1234;
1851    }
1852    else if ( SwapCode == 3412 ) 
1853    {
1854       SwapCode = 2143;
1855    }
1856    else if ( SwapCode == 2143 )
1857    {
1858       SwapCode = 3412;
1859    }
1860 }
1861
1862 /**
1863  * \brief  during parsing, Header Elements too long are not loaded in memory 
1864  * @param newSize
1865  */
1866 void Document::SetMaxSizeLoadEntry(long newSize) 
1867 {
1868    if ( newSize < 0 )
1869    {
1870       return;
1871    }
1872    if ((uint32_t)newSize >= (uint32_t)0xffffffff )
1873    {
1874       MaxSizeLoadEntry = 0xffffffff;
1875       return;
1876    }
1877    MaxSizeLoadEntry = newSize;
1878 }
1879
1880 /**
1881  * \brief Header Elements too long will not be printed
1882  * \todo  See comments of \ref Document::MAX_SIZE_PRINT_ELEMENT_VALUE 
1883  * @param newSize
1884  */
1885 void Document::SetMaxSizePrintEntry(long newSize) 
1886 {
1887    if ( newSize < 0 )
1888    {
1889       return;
1890    }
1891    if ((uint32_t)newSize >= (uint32_t)0xffffffff )
1892    {
1893       MaxSizePrintEntry = 0xffffffff;
1894       return;
1895    }
1896    MaxSizePrintEntry = newSize;
1897 }
1898
1899
1900 /**
1901  * \brief   Read the next tag but WITHOUT loading it's value
1902  *          (read the 'Group Number', the 'Element Number',
1903  *          gets the Dict Entry
1904  *          gets the VR, gets the length, gets the offset value)
1905  * @return  On succes the newly created DocEntry, NULL on failure.      
1906  */
1907 DocEntry *Document::ReadNextDocEntry()
1908 {
1909    uint16_t group;
1910    uint16_t elem;
1911
1912    try
1913    {
1914       group = ReadInt16();
1915       elem  = ReadInt16();
1916    }
1917    catch ( FormatError e )
1918    {
1919       // We reached the EOF (or an error occured) therefore 
1920       // header parsing has to be considered as finished.
1921       //std::cout << e;
1922       return 0;
1923    }
1924
1925    // Sometimes file contains groups of tags with reversed endianess.
1926    HandleBrokenEndian(group, elem);
1927
1928    // In 'true DICOM' files Group 0002 is always little endian
1929    if ( HasDCMPreamble )
1930       HandleOutOfGroup0002(group, elem);
1931  
1932    std::string vr = FindDocEntryVR();
1933    std::string realVR = vr;
1934
1935    if( vr == GDCM_UNKNOWN)
1936    {
1937       DictEntry *dictEntry = GetDictEntry(group,elem);
1938       if( dictEntry )
1939          realVR = dictEntry->GetVR();
1940    }
1941
1942    DocEntry *newEntry;
1943    if( Global::GetVR()->IsVROfSequence(realVR) )
1944       newEntry = NewSeqEntry(group, elem);
1945    else if( Global::GetVR()->IsVROfStringRepresentable(realVR) )
1946       newEntry = NewValEntry(group, elem,vr);
1947    else
1948       newEntry = NewBinEntry(group, elem,vr);
1949
1950    if( vr == GDCM_UNKNOWN )
1951    {
1952       if( Filetype == ExplicitVR )
1953       {
1954          // We thought this was explicit VR, but we end up with an
1955          // implicit VR tag. Let's backtrack.
1956          if ( newEntry->GetGroup() != 0xfffe )
1957          { 
1958             std::string msg;
1959             msg = Util::Format("Entry (%04x,%04x) should be Explicit VR\n", 
1960                           newEntry->GetGroup(), newEntry->GetElement());
1961             gdcmVerboseMacro( msg.c_str() );
1962           }
1963       }
1964       newEntry->SetImplicitVR();
1965    }
1966
1967    try
1968    {
1969       FindDocEntryLength(newEntry);
1970    }
1971    catch ( FormatError e )
1972    {
1973       // Call it quits
1974       //std::cout << e;
1975       delete newEntry;
1976       return 0;
1977    }
1978
1979    newEntry->SetOffset(Fp->tellg());  
1980
1981    return newEntry;
1982 }
1983
1984 /**
1985  * \brief   Handle broken private tag from Philips NTSCAN
1986  *          where the endianess is being switch to BigEndian for no
1987  *          apparent reason
1988  * @return  no return
1989  */
1990 void Document::HandleBrokenEndian(uint16_t &group, uint16_t &elem)
1991 {
1992    // Endian reversion. Some files contain groups of tags with reversed endianess.
1993    static int reversedEndian = 0;
1994    // try to fix endian switching in the middle of headers
1995    if ((group == 0xfeff) && (elem == 0x00e0))
1996    {
1997      // start endian swap mark for group found
1998      reversedEndian++;
1999      SwitchByteSwapCode();
2000      // fix the tag
2001      group = 0xfffe;
2002      elem  = 0xe000;
2003    } 
2004    else if (group == 0xfffe && elem == 0xe00d && reversedEndian) 
2005    {
2006      // end of reversed endian group
2007      reversedEndian--;
2008      SwitchByteSwapCode();
2009    }
2010 }
2011
2012 /**
2013  * \brief   Group 0002 is always coded Little Endian
2014  *          whatever Transfer Syntax is
2015  * @return  no return
2016  */
2017 void Document::HandleOutOfGroup0002(uint16_t &group, uint16_t &elem)
2018 {
2019    // Endian reversion. Some files contain groups of tags with reversed endianess.
2020    if ( !Group0002Parsed && group != 0x0002)
2021    {
2022       Group0002Parsed = true;
2023       // we just came out of group 0002
2024       // if Transfer syntax is Big Endian we have to change CheckSwap
2025
2026       std::string ts = GetTransferSyntax();
2027       if ( !Global::GetTS()->IsTransferSyntax(ts) )
2028       {
2029          gdcmVerboseMacro("True DICOM File, with NO Tansfer Syntax: " << ts );
2030          return;
2031       }
2032
2033       // Group 0002 is always 'Explicit ...' enven when Transfer Syntax says 'Implicit ..." 
2034
2035       if ( Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRLittleEndian )
2036          {
2037             Filetype = ImplicitVR;
2038          }
2039        
2040       // FIXME Strangely, this works with 
2041       //'Implicit VR Transfer Syntax (GE Private)
2042       if ( Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRBigEndian )
2043       {
2044          gdcmVerboseMacro("Transfer Syntax Name = [" 
2045                         << GetTransferSyntaxName() << "]" );
2046          SwitchByteSwapCode();
2047          group = SwapShort(group);
2048          elem  = SwapShort(elem);
2049       }
2050    }
2051 }
2052
2053 /**
2054  * \brief   Compares two documents, according to \ref DicomDir rules
2055  * \warning Does NOT work with ACR-NEMA files
2056  * \todo    Find a trick to solve the pb (use RET fields ?)
2057  * @param   document
2058  * @return  true if 'smaller'
2059  */
2060 bool Document::operator<(Document &document)
2061 {
2062    // Patient Name
2063    std::string s1 = GetEntryValue(0x0010,0x0010);
2064    std::string s2 = document.GetEntryValue(0x0010,0x0010);
2065    if(s1 < s2)
2066    {
2067       return true;
2068    }
2069    else if( s1 > s2 )
2070    {
2071       return false;
2072    }
2073    else
2074    {
2075       // Patient ID
2076       s1 = GetEntryValue(0x0010,0x0020);
2077       s2 = document.GetEntryValue(0x0010,0x0020);
2078       if ( s1 < s2 )
2079       {
2080          return true;
2081       }
2082       else if ( s1 > s2 )
2083       {
2084          return false;
2085       }
2086       else
2087       {
2088          // Study Instance UID
2089          s1 = GetEntryValue(0x0020,0x000d);
2090          s2 = document.GetEntryValue(0x0020,0x000d);
2091          if ( s1 < s2 )
2092          {
2093             return true;
2094          }
2095          else if( s1 > s2 )
2096          {
2097             return false;
2098          }
2099          else
2100          {
2101             // Serie Instance UID
2102             s1 = GetEntryValue(0x0020,0x000e);
2103             s2 = document.GetEntryValue(0x0020,0x000e);    
2104             if ( s1 < s2 )
2105             {
2106                return true;
2107             }
2108             else if( s1 > s2 )
2109             {
2110                return false;
2111             }
2112          }
2113       }
2114    }
2115    return false;
2116 }
2117
2118 //-----------------------------------------------------------------------------
2119 // Print
2120 /**
2121   * \brief   Prints The Dict Entries of THE public Dicom Dictionary
2122   * @param os ostream to print to
2123   * @return
2124   */  
2125 void Document::PrintPubDict(std::ostream &os)
2126 {
2127    RefPubDict->SetPrintLevel(PrintLevel);
2128    RefPubDict->Print(os);
2129 }
2130
2131 /**
2132   * \brief   Prints The Dict Entries of THE shadow Dicom Dictionary
2133   * @param os ostream to print to
2134   * @return
2135   */
2136 void Document::PrintShaDict(std::ostream &os)
2137 {
2138    RefShaDict->SetPrintLevel(PrintLevel);
2139    RefShaDict->Print(os);
2140 }
2141
2142 //-----------------------------------------------------------------------------
2143 } // end namespace gdcm