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