1 // gdcmHeaderEntrySet.cxx
2 //-----------------------------------------------------------------------------
4 #include "gdcmHeaderEntrySet.h"
6 #ifdef GDCM_NO_ANSI_STRING_STREAM
8 # define ostringstream ostrstream
13 #include <iomanip> // for std::ios::left, ...
15 //-----------------------------------------------------------------------------
16 // Constructor / Destructor
18 * \ingroup gdcmHeaderEntrySet
21 gdcmHeaderEntrySet::~gdcmHeaderEntrySet() {
22 for (TagHeaderEntryHT::iterator tag = tagHT.begin(); tag != tagHT.end(); ++tag) {
23 gdcmHeaderEntry* EntryToDelete = tag->second;
31 //-----------------------------------------------------------------------------
34 * \ingroup gdcmHeaderEntrySet
35 * \brief prints the Dicom Elements of the gdcmHeader
36 * using both H table and Chained List
37 * @param os The output stream to be written to.
39 void gdcmHeaderEntrySet::Print(std::ostream & os) {
42 unsigned short int g, e;
45 gdcmTS * ts = gdcmGlobal::GetTS();
49 // DO NOT remove this code right now.
52 s << "------------- using tagHT ---------------------" << std::endl;
53 for (TagHeaderEntryHT::iterator tag = tagHT.begin();
56 g = tag->second->GetGroup();
57 e = tag->second->GetElement();
58 v = tag->second->GetValue();
59 o = tag->second->GetOffset();
60 d2 = _CreateCleanString(v); // replace non printable characters by '.'
62 s << tag->first << ": ";
63 s << " lgr : " <<tag->second->GetLength();
64 s << ",\t Offset : " << o;
65 s << " x(" << std::hex << o << std::dec << ") ";
66 s << "\t[" << tag->second->GetVR() << "]";
67 s << "\t[" << tag->second->GetName() << "]";
68 s << "\t[" << d2 << "]";
70 // Display the UID value (instead of displaying the rough code)
71 if (g == 0x0002) { // Some more to be displayed ?
72 if ( (e == 0x0010) || (e == 0x0002) )
73 s << " ==>\t[" << ts->GetValue(v) << "]";
76 if ( (e == 0x0016) || (e == 0x1150) )
77 s << " ==>\t[" << ts->GetValue(v) << "]";
85 char greltag[10]; //group element tag
87 s << "------------ using listEntries ----------------" << std::endl;
90 for (ListTag::iterator i = listEntries.begin();
91 i != listEntries.end();
94 e = (*i)->GetElement();
96 o = (*i)->GetOffset();
97 sprintf(greltag,"%04x|%04x",g,e);
98 d2 = _CreateCleanString(v); // replace non printable characters by '.'
99 s << greltag << " lg : ";
100 //lgth = (*i)->GetLength();
101 lgth = (*i)->GetReadLength();
102 if (lgth == 0xffffffff) {
103 sprintf(st,"x(%ff)");
104 s.setf(std::ios::left);
105 s << std::setw(10-strlen(st)) << " ";
107 s.setf(std::ios::left);
108 s << std::setw(8) << "-1";
110 sprintf(st,"x(%x)",lgth);
111 s.setf(std::ios::left);
112 s << std::setw(10-strlen(st)) << " ";
114 s.setf(std::ios::left);
115 s << std::setw(8) << lgth;
118 sprintf(st,"x(%x)",o);
119 s << std::setw(10-strlen(st)) << " ";
121 s << std::setw(8) << o;
122 s << "[" << (*i)->GetVR() << "] ";
123 s.setf(std::ios::left);
124 s << std::setw(66-(*i)->GetName().length()) << " ";
125 s << "[" << (*i)->GetName()<< "] ";
126 s << "[" << d2 << "]";
127 // Display the UID value (instead of displaying the rough code)
128 if (g == 0x0002) { // Any more to be displayed ?
129 if ( (e == 0x0010) || (e == 0x0002) )
130 s << " ==>\t[" << ts->GetValue(v) << "]";
133 if ( (e == 0x0016) || (e == 0x1150) )
134 s << " ==>\t[" << ts->GetValue(v) << "]";
137 if (e == 0x0000) { // elem 0x0000 --> group length
138 if (v == "4294967295") // to avoid troubles in convertion
139 sprintf (st," x(ffffffff)");
141 sprintf(st," x(%08x)",atoi(v.c_str()));
149 //-----------------------------------------------------------------------------
152 * \ingroup gdcmHeaderEntrySet
153 * \brief add a new Dicom Element pointer to
154 * the H Table and to the chained List
155 * \warning push_bash in listEntries ONLY during ParseHeader
156 * \todo something to allow further Elements addition,
157 * \ when position to be taken care of
158 * @param newHeaderEntry
160 void gdcmHeaderEntrySet::Add(gdcmHeaderEntry * newHeaderEntry) {
162 // tagHT [newHeaderEntry->GetKey()] = newHeaderEntry;
163 tagHT.insert( PairHT( newHeaderEntry->GetKey(),newHeaderEntry) );
164 listEntries.push_back(newHeaderEntry);
168 * \ingroup gdcmHeaderEntrySet
169 * \brief retrieves a Dicom Element (the first one) using (group, element)
170 * \ warning (group, element) IS NOT an identifier inside the Dicom Header
171 * if you think it's NOT UNIQUE, check the count number
172 * and use iterators to retrieve ALL the Dicoms Elements within
173 * a given couple (group, element)
174 * @param group Group number of the searched Dicom Element
175 * @param element Element number of the searched Dicom Element
178 gdcmHeaderEntry* gdcmHeaderEntrySet::GetHeaderEntryByNumber(guint16 group, guint16 element) {
179 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
180 if ( ! tagHT.count(key))
182 return tagHT.find(key)->second;
186 * \ingroup gdcmHeaderEntrySet
187 * \brief Gets the value (string) of the target Dicom Element
188 * @param group Group number of the searched Dicom Element
189 * @param element Element number of the searched Dicom Element
192 std::string gdcmHeaderEntrySet::GetEntryByNumber(guint16 group, guint16 element) {
193 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
194 if ( ! tagHT.count(key))
196 return tagHT.find(key)->second->GetValue();
200 * \ingroup gdcmHeaderEntrySet
201 * \brief Sets the value (string) of the target Dicom Element
202 * @param content string value of the Dicom Element
203 * @param group Group number of the searched Dicom Element
204 * @param element Element number of the searched Dicom Element
207 bool gdcmHeaderEntrySet::SetEntryByNumber(std::string content,
208 guint16 group, guint16 element) {
209 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
210 if ( ! tagHT.count(key))
212 int l = content.length();
213 if(l%2) { // Odd length are padded with a space (020H).
215 content = content + '\0';
218 //tagHT[key]->SetValue(content);
221 TagHeaderEntryHT::iterator p2;
222 // DO NOT remove the following lines : they explain the stuff
223 //p= tagHT.equal_range(key); // get a pair of iterators first-last synonym
224 //p2=p.first; // iterator on the first synonym
225 //a=p2->second; // H Table target column (2-nd col)
228 a = ((tagHT.equal_range(key)).first)->second;
230 a-> SetValue(content);
232 //std::string vr = tagHT[key]->GetVR();
233 std::string vr = a->GetVR();
236 if( (vr == "US") || (vr == "SS") )
238 else if( (vr == "UL") || (vr == "SL") )
242 //tagHT[key]->SetLength(lgr);
248 * \ingroup gdcmHeaderEntrySet
249 * \brief Sets the value length of the Dicom Element
250 * \warning : use with caution !
252 * @param group Group number of the searched Dicom Element
253 * @param element Element number of the searched Dicom Element
256 bool gdcmHeaderEntrySet::SetEntryLengthByNumber(guint32 length,
257 guint16 group, guint16 element) {
258 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
259 if ( ! tagHT.count(key))
261 if (length%2) length++; // length must be even
262 //tagHT[key]->SetLength(length);
263 ( ((tagHT.equal_range(key)).first)->second )->SetLength(length);
268 * \ingroup gdcmHeaderEntrySet
269 * \brief Sets a 'non string' value to a given Dicom Element
271 * @param group Group number of the searched Dicom Element
272 * @param element Element number of the searched Dicom Element
275 bool gdcmHeaderEntrySet::SetVoidAreaByNumber(void * area,
276 guint16 group, guint16 element) {
277 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
278 if ( ! tagHT.count(key))
280 //tagHT[key]->SetVoidArea(area);
281 ( ((tagHT.equal_range(key)).first)->second )->SetVoidArea(area);
286 * \ingroup gdcmHeaderEntrySet
287 * \brief Generate a free TagKey i.e. a TagKey that is not present
288 * in the TagHt dictionary.
289 * @param group The generated tag must belong to this group.
290 * @return The element of tag with given group which is fee.
292 guint32 gdcmHeaderEntrySet::GenerateFreeTagKeyInGroup(guint16 group) {
293 for (guint32 elem = 0; elem < UINT32_MAX; elem++) {
294 TagKey key = gdcmDictEntry::TranslateToKey(group, elem);
295 if (tagHT.count(key) == 0)
302 * \ingroup gdcmHeaderEntrySet
303 * \brief Checks if a given Dicom Element exists
304 * \ within the H table
305 * @param group Group number of the searched Dicom Element
306 * @param element Element number of the searched Dicom Element
307 * @return number of occurences
309 int gdcmHeaderEntrySet::CheckIfExistByNumber(guint16 group, guint16 element ) {
310 std::string key = gdcmDictEntry::TranslateToKey(group, element );
311 return (tagHT.count(key));
315 // TODO to be re-written using the chained list instead of the H table
316 // so we can remove the GroupHT from the gdcmHeader
319 * \ingroup gdcmHeaderEntrySet
321 * @param _fp already open file pointer
322 * @param type type of the File to be written
323 * (ACR-NEMA, ExplicitVR, ImplicitVR)
324 * @return always "True" ?!
326 bool gdcmHeaderEntrySet::Write(FILE * _fp, FileType type) {
329 // Comment pourrait-on savoir si le DcmHeader vient d'un fichier DicomV3 ou non
330 // (FileType est un champ de gdcmHeader ...)
331 // WARNING : Si on veut ecrire du DICOM V3 a partir d'un DcmHeader ACR-NEMA
333 // a moins de se livrer a un tres complique ajout des champs manquants.
334 // faire un CheckAndCorrectHeader (?)
336 if ( (type == ImplicitVR) || (type == ExplicitVR) )
337 UpdateGroupLength(false,type);
339 UpdateGroupLength(true,ACR);
341 WriteEntries(type, _fp);
345 //-----------------------------------------------------------------------------
348 //-----------------------------------------------------------------------------
351 * \ingroup gdcmHeaderEntrySet
352 * \brief Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader
353 * \warning : to be re-written using the chained list instead of the H table.
354 * \warning : DO NOT use (doesn't work any longer because of the multimap)
355 * \todo : to be re-written using the chained list instead of the H table
356 * @param SkipSequence TRUE if we don't want to write Sequences (ACR-NEMA Files)
357 * @param type Type of the File (ExplicitVR,ImplicitVR, ACR, ...)
359 void gdcmHeaderEntrySet::UpdateGroupLength(bool SkipSequence, FileType type) {
363 gdcmHeaderEntry *elem;
365 std::string str_trash;
368 GroupHT groupHt; // to hold the length of each group
371 // typedef std::map<GroupKey, int> GroupHT;
373 gdcmHeaderEntry *elemZ;
375 // for each Tag in the DCM Header
377 for (TagHeaderEntryHT::iterator tag2 = tagHT.begin();
382 gr = elem->GetGroup();
383 el = elem->GetElement();
386 sprintf(trash, "%04x", gr);
387 key = trash; // generate 'group tag'
389 // if the caller decided not to take SEQUENCEs into account
390 // e.g : he wants to write an ACR-NEMA File
392 if (SkipSequence && vr == "SQ") continue;
394 // Still unsolved problem :
395 // we cannot find the 'Sequence Delimitation Item'
396 // since it's at the end of the Hash Table
399 // pas SEQUENCE en ACR-NEMA
401 // --> la descente a l'interieur' des SQ
402 // devrait etre faite avec une liste chainee, pas avec une HTable...
404 if ( groupHt.count(key) == 0) { // we just read the first elem of a given group
405 if (el == 0x0000) { // the first elem is 0x0000
406 groupHt[key] = 0; // initialize group length
408 groupHt[key] = 2 + 2 + 4 + elem->GetLength(); // non 0x0000 first group elem
410 } else { // any elem but the first
411 if (type == ExplicitVR) {
412 if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
413 groupHt[key] += 4; // explicit VR AND OB, OW, SQ : 4 more bytes
416 groupHt[key] += 2 + 2 + 4 + elem->GetLength();
420 unsigned short int gr_bid;
422 for (GroupHT::iterator g = groupHt.begin(); // for each group we found
425 // FIXME: g++ -Wall -Wstrict-prototypes reports on following line:
426 // warning: unsigned int format, different type arg
427 sscanf(g->first.c_str(),"%x",&gr_bid);
428 tk = g->first + "|0000"; // generate the element full tag
430 if ( tagHT.count(tk) == 0) { // if element 0x0000 not found
431 gdcmDictEntry * tagZ = new gdcmDictEntry(gr_bid, 0x0000, "UL");
432 elemZ = new gdcmHeaderEntry(tagZ);
434 Add(elemZ); // create it
436 elemZ=GetHeaderEntryByNumber(gr_bid, 0x0000);
438 sprintf(trash ,"%d",g->second);
440 elemZ->SetValue(str_trash);
445 * \ingroup gdcmHeaderEntrySet
446 * \brief writes on disc according to the requested format
447 * \ (ACR-NEMA, ExplicitVR, ImplicitVR) the image
448 * \ warning does NOT add the missing elements in the header :
449 * \ it's up to the user doing it !
450 * \ (function CheckHeaderCoherence to be written)
451 * @param type type of the File to be written
452 * (ACR-NEMA, ExplicitVR, ImplicitVR)
453 * @param _fp already open file pointer
455 void gdcmHeaderEntrySet::WriteEntries(FileType type, FILE * _fp) {
463 std::vector<std::string> tokens;
465 // uses now listEntries to iterate, not TagHt!
467 // pb : gdcmHeaderEntrySet.Add does NOT update listEntries
468 // TODO : find a trick (in STL?) to do it, at low cost !
472 // TODO (?) tester les echecs en ecriture (apres chaque fwrite)
474 for (ListTag::iterator tag2=listEntries.begin();
475 tag2 != listEntries.end();
478 gr = (*tag2)->GetGroup();
479 el = (*tag2)->GetElement();
480 lgr = (*tag2)->GetLength();
481 val = (*tag2)->GetValue().c_str();
482 vr = (*tag2)->GetVR();
485 if (gr < 0x0008) continue; // ignore pure DICOM V3 groups
486 if (gr %2) continue; // ignore shadow groups
487 if (vr == "SQ" ) continue; // ignore Sequences
488 // TODO : find a trick to *skip* the SeQuences !
489 // Not only ignore the SQ element
490 if (gr == 0xfffe ) continue; // ignore delimiters
493 fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp); //group
494 fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp); //element
496 if ( (type == ExplicitVR) && (gr <= 0x0002) ) {
498 guint16 z=0, shortLgr;
499 fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp);
501 if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
502 fwrite ( &z, (size_t)2 ,(size_t)1 ,_fp);
503 fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
507 fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp);
509 } else { // IMPLICIT VR
510 fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
513 if (vr == "US" || vr == "SS") {
514 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
515 Tokenize ((*tag2)->GetValue(), tokens, "\\");
516 for (unsigned int i=0; i<tokens.size();i++) {
517 val_uint16 = atoi(tokens[i].c_str());
519 fwrite ( ptr,(size_t)2 ,(size_t)1 ,_fp);
524 if (vr == "UL" || vr == "SL") {
525 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
526 Tokenize ((*tag2)->GetValue(), tokens, "\\");
527 for (unsigned int i=0; i<tokens.size();i++) {
528 val_uint32 = atoi(tokens[i].c_str());
530 fwrite ( ptr,(size_t)4 ,(size_t)1 ,_fp);
535 // Pixels are never loaded in the element !
536 if ((gr == 0x7fe0) && (el == 0x0010) ) break;
538 fwrite ( val,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
542 //-----------------------------------------------------------------------------