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