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