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