]> Creatis software - gdcm.git/blob - src/gdcmParser.cxx
1b86aecabd9d9ed3f237431196b3b80db05ac348
[gdcm.git] / src / gdcmParser.cxx
1 // gdcmHeader.cxx
2 //-----------------------------------------------------------------------------
3 #include "gdcmParser.h"
4 #include "gdcmUtil.h"
5
6 // For nthos:
7 #ifdef _MSC_VER
8    #include <winsock.h>
9 #else
10    #include <netinet/in.h>
11 #endif
12
13 #ifdef GDCM_NO_ANSI_STRING_STREAM
14 #  include <strstream>
15 #  define  ostringstream ostrstream
16 # else
17 #  include <sstream>
18 #endif
19
20 //-----------------------------------------------------------------------------
21 // Refer to gdcmParser::CheckSwap()
22 const unsigned int gdcmParser::HEADER_LENGTH_TO_READ = 256;
23
24 // Refer to gdcmParser::SetMaxSizeLoadElementValue()
25 const unsigned int gdcmParser::MAX_SIZE_LOAD_ELEMENT_VALUE = 4096;
26
27 //-----------------------------------------------------------------------------
28 // Constructor / Destructor
29 /**
30  * \ingroup gdcmParser
31  * \brief   
32  * @param   InFilename
33  * @param   exception_on_error
34  * @param   enable_sequences = true to allow the header 
35  *          to be parsed *inside* the SeQuences, 
36  *          when they have an actual length 
37  *\TODO    may be we need one more bool, 
38  *         to allow skipping the private elements while parsing the header
39  *         in order to save space         
40  */
41 gdcmParser::gdcmParser(const char *InFilename, 
42                        bool exception_on_error,
43                        bool enable_sequences ) 
44 {
45    enableSequences=enable_sequences;
46    
47    SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE);
48    filename = InFilename;
49    Initialise();
50
51    if ( !OpenFile(exception_on_error))
52       return;
53    Parse();
54    LoadHeaderEntries();
55    CloseFile();
56
57    wasUpdated = 0;  // will be set to 1 if user adds an entry
58    printLevel = 1;  // 'Heavy' header print by default
59 }
60
61 /**
62  * \ingroup gdcmParser
63  * \brief   
64  * @param   exception_on_error
65  */
66 gdcmParser::gdcmParser(bool exception_on_error) 
67 {
68    enableSequences=0;
69
70    SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE);
71    Initialise();
72
73    wasUpdated = 0;  // will be set to 1 if user adds an entry
74    printLevel = 1;  // 'Heavy' header print by default
75 }
76
77 /**
78  * \ingroup gdcmParser
79  * \brief   Canonical destructor.
80  */
81 gdcmParser::~gdcmParser (void) 
82 {
83    dicom_vr =   (gdcmVR*)0; 
84    Dicts    =   (gdcmDictSet*)0;
85    RefPubDict = (gdcmDict*)0;
86    RefShaDict = (gdcmDict*)0;
87 }
88
89 //-----------------------------------------------------------------------------
90 // Print
91 /**
92   * \ingroup gdcmParser
93   * \brief   Prints the Header Entries (Dicom Elements)
94   *          both from the H Table and the chained list
95   * @return
96   */ 
97 void gdcmParser::PrintPubEntry(std::ostream & os) 
98 {
99    std::ostringstream s;   
100            
101 //   guint32 lgth;
102 //   char greltag[10];  //group element tag
103  
104    s << "------------ using listEntries ----------------" << std::endl; 
105
106 //   char st[20];
107    for (ListTag::iterator i = listEntries.begin();  
108            i != listEntries.end();
109            ++i)
110    {
111            (*i)->SetPrintLevel(printLevel);
112            (*i)->Print(os);   
113    } 
114    os<<s.str();
115 }
116
117 /**
118   * \ingroup gdcmParser
119   * \brief   Prints The Dict Entries of THE public Dicom Dictionnry
120   * @return
121   */  
122 void gdcmParser::PrintPubDict(std::ostream & os) 
123 {
124    RefPubDict->Print(os);
125 }
126
127 /**
128   * \ingroup gdcmParser
129   * \brief   Prints The Dict Entries of THE shadow Dicom Dictionnry
130   * @return
131   */
132 void gdcmParser::PrintShaDict(std::ostream & os) 
133 {
134    RefShaDict->Print(os);
135 }
136
137 //-----------------------------------------------------------------------------
138 // Public
139 /**
140  * \ingroup gdcmParser
141  * \brief  This predicate, based on hopefully reasonable heuristics,
142  *         decides whether or not the current gdcmParser was properly parsed
143  *         and contains the mandatory information for being considered as
144  *         a well formed and usable Dicom/Acr File.
145  * @return true when gdcmParser is the one of a reasonable Dicom/Acr file,
146  *         false otherwise. 
147  */
148 bool gdcmParser::IsReadable(void) 
149 {
150    std::string res = GetEntryByNumber(0x0028, 0x0005);
151    if ( res != GDCM_UNFOUND && atoi(res.c_str()) > 4 ) 
152    {
153       return false; // Image Dimensions
154    }
155
156    if ( !GetHeaderEntryByNumber(0x0028, 0x0100) )
157       return false; // "Bits Allocated"
158    if ( !GetHeaderEntryByNumber(0x0028, 0x0101) )
159       return false; // "Bits Stored"
160    if ( !GetHeaderEntryByNumber(0x0028, 0x0102) )
161       return false; // "High Bit"
162    if ( !GetHeaderEntryByNumber(0x0028, 0x0103) )
163       return false; // "Pixel Representation"
164    return true;
165 }
166
167 /**
168  * \ingroup gdcmParser
169  * \brief   Determines if the Transfer Syntax was already encountered
170  *          and if it corresponds to a ImplicitVRLittleEndian one.
171  *
172  * @return  True when ImplicitVRLittleEndian found. False in all other cases.
173  */
174 bool gdcmParser::IsImplicitVRLittleEndianTransferSyntax(void) 
175 {
176    gdcmHeaderEntry *Element = GetHeaderEntryByNumber(0x0002, 0x0010);
177    if ( !Element )
178       return false;
179    LoadHeaderEntrySafe(Element);
180
181    std::string Transfer = Element->GetValue();
182    if ( Transfer == "1.2.840.10008.1.2" )
183       return true;
184    return false;
185 }
186
187 /**
188  * \ingroup gdcmParser
189  * \brief   Determines if the Transfer Syntax was already encountered
190  *          and if it corresponds to a ExplicitVRLittleEndian one.
191  *
192  * @return  True when ExplicitVRLittleEndian found. False in all other cases.
193  */
194 bool gdcmParser::IsExplicitVRLittleEndianTransferSyntax(void) 
195 {
196    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
197    if ( !Element )
198       return false;
199    LoadHeaderEntrySafe(Element);
200
201    std::string Transfer = Element->GetValue();
202    if ( Transfer == "1.2.840.10008.1.2.1" )
203       return true;
204    return false;
205 }
206
207 /**
208  * \ingroup gdcmParser
209  * \brief   Determines if the Transfer Syntax was already encountered
210  *          and if it corresponds to a DeflatedExplicitVRLittleEndian one.
211  *
212  * @return  True when DeflatedExplicitVRLittleEndian found. False in all other cases.
213  */
214 bool gdcmParser::IsDeflatedExplicitVRLittleEndianTransferSyntax(void) 
215 {
216    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
217    if ( !Element )
218       return false;
219    LoadHeaderEntrySafe(Element);
220
221    std::string Transfer = Element->GetValue();
222    if ( Transfer == "1.2.840.10008.1.2.1.99" )
223       return true;
224    return false;
225 }
226
227 /**
228  * \ingroup gdcmParser
229  * \brief   Determines if the Transfer Syntax was already encountered
230  *          and if it corresponds to a Explicit VR Big Endian one.
231  *
232  * @return  True when big endian found. False in all other cases.
233  */
234 bool gdcmParser::IsExplicitVRBigEndianTransferSyntax(void) 
235 {
236    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
237    if ( !Element )
238       return false;
239    LoadHeaderEntrySafe(Element);
240
241    std::string Transfer = Element->GetValue();
242    if ( Transfer == "1.2.840.10008.1.2.2" )  //1.2.2 ??? A verifier !
243       return true;
244    return false;
245 }
246
247 /**
248  * \ingroup gdcmParser
249  * \brief  returns the File Type 
250  *         (ACR, ACR_LIBIDO, ExplicitVR, ImplicitVR, Unknown)
251  * @return 
252  */
253 FileType gdcmParser::GetFileType(void) {
254    return(filetype);
255 }
256
257 /**
258  * \ingroup gdcmParser
259  * \brief   opens the file
260  * @param   exception_on_error
261  * @return  
262  */
263 FILE *gdcmParser::OpenFile(bool exception_on_error)
264   throw(gdcmFileError) 
265 {
266   fp=fopen(filename.c_str(),"rb");
267   if(exception_on_error) 
268   {
269     if(!fp)
270       throw gdcmFileError("gdcmParser::gdcmParser(const char *, bool)");
271   }
272
273   if ( fp ) 
274   {
275      guint16 zero;
276      fread(&zero,  (size_t)2, (size_t)1, fp);
277
278     //ACR -- or DICOM with no Preamble --
279     if( zero == 0x0008 || zero == 0x0800 || zero == 0x0002 || zero == 0x0200)
280        return(fp);
281
282     //DICOM
283     fseek(fp, 126L, SEEK_CUR);
284     char dicm[4];
285     fread(dicm,  (size_t)4, (size_t)1, fp);
286     if( memcmp(dicm, "DICM", 4) == 0 )
287        return(fp);
288
289     fclose(fp);
290     dbg.Verbose(0, "gdcmParser::gdcmParser not DICOM/ACR", filename.c_str());
291   }
292   else {
293     std::cerr<<filename.c_str()<<std::endl;
294     dbg.Verbose(0, "gdcmParser::gdcmParser cannot open file", filename.c_str());
295   }
296   return(NULL);
297 }
298
299 /**
300  * \ingroup gdcmParser
301  * \brief closes the file  
302  * @return  TRUE if the close was successfull 
303  */
304 bool gdcmParser::CloseFile(void) 
305 {
306   int closed = fclose(fp);
307   fp = (FILE *)0;
308   if (! closed)
309      return false;
310   return true;
311 }
312
313 /**
314  * \ingroup gdcmParser
315  * \brief   Parses the header of the file but WITHOUT loading element values.
316  */
317 void gdcmParser::Parse(bool exception_on_error) throw(gdcmFormatError) 
318 {
319    gdcmHeaderEntry *newHeaderEntry = (gdcmHeaderEntry *)0;
320    
321    rewind(fp);
322    CheckSwap();
323    while ( (newHeaderEntry = ReadNextHeaderEntry()) ) 
324    {
325       SkipHeaderEntry(newHeaderEntry);
326       AddHeaderEntry(newHeaderEntry);
327    }
328 }
329
330 /**
331  * \ingroup gdcmParser
332  * \brief 
333  * @param fp file pointer on an already open file
334  * @param   type type of the File to be written 
335  *          (ACR-NEMA, ExplicitVR, ImplicitVR)
336  * @return  always "True" ?!
337  */
338 bool gdcmParser::Write(FILE *fp, FileType type) 
339 {
340 // ==============
341 // TODO The stuff has been rewritten using the chained list instead 
342 //      of the H table
343 //      so we could remove the GroupHT from the gdcmParser
344 // To be checked
345 // =============
346
347    // TODO : move the following lines (and a lot of others, to be written)
348    // to a future function CheckAndCorrectHeader
349    
350         // Question :
351         // Comment pourrait-on savoir si le DcmHeader vient d'un fichier DicomV3 ou non
352         // (FileType est un champ de gdcmParser ...)
353         // WARNING : Si on veut ecrire du DICOM V3 a partir d'un DcmHeader ACR-NEMA
354         // no way 
355         // a moins de se livrer a un tres complique ajout des champs manquants.
356         // faire un CheckAndCorrectHeader (?)  
357          
358
359    if (type == ImplicitVR) 
360    {
361       std::string implicitVRTransfertSyntax = "1.2.840.10008.1.2";
362       ReplaceOrCreateByNumber(implicitVRTransfertSyntax,0x0002, 0x0010);
363       
364       //FIXME Refer to standards on page 21, chapter 6.2 "Value representation":
365       //      values with a VR of UI shall be padded with a single trailing null
366       //      Dans le cas suivant on doit pader manuellement avec un 0
367       
368       SetEntryLengthByNumber(18, 0x0002, 0x0010);
369    } 
370
371    if (type == ExplicitVR) 
372    {
373       std::string explicitVRTransfertSyntax = "1.2.840.10008.1.2.1";
374       ReplaceOrCreateByNumber(explicitVRTransfertSyntax,0x0002, 0x0010);
375       
376       //FIXME Refer to standards on page 21, chapter 6.2 "Value representation":
377       //      values with a VR of UI shall be padded with a single trailing null
378       //      Dans le cas suivant on doit pader manuellement avec un 0
379       
380       SetEntryLengthByNumber(20, 0x0002, 0x0010);
381    }
382
383
384    if ( (type == ImplicitVR) || (type == ExplicitVR) )
385       UpdateGroupLength(false,type);
386    if ( type == ACR)
387       UpdateGroupLength(true,ACR);
388
389    WriteEntries(type, fp);
390    return(true);
391  }
392
393 /**
394  * \ingroup gdcmParser
395  * \brief   Modifies the value of a given Header Entry (Dicom Element)
396  *          if it exists; Creates it with the given value if it doesn't
397  * @param   Value passed as a std::string
398  * @param   Group
399  * @param   Elem
400  * \return  boolean
401  */
402 bool gdcmParser::ReplaceOrCreateByNumber(std::string Value, 
403                                         guint16 Group, guint16 Elem ) 
404 {
405    if (CheckIfEntryExistByNumber(Group, Elem) == 0) {
406       gdcmHeaderEntry *a =NewHeaderEntryByNumber(Group, Elem);
407       if (a == NULL) 
408          return false;
409       AddHeaderEntry(a);
410    }   
411    SetEntryByNumber(Value, Group, Elem);
412    return(true);
413 }   
414
415 /**
416  * \ingroup gdcmParser
417  * \brief   Modifies the value of a given Header Entry (Dicom Element)
418  *          if it exists; Creates it with the given value if it doesn't
419  * @param   Value passed as a char*
420  * @param   Group
421  * @param   Elem
422  * \return  boolean 
423  * 
424  */
425 bool gdcmParser::ReplaceOrCreateByNumber(char* Value, guint16 Group, guint16 Elem ) 
426 {
427    gdcmHeaderEntry* nvHeaderEntry=NewHeaderEntryByNumber(Group, Elem);
428
429    if(!nvHeaderEntry)
430       return(false);
431
432    AddHeaderEntry(nvHeaderEntry);
433
434    std::string v = Value;       
435    SetEntryByNumber(v, Group, Elem);
436    return(true);
437 }  
438
439 /**
440  * \ingroup gdcmParser
441  * \brief   Set a new value if the invoked element exists
442  *          Seems to be useless !!!
443  * @param   Value
444  * @param   Group
445  * @param   Elem
446  * \return  boolean 
447  */
448 bool gdcmParser::ReplaceIfExistByNumber(char* Value, guint16 Group, guint16 Elem ) 
449 {
450    std::string v = Value;       
451    SetEntryByNumber(v, Group, Elem);
452    return true;
453
454
455 //-----------------------------------------------------------------------------
456 // Protected
457 /**
458  * \ingroup gdcmParser
459  * \brief   Checks if a given Dicom Element exists
460  * \        within the H table
461  * @param   group Group   number of the searched Dicom Element 
462  * @param   element  Element number of the searched Dicom Element 
463  * @return  number of occurences
464  */
465 int gdcmParser::CheckIfEntryExistByNumber(guint16 group, guint16 element ) 
466 {
467         std::string key = gdcmDictEntry::TranslateToKey(group, element );
468         return (tagHT.count(key));
469 }
470
471 /**
472  * \ingroup gdcmParser
473  * \brief   Searches within Header Entries (Dicom Elements) parsed with 
474  *          the public and private dictionaries 
475  *          for the element value of a given tag.
476  * \warning Don't use any longer : use GetPubEntryByName
477  * @param   tagName name of the searched element.
478  * @return  Corresponding element value when it exists,
479  *          and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
480  */
481 std::string gdcmParser::GetEntryByName(std::string tagName) 
482 {
483    gdcmDictEntry *dictEntry = RefPubDict->GetTagByName(tagName); 
484    if( dictEntry == NULL)
485       return GDCM_UNFOUND;
486
487    return(GetEntryByNumber(dictEntry->GetGroup(),dictEntry->GetElement()));  
488 }
489
490 /**
491  * \ingroup gdcmParser
492  * \brief   Searches within Header Entries (Dicom Elements) parsed with 
493  *          the public and private dictionaries 
494  *          for the element value representation of a given tag.
495  *
496  *          Obtaining the VR (Value Representation) might be needed by caller
497  *          to convert the string typed content to caller's native type 
498  *          (think of C++ vs Python). The VR is actually of a higher level
499  *          of semantics than just the native C++ type.
500  * @param   tagName name of the searched element.
501  * @return  Corresponding element value representation when it exists,
502  *          and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
503  */
504 std::string gdcmParser::GetEntryVRByName(std::string tagName) 
505 {
506    gdcmDictEntry *dictEntry = RefPubDict->GetTagByName(tagName); 
507    if( dictEntry == NULL)
508       return GDCM_UNFOUND;
509
510    gdcmHeaderEntry* elem =  GetHeaderEntryByNumber(dictEntry->GetGroup(),
511                                                    dictEntry->GetElement());                                    
512    return elem->GetVR();
513 }
514
515 /**
516  * \ingroup gdcmParser
517  * \brief   Searches within Header Entries (Dicom Elements) parsed with 
518  *          the public and private dictionaries 
519  *          for the element value representation of a given tag.
520  * @param   group Group of the searched tag.
521  * @param   element Element of the searched tag.
522  * @return  Corresponding element value representation when it exists,
523  *          and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
524  */
525 std::string gdcmParser::GetEntryByNumber(guint16 group, guint16 element) 
526 {
527    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
528    if ( ! tagHT.count(key))
529       return GDCM_UNFOUND;
530    return tagHT.find(key)->second->GetValue();
531 }
532
533 /**
534  * \ingroup gdcmParser
535  * \brief   Searches within Header Entries (Dicom Elements) parsed with 
536  *          the public and private dictionaries 
537  *          for the element value representation of a given tag..
538  *
539  *          Obtaining the VR (Value Representation) might be needed by caller
540  *          to convert the string typed content to caller's native type 
541  *          (think of C++ vs Python). The VR is actually of a higher level
542  *          of semantics than just the native C++ type.
543  * @param   group Group of the searched tag.
544  * @param   element Element of the searched tag.
545  * @return  Corresponding element value representation when it exists,
546  *          and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
547  */
548 std::string gdcmParser::GetEntryVRByNumber(guint16 group, guint16 element) 
549 {
550    gdcmHeaderEntry* elem =  GetHeaderEntryByNumber(group, element);
551    if ( !elem )
552       return GDCM_UNFOUND;
553    return elem->GetVR();
554 }
555
556 /**
557  * \ingroup gdcmParser
558  * \brief   Sets the value (string) of the Header Entry (Dicom Element)
559  * @param   content string value of the Dicom Element
560  * @param   tagName name of the searched Dicom Element.
561  * @return  true when found
562  */
563 bool gdcmParser::SetEntryByName(std::string content,std::string tagName) 
564 {
565    gdcmDictEntry *dictEntry = RefPubDict->GetTagByName(tagName); 
566    if( dictEntry == NULL)
567       return false;                                 
568
569    return(SetEntryByNumber(content,dictEntry->GetGroup(),
570                                    dictEntry->GetElement()));
571 /*   TagKey key = gdcmDictEntry::TranslateToKey(dictEntry->GetGroup(), 
572                                               dictEntry->GetElement());
573    if ( GetPubEntry().count(key) == 0 )
574       return false;
575
576    int l = content.length();
577    if(l%2) // Odd length are padded with a space (020H).
578    {  
579       l++;
580       content = content + '\0';
581    }
582       
583    //tagHt[key]->SetValue(content);   
584    gdcmHeaderEntry * a;
585    IterHT p;
586    TagHeaderEntryHT::iterator p2;
587    // DO NOT remove the following lines : they explain how the stuff works 
588    //p= tagHt.equal_range(key); // get a pair of iterators first-last synonym
589    //p2=p.first;                // iterator on the first synonym 
590    //a=p2->second;              // H Table target column (2-nd col)    
591    // or, easier :
592    a = ((GetPubEntry().equal_range(key)).first)->second;       
593    a-> SetValue(content);   
594    std::string vr = a->GetVR();
595    
596    guint32 lgr;
597    if( (vr == "US") || (vr == "SS") ) 
598       lgr = 2;
599    else if( (vr == "UL") || (vr == "SL") )
600       lgr = 4;
601    else
602       lgr = l;     
603    a->SetLength(lgr);   
604    return true;*/
605 }
606
607 /**
608  * \ingroup gdcmParser
609  * \brief   Accesses an existing gdcmHeaderEntry (i.e. a Dicom Element)
610  *          through it's (group, element) and modifies it's content with
611  *          the given value.
612  * \warning Don't use any longer : use SetPubEntryByNumber
613  * @param   content new value to substitute with
614  * @param   group   group of the Dicom Element to modify
615  * @param   element element of the Dicom Element to modify
616  */
617 bool gdcmParser::SetEntryByNumber(std::string content, 
618                                   guint16 group,
619                                   guint16 element) 
620 {
621    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
622    if ( ! tagHT.count(key))
623       return false;
624    int l = content.length();
625    if(l%2) // Odd length are padded with a space (020H).
626    {  
627       l++;
628       content = content + '\0';
629    }
630       
631    //tagHT[key]->SetValue(content);   
632    gdcmHeaderEntry * a;
633    IterHT p;
634    TagHeaderEntryHT::iterator p2;
635    // DO NOT remove the following lines : they explain the stuff   
636    //p= tagHT.equal_range(key); // get a pair of iterators first-last synonym
637    //p2=p.first;                // iterator on the first synonym 
638    //a=p2->second;              // H Table target column (2-nd col)
639     
640    // or, easier :
641    a = ((tagHT.equal_range(key)).first)->second; 
642        
643    a-> SetValue(content); 
644    
645    //std::string vr = tagHT[key]->GetVR();
646    std::string vr = a->GetVR();
647    
648    guint32 lgr;
649    if( (vr == "US") || (vr == "SS") ) 
650       lgr = 2;
651    else if( (vr == "UL") || (vr == "SL") )
652       lgr = 4;
653    else
654       lgr = l;     
655
656    //tagHT[key]->SetLength(lgr);
657    a->SetLength(lgr);   
658    return true;
659 }                                         
660
661 /**
662  * \ingroup gdcmParser
663  * \brief   Accesses an existing gdcmHeaderEntry (i.e. a Dicom Element)
664  *          in the PubHeaderEntrySet of this instance
665  *          through it's (group, element) and modifies it's length with
666  *          the given value.
667  * \warning Use with extreme caution.
668  * @param   length new length to substitute with
669  * @param   group   group of the ElVal to modify
670  * @param   element element of the ElVal to modify
671  * @return  1 on success, 0 otherwise.
672  */
673
674 bool gdcmParser::SetEntryLengthByNumber(guint32 length, 
675                                            guint16 group, guint16 element) 
676 {
677    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
678    if ( ! tagHT.count(key))
679       return false;
680    if (length%2) length++; // length must be even
681    //tagHT[key]->SetLength(length);
682    ( ((tagHT.equal_range(key)).first)->second )->SetLength(length);      
683          
684    return true ;                
685 }
686
687 /**
688  * \ingroup gdcmParser
689  * \brief   Gets (from Header) the offset  of a 'non string' element value 
690  * \        (LoadElementValues has already be executed)
691  * @param   Group
692  * @param   Elem
693  * @return File Offset of the Element Value 
694  */
695 size_t gdcmParser::GetEntryOffsetByNumber(guint16 Group, guint16 Elem) 
696 {
697    gdcmHeaderEntry* Entry = GetHeaderEntryByNumber(Group, Elem);         
698    if (!Entry) 
699    {
700       dbg.Verbose(1, "gdcmParser::GetHeaderEntryByNumber",
701                       "failed to Locate gdcmHeaderEntry");
702       return (size_t)0;
703    }
704    return Entry->GetOffset();
705 }
706
707 /**
708  * \ingroup gdcmParser
709  * \brief   Gets (from Header) a 'non string' element value 
710  * \        (LoadElementValues has already be executed)  
711  * @param   Group
712  * @param   Elem
713  * @return Pointer to the 'non string' area
714  */
715 void * gdcmParser::GetEntryVoidAreaByNumber(guint16 Group, guint16 Elem) 
716 {
717    gdcmHeaderEntry* Entry = GetHeaderEntryByNumber(Group, Elem);         
718    if (!Entry) 
719    {
720       dbg.Verbose(1, "gdcmParser::GetHeaderEntryByNumber",
721                   "failed to Locate gdcmHeaderEntry");
722       return (NULL);
723    }
724    return Entry->GetVoidArea();
725 }
726
727 /**
728  * \ingroup       gdcmParser
729  * \brief         Loads (from disk) the element content 
730  *                when a string is not suitable
731  */
732 void *gdcmParser::LoadEntryVoidArea(guint16 Group, guint16 Elem) 
733 {
734    gdcmHeaderEntry * Element= GetHeaderEntryByNumber(Group, Elem);
735    if ( !Element )
736       return NULL;
737    size_t o =(size_t)Element->GetOffset();
738    fseek(fp, o, SEEK_SET);
739    int l=Element->GetLength();
740    void * a = malloc(l);
741    if(!a) 
742         return NULL;
743
744    SetEntryVoidAreaByNumber(a, Group, Elem);
745    // TODO check the result 
746    size_t l2 = fread(a, 1, l ,fp);
747    if(l != l2) 
748    {
749         free(a);
750         return NULL;
751    }
752    return a;  
753 }
754
755 /**
756  * \ingroup gdcmParser
757  * \brief   Sets a 'non string' value to a given Dicom Element
758  * @param   area
759  * @param   group Group number of the searched Dicom Element 
760  * @param   element Element number of the searched Dicom Element 
761  * @return  
762  */
763 bool gdcmParser::SetEntryVoidAreaByNumber(void * area,guint16 group, guint16 element) 
764 {
765    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
766    if ( ! tagHT.count(key))
767       return false;
768    //tagHT[key]->SetVoidArea(area);
769    ( ((tagHT.equal_range(key)).first)->second )->SetVoidArea(area);      
770    return true;
771 }
772
773 /**
774  * \ingroup gdcmParser
775  * \brief   Searches within the Header Entries for a Dicom Element of
776  *          a given tag.
777  * @param   tagName name of the searched Dicom Element.
778  * @return  Corresponding Dicom Element when it exists, and NULL
779  *          otherwise.
780  */
781  gdcmHeaderEntry *gdcmParser::GetHeaderEntryByName(std::string tagName) 
782  {
783    gdcmDictEntry *dictEntry = RefPubDict->GetTagByName(tagName); 
784    if( dictEntry == NULL)
785       return NULL;
786
787   return(GetHeaderEntryByNumber(dictEntry->GetGroup(),dictEntry->GetElement()));
788 }
789
790 /**
791  * \ingroup gdcmParser
792  * \brief  retrieves a Dicom Element (the first one) using (group, element)
793  * \ warning (group, element) IS NOT an identifier inside the Dicom Header
794  *           if you think it's NOT UNIQUE, check the count number
795  *           and use iterators to retrieve ALL the Dicoms Elements within
796  *           a given couple (group, element)
797  * @param   group Group number of the searched Dicom Element 
798  * @param   element Element number of the searched Dicom Element 
799  * @return  
800  */
801 gdcmHeaderEntry* gdcmParser::GetHeaderEntryByNumber(guint16 group, guint16 element) 
802 {
803    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
804    if ( ! tagHT.count(key))
805       return NULL;
806    return tagHT.find(key)->second;
807 }
808
809 /**
810  * \ingroup       gdcmParser
811  * \brief         Loads the element while preserving the current
812  *                underlying file position indicator as opposed to
813  *                to LoadHeaderEntry that modifies it.
814  * @param entry   Header Entry whose value shall be loaded. 
815  * @return  
816  */
817 void gdcmParser::LoadHeaderEntrySafe(gdcmHeaderEntry * entry) 
818 {
819    long PositionOnEntry = ftell(fp);
820    LoadHeaderEntry(entry);
821    fseek(fp, PositionOnEntry, SEEK_SET);
822 }
823
824 /**
825  * \ingroup gdcmParser
826  * \brief   Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader
827  * \warning : to be re-written using the chained list instead of the H table.
828  * \warning : DO NOT use (doesn't work any longer because of the multimap)
829  * \todo : to be re-written using the chained list instead of the H table
830  * @param   SkipSequence TRUE if we don't want to write Sequences (ACR-NEMA Files)
831  * @param   type Type of the File (ExplicitVR,ImplicitVR, ACR, ...) 
832  */
833 void gdcmParser::UpdateGroupLength(bool SkipSequence, FileType type) 
834 {
835    guint16 gr, el;
836    std::string vr;
837    
838    gdcmHeaderEntry *elem;
839    char trash[10];
840    std::string str_trash;
841    
842    GroupKey key;
843    GroupHT groupHt;  // to hold the length of each group
844    TagKey tk;
845    // remember :
846    // typedef std::map<GroupKey, int> GroupHT;
847    
848    gdcmHeaderEntry *elemZ;
849   
850    // for each Tag in the DCM Header
851    
852    for (TagHeaderEntryHT::iterator tag2 = tagHT.begin(); 
853         tag2 != tagHT.end();
854         ++tag2)
855    {
856       elem  = tag2->second;
857       gr = elem->GetGroup();
858       el = elem->GetElement();
859       vr = elem->GetVR(); 
860                  
861       sprintf(trash, "%04x", gr);
862       key = trash;              // generate 'group tag'
863       
864       // if the caller decided not to take SEQUENCEs into account 
865       // e.g : he wants to write an ACR-NEMA File 
866                 
867       if (SkipSequence && vr == "SQ") 
868          continue;
869       
870       // Still unsolved problem :
871       // we cannot find the 'Sequence Delimitation Item'
872       // since it's at the end of the Hash Table
873       // (fffe,e0dd) 
874        
875       // pas SEQUENCE en ACR-NEMA
876       // WARNING : 
877       // --> la descente a l'interieur' des SQ 
878       // devrait etre faite avec une liste chainee, pas avec une HTable...
879             
880       if ( groupHt.count(key) == 0) // we just read the first elem of a given group
881       { 
882          if (el == 0x0000) // the first elem is 0x0000
883          {            
884             groupHt[key] = 0;         // initialize group length 
885          } 
886          else 
887          {
888             groupHt[key] = 2 + 2 + 4 + elem->GetLength(); // non 0x0000 first group elem
889          } 
890       } 
891       else // any elem but the first
892       {   
893          if (type == ExplicitVR) 
894          {
895             if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) 
896             {
897                groupHt[key] +=  4; // explicit VR AND OB, OW, SQ : 4 more bytes
898             }
899          }
900          groupHt[key] += 2 + 2 + 4 + elem->GetLength(); 
901       } 
902    }
903
904    unsigned short int gr_bid;
905   
906    for (GroupHT::iterator g = groupHt.begin(); // for each group we found
907         g != groupHt.end();
908         ++g)
909    { 
910       // FIXME: g++ -Wall -Wstrict-prototypes reports on following line:
911       //        warning: unsigned int format, different type arg
912       sscanf(g->first.c_str(),"%x",&gr_bid);
913       tk = g->first + "|0000";                  // generate the element full tag
914                      
915       if ( tagHT.count(tk) == 0) // if element 0x0000 not found
916       {                 
917          gdcmDictEntry * tagZ = new gdcmDictEntry(gr_bid, 0x0000, "UL");       
918          elemZ = new gdcmHeaderEntry(tagZ);
919          elemZ->SetLength(4);
920          AddHeaderEntry(elemZ);                         // create it
921       } 
922       else 
923       {
924          elemZ=GetHeaderEntryByNumber(gr_bid, 0x0000);
925       }     
926       sprintf(trash ,"%d",g->second);
927       str_trash=trash;
928       elemZ->SetValue(str_trash);
929    }   
930 }
931
932 /**
933  * \ingroup gdcmParser
934  * \brief   writes on disc according to the requested format
935  * \        (ACR-NEMA, ExplicitVR, ImplicitVR) the image
936  * \ warning does NOT add the missing elements in the header :
937  * \         it's up to the user doing it !
938  * \         (function CheckHeaderCoherence to be written)
939  * @param   type type of the File to be written 
940  *          (ACR-NEMA, ExplicitVR, ImplicitVR)
941  * @param   _fp already open file pointer
942  */
943 void gdcmParser::WriteEntries(FileType type, FILE * _fp) 
944 {
945    guint16 gr, el;
946    guint32 lgr;
947    const char * val;
948    std::string vr;
949    guint32 val_uint32;
950    guint16 val_uint16;
951    
952    std::vector<std::string> tokens;
953    
954    //  uses now listEntries to iterate, not TagHt!
955    //
956    //        pb : gdcmParser.Add does NOT update listEntries
957    //       TODO : find a trick (in STL?) to do it, at low cost !
958
959    void *ptr;
960
961    // TODO (?) tester les echecs en ecriture (apres chaque fwrite)
962
963    for (ListTag::iterator tag2=listEntries.begin();
964         tag2 != listEntries.end();
965         ++tag2)
966    {
967       gr =  (*tag2)->GetGroup();
968       el =  (*tag2)->GetElement();
969       lgr = (*tag2)->GetLength();
970       val = (*tag2)->GetValue().c_str();
971       vr =  (*tag2)->GetVR();
972       
973       if ( type == ACR ) 
974       { 
975          if (gr < 0x0008)   continue; // ignore pure DICOM V3 groups
976          if (gr %2)         continue; // ignore shadow groups
977          if (vr == "SQ" )   continue; // ignore Sequences
978                    // TODO : find a trick to *skip* the SeQuences !
979                    // Not only ignore the SQ element
980          if (gr == 0xfffe ) continue; // ignore delimiters
981       } 
982
983       fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp);  //group
984       fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp);  //element
985
986       if ( (type == ExplicitVR) && (gr <= 0x0002) ) 
987       {
988          // EXPLICIT VR
989          guint16 z=0, shortLgr;
990          fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp);
991
992          if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) 
993          {
994             fwrite ( &z,  (size_t)2 ,(size_t)1 ,_fp);
995             fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
996
997          } 
998          else 
999          {
1000             shortLgr=lgr;
1001             fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp);
1002          }
1003       } 
1004       else // IMPLICIT VR
1005       { 
1006          fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
1007       }
1008
1009       if (vr == "US" || vr == "SS") 
1010       {
1011          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
1012          Tokenize ((*tag2)->GetValue(), tokens, "\\");
1013          for (unsigned int i=0; i<tokens.size();i++) 
1014          {
1015             val_uint16 = atoi(tokens[i].c_str());
1016             ptr = &val_uint16;
1017             fwrite ( ptr,(size_t)2 ,(size_t)1 ,_fp);
1018          }
1019          tokens.clear();
1020          continue;
1021       }
1022       if (vr == "UL" || vr == "SL") 
1023       {
1024          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
1025          Tokenize ((*tag2)->GetValue(), tokens, "\\");
1026          for (unsigned int i=0; i<tokens.size();i++) 
1027          {
1028             val_uint32 = atoi(tokens[i].c_str());
1029             ptr = &val_uint32;
1030             fwrite ( ptr,(size_t)4 ,(size_t)1 ,_fp);
1031          }
1032          tokens.clear();
1033          continue;
1034       }     
1035       // Pixels are never loaded in the element !
1036       if ((gr == 0x7fe0) && (el == 0x0010) ) 
1037          break;
1038
1039       fwrite ( val,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
1040    }
1041 }
1042
1043 /**
1044  * \ingroup gdcmParser
1045  * \brief   Swaps back the bytes of 4-byte long integer accordingly to
1046  *          processor order.
1047  * @return  The properly swaped 32 bits integer.
1048  */
1049 guint32 gdcmParser::SwapLong(guint32 a) 
1050 {
1051    switch (sw) 
1052    {
1053       case    0 :
1054          break;
1055       case 4321 :
1056          a=( ((a<<24) & 0xff000000) | ((a<<8)  & 0x00ff0000) | 
1057              ((a>>8)  & 0x0000ff00) | ((a>>24) & 0x000000ff) );
1058          break;
1059    
1060       case 3412 :
1061          a=( ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
1062          break;
1063    
1064       case 2143 :
1065          a=( ((a<<8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff)  );
1066          break;
1067       default :
1068          dbg.Error(" gdcmParser::SwapLong : unset swap code");
1069          a=0;
1070    }
1071    return(a);
1072 }
1073
1074 /**
1075  * \ingroup gdcmParser
1076  * \brief   Swaps the bytes so they agree with the processor order
1077  * @return  The properly swaped 16 bits integer.
1078  */
1079 guint16 gdcmParser::SwapShort(guint16 a) 
1080 {
1081    if ( (sw==4321)  || (sw==2143) )
1082       a =(((a<<8) & 0x0ff00) | ((a>>8)&0x00ff));
1083    return (a);
1084 }
1085
1086 //-----------------------------------------------------------------------------
1087 // Private
1088 /**
1089  * \ingroup gdcmParser
1090  * \brief   Loads the element values of all the Header Entries pointed in the
1091  *          public Chained List.
1092  */
1093 void gdcmParser::LoadHeaderEntries(void) 
1094 {
1095    rewind(fp);
1096    for (ListTag::iterator i = GetPubListEntry().begin();
1097       i != GetPubListEntry().end();
1098       ++i)
1099    {
1100          LoadHeaderEntry(*i);
1101    }   
1102             
1103    rewind(fp);
1104
1105    // Load 'non string' values   
1106    std::string PhotometricInterpretation = GetEntryByNumber(0x0028,0x0004);   
1107    if( PhotometricInterpretation == "PALETTE COLOR " )
1108    {
1109       LoadEntryVoidArea(0x0028,0x1200);  // gray LUT   
1110       LoadEntryVoidArea(0x0028,0x1201);  // R    LUT
1111       LoadEntryVoidArea(0x0028,0x1202);  // G    LUT
1112       LoadEntryVoidArea(0x0028,0x1203);  // B    LUT
1113       
1114       LoadEntryVoidArea(0x0028,0x1221);  // Segmented Red   Palette Color LUT Data
1115       LoadEntryVoidArea(0x0028,0x1222);  // Segmented Green Palette Color LUT Data
1116       LoadEntryVoidArea(0x0028,0x1223);  // Segmented Blue  Palette Color LUT Data
1117    }
1118
1119    // --------------------------------------------------------------
1120    // Special Patch to allow gdcm to read ACR-LibIDO formated images
1121    //
1122    // if recognition code tells us we deal with a LibIDO image
1123    // we switch lineNumber and columnNumber
1124    //
1125    std::string RecCode; 
1126    RecCode = GetEntryByNumber(0x0008, 0x0010); // recognition code
1127    if (RecCode == "ACRNEMA_LIBIDO_1.1" ||
1128        RecCode == "CANRME_AILIBOD1_1." ) 
1129    {
1130          filetype = ACR_LIBIDO; 
1131          std::string rows    = GetEntryByNumber(0x0028, 0x0010);
1132          std::string columns = GetEntryByNumber(0x0028, 0x0011);
1133          SetEntryByNumber(columns, 0x0028, 0x0010);
1134          SetEntryByNumber(rows   , 0x0028, 0x0011);
1135    }
1136    // ----------------- End of Special Patch ----------------
1137 }
1138
1139 /**
1140  * \ingroup       gdcmParser
1141  * \brief         Loads the element content if it's length is not bigger
1142  *                than the value specified with
1143  *                gdcmParser::SetMaxSizeLoadEntry()
1144  * @param        ElVal Header Entry (Dicom Element) to be dealt with
1145  */
1146 void gdcmParser::LoadHeaderEntry(gdcmHeaderEntry * ElVal) 
1147 {
1148    size_t item_read;
1149    guint16 group  = ElVal->GetGroup();
1150    std::string  vr= ElVal->GetVR();
1151    guint32 length = ElVal->GetLength();
1152    bool SkipLoad  = false;
1153
1154    fseek(fp, (long)ElVal->GetOffset(), SEEK_SET);
1155    
1156    // the test was commented out to 'go inside' the SeQuences
1157    // we don't any longer skip them !
1158     
1159    // if( vr == "SQ" )  //  (DO NOT remove this comment)
1160    //    SkipLoad = true;
1161
1162    // A SeQuence "contains" a set of Elements.  
1163    //          (fffe e000) tells us an Element is beginning
1164    //          (fffe e00d) tells us an Element just ended
1165    //          (fffe e0dd) tells us the current SeQuence just ended
1166    if( group == 0xfffe )
1167       SkipLoad = true;
1168
1169    if ( SkipLoad ) 
1170    {
1171       ElVal->SetLength(0);
1172       ElVal->SetValue("gdcm::Skipped");
1173       return;
1174    }
1175
1176    // When the length is zero things are easy:
1177    if ( length == 0 ) 
1178    {
1179       ElVal->SetValue("");
1180       return;
1181    }
1182
1183    // The elements whose length is bigger than the specified upper bound
1184    // are not loaded. Instead we leave a short notice of the offset of
1185    // the element content and it's length.
1186    if (length > MaxSizeLoadEntry) 
1187    {
1188       std::ostringstream s;
1189       s << "gdcm::NotLoaded.";
1190       s << " Address:" << (long)ElVal->GetOffset();
1191       s << " Length:"  << ElVal->GetLength();
1192       s << " x(" << std::hex << ElVal->GetLength() << ")";
1193       ElVal->SetValue(s.str());
1194       return;
1195    }
1196    
1197    // When integer(s) are expected, read and convert the following 
1198    // n *(two or four bytes)
1199    // properly i.e. as integers as opposed to a strings.        
1200    // Elements with Value Multiplicity > 1
1201    // contain a set of integers (not a single one) 
1202         
1203    // Any compacter code suggested (?)
1204    if ( IsHeaderEntryAnInteger(ElVal) ) 
1205    {
1206       guint32 NewInt;
1207       std::ostringstream s;
1208       int nbInt;
1209       if (vr == "US" || vr == "SS") 
1210       {
1211          nbInt = length / 2;
1212          NewInt = ReadInt16();
1213          s << NewInt;
1214          if (nbInt > 1) 
1215          {
1216             for (int i=1; i < nbInt; i++) 
1217             {
1218                s << '\\';
1219                NewInt = ReadInt16();
1220                s << NewInt;
1221             }
1222          }
1223                         
1224       } 
1225       else if (vr == "UL" || vr == "SL") 
1226       {
1227          nbInt = length / 4;
1228          NewInt = ReadInt32();
1229          s << NewInt;
1230          if (nbInt > 1) 
1231          {
1232             for (int i=1; i < nbInt; i++) 
1233             {
1234                s << '\\';
1235                NewInt = ReadInt32();
1236                s << NewInt;
1237             }
1238          }
1239       }                                 
1240 #ifdef GDCM_NO_ANSI_STRING_STREAM
1241       s << std::ends; // to avoid oddities on Solaris
1242 #endif //GDCM_NO_ANSI_STRING_STREAM
1243       ElVal->SetValue(s.str());
1244       return;   
1245    }
1246    
1247    // We need an additional byte for storing \0 that is not on disk
1248    char* NewValue = (char*)malloc(length+1);
1249    if( !NewValue) 
1250    {
1251       dbg.Verbose(1, "LoadElementValue: Failed to allocate NewValue");
1252       return;
1253    }
1254    NewValue[length]= 0;
1255    
1256    item_read = fread(NewValue, (size_t)length, (size_t)1, fp);
1257    if ( item_read != 1 ) 
1258    {
1259       free(NewValue);
1260       dbg.Verbose(1, "gdcmParser::LoadElementValue","unread element value");
1261       ElVal->SetValue("gdcm::UnRead");
1262       return;
1263    }
1264    ElVal->SetValue(NewValue);
1265    free(NewValue);
1266 }
1267
1268 /**
1269  * \ingroup gdcmParser
1270  * \brief   add a new Dicom Element pointer to 
1271  *          the H Table and to the chained List
1272  * \warning push_bash in listEntries ONLY during ParseHeader
1273  * \todo    something to allow further Elements addition,
1274  * \        when position to be taken care of     
1275  * @param   newHeaderEntry
1276  */
1277 void gdcmParser::AddHeaderEntry(gdcmHeaderEntry * newHeaderEntry) 
1278 {
1279    tagHT.insert( PairHT( newHeaderEntry->GetKey(),newHeaderEntry) );
1280    listEntries.push_back(newHeaderEntry); 
1281    wasUpdated = 1;
1282 }
1283
1284 /**
1285  * \ingroup gdcmParser
1286  * \brief   
1287  * @param   ElVal : Header Entry whose length of the value shall be loaded. 
1288
1289  * @return 
1290  */
1291  void gdcmParser::FindHeaderEntryLength (gdcmHeaderEntry * ElVal) 
1292  {
1293    guint16 element = ElVal->GetElement();
1294    guint16 group   = ElVal->GetGroup();
1295    std::string  vr = ElVal->GetVR();
1296    guint16 length16;
1297    if( (element == 0x0010) && (group == 0x7fe0) ) 
1298    {
1299       dbg.SetDebug(-1);
1300       dbg.Verbose(2, "gdcmParser::FindLength: ",
1301                      "we reached 7fe0 0010");
1302    }   
1303    
1304    if ( (filetype == ExplicitVR) && ! ElVal->IsImplicitVr() ) 
1305    {
1306       if ( (vr=="OB") || (vr=="OW") || (vr=="SQ") || (vr=="UN") ) 
1307       {
1308          // The following reserved two bytes (see PS 3.5-2001, section
1309          // 7.1.2 Data element structure with explicit vr p27) must be
1310          // skipped before proceeding on reading the length on 4 bytes.
1311          fseek(fp, 2L, SEEK_CUR);
1312          guint32 length32 = ReadInt32();
1313
1314          if ( (vr == "OB") && (length32 == 0xffffffff) ) 
1315          {
1316             ElVal->SetLength(FindHeaderEntryLengthOB());
1317             return;
1318          }
1319          FixHeaderEntryFoundLength(ElVal, length32); 
1320          return;
1321       }
1322
1323       // Length is encoded on 2 bytes.
1324       length16 = ReadInt16();
1325       
1326       // We can tell the current file is encoded in big endian (like
1327       // Data/US-RGB-8-epicard) when we find the "Transfer Syntax" tag
1328       // and it's value is the one of the encoding of a big endian file.
1329       // In order to deal with such big endian encoded files, we have
1330       // (at least) two strategies:
1331       // * when we load the "Transfer Syntax" tag with value of big endian
1332       //   encoding, we raise the proper flags. Then we wait for the end
1333       //   of the META group (0x0002) among which is "Transfer Syntax",
1334       //   before switching the swap code to big endian. We have to postpone
1335       //   the switching of the swap code since the META group is fully encoded
1336       //   in little endian, and big endian coding only starts at the next
1337       //   group. The corresponding code can be hard to analyse and adds
1338       //   many additional unnecessary tests for regular tags.
1339       // * the second strategy consists in waiting for trouble, that shall
1340       //   appear when we find the first group with big endian encoding. This
1341       //   is easy to detect since the length of a "Group Length" tag (the
1342       //   ones with zero as element number) has to be of 4 (0x0004). When we
1343       //   encounter 1024 (0x0400) chances are the encoding changed and we
1344       //   found a group with big endian encoding.
1345       // We shall use this second strategy. In order to make sure that we
1346       // can interpret the presence of an apparently big endian encoded
1347       // length of a "Group Length" without committing a big mistake, we
1348       // add an additional check: we look in the already parsed elements
1349       // for the presence of a "Transfer Syntax" whose value has to be "big
1350       // endian encoding". When this is the case, chances are we have got our
1351       // hands on a big endian encoded file: we switch the swap code to
1352       // big endian and proceed...
1353       if ( (element  == 0x0000) && (length16 == 0x0400) ) 
1354       {
1355          if ( ! IsExplicitVRBigEndianTransferSyntax() ) 
1356          {
1357             dbg.Verbose(0, "gdcmParser::FindLength", "not explicit VR");
1358             errno = 1;
1359             return;
1360          }
1361          length16 = 4;
1362          SwitchSwapToBigEndian();
1363          // Restore the unproperly loaded values i.e. the group, the element
1364          // and the dictionary entry depending on them.
1365          guint16 CorrectGroup   = SwapShort(ElVal->GetGroup());
1366          guint16 CorrectElem    = SwapShort(ElVal->GetElement());
1367          gdcmDictEntry * NewTag = GetDictEntryByNumber(CorrectGroup,
1368                                                        CorrectElem);
1369          if (!NewTag) 
1370          {
1371             // This correct tag is not in the dictionary. Create a new one.
1372             NewTag = Dicts->NewVirtualDictEntry(CorrectGroup, CorrectElem);
1373          }
1374          // FIXME this can create a memory leaks on the old entry that be
1375          // left unreferenced.
1376          ElVal->SetDictEntry(NewTag);
1377       }
1378        
1379       // Heuristic: well some files are really ill-formed.
1380       if ( length16 == 0xffff) 
1381       {
1382          length16 = 0;
1383          //dbg.Verbose(0, "gdcmParser::FindLength",
1384          //            "Erroneous element length fixed.");
1385          // Actually, length= 0xffff means that we deal with
1386          // Unknown Sequence Length 
1387       }
1388
1389       FixHeaderEntryFoundLength(ElVal, (guint32)length16);
1390       return;
1391    }
1392
1393    // Either implicit VR or a non DICOM conformal (see not below) explicit
1394    // VR that ommited the VR of (at least) this element. Farts happen.
1395    // [Note: according to the part 5, PS 3.5-2001, section 7.1 p25
1396    // on Data elements "Implicit and Explicit VR Data Elements shall
1397    // not coexist in a Data Set and Data Sets nested within it".]
1398    // Length is on 4 bytes.
1399    FixHeaderEntryFoundLength(ElVal, ReadInt32());
1400    return;
1401 }
1402
1403 /**
1404  * \ingroup   gdcmParser
1405  * \brief     Find the Value Representation of the current Dicom Element.
1406  * @param ElVal
1407  */
1408 void gdcmParser::FindHeaderEntryVR( gdcmHeaderEntry *ElVal) 
1409 {
1410    if (filetype != ExplicitVR)
1411       return;
1412
1413    char VR[3];
1414    std::string vr;
1415    int lgrLue;
1416    char msg[100]; // for sprintf. Sorry
1417
1418    long PositionOnEntry = ftell(fp);
1419    // Warning: we believe this is explicit VR (Value Representation) because
1420    // we used a heuristic that found "UL" in the first tag. Alas this
1421    // doesn't guarantee that all the tags will be in explicit VR. In some
1422    // cases (see e-film filtered files) one finds implicit VR tags mixed
1423    // within an explicit VR file. Hence we make sure the present tag
1424    // is in explicit VR and try to fix things if it happens not to be
1425    // the case.
1426    bool RealExplicit = true;
1427    
1428    lgrLue=fread (&VR, (size_t)2,(size_t)1, fp);
1429    VR[2]=0;
1430    vr = std::string(VR);
1431       
1432    // Assume we are reading a falsely explicit VR file i.e. we reached
1433    // a tag where we expect reading a VR but are in fact we read the
1434    // first to bytes of the length. Then we will interogate (through find)
1435    // the dicom_vr dictionary with oddities like "\004\0" which crashes
1436    // both GCC and VC++ implementations of the STL map. Hence when the
1437    // expected VR read happens to be non-ascii characters we consider
1438    // we hit falsely explicit VR tag.
1439
1440    if ( (!isalpha(VR[0])) && (!isalpha(VR[1])) )
1441       RealExplicit = false;
1442
1443    // CLEANME searching the dicom_vr at each occurence is expensive.
1444    // PostPone this test in an optional integrity check at the end
1445    // of parsing or only in debug mode.
1446    if ( RealExplicit && !dicom_vr->Count(vr) )
1447       RealExplicit= false;
1448
1449    if ( RealExplicit ) 
1450    {
1451       if ( ElVal->IsVRUnknown() ) 
1452       {
1453          // When not a dictionary entry, we can safely overwrite the VR.
1454          ElVal->SetVR(vr);
1455          return; 
1456       }
1457       if ( ElVal->GetVR() == vr ) 
1458       {
1459          // The VR we just read and the dictionary agree. Nothing to do.
1460          return;
1461       }
1462       // The VR present in the file and the dictionary disagree. We assume
1463       // the file writer knew best and use the VR of the file. Since it would
1464       // be unwise to overwrite the VR of a dictionary (since it would
1465       // compromise it's next user), we need to clone the actual DictEntry
1466       // and change the VR for the read one.
1467       gdcmDictEntry* NewTag = Dicts->NewVirtualDictEntry(ElVal->GetGroup(),
1468                                  ElVal->GetElement(),
1469                                  vr,
1470                                  "FIXME",
1471                                  ElVal->GetName());
1472       ElVal->SetDictEntry(NewTag);
1473       return; 
1474    }
1475    
1476    // We thought this was explicit VR, but we end up with an
1477    // implicit VR tag. Let's backtrack.   
1478    
1479       sprintf(msg,"Falsely explicit vr file (%04x,%04x)\n", 
1480                    ElVal->GetGroup(),ElVal->GetElement());
1481       dbg.Verbose(1, "gdcmParser::FindVR: ",msg);
1482    
1483    fseek(fp, PositionOnEntry, SEEK_SET);
1484    // When this element is known in the dictionary we shall use, e.g. for
1485    // the semantics (see the usage of IsAnInteger), the VR proposed by the
1486    // dictionary entry. Still we have to flag the element as implicit since
1487    // we know now our assumption on expliciteness is not furfilled.
1488    // avoid  .
1489    if ( ElVal->IsVRUnknown() )
1490       ElVal->SetVR("Implicit");
1491    ElVal->SetImplicitVr();
1492 }
1493
1494 /**
1495  * \ingroup gdcmParser
1496  * \brief  Skip a given Header Entry 
1497  * \warning NOT end user intended method !
1498  * @param entry 
1499  * @return 
1500  */
1501 void gdcmParser::SkipHeaderEntry(gdcmHeaderEntry * entry) 
1502 {
1503     SkipBytes(entry->GetLength());
1504 }
1505
1506 /**
1507  * \ingroup gdcmParser
1508  * \brief   When the length of an element value is obviously wrong (because
1509  *          the parser went Jabberwocky) one can hope improving things by
1510  *          applying this heuristic.
1511  */
1512 void gdcmParser::FixHeaderEntryFoundLength(gdcmHeaderEntry * ElVal, guint32 FoundLength) 
1513 {
1514    ElVal->SetReadLength(FoundLength); // will be updated only if a bug is found
1515                      
1516    if ( FoundLength == 0xffffffff) 
1517    {
1518       FoundLength = 0;
1519    }
1520       
1521    // Sorry for the patch!  
1522    // XMedCom did the trick to read some nasty GE images ...
1523    else if (FoundLength == 13) 
1524    {
1525       // The following 'if' will be removed when there is no more
1526       // images on Creatis HDs with a 13 length for Manufacturer...
1527       if ( (ElVal->GetGroup() != 0x0008) ||  
1528            ( (ElVal->GetElement() != 0x0070) && (ElVal->GetElement() != 0x0080) ) ) {
1529       // end of remove area
1530          FoundLength =10;
1531          ElVal->SetReadLength(10); // a bug is to be fixed
1532       }
1533    }
1534
1535    // to fix some garbage 'Leonardo' Siemens images
1536    // May be commented out to avoid overhead
1537    else if ( (ElVal->GetGroup() == 0x0009) &&
1538        ( (ElVal->GetElement() == 0x1113) || (ElVal->GetElement() == 0x1114) ) )
1539    {
1540       FoundLength =4;
1541       ElVal->SetReadLength(4); // a bug is to be fixed 
1542    } 
1543    // end of fix
1544          
1545    // to try to 'go inside' SeQuences (with length), and not to skip them        
1546    else if ( ElVal->GetVR() == "SQ") 
1547    { 
1548       if (enableSequences)    // only if the user does want to !
1549          FoundLength =0;         
1550    } 
1551     
1552    // a SeQuence Element is beginning                                          
1553    // Let's forget it's length                                                 
1554    // (we want to 'go inside')  
1555
1556    // Pb : *normaly*  fffe|e000 is just a marker, its length *should be* zero
1557    // in gdcm-MR-PHILIPS-16-Multi-Seq.dcm we find lengthes as big as 28800
1558    // if we set the length to zero IsHeaderEntryAnInteger() breaks...
1559    // if we don't, we lost 28800 characters from the Header :-(
1560                                                  
1561    else if(ElVal->GetGroup() == 0xfffe)
1562    { 
1563                        // sometimes, length seems to be wrong                                      
1564       FoundLength =0;  // some more clever checking to be done !
1565                        // I give up!
1566                        // only  gdcm-MR-PHILIPS-16-Multi-Seq.dcm
1567                        // causes troubles :-(                                                     
1568    }     
1569     
1570    ElVal->SetUsableLength(FoundLength);
1571 }
1572
1573 /**
1574  * \ingroup gdcmParser
1575  * \brief   Apply some heuristics to predict wether the considered 
1576  *          element value contains/represents an integer or not.
1577  * @param   ElVal The element value on which to apply the predicate.
1578  * @return  The result of the heuristical predicate.
1579  */
1580 bool gdcmParser::IsHeaderEntryAnInteger(gdcmHeaderEntry * ElVal) 
1581 {
1582    guint16 element = ElVal->GetElement();
1583    guint16 group   = ElVal->GetGroup();
1584    std::string  vr = ElVal->GetVR();
1585    guint32 length  = ElVal->GetLength();
1586
1587    // When we have some semantics on the element we just read, and if we
1588    // a priori know we are dealing with an integer, then we shall be
1589    // able to swap it's element value properly.
1590    if ( element == 0 )  // This is the group length of the group
1591    {  
1592       if (length == 4)
1593          return true;
1594       else 
1595       {
1596          std::ostringstream s;
1597          s << "Erroneous Group Length element length  on :" \
1598            << std::hex << group << " , " << element;
1599          dbg.Error("gdcmParser::IsAnInteger",
1600             s.str().c_str());     
1601       }
1602    }
1603    if ( (vr == "UL") || (vr == "US") || (vr == "SL") || (vr == "SS") )
1604       return true;
1605    
1606    return false;
1607 }
1608
1609 /**
1610  * \ingroup gdcmParser
1611  * \brief   
1612  *
1613  * @return 
1614  */
1615  guint32 gdcmParser::FindHeaderEntryLengthOB(void) 
1616  {
1617    // See PS 3.5-2001, section A.4 p. 49 on encapsulation of encoded pixel data.
1618    guint16 g;
1619    guint16 n; 
1620    long PositionOnEntry = ftell(fp);
1621    bool FoundSequenceDelimiter = false;
1622    guint32 TotalLength = 0;
1623    guint32 ItemLength;
1624
1625    while ( ! FoundSequenceDelimiter) 
1626    {
1627       g = ReadInt16();
1628       n = ReadInt16();   
1629       if (errno == 1)
1630          return 0;
1631       TotalLength += 4;  // We even have to decount the group and element 
1632      
1633       if ( g != 0xfffe && g!=0xb00c ) /*for bogus header */ 
1634       {
1635          char msg[100]; // for sprintf. Sorry
1636          sprintf(msg,"wrong group (%04x) for an item sequence (%04x,%04x)\n",g, g,n);
1637          dbg.Verbose(1, "gdcmParser::FindLengthOB: ",msg); 
1638          errno = 1;
1639          return 0;
1640       }
1641       if ( n == 0xe0dd || ( g==0xb00c && n==0x0eb6 ) ) /* for bogus header  */ 
1642          FoundSequenceDelimiter = true;
1643       else if ( n != 0xe000 )
1644       {
1645          char msg[100];  // for sprintf. Sorry
1646          sprintf(msg,"wrong element (%04x) for an item sequence (%04x,%04x)\n",
1647                       n, g,n);
1648          dbg.Verbose(1, "gdcmParser::FindLengthOB: ",msg);
1649          errno = 1;
1650          return 0;
1651       }
1652       ItemLength = ReadInt32();
1653       TotalLength += ItemLength + 4;  // We add 4 bytes since we just read
1654                                       // the ItemLength with ReadInt32                                     
1655       SkipBytes(ItemLength);
1656    }
1657    fseek(fp, PositionOnEntry, SEEK_SET);
1658    return TotalLength;
1659 }
1660
1661 /**
1662  * \ingroup gdcmParser
1663  * \brief Reads a supposed to be 16 Bits integer
1664  * \     (swaps it depending on processor endianity) 
1665  *
1666  * @return integer acts as a boolean
1667  */
1668 guint16 gdcmParser::ReadInt16(void) 
1669 {
1670    guint16 g;
1671    size_t item_read;
1672    item_read = fread (&g, (size_t)2,(size_t)1, fp);
1673    if ( item_read != 1 ) 
1674    {
1675       if(ferror(fp)) 
1676          dbg.Verbose(0, "gdcmParser::ReadInt16", " File Error");
1677       errno = 1;
1678       return 0;
1679    }
1680    errno = 0;
1681    g = SwapShort(g);
1682    return g;
1683 }
1684
1685 /**
1686  * \ingroup gdcmParser
1687  * \brief  Reads a supposed to be 32 Bits integer
1688  * \       (swaps it depending on processor endianity)  
1689  *
1690  * @return 
1691  */
1692 guint32 gdcmParser::ReadInt32(void) 
1693 {
1694    guint32 g;
1695    size_t item_read;
1696    item_read = fread (&g, (size_t)4,(size_t)1, fp);
1697    if ( item_read != 1 ) 
1698    { 
1699      if(ferror(fp)) 
1700          dbg.Verbose(0, "gdcmParser::ReadInt32", " File Error");   
1701       errno = 1;
1702       return 0;
1703    }
1704    errno = 0;   
1705    g = SwapLong(g);
1706    return g;
1707 }
1708
1709 /**
1710  * \ingroup gdcmParser
1711  * \brief   
1712  *
1713  * @return 
1714  */
1715 void gdcmParser::SkipBytes(guint32 NBytes) 
1716 {
1717    //FIXME don't dump the returned value
1718    (void)fseek(fp, (long)NBytes, SEEK_CUR);
1719 }
1720
1721 /**
1722  * \ingroup gdcmParser
1723  * \brief   
1724  */
1725 void gdcmParser::Initialise(void) 
1726 {
1727    dicom_vr = gdcmGlobal::GetVR();
1728    dicom_ts = gdcmGlobal::GetTS();
1729    Dicts    = gdcmGlobal::GetDicts();
1730    RefPubDict = Dicts->GetDefaultPubDict();
1731    RefShaDict = (gdcmDict*)0;
1732 }
1733
1734 /**
1735  * \ingroup gdcmParser
1736  * \brief   Discover what the swap code is (among little endian, big endian,
1737  *          bad little endian, bad big endian).
1738  *
1739  */
1740 void gdcmParser::CheckSwap()
1741 {
1742    // Fourth semantics:
1743    //
1744    // ---> Warning : This fourth field is NOT part 
1745    //                of the 'official' Dicom Dictionnary
1746    //                and should NOT be used.
1747    //                (Not defined for all the groups
1748    //                 may be removed in a future release)
1749    //
1750    // CMD      Command        
1751    // META     Meta Information 
1752    // DIR      Directory
1753    // ID
1754    // PAT      Patient
1755    // ACQ      Acquisition
1756    // REL      Related
1757    // IMG      Image
1758    // SDY      Study
1759    // VIS      Visit 
1760    // WAV      Waveform
1761    // PRC
1762    // DEV      Device
1763    // NMI      Nuclear Medicine
1764    // MED
1765    // BFS      Basic Film Session
1766    // BFB      Basic Film Box
1767    // BIB      Basic Image Box
1768    // BAB
1769    // IOB
1770    // PJ
1771    // PRINTER
1772    // RT       Radio Therapy
1773    // DVH   
1774    // SSET
1775    // RES      Results
1776    // CRV      Curve
1777    // OLY      Overlays
1778    // PXL      Pixels
1779    // DL       Delimiters
1780    //
1781
1782    // The only guaranted way of finding the swap code is to find a
1783    // group tag since we know it's length has to be of four bytes i.e.
1784    // 0x00000004. Finding the swap code in then straigthforward. Trouble
1785    // occurs when we can't find such group...
1786    guint32  s;
1787    guint32  x=4;  // x : for ntohs
1788    bool net2host; // true when HostByteOrder is the same as NetworkByteOrder
1789     
1790    int lgrLue;
1791    char *entCur;
1792    char deb[HEADER_LENGTH_TO_READ];
1793     
1794    // First, compare HostByteOrder and NetworkByteOrder in order to
1795    // determine if we shall need to swap bytes (i.e. the Endian type).
1796    if (x==ntohs(x))
1797       net2host = true;
1798    else
1799       net2host = false; 
1800     //cout << net2host << endl;
1801          
1802    // The easiest case is the one of a DICOM header, since it possesses a
1803    // file preamble where it suffice to look for the string "DICM".
1804    lgrLue = fread(deb, 1, HEADER_LENGTH_TO_READ, fp);
1805    
1806    entCur = deb + 128;
1807    if(memcmp(entCur, "DICM", (size_t)4) == 0) 
1808    {
1809       dbg.Verbose(1, "gdcmParser::CheckSwap:", "looks like DICOM Version3");
1810       // Next, determine the value representation (VR). Let's skip to the
1811       // first element (0002, 0000) and check there if we find "UL" 
1812       // - or "OB" if the 1st one is (0002,0001) -,
1813       // in which case we (almost) know it is explicit VR.
1814       // WARNING: if it happens to be implicit VR then what we will read
1815       // is the length of the group. If this ascii representation of this
1816       // length happens to be "UL" then we shall believe it is explicit VR.
1817       // FIXME: in order to fix the above warning, we could read the next
1818       // element value (or a couple of elements values) in order to make
1819       // sure we are not commiting a big mistake.
1820       // We need to skip :
1821       // * the 128 bytes of File Preamble (often padded with zeroes),
1822       // * the 4 bytes of "DICM" string,
1823       // * the 4 bytes of the first tag (0002, 0000),or (0002, 0001)
1824       // i.e. a total of  136 bytes.
1825       entCur = deb + 136;
1826       // FIXME
1827       // Use gdcmParser::dicom_vr to test all the possibilities
1828       // instead of just checking for UL, OB and UI !?
1829       if(  (memcmp(entCur, "UL", (size_t)2) == 0) ||
1830           (memcmp(entCur, "OB", (size_t)2) == 0) ||
1831           (memcmp(entCur, "UI", (size_t)2) == 0) )   
1832       {
1833          filetype = ExplicitVR;
1834          dbg.Verbose(1, "gdcmParser::CheckSwap:",
1835                      "explicit Value Representation");
1836       } 
1837       else 
1838       {
1839          filetype = ImplicitVR;
1840          dbg.Verbose(1, "gdcmParser::CheckSwap:",
1841                      "not an explicit Value Representation");
1842       }
1843       if (net2host) 
1844       {
1845          sw = 4321;
1846          dbg.Verbose(1, "gdcmParser::CheckSwap:",
1847                         "HostByteOrder != NetworkByteOrder");
1848       } 
1849       else 
1850       {
1851          sw = 0;
1852          dbg.Verbose(1, "gdcmParser::CheckSwap:",
1853                         "HostByteOrder = NetworkByteOrder");
1854       }
1855       
1856       // Position the file position indicator at first tag (i.e.
1857       // after the file preamble and the "DICM" string).
1858       rewind(fp);
1859       fseek (fp, 132L, SEEK_SET);
1860       return;
1861    } // End of DicomV3
1862
1863    // Alas, this is not a DicomV3 file and whatever happens there is no file
1864    // preamble. We can reset the file position indicator to where the data
1865    // is (i.e. the beginning of the file).
1866    dbg.Verbose(1, "gdcmParser::CheckSwap:", "not a DICOM Version3 file");
1867    rewind(fp);
1868
1869    // Our next best chance would be to be considering a 'clean' ACR/NEMA file.
1870    // By clean we mean that the length of the first tag is written down.
1871    // If this is the case and since the length of the first group HAS to be
1872    // four (bytes), then determining the proper swap code is straightforward.
1873
1874    entCur = deb + 4;
1875    // We assume the array of char we are considering contains the binary
1876    // representation of a 32 bits integer. Hence the following dirty
1877    // trick :
1878    s = *((guint32 *)(entCur));
1879    
1880    switch (s)
1881    {
1882       case 0x00040000 :
1883          sw = 3412;
1884          filetype = ACR;
1885          return;
1886       case 0x04000000 :
1887          sw = 4321;
1888          filetype = ACR;
1889          return;
1890       case 0x00000400 :
1891          sw = 2143;
1892          filetype = ACR;
1893          return;
1894       case 0x00000004 :
1895          sw = 0;
1896          filetype = ACR;
1897          return;
1898       default :
1899          dbg.Verbose(0, "gdcmParser::CheckSwap:",
1900                         "ACR/NEMA unfound swap info (time to raise bets)");
1901    }
1902
1903    // We are out of luck. It is not a DicomV3 nor a 'clean' ACR/NEMA file.
1904    // It is time for despaired wild guesses. So, let's assume this file
1905    // happens to be 'dirty' ACR/NEMA, i.e. the length of the group is
1906    // not present. Then the only info we have is the net2host one.
1907    filetype = Unknown;
1908    if (! net2host )
1909       sw = 0;
1910    else
1911       sw = 4321;
1912    return;
1913 }
1914
1915 /**
1916  * \ingroup gdcmParser
1917  * \brief   
1918  */
1919 void gdcmParser::SwitchSwapToBigEndian(void) 
1920 {
1921    dbg.Verbose(1, "gdcmParser::SwitchSwapToBigEndian",
1922                   "Switching to BigEndian mode.");
1923    if ( sw == 0    ) 
1924    {
1925       sw = 4321;
1926       return;
1927    }
1928    if ( sw == 4321 ) 
1929    {
1930       sw = 0;
1931       return;
1932    }
1933    if ( sw == 3412 ) 
1934    {
1935       sw = 2143;
1936       return;
1937    }
1938    if ( sw == 2143 )
1939       sw = 3412;
1940 }
1941
1942 /**
1943  * \ingroup gdcmParser
1944  * \brief   
1945  * @param NewSize
1946  * @return 
1947  */
1948 void gdcmParser::SetMaxSizeLoadEntry(long NewSize) 
1949 {
1950    if (NewSize < 0)
1951       return;
1952    if ((guint32)NewSize >= (guint32)0xffffffff) 
1953    {
1954       MaxSizeLoadEntry = 0xffffffff;
1955       return;
1956    }
1957    MaxSizeLoadEntry = NewSize;
1958 }
1959
1960 /**
1961  * \ingroup gdcmParser
1962  * \brief   Searches both the public and the shadow dictionary (when they
1963  *          exist) for the presence of the DictEntry with given name.
1964  *          The public dictionary has precedence on the shadow one.
1965  * @param   Name name of the searched DictEntry
1966  * @return  Corresponding DictEntry when it exists, NULL otherwise.
1967  */
1968 gdcmDictEntry *gdcmParser::GetDictEntryByName(std::string Name) 
1969 {
1970    gdcmDictEntry *found = (gdcmDictEntry *)0;
1971    if (!RefPubDict && !RefShaDict) 
1972    {
1973       dbg.Verbose(0, "gdcmParser::GetDictEntry",
1974                      "we SHOULD have a default dictionary");
1975    }
1976    if (RefPubDict) 
1977    {
1978       found = RefPubDict->GetTagByName(Name);
1979       if (found)
1980          return found;
1981    }
1982    if (RefShaDict) 
1983    {
1984       found = RefShaDict->GetTagByName(Name);
1985       if (found)
1986          return found;
1987    }
1988    return found;
1989 }
1990
1991 /**
1992  * \ingroup gdcmParser
1993  * \brief   Searches both the public and the shadow dictionary (when they
1994  *          exist) for the presence of the DictEntry with given
1995  *          group and element. The public dictionary has precedence on the
1996  *          shadow one.
1997  * @param   group   group of the searched DictEntry
1998  * @param   element element of the searched DictEntry
1999  * @return  Corresponding DictEntry when it exists, NULL otherwise.
2000  */
2001 gdcmDictEntry *gdcmParser::GetDictEntryByNumber(guint16 group,guint16 element) 
2002 {
2003    gdcmDictEntry *found = (gdcmDictEntry *)0;
2004    if (!RefPubDict && !RefShaDict) 
2005    {
2006       dbg.Verbose(0, "gdcmParser::GetDictEntry",
2007                      "we SHOULD have a default dictionary");
2008    }
2009    if (RefPubDict) 
2010    {
2011       found = RefPubDict->GetTagByNumber(group, element);
2012       if (found)
2013          return found;
2014    }
2015    if (RefShaDict) 
2016    {
2017       found = RefShaDict->GetTagByNumber(group, element);
2018       if (found)
2019          return found;
2020    }
2021    return found;
2022 }
2023
2024 /**
2025  * \ingroup gdcmParser
2026  * \brief   Read the next tag but WITHOUT loading it's value
2027  * @return  On succes the newly created HeaderEntry, NULL on failure.      
2028  */
2029 gdcmHeaderEntry *gdcmParser::ReadNextHeaderEntry(void) 
2030 {
2031    guint16 g,n;
2032    gdcmHeaderEntry *NewElVal;
2033    
2034    g = ReadInt16();
2035    n = ReadInt16();
2036       
2037    if (errno == 1)
2038       // We reached the EOF (or an error occured) and header parsing
2039       // has to be considered as finished.
2040       return (gdcmHeaderEntry *)0;
2041    
2042    NewElVal = NewHeaderEntryByNumber(g, n);
2043    FindHeaderEntryVR(NewElVal);
2044    FindHeaderEntryLength(NewElVal);
2045         
2046    if (errno == 1) 
2047    {
2048       // Call it quits
2049       return NULL;
2050    }
2051    NewElVal->SetOffset(ftell(fp));  
2052    //if ( (g==0x7fe0) && (n==0x0010) ) 
2053    return NewElVal;
2054 }
2055
2056 /**
2057  * \ingroup gdcmParser
2058  * \brief   Build a new Element Value from all the low level arguments. 
2059  *          Check for existence of dictionary entry, and build
2060  *          a default one when absent.
2061  * @param   Name    Name of the underlying DictEntry
2062  */
2063 gdcmHeaderEntry *gdcmParser::NewHeaderEntryByName(std::string Name) 
2064 {
2065    gdcmDictEntry *NewTag = GetDictEntryByName(Name);
2066    if (!NewTag)
2067       NewTag = Dicts->NewVirtualDictEntry(0xffff, 0xffff, "LO", "Unknown", Name);
2068
2069    gdcmHeaderEntry* NewElVal = new gdcmHeaderEntry(NewTag);
2070    if (!NewElVal) 
2071    {
2072       dbg.Verbose(1, "gdcmParser::ObtainHeaderEntryByName",
2073                   "failed to allocate gdcmHeaderEntry");
2074       return (gdcmHeaderEntry *)0;
2075    }
2076    return NewElVal;
2077 }  
2078
2079 /**
2080  * \ingroup gdcmParser
2081  * \brief   Build a new Element Value from all the low level arguments. 
2082  *          Check for existence of dictionary entry, and build
2083  *          a default one when absent.
2084  * @param   Group group   of the underlying DictEntry
2085  * @param   Elem  element of the underlying DictEntry
2086  */
2087 gdcmHeaderEntry *gdcmParser::NewHeaderEntryByNumber(guint16 Group, guint16 Elem) 
2088 {
2089    // Find out if the tag we encountered is in the dictionaries:
2090    gdcmDictEntry *NewTag = GetDictEntryByNumber(Group, Elem);
2091    if (!NewTag)
2092       NewTag = Dicts->NewVirtualDictEntry(Group, Elem);
2093
2094    gdcmHeaderEntry* NewElVal = new gdcmHeaderEntry(NewTag);
2095    if (!NewElVal) 
2096    {
2097       dbg.Verbose(1, "gdcmParser::NewHeaderEntryByNumber",
2098                   "failed to allocate gdcmHeaderEntry");
2099       return NULL;
2100    }
2101    return NewElVal;
2102 }
2103
2104 /**
2105  * \ingroup gdcmParser
2106  * \brief   Small utility function that creates a new manually crafted
2107  *          (as opposed as read from the file) gdcmHeaderEntry with user
2108  *          specified name and adds it to the public tag hash table.
2109  * \note    A fake TagKey is generated so the PubDict can keep it's coherence.
2110  * @param   NewTagName The name to be given to this new tag.
2111  * @param   VR The Value Representation to be given to this new tag.
2112  * @return  The newly hand crafted Element Value.
2113  */
2114 gdcmHeaderEntry *gdcmParser::NewManualHeaderEntryToPubDict(std::string NewTagName, 
2115                                                            std::string VR) 
2116 {
2117    gdcmHeaderEntry *NewElVal = (gdcmHeaderEntry *)0;
2118    guint32 StuffGroup = 0xffff;   // Group to be stuffed with additional info
2119    guint32 FreeElem = 0;
2120    gdcmDictEntry *NewEntry = (gdcmDictEntry *)0;
2121
2122    FreeElem = GenerateFreeTagKeyInGroup(StuffGroup);
2123    if (FreeElem == UINT32_MAX) 
2124    {
2125       dbg.Verbose(1, "gdcmHeader::NewManualElValToPubDict",
2126                      "Group 0xffff in Public Dict is full");
2127       return NULL;
2128    }
2129
2130    NewEntry = Dicts->NewVirtualDictEntry(StuffGroup, FreeElem,
2131                                 VR, "GDCM", NewTagName);
2132    NewElVal = new gdcmHeaderEntry(NewEntry);
2133    AddHeaderEntry(NewElVal);
2134    return NewElVal;
2135 }
2136
2137 /**
2138  * \ingroup gdcmParser
2139  * \brief   Generate a free TagKey i.e. a TagKey that is not present
2140  *          in the TagHt dictionary.
2141  * @param   group The generated tag must belong to this group.  
2142  * @return  The element of tag with given group which is fee.
2143  */
2144 guint32 gdcmParser::GenerateFreeTagKeyInGroup(guint16 group) 
2145 {
2146    for (guint32 elem = 0; elem < UINT32_MAX; elem++) 
2147    {
2148       TagKey key = gdcmDictEntry::TranslateToKey(group, elem);
2149       if (tagHT.count(key) == 0)
2150          return elem;
2151    }
2152    return UINT32_MAX;
2153 }
2154
2155 //-----------------------------------------------------------------------------