-/**
- * \brief Find the Value Representation of the current Dicom Element.
- * @param entry
- */
-void gdcmDocument::FindDocEntryVR( gdcmDocEntry *entry )
-{
- if ( Filetype != gdcmExplicitVR )
- {
- return;
- }
-
- char vr[3];
-
- long positionOnEntry = ftell(Fp);
- // Warning: we believe this is explicit VR (Value Representation) because
- // we used a heuristic that found "UL" in the first tag. Alas this
- // doesn't guarantee that all the tags will be in explicit VR. In some
- // cases (see e-film filtered files) one finds implicit VR tags mixed
- // within an explicit VR file. Hence we make sure the present tag
- // is in explicit VR and try to fix things if it happens not to be
- // the case.
-
- fread (vr, (size_t)2,(size_t)1, Fp);
- vr[2] = 0;
-
- if( !CheckDocEntryVR(entry, vr) )
- {
- fseek(Fp, positionOnEntry, SEEK_SET);
- // When this element is known in the dictionary we shall use, e.g. for
- // the semantics (see the usage of IsAnInteger), the VR proposed by the
- // dictionary entry. Still we have to flag the element as implicit since
- // we know now our assumption on expliciteness is not furfilled.
- // avoid .
- if ( entry->IsVRUnknown() )
- {
- entry->SetVR("Implicit");
- }
- entry->SetImplicitVR();
- }
-}
-
-/**
- * \brief Check the correspondance between the VR of the header entry
- * and the taken VR. If they are different, the header entry is
- * updated with the new VR.
- * @param entry Header Entry to check
- * @param vr Dicom Value Representation
- * @return false if the VR is incorrect of if the VR isn't referenced
- * otherwise, it returns true
-*/
-bool gdcmDocument::CheckDocEntryVR(gdcmDocEntry *entry, gdcmVRKey vr)
-{
- char msg[100]; // for sprintf
- bool realExplicit = true;
-
- // Assume we are reading a falsely explicit VR file i.e. we reached
- // a tag where we expect reading a VR but are in fact we read the
- // first to bytes of the length. Then we will interogate (through find)
- // the dicom_vr dictionary with oddities like "\004\0" which crashes
- // both GCC and VC++ implementations of the STL map. Hence when the
- // expected VR read happens to be non-ascii characters we consider
- // we hit falsely explicit VR tag.
-
- if ( !isalpha(vr[0]) && !isalpha(vr[1]) )
- {
- realExplicit = false;
- }
-
- // CLEANME searching the dicom_vr at each occurence is expensive.
- // PostPone this test in an optional integrity check at the end
- // of parsing or only in debug mode.
- if ( realExplicit && !gdcmGlobal::GetVR()->Count(vr) )
- {
- realExplicit = false;
- }
-
- if ( !realExplicit )
- {
- // We thought this was explicit VR, but we end up with an
- // implicit VR tag. Let's backtrack.
- sprintf(msg,"Falsely explicit vr file (%04x,%04x)\n",
- entry->GetGroup(), entry->GetElement());
- dbg.Verbose(1, "gdcmDocument::FindVR: ",msg);
-
- if( entry->GetGroup() % 2 && entry->GetElement() == 0x0000)
- {
- // Group length is UL !
- gdcmDictEntry* newEntry = NewVirtualDictEntry(
- entry->GetGroup(), entry->GetElement(),
- "UL", "FIXME", "Group Length");
- entry->SetDictEntry( newEntry );
- }
- return false;
- }
-
- if ( entry->IsVRUnknown() )
- {
- // When not a dictionary entry, we can safely overwrite the VR.
- if( entry->GetElement() == 0x0000 )
- {
- // Group length is UL !
- entry->SetVR("UL");
- }
- else
- {
- entry->SetVR(vr);
- }
- }
- else if ( entry->GetVR() != vr )
- {
- // The VR present in the file and the dictionary disagree. We assume
- // the file writer knew best and use the VR of the file. Since it would
- // be unwise to overwrite the VR of a dictionary (since it would
- // compromise it's next user), we need to clone the actual DictEntry
- // and change the VR for the read one.
- gdcmDictEntry* newEntry = NewVirtualDictEntry(
- entry->GetGroup(), entry->GetElement(),
- vr, "FIXME", entry->GetName());
- entry->SetDictEntry(newEntry);
- }
-
- return true;
-}
-
-/**
- * \brief Get the transformed value of the header entry. The VR value
- * is used to define the transformation to operate on the value
- * \warning NOT end user intended method !
- * @param entry entry to tranform
- * @return Transformed entry value
- */
-std::string gdcmDocument::GetDocEntryValue(gdcmDocEntry *entry)
-{
- if ( IsDocEntryAnInteger(entry) && entry->IsImplicitVR() )
- {
- std::string val = ((gdcmValEntry *)entry)->GetValue();
- std::string vr = entry->GetVR();
- uint32_t length = entry->GetLength();
- std::ostringstream s;
- int nbInt;
-
- // When short integer(s) are expected, read and convert the following
- // n * 2 bytes properly i.e. as a multivaluated strings
- // (each single value is separated fromthe next one by '\'
- // as usual for standard multivaluated filels
- // Elements with Value Multiplicity > 1
- // contain a set of short integers (not a single one)
-
- if( vr == "US" || vr == "SS" )
- {
- uint16_t newInt16;
-
- nbInt = length / 2;
- for (int i=0; i < nbInt; i++)
- {
- if( i != 0 )
- {
- s << '\\';
- }
- newInt16 = ( val[2*i+0] & 0xFF ) + ( ( val[2*i+1] & 0xFF ) << 8);
- newInt16 = SwapShort( newInt16 );
- s << newInt16;
- }
- }
-
- // When integer(s) are expected, read and convert the following
- // n * 4 bytes properly i.e. as a multivaluated strings
- // (each single value is separated fromthe next one by '\'
- // as usual for standard multivaluated filels
- // Elements with Value Multiplicity > 1
- // contain a set of integers (not a single one)
- else if( vr == "UL" || vr == "SL" )
- {
- uint32_t newInt32;
-
- nbInt = length / 4;
- for (int i=0; i < nbInt; i++)
- {
- if( i != 0)
- {
- s << '\\';
- }
- newInt32 = ( val[4*i+0] & 0xFF )
- + (( val[4*i+1] & 0xFF ) << 8 )
- + (( val[4*i+2] & 0xFF ) << 16 )
- + (( val[4*i+3] & 0xFF ) << 24 );
- newInt32 = SwapLong( newInt32 );
- s << newInt32;
- }
- }
-#ifdef GDCM_NO_ANSI_STRING_STREAM
- s << std::ends; // to avoid oddities on Solaris
-#endif //GDCM_NO_ANSI_STRING_STREAM
- return s.str();
- }
-
- return ((gdcmValEntry *)entry)->GetValue();
-}
-
-/**
- * \brief Get the reverse transformed value of the header entry. The VR
- * value is used to define the reverse transformation to operate on
- * the value
- * \warning NOT end user intended method !
- * @param entry Entry to reverse transform
- * @return Reverse transformed entry value
- */
-std::string gdcmDocument::GetDocEntryUnvalue(gdcmDocEntry* entry)
-{
- if ( IsDocEntryAnInteger(entry) && entry->IsImplicitVR() )
- {
- std::string vr = entry->GetVR();
- std::vector<std::string> tokens;
- std::ostringstream s;
-
- if ( vr == "US" || vr == "SS" )
- {
- uint16_t newInt16;
-
- tokens.erase( tokens.begin(), tokens.end()); // clean any previous value
- Tokenize (((gdcmValEntry *)entry)->GetValue(), tokens, "\\");
- for (unsigned int i=0; i<tokens.size(); i++)
- {
- newInt16 = atoi(tokens[i].c_str());
- s << ( newInt16 & 0xFF )
- << (( newInt16 >> 8 ) & 0xFF );
- }
- tokens.clear();
- }
- if ( vr == "UL" || vr == "SL")
- {
- uint32_t newInt32;
-
- tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
- Tokenize (((gdcmValEntry *)entry)->GetValue(), tokens, "\\");
- for (unsigned int i=0; i<tokens.size();i++)
- {
- newInt32 = atoi(tokens[i].c_str());
- s << (char)( newInt32 & 0xFF )
- << (char)(( newInt32 >> 8 ) & 0xFF )
- << (char)(( newInt32 >> 16 ) & 0xFF )
- << (char)(( newInt32 >> 24 ) & 0xFF );
- }
- tokens.clear();
- }
-
-#ifdef GDCM_NO_ANSI_STRING_STREAM
- s << std::ends; // to avoid oddities on Solaris
-#endif //GDCM_NO_ANSI_STRING_STREAM
- return s.str();
- }
-
- return ((gdcmValEntry *)entry)->GetValue();
-}
-
-/**
- * \brief Skip a given Header Entry
- * \warning NOT end user intended method !
- * @param entry entry to skip
- */
-void gdcmDocument::SkipDocEntry(gdcmDocEntry *entry)
-{
- SkipBytes(entry->GetLength());
-}
-
-/**
- * \brief Skips to the begining of the next Header Entry
- * \warning NOT end user intended method !
- * @param entry entry to skip
- */
-void gdcmDocument::SkipToNextDocEntry(gdcmDocEntry *entry)
-{
- fseek(Fp, (long)(entry->GetOffset()), SEEK_SET);
- fseek(Fp, (long)(entry->GetReadLength()), SEEK_CUR);
-}
-
-/**
- * \brief When the length of an element value is obviously wrong (because
- * the parser went Jabberwocky) one can hope improving things by
- * applying some heuristics.
- * @param entry entry to check
- * @param foundLength fist assumption about length
- */
-void gdcmDocument::FixDocEntryFoundLength(gdcmDocEntry *entry,
- uint32_t foundLength)
-{
- entry->SetReadLength( foundLength ); // will be updated only if a bug is found
- if ( foundLength == 0xffffffff)
- {
- foundLength = 0;
- }
-
- uint16_t gr = entry->GetGroup();
- uint16_t el = entry->GetElement();
-
- if ( foundLength % 2)
- {
- std::ostringstream s;
- s << "Warning : Tag with uneven length "
- << foundLength
- << " in x(" << std::hex << gr << "," << el <<")" << std::dec;
- dbg.Verbose(0, s.str().c_str());
- }
-
- //////// Fix for some naughty General Electric images.
- // Allthough not recent many such GE corrupted images are still present
- // on Creatis hard disks. Hence this fix shall remain when such images
- // are no longer in user (we are talking a few years, here)...
- // Note: XMedCom probably uses such a trick since it is able to read
- // those pesky GE images ...
- if ( foundLength == 13)
- {
- // Only happens for this length !
- if ( entry->GetGroup() != 0x0008
- || ( entry->GetElement() != 0x0070
- && entry->GetElement() != 0x0080 ) )
- {
- foundLength = 10;
- entry->SetReadLength(10); /// \todo a bug is to be fixed !?
- }
- }
-
- //////// Fix for some brain-dead 'Leonardo' Siemens images.
- // Occurence of such images is quite low (unless one leaves close to a
- // 'Leonardo' source. Hence, one might consider commenting out the
- // following fix on efficiency reasons.
- else if ( entry->GetGroup() == 0x0009
- && ( entry->GetElement() == 0x1113
- || entry->GetElement() == 0x1114 ) )
- {
- foundLength = 4;
- entry->SetReadLength(4); /// \todo a bug is to be fixed !?
- }
-
- else if ( entry->GetVR() == "SQ" )
- {
- foundLength = 0; // ReadLength is unchanged
- }
-
- //////// We encountered a 'delimiter' element i.e. a tag of the form
- // "fffe|xxxx" which is just a marker. Delimiters length should not be
- // taken into account.
- else if( entry->GetGroup() == 0xfffe )
- {
- // According to the norm, fffe|0000 shouldn't exist. BUT the Philips
- // image gdcmData/gdcm-MR-PHILIPS-16-Multi-Seq.dcm happens to
- // causes extra troubles...
- if( entry->GetElement() != 0x0000 )
- {
- foundLength = 0;
- }
- }
-
- entry->SetUsableLength(foundLength);
-}
-
-/**
- * \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.
- */
-bool gdcmDocument::IsDocEntryAnInteger(gdcmDocEntry *entry)
-{
- uint16_t element = entry->GetElement();
- uint16_t group = entry->GetGroup();
- std::string vr = entry->GetVR();
- uint32_t length = entry->GetLength();
-
- // When we have some semantics on the element we just read, and if we
- // a priori know we are dealing with an integer, then we shall be
- // able to swap it's element value properly.
- if ( element == 0 ) // This is the group length of the group
- {
- if ( length == 4 )
- {
- return true;
- }
- else
- {
- // Allthough this should never happen, still some images have a
- // corrupted group length [e.g. have a glance at offset x(8336) of
- // gdcmData/gdcm-MR-PHILIPS-16-Multi-Seq.dcm].
- // Since for dicom compliant and well behaved headers, the present
- // test is useless (and might even look a bit paranoid), when we
- // encounter such an ill-formed image, we simply display a warning
- // message and proceed on parsing (while crossing fingers).
- std::ostringstream s;
- int filePosition = ftell(Fp);
- s << "Erroneous Group Length element length on : (" \
- << std::hex << group << " , " << element
- << ") -before- position x(" << filePosition << ")"
- << "lgt : " << length;
- dbg.Verbose(0, "gdcmDocument::IsDocEntryAnInteger", s.str().c_str() );
- }
- }
-
- if ( vr == "UL" || vr == "US" || vr == "SL" || vr == "SS" )
- {
- return true;
- }
-
- return false;
-}
-