]> Creatis software - gdcm.git/blobdiff - src/gdcmParser.cxx
Now the tree-like structure describing a DICOMDIR comming from an already
[gdcm.git] / src / gdcmParser.cxx
index 42db7b9f395e4baa8cc1cc6fbc81bf8608b72dc8..03fdde5d55a873c1a69c6f3574ca2b2f36062b0d 100644 (file)
@@ -3,6 +3,7 @@
 #include "gdcmParser.h"
 #include "gdcmUtil.h"
 #include <errno.h>
+#include <unistd.h>
 
 // For nthos:
 #ifdef _MSC_VER
@@ -65,8 +66,8 @@
    //
 
    // Other usefull abreviations :
-   //Radiographic view associated with Patient Position (0018,5100).
-   //  Defined Terms:
+   // Radiographic view associated with Patient Position (0018,5100).
+   // Defined Terms:
    // 
    //  AP = Anterior/Posterior 
    //  PA = Posterior/Anterior 
@@ -95,7 +96,7 @@ const unsigned int gdcmParser::MAX_SIZE_PRINT_ELEMENT_VALUE = 64;
  * \ingroup gdcmParser
  * \brief constructor  
  * @param   inFilename
- * @param   exception_on_error
+ * @param   exception_on_error whether we throw an exception or not
  * @param   enable_sequences = true to allow the header 
  *          to be parsed *inside* the SeQuences, 
  *          when they have an actual length 
@@ -112,7 +113,7 @@ gdcmParser::gdcmParser(const char *inFilename,
    enableSequences=enable_sequences;
    ignoreShadow   =ignore_shadow;
    
-   SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE);
+   SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE); 
    filename = inFilename;
    Initialise();
 
@@ -183,7 +184,7 @@ void gdcmParser::PrintPubDict(std::ostream & os) {
 
 /**
   * \ingroup gdcmParser
-  * \brief   Prints The Dict Entries of THE shadow Dicom Dictionnry
+  * \brief   Prints The Dict Entries of THE shadow Dicom Dictionnary
   * @return
   */
 void gdcmParser::PrintShaDict(std::ostream & os) {
@@ -453,22 +454,28 @@ bool gdcmParser::Write(FILE *fp, FileType type) {
  * \ingroup gdcmParser
  * \brief   Modifies the value of a given Header Entry (Dicom Element)
  *          if it exists; Creates it with the given value if it doesn't
+ * \warning : adds the Header Entry to the HTable, NOT to the chained List
  * @param   Value passed as a std::string
- * @param   Group
- * @param   Elem
- * \return  false only if new element creation fails
+ * @param Group   group of the Entry 
+ * @param Elem element of the Entry
+ * \return  pointer to the created Header Entry
+ *          NULL if creation failed
  */
-bool gdcmParser::ReplaceOrCreateByNumber(std::string Value, 
+gdcmHeaderEntry * gdcmParser::ReplaceOrCreateByNumber(
+                                         std::string Value, 
                                          guint16 Group, 
-                                        guint16 Elem ){
-   if (CheckIfEntryExistByNumber(Group, Elem) == 0) {
+                                        guint16 Elem ){                                         
+   gdcmHeaderEntry* a;
+   a = GetHeaderEntryByNumber( Group, Elem);                                    
+   if (a == NULL) {
       gdcmHeaderEntry *a =NewHeaderEntryByNumber(Group, Elem);
       if (a == NULL) 
-         return false;
+         return NULL;
       AddHeaderEntry(a);
    }   
-   SetEntryByNumber(Value, Group, Elem);
-   return(true);
+   //SetEntryByNumber(Value, Group, Elem);
+   a->SetValue(Value);
+   return(a);
 }   
 
 /**
@@ -478,20 +485,24 @@ bool gdcmParser::ReplaceOrCreateByNumber(std::string Value,
  * @param   Value passed as a char*
  * @param Group   group of the Entry 
  * @param Elem element of the Entry
- * \return  boolean 
+ * \return  pointer to the created Header Entry
+ *          NULL if creation failed 
  * 
  */
-bool gdcmParser::ReplaceOrCreateByNumber(char* Value, guint16 Group, guint16 Elem ) {
+gdcmHeaderEntry *  gdcmParser::ReplaceOrCreateByNumber(
+                                     char* Value, 
+                                     guint16 Group, 
+                                     guint16 Elem ) {
    gdcmHeaderEntry* nvHeaderEntry=NewHeaderEntryByNumber(Group, Elem);
 
    if(!nvHeaderEntry)
-      return(false);
+      return(NULL);
 
    AddHeaderEntry(nvHeaderEntry);
 
    std::string v = Value;      
    SetEntryByNumber(v, Group, Elem);
-   return(true);
+   return(nvHeaderEntry);
 }  
 
 /**
@@ -808,7 +819,7 @@ bool gdcmParser::SetEntryVoidAreaByNumber(void * area,
 /**
  * \ingroup gdcmParser
  * \brief   Update the entries with the shadow dictionary. 
- *          Only non even entries are  analyzed       
+ *          Only non even entries are analyzed       
  */
 void gdcmParser::UpdateShaEntries(void) {
    gdcmDictEntry *entry;
@@ -1021,19 +1032,14 @@ void gdcmParser::UpdateGroupLength(bool SkipSequence, FileType type) {
 /**
  * \ingroup gdcmParser
  * \brief   writes on disc according to the requested format
- *          (ACR-NEMA, ExplicitVR, ImplicitVR) the image
- *          using the Chained List
- * \warning does NOT add the missing elements in the header :
- *           it's up to the user doing it !
- *           (function CheckHeaderCoherence to be written)
- * \warning DON'T try, right now, to write a DICOM image
- *           from an ACR Header (meta elements will be missing!)
- * \sa WriteEntriesDeprecated (Special temporay method for Theralys)
+ *          (ACR-NEMA, ExplicitVR, ImplicitVR) ONE
+ *          gdcmHeaderEntry 
+ * @param   tag pointer on the gdcmHeaderEntry to be written
  * @param   type type of the File to be written 
  *          (ACR-NEMA, ExplicitVR, ImplicitVR)
  * @param   _fp already open file pointer
  */
-void gdcmParser::WriteEntries(FILE *_fp,FileType type)
+void gdcmParser::WriteEntry(gdcmHeaderEntry *tag, FILE *_fp,FileType type)
 {
    guint16 gr, el;
    guint32 lgr;
@@ -1045,46 +1051,36 @@ void gdcmParser::WriteEntries(FILE *_fp,FileType type)
    guint16 valZero =0;
    void *voidArea;
    std::vector<std::string> tokens;
-   
-   // TODO : function CheckHeaderCoherence to be written
-   
-   //  uses now listEntries to iterate, not TagHt!
-   //
-   //        pb : gdcmParser.Add does NOT update listEntries
-   //       TODO : find a trick (in STL?) to do it, at low cost !
 
    void *ptr;
    int ff=0xffffffff;
    // TODO (?) tester les echecs en ecriture (apres chaque fwrite)
    int compte =0;
-   
-   for (ListTag::iterator tag2=listEntries.begin();
-        tag2 != listEntries.end();
-        ++tag2)
-   {
+   itsTimeToWritePixels = false;
+
+      gr    = tag->GetGroup();
+      el    = tag->GetElement();
+      lgr   = tag->GetReadLength();
+      val   = tag->GetValue().c_str();
+      vr    = tag->GetVR();
+      voidArea = tag->GetVoidArea();
+           
       // === Deal with the length
       //     --------------------
-      if(((*tag2)->GetLength())%2==1)
+      if((tag->GetLength())%2==1)
       { 
-         (*tag2)->SetValue((*tag2)->GetValue()+"\0");
-         (*tag2)->SetLength((*tag2)->GetReadLength()+1);
+         tag->SetValue(tag->GetValue()+"\0");
+         tag->SetLength(tag->GetReadLength()+1);
       }
-
-      gr    = (*tag2)->GetGroup();
-      el    = (*tag2)->GetElement();
-      lgr   = (*tag2)->GetReadLength();
-      val   = (*tag2)->GetValue().c_str();
-      vr    = (*tag2)->GetVR();
-      voidArea = (*tag2)->GetVoidArea();
       
       if ( type == ACR ) 
       { 
-         if (gr < 0x0008)   continue; // ignore pure DICOM V3 groups
-         if (gr %2)         continue; // ignore shadow groups
-         if (vr == "SQ" )   continue; // ignore Sequences
+         if (gr < 0x0008)   return; // ignore pure DICOM V3 groups
+         if (gr %2)         return; // ignore shadow groups
+         if (vr == "SQ" )   return; // ignore Sequences
                   // TODO : find a trick to *skip* the SeQuences !
                   // Not only ignore the SQ element
-         if (gr == 0xfffe ) continue; // ignore delimiters
+         if (gr == 0xfffe ) return; // ignore delimiters
       } 
 
       fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp);  //group
@@ -1095,9 +1091,16 @@ void gdcmParser::WriteEntries(FILE *_fp,FileType type)
          guint16 z=0, shortLgr;
         
          if (gr == 0xfffe) { // NO Value Representation for 'delimiters'
-               // no length : write ffffffff           
+                             // no length : write ffffffff
+                                                    
+                                        // special patch to make some MR PHILIPS
+             if (el == 0x0000) return;  // images e-film readable                                      // see gdcmData/gdcm-MR-PHILIPS-16-Multi-Seq.dcm
+                                       // from Hospital Guy de Chauliac,
+                                        // Montpellier
+                                       // we just ignore spurious fffe|0000 tag !
+                       
             fwrite (&ff,(size_t)4 ,(size_t)1 ,_fp);
-            continue;       // NO value for 'delimiters'                           
+            return;       // NO value for 'delimiters'                     
         }
         
         shortLgr=lgr;   
@@ -1122,19 +1125,19 @@ void gdcmParser::WriteEntries(FILE *_fp,FileType type)
       
       // === Deal with the value
       //     -------------------
-      if (vr == "SQ")  continue; // no "value" to write for the SEQuences
-      if (gr == 0xfffe)continue; // no "value" to write for the delimiters
+      if (vr == "SQ")  return; // no "value" to write for the SEQuences
+      if (gr == 0xfffe)return; // no "value" to write for the delimiters
       
       if (voidArea != NULL) 
       { // there is a 'non string' LUT, overlay, etc
          fwrite ( voidArea,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
-         continue;            
+         return;            
       }
       
       if (vr == "US" || vr == "SS") 
       {
          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
-         Tokenize ((*tag2)->GetValue(), tokens, "\\");
+         Tokenize (tag->GetValue(), tokens, "\\");
          for (unsigned int i=0; i<tokens.size();i++) 
          {
             val_uint16 = atoi(tokens[i].c_str());
@@ -1142,12 +1145,12 @@ void gdcmParser::WriteEntries(FILE *_fp,FileType type)
             fwrite ( ptr,(size_t)2 ,(size_t)1 ,_fp);
          }
          tokens.clear();
-         continue;
+         return;
       }
       if (vr == "UL" || vr == "SL") 
       {
          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
-         Tokenize ((*tag2)->GetValue(), tokens, "\\");
+         Tokenize (tag->GetValue(), tokens, "\\");
          for (unsigned int i=0; i<tokens.size();i++) 
          {
             val_uint32 = atoi(tokens[i].c_str());
@@ -1155,7 +1158,7 @@ void gdcmParser::WriteEntries(FILE *_fp,FileType type)
             fwrite ( ptr,(size_t)4 ,(size_t)1 ,_fp);
          }
          tokens.clear();
-         continue;
+         return;
       } 
           
       // Pixels are never loaded in the element !
@@ -1164,13 +1167,43 @@ void gdcmParser::WriteEntries(FILE *_fp,FileType type)
             
       if ((gr == GrPixel) && (el == NumPixel) ) {
          compte++;
-         if (compte == countGrPixel) // we passed *all* the GrPixel,NumPixel   
-            break;
+         if (compte == countGrPixel) {// we passed *all* the GrPixel,NumPixel   
+            itsTimeToWritePixels = true;
+           return;
+        }
       }       
       fwrite ( val,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
-   }
 }
 
+/**
+ * \ingroup gdcmParser
+ * \brief   writes on disc according to the requested format
+ *          (ACR-NEMA, ExplicitVR, ImplicitVR) the image
+ *          using the Chained List
+ * \warning does NOT add the missing elements in the header :
+ *           it's up to the user doing it !
+ *           (function CheckHeaderCoherence to be written)
+ * \warning DON'T try, right now, to write a DICOM image
+ *           from an ACR Header (meta elements will be missing!)
+ * \sa WriteEntriesDeprecated (Special temporary method for Theralys)
+ * @param   type type of the File to be written 
+ *          (ACR-NEMA, ExplicitVR, ImplicitVR)
+ * @param   _fp already open file pointer
+ */
+
+void gdcmParser::WriteEntries(FILE *_fp,FileType type)
+{   
+   // TODO (?) tester les echecs en ecriture (apres chaque fwrite)
+   
+   for (ListTag::iterator tag2=listEntries.begin();
+                          tag2 != listEntries.end();
+                          ++tag2)
+   {
+   WriteEntry(*tag2,_fp,type);
+   if (itsTimeToWritePixels) 
+      break;
+   }
+}   
 
 /**
  * \ingroup gdcmParser
@@ -1190,93 +1223,18 @@ void gdcmParser::WriteEntries(FILE *_fp,FileType type)
  *          (ACR-NEMA, ExplicitVR, ImplicitVR)
  */
 void gdcmParser::WriteEntriesDeprecated(FILE *_fp,FileType type) {
-   guint16 gr, el;
-   guint32 lgr;
-   const char * val;
-   std::string vr;
-   guint32 val_uint32;
-   guint16 val_uint16;
-   
-   std::vector<std::string> tokens;
 
-   void *ptr;
-
-   // Tout ceci ne marche QUE parce qu'on est sur un proc Little Endian 
    // restent a tester les echecs en ecriture (apres chaque fwrite)
 
    for (TagHeaderEntryHT::iterator tag2=tagHT.begin();
         tag2 != tagHT.end();
         ++tag2){
-
-      gr =  tag2->second->GetGroup();
-      el =  tag2->second->GetElement();
-      lgr = tag2->second->GetLength();
-      val = tag2->second->GetValue().c_str();
-      vr =  tag2->second->GetVR();
-      
-     // std::cout << "Tag "<< std::hex << gr << " " << el << std::endl;
-
-      if ( type == ACR ) { 
-         if (gr < 0x0008)   continue; // ignore pure DICOM V3 groups
-         if (gr %2)         continue; // ignore shadow groups
-         if (vr == "SQ" )   continue; // ignore Sequences
-         if (gr == 0xfffe ) continue; // ignore delimiters
-      } 
-
-      fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp);  //group
-      fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp);  //element
-
-      if ( (type == ExplicitVR) && (gr <= 0x0002) ) {
-         // EXPLICIT VR
-         guint16 z=0, shortLgr;
-         fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp);
-
-         if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
-            fwrite ( &z,  (size_t)2 ,(size_t)1 ,_fp);
-            fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
-
-         } else {
-            shortLgr=lgr;
-            fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp);
-         }
-      } else { // IMPLICIT VR
-         fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
-      }
-
-      if (vr == "US" || vr == "SS") {
-         tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
-         Tokenize (tag2->second->GetValue(), tokens, "\\");
-         for (unsigned int i=0; i<tokens.size();i++) {
-            val_uint16 = atoi(tokens[i].c_str());
-            ptr = &val_uint16;
-            fwrite ( ptr,(size_t)2 ,(size_t)1 ,_fp);
-         }
-         tokens.clear();
-         continue;
-      }
-      if (vr == "UL" || vr == "SL") {
-         tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
-         Tokenize (tag2->second->GetValue(), tokens, "\\");
-         for (unsigned int i=0; i<tokens.size();i++) {
-            val_uint32 = atoi(tokens[i].c_str());
-            ptr = &val_uint32;
-            fwrite ( ptr,(size_t)4 ,(size_t)1 ,_fp);
-         }
-         tokens.clear();
-         continue;
-      }     
-      // Pixels are never loaded in the element !
-      if ((gr == 0x7fe0) && (el == 0x0010) ) break;
-
-      fwrite ( val,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
+      WriteEntry(tag2->second,_fp,type);
+      if (itsTimeToWritePixels) 
+         break;
    }
 }
 
-
-
-
-
-
 /**
  * \ingroup gdcmParser
  * \brief   Swaps back the bytes of 4-byte long integer accordingly to
@@ -1524,7 +1482,7 @@ void gdcmParser::LoadHeaderEntry(gdcmHeaderEntry *Entry)  {
 /**
  * \ingroup gdcmParser
  * \brief   add a new Dicom Element pointer to 
- *          the H Table and to the chained List
+ *          the H Table and at the end of the chained List
  * \warning push_bash in listEntries ONLY during ParseHeader
  * \todo    something to allow further Elements addition,
  *          (at their right place in the chained list)
@@ -1941,31 +1899,20 @@ void gdcmParser::FixHeaderEntryFoundLength(gdcmHeaderEntry *Entry, guint32 Found
    } 
     
    // a SeQuence Element is beginning                                          
-   // Let's forget it's length                                                 
-   // (we want to 'go inside')  
-
-   // Pb : *normaly*  fffe|e000 is just a marker, its length *should be* zero
-   // in gdcm-MR-PHILIPS-16-Multi-Seq.dcm we find lengthes as big as 28800
-   // if we set the length to zero IsHeaderEntryAnInteger() breaks...
-   // if we don't, we lost 28800 characters from the Header :-(
-                                                 
+   // fffe|e000 is just a marker, its length *should be* zero                                               
    else if(Entry->GetGroup() == 0xfffe)
    { 
-  // cout << "ReadLength " <<Entry->GetReadLength() << " UsableLength " << FoundLength << endl;  
-  //   Entry->Print();                                                           
-                       // sometimes, length seems to be wrong                                      
-      FoundLength =0;  // some more clever checking to be done !
-                       // I give up!
-                      // only  gdcm-MR-PHILIPS-16-Multi-Seq.dcm
-                      // causes troubles :-(                  
-   }     
-    
+                                         // *normally, fffe|0000 doesn't exist ! 
+     if( Entry->GetElement() != 0x0000 ) // gdcm-MR-PHILIPS-16-Multi-Seq.dcm
+                                        // causes extra troubles :-(                                                        
+         FoundLength =0;
+   }         
    Entry->SetUsableLength(FoundLength);
 }
 
 /**
  * \ingroup gdcmParser
- * \brief   Apply some heuristics to predict wether the considered 
+ * \brief   Apply some heuristics to predict whether the considered 
  *          element value contains/represents an integer or not.
  * @param   Entry The element value on which to apply the predicate.
  * @return  The result of the heuristical predicate.
@@ -2104,7 +2051,7 @@ guint32 gdcmParser::ReadInt32(void) {
 
 /**
  * \ingroup gdcmParser
- * \brief   
+ * \brief skips bytes inside the source file 
  * \warning NOT end user intended method !
  * @return 
  */
@@ -2115,7 +2062,8 @@ void gdcmParser::SkipBytes(guint32 NBytes) {
 
 /**
  * \ingroup gdcmParser
- * \brief   
+ * \brief Loads all the needed Dictionaries
+ * \warning NOT end user intended method !   
  */
 void gdcmParser::Initialise(void) 
 {
@@ -2310,7 +2258,8 @@ bool gdcmParser::CheckSwap() {
 
 /**
  * \ingroup gdcmParser
- * \brief   
+ * \brief Restore the unproperly loaded values i.e. the group, the element
+ *        and the dictionary entry depending on them. 
  */
 void gdcmParser::SwitchSwapToBigEndian(void) 
 {