]> Creatis software - gdcm.git/blob - src/gdcmHeader.cxx
* gdcmPython/Makefile.am now avoids calling the wrappers for the
[gdcm.git] / src / gdcmHeader.cxx
1 // $Header: /cvs/public/gdcm/src/Attic/gdcmHeader.cxx,v 1.73 2003/07/01 10:04:36 frog Exp $
2
3 #include <stdio.h>
4 #include <cerrno>
5 // For nthos:
6 #ifdef _MSC_VER
7 #include <winsock.h>
8 #else
9 #include <netinet/in.h>
10 #endif
11 #include <cctype>    // for isalpha
12 #include <sstream>
13 #include "gdcmUtil.h"
14 #include "gdcmHeader.h"
15 using namespace std;
16
17
18 // TODO : remove DEBUG
19 #define DEBUG 0
20
21 // Refer to gdcmHeader::CheckSwap()
22 #define HEADER_LENGTH_TO_READ       256
23 // Refer to gdcmHeader::SetMaxSizeLoadElementValue()
24 #define _MaxSizeLoadElementValue_   1024
25
26 /**
27  * \ingroup gdcmHeader
28  * \brief   
29  */
30 void gdcmHeader::Initialise(void) {
31    dicom_vr = gdcmGlobal::GetVR();
32    dicom_ts = gdcmGlobal::GetTS();
33    Dicts =    gdcmGlobal::GetDicts();
34    RefPubDict = Dicts->GetDefaultPubDict();
35    RefShaDict = (gdcmDict*)0;
36 }
37
38 /**
39  * \ingroup gdcmHeader
40  * \brief   
41  * @param   InFilename
42  * @param   exception_on_error
43  */
44  gdcmHeader::gdcmHeader(const char *InFilename, bool exception_on_error) {
45   SetMaxSizeLoadElementValue(_MaxSizeLoadElementValue_);
46   filename = InFilename;
47   Initialise();
48   if ( !OpenFile(exception_on_error))
49      return;
50   ParseHeader();
51   LoadElements();
52   CloseFile();
53 }
54
55 /**
56  * \ingroup gdcmHeader
57  * \brief   
58  * @param   exception_on_error
59  */
60 gdcmHeader::gdcmHeader(bool exception_on_error) {
61   SetMaxSizeLoadElementValue(_MaxSizeLoadElementValue_);
62   Initialise();
63 }
64
65 /**
66  * \ingroup gdcmHeader
67  * \brief   
68  * @param   exception_on_error
69  * @return  
70  */
71  bool gdcmHeader::OpenFile(bool exception_on_error)
72   throw(gdcmFileError) {
73   fp=fopen(filename.c_str(),"rb");
74   if(exception_on_error) {
75     if(!fp)
76       throw gdcmFileError("gdcmHeader::gdcmHeader(const char *, bool)");
77   }
78   if ( fp )
79      return true;
80   dbg.Verbose(0, "gdcmHeader::gdcmHeader cannot open file", filename.c_str());
81   return false;
82 }
83
84 /**
85  * \ingroup gdcmHeader
86  * \brief   
87  * @return  
88  */
89 bool gdcmHeader::CloseFile(void) {
90   int closed = fclose(fp);
91   fp = (FILE *)0;
92   if (! closed)
93      return false;
94   return true;
95 }
96
97 /**
98  * \ingroup gdcmHeader
99  * \brief   Canonical destructor.
100  */
101 gdcmHeader::~gdcmHeader (void) {
102    dicom_vr =   (gdcmVR*)0; 
103    Dicts    =   (gdcmDictSet*)0;
104    RefPubDict = (gdcmDict*)0;
105    RefShaDict = (gdcmDict*)0;
106    return;
107 }
108
109 // Fourth semantics:
110 // CMD      Command        
111 // META     Meta Information 
112 // DIR      Directory
113 // ID
114 // PAT      Patient
115 // ACQ      Acquisition
116 // REL      Related
117 // IMG      Image
118 // SDY      Study
119 // VIS      Visit 
120 // WAV      Waveform
121 // PRC
122 // DEV      Device
123 // NMI      Nuclear Medicine
124 // MED
125 // BFS      Basic Film Session
126 // BFB      Basic Film Box
127 // BIB      Basic Image Box
128 // BAB
129 // IOB
130 // PJ
131 // PRINTER
132 // RT       Radio Therapy
133 // DVH   
134 // SSET
135 // RES      Results
136 // CRV      Curve
137 // OLY      Overlays
138 // PXL      Pixels
139 //
140
141 /**
142  * \ingroup gdcmHeader
143  * \brief   Discover what the swap code is (among little endian, big endian,
144  *          bad little endian, bad big endian).
145  *
146  */
147 void gdcmHeader::CheckSwap()
148 {
149    // The only guaranted way of finding the swap code is to find a
150    // group tag since we know it's length has to be of four bytes i.e.
151    // 0x00000004. Finding the swap code in then straigthforward. Trouble
152    // occurs when we can't find such group...
153    guint32  s;
154    guint32  x=4;  // x : pour ntohs
155    bool net2host; // true when HostByteOrder is the same as NetworkByteOrder
156     
157    int lgrLue;
158    char * entCur;
159    char deb[HEADER_LENGTH_TO_READ];
160     
161    // First, compare HostByteOrder and NetworkByteOrder in order to
162    // determine if we shall need to swap bytes (i.e. the Endian type).
163    if (x==ntohs(x))
164       net2host = true;
165    else
166       net2host = false;
167    
168    // The easiest case is the one of a DICOM header, since it possesses a
169    // file preamble where it suffice to look for the string "DICM".
170    lgrLue = fread(deb, 1, HEADER_LENGTH_TO_READ, fp);
171    
172    entCur = deb + 128;
173    if(memcmp(entCur, "DICM", (size_t)4) == 0) {
174       dbg.Verbose(1, "gdcmHeader::CheckSwap:", "looks like DICOM Version3");
175       // Next, determine the value representation (VR). Let's skip to the
176       // first element (0002, 0000) and check there if we find "UL" 
177       // - or "OB" if the 1st one is (0002,0001) -,
178       // in which case we (almost) know it is explicit VR.
179       // WARNING: if it happens to be implicit VR then what we will read
180       // is the length of the group. If this ascii representation of this
181       // length happens to be "UL" then we shall believe it is explicit VR.
182       // FIXME: in order to fix the above warning, we could read the next
183       // element value (or a couple of elements values) in order to make
184       // sure we are not commiting a big mistake.
185       // We need to skip :
186       // * the 128 bytes of File Preamble (often padded with zeroes),
187       // * the 4 bytes of "DICM" string,
188       // * the 4 bytes of the first tag (0002, 0000),or (0002, 0001)
189       // i.e. a total of  136 bytes.
190       entCur = deb + 136;
191       // FIXME
192       // Use gdcmHeader::dicom_vr to test all the possibilities
193       // instead of just checking for UL, OB and UI !?
194       if(  (memcmp(entCur, "UL", (size_t)2) == 0) ||
195           (memcmp(entCur, "OB", (size_t)2) == 0) ||
196           (memcmp(entCur, "UI", (size_t)2) == 0) )
197         {
198          filetype = ExplicitVR;
199          dbg.Verbose(1, "gdcmHeader::CheckSwap:",
200                      "explicit Value Representation");
201       } else {
202          filetype = ImplicitVR;
203          dbg.Verbose(1, "gdcmHeader::CheckSwap:",
204                      "not an explicit Value Representation");
205       }
206
207       if (net2host) {
208          sw = 4321;
209          dbg.Verbose(1, "gdcmHeader::CheckSwap:",
210                         "HostByteOrder != NetworkByteOrder");
211       } else {
212          sw = 0;
213          dbg.Verbose(1, "gdcmHeader::CheckSwap:",
214                         "HostByteOrder = NetworkByteOrder");
215       }
216       
217       // Position the file position indicator at first tag (i.e.
218       // after the file preamble and the "DICM" string).
219       rewind(fp);
220       fseek (fp, 132L, SEEK_SET);
221       return;
222    } // End of DicomV3
223
224    // Alas, this is not a DicomV3 file and whatever happens there is no file
225    // preamble. We can reset the file position indicator to where the data
226    // is (i.e. the beginning of the file).
227     dbg.Verbose(1, "gdcmHeader::CheckSwap:", "not a DICOM Version3 file");
228    rewind(fp);
229
230    // Our next best chance would be to be considering a 'clean' ACR/NEMA file.
231    // By clean we mean that the length of the first tag is written down.
232    // If this is the case and since the length of the first group HAS to be
233    // four (bytes), then determining the proper swap code is straightforward.
234
235    entCur = deb + 4;
236    // We assume the array of char we are considering contains the binary
237    // representation of a 32 bits integer. Hence the following dirty
238    // trick :
239    s = *((guint32 *)(entCur));
240    
241    switch (s) {
242    case 0x00040000 :
243       sw = 3412;
244       filetype = ACR;
245       return;
246    case 0x04000000 :
247       sw = 4321;
248       filetype = ACR;
249       return;
250    case 0x00000400 :
251       sw = 2143;
252       filetype = ACR;
253       return;
254    case 0x00000004 :
255       sw = 0;
256       filetype = ACR;
257       return;
258    default :
259       dbg.Verbose(0, "gdcmHeader::CheckSwap:",
260                      "ACR/NEMA unfound swap info (time to raise bets)");
261    }
262
263    // We are out of luck. It is not a DicomV3 nor a 'clean' ACR/NEMA file.
264    // It is time for despaired wild guesses. So, let's assume this file
265    // happens to be 'dirty' ACR/NEMA, i.e. the length of the group is
266    // not present. Then the only info we have is the net2host one.
267    filetype = Unknown;
268    if (! net2host )
269       sw = 0;
270    else
271       sw = 4321;
272    return;
273 }
274
275 /**
276  * \ingroup gdcmHeader
277  * \brief   
278  */
279 void gdcmHeader::SwitchSwapToBigEndian(void) {
280    dbg.Verbose(1, "gdcmHeader::SwitchSwapToBigEndian",
281                   "Switching to BigEndian mode.");
282    if ( sw == 0    ) {
283       sw = 4321;
284       return;
285    }
286    if ( sw == 4321 ) {
287       sw = 0;
288       return;
289    }
290    if ( sw == 3412 ) {
291       sw = 2143;
292       return;
293    }
294    if ( sw == 2143 )
295       sw = 3412;
296 }
297
298 /**
299  * \ingroup   gdcmHeader
300  * \brief     Find the value representation of the current tag.
301  */
302 void gdcmHeader::FindVR( gdcmElValue *ElVal) {
303    if (filetype != ExplicitVR)
304       return;
305
306    char VR[3];
307    string vr;
308    int lgrLue;
309    char msg[100]; // for sprintf. Sorry
310
311    long PositionOnEntry = ftell(fp);
312    // Warning: we believe this is explicit VR (Value Representation) because
313    // we used a heuristic that found "UL" in the first tag. Alas this
314    // doesn't guarantee that all the tags will be in explicit VR. In some
315    // cases (see e-film filtered files) one finds implicit VR tags mixed
316    // within an explicit VR file. Hence we make sure the present tag
317    // is in explicit VR and try to fix things if it happens not to be
318    // the case.
319    bool RealExplicit = true;
320    
321    lgrLue=fread (&VR, (size_t)2,(size_t)1, fp);
322    VR[2]=0;
323    vr = string(VR);
324       
325    // Assume we are reading a falsely explicit VR file i.e. we reached
326    // a tag where we expect reading a VR but are in fact we read the
327    // first to bytes of the length. Then we will interogate (through find)
328    // the dicom_vr dictionary with oddities like "\004\0" which crashes
329    // both GCC and VC++ implementations of the STL map. Hence when the
330    // expected VR read happens to be non-ascii characters we consider
331    // we hit falsely explicit VR tag.
332
333    if ( (!isalpha(VR[0])) && (!isalpha(VR[1])) )
334       RealExplicit = false;
335
336    // CLEANME searching the dicom_vr at each occurence is expensive.
337    // PostPone this test in an optional integrity check at the end
338    // of parsing or only in debug mode.
339    if ( RealExplicit && !dicom_vr->Count(vr) )
340       RealExplicit= false;
341
342    if ( RealExplicit ) {
343       if ( ElVal->IsVrUnknown() ) {
344          // When not a dictionary entry, we can safely overwrite the vr.
345          ElVal->SetVR(vr);
346          return; 
347       }
348       if ( ElVal->GetVR() == vr ) {
349          // The vr we just read and the dictionary agree. Nothing to do.
350          return;
351       }
352       // The vr present in the file and the dictionary disagree. We assume
353       // the file writer knew best and use the vr of the file. Since it would
354       // be unwise to overwrite the vr of a dictionary (since it would
355       // compromise it's next user), we need to clone the actual DictEntry
356       // and change the vr for the read one.
357       gdcmDictEntry* NewTag = new gdcmDictEntry(ElVal->GetGroup(),
358                                  ElVal->GetElement(),
359                                  vr,
360                                  "FIXME",
361                                  ElVal->GetName());
362       ElVal->SetDictEntry(NewTag);
363       return; 
364    }
365    
366    // We thought this was explicit VR, but we end up with an
367    // implicit VR tag. Let's backtrack.   
368    
369       sprintf(msg,"Falsely explicit vr file (%04x,%04x)\n", ElVal->GetGroup(),ElVal->GetElement());
370       dbg.Verbose(1, "gdcmHeader::FindVR: ",msg);
371    
372    fseek(fp, PositionOnEntry, SEEK_SET);
373    // When this element is known in the dictionary we shall use, e.g. for
374    // the semantics (see  the usage of IsAnInteger), the vr proposed by the
375    // dictionary entry. Still we have to flag the element as implicit since
376    // we know now our assumption on expliciteness is not furfilled.
377    // avoid  .
378    if ( ElVal->IsVrUnknown() )
379       ElVal->SetVR("Implicit");
380    ElVal->SetImplicitVr();
381 }
382
383 /**
384  * \ingroup gdcmHeader
385  * \brief   Determines if the Transfer Syntax was allready encountered
386  *          and if it corresponds to a ImplicitVRLittleEndian one.
387  *
388  * @return  True when ImplicitVRLittleEndian found. False in all other cases.
389  */
390 bool gdcmHeader::IsImplicitVRLittleEndianTransferSyntax(void) {
391    gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
392    if ( !Element )
393       return false;
394    LoadElementValueSafe(Element);
395    string Transfer = Element->GetValue();
396    if ( Transfer == "1.2.840.10008.1.2" )
397       return true;
398    return false;
399 }
400
401 /**
402  * \ingroup gdcmHeader
403  * \brief   Determines if the Transfer Syntax was allready encountered
404  *          and if it corresponds to a ExplicitVRLittleEndian one.
405  *
406  * @return  True when ExplicitVRLittleEndian found. False in all other cases.
407  */
408 bool gdcmHeader::IsExplicitVRLittleEndianTransferSyntax(void) {
409    gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
410    if ( !Element )
411       return false;
412    LoadElementValueSafe(Element);
413    string Transfer = Element->GetValue();
414    if ( Transfer == "1.2.840.10008.1.2.1" )
415       return true;
416    return false;
417 }
418
419 /**
420  * \ingroup gdcmHeader
421  * \brief   Determines if the Transfer Syntax was allready encountered
422  *          and if it corresponds to a DeflatedExplicitVRLittleEndian one.
423  *
424  * @return  True when DeflatedExplicitVRLittleEndian found. False in all other cases.
425  */
426 bool gdcmHeader::IsDeflatedExplicitVRLittleEndianTransferSyntax(void) {
427    gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
428    if ( !Element )
429       return false;
430    LoadElementValueSafe(Element);
431    string Transfer = Element->GetValue();
432    if ( Transfer == "1.2.840.10008.1.2.1.99" )
433       return true;
434    return false;
435 }
436
437 /**
438  * \ingroup gdcmHeader
439  * \brief   Determines if the Transfer Syntax was allready encountered
440  *          and if it corresponds to a Explicit VR Big Endian one.
441  *
442  * @return  True when big endian found. False in all other cases.
443  */
444 bool gdcmHeader::IsExplicitVRBigEndianTransferSyntax(void) {
445    gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
446    if ( !Element )
447       return false;
448    LoadElementValueSafe(Element);
449    string Transfer = Element->GetValue();
450    if ( Transfer == "1.2.840.10008.1.2.2" )  //1.2.2 ??? A verifier !
451       return true;
452    return false;
453 }
454
455 /**
456  * \ingroup gdcmHeader
457  * \brief   Determines if the Transfer Syntax was allready encountered
458  *          and if it corresponds to a JPEGBaseLineProcess1 one.
459  *
460  * @return  True when JPEGBaseLineProcess1found. False in all other cases.
461  */
462 bool gdcmHeader::IsJPEGBaseLineProcess1TransferSyntax(void) {
463    gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
464    if ( !Element )
465       return false;
466    LoadElementValueSafe(Element);
467    string Transfer = Element->GetValue();
468    if ( Transfer == "1.2.840.10008.1.2.4.50" )
469       return true;
470    return false;
471 }
472
473 /**
474  * \ingroup gdcmHeader
475  * \brief   
476  *
477  * @return 
478  */
479 bool gdcmHeader::IsJPEGLossless(void) {
480    gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
481     // faire qq chose d'intelligent a la place de Ã§a
482    if ( !Element )
483       return false;
484    LoadElementValueSafe(Element);
485    const char * Transfert = Element->GetValue().c_str();
486    if ( memcmp(Transfert+strlen(Transfert)-2 ,"70",2)==0) return true;
487    if ( memcmp(Transfert+strlen(Transfert)-2 ,"55",2)==0) return true;
488    return false;
489 }
490
491
492 /**
493  * \ingroup gdcmHeader
494  * \brief   Determines if the Transfer Syntax was allready encountered
495  *          and if it corresponds to a JPEGExtendedProcess2-4 one.
496  *
497  * @return  True when JPEGExtendedProcess2-4 found. False in all other cases.
498  */
499 bool gdcmHeader::IsJPEGExtendedProcess2_4TransferSyntax(void) {
500    gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
501    if ( !Element )
502       return false;
503    LoadElementValueSafe(Element);
504    string Transfer = Element->GetValue();
505    if ( Transfer == "1.2.840.10008.1.2.4.51" )
506       return true;
507    return false;
508 }
509
510 /**
511  * \ingroup gdcmHeader
512  * \brief   Determines if the Transfer Syntax was allready encountered
513  *          and if it corresponds to a JPEGExtendeProcess3-5 one.
514  *
515  * @return  True when JPEGExtendedProcess3-5 found. False in all other cases.
516  */
517 bool gdcmHeader::IsJPEGExtendedProcess3_5TransferSyntax(void) {
518    gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
519    if ( !Element )
520       return false;
521    LoadElementValueSafe(Element);
522    string Transfer = Element->GetValue();
523    if ( Transfer == "1.2.840.10008.1.2.4.52" )
524       return true;
525    return false;
526 }
527
528 /**
529  * \ingroup gdcmHeader
530  * \brief   Determines if the Transfer Syntax was allready encountered
531  *          and if it corresponds to a JPEGSpectralSelectionProcess6-8 one.
532  *
533  * @return  True when JPEGSpectralSelectionProcess6-8 found. False in all
534  *          other cases.
535  */
536 bool gdcmHeader::IsJPEGSpectralSelectionProcess6_8TransferSyntax(void) {
537    gdcmElValue* Element = PubElValSet.GetElementByNumber(0x0002, 0x0010);
538    if ( !Element )
539       return false;
540    LoadElementValueSafe(Element);
541    string Transfer = Element->GetValue();
542    if ( Transfer == "1.2.840.10008.1.2.4.53" )
543       return true;
544    return false;
545 }
546
547 /**
548  * \ingroup gdcmHeader
549  * \brief   Predicate for dicom version 3 file.
550  * @return  True when the file is a dicom version 3.
551  */
552 bool gdcmHeader::IsDicomV3(void) {
553    if (   (filetype == ExplicitVR)
554        || (filetype == ImplicitVR) )
555       return true;
556    return false;
557 }
558
559 /**
560  * \ingroup gdcmHeader
561  * \brief   When the length of an element value is obviously wrong (because
562  *          the parser went Jabberwocky) one can hope improving things by
563  *          applying this heuristic.
564  */
565 void gdcmHeader::FixFoundLength(gdcmElValue * ElVal, guint32 FoundLength) {
566    if ( FoundLength == 0xffffffff)
567       FoundLength = 0;
568    ElVal->SetLength(FoundLength);
569 }
570
571 /**
572  * \ingroup gdcmHeader
573  * \brief   
574  *
575  * @return 
576  */
577  guint32 gdcmHeader::FindLengthOB(void) {
578    // See PS 3.5-2001, section A.4 p. 49 on encapsulation of encoded pixel data.
579    guint16 g;
580    guint16 n; 
581    long PositionOnEntry = ftell(fp);
582    bool FoundSequenceDelimiter = false;
583    guint32 TotalLength = 0;
584    guint32 ItemLength;
585
586    while ( ! FoundSequenceDelimiter) {
587       g = ReadInt16();
588       n = ReadInt16();
589       
590  if (DEBUG) printf ("dans FindLengthOB (%04x,%04x)\n",g,n);
591  long l = ftell(fp);
592  if (DEBUG) printf("en  %d o(%o) x(%x)\n",l,l,l); 
593
594       if (errno == 1)
595          return 0;
596       TotalLength += 4;  // We even have to decount the group and element 
597      
598       if ( g != 0xfffe           && g!=0xb00c ) /*for bogus headerJPR */ {
599          char msg[100]; // for sprintf. Sorry
600          sprintf(msg,"wrong group (%04x) for an item sequence (%04x,%04x)\n",g, g,n);
601          dbg.Verbose(1, "gdcmHeader::FindLengthOB: ",msg); 
602          long l = ftell(fp);
603          if (DEBUG) printf("en  %d o(%o) x(%x)\n",l,l,l); 
604          errno = 1;
605          return 0;
606       }
607  
608       if ( n == 0xe0dd       || ( g==0xb00c && n==0x0eb6 ) ) /* for bogus header JPR */ 
609          FoundSequenceDelimiter = true;
610       else if ( n != 0xe000 ){
611          char msg[100];  // for sprintf. Sorry
612          sprintf(msg,"wrong element (%04x) for an item sequence (%04x,%04x)\n",n, g,n);
613          dbg.Verbose(1, "gdcmHeader::FindLengthOB: ",msg);
614          if (DEBUG) printf("wrong element (%04x) for an item sequence (%04x,%04x)\n",n, g,n);    
615          errno = 1;
616          return 0;
617       }
618       ItemLength = ReadInt32();
619       TotalLength += ItemLength + 4;  // We add 4 bytes since we just read
620                                       // the ItemLength with ReadInt32
621                                       
622       if (DEBUG) printf("TotalLength %d\n",TotalLength);
623       SkipBytes(ItemLength);
624    }
625    fseek(fp, PositionOnEntry, SEEK_SET);
626    return TotalLength;
627 }
628
629 /**
630  * \ingroup gdcmHeader
631  * \brief   
632  *
633  * @return 
634  */
635  void gdcmHeader::FindLength (gdcmElValue * ElVal) {
636    guint16 element = ElVal->GetElement();
637    guint16 group = ElVal->GetGroup(); // JPR a virer
638    string  vr      = ElVal->GetVR();
639    guint16 length16;
640    if( (element == 0x0010) && (group == 0x7fe0) ) {// JPR
641  
642       dbg.SetDebug(10);
643       dbg.Verbose(2, "gdcmHeader::FindLength: ", // JPR
644                      "on est sur 7fe0 0010");
645    }   
646    
647    if ( (filetype == ExplicitVR) && ! ElVal->IsImplicitVr() ) {
648       if ( (vr=="OB") || (vr=="OW") || (vr=="SQ") || (vr=="UN") ) {
649       
650          // The following reserved two bytes (see PS 3.5-2001, section
651          // 7.1.2 Data element structure with explicit vr p27) must be
652          // skipped before proceeding on reading the length on 4 bytes.
653          fseek(fp, 2L, SEEK_CUR);
654
655          guint32 length32 = ReadInt32();
656          if ( (vr == "OB") && (length32 == 0xffffffff) ) {
657             ElVal->SetLength(FindLengthOB());
658             return;
659          }
660          FixFoundLength(ElVal, length32);        
661          return;
662       }
663
664       // Length is encoded on 2 bytes.
665       length16 = ReadInt16();
666       
667       // We can tell the current file is encoded in big endian (like
668       // Data/US-RGB-8-epicard) when we find the "Transfer Syntax" tag
669       // and it's value is the one of the encoding of a big endian file.
670       // In order to deal with such big endian encoded files, we have
671       // (at least) two strategies:
672       // * when we load the "Transfer Syntax" tag with value of big endian
673       //   encoding, we raise the proper flags. Then we wait for the end
674       //   of the META group (0x0002) among which is "Transfer Syntax",
675       //   before switching the swap code to big endian. We have to postpone
676       //   the switching of the swap code since the META group is fully encoded
677       //   in little endian, and big endian coding only starts at the next
678       //   group. The corresponding code can be hard to analyse and adds
679       //   many additional unnecessary tests for regular tags.
680       // * the second strategy consists in waiting for trouble, that shall
681       //   appear when we find the first group with big endian encoding. This
682       //   is easy to detect since the length of a "Group Length" tag (the
683       //   ones with zero as element number) has to be of 4 (0x0004). When we
684       //   encouter 1024 (0x0400) chances are the encoding changed and we
685       //   found a group with big endian encoding.
686       // We shall use this second strategy. In order to make sure that we
687       // can interpret the presence of an apparently big endian encoded
688       // length of a "Group Length" without committing a big mistake, we
689       // add an additional check: we look in the allready parsed elements
690       // for the presence of a "Transfer Syntax" whose value has to be "big
691       // endian encoding". When this is the case, chances are we have got our
692       // hands on a big endian encoded file: we switch the swap code to
693       // big endian and proceed...
694       if ( (element  == 0x0000) && (length16 == 0x0400) ) {
695          if ( ! IsExplicitVRBigEndianTransferSyntax() ) {
696             dbg.Verbose(0, "gdcmHeader::FindLength", "not explicit VR");
697             errno = 1;
698             return;
699          }
700          length16 = 4;
701          SwitchSwapToBigEndian();
702          // Restore the unproperly loaded values i.e. the group, the element
703          // and the dictionary entry depending on them.
704          guint16 CorrectGroup   = SwapShort(ElVal->GetGroup());
705          guint16 CorrectElem    = SwapShort(ElVal->GetElement());
706          gdcmDictEntry * NewTag = GetDictEntryByNumber(CorrectGroup,
707                                                        CorrectElem);
708          if (!NewTag) {
709             // This correct tag is not in the dictionary. Create a new one.
710             NewTag = new gdcmDictEntry(CorrectGroup, CorrectElem);
711          }
712          // FIXME this can create a memory leaks on the old entry that be
713          // left unreferenced.
714          ElVal->SetDictEntry(NewTag);
715       }
716        
717       // Heuristic: well some files are really ill-formed.
718       if ( length16 == 0xffff) {
719          length16 = 0;
720          dbg.Verbose(0, "gdcmHeader::FindLength",
721                      "Erroneous element length fixed.");
722       }
723       FixFoundLength(ElVal, (guint32)length16);
724       return;
725    }
726
727    // Either implicit VR or a non DICOM conformal (see not below) explicit
728    // VR that ommited the VR of (at least) this element. Farts happen.
729    // [Note: according to the part 5, PS 3.5-2001, section 7.1 p25
730    // on Data elements "Implicit and Explicit VR Data Elements shall
731    // not coexist in a Data Set and Data Sets nested within it".]
732    // Length is on 4 bytes.
733    FixFoundLength(ElVal, ReadInt32());
734 }
735
736 /**
737  * \ingroup gdcmHeader
738  * \brief   Swaps back the bytes of 4-byte long integer accordingly to
739  *          processor order.
740  *
741  * @return  The suggested integer.
742  */
743 guint32 gdcmHeader::SwapLong(guint32 a) {
744    switch (sw) {
745    case    0 :
746       break;
747    case 4321 :
748       a=(   ((a<<24) & 0xff000000) | ((a<<8)  & 0x00ff0000)    | 
749             ((a>>8)  & 0x0000ff00) | ((a>>24) & 0x000000ff) );
750       break;
751    
752    case 3412 :
753       a=(   ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
754       break;
755    
756    case 2143 :
757       a=(    ((a<<8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff)  );
758       break;
759    default :
760       dbg.Error(" gdcmHeader::SwapLong : unset swap code");
761       a=0;
762    }
763    return(a);
764 }
765
766 /**
767  * \ingroup gdcmHeader
768  * \brief   Swaps the bytes so they agree with the processor order
769  * @return  The properly swaped 16 bits integer.
770  */
771 guint16 gdcmHeader::SwapShort(guint16 a) {
772    if ( (sw==4321)  || (sw==2143) )
773       a =(((a<<8) & 0x0ff00) | ((a>>8)&0x00ff));
774    return (a);
775 }
776
777 /**
778  * \ingroup gdcmHeader
779  * \brief   
780  *
781  * @return 
782  */
783  void gdcmHeader::SkipBytes(guint32 NBytes) {
784    //FIXME don't dump the returned value
785    (void)fseek(fp, (long)NBytes, SEEK_CUR);
786 }
787
788 /**
789  * \ingroup gdcmHeader
790  * \brief   
791  *
792  * @return 
793  */
794  void gdcmHeader::SkipElementValue(gdcmElValue * ElVal) {
795    SkipBytes(ElVal->GetLength());
796 }
797
798 /**
799  * \ingroup gdcmHeader
800  * \brief   
801  *
802  * @return 
803  */
804  void gdcmHeader::SetMaxSizeLoadElementValue(long NewSize) {
805    if (NewSize < 0)
806       return;
807    if ((guint32)NewSize >= (guint32)0xffffffff) {
808       MaxSizeLoadElementValue = 0xffffffff;
809       return;
810    }
811    MaxSizeLoadElementValue = NewSize;
812 }
813
814 /**
815  * \ingroup       gdcmHeader
816  * \brief         Loads the element content if it's length is not bigger
817  *                than the value specified with
818  *                gdcmHeader::SetMaxSizeLoadElementValue()
819  */
820 void gdcmHeader::LoadElementValue(gdcmElValue * ElVal) {
821    size_t item_read;
822    guint16 group  = ElVal->GetGroup();
823    string  vr     = ElVal->GetVR();
824    guint32 length = ElVal->GetLength();
825    bool SkipLoad  = false;
826
827    fseek(fp, (long)ElVal->GetOffset(), SEEK_SET);
828    
829    // FIXME Sequences not treated yet !
830    //
831    // Ne faudrait-il pas au contraire trouver immediatement
832    // une maniere 'propre' de traiter les sequences (vr = SQ)
833    // car commencer par les ignorer risque de conduire a qq chose
834    // qui pourrait ne pas etre generalisable
835    // Well, I'm expecting your code !!!
836     
837    if( vr == "SQ" )
838       SkipLoad = true;
839
840    // Heuristic : a sequence "contains" a set of tags (called items). It looks
841    // like the last tag of a sequence (the one that terminates the sequence)
842    // has a group of 0xfffe (with a dummy length).
843    if( group == 0xfffe )
844       SkipLoad = true;
845
846    if ( SkipLoad ) {
847       ElVal->SetLength(0);
848       ElVal->SetValue("gdcm::Skipped");
849       return;
850    }
851
852    // When the length is zero things are easy:
853    if ( length == 0 ) {
854       ElVal->SetValue("");
855       return;
856    }
857
858    // The elements whose length is bigger than the specified upper bound
859    // are not loaded. Instead we leave a short notice of the offset of
860    // the element content and it's length.
861    if (length > MaxSizeLoadElementValue) {
862       ostringstream s;
863       s << "gdcm::NotLoaded.";
864       s << " Address:" << (long)ElVal->GetOffset();
865       s << " Length:"  << ElVal->GetLength();
866       ElVal->SetValue(s.str());
867       return;
868    }
869    
870    // When an integer is expected, read and convert the following two or
871    // four bytes properly i.e. as an integer as opposed to a string.
872         
873         // pour les elements de Value Multiplicity > 1
874         // on aura en fait une serie d'entiers
875         
876         // on devrait pouvoir faire + compact (?)
877                 
878    if ( IsAnInteger(ElVal) ) {
879       guint32 NewInt;
880       ostringstream s;
881       int nbInt;
882       if (vr == "US" || vr == "SS") {
883          nbInt = length / 2;
884          NewInt = ReadInt16();
885          s << NewInt;
886          if (nbInt > 1) {
887             for (int i=1; i < nbInt; i++) {
888                s << '\\';
889                NewInt = ReadInt16();
890                s << NewInt;
891             }
892          }
893                         
894       } else if (vr == "UL" || vr == "SL") {
895          nbInt = length / 4;
896          NewInt = ReadInt32();
897          s << NewInt;
898          if (nbInt > 1) {
899             for (int i=1; i < nbInt; i++) {
900                s << '\\';
901                NewInt = ReadInt32();
902                s << NewInt;
903             }
904          }
905       }                                 
906       ElVal->SetValue(s.str());
907       return;   
908    }
909    
910    // We need an additional byte for storing \0 that is not on disk
911    char* NewValue = (char*)malloc(length+1);
912    if( !NewValue) {
913       dbg.Verbose(1, "LoadElementValue: Failed to allocate NewValue");
914       return;
915    }
916    NewValue[length]= 0;
917    
918    item_read = fread(NewValue, (size_t)length, (size_t)1, fp);
919    if ( item_read != 1 ) {
920       free(NewValue);
921       dbg.Verbose(1, "gdcmHeader::LoadElementValue","unread element value");
922       ElVal->SetValue("gdcm::UnRead");
923       return;
924    }
925    ElVal->SetValue(NewValue);
926    free(NewValue);
927 }
928
929 /**
930  * \ingroup       gdcmHeader
931  * \brief         Loads the element while preserving the current
932  *                underlying file position indicator as opposed to
933  *                to LoadElementValue that modifies it.
934  * @param ElVal   Element whose value shall be loaded. 
935  * @return  
936  */
937 void gdcmHeader::LoadElementValueSafe(gdcmElValue * ElVal) {
938    long PositionOnEntry = ftell(fp);
939    LoadElementValue(ElVal);
940    fseek(fp, PositionOnEntry, SEEK_SET);
941 }
942
943 /**
944  * \ingroup gdcmHeader
945  * \brief   
946  *
947  * @return 
948  */
949 guint16 gdcmHeader::ReadInt16(void) {
950    guint16 g;
951    size_t item_read;
952    item_read = fread (&g, (size_t)2,(size_t)1, fp);
953    if ( item_read != 1 ) {
954       dbg.Verbose(1, "gdcmHeader::ReadInt16", " Failed to read :");
955       if(feof(fp)) 
956          dbg.Verbose(1, "gdcmHeader::ReadInt16", " End of File encountered");
957      if(ferror(fp)) 
958          dbg.Verbose(1, "gdcmHeader::ReadInt16", " File Error");
959       errno = 1;
960       return 0;
961    }
962    errno = 0;
963    g = SwapShort(g);
964    return g;
965 }
966
967 /**
968  * \ingroup gdcmHeader
969  * \brief   
970  *
971  * @return 
972  */
973 guint32 gdcmHeader::ReadInt32(void) {
974    guint32 g;
975    size_t item_read;
976    item_read = fread (&g, (size_t)4,(size_t)1, fp);
977    if ( item_read != 1 ) {
978    
979       dbg.Verbose(1, "gdcmHeader::ReadInt32", " Failed to read :");
980       if(feof(fp)) 
981          dbg.Verbose(1, "gdcmHeader::ReadInt32", " End of File encountered");
982      if(ferror(fp)) 
983          dbg.Verbose(1, "gdcmHeader::ReadInt32", " File Error");   
984       errno = 1;
985       return 0;
986    }
987    errno = 0;   
988    g = SwapLong(g);
989    return g;
990 }
991
992 /**
993  * \ingroup gdcmHeader
994  * \brief   
995  *
996  * @return 
997  */
998  gdcmElValue* gdcmHeader::GetElValueByNumber(guint16 Group, guint16 Elem) {
999
1000    gdcmElValue* elValue = PubElValSet.GetElementByNumber(Group, Elem);   
1001    if (!elValue) {
1002       dbg.Verbose(1, "gdcmHeader::GetElValueByNumber",
1003                   "failed to Locate gdcmElValue");
1004       return (gdcmElValue*)0;
1005    }
1006    return elValue;
1007 }
1008
1009 /**
1010  * \ingroup gdcmHeader
1011  * \brief   Build a new Element Value from all the low level arguments. 
1012  *          Check for existence of dictionary entry, and build
1013  *          a default one when absent.
1014  * @param   Group group   of the underlying DictEntry
1015  * @param   Elem  element of the underlying DictEntry
1016  */
1017 gdcmElValue* gdcmHeader::NewElValueByNumber(guint16 Group, guint16 Elem) {
1018    // Find out if the tag we encountered is in the dictionaries:
1019    gdcmDictEntry * NewTag = GetDictEntryByNumber(Group, Elem);
1020    if (!NewTag)
1021       NewTag = new gdcmDictEntry(Group, Elem);
1022
1023    gdcmElValue* NewElVal = new gdcmElValue(NewTag);
1024    if (!NewElVal) {
1025       dbg.Verbose(1, "gdcmHeader::NewElValueByNumber",
1026                   "failed to allocate gdcmElValue");
1027       return (gdcmElValue*)0;
1028    }
1029    return NewElVal;
1030 }
1031
1032 /**
1033  * \ingroup gdcmHeader
1034  * \brief   TODO
1035  * @param   
1036  */
1037 int gdcmHeader::ReplaceOrCreateByNumber(string Value, guint16 Group, guint16 Elem ) {
1038
1039         // TODO : FIXME JPRx
1040         // curieux, non ?
1041         // on (je) cree une Elvalue ne contenant pas de valeur
1042         // on l'ajoute au ElValSet
1043         // on affecte une valeur a cette ElValue a l'interieur du ElValSet
1044         // --> devrait pouvoir etre fait + simplement ???
1045         
1046         gdcmElValue* nvElValue=NewElValueByNumber(Group, Elem);
1047         PubElValSet.Add(nvElValue);     
1048         PubElValSet.SetElValueByNumber(Value, Group, Elem);
1049         return(1);
1050 }   
1051
1052
1053 /**
1054  * \ingroup gdcmHeader
1055  * \brief   TODO
1056  * @param   
1057  */
1058 int gdcmHeader::ReplaceOrCreateByNumber(char* Value, guint16 Group, guint16 Elem ) {
1059
1060         gdcmElValue* nvElValue=NewElValueByNumber(Group, Elem);
1061         PubElValSet.Add(nvElValue);
1062         string v = Value;       
1063         PubElValSet.SetElValueByNumber(v, Group, Elem);
1064         return(1);
1065 }  
1066
1067 /**
1068  * \ingroup gdcmHeader
1069  * \brief   TODO
1070  * @param   
1071  */
1072  
1073  int gdcmHeader::CheckIfExistByNumber(guint16 Group, guint16 Elem ) {
1074         return (PubElValSet.CheckIfExistByNumber(Group, Elem));
1075  }
1076  
1077  
1078 /**
1079  * \ingroup gdcmHeader
1080  * \brief   Build a new Element Value from all the low level arguments. 
1081  *          Check for existence of dictionary entry, and build
1082  *          a default one when absent.
1083  * @param   Name    Name of the underlying DictEntry
1084  */
1085 gdcmElValue* gdcmHeader::NewElValueByName(string Name) {
1086
1087    gdcmDictEntry * NewTag = GetDictEntryByName(Name);
1088    if (!NewTag)
1089       NewTag = new gdcmDictEntry(0xffff, 0xffff, "LO", "Unknown", Name);
1090
1091    gdcmElValue* NewElVal = new gdcmElValue(NewTag);
1092    if (!NewElVal) {
1093       dbg.Verbose(1, "gdcmHeader::ObtainElValueByName",
1094                   "failed to allocate gdcmElValue");
1095       return (gdcmElValue*)0;
1096    }
1097    return NewElVal;
1098 }  
1099
1100 /**
1101  * \ingroup gdcmHeader
1102  * \brief   Read the next tag but WITHOUT loading it's value
1103  * @return  On succes the newly created ElValue, NULL on failure.      
1104  */
1105 gdcmElValue * gdcmHeader::ReadNextElement(void) {
1106   
1107    guint16 g,n;
1108    gdcmElValue * NewElVal;
1109    
1110    g = ReadInt16();
1111    n = ReadInt16();
1112    
1113    if ( (g==0x7fe0) && (n==0x0010) ) 
1114         if (DEBUG) 
1115                 printf("in gdcmHeader::ReadNextElement try to read 7fe0 0010 \n");
1116    
1117    if (errno == 1)
1118       // We reached the EOF (or an error occured) and header parsing
1119       // has to be considered as finished.
1120       return (gdcmElValue *)0;
1121    
1122    NewElVal = NewElValueByNumber(g, n);
1123    FindVR(NewElVal);
1124    FindLength(NewElVal);
1125    if (errno == 1) {
1126       // Call it quits
1127       if (DEBUG) printf("in gdcmHeader::ReadNextElement : g %04x n %04x errno %d\n",g, n, errno);
1128       return (gdcmElValue *)0;
1129    }
1130    NewElVal->SetOffset(ftell(fp));  
1131    if ( (g==0x7fe0) && (n==0x0010) ) 
1132         if (DEBUG) 
1133                 printf("sortie de gdcmHeader::ReadNextElement 7fe0 0010 \n");
1134    return NewElVal;
1135 }
1136
1137 /**
1138  * \ingroup gdcmHeader
1139  * \brief   Apply some heuristics to predict wether the considered 
1140  *          element value contains/represents an integer or not.
1141  * @param   ElVal The element value on which to apply the predicate.
1142  * @return  The result of the heuristical predicate.
1143  */
1144 bool gdcmHeader::IsAnInteger(gdcmElValue * ElVal) {
1145    guint16 group   = ElVal->GetGroup();
1146    guint16 element = ElVal->GetElement();
1147    string  vr      = ElVal->GetVR();
1148    guint32 length  = ElVal->GetLength();
1149
1150    // When we have some semantics on the element we just read, and if we
1151    // a priori know we are dealing with an integer, then we shall be
1152    // able to swap it's element value properly.
1153    if ( element == 0 )  {  // This is the group length of the group
1154       if (length == 4)
1155          return true;
1156       else {
1157          if (DEBUG) printf("Erroneous Group Length element length (%04x , %04x) : %d\n",
1158             group, element,length);
1159                     
1160          dbg.Error("gdcmHeader::IsAnInteger",
1161             "Erroneous Group Length element length.");     
1162       }
1163    }
1164    if ( (vr == "UL") || (vr == "US") || (vr == "SL") || (vr == "SS") )
1165       return true;
1166    
1167    return false;
1168 }
1169
1170 /**
1171  * \ingroup gdcmHeader
1172  * \brief   Recover the offset (from the beginning of the file) of the pixels.
1173  */
1174 size_t gdcmHeader::GetPixelOffset(void) {
1175    // If this file complies with the norm we should encounter the
1176    // "Image Location" tag (0x0028,  0x0200). This tag contains the
1177    // the group that contains the pixel data (hence the "Pixel Data"
1178    // is found by indirection through the "Image Location").
1179    // Inside the group pointed by "Image Location" the searched element
1180    // is conventionally the element 0x0010 (when the norm is respected).
1181    // When the "Image Location" is absent we default to group 0x7fe0.
1182    guint16 grPixel;
1183    guint16 numPixel;
1184    string ImageLocation = GetPubElValByName("Image Location");
1185    if ( ImageLocation == "gdcm::Unfound" ) {
1186       grPixel = 0x7fe0;
1187    } else {
1188       grPixel = (guint16) atoi( ImageLocation.c_str() );
1189    }
1190    if (grPixel != 0x7fe0)
1191       // This is a kludge for old dirty Philips imager.
1192       numPixel = 0x1010;
1193    else
1194       numPixel = 0x0010;
1195          
1196    gdcmElValue* PixelElement = PubElValSet.GetElementByNumber(grPixel,
1197                                                               numPixel);
1198    if (PixelElement)
1199       return PixelElement->GetOffset();
1200    else
1201       return 0;
1202 }
1203
1204 /**
1205  * \ingroup gdcmHeader
1206  * \brief   Searches both the public and the shadow dictionary (when they
1207  *          exist) for the presence of the DictEntry with given
1208  *          group and element. The public dictionary has precedence on the
1209  *          shadow one.
1210  * @param   group   group of the searched DictEntry
1211  * @param   element element of the searched DictEntry
1212  * @return  Corresponding DictEntry when it exists, NULL otherwise.
1213  */
1214 gdcmDictEntry * gdcmHeader::GetDictEntryByNumber(guint16 group,
1215                                                  guint16 element) {
1216    gdcmDictEntry * found = (gdcmDictEntry*)0;
1217    if (!RefPubDict && !RefShaDict) {
1218       dbg.Verbose(0, "gdcmHeader::GetDictEntry",
1219                      "we SHOULD have a default dictionary");
1220    }
1221    if (RefPubDict) {
1222       found = RefPubDict->GetTagByNumber(group, element);
1223       if (found)
1224          return found;
1225    }
1226    if (RefShaDict) {
1227       found = RefShaDict->GetTagByNumber(group, element);
1228       if (found)
1229          return found;
1230    }
1231    return found;
1232 }
1233
1234 /**
1235  * \ingroup gdcmHeader
1236  * \brief   Searches both the public and the shadow dictionary (when they
1237  *          exist) for the presence of the DictEntry with given name.
1238  *          The public dictionary has precedence on the shadow one.
1239  * @param   Name name of the searched DictEntry
1240  * @return  Corresponding DictEntry when it exists, NULL otherwise.
1241  */
1242 gdcmDictEntry * gdcmHeader::GetDictEntryByName(string Name) {
1243    gdcmDictEntry * found = (gdcmDictEntry*)0;
1244    if (!RefPubDict && !RefShaDict) {
1245       dbg.Verbose(0, "gdcmHeader::GetDictEntry",
1246                      "we SHOULD have a default dictionary");
1247    }
1248    if (RefPubDict) {
1249       found = RefPubDict->GetTagByName(Name);
1250       if (found)
1251          return found;
1252    }
1253    if (RefShaDict) {
1254       found = RefShaDict->GetTagByName(Name);
1255       if (found)
1256          return found;
1257    }
1258    return found;
1259 }
1260
1261 /**
1262  * \ingroup gdcmHeader
1263  * \brief   Searches within the public dictionary for element value of
1264  *          a given tag.
1265  * @param   group Group of the researched tag.
1266  * @param   element Element of the researched tag.
1267  * @return  Corresponding element value when it exists, and the string
1268  *          "gdcm::Unfound" otherwise.
1269  */
1270 string gdcmHeader::GetPubElValByNumber(guint16 group, guint16 element) {
1271    return PubElValSet.GetElValueByNumber(group, element);
1272 }
1273
1274 /**
1275  * \ingroup gdcmHeader
1276  * \brief   Searches within the public dictionary for element value
1277  *          representation of a given tag.
1278  *
1279  *          Obtaining the VR (Value Representation) might be needed by caller
1280  *          to convert the string typed content to caller's native type 
1281  *          (think of C++ vs Python). The VR is actually of a higher level
1282  *          of semantics than just the native C++ type.
1283  * @param   group Group of the researched tag.
1284  * @param   element Element of the researched tag.
1285  * @return  Corresponding element value representation when it exists,
1286  *          and the string "gdcm::Unfound" otherwise.
1287  */
1288 string gdcmHeader::GetPubElValRepByNumber(guint16 group, guint16 element) {
1289    gdcmElValue* elem =  PubElValSet.GetElementByNumber(group, element);
1290    if ( !elem )
1291       return "gdcm::Unfound";
1292    return elem->GetVR();
1293 }
1294
1295 /**
1296  * \ingroup gdcmHeader
1297  * \brief   Searches within the public dictionary for element value of
1298  *          a given tag.
1299  * @param   TagName name of the researched element.
1300  * @return  Corresponding element value when it exists, and the string
1301  *          "gdcm::Unfound" otherwise.
1302  */
1303 string gdcmHeader::GetPubElValByName(string TagName) {
1304    return PubElValSet.GetElValueByName(TagName);
1305 }
1306
1307 /**
1308  * \ingroup gdcmHeader
1309  * \brief   Searches within the elements parsed with the public dictionary for
1310  *          the element value representation of a given tag.
1311  *
1312  *          Obtaining the VR (Value Representation) might be needed by caller
1313  *          to convert the string typed content to caller's native type 
1314  *          (think of C++ vs Python). The VR is actually of a higher level
1315  *          of semantics than just the native C++ type.
1316  * @param   TagName name of the researched element.
1317  * @return  Corresponding element value representation when it exists,
1318  *          and the string "gdcm::Unfound" otherwise.
1319  */
1320 string gdcmHeader::GetPubElValRepByName(string TagName) {
1321    gdcmElValue* elem =  PubElValSet.GetElementByName(TagName);
1322    if ( !elem )
1323       return "gdcm::Unfound";
1324    return elem->GetVR();
1325 }
1326
1327 /**
1328  * \ingroup gdcmHeader
1329  * \brief   Searches within elements parsed with the SHADOW dictionary 
1330  *          for the element value of a given tag.
1331  * @param   group Group of the researched tag.
1332  * @param   element Element of the researched tag.
1333  * @return  Corresponding element value representation when it exists,
1334  *          and the string "gdcm::Unfound" otherwise.
1335  */
1336 string gdcmHeader::GetShaElValByNumber(guint16 group, guint16 element) {
1337    return ShaElValSet.GetElValueByNumber(group, element);
1338 }
1339
1340 /**
1341  * \ingroup gdcmHeader
1342  * \brief   Searches within the elements parsed with the SHADOW dictionary
1343  *          for the element value representation of a given tag.
1344  *
1345  *          Obtaining the VR (Value Representation) might be needed by caller
1346  *          to convert the string typed content to caller's native type 
1347  *          (think of C++ vs Python). The VR is actually of a higher level
1348  *          of semantics than just the native C++ type.
1349  * @param   group Group of the researched tag.
1350  * @param   element Element of the researched tag.
1351  * @return  Corresponding element value representation when it exists,
1352  *          and the string "gdcm::Unfound" otherwise.
1353  */
1354 string gdcmHeader::GetShaElValRepByNumber(guint16 group, guint16 element) {
1355    gdcmElValue* elem =  ShaElValSet.GetElementByNumber(group, element);
1356    if ( !elem )
1357       return "gdcm::Unfound";
1358    return elem->GetVR();
1359 }
1360
1361 /**
1362  * \ingroup gdcmHeader
1363  * \brief   Searches within the elements parsed with the shadow dictionary
1364  *          for an element value of given tag.
1365  * @param   TagName name of the researched element.
1366  * @return  Corresponding element value when it exists, and the string
1367  *          "gdcm::Unfound" otherwise.
1368  */
1369 string gdcmHeader::GetShaElValByName(string TagName) {
1370    return ShaElValSet.GetElValueByName(TagName);
1371 }
1372
1373 /**
1374  * \ingroup gdcmHeader
1375  * \brief   Searches within the elements parsed with the shadow dictionary for
1376  *          the element value representation of a given tag.
1377  *
1378  *          Obtaining the VR (Value Representation) might be needed by caller
1379  *          to convert the string typed content to caller's native type 
1380  *          (think of C++ vs Python). The VR is actually of a higher level
1381  *          of semantics than just the native C++ type.
1382  * @param   TagName name of the researched element.
1383  * @return  Corresponding element value representation when it exists,
1384  *          and the string "gdcm::Unfound" otherwise.
1385  */
1386 string gdcmHeader::GetShaElValRepByName(string TagName) {
1387    gdcmElValue* elem =  ShaElValSet.GetElementByName(TagName);
1388    if ( !elem )
1389       return "gdcm::Unfound";
1390    return elem->GetVR();
1391 }
1392
1393 /**
1394  * \ingroup gdcmHeader
1395  * \brief   Searches within elements parsed with the public dictionary 
1396  *          and then within the elements parsed with the shadow dictionary
1397  *          for the element value of a given tag.
1398  * @param   group Group of the researched tag.
1399  * @param   element Element of the researched tag.
1400  * @return  Corresponding element value representation when it exists,
1401  *          and the string "gdcm::Unfound" otherwise.
1402  */
1403 string gdcmHeader::GetElValByNumber(guint16 group, guint16 element) {
1404    string pub = GetPubElValByNumber(group, element);
1405    if (pub.length())
1406       return pub;
1407    return GetShaElValByNumber(group, element);
1408 }
1409
1410 /**
1411  * \ingroup gdcmHeader
1412  * \brief   Searches within elements parsed with the public dictionary 
1413  *          and then within the elements parsed with the shadow dictionary
1414  *          for the element value representation of a given tag.
1415  *
1416  *          Obtaining the VR (Value Representation) might be needed by caller
1417  *          to convert the string typed content to caller's native type 
1418  *          (think of C++ vs Python). The VR is actually of a higher level
1419  *          of semantics than just the native C++ type.
1420  * @param   group Group of the researched tag.
1421  * @param   element Element of the researched tag.
1422  * @return  Corresponding element value representation when it exists,
1423  *          and the string "gdcm::Unfound" otherwise.
1424  */
1425 string gdcmHeader::GetElValRepByNumber(guint16 group, guint16 element) {
1426    string pub = GetPubElValRepByNumber(group, element);
1427    if (pub.length())
1428       return pub;
1429    return GetShaElValRepByNumber(group, element);
1430 }
1431
1432 /**
1433  * \ingroup gdcmHeader
1434  * \brief   Searches within elements parsed with the public dictionary 
1435  *          and then within the elements parsed with the shadow dictionary
1436  *          for the element value of a given tag.
1437  * @param   TagName name of the researched element.
1438  * @return  Corresponding element value when it exists,
1439  *          and the string "gdcm::Unfound" otherwise.
1440  */
1441 string gdcmHeader::GetElValByName(string TagName) {
1442    string pub = GetPubElValByName(TagName);
1443    if (pub.length())
1444       return pub;
1445    return GetShaElValByName(TagName);
1446 }
1447
1448 /**
1449  * \ingroup gdcmHeader
1450  * \brief   Searches within elements parsed with the public dictionary 
1451  *          and then within the elements parsed with the shadow dictionary
1452  *          for the element value representation of a given tag.
1453  *
1454  *          Obtaining the VR (Value Representation) might be needed by caller
1455  *          to convert the string typed content to caller's native type 
1456  *          (think of C++ vs Python). The VR is actually of a higher level
1457  *          of semantics than just the native C++ type.
1458  * @param   TagName name of the researched element.
1459  * @return  Corresponding element value representation when it exists,
1460  *          and the string "gdcm::Unfound" otherwise.
1461  */
1462 string gdcmHeader::GetElValRepByName(string TagName) {
1463    string pub = GetPubElValRepByName(TagName);
1464    if (pub.length())
1465       return pub;
1466    return GetShaElValRepByName(TagName);
1467 }
1468
1469 /**
1470  * \ingroup gdcmHeader
1471  * \brief   Accesses an existing gdcmElValue in the PubElValSet of this instance
1472  *          through it's (group, element) and modifies it's content with
1473  *          the given value.
1474  * @param   content new value to substitute with
1475  * @param   group   group of the ElVal to modify
1476  * @param   element element of the ElVal to modify
1477  */
1478 int gdcmHeader::SetPubElValByNumber(string content, guint16 group,
1479                                     guint16 element)
1480                                     
1481 //TODO  : homogeneiser les noms : SetPubElValByNumber   qui appelle PubElValSet.SetElValueByNumber 
1482 //        pourquoi pas            SetPubElValueByNumber ??
1483 {
1484
1485    return (  PubElValSet.SetElValueByNumber (content, group, element) );
1486 }
1487
1488 /**
1489  * \ingroup gdcmHeader
1490  * \brief   Accesses an existing gdcmElValue in the PubElValSet of this instance
1491  *          through tag name and modifies it's content with the given value.
1492  * @param   content new value to substitute with
1493  * @param   TagName name of the tag to be modified
1494  */
1495 int gdcmHeader::SetPubElValByName(string content, string TagName) {
1496    return (  PubElValSet.SetElValueByName (content, TagName) );
1497 }
1498
1499 /**
1500  * \ingroup gdcmHeader
1501  * \brief   Accesses an existing gdcmElValue in the PubElValSet of this instance
1502  *          through it's (group, element) and modifies it's length with
1503  *          the given value.
1504  * \warning Use with extreme caution.
1505  * @param   length new length to substitute with
1506  * @param   group   group of the ElVal to modify
1507  * @param   element element of the ElVal to modify
1508  * @return  1 on success, 0 otherwise.
1509  */
1510
1511 int gdcmHeader::SetPubElValLengthByNumber(guint32 length, guint16 group,
1512                                     guint16 element) {
1513         return (  PubElValSet.SetElValueLengthByNumber (length, group, element) );
1514 }
1515
1516 /**
1517  * \ingroup gdcmHeader
1518  * \brief   Accesses an existing gdcmElValue in the ShaElValSet of this instance
1519  *          through it's (group, element) and modifies it's content with
1520  *          the given value.
1521  * @param   content new value to substitute with
1522  * @param   group   group of the ElVal to modify
1523  * @param   element element of the ElVal to modify
1524  * @return  1 on success, 0 otherwise.
1525  */
1526 int gdcmHeader::SetShaElValByNumber(string content,
1527                                     guint16 group, guint16 element) {
1528    return (  ShaElValSet.SetElValueByNumber (content, group, element) );
1529 }
1530
1531 /**
1532  * \ingroup gdcmHeader
1533  * \brief   Accesses an existing gdcmElValue in the ShaElValSet of this instance
1534  *          through tag name and modifies it's content with the given value.
1535  * @param   content new value to substitute with
1536  * @param   ShadowTagName name of the tag to be modified
1537  */
1538 int gdcmHeader::SetShaElValByName(string content, string ShadowTagName) {
1539    return (  ShaElValSet.SetElValueByName (content, ShadowTagName) );
1540 }
1541
1542 /**
1543  * \ingroup gdcmHeader
1544  * \brief   Parses the header of the file but WITHOUT loading element values.
1545  */
1546 void gdcmHeader::ParseHeader(bool exception_on_error) throw(gdcmFormatError) {
1547    gdcmElValue * newElValue = (gdcmElValue *)0;
1548    
1549    rewind(fp);
1550    CheckSwap();
1551    while ( (newElValue = ReadNextElement()) ) {
1552       SkipElementValue(newElValue);
1553       PubElValSet.Add(newElValue);
1554    }
1555 }
1556
1557
1558 //
1559 // TODO : JPR
1560 // des que les element values sont chargees, stocker, 
1561 // en une seule fois, dans des entiers 
1562 // NX, NY, NZ, Bits allocated, Bits Stored, High Bit, Samples Per Pixel
1563 // (TODO : preciser les autres)
1564 // et refaire ceux des accesseurs qui renvoient les entiers correspondants
1565 //
1566 // --> peut etre dangereux ?
1567 // si l'utilisateur modifie 'manuellement' l'un des paramètres
1568 // l'entier de sera pas modifié ...
1569 // (pb de la mise Ã  jour en cas de redondance :-(
1570
1571 /**
1572  * \ingroup gdcmHeader
1573  * \brief   Retrieve the number of columns of image.
1574  * @return  The encountered size when found, 0 by default.
1575  */
1576 int gdcmHeader::GetXSize(void) {
1577    // We cannot check for "Columns" because the "Columns" tag is present
1578    // both in IMG (0028,0011) and OLY (6000,0011) sections of the dictionary.
1579    string StrSize = GetPubElValByNumber(0x0028,0x0011);
1580    if (StrSize == "gdcm::Unfound")
1581       return 0;
1582    return atoi(StrSize.c_str());
1583 }
1584
1585 /**
1586  * \ingroup gdcmHeader
1587  * \brief   Retrieve the number of lines of image.
1588  * \warning The defaulted value is 1 as opposed to gdcmHeader::GetXSize()
1589  * @return  The encountered size when found, 1 by default.
1590  */
1591 int gdcmHeader::GetYSize(void) {
1592    // We cannot check for "Rows" because the "Rows" tag is present
1593    // both in IMG (0028,0010) and OLY (6000,0010) sections of the dictionary.
1594    string StrSize = GetPubElValByNumber(0x0028,0x0010);
1595    if (StrSize != "gdcm::Unfound")
1596       return atoi(StrSize.c_str());
1597    if ( IsDicomV3() )
1598       return 0;
1599    else
1600       // The Rows (0028,0010) entry is optional for ACR/NEMA. It might
1601       // hence be a signal (1d image). So we default to 1:
1602       return 1;
1603 }
1604
1605 /**
1606  * \ingroup gdcmHeader
1607  * \brief   Retrieve the number of planes of volume or the number
1608  *          of frames of a multiframe.
1609  * \warning When present we consider the "Number of Frames" as the third
1610  *          dimension. When absent we consider the third dimension as
1611  *          being the "Planes" tag content.
1612  * @return  The encountered size when found, 1 by default.
1613  */
1614 int gdcmHeader::GetZSize(void) {
1615    // Both in DicomV3 and ACR/Nema the consider the "Number of Frames"
1616    // as the third dimension.
1617    string StrSize = GetPubElValByNumber(0x0028,0x0008);
1618    if (StrSize != "gdcm::Unfound")
1619       return atoi(StrSize.c_str());
1620
1621    // We then consider the "Planes" entry as the third dimension [we
1622    // cannot retrieve by name since "Planes tag is present both in
1623    // IMG (0028,0012) and OLY (6000,0012) sections of the dictionary]. 
1624    StrSize = GetPubElValByNumber(0x0028,0x0012);
1625    if (StrSize != "gdcm::Unfound")
1626       return atoi(StrSize.c_str());
1627    return 1;
1628 }
1629
1630
1631 /**
1632  * \ingroup gdcmHeader
1633  * \brief   Retrieve the number of Bits Stored
1634  *          (as opposite to number of Bits Allocated)
1635  * 
1636  * @return  The encountered number of Bits Stored, 0 by default.
1637  */
1638 int gdcmHeader::GetBitsStored(void) { 
1639    string StrSize = GetPubElValByNumber(0x0028,0x0101);
1640    if (StrSize == "gdcm::Unfound")
1641       return 1;
1642    return atoi(StrSize.c_str());
1643 }
1644
1645
1646 /**
1647  * \ingroup gdcmHeader
1648  * \brief   Retrieve the number of Samples Per Pixel
1649  *          (1 : gray level, 3 : RGB)
1650  * 
1651  * @return  The encountered number of Samples Per Pixel, 1 by default.
1652  */
1653 int gdcmHeader::GetSamplesPerPixel(void) { 
1654    string StrSize = GetPubElValByNumber(0x0028,0x0002);
1655    if (StrSize == "gdcm::Unfound")
1656       return 1; // Well, it's supposed to be mandatory ...
1657    return atoi(StrSize.c_str());
1658 }
1659 /**
1660  * \ingroup gdcmHeader
1661  * \brief   Return the size (in bytes) of a single pixel of data.
1662  * @return  The size in bytes of a single pixel of data.
1663  *
1664  */
1665 int gdcmHeader::GetPixelSize(void) {
1666    string PixelType = GetPixelType();
1667    if (PixelType == "8U"  || PixelType == "8S")
1668       return 1;
1669    if (PixelType == "16U" || PixelType == "16S")
1670       return 2;
1671    if (PixelType == "32U" || PixelType == "32S")
1672       return 4;
1673    dbg.Verbose(0, "gdcmHeader::GetPixelSize: Unknown pixel type");
1674    return 0;
1675 }
1676
1677 /**
1678  * \ingroup gdcmHeader
1679  * \brief   Build the Pixel Type of the image.
1680  *          Possible values are:
1681  *          - 8U  unsigned  8 bit,
1682  *          - 8S    signed  8 bit,
1683  *          - 16U unsigned 16 bit,
1684  *          - 16S   signed 16 bit,
1685  *          - 32U unsigned 32 bit,
1686  *          - 32S   signed 32 bit,
1687  * \warning 12 bit images appear as 16 bit.
1688  * @return  
1689  */
1690 string gdcmHeader::GetPixelType(void) {
1691    string BitsAlloc;
1692    BitsAlloc = GetElValByName("Bits Allocated");
1693    if (BitsAlloc == "gdcm::Unfound") {
1694       dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Bits Allocated");
1695       BitsAlloc = string("16");
1696    }
1697    if (BitsAlloc == "12")
1698       BitsAlloc = string("16");
1699
1700    string Signed;
1701    Signed = GetElValByName("Pixel Representation");
1702    if (Signed == "gdcm::Unfound") {
1703       dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Pixel Representation");
1704       BitsAlloc = string("0");
1705    }
1706    if (Signed == "0")
1707       Signed = string("U");
1708    else
1709       Signed = string("S");
1710
1711    return( BitsAlloc + Signed);
1712 }
1713
1714
1715 /**
1716  * \ingroup gdcmHeader
1717  * \brief  This predicate, based on hopefully reasonnable heuristics,
1718  *         decides whether or not the current gdcmHeader was properly parsed
1719  *         and contains the mandatory information for being considered as
1720  *         a well formed and usable image.
1721  * @return true when gdcmHeader is the one of a reasonable Dicom file,
1722  *         false otherwise. 
1723  */
1724 bool gdcmHeader::IsReadable(void) {
1725    if (   GetElValByName("Image Dimensions") != "gdcm::Unfound"
1726       && atoi(GetElValByName("Image Dimensions").c_str()) > 4 ) {
1727       return false;
1728    }
1729    if ( GetElValByName("Bits Allocated") == "gdcm::Unfound" )
1730       return false;
1731    if ( GetElValByName("Bits Stored") == "gdcm::Unfound" )
1732       return false;
1733    if ( GetElValByName("High Bit") == "gdcm::Unfound" )
1734       return false;
1735    if ( GetElValByName("Pixel Representation") == "gdcm::Unfound" )
1736       return false;
1737    return true;
1738 }
1739
1740 /**
1741  * \ingroup gdcmHeader
1742  * \brief   Small utility function that creates a new manually crafted
1743  *          (as opposed as read from the file) gdcmElValue with user
1744  *          specified name and adds it to the public tag hash table.
1745  * \note    A fake TagKey is generated so the PubDict can keep it's coherence.
1746  * @param   NewTagName The name to be given to this new tag.
1747  * @param   VR The Value Representation to be given to this new tag.
1748  * @ return The newly hand crafted Element Value.
1749  */
1750 gdcmElValue* gdcmHeader::NewManualElValToPubDict(string NewTagName, string VR) {
1751    gdcmElValue* NewElVal = (gdcmElValue*)0;
1752    guint32 StuffGroup = 0xffff;   // Group to be stuffed with additional info
1753    guint32 FreeElem = 0;
1754    gdcmDictEntry* NewEntry = (gdcmDictEntry*)0;
1755
1756    FreeElem = PubElValSet.GenerateFreeTagKeyInGroup(StuffGroup);
1757    if (FreeElem == UINT32_MAX) {
1758       dbg.Verbose(1, "gdcmHeader::NewManualElValToPubDict",
1759                      "Group 0xffff in Public Dict is full");
1760       return (gdcmElValue*)0;
1761    }
1762    NewEntry = new gdcmDictEntry(StuffGroup, FreeElem,
1763                                 VR, "GDCM", NewTagName);
1764    NewElVal = new gdcmElValue(NewEntry);
1765    PubElValSet.Add(NewElVal);
1766    return NewElVal;
1767 }
1768
1769 /**
1770  * \ingroup gdcmHeader
1771  * \brief   Loads the element values of all the elements present in the
1772  *          public tag based hash table.
1773  */
1774 void gdcmHeader::LoadElements(void) {
1775    rewind(fp);   
1776    TagElValueHT ht = PubElValSet.GetTagHt();
1777    for (TagElValueHT::iterator tag = ht.begin(); tag != ht.end(); ++tag) {
1778       LoadElementValue(tag->second);
1779    }
1780 }
1781
1782 /**
1783   * \ingroup gdcmHeader
1784   * \brief
1785   * @return
1786   */ 
1787 void gdcmHeader::PrintPubElVal(std::ostream & os) {
1788    PubElValSet.Print(os);
1789 }
1790
1791 /**
1792   * \ingroup gdcmHeader
1793   * \brief
1794   * @return
1795   */  
1796 void gdcmHeader::PrintPubDict(std::ostream & os) {
1797    RefPubDict->Print(os);
1798 }
1799
1800 /**
1801   * \ingroup gdcmHeader
1802   * \brief
1803   * @return
1804   */
1805   
1806 int gdcmHeader::Write(FILE * fp, FileType type) {
1807    return PubElValSet.Write(fp, type);
1808 }
1809
1810 /**
1811   * \ingroup gdcmHeader
1812   * \brief
1813   * @return
1814   */
1815 float gdcmHeader::GetXSpacing(void) {
1816     float xspacing, yspacing;
1817     string StrSpacing = GetPubElValByNumber(0x0028,0x0030);
1818
1819     if (StrSpacing == "gdcm::Unfound") {
1820        dbg.Verbose(0, "gdcmHeader::GetXSpacing: unfound Pixel Spacing");
1821        return 1.;
1822      }
1823    if( sscanf( StrSpacing.c_str(), "%f\\%f", &xspacing, &yspacing) != 2)
1824      return 0.;
1825    //else
1826    return xspacing;
1827 }
1828
1829 /**
1830   * \ingroup gdcmHeader
1831   * \brief
1832   * @return
1833   */
1834 float gdcmHeader::GetYSpacing(void) {
1835    float xspacing, yspacing;
1836    string StrSpacing = GetPubElValByNumber(0x0028,0x0030);
1837   
1838    if (StrSpacing == "gdcm::Unfound") {
1839       dbg.Verbose(0, "gdcmHeader::GetYSpacing: unfound Pixel Spacing");
1840       return 1.;
1841     }
1842
1843   if( sscanf( StrSpacing.c_str(), "%f\\%f", &xspacing, &yspacing) != 2)
1844     return 0.;
1845
1846   if (yspacing == 0.)
1847   {
1848     dbg.Verbose(0, "gdcmHeader::GetYSpacing: gdcmData/CT-MONO2-8-abdo.dcm problem");
1849     // seems to be a bug in the header ...
1850     sscanf( StrSpacing.c_str(), "%f\\0\\%f", &xspacing, &yspacing);
1851   }
1852   return yspacing;
1853
1854
1855
1856 /**
1857   * \ingroup gdcmHeader
1858   * \brief
1859   * @return
1860   */
1861 float gdcmHeader::GetZSpacing(void) {
1862    // TODO : translate into English
1863    // Spacing Between Slices : distance entre le milieu de chaque coupe
1864    // Les coupes peuvent etre :
1865    //   jointives     (Spacing between Slices = Slice Thickness)
1866    //   chevauchantes (Spacing between Slices < Slice Thickness)
1867    //   disjointes    (Spacing between Slices > Slice Thickness)
1868    // Slice Thickness : epaisseur de tissus sur laquelle est acquis le signal
1869    //   ca interesse le physicien de l'IRM, pas le visualisateur de volumes ...
1870    //   Si le Spacing Between Slices est absent, 
1871    //   on suppose que les coupes sont jointives
1872    
1873    string StrSpacingBSlices = GetPubElValByNumber(0x0018,0x0088);
1874
1875    if (StrSpacingBSlices == "gdcm::Unfound") {
1876       dbg.Verbose(0, "gdcmHeader::GetZSpacing: unfound StrSpacingBSlices");
1877       string StrSliceThickness = GetPubElValByNumber(0x0018,0x0050);       
1878       if (StrSliceThickness == "gdcm::Unfound")
1879          return 1.;
1880       else
1881          return atof(StrSliceThickness.c_str());  
1882    } else {
1883       return atof(StrSpacingBSlices.c_str());
1884    }
1885 }
1886
1887 //
1888 //  Image Position Patient :
1889 // If not found (AVR-NEMA), we consider Slice Location (20,1041)
1890 // or Location (20,50) as the Z coordinate, 
1891 // 0. for all the coordinates if Slice Location not found
1892 // TODO : find a way to inform the caller nothing was found
1893 // TODO : How to tell the caller a wrong number of values was found?
1894 /**
1895   * \ingroup gdcmHeader
1896   * \brief
1897   * @return
1898   */
1899 float gdcmHeader::GetXImagePosition(void) {
1900     float xImPos, yImPos, zImPos;
1901     // 0020,0032 : Image Position Patient
1902     // 0020,1041 : Slice Location
1903     string StrImPos = GetPubElValByNumber(0x0020,0x0032);
1904
1905     if (StrImPos == "gdcm::Unfound") {
1906        dbg.Verbose(0, "gdcmHeader::GetXImagePosition: unfound Image Position Patient (0020,0032)");
1907        StrImPos = GetPubElValByNumber(0x0020,0x0030); // For ACR-NEMA images
1908        if (StrImPos == "gdcm::Unfound") {
1909           dbg.Verbose(0, "gdcmHeader::GetXImagePosition: unfound Image Position (RET) (0020,0030)");
1910           string StrSliceLoc = GetPubElValByNumber(0x0020,0x1041); // for *very* old ACR-NEMA images
1911           if (StrSliceLoc == "gdcm::Unfound") {
1912             dbg.Verbose(0, "gdcmHeader::GetXImagePosition: unfound Slice Location (0020,1041)");
1913              // How to tell the caller nothing was found?
1914           } 
1915        }  
1916        return 0.;
1917      }
1918    if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3)
1919      // How to tell the caller a wrong number of values was found?
1920      return 0.;
1921    //else
1922    return xImPos;
1923 }
1924
1925 /**
1926   * \ingroup gdcmHeader
1927   * \brief
1928   * @return
1929   */
1930 float gdcmHeader::GetYImagePosition(void) {
1931     float xImPos, yImPos, zImPos;
1932     // 0020,0032 : Image Position Patient
1933     // 0020,1041 : Slice Location
1934     // 0020,0050 : Location
1935     string StrImPos = GetPubElValByNumber(0x0020,0x0032);
1936
1937     if (StrImPos == "gdcm::Unfound") {
1938        dbg.Verbose(0, "gdcmHeader::GetYImagePosition: unfound Image Position Patient (0020,0032)");
1939        StrImPos = GetPubElValByNumber(0x0020,0x0030); // For ACR-NEMA images
1940        if (StrImPos == "gdcm::Unfound") {
1941           dbg.Verbose(0, "gdcmHeader::GetYImagePosition: unfound Image Position (RET) (0020,0030)");
1942           string StrSliceLoc = GetPubElValByNumber(0x0020,0x1041); // for *very* old ACR-NEMA images
1943           if (StrSliceLoc == "gdcm::Unfound") {
1944             dbg.Verbose(0, "gdcmHeader::GetYImagePosition: unfound Slice Location (0020,1041)");
1945              // How to tell the caller nothing was found?
1946           } 
1947        }  
1948        return 0.;
1949      }
1950    if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3)
1951      // How to tell the caller a wrong number of values was found?
1952      return 0.;
1953    return yImPos;
1954 }
1955
1956 /**
1957   * \ingroup gdcmHeader
1958   * \brief
1959   * @return
1960   */
1961 float gdcmHeader::GetZImagePosition(void) {
1962    float xImPos, yImPos, zImPos;
1963     // 0020,0032 : Image Position Patient
1964     // 0020,1041 : Slice Location
1965     // 0020,0050 : Location
1966    
1967    // TODO : How to tell the caller nothing was found?
1968    // TODO : How to tell the caller a wrong number of values was found?
1969   
1970    string StrImPos = GetPubElValByNumber(0x0020,0x0032);
1971    if (StrImPos != "gdcm::Unfound") {
1972       if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3) {
1973          dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Image Position Patient (0020,0032)");
1974          return 0.;  // bug in the element 0x0020,0x0032
1975       } else {
1976          return zImPos;
1977       }    
1978    }
1979    
1980    StrImPos = GetPubElValByNumber(0x0020,0x0030); // For ACR-NEMA images
1981    if (StrImPos != "gdcm::Unfound") {
1982       if( sscanf( StrImPos.c_str(), "%f\\%f\\%f", &xImPos, &yImPos, &zImPos) != 3) {
1983          dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Image Position (RET) (0020,0030)");
1984          return 0.;  // bug in the element 0x0020,0x0032
1985       } else {
1986          return zImPos;
1987       }    
1988    }
1989                  
1990    string StrSliceLocation = GetPubElValByNumber(0x0020,0x1041);// for *very* old ACR-NEMA images
1991    if (StrSliceLocation != "gdcm::Unfound") {
1992       if( sscanf( StrSliceLocation.c_str(), "%f", &zImPos) !=1) {
1993          dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Slice Location (0020,1041)");
1994          return 0.;  // bug in the element 0x0020,0x1041
1995       } else {
1996          return zImPos;
1997       }
1998    }   
1999    dbg.Verbose(0, "gdcmHeader::GetZImagePosition: unfound Slice Location (0020,1041)");
2000
2001    string StrLocation = GetPubElValByNumber(0x0020,0x0050);
2002    if (StrLocation != "gdcm::Unfound") {
2003       if( sscanf( StrLocation.c_str(), "%f", &zImPos) !=1) {
2004          dbg.Verbose(0, "gdcmHeader::GetZImagePosition: wrong Location (0020,0050)");
2005          return 0.;  // bug in the element 0x0020,0x0050
2006       } else {
2007          return zImPos;
2008       }
2009    }
2010    dbg.Verbose(0, "gdcmHeader::GetYImagePosition: unfound Slice Location");
2011    
2012    return 0.; // Hopeless
2013 }
2014
2015
2016