2 //-----------------------------------------------------------------------------
4 #include "gdcmElValSet.h"
6 #ifdef GDCM_NO_ANSI_STRING_STREAM
8 # define ostringstream ostrstream
13 //-----------------------------------------------------------------------------
14 // Constructor / Destructor
16 * \ingroup gdcmElValSet
19 gdcmElValSet::~gdcmElValSet() {
20 for (TagElValueHT::iterator tag = tagHt.begin(); tag != tagHt.end(); ++tag) {
21 gdcmElValue* EntryToDelete = tag->second;
30 //-----------------------------------------------------------------------------
33 * \ingroup gdcmElValSet
34 * \brief prints the Dicom Elements of the gdcmHeader
35 * using both H table and Chained List
36 * @param os The output stream to be written to.
38 void gdcmElValSet::Print(std::ostream & os) {
41 unsigned short int g, e;
44 gdcmTS * ts = gdcmGlobal::GetTS();
48 s << "------------- using tagHt ---------------------" << std::endl;
51 s << "------------- using tagHt ---------------------" << std::endl;
52 for (TagElValueHT::iterator tag = tagHt.begin();
55 g = tag->second->GetGroup();
56 e = tag->second->GetElement();
57 v = tag->second->GetValue();
58 o = tag->second->GetOffset();
59 d2 = _CreateCleanString(v); // replace non printable characters by '.'
61 s << tag->first << ": ";
62 s << " lgr : " <<tag->second->GetLength();
63 s << ",\t Offset : " << o;
64 s << " x(" << std::hex << o << std::dec << ") ";
65 s << "\t[" << tag->second->GetVR() << "]";
66 s << "\t[" << tag->second->GetName() << "]";
67 s << "\t[" << d2 << "]";
69 // Display the UID value (instead of displaying the rough code)
70 if (g == 0x0002) { // Some more to be displayed ?
71 if ( (e == 0x0010) || (e == 0x0002) )
72 s << " ==>\t[" << ts->GetValue(v) << "]";
75 if ( (e == 0x0016) || (e == 0x1150) )
76 s << " ==>\t[" << ts->GetValue(v) << "]";
84 char greltag[10]; //group element tag
87 s << "------------ using listElem -------------------" << std::endl;
89 for (ListTag::iterator i = listElem.begin();
93 e = (*i)->GetElement();
95 o = (*i)->GetOffset();
96 sprintf(greltag,"%04x|%04x",g,e);
97 d2 = _CreateCleanString(v); // replace non printable characters by '.'
98 s << greltag << ": lgth : ";
99 lgth = (*i)->GetReadLength();
100 if ( lgth == 0xffffffff)
101 s << std::hex << lgth << std::dec ;
104 s << ",\t Offset : " << o;
105 s << " x(" << std::hex << o << std::dec << ") ";
106 s << "\t[" << (*i)->GetVR() << "]";
107 s << "\t[" << (*i)->GetName() << "]";
108 s << "\t[" << d2 << "]";
110 // Display the UID value (instead of displaying the rough code)
111 if (g == 0x0002) { // Any more to be displayed ?
112 if ( (e == 0x0010) || (e == 0x0002) )
113 s << " ==>\t[" << ts->GetValue(v) << "]";
116 if ( (e == 0x0016) || (e == 0x1150) )
117 s << " ==>\t[" << ts->GetValue(v) << "]";
125 //-----------------------------------------------------------------------------
128 * \ingroup gdcmElValSet
129 * \brief add a new Dicom Element pointer to
130 * the H Table and to the chained List
133 void gdcmElValSet::Add(gdcmElValue * newElValue) {
135 // tagHt [newElValue->GetKey()] = newElValue;
137 tagHt.insert( PairHT( newElValue->GetKey(),newElValue) );
139 // WARNING : push_bash in listElem ONLY during ParseHeader
140 // TODO : something to allow further Elements addition
141 // position to be taken care of !
142 listElem.push_back(newElValue);
146 * \ingroup gdcmElValSet
147 * \brief retrieves a Dicom Element (the first one) using (group, element)
148 * \ warning (group, element) IS NOT an identifier inside the Dicom Header
149 * if you think it's NOT UNIQUE, check the count number
150 * and use iterators to retrieve ALL the Dicoms Elements within
151 * a given couple (group, element)
152 * @param group Group number of the searched Dicom Element
153 * @param element Element number of the searched Dicom Element
156 gdcmElValue* gdcmElValSet::GetElementByNumber(guint16 group, guint16 element) {
157 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
158 if ( ! tagHt.count(key))
159 return (gdcmElValue*)0;
160 return tagHt.find(key)->second;
164 * \ingroup gdcmElValSet
165 * \brief Gets the value (string) of the target Dicom Element
166 * @param group Group number of the searched Dicom Element
167 * @param element Element number of the searched Dicom Element
170 std::string gdcmElValSet::GetElValueByNumber(guint16 group, guint16 element) {
171 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
172 if ( ! tagHt.count(key))
174 return tagHt.find(key)->second->GetValue();
178 * \ingroup gdcmElValSet
179 * \brief Sets the value (string) of the target Dicom Element
180 * @param content string value of the Dicom Element
181 * @param group Group number of the searched Dicom Element
182 * @param element Element number of the searched Dicom Element
185 bool gdcmElValSet::SetElValueByNumber(std::string content,
186 guint16 group, guint16 element) {
187 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
188 if ( ! tagHt.count(key))
190 int l = content.length();
191 if(l%2) { // Odd length are padded with a space (020H).
193 content = content + '\0';
196 //tagHt[key]->SetValue(content);
199 TagElValueHT::iterator p2;
200 // DO NOT remove the following lines : they explain the stuff
201 //p= tagHt.equal_range(key); // get a pair of iterators first-last synonym
202 //p2=p.first; // iterator on the first synonym
203 //a=p2->second; // H Table target column (2-nd col)
206 a = ((tagHt.equal_range(key)).first)->second;
208 a-> SetValue(content);
210 //std::string vr = tagHt[key]->GetVR();
211 std::string vr = a->GetVR();
214 if( (vr == "US") || (vr == "SS") )
216 else if( (vr == "UL") || (vr == "SL") )
220 //tagHt[key]->SetLength(lgr);
226 * \ingroup gdcmElValSet
227 * \brief Sets the value length of the Dicom Element
228 * \warning : use with caution !
230 * @param group Group number of the searched Dicom Element
231 * @param element Element number of the searched Dicom Element
234 bool gdcmElValSet::SetElValueLengthByNumber(guint32 length,
235 guint16 group, guint16 element) {
236 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
237 if ( ! tagHt.count(key))
239 if (length%2) length++; // length must be even
240 //tagHt[key]->SetLength(length);
241 ( ((tagHt.equal_range(key)).first)->second )->SetLength(length);
246 * \ingroup gdcmElValSet
247 * \brief Sets a 'non string' value to a given Dicom Element
249 * @param group Group number of the searched Dicom Element
250 * @param element Element number of the searched Dicom Element
253 bool gdcmElValSet::SetVoidAreaByNumber(void * area,
254 guint16 group, guint16 element) {
255 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
256 if ( ! tagHt.count(key))
258 //tagHt[key]->SetVoidArea(area);
259 ( ((tagHt.equal_range(key)).first)->second )->SetVoidArea(area);
264 * \ingroup gdcmElValSet
265 * \brief Generate a free TagKey i.e. a TagKey that is not present
266 * in the TagHt dictionary.
267 * @param group The generated tag must belong to this group.
268 * @return The element of tag with given group which is fee.
270 guint32 gdcmElValSet::GenerateFreeTagKeyInGroup(guint16 group) {
271 for (guint32 elem = 0; elem < UINT32_MAX; elem++) {
272 TagKey key = gdcmDictEntry::TranslateToKey(group, elem);
273 if (tagHt.count(key) == 0)
280 * \ingroup gdcmElValSet
281 * \brief Checks if a given Dicom Element exists
282 * \ within the H table
283 * @param group Group number of the searched Dicom Element
284 * @param element Element number of the searched Dicom Element
285 * @return number of occurences
287 int gdcmElValSet::CheckIfExistByNumber(guint16 group, guint16 element ) {
288 std::string key = gdcmDictEntry::TranslateToKey(group, element );
289 return (tagHt.count(key));
293 // TODO to be re-written using the chained list instead of the H table
294 // so we can remove the GroupHT from the gdcmHeader
297 * \ingroup gdcmElValSet
299 * @param _fp already open file pointer
300 * @param type type of the File to be written
301 * (ACR-NEMA, ExplicitVR, ImplicitVR)
302 * @return always "True" ?!
304 bool gdcmElValSet::Write(FILE * _fp, FileType type) {
307 // Comment pourrait-on savoir si le DcmHeader vient d'un fichier DicomV3 ou non
308 // (FileType est un champ de gdcmHeader ...)
309 // WARNING : Si on veut ecrire du DICOM V3 a partir d'un DcmHeader ACR-NEMA
311 // a moins de se livrer a un tres complique ajout des champs manquants.
312 // faire un CheckAndCorrectHeader (?)
314 if ( (type == ImplicitVR) || (type == ExplicitVR) )
315 UpdateGroupLength(false,type);
317 UpdateGroupLength(true,ACR);
319 WriteElements(type, _fp);
323 //-----------------------------------------------------------------------------
326 //-----------------------------------------------------------------------------
329 * \ingroup gdcmElValSet
330 * \brief Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader
331 * \warning : to be re-written using the chained list instead of the H table.
332 * \todo : to be re-written using the chained list instead of the H table
333 * @param SkipSequence TRUE if we don't want to write Sequences (ACR-NEMA Files)
334 * @param type Type of the File (ExplicitVR,ImplicitVR, ACR, ...)
336 void gdcmElValSet::UpdateGroupLength(bool SkipSequence, FileType type) {
342 std::string str_trash;
345 GroupHT groupHt; // to hold the length of each group
348 // typedef std::map<GroupKey, int> GroupHT;
352 // for each Tag in the DCM Header
354 for (TagElValueHT::iterator tag2 = tagHt.begin();
359 gr = elem->GetGroup();
360 el = elem->GetElement();
363 sprintf(trash, "%04x", gr);
364 key = trash; // generate 'group tag'
366 // if the caller decided not to take SEQUENCEs into account
367 // e.g : he wants to write an ACR-NEMA File
369 if (SkipSequence && vr == "SQ") continue;
371 // Still unsolved problem :
372 // we cannot find the 'Sequence Delimitation Item'
373 // since it's at the end of the Hash Table
376 // pas SEQUENCE en ACR-NEMA
378 // --> la descente a l'interieur' des SQ
379 // devrait etre faite avec une liste chainee, pas avec une HTable...
381 if ( groupHt.count(key) == 0) { // we just read the first elem of a given group
382 if (el == 0x0000) { // the first elem is 0x0000
383 groupHt[key] = 0; // initialize group length
385 groupHt[key] = 2 + 2 + 4 + elem->GetLength(); // non 0x0000 first group elem
387 } else { // any elem but the first
388 if (type == ExplicitVR) {
389 if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
390 groupHt[key] += 4; // explicit VR AND OB, OW, SQ : 4 more bytes
393 groupHt[key] += 2 + 2 + 4 + elem->GetLength();
397 unsigned short int gr_bid;
399 for (GroupHT::iterator g = groupHt.begin(); // for each group we found
402 // FIXME: g++ -Wall -Wstrict-prototypes reports on following line:
403 // warning: unsigned int format, different type arg
404 sscanf(g->first.c_str(),"%x",&gr_bid);
405 tk = g->first + "|0000"; // generate the element full tag
407 if ( tagHt.count(tk) == 0) { // if element 0x0000 not found
408 gdcmDictEntry * tagZ = new gdcmDictEntry(gr_bid, 0x0000, "UL");
409 elemZ = new gdcmElValue(tagZ);
411 Add(elemZ); // create it
413 elemZ=GetElementByNumber(gr_bid, 0x0000);
415 sprintf(trash ,"%d",g->second);
417 elemZ->SetValue(str_trash);
422 * \ingroup gdcmElValSet
423 * \brief writes on disc according to the requested format
424 * \ (ACR-NEMA, ExplicitVR, ImplicitVR) the image
425 * \ warning does NOT add the missing elements in the header :
426 * \ it's up to the user doing it !
427 * \ (function CheckHeaderCoherence to be written)
428 * @param type type of the File to be written
429 * (ACR-NEMA, ExplicitVR, ImplicitVR)
430 * @param _fp already open file pointer
433 void gdcmElValSet::WriteElements(FileType type, FILE * _fp) {
441 std::vector<std::string> tokens;
443 // TODO : use listElem to iterate, not TagHt!
444 // pb : gdcmElValSet.Add does NOT update listElem
445 // find a trick in STL to do it, at low cost !
449 // Tout ceci ne marche QUE parce qu'on est sur un proc Little Endian
450 // restent a tester les echecs en ecriture (apres chaque fwrite)
452 for (TagElValueHT::iterator tag2=tagHt.begin();
456 gr = tag2->second->GetGroup();
457 el = tag2->second->GetElement();
458 lgr = tag2->second->GetLength();
459 val = tag2->second->GetValue().c_str();
460 vr = tag2->second->GetVR();
463 if (gr < 0x0008) continue; // ignore pure DICOM V3 groups
464 if (gr %2) continue; // ignore shadow groups
465 if (vr == "SQ" ) continue; // ignore Sequences
466 // TODO : find a trick to *skip* the SeQuences !
467 // Not only ignore the SQ element
468 if (gr == 0xfffe ) continue; // ignore delimiters
471 fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp); //group
472 fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp); //element
474 if ( (type == ExplicitVR) && (gr <= 0x0002) ) {
476 guint16 z=0, shortLgr;
477 fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp);
479 if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
480 fwrite ( &z, (size_t)2 ,(size_t)1 ,_fp);
481 fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
485 fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp);
487 } else { // IMPLICIT VR
488 fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
491 if (vr == "US" || vr == "SS") {
492 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
493 Tokenize (tag2->second->GetValue(), tokens, "\\");
494 for (unsigned int i=0; i<tokens.size();i++) {
495 val_uint16 = atoi(tokens[i].c_str());
497 fwrite ( ptr,(size_t)2 ,(size_t)1 ,_fp);
502 if (vr == "UL" || vr == "SL") {
503 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
504 Tokenize (tag2->second->GetValue(), tokens, "\\");
505 for (unsigned int i=0; i<tokens.size();i++) {
506 val_uint32 = atoi(tokens[i].c_str());
508 fwrite ( ptr,(size_t)4 ,(size_t)1 ,_fp);
513 // Pixels are never loaded in the element !
514 if ((gr == 0x7fe0) && (el == 0x0010) ) break;
516 fwrite ( val,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
520 //-----------------------------------------------------------------------------