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