]> Creatis software - gdcm.git/blob - src/gdcmDataEntry.cxx
// User should ask gdcm to write an image in Explicit VR mode
[gdcm.git] / src / gdcmDataEntry.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDataEntry.cxx,v $
5   Language:  C++
6   Date:      $Date: 2006/03/13 14:44:07 $
7   Version:   $Revision: 1.33 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18
19 #include "gdcmDataEntry.h"
20 #include "gdcmVR.h"
21 #include "gdcmTS.h"
22 #include "gdcmGlobal.h"
23 #include "gdcmUtil.h"
24 #include "gdcmDebug.h"
25
26 #include <fstream>
27
28 #if defined(__BORLANDC__)
29  #include <mem.h> // for memcpy
30  #include <stdlib.h> // for atof
31  #include <ctype.h> // for isdigit
32 #endif
33
34 namespace gdcm 
35 {
36 //-----------------------------------------------------------------------------
37 #define MAX_SIZE_PRINT_ELEMENT_VALUE 0x7fffffff
38 uint32_t DataEntry::MaxSizePrintEntry = MAX_SIZE_PRINT_ELEMENT_VALUE;
39
40 //-----------------------------------------------------------------------------
41 // Constructor / Destructor
42 /**
43  * \brief   Constructor for a given DictEntry
44  * @param   e Pointer to existing dictionary entry
45  */
46 DataEntry::DataEntry(DictEntry *e) 
47             : DocEntry(e)
48 {
49    State = STATE_LOADED;
50    Flag = FLAG_NONE;
51
52    BinArea = 0;
53    SelfArea = true;
54 }
55
56 /**
57  * \brief   Constructor for a given DocEntry
58  * @param   e Pointer to existing Doc entry
59  */
60 DataEntry::DataEntry(DocEntry *e)
61             : DocEntry(e->GetDictEntry())
62 {
63    Flag = FLAG_NONE;
64    BinArea = 0;
65    SelfArea = true;
66
67    Copy(e);
68 }
69
70 /**
71  * \brief   Canonical destructor.
72  */
73 DataEntry::~DataEntry ()
74 {
75    DeleteBinArea();
76 }
77
78 //-----------------------------------------------------------------------------
79 // Print
80
81 //-----------------------------------------------------------------------------
82 // Public
83 /**
84  * \brief Sets the value (non string) of the current DataEntry
85  * @param area area
86  * @param self self
87  */
88 void DataEntry::SetBinArea( uint8_t *area, bool self )  
89
90    DeleteBinArea();
91
92    BinArea = area;
93    SelfArea = self;
94
95    State = STATE_LOADED;
96 }
97 /**
98  * \brief Inserts the value (non string) into the current DataEntry
99  * @param area area
100  * @param length length 
101  */
102 void DataEntry::CopyBinArea( uint8_t *area, uint32_t length )
103 {
104    DeleteBinArea();
105
106    uint32_t lgh = length + length%2;
107    SetLength(lgh);
108
109    if( area && length > 0 )
110    {
111       NewBinArea();
112       memcpy(BinArea,area,length);
113       if( length!=lgh )
114          BinArea[length]=0;
115
116       State = STATE_LOADED;
117    }
118 }
119
120 /**
121  * \brief Inserts the elementary (non string) value into the current DataEntry
122  * @param id index of the elementary value to be set
123  * @param val value, passed as a double 
124  */
125 void DataEntry::SetValue(const uint32_t &id, const double &val)
126 {
127    if( !BinArea )
128       NewBinArea();
129    State = STATE_LOADED;
130
131    if( id > GetValueCount() )
132    {
133       gdcmErrorMacro("Index (" << id << ")is greater than the data size");
134       return;
135    }
136
137    const VRKey &vr = GetVR();
138    if( vr == "US" || vr == "SS" )
139    {
140       uint16_t *data = (uint16_t *)BinArea;
141       data[id] = (uint16_t)val;
142    }
143    else if( vr == "UL" || vr == "SL" )
144    {
145       uint32_t *data = (uint32_t *)BinArea;
146       data[id] = (uint32_t)val;
147    }
148    else if( vr == "FL" )
149    {
150       float *data = (float *)BinArea;
151       data[id] = (float)val;
152    }
153    else if( vr == "FD" )
154    {
155       double *data = (double *)BinArea;
156       data[id] = (double)val;
157    }
158    else if( Global::GetVR()->IsVROfStringRepresentable(vr) )
159    {
160       gdcmErrorMacro("SetValue on String representable not implemented yet");
161    }
162    else
163    {
164       BinArea[id] = (uint8_t)val;
165    }
166 }
167 /**
168  * \brief returns, as a double (?!?) one of the values 
169  *      (when entry is multivaluated), identified by its index.
170  *      Returns 0.0 if index is wrong
171  * @param id id
172  */
173 double DataEntry::GetValue(const uint32_t &id) const
174 {
175    if( !BinArea )
176    {
177    /// \todo warn the user there was a problem !
178       gdcmErrorMacro("BinArea not set" << std::hex << GetGroup() << " " << GetElement() << " Can't get the value");
179       return 0.0;
180    }
181
182    uint32_t count = GetValueCount();
183    if( id > count )
184    {
185       gdcmErrorMacro("Index (" << id << ") is greater than the data size");
186       return 0.0;
187    }
188
189    /// \todo FIX the API : user *knows* that entry contains a US
190    ///               and he receives a double ?!?
191    
192    const VRKey &vr = GetVR();
193    /// \todo FIX the API : user *knows* that entry contains a US,
194    ///       the method is supposed to return a double
195    ///       but sends a US ?!? 
196    if( vr == "US" || vr == "SS" )
197       return ((uint16_t *)BinArea)[id];
198    else if( vr == "UL" || vr == "SL" )
199       return ((uint32_t *)BinArea)[id];
200    else if( vr == "FL" )
201       return ((float *)BinArea)[id];
202    else if( vr == "FD" )
203       return ((double *)BinArea)[id];
204    else if( Global::GetVR()->IsVROfStringRepresentable(vr) )
205    {
206       // this is for VR = "DS", ...
207       if( GetLength() )
208       {
209          // Don't use std::string to accelerate processing
210          double val;
211          char *tmp = new char[GetLength()+1];
212          memcpy(tmp,BinArea,GetLength());
213          tmp[GetLength()]=0;
214
215          if( count == 0 )
216          {
217             val = atof(tmp);
218          }
219          else
220          {
221             count = id;
222             char *beg = tmp;
223             for(uint32_t i=0;i<GetLength();i++)
224             {
225                if( tmp[i] == '\\' )
226                {
227                   if( count == 0 )
228                   {
229                      tmp[i] = 0;
230                      break;
231                   }
232                   else
233                   {
234                      count--;
235                      beg = &(tmp[i+1]);
236                   }
237                }
238             }
239             val = atof(beg);
240          }
241
242          delete[] tmp;
243          return val;
244       }
245       else 
246          return 0.0;
247    }
248    else
249       return BinArea[id];
250 }
251
252 /**
253  * \brief Checks if the multiplicity of the value follows Dictionary VM
254  */
255 bool DataEntry::IsValueCountValid() const
256 {
257   uint32_t vm;
258   const std::string &strVM = GetVM();
259   uint32_t vc = GetValueCount();
260   bool valid = vc == 0;
261   if( valid )
262     return true;
263   
264   // FIXME : what shall we do with VM = "2-n", "3-n", etc
265   
266   if( strVM == "1-n" )
267   {
268     // make sure there is at least one ??? FIXME
269     valid = vc >= 1;
270   }
271   else
272   {
273     std::istringstream os;
274     os.str( strVM );
275     os >> vm;
276     // Two cases:
277     // vm respects the one from the dict
278     // vm is 0 (we need to check if this element is allowed to be empty) FIXME
279
280     // note  (JPR)
281     // ----    
282     // Entries whose type is 1 are mandatory, with a mandatory value.
283     // Entries whose type is 1c are mandatory-inside-a-Sequence,
284     //                          with a mandatory value.
285     // Entries whose type is 2 are mandatory, with an optional value.
286     // Entries whose type is 2c are mandatory-inside-a-Sequence,
287     //                          with an optional value.
288     // Entries whose type is 3 are optional.
289
290     // case vc == 0 is only applicable for 'type 2' entries.
291     // Problem : entry type may depend on the modality and/or the Sequence
292     //           it's embedded in !
293     //          (Get the information in the 'Conformance Statements' ...)  
294     valid = vc == vm;
295   }
296   return valid;
297 }
298
299 /**
300  * \brief returns the number of elementary values
301  */ 
302 uint32_t DataEntry::GetValueCount( ) const
303 {
304    const VRKey &vr = GetVR();
305    if( vr == "US" || vr == "SS" )
306       return GetLength()/sizeof(uint16_t);
307    else if( vr == "UL" || vr == "SL" )
308       return GetLength()/sizeof(uint32_t);
309    else if( vr == "FL" || vr == "OF" )
310       return GetLength()/4 ; // FL has a *4* length! sizeof(float);
311    else if( vr == "FD" )
312       return GetLength()/8;  // FD has a *8* length! sizeof(double);
313    else if( Global::GetVR()->IsVROfStringRepresentable(vr) )
314    {
315       // Some element in DICOM are allowed to be empty
316       if( !GetLength() ) 
317          return 0;
318       // Don't use std::string to accelerate processing
319       uint32_t count = 1;
320       for(uint32_t i=0;i<GetLength();i++)
321       {
322          if( BinArea[i] == '\\')
323             count++;
324       }
325       return count;
326    }
327    return GetLength();
328 }
329
330 /**
331  * \brief Gets a std::vector <double> holding the value(s) of a DS DataEntry
332  * @param valueVector std::vector <double> of value(s)
333  * \return false if VR not "DS" or DataEntry empty
334  */
335  bool DataEntry::GetDSValue(std::vector <double> &valueVector)
336  {
337     /// \todo rewrite the whole method, in order *not to use* std::string !
338     std::vector<std::string> tokens;
339     
340     if (GetVR() != "DS") // never trust a user !
341        return false;    
342        
343     Util::Tokenize ( GetString().c_str(), tokens, "\\" );
344         
345     int nbValues= tokens.size();
346     if (nbValues == 0)
347        return false;
348                
349     for (int loop=0; loop<nbValues; loop++) 
350        valueVector.push_back(atof(tokens[loop].c_str()));
351     
352     return true;  
353  }
354  
355 /**
356  * \brief Sets the 'value' of a DataEntry, passed as a std::string
357  * @param value string representation of the value to be set
358  */ 
359 void DataEntry::SetString(std::string const &value)
360 {
361    DeleteBinArea();
362
363    const VRKey &vr = GetVR();
364    if ( vr == "US" || vr == "SS" )
365    {
366       std::vector<std::string> tokens;
367       Util::Tokenize (value, tokens, "\\");
368       SetLength(tokens.size()*sizeof(uint16_t));
369       NewBinArea();
370
371       uint16_t *data = (uint16_t *)BinArea;
372       for (unsigned int i=0; i<tokens.size();i++)
373          data[i] = atoi(tokens[i].c_str());
374       tokens.clear();
375    }
376    else if ( vr == "UL" || vr == "SL" )
377    {
378       std::vector<std::string> tokens;
379       Util::Tokenize (value, tokens, "\\");
380       SetLength(tokens.size()*sizeof(uint32_t));
381       NewBinArea();
382
383       uint32_t *data = (uint32_t *)BinArea;
384       for (unsigned int i=0; i<tokens.size();i++)
385          data[i] = atoi(tokens[i].c_str());
386       tokens.clear();
387    }
388    else if ( vr == "FL" )
389    {
390       std::vector<std::string> tokens;
391       Util::Tokenize (value, tokens, "\\");
392       SetLength(tokens.size()*sizeof(float));
393       NewBinArea();
394
395       float *data = (float *)BinArea;
396       for (unsigned int i=0; i<tokens.size();i++)
397          data[i] = (float)atof(tokens[i].c_str());
398       tokens.clear();
399    }
400    else if ( vr == "FD" )
401    {
402       std::vector<std::string> tokens;
403       Util::Tokenize (value, tokens, "\\");
404       SetLength(tokens.size()*sizeof(double));
405       NewBinArea();
406
407       double *data = (double *)BinArea;
408       for (unsigned int i=0; i<tokens.size();i++)
409          data[i] = atof(tokens[i].c_str());
410       tokens.clear();
411    }
412    else
413    {
414       if( value.size() > 0 )
415       {
416          size_t l =  value.size();    
417          SetLength(l + l%2);
418          NewBinArea();
419          memcpy(BinArea, value.c_str(), l);
420          if (l%2)
421             BinArea[l] = '\0';
422       }
423    }
424    State = STATE_LOADED;
425 }
426 /**
427  * \brief   returns as a string (when possible) the value of the DataEntry
428  */
429 std::string const &DataEntry::GetString() const
430 {
431    static std::ostringstream s;
432    const VRKey &vr = GetVR();
433
434    s.str("");
435    StrArea="";
436
437    if( !BinArea )
438       return StrArea;
439       
440    // When short integer(s) are stored, convert the following (n * 2) characters 
441    // as a displayable string, the values being separated by a back-slash
442
443    if( vr == "US" || vr == "SS" )
444    {
445       uint16_t *data=(uint16_t *)BinArea;
446
447       for (unsigned int i=0; i < GetValueCount(); i++)
448       {
449          if( i!=0 )
450             s << '\\';
451          s << data[i];
452       }
453       StrArea=s.str();
454    }
455    // See above comment on multiple short integers (mutatis mutandis).
456    else if( vr == "UL" || vr == "SL" )
457    {
458       uint32_t *data=(uint32_t *)BinArea;
459
460       for (unsigned int i=0; i < GetValueCount(); i++)
461       {
462          if( i!=0 )
463             s << '\\';
464          s << data[i];
465       }
466       StrArea=s.str();
467    }
468    else if( vr == "FL" )
469    {
470       float *data=(float *)BinArea;
471
472       for (unsigned int i=0; i < GetValueCount(); i++)
473       {
474          if( i!=0 )
475             s << '\\';
476          s << data[i];
477       }
478       StrArea=s.str();
479    }
480    else if( vr == "FD" )
481    {
482       double *data=(double *)BinArea;
483
484       for (unsigned int i=0; i < GetValueCount(); i++)
485       {
486          if( i!=0 )
487             s << '\\';
488          s << data[i];
489       }
490       StrArea=s.str();
491    }
492    else
493    {
494       StrArea.append((const char *)BinArea,GetLength());
495       // to avoid gdcm propagate oddities in lengthes
496       if ( GetLength()%2)
497          StrArea.append(" ",1);  
498    }
499    return StrArea;
500 }
501
502 /**
503  * \brief Copies all the attributes from an other DocEntry 
504  * @param doc entry to copy from
505  * @remarks The content BinArea is copied too
506  */
507 void DataEntry::Copy(DocEntry *doc)
508 {
509    DocEntry::Copy(doc);
510
511    DataEntry *entry = dynamic_cast<DataEntry *>(doc);
512    if ( entry )
513    {
514       State = entry->State;
515       Flag = entry->Flag;
516       CopyBinArea(entry->BinArea,entry->GetLength());
517    }
518 }
519
520 /**
521  * \brief   Writes the 'value' area of a DataEntry
522  * @param fp already open ofstream pointer
523  * @param filetype type of the file (ACR, ImplicitVR, ExplicitVR, ...)
524  */
525 void DataEntry::WriteContent(std::ofstream *fp, FileType filetype)
526
527    DocEntry::WriteContent(fp, filetype);
528
529    if ( GetGroup() == 0xfffe )
530    {
531       return; //delimitors have NO value
532    }
533    
534    // --> We only deal with Little Endian writting.
535    // --> forget Big Endian Transfer Syntax writting!
536    //     Next DICOM version will give it up ...
537  
538    // WARNING - For Implicit VR private element,
539    //           we have *no choice* but considering them as
540    //           something like 'OB' values.
541    //           we rewrite them as we found them on disc.
542    //           Some trouble will occur if element was 
543    //           *actually* OW, if image was produced 
544    //           on Big endian based processor, read and writen 
545    //           on Little endian based processor
546    //           and, later on, somebody needs
547    //           this 'OW' Implicit VR private element (?!?)
548    //           (Same stuff, mutatis mutandis, for Little/Big)
549  
550    // 8/16 bits Pixels problem should be solved automatiquely,
551    // since we ensure the VR (OB vs OW) is conform to Pixel size.
552         
553    uint8_t *data = BinArea; //safe notation
554    size_t l = GetLength(); 
555    gdcmDebugMacro("in DataEntry::WriteContent " << GetKey() << " AtomicLength: "
556               << Global::GetVR()->GetAtomicElementLength(this->GetVR() ) // << " BinArea in :" << &BinArea
557              );
558    if (BinArea) // the binArea was *actually* loaded
559    {
560 #if defined(GDCM_WORDS_BIGENDIAN) || defined(GDCM_FORCE_BIGENDIAN_EMULATION)
561       unsigned short vrLgth = 
562                         Global::GetVR()->GetAtomicElementLength(this->GetVR());
563       unsigned int i;
564       switch(vrLgth)
565       {
566          case 1:
567          {
568             binary_write (*fp, data, l );           
569             break;
570          }     
571          case 2:
572          {
573             uint16_t *data16 = (uint16_t *)data;
574             for(i=0;i<l/vrLgth;i++)
575                binary_write( *fp, data16[i]);
576             break;
577          }
578          case 4:
579          {
580             uint32_t *data32 = (uint32_t *)data;
581             for(i=0;i<l/vrLgth;i++)
582                binary_write( *fp, data32[i]);
583             break;
584          }
585          case 8:
586          {
587             double *data64 = (double *)data;
588             for(i=0;i<l/vrLgth;i++)
589                binary_write( *fp, data64[i]);
590             break;
591          }
592       }
593 #else
594    binary_write (*fp, data, l );
595 #endif //GDCM_WORDS_BIGENDIAN
596
597    }
598    else
599    {
600       // nothing was loaded, but we need to skip space on disc     
601       if (l != 0)
602       {
603       //  --> WARNING : nothing is written; 
604       //  --> the initial data (on the the source image) is lost
605       //  --> user is *not* informed !      
606          gdcmDebugMacro ("Nothing was loaded, but we need to skip space on disc. "
607                       << "Length =" << l << " for " << GetKey() );   
608          fp->seekp(l, std::ios::cur); // At Write time, for unloaded elems
609       }
610    }
611    // to avoid gdcm to propagate oddities
612    // (length was already modified)  
613    if (l%2)
614       fp->seekp(1, std::ios::cur);  // At Write time, for non even length elems
615 }
616
617 /**
618  * \brief   Compute the full length of the elementary DataEntry (not only value
619  *          length) depending on the VR.
620  */
621 uint32_t DataEntry::ComputeFullLength()
622 {
623    return GetFullLength();
624 }
625
626 //-----------------------------------------------------------------------------
627 // Protected
628 /// \brief Creates a DataEntry owned BinArea (remove previous one if any)
629 void DataEntry::NewBinArea( )
630 {
631    DeleteBinArea();
632    if( GetLength() > 0 )
633       BinArea = new uint8_t[GetLength()];
634    SelfArea = true;
635 }
636 /// \brief Removes the BinArea, if owned by the DataEntry
637 void DataEntry::DeleteBinArea(void)
638 {
639    if (BinArea && SelfArea)
640    {
641       delete[] BinArea;
642       BinArea = NULL;
643    }
644 }
645
646 //-----------------------------------------------------------------------------
647 // Private
648
649 //-----------------------------------------------------------------------------
650 // Print
651 /**
652  * \brief   Prints a DataEntry (Dicom entry)
653  * @param   os ostream we want to print in
654  * @param indent Indentation string to be prepended during printing
655  */
656 void DataEntry::Print(std::ostream &os, std::string const & )
657 {
658    os << "D ";
659    DocEntry::Print(os);
660
661    uint16_t g = GetGroup();
662    if (g == 0xfffe) // delimiters have NO value
663    {          
664       return; // just to avoid identing all the remaining code 
665    }
666
667    std::ostringstream s;
668    TSAtr v;
669
670    if( BinArea )
671    {
672       v = GetString();
673       const VRKey &vr = GetVR();
674
675       if( vr == "US" || vr == "SS" || vr == "UL" || vr == "SL" 
676        || vr == "FL" || vr == "FD")
677          s << " [" << GetString() << "]";
678       else
679       { 
680          if(Global::GetVR()->IsVROfStringRepresentable(vr))
681          {
682             // replace non printable characters by '.'
683             std::string cleanString = Util::CreateCleanString(v);
684             if ( cleanString.length() <= GetMaxSizePrintEntry()
685               || PrintLevel >= 3
686               || IsNotLoaded() )
687            // FIXME : when IsNotLoaded(), you create a Clean String ?!?
688            // FIXME : PrintLevel<2 *does* print the values 
689            //        (3 is only for extra offsets printing)
690            // What do you wanted to do ? JPR
691             {
692                s << " [" << cleanString << "]";
693             }
694             else
695             {
696                s << " [gdcm::too long for print (" << cleanString.length() << ") ]";
697             }
698          }
699          else
700          {
701             // A lot of Private elements (with no VR) contain actually 
702             // only printable characters;
703             // Let's deal with them as is they were VR std::string representable
704     
705             if ( Util::IsCleanArea( GetBinArea(), GetLength()  ) )
706             {
707                // FIXME : since the 'Area' *is* clean, just use
708                //         a 'CreateString' method, to save CPU time.
709                std::string cleanString = 
710                      Util::CreateCleanString( BinArea,GetLength()  );
711                s << " [" << cleanString << "]";
712             }
713             else
714             {
715                s << " [" << GDCM_BINLOADED << ";"
716                << "length = " << GetLength() << "]";
717             }
718          }
719       }
720    }
721    else
722    {
723       if( IsNotLoaded() )
724          s << " [" << GDCM_NOTLOADED << "]";
725       else if( IsUnfound() )
726          s << " [" << GDCM_UNFOUND << "]";
727       else if( IsUnread() )
728          s << " [" << GDCM_UNREAD << "]";
729       else if ( GetLength() == 0 )
730          s << " []";
731    }
732
733    if( IsPixelData() )
734       s << " (" << GDCM_PIXELDATA << ")";
735
736    // Display the UID value (instead of displaying only the rough code)
737    // First 'clean' trailing character (space or zero) 
738    if(BinArea)
739    {
740       const uint16_t &gr = GetGroup();
741       const uint16_t &elt = GetElement();
742       TS *ts = Global::GetTS();
743
744       if (gr == 0x0002)
745       {
746          // Any more to be displayed ?
747          if ( elt == 0x0010 || elt == 0x0002 )
748          {
749             if ( v.length() != 0 )  // for brain damaged headers
750             {
751                if ( ! isdigit((unsigned char)v[v.length()-1]) )
752                {
753                   v.erase(v.length()-1, 1);
754                }
755             }
756             s << "  ==>\t[" << ts->GetValue(v) << "]";
757          }
758       }
759       else if (gr == 0x0008)
760       {
761          if ( elt == 0x0016 || elt == 0x1150 )
762          {
763             if ( v.length() != 0 )  // for brain damaged headers
764             {
765                if ( ! isdigit((unsigned char)v[v.length()-1]) )
766                {
767                   v.erase(v.length()-1, 1);
768                }
769             }
770             s << "  ==>\t[" << ts->GetValue(v) << "]";
771          }
772       }
773       else if (gr == 0x0004)
774       {
775          if ( elt == 0x1510 || elt == 0x1512  )
776          {
777             if ( v.length() != 0 )  // for brain damaged headers  
778             {
779                if ( ! isdigit((unsigned char)v[v.length()-1]) )
780                {
781                   v.erase(v.length()-1, 1);  
782                }
783             }
784             s << "  ==>\t[" << ts->GetValue(v) << "]";
785          }
786       }
787    }
788
789    os << s.str();
790 }
791
792 //-----------------------------------------------------------------------------
793 } // end namespace gdcm
794