X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmDocument.cxx;h=557eb4e6b6a8ee1d20e998d485cc7fc0857c3823;hb=951d1f96c7bff6329f19fa28444fae5c941c352d;hp=5ce5f5769ba9a4df2ba73308bad28385668d0732;hpb=a6152ede9f919769745b51f8fe560251a20c6a1a;p=gdcm.git diff --git a/src/gdcmDocument.cxx b/src/gdcmDocument.cxx index 5ce5f576..557eb4e6 100644 --- a/src/gdcmDocument.cxx +++ b/src/gdcmDocument.cxx @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmDocument.cxx,v $ Language: C++ - Date: $Date: 2005/07/06 00:15:11 $ - Version: $Revision: 1.256 $ + Date: $Date: 2005/08/30 15:34:35 $ + Version: $Revision: 1.274 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -62,11 +62,13 @@ Document::Document() // Load will set it to true if sucessfull Group0002Parsed = false; IsDocumentAlreadyLoaded = false; - LoadMode = 0x00000000; // default : load everything, later + IsDocumentModified = true; + LoadMode = LD_ALL; // default : load everything, later + SetFileName(""); } /** - * \brief Constructor (not to break the API) + * \brief Constructor (DEPRECATED : not to break the API) * @param fileName 'Document' (File or DicomDir) to be open for parsing */ Document::Document( std::string const &fileName ) @@ -79,46 +81,78 @@ Document::Document( std::string const &fileName ) SwapCode = 1234; Filetype = ExplicitVR; Group0002Parsed = false; - LoadMode = 0x00000000; // Load everything, later + LoadMode = LD_ALL; // Load everything, later // Load will set it to true if sucessfull IsDocumentAlreadyLoaded = false; + IsDocumentModified = true; - Load(fileName); + SetFileName(fileName); + Load( ); } /** * \brief Canonical destructor. */ Document::~Document () { - RefPubDict = NULL; - RefShaDict = NULL; + CloseFile(); } //----------------------------------------------------------------------------- // Public /** - * \brief Loader - * @param fileName 'Document' (File or DicomDir) to be open for parsing + * \brief Loader. use SetLoadMode(), SetFileName() before ! * @return false if file cannot be open or no swap info was found, * or no tag was found. */ -bool Document::Load( std::string const &fileName ) +bool Document::Load( ) { - // We should clean out anything that already exists. - // Check IsDocumentAlreadyLoaded to be sure. - if ( IsDocumentAlreadyLoaded ) + if ( GetFileName() == "" ) { - gdcmWarningMacro( "A file was already parsed inside this " - << "gdcm::Document (previous name was: " - << Filename.c_str() << ". New name is :" - << fileName ); - // todo : clean out the 'Document' - // Should we call ClearEntry() on the parent object ?!? + gdcmWarningMacro( "Use SetFileName, before !" ); + return false; } - + return DoTheLoadingDocumentJob( ); +} +/** + * \brief Loader. (DEPRECATED : not to break the API) + * @param fileName 'Document' (File or DicomDir) to be open for parsing + * @return false if file cannot be open or no swap info was found, + * or no tag was found. + */ +bool Document::Load( std::string const &fileName ) +{ Filename = fileName; + return DoTheLoadingDocumentJob( ); +} + +/** + * \brief Performs the Loading Job (internal use only) + * @return false if file cannot be open or no swap info was found, + * or no tag was found. + */ +bool Document::DoTheLoadingDocumentJob( ) +{ + if ( ! IsDocumentModified ) // Nothing to do ! + return true; + + // if ( Filename == fileName ) + // { + // gdcmWarningMacro( "The file was already parsed inside this " + // << "gdcm::Document (its name is: " + // << Filename.c_str() ); + // return true; + // } + + //gdcmWarningMacro( "A file was already parsed inside this " + // << "gdcm::Document (previous name was: " + // << Filename.c_str() << ". New name is :" + // << fileName ); + // clean out the Entries, if already parsed + // (probabely a mistake from the user) + + ClearEntry(); Fp = 0; if ( !OpenFile() ) @@ -223,6 +257,37 @@ bool Document::Load( std::string const &fileName ) } } + // Force Loading some more elements if user asked to. + + gdcm::DocEntry *d; + for (ListElements::iterator it = UserForceLoadList.begin(); + it != UserForceLoadList.end(); + ++it) + { + d = GetDocEntry( (*it).Group, (*it).Elem); + + if ( d == NULL) + continue; + + if ( dynamic_cast(d) ) + { + LoadDocEntry(d, true); + continue; + } + + if ( dynamic_cast(d) ) + { + LoadEntryBinArea((*it).Group, (*it).Elem); + continue; + } + + if ( dynamic_cast(d) ) + { + gdcmWarningMacro( "You cannot 'ForceLoad' a SeqEntry "); + continue; + } + } + CloseFile(); // ---------------------------- @@ -248,10 +313,22 @@ bool Document::Load( std::string const &fileName ) SetValEntry(rows , 0x0028, 0x0011); } // --- End of ACR-LibIDO kludge --- - return true; } + +/** + * \brief Adds a new element we want to load anyway + * @param group Group number of the target tag. + * @param elem Element number of the target tag. + */ +void Document::AddForceLoadElement (uint16_t group, uint16_t elem) +{ + Element el; + el.Group = group; + el.Elem = elem; + UserForceLoadList.push_back(el); +} /** * \brief Get the public dictionary used */ @@ -641,7 +718,9 @@ void Document::LoadEntryBinArea(BinEntry *elem) uint8_t *a = new uint8_t[l]; if ( !a ) { - gdcmWarningMacro( "Cannot allocate BinEntry content"); + gdcmWarningMacro( "Cannot allocate BinEntry content for : " + << std::hex << elem->GetGroup() + << "|" << elem->GetElement() ); return; } @@ -858,7 +937,7 @@ void Document::Initialize() * \brief Parses a DocEntrySet (Zero-level DocEntries or SQ Item DocEntries) * @param set DocEntrySet we are going to parse ('zero level' or a SQItem) * @param offset start of parsing - * @param l_max length to parse + * @param l_max length to parse (meaningless when we are in 'delimitor mode') * @param delim_mode : whether we are in 'delimitor mode' (l=0xffffff) or not */ void Document::ParseDES(DocEntrySet *set, long offset, @@ -871,7 +950,7 @@ void Document::ParseDES(DocEntrySet *set, long offset, VRKey vr; bool used; bool delim_mode_intern = delim_mode; - + bool first = true; while (true) { if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max) @@ -879,23 +958,43 @@ void Document::ParseDES(DocEntrySet *set, long offset, break; } - used = true; newDocEntry = ReadNextDocEntry( ); + // Uncoment this printf line to be able to 'follow' the DocEntries + // when something *very* strange happens + + //printf( "%04x|%04x %s\n",newDocEntry->GetGroup(), + // newDocEntry->GetElement(), + // newDocEntry->GetVR().c_str() ); + if ( !newDocEntry ) { break; } - vr = newDocEntry->GetVR(); + // an Item Starter found elsewhere but the first postition + // of a SeqEntry means previous entry was a Sequence + // but we didn't get it (private Sequence + Implicit VR) + // we have to backtrack. + if ( !first && newDocEntry->IsItemStarter() ) + { + newDocEntry = Backtrack(newDocEntry); + } + else + { + PreviousDocEntry = newDocEntry; + } + + used = true; newValEntry = dynamic_cast(newDocEntry); newBinEntry = dynamic_cast(newDocEntry); - newSeqEntry = dynamic_cast(newDocEntry); - if ( newValEntry || newBinEntry ) + if ( newValEntry || newBinEntry ) { + //////////////////////////// ContentEntry if ( newBinEntry ) { + vr = newDocEntry->GetVR(); if ( Filetype == ExplicitVR && !Global::GetVR()->IsVROfBinaryRepresentable(vr) ) { @@ -907,59 +1006,78 @@ void Document::ParseDES(DocEntrySet *set, long offset, } //////////////////// BinEntry or UNKOWN VR: + // When "this" is a Document the Key is simply of the // form ( group, elem )... - if ( dynamic_cast< Document* > ( set ) ) - { - newBinEntry->SetKey( newBinEntry->GetKey() ); - } + //if ( set == this ) // ( dynamic_cast< Document* > ( set ) ) + //{ + // newBinEntry->SetKey( newBinEntry->GetKey() ); + //} // but when "this" is a SQItem, we are inserting this new // valEntry in a sequence item, and the key has the // generalized form (refer to \ref BaseTagKey): - if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) ) - { - newBinEntry->SetKey( parentSQItem->GetBaseTagKey() - + newBinEntry->GetKey() ); - } - LoadDocEntry( newBinEntry ); + // time waste hunting + //if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) ) + //{ + // newBinEntry->SetKey( parentSQItem->GetBaseTagKey() + // + newBinEntry->GetKey() ); + //} + if ( !set->AddEntry( newBinEntry ) ) { - //Expect big troubles if here - //delete newBinEntry; - gdcmWarningMacro("in ParseDES : shouldn't get here !"); - used=false; + gdcmWarningMacro( "in ParseDES : cannot add a BinEntry " + << newBinEntry->GetKey() + << " (at offset : " + << newBinEntry->GetOffset() << " )" ); + used=false; } - } + else + { + // Load only if we can add (not a duplicate key) + LoadDocEntry( newBinEntry ); + } + } // end BinEntry else { /////////////////////// ValEntry // When "set" is a Document, then we are at the top of the // hierarchy and the Key is simply of the form ( group, elem )... - if ( dynamic_cast< Document* > ( set ) ) - { - newValEntry->SetKey( newValEntry->GetKey() ); - } + //if ( set == this ) // ( dynamic_cast< Document* > ( set ) ) + //{ + // newValEntry->SetKey( newValEntry->GetKey() ); + //} // ...but when "set" is a SQItem, we are inserting this new // valEntry in a sequence item. Hence the key has the // generalized form (refer to \ref BaseTagKey): - if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) ) + + // time waste hunting + //if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) ) + //{ + // newValEntry->SetKey( parentSQItem->GetBaseTagKey() + // + newValEntry->GetKey() ); + //} + + if ( !set->AddEntry( newValEntry ) ) + { + gdcmWarningMacro( "in ParseDES : cannot add a ValEntry " + << newValEntry->GetKey() + << " (at offset : " + << newValEntry->GetOffset() << " )" ); + used=false; + } + else { - newValEntry->SetKey( parentSQItem->GetBaseTagKey() - + newValEntry->GetKey() ); + // Load only if we can add (not a duplicate key) + LoadDocEntry( newValEntry ); } - - LoadDocEntry( newValEntry ); - bool delimitor=newValEntry->IsItemDelimitor(); - if ( LoadMode & NO_SHADOW ) // User asked to skip, if possible, - // shadow groups ( if possible : - // whether element 0x0000 exits) + if ( newValEntry->GetElement() == 0x0000 ) // if on group length { - if ( newValEntry->GetGroup()%2 != 0 ) + if ( newValEntry->GetGroup()%2 != 0 ) // if Shadow Group { - if ( newValEntry->GetElement() == 0x0000 ) + if ( LoadMode & LD_NOSHADOW ) // if user asked to skip shad.gr { std::string strLgrGroup = newValEntry->GetValue(); int lgrGroup; @@ -968,26 +1086,18 @@ void Document::ParseDES(DocEntrySet *set, long offset, lgrGroup = atoi(strLgrGroup.c_str()); Fp->seekg(lgrGroup, std::ios::cur); used = false; + RemoveEntry( newDocEntry ); + newDocEntry = 0; continue; } } } - } - - if ( !set->AddEntry( newValEntry ) ) - { - // If here expect big troubles - // delete newValEntry; //otherwise mem leak - used=false; } - if (delimitor) - { - if ( !used ) - delete newDocEntry; - break; - } - if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max ) + bool delimitor=newValEntry->IsItemDelimitor(); + + if ( (delimitor) || + (!delim_mode && ((long)(Fp->tellg())-offset) >= l_max) ) { if ( !used ) delete newDocEntry; @@ -1015,7 +1125,7 @@ void Document::ParseDES(DocEntrySet *set, long offset, } } - if ( (LoadMode & NO_SHADOWSEQ) && ! delim_mode_intern ) + if ( (LoadMode & LD_NOSHADOWSEQ) && ! delim_mode_intern ) { // User asked to skip SeQuences *only* if they belong to Shadow Group if ( newDocEntry->GetGroup()%2 != 0 ) @@ -1025,34 +1135,40 @@ void Document::ParseDES(DocEntrySet *set, long offset, continue; } } - if ( (LoadMode & NO_SEQ) && ! delim_mode_intern ) + if ( (LoadMode & LD_NOSEQ) && ! delim_mode_intern ) { // User asked to skip *any* SeQuence Fp->seekg( l, std::ios::cur); used = false; continue; } + // delay the dynamic cast as late as possible + newSeqEntry = dynamic_cast(newDocEntry); - // no other way to create it ... + // no other way to create the Delimitor ... newSeqEntry->SetDelimitorMode( delim_mode_intern ); // At the top of the hierarchy, stands a Document. When "set" // is a Document, then we are building the first depth level. // Hence the SeqEntry we are building simply has a depth // level of one: - if ( dynamic_cast< Document* > ( set ) ) +// SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ); + if ( set == this ) // ( dynamic_cast< Document* > ( set ) ) { newSeqEntry->SetDepthLevel( 1 ); - newSeqEntry->SetKey( newSeqEntry->GetKey() ); + // newSeqEntry->SetKey( newSeqEntry->GetKey() ); } // But when "set" is already a SQItem, we are building a nested // sequence, and hence the depth level of the new SeqEntry // we are building, is one level deeper: - if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) ) + + // time waste hunting + else if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) ) { newSeqEntry->SetDepthLevel( parentSQItem->GetDepthLevel() + 1 ); - newSeqEntry->SetKey( parentSQItem->GetBaseTagKey() - + newSeqEntry->GetKey() ); + + // newSeqEntry->SetKey( parentSQItem->GetBaseTagKey() + // + newSeqEntry->GetKey() ); } if ( l != 0 ) @@ -1063,20 +1179,27 @@ void Document::ParseDES(DocEntrySet *set, long offset, } if ( !set->AddEntry( newSeqEntry ) ) { + gdcmWarningMacro( "in ParseDES : cannot add a SeqEntry " + << newSeqEntry->GetKey() + << " (at offset : " + << newSeqEntry->GetOffset() << " )" ); used = false; } - if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max) + if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max) { if ( !used ) - delete newDocEntry; - break; + delete newDocEntry; + break; } - } + } // end SeqEntry : VR = "SQ" if ( !used ) + { delete newDocEntry; - } + } + first = false; + } // end While } /** @@ -1116,12 +1239,14 @@ void Document::ParseSQ( SeqEntry *seqEntry, } // create the current SQItem SQItem *itemSQ = new SQItem( seqEntry->GetDepthLevel() ); +/* std::ostringstream newBase; newBase << seqEntry->GetKey() << "/" << SQItemNumber << "#"; itemSQ->SetBaseTagKey( newBase.str() ); +*/ unsigned int l = newDocEntry->GetReadLength(); if ( l == 0xffffffff ) @@ -1154,11 +1279,48 @@ void Document::ParseSQ( SeqEntry *seqEntry, } /** - * \brief Loads the element content if its length doesn't exceed - * the value specified with Document::SetMaxSizeLoadEntry() + * \brief When a private Sequence + Implicit VR is encountered + * we cannot guess it's a Sequence till we find the first + * Item Starter. We then backtrack to do the job. + * @param docEntry Item Starter that warned us + */ +DocEntry *Document::Backtrack(DocEntry *docEntry) +{ + // delete the Item Starter, built erroneously out of any Sequence + // it's not yet in the HTable/chained list + delete docEntry; + + // Get all info we can from PreviousDocEntry + uint16_t group = PreviousDocEntry->GetGroup(); + uint16_t elem = PreviousDocEntry->GetElement(); + uint32_t lgt = PreviousDocEntry->GetLength(); + long offset = PreviousDocEntry->GetOffset(); + + gdcmWarningMacro( "Backtrack :" << std::hex << group + << "|" << elem + << " at offset " << offset ); + RemoveEntry( PreviousDocEntry ); + + // forge the Seq Entry + DocEntry *newEntry = NewSeqEntry(group, elem); + newEntry->SetLength(lgt); + newEntry->SetOffset(offset); + + // Move back to the beginning of the Sequence + Fp->seekg( 0, std::ios::beg); + Fp->seekg(offset, std::ios::cur); + +return newEntry; +} + +/** + * \brief Loads (or not) the element content depending if its length exceeds + * or not the value specified with Document::SetMaxSizeLoadEntry() * @param entry Header Entry (Dicom Element) to be dealt with + * @param forceLoad wheter we want to load its content even if its length + * exceeds the value specified with Document::SetMaxSizeLoadEntry() */ -void Document::LoadDocEntry(DocEntry *entry) +void Document::LoadDocEntry(DocEntry *entry, bool forceLoad) { uint16_t group = entry->GetGroup(); std::string vr = entry->GetVR(); @@ -1188,37 +1350,41 @@ void Document::LoadDocEntry(DocEntry *entry) // the element content and it's length. std::ostringstream s; - if (length > MaxSizeLoadEntry) - { - if (BinEntry *binEntryPtr = dynamic_cast< BinEntry* >(entry) ) - { - s << GDCM_NOTLOADED; - s << " Ad.:" << (long)entry->GetOffset(); - s << " x(" << std::hex << entry->GetOffset() << ")"; - s << std::dec; - s << " Lgt:" << entry->GetLength(); - s << " x(" << std::hex << entry->GetLength() << ")"; - binEntryPtr->SetValue(s.str()); - } - else if (ValEntry *valEntryPtr = dynamic_cast< ValEntry* >(entry) ) - { - s << GDCM_NOTLOADED; - s << " Address:" << (long)entry->GetOffset(); - s << " Length:" << entry->GetLength(); - s << " x(" << std::hex << entry->GetLength() << ")"; - valEntryPtr->SetValue(s.str()); - } - else + + if (!forceLoad) + { + if (length > MaxSizeLoadEntry) { - // fusible - gdcmErrorMacro( "MaxSizeLoadEntry exceeded, neither a BinEntry " - << "nor a ValEntry ?! Should never print that !" ); - } + if (BinEntry *binEntryPtr = dynamic_cast< BinEntry* >(entry) ) + { + s << GDCM_NOTLOADED; + s << " Ad.:" << (long)entry->GetOffset(); + s << " x(" << std::hex << entry->GetOffset() << ")"; + s << std::dec; + s << " Lgt:" << entry->GetLength(); + s << " x(" << std::hex << entry->GetLength() << ")"; + binEntryPtr->SetValue(s.str()); + } + else if (ValEntry *valEntryPtr = dynamic_cast< ValEntry* >(entry) ) + { + s << GDCM_NOTLOADED; + s << " Address:" << (long)entry->GetOffset(); + s << " Length:" << entry->GetLength(); + s << " x(" << std::hex << entry->GetLength() << ")"; + valEntryPtr->SetValue(s.str()); + } + else + { + // fusible + gdcmErrorMacro( "MaxSizeLoadEntry exceeded, neither a BinEntry " + << "nor a ValEntry ?! Should never print that !" ); + } - // to be sure we are at the end of the value ... - Fp->seekg((long)entry->GetOffset()+(long)entry->GetLength(), - std::ios::beg); - return; + // to be sure we are at the end of the value ... + Fp->seekg((long)entry->GetOffset()+(long)entry->GetLength(), + std::ios::beg); + return; + } } // When we find a BinEntry not very much can be done : @@ -1663,9 +1829,15 @@ void Document::SkipDocEntry(DocEntry *entry) */ void Document::SkipToNextDocEntry(DocEntry *currentDocEntry) { - Fp->seekg((long)(currentDocEntry->GetOffset()), std::ios::beg); + int l = currentDocEntry->GetReadLength(); + if ( l == -1 ) // length = 0xffff shouldn't appear here ... + // ... but PMS imagers happen ! + return; + Fp->seekg((long)(currentDocEntry->GetOffset()), std::ios::beg); if (currentDocEntry->GetGroup() != 0xfffe) // for fffe pb + { Fp->seekg( (long)(currentDocEntry->GetReadLength()),std::ios::cur); + } } /** @@ -2077,7 +2249,9 @@ DocEntry *Document::ReadNextDocEntry() { DictEntry *dictEntry = GetDictEntry(group,elem); if ( dictEntry ) + { realVR = dictEntry->GetVR(); + } } } @@ -2119,7 +2293,7 @@ DocEntry *Document::ReadNextDocEntry() } newEntry->SetOffset(Fp->tellg()); - + return newEntry; }