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