1 // gdcmHeaderEntrySet.cxx
2 //-----------------------------------------------------------------------------
4 #include "gdcmHeaderEntrySet.h"
6 #ifdef GDCM_NO_ANSI_STRING_STREAM
8 # define ostringstream ostrstream
13 //-----------------------------------------------------------------------------
14 // Constructor / Destructor
16 * \ingroup gdcmHeaderEntrySet
19 gdcmHeaderEntrySet::~gdcmHeaderEntrySet() {
20 for (TagHeaderEntryHT::iterator tag = tagHT.begin(); tag != tagHT.end(); ++tag) {
21 gdcmHeaderEntry* EntryToDelete = tag->second;
30 //-----------------------------------------------------------------------------
33 * \ingroup gdcmHeaderEntrySet
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 gdcmHeaderEntrySet::Print(std::ostream & os) {
41 unsigned short int g, e;
44 gdcmTS * ts = gdcmGlobal::GetTS();
49 s << "------------- using tagHT ---------------------" << std::endl;
50 for (TagHeaderEntryHT::iterator tag = tagHT.begin();
53 g = tag->second->GetGroup();
54 e = tag->second->GetElement();
55 v = tag->second->GetValue();
56 o = tag->second->GetOffset();
57 d2 = _CreateCleanString(v); // replace non printable characters by '.'
59 s << tag->first << ": ";
60 s << " lgr : " <<tag->second->GetLength();
61 s << ",\t Offset : " << o;
62 s << " x(" << std::hex << o << std::dec << ") ";
63 s << "\t[" << tag->second->GetVR() << "]";
64 s << "\t[" << tag->second->GetName() << "]";
65 s << "\t[" << d2 << "]";
67 // Display the UID value (instead of displaying the rough code)
68 if (g == 0x0002) { // Some more to be displayed ?
69 if ( (e == 0x0010) || (e == 0x0002) )
70 s << " ==>\t[" << ts->GetValue(v) << "]";
73 if ( (e == 0x0016) || (e == 0x1150) )
74 s << " ==>\t[" << ts->GetValue(v) << "]";
82 char greltag[10]; //group element tag
85 s << "------------ using listEntries -------------------" << std::endl;
87 for (ListTag::iterator i = listEntries.begin();
88 i != listEntries.end();
91 e = (*i)->GetElement();
93 o = (*i)->GetOffset();
94 sprintf(greltag,"%04x|%04x",g,e);
95 d2 = _CreateCleanString(v); // replace non printable characters by '.'
96 s << greltag << ": lgth : ";
97 lgth = (*i)->GetReadLength();
99 s << std::hex << "x(" << lgth << ") "<< std::dec << lgth;
100 s << ",\t Offset : " << o;
101 s << " x(" << std::hex << o << std::dec << ") ";
102 s << "\t[" << (*i)->GetVR() << "]";
103 s << "\t[" << (*i)->GetName() << "]";
104 s << "\t[" << d2 << "]";
106 // Display the UID value (instead of displaying the rough code)
107 if (g == 0x0002) { // Any more to be displayed ?
108 if ( (e == 0x0010) || (e == 0x0002) )
109 s << " ==>\t[" << ts->GetValue(v) << "]";
112 if ( (e == 0x0016) || (e == 0x1150) )
113 s << " ==>\t[" << ts->GetValue(v) << "]";
121 //-----------------------------------------------------------------------------
124 * \ingroup gdcmHeaderEntrySet
125 * \brief add a new Dicom Element pointer to
126 * the H Table and to the chained List
127 * @param newHeaderEntry
129 void gdcmHeaderEntrySet::Add(gdcmHeaderEntry * newHeaderEntry) {
131 // tagHT [newHeaderEntry->GetKey()] = newHeaderEntry;
133 tagHT.insert( PairHT( newHeaderEntry->GetKey(),newHeaderEntry) );
135 // WARNING : push_bash in listEntries ONLY during ParseHeader
136 // TODO : something to allow further Elements addition
137 // position to be taken care of !
138 listEntries.push_back(newHeaderEntry);
142 * \ingroup gdcmHeaderEntrySet
143 * \brief retrieves a Dicom Element (the first one) using (group, element)
144 * \ warning (group, element) IS NOT an identifier inside the Dicom Header
145 * if you think it's NOT UNIQUE, check the count number
146 * and use iterators to retrieve ALL the Dicoms Elements within
147 * a given couple (group, element)
148 * @param group Group number of the searched Dicom Element
149 * @param element Element number of the searched Dicom Element
152 gdcmHeaderEntry* gdcmHeaderEntrySet::GetHeaderEntryByNumber(guint16 group, guint16 element) {
153 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
154 if ( ! tagHT.count(key))
156 return tagHT.find(key)->second;
160 * \ingroup gdcmHeaderEntrySet
161 * \brief Gets the value (string) of the target Dicom Element
162 * @param group Group number of the searched Dicom Element
163 * @param element Element number of the searched Dicom Element
166 std::string gdcmHeaderEntrySet::GetEntryByNumber(guint16 group, guint16 element) {
167 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
168 if ( ! tagHT.count(key))
170 return tagHT.find(key)->second->GetValue();
174 * \ingroup gdcmHeaderEntrySet
175 * \brief Sets the value (string) of the target Dicom Element
176 * @param content string value of the Dicom Element
177 * @param group Group number of the searched Dicom Element
178 * @param element Element number of the searched Dicom Element
181 bool gdcmHeaderEntrySet::SetEntryByNumber(std::string content,
182 guint16 group, guint16 element) {
183 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
184 if ( ! tagHT.count(key))
186 int l = content.length();
187 if(l%2) { // Odd length are padded with a space (020H).
189 content = content + '\0';
192 //tagHT[key]->SetValue(content);
195 TagHeaderEntryHT::iterator p2;
196 // DO NOT remove the following lines : they explain the stuff
197 //p= tagHT.equal_range(key); // get a pair of iterators first-last synonym
198 //p2=p.first; // iterator on the first synonym
199 //a=p2->second; // H Table target column (2-nd col)
202 a = ((tagHT.equal_range(key)).first)->second;
204 a-> SetValue(content);
206 //std::string vr = tagHT[key]->GetVR();
207 std::string vr = a->GetVR();
210 if( (vr == "US") || (vr == "SS") )
212 else if( (vr == "UL") || (vr == "SL") )
216 //tagHT[key]->SetLength(lgr);
222 * \ingroup gdcmHeaderEntrySet
223 * \brief Sets the value length of the Dicom Element
224 * \warning : use with caution !
226 * @param group Group number of the searched Dicom Element
227 * @param element Element number of the searched Dicom Element
230 bool gdcmHeaderEntrySet::SetEntryLengthByNumber(guint32 length,
231 guint16 group, guint16 element) {
232 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
233 if ( ! tagHT.count(key))
235 if (length%2) length++; // length must be even
236 //tagHT[key]->SetLength(length);
237 ( ((tagHT.equal_range(key)).first)->second )->SetLength(length);
242 * \ingroup gdcmHeaderEntrySet
243 * \brief Sets a 'non string' value to a given Dicom Element
245 * @param group Group number of the searched Dicom Element
246 * @param element Element number of the searched Dicom Element
249 bool gdcmHeaderEntrySet::SetVoidAreaByNumber(void * area,
250 guint16 group, guint16 element) {
251 TagKey key = gdcmDictEntry::TranslateToKey(group, element);
252 if ( ! tagHT.count(key))
254 //tagHT[key]->SetVoidArea(area);
255 ( ((tagHT.equal_range(key)).first)->second )->SetVoidArea(area);
260 * \ingroup gdcmHeaderEntrySet
261 * \brief Generate a free TagKey i.e. a TagKey that is not present
262 * in the TagHt dictionary.
263 * @param group The generated tag must belong to this group.
264 * @return The element of tag with given group which is fee.
266 guint32 gdcmHeaderEntrySet::GenerateFreeTagKeyInGroup(guint16 group) {
267 for (guint32 elem = 0; elem < UINT32_MAX; elem++) {
268 TagKey key = gdcmDictEntry::TranslateToKey(group, elem);
269 if (tagHT.count(key) == 0)
276 * \ingroup gdcmHeaderEntrySet
277 * \brief Checks if a given Dicom Element exists
278 * \ within the H table
279 * @param group Group number of the searched Dicom Element
280 * @param element Element number of the searched Dicom Element
281 * @return number of occurences
283 int gdcmHeaderEntrySet::CheckIfExistByNumber(guint16 group, guint16 element ) {
284 std::string key = gdcmDictEntry::TranslateToKey(group, element );
285 return (tagHT.count(key));
289 // TODO to be re-written using the chained list instead of the H table
290 // so we can remove the GroupHT from the gdcmHeader
293 * \ingroup gdcmHeaderEntrySet
295 * @param _fp already open file pointer
296 * @param type type of the File to be written
297 * (ACR-NEMA, ExplicitVR, ImplicitVR)
298 * @return always "True" ?!
300 bool gdcmHeaderEntrySet::Write(FILE * _fp, FileType type) {
303 // Comment pourrait-on savoir si le DcmHeader vient d'un fichier DicomV3 ou non
304 // (FileType est un champ de gdcmHeader ...)
305 // WARNING : Si on veut ecrire du DICOM V3 a partir d'un DcmHeader ACR-NEMA
307 // a moins de se livrer a un tres complique ajout des champs manquants.
308 // faire un CheckAndCorrectHeader (?)
310 if ( (type == ImplicitVR) || (type == ExplicitVR) )
311 UpdateGroupLength(false,type);
313 UpdateGroupLength(true,ACR);
315 WriteEntries(type, _fp);
319 //-----------------------------------------------------------------------------
322 //-----------------------------------------------------------------------------
325 * \ingroup gdcmHeaderEntrySet
326 * \brief Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader
327 * \warning : to be re-written using the chained list instead of the H table.
328 * \todo : to be re-written using the chained list instead of the H table
329 * @param SkipSequence TRUE if we don't want to write Sequences (ACR-NEMA Files)
330 * @param type Type of the File (ExplicitVR,ImplicitVR, ACR, ...)
332 void gdcmHeaderEntrySet::UpdateGroupLength(bool SkipSequence, FileType type) {
336 gdcmHeaderEntry *elem;
338 std::string str_trash;
341 GroupHT groupHt; // to hold the length of each group
344 // typedef std::map<GroupKey, int> GroupHT;
346 gdcmHeaderEntry *elemZ;
348 // for each Tag in the DCM Header
350 for (TagHeaderEntryHT::iterator tag2 = tagHT.begin();
355 gr = elem->GetGroup();
356 el = elem->GetElement();
359 sprintf(trash, "%04x", gr);
360 key = trash; // generate 'group tag'
362 // if the caller decided not to take SEQUENCEs into account
363 // e.g : he wants to write an ACR-NEMA File
365 if (SkipSequence && vr == "SQ") continue;
367 // Still unsolved problem :
368 // we cannot find the 'Sequence Delimitation Item'
369 // since it's at the end of the Hash Table
372 // pas SEQUENCE en ACR-NEMA
374 // --> la descente a l'interieur' des SQ
375 // devrait etre faite avec une liste chainee, pas avec une HTable...
377 if ( groupHt.count(key) == 0) { // we just read the first elem of a given group
378 if (el == 0x0000) { // the first elem is 0x0000
379 groupHt[key] = 0; // initialize group length
381 groupHt[key] = 2 + 2 + 4 + elem->GetLength(); // non 0x0000 first group elem
383 } else { // any elem but the first
384 if (type == ExplicitVR) {
385 if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
386 groupHt[key] += 4; // explicit VR AND OB, OW, SQ : 4 more bytes
389 groupHt[key] += 2 + 2 + 4 + elem->GetLength();
393 unsigned short int gr_bid;
395 for (GroupHT::iterator g = groupHt.begin(); // for each group we found
398 // FIXME: g++ -Wall -Wstrict-prototypes reports on following line:
399 // warning: unsigned int format, different type arg
400 sscanf(g->first.c_str(),"%x",&gr_bid);
401 tk = g->first + "|0000"; // generate the element full tag
403 if ( tagHT.count(tk) == 0) { // if element 0x0000 not found
404 gdcmDictEntry * tagZ = new gdcmDictEntry(gr_bid, 0x0000, "UL");
405 elemZ = new gdcmHeaderEntry(tagZ);
407 Add(elemZ); // create it
409 elemZ=GetHeaderEntryByNumber(gr_bid, 0x0000);
411 sprintf(trash ,"%d",g->second);
413 elemZ->SetValue(str_trash);
418 * \ingroup gdcmHeaderEntrySet
419 * \brief writes on disc according to the requested format
420 * \ (ACR-NEMA, ExplicitVR, ImplicitVR) the image
421 * \ warning does NOT add the missing elements in the header :
422 * \ it's up to the user doing it !
423 * \ (function CheckHeaderCoherence to be written)
424 * @param type type of the File to be written
425 * (ACR-NEMA, ExplicitVR, ImplicitVR)
426 * @param _fp already open file pointer
429 void gdcmHeaderEntrySet::WriteEntries(FileType type, FILE * _fp) {
437 std::vector<std::string> tokens;
439 // TODO : use listEntries to iterate, not TagHt!
440 // pb : gdcmHeaderEntrySet.Add does NOT update listEntries
441 // find a trick in STL to do it, at low cost !
445 // Tout ceci ne marche QUE parce qu'on est sur un proc Little Endian
446 // restent a tester les echecs en ecriture (apres chaque fwrite)
448 for (TagHeaderEntryHT::iterator tag2=tagHT.begin();
452 gr = tag2->second->GetGroup();
453 el = tag2->second->GetElement();
454 lgr = tag2->second->GetLength();
455 val = tag2->second->GetValue().c_str();
456 vr = tag2->second->GetVR();
459 if (gr < 0x0008) continue; // ignore pure DICOM V3 groups
460 if (gr %2) continue; // ignore shadow groups
461 if (vr == "SQ" ) continue; // ignore Sequences
462 // TODO : find a trick to *skip* the SeQuences !
463 // Not only ignore the SQ element
464 if (gr == 0xfffe ) continue; // ignore delimiters
467 fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp); //group
468 fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp); //element
470 if ( (type == ExplicitVR) && (gr <= 0x0002) ) {
472 guint16 z=0, shortLgr;
473 fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp);
475 if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
476 fwrite ( &z, (size_t)2 ,(size_t)1 ,_fp);
477 fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
481 fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp);
483 } else { // IMPLICIT VR
484 fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
487 if (vr == "US" || vr == "SS") {
488 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
489 Tokenize (tag2->second->GetValue(), tokens, "\\");
490 for (unsigned int i=0; i<tokens.size();i++) {
491 val_uint16 = atoi(tokens[i].c_str());
493 fwrite ( ptr,(size_t)2 ,(size_t)1 ,_fp);
498 if (vr == "UL" || vr == "SL") {
499 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
500 Tokenize (tag2->second->GetValue(), tokens, "\\");
501 for (unsigned int i=0; i<tokens.size();i++) {
502 val_uint32 = atoi(tokens[i].c_str());
504 fwrite ( ptr,(size_t)4 ,(size_t)1 ,_fp);
509 // Pixels are never loaded in the element !
510 if ((gr == 0x7fe0) && (el == 0x0010) ) break;
512 fwrite ( val,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
516 //-----------------------------------------------------------------------------