]> Creatis software - gdcm.git/blob - src/gdcmElValSet.cxx
* src/gdcmDict.cxx, gdcmElValSet.cxx : bug fix under windows for prints.
[gdcm.git] / src / gdcmElValSet.cxx
1 // gdcmElValSet.cxx
2
3 #include "gdcmUtil.h"
4 #include "gdcmElValSet.h"
5 #include "gdcmTS.h"
6 #ifdef GDCM_NO_ANSI_STRING_STREAM
7 #  include <strstream>
8 #  define  ostringstream ostrstream
9 # else
10 #  include <sstream>
11 #endif
12
13
14 gdcmElValSet::~gdcmElValSet() {
15    for (TagElValueHT::iterator tag = tagHt.begin(); tag != tagHt.end(); ++tag) {
16       gdcmElValue* EntryToDelete = tag->second;
17       if ( EntryToDelete )
18          delete EntryToDelete;
19       tag->second=NULL;
20    }
21
22    tagHt.clear();
23    // Since Add() adds symetrical in both tagHt and NameHt we can
24    // assume all the pointed gdcmElValues are already cleaned-up when
25    // we cleaned tagHt.
26    NameHt.clear();
27 }
28
29 /**
30  * \ingroup gdcmElValSet
31  * \brief   
32  * @param   newElValue
33  * @return  
34  */
35 void gdcmElValSet::Add(gdcmElValue * newElValue) {
36         tagHt [newElValue->GetKey()]  = newElValue;
37         NameHt[newElValue->GetName()] = newElValue;
38         
39 // WARNING : push_bash in listElem ONLY during ParseHeader
40 // TODO : something to allow further Elements addition 
41 // position to be taken care of !       
42         listElem.push_back(newElValue); 
43 }
44
45 /**
46  * \ingroup gdcmElValSet
47  * \brief   Checks if a given Dicom element exists
48  * \        within a ElValSet
49  * @param   Group
50  * @param   Elem
51  * @return  
52  */
53 int gdcmElValSet::CheckIfExistByNumber(guint16 Group, guint16 Elem ) {
54         std::string key = TranslateToKey(Group, Elem );
55         return (tagHt.count(key));
56 }
57
58 /**
59  * \ingroup gdcmElValSet
60  * \brief   
61  */
62 void gdcmElValSet::Print(std::ostream & os) {
63
64    size_t o;
65    unsigned short int g, e;
66    TSKey v;
67    std::string d2;
68    gdcmTS * ts = gdcmGlobal::GetTS();
69    std::ostringstream s;
70    
71    for (TagElValueHT::iterator tag = tagHt.begin();
72            tag != tagHt.end();
73            ++tag){
74       g = tag->second->GetGroup();
75       e = tag->second->GetElement();
76       v = tag->second->GetValue();
77       o = tag->second->GetOffset();
78       d2 = _CreateCleanString(v);  // replace non printable characters by '.'
79
80       s << tag->first << ": ";
81       s << " lgr : " <<tag->second->GetLength();
82       s << ",\t Offset : " << o;
83       s << " x(" << std::hex << o << std::dec << ") ";
84       s << "\t[" << tag->second->GetVR()    << "]";
85       s << "\t[" << tag->second->GetName()  << "]";       
86       s << "\t[" << d2 << "]";
87
88       // Display the UID value (instead of displaying the rough code)  
89       if (g == 0x0002) {  // Some more to be displayed ?
90          if ( (e == 0x0010) || (e == 0x0002) )     
91             s << "  ==>\t[" << ts->GetValue(v) << "]";   
92       } else {
93          if (g == 0x0008) {
94             if ( (e == 0x0016) || (e == 0x1150)  )         
95                s << "  ==>\t[" << ts->GetValue(v) << "]"; 
96          }
97       }              
98       s << std::endl;
99    }
100
101    
102    guint32 lgth;
103    char greltag[10];  //group element tag
104    
105    for (ListTag::iterator i = listElem.begin();  
106            i != listElem.end();
107            ++i){
108       g = (*i)->GetGroup();
109       e = (*i)->GetElement();
110       v = (*i)->GetValue();
111       o = (*i)->GetOffset();
112       sprintf(greltag,"%04x|%04x",g,e);           
113       d2 = _CreateCleanString(v);  // replace non printable characters by '.'
114       s << greltag << ": lgth : ";
115       lgth = (*i)->GetReadLength();
116       if ( lgth == 0xffffffff) 
117          s << std::hex << lgth << std::dec ;
118       else
119          s << lgth;
120       s << ",\t Offset : " << o;
121       s << " x(" << std::hex << o << std::dec << ") ";
122       s << "\t[" << (*i)->GetVR()    << "]";
123       s << "\t[" << (*i)->GetName()  << "]";       
124       s << "\t[" << d2 << "]";
125       
126       // Display the UID value (instead of displaying the rough code)  
127       if (g == 0x0002) {  // Any more to be displayed ?
128          if ( (e == 0x0010) || (e == 0x0002) )     
129             s << "  ==>\t[" << ts->GetValue(v) << "]";   
130       } else {
131          if (g == 0x0008) {
132             if ( (e == 0x0016) || (e == 0x1150)  )         
133                s << "  ==>\t[" << ts->GetValue(v) << "]"; 
134          }
135       }              
136       s << std::endl;
137    }
138    os<<s.str();
139
140
141 /**
142  * \ingroup gdcmElValSet
143  * \brief   
144  */
145 void gdcmElValSet::PrintByName(std::ostream & os) {
146    for (TagElValueNameHT::iterator tag = NameHt.begin();
147           tag != NameHt.end();
148           ++tag){
149       os << tag->first << ": ";
150       os << "[" << tag->second->GetValue() << "]";
151       os << "[" << tag->second->GetKey()   << "]";
152       os << "[" << tag->second->GetVR()    << "]" << std::endl;
153    }
154 }
155
156 /**
157  * \ingroup gdcmElValSet
158  * \brief   
159  * @param   group 
160  * @param   element 
161  * @return  
162  */
163 gdcmElValue* gdcmElValSet::GetElementByNumber(guint16 group, guint16 element) {
164    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
165    if ( ! tagHt.count(key))
166       return (gdcmElValue*)0;
167    return tagHt.find(key)->second;
168 }
169
170 /**
171  * \ingroup gdcmElValSet
172  * \brief   
173  * @return  
174  */
175 gdcmElValue* gdcmElValSet::GetElementByName(std::string TagName) {
176    if ( ! NameHt.count(TagName))
177       return (gdcmElValue*)0;
178    return NameHt.find(TagName)->second;
179 }
180
181 /**
182  * \ingroup gdcmElValSet
183  * \brief   
184  * @param   group 
185  * @param   element 
186  * @return  
187  */
188 std::string gdcmElValSet::GetElValueByNumber(guint16 group, guint16 element) {
189    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
190    if ( ! tagHt.count(key))
191       return GDCM_UNFOUND;
192    return tagHt.find(key)->second->GetValue();
193 }
194
195 /**
196  * \ingroup gdcmElValSet
197  * \brief   
198  * @return  
199  */
200 std::string gdcmElValSet::GetElValueByName(std::string TagName) {
201    if ( ! NameHt.count(TagName))
202       return GDCM_UNFOUND;
203    return NameHt.find(TagName)->second->GetValue();
204 }
205
206 /**
207  * \ingroup gdcmElValSet
208  * \brief   
209  * @param   content
210  * @param   group 
211  * @param   element 
212  * @return  
213  */
214 int gdcmElValSet::SetElValueByNumber(std::string content,
215                                      guint16 group, guint16 element) {
216    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
217    if ( ! tagHt.count(key))
218       return 0;
219    int l = content.length();
220    if(l%2) {  // Odd length are padded with a space (020H).
221       l++;
222       content = content + '\0';
223    }
224    tagHt[key]->SetValue(content);
225
226    std::string vr = tagHt[key]->GetVR();
227    guint32 lgr;
228
229    if( (vr == "US") || (vr == "SS") ) 
230       lgr = 2;
231    else if( (vr == "UL") || (vr == "SL") )
232       lgr = 4;
233    else
234       lgr = l;     
235    tagHt[key]->SetLength(lgr); 
236    return 1;
237 }
238
239 /**
240  * \ingroup gdcmElValSet
241  * \brief   
242  * @param   content
243  * @param   TagName
244  * @return  
245  */
246 int gdcmElValSet::SetElValueByName(std::string content, std::string TagName) {
247    if ( ! NameHt.count(TagName))
248       return 0;
249    int l = content.length();
250    if(l%2) {  // Odd length are padded with a space (020H).
251       l++;
252       // Well. I know that '/0' is NOT a space
253       // but it doesn't work with a space. 
254       // Use hexedit and see 0002|0010 value (Transfer Syntax UID)
255       content = content + '\0';
256    }
257    NameHt[TagName]->SetValue(content);
258
259    std::string vr = NameHt[TagName]->GetVR();
260    guint32 lgr;
261
262    if( (vr == "US") || (vr == "SS") ) 
263       lgr = 2;
264    else if( (vr == "UL") || (vr == "SL") )
265       lgr = 4;     
266    else 
267       lgr = content.length();
268            
269 // TODO : WARNING: le cas de l'element des pixels (7fe0,0010) n'est pas traite
270 // par SetElValueByName
271 // il faudra utiliser SetElValueByNumber
272            
273    NameHt[TagName]->SetLength(lgr);
274    return 1;            
275 }
276
277 /**
278  * \ingroup gdcmElValSet
279  * \brief   Generate a free TagKey i.e. a TagKey that is not present
280  *          in the TagHt dictionary.
281  * @param   group The generated tag must belong to this group.  
282  * @return  The element of tag with given group which is fee.
283  */
284 guint32 gdcmElValSet::GenerateFreeTagKeyInGroup(guint16 group) {
285    for (guint32 elem = 0; elem < UINT32_MAX; elem++) {
286       TagKey key = gdcmDictEntry::TranslateToKey(group, elem);
287       if (tagHt.count(key) == 0)
288          return elem;
289    }
290    return UINT32_MAX;
291 }
292
293 /**
294  * \ingroup gdcmElValSet
295  * \brief   
296  * @param   area
297  * @param   group 
298  * @param   element 
299  * @return  
300  */
301 int gdcmElValSet::SetVoidAreaByNumber(void * area,
302                                       guint16 group, guint16 element) {
303    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
304    if ( ! tagHt.count(key))
305       return 0;
306    tagHt[key]->SetVoidArea(area);        
307    return 1 ;           
308 }
309
310 /**
311  * \ingroup gdcmElValSet
312  * \brief   
313  * @param   length
314  * @param   group 
315  * @param   element 
316  * @return  int acts as a boolean
317  */
318 int gdcmElValSet::SetElValueLengthByNumber(guint32 length,
319                                            guint16 group, guint16 element) {
320    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
321    if ( ! tagHt.count(key))
322       return 0;
323    if (length%2) length++; // length must be even
324    tagHt[key]->SetLength(length);        
325    return 1 ;           
326 }
327 /**
328  * \ingroup gdcmElValSet
329  * \brief   
330  * @param   length
331  * @param   TagName
332  * @return  
333  */
334 int gdcmElValSet::SetElValueLengthByName(guint32 length, std::string TagName) {
335    if ( ! NameHt.count(TagName))
336       return 0;
337    if (length%2) length++; // length must be even
338    NameHt.find(TagName)->second->SetLength(length);      
339    return 1 ;           
340 }
341
342 /**
343  * \ingroup gdcmElValSet
344  * \brief   Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader
345  * @param   SkipSequence TRUE if we don't want to write Sequences (ACR-NEMA Files)
346  * @param   type
347  */
348 void gdcmElValSet::UpdateGroupLength(bool SkipSequence, FileType type) {
349    guint16 gr, el;
350    std::string vr;
351    
352    gdcmElValue *elem;
353    char trash[10];
354    std::string str_trash;
355    
356    GroupKey key;
357    GroupHT groupHt;  // to hold the length of each group
358    TagKey tk;
359    // remember :
360    // typedef std::map<GroupKey, int> GroupHT;
361    
362    gdcmElValue *elemZ;
363   
364    // for each Tag in the DCM Header
365    
366    for (TagElValueHT::iterator tag2 = tagHt.begin(); 
367         tag2 != tagHt.end();
368         ++tag2){
369
370       elem  = tag2->second;
371       gr = elem->GetGroup();
372       el = elem->GetElement();
373       vr = elem->GetVR(); 
374                  
375       sprintf(trash, "%04x", gr);
376       key = trash;              // generate 'group tag'
377       
378       // if the caller decided not to take SEQUENCEs into account 
379       // e.g : he wants to write an ACR-NEMA File 
380                 
381       if (SkipSequence && vr == "SQ") continue;
382       
383          // Still unsolved problem :
384          // we cannot find the 'Sequence Delimitation Item'
385          // since it's at the end of the Hash Table
386          // (fffe,e0dd) 
387              
388          // pas SEQUENCE en ACR-NEMA
389          // WARNING : 
390          // --> la descente a l'interieur' des SQ 
391          // devrait etre faite avec une liste chainee, pas avec une HTable...
392             
393       if ( groupHt.count(key) == 0) { // we just read the first elem of a given group
394          if (el == 0x0000) {          // the first elem is 0x0000
395             groupHt[key] = 0;         // initialize group length 
396          } else {
397             groupHt[key] = 2 + 2 + 4 + elem->GetLength(); // non 0x0000 first group elem
398          } 
399       } else {   // any elem but the first    
400          if (type == ExplicitVR) {
401             if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
402                groupHt[key] +=  4; // explicit VR AND OB, OW, SQ : 4 more bytes
403             }
404          }
405          groupHt[key] += 2 + 2 + 4 + elem->GetLength(); 
406       } 
407    }
408
409    unsigned short int gr_bid;
410   
411    for (GroupHT::iterator g = groupHt.begin(); // for each group we found
412         g != groupHt.end();
413         ++g){ 
414       // FIXME: g++ -Wall -Wstrict-prototypes reports on following line:
415       //        warning: unsigned int format, different type arg
416       sscanf(g->first.c_str(),"%x",&gr_bid);
417       tk = g->first + "|0000";                  // generate the element full tag
418                      
419       if ( tagHt.count(tk) == 0) {              // if element 0x0000 not found
420          gdcmDictEntry * tagZ = new gdcmDictEntry(gr_bid, 0x0000, "UL");       
421          elemZ = new gdcmElValue(tagZ);
422          elemZ->SetLength(4);
423          Add(elemZ);                            // create it
424       } else {
425          elemZ=GetElementByNumber(gr_bid, 0x0000);
426       }     
427       sprintf(trash ,"%d",g->second);
428       str_trash=trash;
429       elemZ->SetValue(str_trash);
430    }   
431 }
432
433 /**
434  * \ingroup gdcmElValSet
435  * \brief   writes on disc according to the requested format
436  * \        (ACR-NEMA, DICOM, RAW) the image
437  * \ warning does NOT add the missing elements in the header :
438  * \         it's up to the user doing it !
439  * \         (function CheckHeaderCoherence to be written)
440  * @param   type type of the File to be written 
441  *          (ACR-NEMA, DICOM, RAW)
442  * @param   _fp already open file pointer
443  * @return  
444  */
445 void gdcmElValSet::WriteElements(FileType type, FILE * _fp) {
446    guint16 gr, el;
447    guint32 lgr;
448    const char * val;
449    std::string vr;
450    guint32 val_uint32;
451    guint16 val_uint16;
452    
453    std::vector<std::string> tokens;
454    
455    // TODO : use listElem to iterate, not TagHt!
456    //        pb : gdcmElValSet.Add does NOT update listElem
457    //        find a trick in STL to do it, at low cost !
458
459    void *ptr;
460
461    // Tout ceci ne marche QUE parce qu'on est sur un proc Little Endian 
462    // restent a tester les echecs en ecriture (apres chaque fwrite)
463
464    for (TagElValueHT::iterator tag2=tagHt.begin();
465         tag2 != tagHt.end();
466         ++tag2){
467
468       gr =  tag2->second->GetGroup();
469       el =  tag2->second->GetElement();
470       lgr = tag2->second->GetLength();
471       val = tag2->second->GetValue().c_str();
472       vr =  tag2->second->GetVR();
473       
474       if ( type == ACR ) { 
475          if (gr < 0x0008)   continue; // ignore pure DICOM V3 groups
476          if (gr %2)         continue; // ignore shadow groups
477          if (vr == "SQ" )   continue; // ignore Sequences
478          if (gr == 0xfffe ) continue; // ignore delimiters
479       } 
480
481       fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp);  //group
482       fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp);  //element
483
484       if ( (type == ExplicitVR) && (gr <= 0x0002) ) {
485          // EXPLICIT VR
486          guint16 z=0, shortLgr;
487          fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp);
488
489          if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
490             fwrite ( &z,  (size_t)2 ,(size_t)1 ,_fp);
491             fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
492
493          } else {
494             shortLgr=lgr;
495             fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp);
496          }
497       } else { // IMPLICIT VR
498          fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
499       }
500
501       if (vr == "US" || vr == "SS") {
502          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
503          Tokenize (tag2->second->GetValue(), tokens, "\\");
504          for (unsigned int i=0; i<tokens.size();i++) {
505             val_uint16 = atoi(tokens[i].c_str());
506             ptr = &val_uint16;
507             fwrite ( ptr,(size_t)2 ,(size_t)1 ,_fp);
508          }
509          tokens.clear();
510          continue;
511       }
512       if (vr == "UL" || vr == "SL") {
513          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
514          Tokenize (tag2->second->GetValue(), tokens, "\\");
515          for (unsigned int i=0; i<tokens.size();i++) {
516             val_uint32 = atoi(tokens[i].c_str());
517             ptr = &val_uint32;
518             fwrite ( ptr,(size_t)4 ,(size_t)1 ,_fp);
519          }
520          tokens.clear();
521          continue;
522       }     
523       // Pixels are never loaded in the element !
524       if ((gr == 0x7fe0) && (el == 0x0010) ) break;
525
526       fwrite ( val,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
527    }
528 }
529
530 /**
531  * \ingroup gdcmElValSet
532  * \brief   
533  * @param   _fp
534  * @param   type
535  * @return  
536  */
537 int gdcmElValSet::Write(FILE * _fp, FileType type) {
538
539         // Question :
540         // Comment pourrait-on savoir si le DcmHeader vient d'un fichier DicomV3 ou non
541         // (FileType est un champ de gdcmHeader ...)
542         // WARNING : Si on veut ecrire du DICOM V3 a partir d'un DcmHeader ACR-NEMA
543         // no way 
544         // a moins de se livrer a un tres complique ajout des champs manquants.
545         // faire un CheckAndCorrectHeader (?)
546
547    if ( (type == ImplicitVR) || (type == ExplicitVR) )
548       UpdateGroupLength(false,type);
549    if ( type == ACR)
550       UpdateGroupLength(true,ACR);
551
552    WriteElements(type, _fp);
553    return(1);
554 }