]> Creatis software - gdcm.git/blob - src/gdcmHeader.cxx
ajout quelques fonctions pour WriteDcm
[gdcm.git] / src / gdcmHeader.cxx
1 // gdcmHeader.cxx
2
3 #include "gdcm.h"
4 #include <stdio.h>
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 <map>
13 #include <sstream>
14 #include "gdcmUtil.h"
15
16 #define HEADER_LENGTH_TO_READ 256 // on ne lit plus que le debut
17
18 #define DEBUG 1
19
20 namespace Error {
21         struct FileReadError {
22                 FileReadError(FILE* fp, const char* Mesg) {
23                         if (feof(fp))
24                                 dbg.Verbose(1, "EOF encountered :", Mesg);
25                         if (ferror(fp))
26                                 dbg.Verbose(1, "Error on reading :", Mesg);
27                 }
28         };
29 }
30
31 //FIXME: this looks dirty to me...
32
33 #define str2num(str, typeNum) *((typeNum *)(str))
34
35 // str est un pointeur dans un tableau de caractères, qui doit contenir, 
36 // à cet endroit la, la représentation binaire d'un entier (16 ou 32 bits)
37 // je veux récupérer ça ... dans un entier.
38 // s'il y a une autre solution, évitant des cast et les indirections,
39 // je suis preneur
40
41 VRHT * gdcmHeader::dicom_vr = (VRHT*)0;
42 gdcmDictSet* gdcmHeader::Dicts = new gdcmDictSet();
43
44 void gdcmHeader::Initialise(void) {
45         if (!gdcmHeader::dicom_vr)
46                 InitVRDict();
47         RefPubDict = gdcmHeader::Dicts->GetDefaultPublicDict();
48         RefShaDict = (gdcmDict*)0;
49 }
50
51 gdcmHeader::gdcmHeader (const char* InFilename) {
52         SetMaxSizeLoadElementValue(1024);
53         filename = InFilename;
54         Initialise();
55         fp=fopen(InFilename,"rw");
56         dbg.Error(!fp, "gdcmHeader::gdcmHeader cannot open file", InFilename);
57         ParseHeader();
58 }
59
60 gdcmHeader::~gdcmHeader (void) {
61         fclose(fp);
62         return;
63 }
64
65 void gdcmHeader::InitVRDict (void) {
66         if (dicom_vr) {
67                 dbg.Verbose(0, "gdcmHeader::InitVRDict:", "VR dictionary allready set");
68                 return;
69         }
70         VRHT *vr = new VRHT;
71         (*vr)["AE"] = "Application Entity";       // At most 16 bytes
72         (*vr)["AS"] = "Age String";               // Exactly 4 bytes
73         (*vr)["AT"] = "Attribute Tag";            // 2 16-bit unsigned short integers
74         (*vr)["CS"] = "Code String";              // At most 16 bytes
75         (*vr)["DA"] = "Date";                     // Exactly 8 bytes
76         (*vr)["DS"] = "Decimal String";           // At most 16 bytes
77         (*vr)["DT"] = "Date Time";                // At most 26 bytes
78         (*vr)["FL"] = "Floating Point Single";    // 32-bit IEEE 754:1985 float
79         (*vr)["FD"] = "Floating Point Double";    // 64-bit IEEE 754:1985 double
80         (*vr)["IS"] = "Integer String";           // At most 12 bytes
81         (*vr)["LO"] = "Long String";              // At most 64 chars
82         (*vr)["LT"] = "Long Text";                // At most 10240 chars
83         (*vr)["OB"] = "Other Byte String";        // String of bytes (vr independant)
84         (*vr)["OW"] = "Other Word String";        // String of 16-bit words (vr dep)
85         (*vr)["PN"] = "Person Name";              // At most 64 chars
86         (*vr)["SH"] = "Short String";             // At most 16 chars
87         (*vr)["SL"] = "Signed Long";              // Exactly 4 bytes
88         (*vr)["SQ"] = "Sequence of Items";        // Not Applicable
89         (*vr)["SS"] = "Signed Short";             // Exactly 2 bytes
90         (*vr)["ST"] = "Short Text";               // At most 1024 chars
91         (*vr)["TM"] = "Time";                     // At most 16 bytes
92         (*vr)["UI"] = "Unique Identifier";        // At most 64 bytes
93         (*vr)["UL"] = "Unsigned Long ";           // Exactly 4 bytes
94         (*vr)["UN"] = "Unknown";                  // Any length of bytes
95         (*vr)["US"] = "Unsigned Short ";          // Exactly 2 bytes
96         (*vr)["UT"] = "Unlimited Text";           // At most 2^32 -1 chars
97    dicom_vr = vr;       
98 }
99
100 /**
101  * \ingroup gdcmHeader
102  * \brief   Discover what the swap code is (among little endian, big endian,
103  *          bad little endian, bad big endian).
104  *
105  */
106 void gdcmHeader::CheckSwap()
107 {
108         // The only guaranted way of finding the swap code is to find a
109         // group tag since we know it's length has to be of four bytes i.e.
110         // 0x00000004. Finding the swap code in then straigthforward. Trouble
111         // occurs when we can't find such group...
112         guint32  s;
113         guint32  x=4;  // x : pour ntohs
114         bool net2host; // true when HostByteOrder is the same as NetworkByteOrder
115          
116         int lgrLue;
117         char * entCur;
118         char deb[HEADER_LENGTH_TO_READ];
119          
120         // First, compare HostByteOrder and NetworkByteOrder in order to
121         // determine if we shall need to swap bytes (i.e. the Endian type).
122         if (x==ntohs(x))
123                 net2host = true;
124         else
125                 net2host = false;
126         
127         // The easiest case is the one of a DICOM header, since it possesses a
128         // file preamble where it suffice to look for the sting "DICM".
129         lgrLue = fread(deb, 1, HEADER_LENGTH_TO_READ, fp);
130         
131         entCur = deb + 128;
132         if(memcmp(entCur, "DICM", (size_t)4) == 0) {
133                 filetype = TrueDicom;
134                 dbg.Verbose(1, "gdcmHeader::CheckSwap:", "looks like DICOM Version3");
135         } else {
136                 filetype = Unknown;
137                 dbg.Verbose(1, "gdcmHeader::CheckSwap:", "not a DICOM Version3 file");
138         }
139
140         if(filetype == TrueDicom) {
141                 // Next, determine the value representation (VR). Let's skip to the
142                 // first element (0002, 0000) and check there if we find "UL", in
143                 // which case we (almost) know it is explicit VR.
144                 // WARNING: if it happens to be implicit VR then what we will read
145                 // is the length of the group. If this ascii representation of this
146                 // length happens to be "UL" then we shall believe it is explicit VR.
147                 // FIXME: in order to fix the above warning, we could read the next
148                 // element value (or a couple of elements values) in order to make
149                 // sure we are not commiting a big mistake.
150                 // We need to skip :
151                 // * the 128 bytes of File Preamble (often padded with zeroes),
152                 // * the 4 bytes of "DICM" string,
153                 // * the 4 bytes of the first tag (0002, 0000),
154                 // i.e. a total of  136 bytes.
155                 entCur = deb + 136;
156                 if(memcmp(entCur, "UL", (size_t)2) == 0) {
157                         filetype = ExplicitVR;
158                         dbg.Verbose(1, "gdcmHeader::CheckSwap:",
159                                     "explicit Value Representation");
160                 } else {
161                         filetype = ImplicitVR;
162                         dbg.Verbose(1, "gdcmHeader::CheckSwap:",
163                                     "not an explicit Value Representation");
164                 }
165
166                 if (net2host) {
167                         sw = 4321;
168                         dbg.Verbose(1, "gdcmHeader::CheckSwap:",
169                                        "HostByteOrder != NetworkByteOrder");
170                 } else {
171                         sw = 0;
172                         dbg.Verbose(1, "gdcmHeader::CheckSwap:",
173                                        "HostByteOrder = NetworkByteOrder");
174                 }
175                 
176                 // Position the file position indicator at first tag (i.e.
177                 // after the file preamble and the "DICM" string).
178                 rewind(fp);
179                 fseek (fp, 132L, SEEK_SET);
180                 return;
181         } // End of TrueDicom
182
183         // Alas, this is not a DicomV3 file and whatever happens there is no file
184         // preamble. We can reset the file position indicator to where the data
185         // is (i.e. the beginning of the file).
186         rewind(fp);
187
188         // Our next best chance would be to be considering a 'clean' ACR/NEMA file.
189         // By clean we mean that the length of the first tag is written down.
190         // If this is the case and since the length of the first group HAS to be
191         // four (bytes), then determining the proper swap code is straightforward.
192
193         entCur = deb + 4;
194         s = str2num(entCur, guint32);
195         
196         switch (s) {
197         case 0x00040000 :
198                 sw = 3412;
199                 filetype = ACR;
200                 return;
201         case 0x04000000 :
202                 sw = 4321;
203                 filetype = ACR;
204                 return;
205         case 0x00000400 :
206                 sw = 2143;
207                 filetype = ACR;
208                 return;
209         case 0x00000004 :
210                 sw = 0;
211                 filetype = ACR;
212                 return;
213         default :
214                 dbg.Verbose(0, "gdcmHeader::CheckSwap:",
215                                "ACR/NEMA unfound swap info (time to raise bets)");
216         }
217
218         // We are out of luck. It is not a DicomV3 nor a 'clean' ACR/NEMA file.
219         // It is time for despaired wild guesses. So, let's assume this file
220         // happens to be 'dirty' ACR/NEMA, i.e. the length of the group is
221         // not present. Then the only info we have is the net2host one.
222         if (! net2host )
223                 sw = 0;
224         else
225                 sw = 4321;
226         return;
227 }
228
229 void gdcmHeader::SwitchSwapToBigEndian(void) {
230         dbg.Verbose(1, "gdcmHeader::SwitchSwapToBigEndian",
231                        "Switching to BigEndian mode.");
232         if ( sw == 0    ) {
233                 sw = 4321;
234                 return;
235         }
236         if ( sw == 4321 ) {
237                 sw = 0;
238                 return;
239         }
240         if ( sw == 3412 ) {
241                 sw = 2143;
242                 return;
243         }
244         if ( sw == 2143 )
245                 sw = 3412;
246 }
247
248 void gdcmHeader::GetPixels(size_t lgrTotale, void* _Pixels) {
249         size_t pixelsOffset; 
250         pixelsOffset = GetPixelOffset();
251         printf("pixelsOffset %d\n",pixelsOffset);
252         fseek(fp, pixelsOffset, SEEK_SET);
253         fread(_Pixels, 1, lgrTotale, fp);
254 }
255
256
257
258 /**
259  * \ingroup   gdcmHeader
260  * \brief     Find the value representation of the current tag.
261  *
262  * @param sw  code swap
263  * @param skippedLength  pointeur sur nombre d'octets que l'on a saute qd
264  *                       la lecture est finie
265  * @param longueurLue    pointeur sur longueur (en nombre d'octets) 
266  *                       effectivement lue
267  * @return               longueur retenue pour le champ 
268  */
269  
270 // -->
271 // --> Oops
272 // --> C'etait la description de quoi, ca?
273 // -->
274
275 void gdcmHeader::FindVR( ElValue *ElVal) {
276         if (filetype != ExplicitVR)
277                 return;
278
279         char VR[3];
280         string vr;
281         int lgrLue;
282         long PositionOnEntry = ftell(fp);
283         // Warning: we believe this is explicit VR (Value Representation) because
284         // we used a heuristic that found "UL" in the first tag. Alas this
285         // doesn't guarantee that all the tags will be in explicit VR. In some
286         // cases (see e-film filtered files) one finds implicit VR tags mixed
287         // within an explicit VR file. Hence we make sure the present tag
288         // is in explicit VR and try to fix things if it happens not to be
289         // the case.
290         bool RealExplicit = true;
291         
292         lgrLue=fread (&VR, (size_t)2,(size_t)1, fp);
293         VR[2]=0;
294         vr = string(VR);
295                 
296         // Assume we are reading a falsely explicit VR file i.e. we reached
297         // a tag where we expect reading a VR but are in fact we read the
298         // first to bytes of the length. Then we will interogate (through find)
299         // the dicom_vr dictionary with oddities like "\004\0" which crashes
300         // both GCC and VC++ implementations of the STL map. Hence when the
301         // expected VR read happens to be non-ascii characters we consider
302         // we hit falsely explicit VR tag.
303
304         if ( (!isalpha(VR[0])) && (!isalpha(VR[1])) )
305                 RealExplicit = false;
306
307         // CLEANME searching the dicom_vr at each occurence is expensive.
308         // PostPone this test in an optional integrity check at the end
309         // of parsing or only in debug mode.
310         if ( RealExplicit && !dicom_vr->count(vr) )
311                 RealExplicit = false;
312
313         if ( RealExplicit ) {
314                 if ( ElVal->IsVrUnknown() ) {
315                         // When not a dictionary entry, we can safely overwrite the vr.
316                         ElVal->SetVR(vr);
317                         return; 
318                 }
319                 if ( ElVal->GetVR() == vr ) {
320                         // The vr we just read and the dictionary agree. Nothing to do.
321                         return;
322                 }
323                 // The vr present in the file and the dictionary disagree. We assume
324                 // the file writer knew best and use the vr of the file. Since it would
325                 // be unwise to overwrite the vr of a dictionary (since it would
326                 // compromise it's next user), we need to clone the actual DictEntry
327                 // and change the vr for the read one.
328                 gdcmDictEntry* NewTag = new gdcmDictEntry(ElVal->GetGroup(),
329                                            ElVal->GetElement(),
330                                            vr,
331                                            "FIXME",
332                                            ElVal->GetName());
333                 ElVal->SetDictEntry(NewTag);
334                 return; 
335         }
336         
337         // We thought this was explicit VR, but we end up with an
338         // implicit VR tag. Let's backtrack.
339         dbg.Verbose(1, "gdcmHeader::FindVR:", "Falsely explicit vr file");
340         fseek(fp, PositionOnEntry, SEEK_SET);
341         // When this element is known in the dictionary we shall use, e.g. for
342         // the semantics (see  the usage of IsAnInteger), the vr proposed by the
343         // dictionary entry. Still we have to flag the element as implicit since
344         // we know now our assumption on expliciteness is not furfilled.
345         // avoid  .
346         if ( ElVal->IsVrUnknown() )
347                 ElVal->SetVR("Implicit");
348         ElVal->SetImplicitVr();
349 }
350
351 /**
352  * \ingroup gdcmHeader
353  * \brief   Determines if the Transfer Syntax was allready encountered
354  *          and if it corresponds to a ImplicitVRLittleEndian one.
355  *
356  * @return  True when ImplicitVRLittleEndian found. False in all other cases.
357  */
358 bool gdcmHeader::IsImplicitVRLittleEndianTransferSyntax(void) {
359         ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010);
360         if ( !Element )
361                 return false;
362         LoadElementValueSafe(Element);
363         string Transfer = Element->GetValue();
364         if ( Transfer == "1.2.840.10008.1.2" )
365                 return true;
366         return false;
367 }
368
369 /**
370  * \ingroup gdcmHeader
371  * \brief   Determines if the Transfer Syntax was allready encountered
372  *          and if it corresponds to a ExplicitVRLittleEndian one.
373  *
374  * @return  True when ExplicitVRLittleEndian found. False in all other cases.
375  */
376 bool gdcmHeader::IsExplicitVRLittleEndianTransferSyntax(void) {
377         ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010);
378         if ( !Element )
379                 return false;
380         LoadElementValueSafe(Element);
381         string Transfer = Element->GetValue();
382         if ( Transfer == "1.2.840.10008.1.2.1" )
383                 return true;
384         return false;
385 }
386
387 /**
388  * \ingroup gdcmHeader
389  * \brief   Determines if the Transfer Syntax was allready encountered
390  *          and if it corresponds to a DeflatedExplicitVRLittleEndian one.
391  *
392  * @return  True when DeflatedExplicitVRLittleEndian found. False in all other cases.
393  */
394 bool gdcmHeader::IsDeflatedExplicitVRLittleEndianTransferSyntax(void) {
395         ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010);
396         if ( !Element )
397                 return false;
398         LoadElementValueSafe(Element);
399         string Transfer = Element->GetValue();
400         if ( Transfer == "1.2.840.10008.1.2.1.99" )
401                 return true;
402         return false;
403 }
404
405
406 /**
407  * \ingroup gdcmHeader
408  * \brief   Determines if the Transfer Syntax was allready encountered
409  *          and if it corresponds to a Explicit VR Big Endian one.
410  *
411  * @return  True when big endian found. False in all other cases.
412  */
413 bool gdcmHeader::IsExplicitVRBigEndianTransferSyntax(void) {
414         ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010);
415         if ( !Element )
416                 return false;
417         LoadElementValueSafe(Element);
418         string Transfer = Element->GetValue();
419         if ( Transfer == "1.2.840.10008.1.2.2" )
420                 return true;
421         return false;
422 }
423
424
425 /**
426  * \ingroup gdcmHeader
427  * \brief   Determines if the Transfer Syntax was allready encountered
428  *          and if it corresponds to a JPEGBaseLineProcess1 one.
429  *
430  * @return  True when JPEGBaseLineProcess1found. False in all other cases.
431  */
432 bool gdcmHeader::IsJPEGBaseLineProcess1TransferSyntax(void) {
433         ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010);
434         if ( !Element )
435                 return false;
436         LoadElementValueSafe(Element);
437         string Transfer = Element->GetValue();
438         if ( Transfer == "1.2.840.10008.1.2.4.50" )
439                 return true;
440         return false;
441 }
442
443 /**
444  * \ingroup gdcmHeader
445  * \brief   Determines if the Transfer Syntax was allready encountered
446  *          and if it corresponds to a JPEGExtendedProcess2-4 one.
447  *
448  * @return  True when JPEGExtendedProcess2-4 found. False in all other cases.
449  */
450 bool gdcmHeader::IsJPEGExtendedProcess2_4TransferSyntax(void) {
451         ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010);
452         if ( !Element )
453                 return false;
454         LoadElementValueSafe(Element);
455         string Transfer = Element->GetValue();
456         if ( Transfer == "1.2.840.10008.1.2.4.51" )
457                 return true;
458         return false;
459 }
460
461
462 /**
463  * \ingroup gdcmHeader
464  * \brief   Determines if the Transfer Syntax was allready encountered
465  *          and if it corresponds to a JPEGExtendeProcess3-5 one.
466  *
467  * @return  True when JPEGExtendedProcess3-5 found. False in all other cases.
468  */
469 bool gdcmHeader::IsJPEGExtendedProcess3_5TransferSyntax(void) {
470         ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010);
471         if ( !Element )
472                 return false;
473         LoadElementValueSafe(Element);
474         string Transfer = Element->GetValue();
475         if ( Transfer == "1.2.840.10008.1.2.4.52" )
476                 return true;
477         return false;
478 }
479
480 /**
481  * \ingroup gdcmHeader
482  * \brief   Determines if the Transfer Syntax was allready encountered
483  *          and if it corresponds to a JPEGSpectralSelectionProcess6-8 one.
484  *
485  * @return  True when JPEGSpectralSelectionProcess6-8 found. False in all other cases.
486  */
487 bool gdcmHeader::IsJPEGSpectralSelectionProcess6_8TransferSyntax(void) {
488         ElValue* Element = PubElVals.GetElementByNumber(0x0002, 0x0010);
489         if ( !Element )
490                 return false;
491         LoadElementValueSafe(Element);
492         string Transfer = Element->GetValue();
493         if ( Transfer == "1.2.840.10008.1.2.4.53" )
494                 return true;
495         return false;
496 }
497
498 //
499 // Euhhhhhhh
500 // Il y en a encore DIX-SEPT, comme ça.
501 // Il faudrait trouver qq chose + rusé ...
502 //
503
504
505 void gdcmHeader::FixFoundLength(ElValue * ElVal, guint32 FoundLength) {
506         // Heuristic: a final fix.
507         if ( FoundLength == 0xffffffff)
508                 FoundLength = 0;
509         ElVal->SetLength(FoundLength);
510 }
511
512 guint32 gdcmHeader::FindLengthOB(void) {
513         // See PS 3.5-2001, section A.4 p. 49 on encapsulation of encoded pixel data.
514         guint16 g;
515         guint16 n; 
516         long PositionOnEntry = ftell(fp);
517         bool FoundSequenceDelimiter = false;
518         guint32 TotalLength = 0;
519         guint32 ItemLength;
520
521         while ( ! FoundSequenceDelimiter) {
522                 g = ReadInt16();
523                 n = ReadInt16();
524                 TotalLength += 4;  // We even have to decount the group and element 
525                 if ( g != 0xfffe ) {
526                         dbg.Verbose(1, "gdcmHeader::FindLengthOB: ",
527                                     "wrong group for an item sequence.");
528                         throw Error::FileReadError(fp, "gdcmHeader::FindLengthOB");
529                 }
530                 if ( n == 0xe0dd )
531                         FoundSequenceDelimiter = true;
532                 else if ( n != 0xe000) {
533                         dbg.Verbose(1, "gdcmHeader::FindLengthOB: ",
534                                     "wrong element for an item sequence.");
535                         throw Error::FileReadError(fp, "gdcmHeader::FindLengthOB");
536                 }
537                 ItemLength = ReadInt32();
538                 TotalLength += ItemLength + 4;  // We add 4 bytes since we just read
539                                                 // the ItemLength with ReadInt32
540                 SkipBytes(ItemLength);
541         }
542         fseek(fp, PositionOnEntry, SEEK_SET);
543         return TotalLength;
544 }
545
546 void gdcmHeader::FindLength(ElValue * ElVal) {
547         guint16 element = ElVal->GetElement();
548         string  vr      = ElVal->GetVR();
549         guint16 length16;
550         
551         if ( (filetype == ExplicitVR) && ! ElVal->IsImplicitVr() ) {
552
553                 if ( (vr=="OB") || (vr=="OW") || (vr=="SQ") || (vr=="UN") ) {
554                         // The following reserved two bytes (see PS 3.5-2001, section
555                         // 7.1.2 Data element structure with explicit vr p27) must be
556                         // skipped before proceeding on reading the length on 4 bytes.
557                         fseek(fp, 2L, SEEK_CUR);
558                         guint32 length32 = ReadInt32();
559                         if ( (vr == "OB") && (length32 == 0xffffffff) ) {
560                                 ElVal->SetLength(FindLengthOB());
561                                 return;
562                         }
563                         FixFoundLength(ElVal, length32);
564                         return;
565                 }
566
567                 // Length is encoded on 2 bytes.
568                 length16 = ReadInt16();
569                 
570                 // We can tell the current file is encoded in big endian (like
571                 // Data/US-RGB-8-epicard) when we find the "Transfer Syntax" tag
572                 // and it's value is the one of the encoding of a big endian file.
573                 // In order to deal with such big endian encoded files, we have
574                 // (at least) two strategies:
575                 // * when we load the "Transfer Syntax" tag with value of big endian
576                 //   encoding, we raise the proper flags. Then we wait for the end
577                 //   of the META group (0x0002) among which is "Transfer Syntax",
578                 //   before switching the swap code to big endian. We have to postpone
579                 //   the switching of the swap code since the META group is fully encoded
580                 //   in little endian, and big endian coding only starts at the next
581                 //   group. The corresponding code can be hard to analyse and adds
582                 //   many additional unnecessary tests for regular tags.
583                 // * the second strategy consists in waiting for trouble, that shall appear
584                 //   when we find the first group with big endian encoding. This is
585                 //   easy to detect since the length of a "Group Length" tag (the
586                 //   ones with zero as element number) has to be of 4 (0x0004). When we
587                 //   encouter 1024 (0x0400) chances are the encoding changed and we
588                 //   found a group with big endian encoding.
589                 // We shall use this second strategy. In order make sure that we
590                 // can interpret the presence of an apparently big endian encoded
591                 // length of a "Group Length" without committing a big mistake, we
592                 // add an additional check: we look in the allready parsed elements
593                 // for the presence of a "Transfer Syntax" whose value has to be "big
594                 // endian encoding". When this is the case, chances are we got our
595                 // hands on a big endian encoded file: we switch the swap code to
596                 // big endian and proceed...
597                 if ( (element  == 0x000) && (length16 == 0x0400) ) {
598                         if ( ! IsExplicitVRBigEndianTransferSyntax() )
599                                 throw Error::FileReadError(fp, "gdcmHeader::FindLength");
600                         length16 = 4;
601                         SwitchSwapToBigEndian();
602                         // Restore the unproperly loaded values i.e. the group, the element
603                         // and the dictionary entry depending on them.
604                         guint16 CorrectGroup   = SwapShort(ElVal->GetGroup());
605                         guint16 CorrectElem    = SwapShort(ElVal->GetElement());
606                         gdcmDictEntry * NewTag = IsInDicts(CorrectGroup, CorrectElem);
607                         if (!NewTag) {
608                                 // This correct tag is not in the dictionary. Create a new one.
609                                 NewTag = new gdcmDictEntry(CorrectGroup, CorrectElem);
610                         }
611                         // FIXME this can create a memory leaks on the old entry that be
612                         // left unreferenced.
613                         ElVal->SetDictEntry(NewTag);
614                 }
615                  
616                 // Heuristic: well some files are really ill-formed.
617                 if ( length16 == 0xffff) {
618                         length16 = 0;
619                         dbg.Verbose(0, "gdcmHeader::FindLength",
620                                     "Erroneous element length fixed.");
621                 }
622                 FixFoundLength(ElVal, (guint32)length16);
623                 return;
624         }
625
626         // Either implicit VR or a non DICOM conformal (see not below) explicit
627         // VR that ommited the VR of (at least) this element. Farts happen.
628         // [Note: according to the part 5, PS 3.5-2001, section 7.1 p25
629         // on Data elements "Implicit and Explicit VR Data Elements shall
630         // not coexist in a Data Set and Data Sets nested within it".]
631         // Length is on 4 bytes.
632         FixFoundLength(ElVal, ReadInt32());
633 }
634
635 /**
636  * \ingroup gdcmHeader
637  * \brief   Swaps back the bytes of 4-byte long integer accordingly to
638  *          processor order.
639  *
640  * @return  The suggested integer.
641  */
642 guint32 gdcmHeader::SwapLong(guint32 a) {
643         // FIXME: il pourrait y avoir un pb pour les entiers negatifs ...
644         switch (sw) {
645         case    0 :
646                 break;
647         case 4321 :
648                 a=(   ((a<<24) & 0xff000000) | ((a<<8)  & 0x00ff0000)    | 
649                       ((a>>8)  & 0x0000ff00) | ((a>>24) & 0x000000ff) );
650                 break;
651         
652         case 3412 :
653                 a=(   ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
654                 break;
655         
656         case 2143 :
657                 a=(    ((a<<8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff)  );
658                 break;
659         default :
660                 dbg.Error(" gdcmHeader::SwapLong : unset swap code");
661                 a=0;
662         }
663         return(a);
664 }
665
666 /**
667  * \ingroup gdcmHeader
668  * \brief   Swaps the bytes so they agree with the processor order
669  * @return  The properly swaped 16 bits integer.
670  */
671 guint16 gdcmHeader::SwapShort(guint16 a) {
672         if ( (sw==4321)  || (sw==2143) )
673                 a =(((a<<8) & 0x0ff00) | ((a>>8)&0x00ff));
674         return (a);
675 }
676
677 void gdcmHeader::SkipBytes(guint32 NBytes) {
678         //FIXME don't dump the returned value
679         (void)fseek(fp, (long)NBytes, SEEK_CUR);
680 }
681
682 void gdcmHeader::SkipElementValue(ElValue * ElVal) {
683         SkipBytes(ElVal->GetLength());
684 }
685
686 void gdcmHeader::SetMaxSizeLoadElementValue(long NewSize) {
687         if (NewSize < 0)
688                 return;
689         if ((guint32)NewSize >= (guint32)0xffffffff) {
690                 MaxSizeLoadElementValue = 0xffffffff;
691                 return;
692         }
693         MaxSizeLoadElementValue = NewSize;
694 }
695
696 /**
697  * \ingroup       gdcmHeader
698  * \brief         Loads the element if it's size is not to big.
699  * @param ElVal   Element whose value shall be loaded. 
700  * @param MaxSize Size treshold above which the element value is not
701  *                loaded in memory. The element value is allways loaded
702  *                when MaxSize is equal to UINT32_MAX.
703  * @return  
704  */
705 void gdcmHeader::LoadElementValue(ElValue * ElVal) {
706         size_t item_read;
707         guint16 group  = ElVal->GetGroup();
708         guint16 elem   = ElVal->GetElement();
709         string  vr     = ElVal->GetVR();
710         guint32 length = ElVal->GetLength();
711         bool SkipLoad  = false;
712
713         fseek(fp, (long)ElVal->GetOffset(), SEEK_SET);
714         
715         // Sequences not treated yet !
716         //
717         // Ne faudrait-il pas au contraire trouver immediatement
718         // une maniere 'propre' de traiter les sequences (vr = SQ)
719         // car commencer par les ignorer risque de conduire a qq chose
720         // qui pourrait ne pas etre generalisable
721         //
722         if( vr == "SQ" )
723                 SkipLoad = true;
724
725         // Heuristic : a sequence "contains" a set of tags (called items). It looks
726         // like the last tag of a sequence (the one that terminates the sequence)
727         // has a group of 0xfffe (with a dummy length).
728         if( group == 0xfffe )
729                 SkipLoad = true;
730
731         // The group length doesn't represent data to be loaded in memory, since
732         // each element of the group shall be loaded individualy.
733         if( elem == 0 )
734                 SkipLoad = true;
735
736         if ( SkipLoad ) {
737                           // FIXME the following skip is not necessary
738                 SkipElementValue(ElVal);
739                 ElVal->SetLength(0);
740                 ElVal->SetValue("gdcm::Skipped");
741                 return;
742         }
743
744         // When the length is zero things are easy:
745         if ( length == 0 ) {
746                 ElVal->SetValue("");
747                 return;
748         }
749
750         // Values bigger than specified are not loaded.
751         //
752         // En fait, c'est les elements dont la longueur est superieure 
753         // a celle fixee qui ne sont pas charges
754         //
755         if (length > MaxSizeLoadElementValue) {
756                 ostringstream s;
757                 s << "gdcm::NotLoaded.";
758                 s << " Address:" << (long)ElVal->GetOffset();
759                 s << " Length:"  << ElVal->GetLength();
760                 //mesg += " Length:"  + ElVal->GetLength();
761                 ElVal->SetValue(s.str());
762                 return;
763         }
764         
765         // When an integer is expected, read and convert the following two or
766         // four bytes properly i.e. as an integer as opposed to a string.
767         if ( IsAnInteger(ElVal) ) {
768                 guint32 NewInt;
769                 if( length == 2 ) {
770                         NewInt = ReadInt16();
771                 } else if( length == 4 ) {
772                         NewInt = ReadInt32();
773                 } else
774                         dbg.Error(true, "LoadElementValue: Inconsistency when reading Int.");
775                 
776                 //FIXME: make the following an util fonction
777                 ostringstream s;
778                 s << NewInt;
779                 ElVal->SetValue(s.str());
780                 return;
781         }
782         
783         // FIXME The exact size should be length if we move to strings or whatever
784         
785         //
786         // QUESTION : y a-t-il une raison pour ne pas utiliser g_malloc ici ?
787         //
788         
789         char* NewValue = (char*)malloc(length+1);
790         if( !NewValue) {
791                 dbg.Verbose(1, "LoadElementValue: Failed to allocate NewValue");
792                 return;
793         }
794         NewValue[length]= 0;
795         
796         item_read = fread(NewValue, (size_t)length, (size_t)1, fp);
797         if ( item_read != 1 ) {
798                 free(NewValue);
799                 Error::FileReadError(fp, "gdcmHeader::LoadElementValue");
800                 ElVal->SetValue("gdcm::UnRead");
801                 return;
802         }
803         ElVal->SetValue(NewValue);
804 }
805
806 /**
807  * \ingroup       gdcmHeader
808  * \brief         Loads the element while preserving the current
809  *                underlying file position indicator as opposed to
810  *                to LoadElementValue that modifies it.
811  * @param ElVal   Element whose value shall be loaded. 
812  * @return  
813  */
814 void gdcmHeader::LoadElementValueSafe(ElValue * ElVal) {
815         long PositionOnEntry = ftell(fp);
816         LoadElementValue(ElVal);
817         fseek(fp, PositionOnEntry, SEEK_SET);
818 }
819
820
821 guint16 gdcmHeader::ReadInt16(void) {
822         guint16 g;
823         size_t item_read;
824         item_read = fread (&g, (size_t)2,(size_t)1, fp);
825         if ( item_read != 1 )
826                 throw Error::FileReadError(fp, "gdcmHeader::ReadInt16");
827         g = SwapShort(g);
828         return g;
829 }
830
831 guint32 gdcmHeader::ReadInt32(void) {
832         guint32 g;
833         size_t item_read;
834         item_read = fread (&g, (size_t)4,(size_t)1, fp);
835         if ( item_read != 1 )
836                 throw Error::FileReadError(fp, "gdcmHeader::ReadInt32");
837         g = SwapLong(g);
838         return g;
839 }
840
841 /**
842  * \ingroup gdcmHeader
843  * \brief   Read the next tag without loading it's value
844  * @return  On succes the newly created ElValue, NULL on failure.      
845  */
846
847 ElValue * gdcmHeader::ReadNextElement(void) {
848         guint16 g;
849         guint16 n;
850         ElValue * NewElVal;
851         
852         try {
853                 g = ReadInt16();
854                 n = ReadInt16();
855         }
856         catch ( Error::FileReadError ) {
857                 // We reached the EOF (or an error occured) and header parsing
858                 // has to be considered as finished.
859                 return (ElValue *)0;
860         }
861
862         // Find out if the tag we encountered is in the dictionaries:
863         gdcmDictEntry * NewTag = IsInDicts(g, n);
864         if (!NewTag)
865                 NewTag = new gdcmDictEntry(g, n);
866
867         NewElVal = new ElValue(NewTag);
868         if (!NewElVal) {
869                 dbg.Verbose(1, "ReadNextElement: failed to allocate ElValue");
870                 return (ElValue*)0;
871         }
872
873         FindVR(NewElVal);
874         try { FindLength(NewElVal); }
875         catch ( Error::FileReadError ) { // Call it quits
876                 return (ElValue *)0;
877         }
878         NewElVal->SetOffset(ftell(fp));
879         return NewElVal;
880 }
881
882 bool gdcmHeader::IsAnInteger(ElValue * ElVal) {
883         guint16 group   = ElVal->GetGroup();
884         guint16 element = ElVal->GetElement();
885         string  vr      = ElVal->GetVR();
886         guint32 length  = ElVal->GetLength();
887
888         // When we have some semantics on the element we just read, and if we
889         // a priori know we are dealing with an integer, then we shall be
890         // able to swap it's element value properly.
891         if ( element == 0 )  {  // This is the group length of the group
892                 if (length == 4)
893                         return true;
894                 else
895                         dbg.Error("gdcmHeader::IsAnInteger",
896                                   "Erroneous Group Length element length.");
897         }
898         
899         if ( group % 2 != 0 )
900                 // We only have some semantics on documented elements, which are
901                 // the even ones.
902                 return false;
903         
904         if ( (length != 4) && ( length != 2) )
905                 // Swapping only make sense on integers which are 2 or 4 bytes long.
906                 return false;
907         
908         if ( (vr == "UL") || (vr == "US") || (vr == "SL") || (vr == "SS") )
909                 return true;
910         
911         if ( (group == 0x0028) && (element == 0x0005) )
912                 // This tag is retained from ACR/NEMA
913                 // CHECKME Why should "Image Dimensions" be a single integer ?
914                 //
915                 // "Image Dimensions", c'est en fait le 'nombre de dimensions'
916                 // de l'objet ACR-NEMA stocké
917                 // 1 : Signal
918                 // 2 : Image
919                 // 3 : Volume
920                 // 4 : Sequence
921                 //
922                 // DICOM V3 ne retient pas cette information
923                 // Par defaut, tout est 'Image',
924                 // C'est a l'utilisateur d'explorer l'ensemble des entetes
925                 // pour savoir à quoi il a a faire
926                 //
927                 // Le Dicom Multiframe peut etre utilise pour stocker,
928                 // dans un seul fichier, une serie temporelle (cardio vasculaire GE, p.ex)
929                 // ou un volume (medecine Nucleaire, p.ex)
930                 //
931                 return true;
932         
933         if ( (group == 0x0028) && (element == 0x0200) )
934                 // This tag is retained from ACR/NEMA
935                 return true;
936         
937         return false;
938 }
939
940 /**
941  * \ingroup gdcmHeader
942  * \brief   Recover the offset (from the beginning of the file) of the pixels.
943  */
944 size_t gdcmHeader::GetPixelOffset(void) {
945         // If this file complies with the norm we should encounter the
946         // "Image Location" tag (0x0028,  0x0200). This tag contains the
947         // the group that contains the pixel data (hence the "Pixel Data"
948         // is found by indirection through the "Image Location").
949         // Inside the group pointed by "Image Location" the searched element
950         // is conventionally the element 0x0010 (when the norm is respected).
951         //    When the "Image Location" is absent we default to group 0x7fe0.
952         guint16 grPixel;
953         guint16 numPixel;
954         string ImageLocation = GetPubElValByName("Image Location");
955         if ( ImageLocation == "gdcm::Unfound" ) {
956                 grPixel = 0x7fe0;
957         } else {
958                 grPixel = (guint16) atoi( ImageLocation.c_str() );
959         }
960         if (grPixel != 0x7fe0)
961                 // FIXME is this still necessary ?
962                 // Now, this looks like an old dirty fix for Philips imager
963                 numPixel = 0x1010;
964         else
965                 numPixel = 0x0010;
966         ElValue* PixelElement = PubElVals.GetElementByNumber(grPixel, numPixel);
967         if (PixelElement)
968                 return PixelElement->GetOffset();
969         else
970                 return 0;
971 }
972
973 gdcmDictEntry * gdcmHeader::IsInDicts(guint32 group, guint32 element) {
974         //
975         // Y a-t-il une raison de lui passer des guint32
976         // alors que group et element sont des guint16?
977         //
978         gdcmDictEntry * found = (gdcmDictEntry*)0;
979         if (!RefPubDict && !RefShaDict) {
980                 //FIXME build a default dictionary !
981                 printf("FIXME in gdcmHeader::IsInDicts\n");
982         }
983         if (RefPubDict) {
984                 found = RefPubDict->GetTag(group, element);
985                 if (found)
986                         return found;
987         }
988         if (RefShaDict) {
989                 found = RefShaDict->GetTag(group, element);
990                 if (found)
991                         return found;
992         }
993         return found;
994 }
995
996 list<string> * gdcmHeader::GetPubTagNames(void) {
997         list<string> * Result = new list<string>;
998         TagHT entries = RefPubDict->GetEntries();
999
1000         for (TagHT::iterator tag = entries.begin(); tag != entries.end(); ++tag){
1001       Result->push_back( tag->second->GetName() );
1002         }
1003         return Result;
1004 }
1005
1006 map<string, list<string> > * gdcmHeader::GetPubTagNamesByCategory(void) {
1007         map<string, list<string> > * Result = new map<string, list<string> >;
1008         TagHT entries = RefPubDict->GetEntries();
1009
1010         for (TagHT::iterator tag = entries.begin(); tag != entries.end(); ++tag){
1011                 (*Result)[tag->second->GetFourth()].push_back(tag->second->GetName());
1012         }
1013         return Result;
1014 }
1015
1016 string gdcmHeader::GetPubElValByNumber(guint16 group, guint16 element) {
1017         return PubElVals.GetElValueByNumber(group, element);
1018 }
1019
1020 string gdcmHeader::GetPubElValRepByNumber(guint16 group, guint16 element) {
1021         ElValue* elem =  PubElVals.GetElementByNumber(group, element);
1022         if ( !elem )
1023                 return "gdcm::Unfound";
1024         return elem->GetVR();
1025 }
1026
1027 string gdcmHeader::GetPubElValByName(string TagName) {
1028         return PubElVals.GetElValueByName(TagName);
1029 }
1030
1031 string gdcmHeader::GetPubElValRepByName(string TagName) {
1032         ElValue* elem =  PubElVals.GetElementByName(TagName);
1033         if ( !elem )
1034                 return "gdcm::Unfound";
1035         return elem->GetVR();
1036 }
1037
1038 string gdcmHeader::GetShaElValByNumber(guint16 group, guint16 element) {
1039         return ShaElVals.GetElValueByNumber(group, element);
1040 }
1041
1042 string gdcmHeader::GetShaElValRepByNumber(guint16 group, guint16 element) {
1043         ElValue* elem =  ShaElVals.GetElementByNumber(group, element);
1044         if ( !elem )
1045                 return "gdcm::Unfound";
1046         return elem->GetVR();
1047 }
1048
1049 string gdcmHeader::GetShaElValByName(string TagName) {
1050         return ShaElVals.GetElValueByName(TagName);
1051 }
1052
1053 string gdcmHeader::GetShaElValRepByName(string TagName) {
1054         ElValue* elem =  ShaElVals.GetElementByName(TagName);
1055         if ( !elem )
1056                 return "gdcm::Unfound";
1057         return elem->GetVR();
1058 }
1059
1060
1061 string gdcmHeader::GetElValByNumber(guint16 group, guint16 element) {
1062         string pub = GetPubElValByNumber(group, element);
1063         if (pub.length())
1064                 return pub;
1065         return GetShaElValByNumber(group, element);
1066 }
1067
1068 string gdcmHeader::GetElValRepByNumber(guint16 group, guint16 element) {
1069         string pub = GetPubElValRepByNumber(group, element);
1070         if (pub.length())
1071                 return pub;
1072         return GetShaElValRepByNumber(group, element);
1073 }
1074
1075 string gdcmHeader::GetElValByName(string TagName) {
1076         string pub = GetPubElValByName(TagName);
1077         if (pub.length())
1078                 return pub;
1079         return GetShaElValByName(TagName);
1080 }
1081
1082 string gdcmHeader::GetElValRepByName(string TagName) {
1083         string pub = GetPubElValRepByName(TagName);
1084         if (pub.length())
1085                 return pub;
1086         return GetShaElValRepByName(TagName);
1087 }
1088
1089 /**
1090  * \ingroup gdcmHeader
1091  * \brief   Modifie la valeur d'un ElValue déja existant
1092  * \    dans le PubElVals du gdcmHeader,
1093  * \    accédé par ses numero de groupe et d'element.
1094  */
1095 int gdcmHeader::SetPubElValByNumber(string content, guint16 group, guint16 element) {
1096         //TagKey key = gdcmDictEntry::TranslateToKey(group, element);
1097         //PubElVals.tagHt[key]->SetValue(content);
1098         
1099         return (  PubElVals.SetElValueByNumber (content, group, element) );
1100 }
1101
1102
1103 /**
1104  * \ingroup gdcmHeader
1105  * \brief   Modifie la valeur d'un ElValue déja existant
1106  * \    dans le PubElVals du gdcmHeader,
1107  * \    accédé par son nom
1108  */
1109 int gdcmHeader::SetPubElValByName(string content, string TagName) {
1110         //TagKey key = gdcmDictEntry::TranslateToKey(group, element);
1111         //PubElVals.tagHt[key]->SetValue(content);
1112         
1113         return (  PubElVals.SetElValueByName (content, TagName) );
1114 }
1115
1116
1117 /**
1118  * \ingroup gdcmHeader
1119  * \brief   Modifie la valeur d'un ElValue déja existant
1120  * \    dans le ShaElVals du gdcmHeader,
1121  * \    accédé par ses numero de groupe et d'element.
1122  */
1123 int gdcmHeader::SetShaElValByNumber(string content, guint16 group, guint16 element) {
1124         
1125         return (  ShaElVals.SetElValueByNumber (content, group, element) );
1126 }
1127
1128
1129 /**
1130  * \ingroup gdcmHeader
1131  * \brief   Modifie la valeur d'un ElValue déja existant
1132  * \    dans le ShaElVals du gdcmHeader,
1133  * \    accédé par son nom
1134  */
1135 int gdcmHeader::SetShaElValByName(string content, string TagName) {
1136         
1137         return (  ShaElVals.SetElValueByName (content, TagName) );
1138 }
1139 /**
1140  * \ingroup gdcmHeader
1141  * \brief   Parses the header of the file but does NOT load element values.
1142  */
1143 void gdcmHeader::ParseHeader(void) {
1144         ElValue * newElValue = (ElValue *)0;
1145         
1146         rewind(fp);
1147         CheckSwap();
1148         while ( (newElValue = ReadNextElement()) ) {
1149                 SkipElementValue(newElValue);
1150                 PubElVals.Add(newElValue);
1151         }
1152 }
1153
1154 /**
1155  * \ingroup gdcmHeader
1156  * \brief   Loads the element values of all the elements present in the
1157  *          public tag based hash table.
1158  */
1159 void gdcmHeader::LoadElements(void) {
1160
1161         if (DEBUG) printf("LoadElements : Entree\n");
1162
1163         rewind(fp);   
1164         if (DEBUG) printf("LoadElements : rewind\n");
1165
1166         TagElValueHT ht = PubElVals.GetTagHt();
1167         
1168         if (DEBUG) printf("LoadElements : GetTagHt\n");
1169
1170         for (TagElValueHT::iterator tag = ht.begin(); tag != ht.end(); ++tag) {
1171                 LoadElementValue(tag->second);
1172                 }
1173 }
1174
1175 void gdcmHeader::PrintPubElVal(ostream & os) {
1176         PubElVals.Print(os);
1177 }
1178
1179 void gdcmHeader::PrintPubDict(ostream & os) {
1180         RefPubDict->Print(os);
1181 }