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