X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmDocument.cxx;h=5ebc0cb127b4806418b0090e40274eda6d11d6f0;hb=01940747ce53f8c87bc883c47fd152e787e73b07;hp=4511aab548db6c6a17ba7bf003d49fbf75804b8c;hpb=44d34727b676570bf3b4a7cab6076210f159d192;p=gdcm.git diff --git a/src/gdcmDocument.cxx b/src/gdcmDocument.cxx index 4511aab5..5ebc0cb1 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/08/23 14:41:59 $ - Version: $Revision: 1.266 $ + Date: $Date: 2005/09/21 09:42:19 $ + Version: $Revision: 1.282 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -63,7 +63,7 @@ Document::Document() Group0002Parsed = false; IsDocumentAlreadyLoaded = false; IsDocumentModified = true; - LoadMode = 0x00000000; // default : load everything, later + LoadMode = LD_ALL; // default : load everything, later SetFileName(""); } @@ -81,7 +81,7 @@ 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; @@ -95,8 +95,7 @@ Document::Document( std::string const &fileName ) */ Document::~Document () { - RefPubDict = NULL; - RefShaDict = NULL; + CloseFile(); } //----------------------------------------------------------------------------- @@ -258,6 +257,48 @@ bool Document::DoTheLoadingDocumentJob( ) } } + // Force Loading some more elements if user asked to. + + gdcm::DocEntry *d; + for (ListElements::iterator it = UserForceLoadList.begin(); + it != UserForceLoadList.end(); + ++it) + { + gdcmWarningMacro( "Force Load " << std::hex + << (*it).Group << "|" <<(*it).Elem ); + + d = GetDocEntry( (*it).Group, (*it).Elem); + + if ( d == NULL) + { + gdcmWarningMacro( "You asked toForce Load " << std::hex + << (*it).Group <<"|"<< (*it).Elem + << " that doesn't exist" ); + continue; + } + + if ( dynamic_cast(d) ) + { + LoadDocEntry(d, true); + continue; + } + + BinEntry *b = dynamic_cast(d); + if ( b ) + { + LoadEntryBinArea(b); + b->SetValue(GDCM_BINLOADED); + continue; + } + + if ( dynamic_cast(d) ) + { + gdcmWarningMacro( "You cannot 'ForceLoad' a SeqEntry :" << std::hex + << (*it).Group <<"|"<< (*it).Elem ); + continue; + } + } + CloseFile(); // ---------------------------- @@ -283,10 +324,22 @@ bool Document::DoTheLoadingDocumentJob( ) 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 */ @@ -552,14 +605,14 @@ std::ifstream *Document::OpenFile() zero == 0x0007 || zero == 0x0700 || zero == 0x0008 || zero == 0x0800 ) { std::string msg = Util::Format( - "ACR/DICOM starting at the begining of the file:(%04x)\n", zero); + "ACR/DICOM starting at the beginning of the file:(%04x)\n", zero); gdcmWarningMacro( msg.c_str() ); return Fp; } //-- DICOM -- Fp->seekg(126L, std::ios::cur); - char dicm[4] = {' ',' ',' ',' '}; + char dicm[4]; // = {' ',' ',' ',' '}; Fp->read(dicm, (size_t)4); if ( Fp->eof() ) { @@ -595,19 +648,14 @@ bool Document::CloseFile() } /** - * \brief Writes in a file all the Header Entries (Dicom Elements) + * \brief Writes in a file all the Entries (Dicom Elements) * @param fp file pointer on an already open file (actually: Output File Stream) * @param filetype Type of the File to be written * (ACR-NEMA, ExplicitVR, ImplicitVR) - * @return Always true. */ void Document::WriteContent(std::ofstream *fp, FileType filetype) { - // \TODO move the following lines (and a lot of others, to be written) - // to a future function CheckAndCorrectHeader - - // (necessary if user wants to write a DICOM V3 file - // starting from an ACR-NEMA (V2) Header + // Skip if user wants to write an ACR-NEMA file if ( filetype == ImplicitVR || filetype == ExplicitVR ) { @@ -646,12 +694,18 @@ void Document::LoadEntryBinArea(uint16_t group, uint16_t elem) // Search the corresponding DocEntry DocEntry *docElement = GetDocEntry(group, elem); if ( !docElement ) + { + gdcmWarningMacro(std::hex << group << "|" << elem + << "doesn't exist" ); return; - + } BinEntry *binElement = dynamic_cast(docElement); if ( !binElement ) + { + gdcmWarningMacro(std::hex << group << "|" << elem + << "is NOT a BinEntry"); return; - + } LoadEntryBinArea(binElement); } @@ -676,7 +730,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; } @@ -833,9 +889,8 @@ void Document::SkipBytes(uint32_t nBytes) /** * \brief Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader - * @param filetype Type of the File to be written */ -int Document::ComputeGroup0002Length( FileType filetype ) +int Document::ComputeGroup0002Length( /*FileType filetype*/ ) { uint16_t gr; std::string vr; @@ -856,15 +911,19 @@ int Document::ComputeGroup0002Length( FileType filetype ) if ( entry->GetElement() != 0x0000 ) { vr = entry->GetVR(); + + // FIXME : group 0x0002 is *always* Explicit VR! - if ( filetype == ExplicitVR ) - { - if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") || (vr == "UT") ) + //if ( filetype == ExplicitVR ) + //{ + // if ( (vr == "OB") || (vr == "OW") || (vr == "UT") || (vr == "SQ") ) + // (no SQ, OW, UT in group 0x0002;) + if ( vr == "OB" ) { // explicit VR AND OB, OW, SQ, UT : 4 more bytes groupLength += 4; } - } + //} groupLength += 2 + 2 + 4 + entry->GetLength(); } } @@ -904,9 +963,10 @@ void Document::ParseDES(DocEntrySet *set, long offset, BinEntry *newBinEntry; SeqEntry *newSeqEntry; VRKey vr; - bool used; + bool used; // will be set to false when something wrong happens to an Entry. + // (Entry will then be deleted) bool delim_mode_intern = delim_mode; - + bool first = true; while (true) { if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max) @@ -916,10 +976,13 @@ void Document::ParseDES(DocEntrySet *set, long offset, newDocEntry = ReadNextDocEntry( ); - if ( !newDocEntry ) - { - break; - } + // FIXME : + // Private tag, in IMplicit VR are defaulted as a BinEntry, + // Very often they are only composed of Printable characters, + // and could be defaulted as a ValEntry. + // It's too late to do the Job + // (we should check the value, but we know it after LoadDocEntry ...) + // --> in next gdcm major release let's unify ValEntry and BinEntry ! // Uncoment this printf line to be able to 'follow' the DocEntries // when something *very* strange happens @@ -927,6 +990,25 @@ void Document::ParseDES(DocEntrySet *set, long offset, //printf( "%04x|%04x %s\n",newDocEntry->GetGroup(), // newDocEntry->GetElement(), // newDocEntry->GetVR().c_str() ); + + if ( !newDocEntry ) + { + break; + } + + // an Item Starter found elsewhere but the first position + // 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); @@ -951,10 +1033,10 @@ void Document::ParseDES(DocEntrySet *set, long offset, // 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): @@ -986,10 +1068,10 @@ void Document::ParseDES(DocEntrySet *set, long offset, // 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): @@ -1019,7 +1101,7 @@ void Document::ParseDES(DocEntrySet *set, long offset, { if ( newValEntry->GetGroup()%2 != 0 ) // if Shadow Group { - if ( LoadMode & NO_SHADOW ) // if user asked to skip shad.gr + if ( LoadMode & LD_NOSHADOW ) // if user asked to skip shad.gr { std::string strLgrGroup = newValEntry->GetValue(); int lgrGroup; @@ -1029,33 +1111,19 @@ void Document::ParseDES(DocEntrySet *set, long offset, Fp->seekg(lgrGroup, std::ios::cur); used = false; RemoveEntry( newDocEntry ); - newDocEntry = 0; + // bcc 5.5 is right "assigned a value that's never used" + // newDocEntry = 0; continue; } } } - } - - bool delimitor=newValEntry->IsItemDelimitor(); - - // FIXME : Brutal patch, waiting till we find a clever way to guess - // if a doc entry is a Sequence, - // - when it's odd number - // - and the file is Implicit VR Transfert Syntax - // - // '&& offset!=132' is a very fierce way to guess - // if we are at zero level (Probabely not enough ...). - // We want to go on parsing. - if ( (delimitor && offset!=132) || + } + + bool delimitor = newValEntry->IsItemDelimitor(); + + if ( (delimitor) || (!delim_mode && ((long)(Fp->tellg())-offset) >= l_max) ) { - if (delimitor && offset!=132) - { - gdcmWarningMacro( "in ParseDES : Item found out of a Sequence " - << newValEntry->GetKey() - << " (at offset : " - << newValEntry->GetOffset() << " )" ); - } if ( !used ) delete newDocEntry; break; @@ -1082,7 +1150,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 ) @@ -1092,7 +1160,7 @@ 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); @@ -1109,17 +1177,18 @@ void Document::ParseDES(DocEntrySet *set, long offset, // 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: // time waste hunting - if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) ) + else if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) ) { newSeqEntry->SetDepthLevel( parentSQItem->GetDepthLevel() + 1 ); @@ -1142,7 +1211,7 @@ void Document::ParseDES(DocEntrySet *set, long offset, used = false; } - if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max) + if ( !delim_mode && ((long)(Fp->tellg())-offset) >= l_max) { if ( !used ) delete newDocEntry; @@ -1154,6 +1223,7 @@ void Document::ParseDES(DocEntrySet *set, long offset, { delete newDocEntry; } + first = false; } // end While } @@ -1234,11 +1304,46 @@ 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 */ -void Document::LoadDocEntry(DocEntry *entry) +void Document::LoadDocEntry(DocEntry *entry, bool forceLoad) { uint16_t group = entry->GetGroup(); std::string vr = entry->GetVR(); @@ -1268,37 +1373,41 @@ void Document::LoadDocEntry(DocEntry *entry) // the element content and it's length. std::ostringstream s; - if (length > MaxSizeLoadEntry) + + if (!forceLoad) { - 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 (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 : @@ -1411,7 +1520,7 @@ void Document::LoadDocEntry(DocEntry *entry) } /** - * \brief Find the value Length of the passed Header Entry + * \brief Find the value Length of the passed Doc Entry * @param entry Header Entry whose length of the value shall be loaded. */ void Document::FindDocEntryLength( DocEntry *entry ) @@ -1422,7 +1531,8 @@ void Document::FindDocEntryLength( DocEntry *entry ) if ( Filetype == ExplicitVR && !entry->IsImplicitVR() ) { - if ( vr == "OB" || vr == "OW" || vr == "SQ" || vr == "UT" || vr == "UN" ) + if ( vr == "OB" || vr == "OW" || vr == "SQ" || vr == "UT" + /*|| vr == "UN"*/ ) { // The following reserved two bytes (see PS 3.5-2003, section // "7.1.2 Data element structure with explicit vr", p 27) must be @@ -1444,6 +1554,9 @@ void Document::FindDocEntryLength( DocEntry *entry ) // chance to get the pixels by deciding the element goes // until the end of the file. Hence we artificially fix the // the length and proceed. + gdcmWarningMacro( " Computing the length failed for " << + entry->GetKey() <<" in " <tellg(); Fp->seekg(0L,std::ios::end); @@ -1585,7 +1698,7 @@ std::string Document::FindDocEntryVR() * and the taken VR. If they are different, the header entry is * updated with the new VR. * @param vr Dicom Value Representation - * @return false if the VR is incorrect of if the VR isn't referenced + * @return false if the VR is incorrect or if the VR isn't referenced * otherwise, it returns true */ bool Document::CheckDocEntryVR(VRKey vr) @@ -2071,7 +2184,7 @@ bool Document::CheckSwap() Filetype = ACR; return true; default : - gdcmWarningMacro( "ACR/NEMA unfound swap info (Really hopeless !)"); + gdcmWarningMacro("ACR/NEMA unfound swap info (Really hopeless !)"); Filetype = Unknown; return false; } @@ -2104,7 +2217,7 @@ void Document::SwitchByteSwapCode() } /** - * \brief during parsing, Header Elements too long are not loaded in memory + * \brief during parsing, Header Elements too long are not loaded in memory * @param newSize new size */ void Document::SetMaxSizeLoadEntry(long newSize) @@ -2138,7 +2251,7 @@ DocEntry *Document::ReadNextDocEntry() group = ReadInt16(); elem = ReadInt16(); } - catch ( FormatError e ) + catch ( FormatError ) { // We reached the EOF (or an error occured) therefore // header parsing has to be considered as finished. @@ -2158,7 +2271,15 @@ DocEntry *Document::ReadNextDocEntry() if ( vr == GDCM_UNKNOWN ) { if ( elem == 0x0000 ) // Group Length + { realVR = "UL"; // must be UL + } + else if (group%2 == 1 && (elem >= 0x0010 && elem <=0x00ff )) + { + // DICOM PS 3-5 7.8.1 a) states that those + // (gggg-0010->00FF where gggg is odd) attributes have to be LO + realVR = "LO"; + } else { DictEntry *dictEntry = GetDictEntry(group,elem); @@ -2173,9 +2294,9 @@ DocEntry *Document::ReadNextDocEntry() if ( Global::GetVR()->IsVROfSequence(realVR) ) newEntry = NewSeqEntry(group, elem); else if ( Global::GetVR()->IsVROfStringRepresentable(realVR) ) - newEntry = NewValEntry(group, elem,vr); + newEntry = NewValEntry(group, elem, realVR); else - newEntry = NewBinEntry(group, elem,vr); + newEntry = NewBinEntry(group, elem, realVR); if ( vr == GDCM_UNKNOWN ) { @@ -2199,7 +2320,7 @@ DocEntry *Document::ReadNextDocEntry() { FindDocEntryLength(newEntry); } - catch ( FormatError e ) + catch ( FormatError ) { // Call it quits delete newEntry; @@ -2207,7 +2328,7 @@ DocEntry *Document::ReadNextDocEntry() } newEntry->SetOffset(Fp->tellg()); - + return newEntry; }