- return ( IsGivenTransferSyntax(UI1_2_840_10008_1_2_4_55)
- || IsGivenTransferSyntax(UI1_2_840_10008_1_2_4_57)
- || IsGivenTransferSyntax(UI1_2_840_10008_1_2_4_70) );
-}
-
-/**
- * \brief Determines if the Transfer Syntax was already encountered
- * and if it corresponds to a JPEG2000 one
- * @return True when JPEG2000 (Lossly or LossLess) found. False in all
- * other cases.
- */
-bool Document::IsJPEG2000()
-{
- return ( IsGivenTransferSyntax(UI1_2_840_10008_1_2_4_90)
- || IsGivenTransferSyntax(UI1_2_840_10008_1_2_4_91) );
-}
-
-/**
- * \brief Determines if the Transfer Syntax corresponds to any form
- * of Jpeg encoded Pixel data.
- * @return True when any form of JPEG found. False otherwise.
- */
-bool Document::IsJPEGTransferSyntax()
-{
- return ( IsJPEGBaseLineProcess1TransferSyntax()
- || IsJPEGExtendedProcess2_4TransferSyntax()
- || IsJPEGExtendedProcess3_5TransferSyntax()
- || IsJPEGSpectralSelectionProcess6_8TransferSyntax()
- || IsJPEGLossless()
- || IsJPEG2000() );
-}
-
-
-/**
- * \brief Determines if the Transfer Syntax corresponds to encapsulated
- * of encoded Pixel Data (as opposed to native).
- * @return True when encapsulated. False when native.
- */
-bool Document::IsEncapsulateTransferSyntax()
-{
- return ( IsJPEGTransferSyntax()
- || IsRLELossLessTransferSyntax() );
-}
-
-/**
- * \brief Predicate for dicom version 3 file.
- * @return True when the file is a dicom version 3.
- */
-bool Document::IsDicomV3()
-{
- // Checking if Transfert Syntax exists is enough
- // Anyway, it's to late check if the 'Preamble' was found ...
- // And ... would it be a rich idea to check ?
- // (some 'no Preamble' DICOM images exist !)
- return GetDocEntryByNumber(0x0002, 0x0010) != NULL;
-}
-
-/**
- * \brief returns the File Type
- * (ACR, ACR_LIBIDO, ExplicitVR, ImplicitVR, Unknown)
- * @return the FileType code
- */
-FileType Document::GetFileType()
-{
- return Filetype;
-}
-
-/**
- * \brief Tries to open the file \ref Document::Filename and
- * checks the preamble when existing.
- * @return The FILE pointer on success.
- */
-std::ifstream* Document::OpenFile()
-{
- Fp = new std::ifstream(Filename.c_str(), std::ios::in | std::ios::binary);
-
- if(!Fp)
- {
- dbg.Verbose( 0,
- "Document::OpenFile cannot open file: ",
- Filename.c_str());
- return 0;
- }
-
- uint16_t zero;
- Fp->read((char*)&zero, (size_t)2 );
-
- //ACR -- or DICOM with no Preamble --
- if( zero == 0x0008 || zero == 0x0800 || zero == 0x0002 || zero == 0x0200 )
- {
- return Fp;
- }
-
- //DICOM
- Fp->seekg(126L, std::ios_base::cur);
- char dicm[4];
- Fp->read(dicm, (size_t)4);
- if( memcmp(dicm, "DICM", 4) == 0 )
- {
- return Fp;
- }
-
- Fp->close();
- dbg.Verbose( 0,
- "Document::OpenFile not DICOM/ACR (missing preamble)",
- Filename.c_str());
-
- return 0;
-}
-
-/**
- * \brief closes the file
- * @return TRUE if the close was successfull
- */
-bool Document::CloseFile()
-{
- Fp->close();
- delete Fp;
- Fp = 0;
-
- return true; //FIXME how do we detect a non-close ifstream ?
-}
-
-/**
- * \brief Writes in a file all the Header Entries (Dicom Elements)
- * @param fp file pointer on an already open file
- * @param filetype Type of the File to be written
- * (ACR-NEMA, ExplicitVR, ImplicitVR)
- * \return Always true.
- */
-void Document::Write(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
-
- if (filetype == ImplicitVR)
- {
- std::string implicitVRTransfertSyntax = UI1_2_840_10008_1_2;
- ReplaceOrCreateByNumber(implicitVRTransfertSyntax,0x0002, 0x0010);
-
- /// \todo Refer to standards on page 21, chapter 6.2
- /// "Value representation": values with a VR of UI shall be
- /// padded with a single trailing null
- /// in the following case we have to padd manually with a 0
-
- SetEntryLengthByNumber(18, 0x0002, 0x0010);
- }
-
- if (filetype == ExplicitVR)
- {
- std::string explicitVRTransfertSyntax = UI1_2_840_10008_1_2_1;
- ReplaceOrCreateByNumber(explicitVRTransfertSyntax,0x0002, 0x0010);
-
- /// \todo Refer to standards on page 21, chapter 6.2
- /// "Value representation": values with a VR of UI shall be
- /// padded with a single trailing null
- /// Dans le cas suivant on doit pader manuellement avec un 0
-
- SetEntryLengthByNumber(20, 0x0002, 0x0010);
- }
-
-/**
- * \todo rewrite later, if really usefull
- * - 'Group Length' element is optional in DICOM
- * - but un-updated odd groups lengthes can causes pb
- * (xmedcon breaker)
- *
- * if ( (filetype == ImplicitVR) || (filetype == ExplicitVR) )
- * UpdateGroupLength(false,filetype);
- * if ( filetype == ACR)
- * UpdateGroupLength(true,ACR);
- */
-
- ElementSet::Write(fp, filetype); // This one is recursive
-
-}
-
-/**
- * \brief Modifies the value of a given Header Entry (Dicom Element)
- * when it exists. Create it with the given value when unexistant.
- * @param value (string) Value to be set
- * @param group Group number of the Entry
- * @param elem Element number of the Entry
- * @param VR V(alue) R(epresentation) of the Entry -if private Entry-
- * \return pointer to the modified/created Header Entry (NULL when creation
- * failed).
- */
-ValEntry* Document::ReplaceOrCreateByNumber(
- std::string const & value,
- uint16_t group,
- uint16_t elem,
- std::string const & VR )
-{
- ValEntry* valEntry = 0;
- DocEntry* currentEntry = GetDocEntryByNumber( group, elem);
-
- if (!currentEntry)
- {
- // check if (group,element) DictEntry exists
- // if it doesn't, create an entry in DictSet::VirtualEntry
- // and use it
-
- // Find out if the tag we received is in the dictionaries:
- Dict *pubDict = Global::GetDicts()->GetDefaultPubDict();
- DictEntry* dictEntry = pubDict->GetDictEntryByNumber(group, elem);
- if (!dictEntry)
- {
- currentEntry = NewDocEntryByNumber(group, elem,VR);
- }
- else
- {
- currentEntry = NewDocEntryByNumber(group, elem);
- }
-
- if (!currentEntry)
- {
- dbg.Verbose(0, "Document::ReplaceOrCreateByNumber: call to"
- " NewDocEntryByNumber failed.");
- return NULL;
- }
- valEntry = new ValEntry(currentEntry);
- if ( !AddEntry(valEntry))
- {
- dbg.Verbose(0, "Document::ReplaceOrCreateByNumber: AddEntry"
- " failed allthough this is a creation.");
- }
- }
- else
- {
- valEntry = dynamic_cast< ValEntry* >(currentEntry);
- if ( !valEntry ) // Euuuuh? It wasn't a ValEntry
- // then we change it to a ValEntry ?
- // Shouldn't it be considered as an error ?
- {
- // We need to promote the DocEntry to a ValEntry:
- valEntry = new ValEntry(currentEntry);
- if (!RemoveEntry(currentEntry))
- {
- dbg.Verbose(0, "Document::ReplaceOrCreateByNumber: removal"
- " of previous DocEntry failed.");
- return NULL;
- }
- if ( !AddEntry(valEntry))
- {
- dbg.Verbose(0, "Document::ReplaceOrCreateByNumber: adding"
- " promoted ValEntry failed.");
- return NULL;
- }
- }
- }
-
- SetEntryByNumber(value, group, elem);
-
- return valEntry;
-}
-
-/*
- * \brief Modifies the value of a given Header Entry (Dicom Element)
- * when it exists. Create it with the given value when unexistant.
- * @param binArea (binary) value to be set
- * @param Group Group number of the Entry
- * @param Elem Element number of the Entry
- * \return pointer to the modified/created Header Entry (NULL when creation
- * failed).
- */
-BinEntry* Document::ReplaceOrCreateByNumber(
- uint8_t* binArea,
- int lgth,
- uint16_t group,
- uint16_t elem,
- std::string const& VR )
-{
- BinEntry* binEntry = 0;
- DocEntry* currentEntry = GetDocEntryByNumber( group, elem);
- if (!currentEntry)
- {
-
- // check if (group,element) DictEntry exists
- // if it doesn't, create an entry in DictSet::VirtualEntry
- // and use it
-
- // Find out if the tag we received is in the dictionaries:
- Dict *pubDict = Global::GetDicts()->GetDefaultPubDict();
- DictEntry *dictEntry = pubDict->GetDictEntryByNumber(group, elem);
-
- if (!dictEntry)
- {
- currentEntry = NewDocEntryByNumber(group, elem, VR);
- }
- else
- {
- currentEntry = NewDocEntryByNumber(group, elem);
- }
- if (!currentEntry)
- {
- dbg.Verbose(0, "Document::ReplaceOrCreateByNumber: call to"
- " NewDocEntryByNumber failed.");
- return NULL;
- }
- binEntry = new BinEntry(currentEntry);
- if ( !AddEntry(binEntry))
- {
- dbg.Verbose(0, "Document::ReplaceOrCreateByNumber: AddEntry"
- " failed allthough this is a creation.");
- }
- }
- else
- {
- binEntry = dynamic_cast< BinEntry* >(currentEntry);
- if ( !binEntry ) // Euuuuh? It wasn't a BinEntry
- // then we change it to a BinEntry ?
- // Shouldn't it be considered as an error ?
- {
- // We need to promote the DocEntry to a BinEntry:
- binEntry = new BinEntry(currentEntry);
- if (!RemoveEntry(currentEntry))
- {
- dbg.Verbose(0, "Document::ReplaceOrCreateByNumber: removal"
- " of previous DocEntry failed.");
- return NULL;
- }
- if ( !AddEntry(binEntry))
- {
- dbg.Verbose(0, "Document::ReplaceOrCreateByNumber: adding"
- " promoted BinEntry failed.");
- return NULL;
- }
- }
- }
-
- SetEntryByNumber(binArea, lgth, group, elem);
-
- return binEntry;
-}
-
-
-/*
- * \brief Modifies the value of a given Header Entry (Dicom Element)
- * when it exists. Create it when unexistant.
- * @param Group Group number of the Entry
- * @param Elem Element number of the Entry
- * \return pointer to the modified/created SeqEntry (NULL when creation
- * failed).
- */
-SeqEntry* Document::ReplaceOrCreateByNumber(
- uint16_t group,
- uint16_t elem)
-{
- SeqEntry* b = 0;
- DocEntry* a = GetDocEntryByNumber( group, elem);
- if (!a)
- {
- a = NewSeqEntryByNumber(group, elem);
- if (!a)
- {
- return 0;
- }
-
- b = new SeqEntry(a, 1); // FIXME : 1 (Depth)
- AddEntry(b);
- }
- return b;
-}
-
-/**
- * \brief Set a new value if the invoked element exists
- * Seems to be useless !!!
- * @param value new element value
- * @param group group number of the Entry
- * @param elem element number of the Entry
- * \return boolean
- */
-bool Document::ReplaceIfExistByNumber(std::string const & value,
- uint16_t group, uint16_t elem )
-{
- SetEntryByNumber(value, group, elem);
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Protected
-
-/**
- * \brief Checks if a given Dicom Element exists within the H table
- * @param group Group number of the searched Dicom Element
- * @param element Element number of the searched Dicom Element
- * @return true is found
- */
-bool Document::CheckIfEntryExistByNumber(uint16_t group, uint16_t element )
-{
- const std::string &key = DictEntry::TranslateToKey(group, element );
- return TagHT.count(key);
-}
-
-/**
- * \brief Searches within Header Entries (Dicom Elements) parsed with
- * the public and private dictionaries
- * for the element value of a given tag.
- * \warning Don't use any longer : use GetPubEntryByName
- * @param tagName name of the searched element.
- * @return Corresponding element value when it exists,
- * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
- */
-std::string Document::GetEntryByName(TagName const& tagName)
-{
- DictEntry* dictEntry = RefPubDict->GetDictEntryByName(tagName);
- if( !dictEntry )
- {
- return GDCM_UNFOUND;
- }
-
- return GetEntryByNumber(dictEntry->GetGroup(),dictEntry->GetElement());
-}
-
-/**
- * \brief Searches within Header Entries (Dicom Elements) parsed with
- * the public and private dictionaries
- * for the element value representation of a given tag.
- *
- * Obtaining the VR (Value Representation) might be needed by caller
- * to convert the string typed content to caller's native type
- * (think of C++ vs Python). The VR is actually of a higher level
- * of semantics than just the native C++ type.
- * @param tagName name of the searched element.
- * @return Corresponding element value representation when it exists,
- * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
- */
-std::string Document::GetEntryVRByName(TagName const& tagName)
-{
- DictEntry *dictEntry = RefPubDict->GetDictEntryByName(tagName);
- if( dictEntry == NULL)
- {
- return GDCM_UNFOUND;
- }
-
- DocEntry* elem = GetDocEntryByNumber(dictEntry->GetGroup(),
- dictEntry->GetElement());
- return elem->GetVR();
-}
-
-/**
- * \brief Searches within Header Entries (Dicom Elements) parsed with
- * the public and private dictionaries
- * for the element value representation of a given tag.
- * @param group Group number of the searched tag.
- * @param element Element number of the searched tag.
- * @return Corresponding element value representation when it exists,
- * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
- */
-std::string Document::GetEntryByNumber(uint16_t group, uint16_t element)
-{
- TagKey key = DictEntry::TranslateToKey(group, element);
- /// \todo use map methods, instead of multimap JPR
- if ( !TagHT.count(key))
- {
- return GDCM_UNFOUND;
- }
-
- return ((ValEntry *)TagHT.find(key)->second)->GetValue();
-}
-
-/**
- * \brief Searches within Header Entries (Dicom Elements) parsed with
- * the public and private dictionaries
- * for the element value representation of a given tag..
- *
- * Obtaining the VR (Value Representation) might be needed by caller
- * to convert the string typed content to caller's native type
- * (think of C++ vs Python). The VR is actually of a higher level
- * of semantics than just the native C++ type.
- * @param group Group number of the searched tag.
- * @param element Element number of the searched tag.
- * @return Corresponding element value representation when it exists,
- * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
- */
-std::string Document::GetEntryVRByNumber(uint16_t group, uint16_t element)
-{
- DocEntry* elem = GetDocEntryByNumber(group, element);
- if ( !elem )
- {
- return GDCM_UNFOUND;
- }
- return elem->GetVR();
-}
-
-/**
- * \brief Searches within Header Entries (Dicom Elements) parsed with
- * the public and private dictionaries
- * for the value length of a given tag..
- * @param group Group number of the searched tag.
- * @param element Element number of the searched tag.
- * @return Corresponding element length; -2 if not found
- */
-int Document::GetEntryLengthByNumber(uint16_t group, uint16_t element)
-{
- DocEntry* elem = GetDocEntryByNumber(group, element);
- if ( !elem )
- {
- return -2; //magic number
- }
- return elem->GetLength();
-}
-/**
- * \brief Sets the value (string) of the Header Entry (Dicom Element)
- * @param content string value of the Dicom Element
- * @param tagName name of the searched Dicom Element.
- * @return true when found
- */
-bool Document::SetEntryByName(std::string const & content,std::string const & tagName)
-{
- DictEntry *dictEntry = RefPubDict->GetDictEntryByName(tagName);
- if( !dictEntry )
- {
- return false;
- }
-
- return SetEntryByNumber(content,dictEntry->GetGroup(),
- dictEntry->GetElement());
-}
-
-/**
- * \brief Accesses an existing DocEntry (i.e. a Dicom Element)
- * through it's (group, element) and modifies it's content with
- * the given value.
- * @param content new value (string) to substitute with
- * @param group group number of the Dicom Element to modify
- * @param element element number of the Dicom Element to modify
- */
-bool Document::SetEntryByNumber(std::string const& content,
- uint16_t group,
- uint16_t element)
-{
- int c;
- int l;
-
- ValEntry* valEntry = GetValEntryByNumber(group, element);
- if (!valEntry )
- {
- dbg.Verbose(0, "Document::SetEntryByNumber: no corresponding",
- " ValEntry (try promotion first).");
- return false;
- }
- // Non even content must be padded with a space (020H)...
- std::string finalContent = content;
- if( finalContent.length() % 2 )
- {
- finalContent += '\0'; // ... therefore we padd with (000H) .!?!
- }
- valEntry->SetValue(finalContent);
-
- // Integers have a special treatement for their length:
-
- l = finalContent.length();
- if ( l != 0) // To avoid to be cheated by 'zero length' integers
- {
- VRKey vr = valEntry->GetVR();
- if( vr == "US" || vr == "SS" )
- {
- // for multivaluated items
- c = Util::CountSubstring(content, "\\") + 1;
- l = c*2;
- }
- else if( vr == "UL" || vr == "SL" )
- {
- // for multivaluated items
- c = Util::CountSubstring(content, "\\") + 1;
- l = c*4;;
- }
- }
- valEntry->SetLength(l);
- return true;
-}
-
-/**
- * \brief Accesses an existing DocEntry (i.e. a Dicom Element)
- * through it's (group, element) and modifies it's content with
- * the given value.
- * @param content new value (void* -> uint8_t*) to substitute with
- * @param lgth new value length
- * @param group group number of the Dicom Element to modify
- * @param element element number of the Dicom Element to modify
- */
-bool Document::SetEntryByNumber(uint8_t*content,
- int lgth,
- uint16_t group,
- uint16_t element)
-{
- (void)lgth; //not used
- TagKey key = DictEntry::TranslateToKey(group, element);
- if ( !TagHT.count(key))
- {
- return false;
- }
-
-/* Hope Binary field length is *never* wrong
- if(lgth%2) // Non even length are padded with a space (020H).
- {
- lgth++;
- //content = content + '\0'; // fing a trick to enlarge a binary field?
- }
-*/
- BinEntry* a = (BinEntry *)TagHT[key];
- a->SetBinArea(content);
- a->SetLength(lgth);
- a->SetValue(GDCM_BINLOADED);
-
- return true;
-}
-
-/**
- * \brief Accesses an existing DocEntry (i.e. a Dicom Element)
- * in the PubDocEntrySet of this instance
- * through it's (group, element) and modifies it's length with
- * the given value.
- * \warning Use with extreme caution.
- * @param l new length to substitute with
- * @param group group number of the Entry to modify
- * @param element element number of the Entry to modify
- * @return true on success, false otherwise.
- */
-bool Document::SetEntryLengthByNumber(uint32_t l,
- uint16_t group,
- uint16_t element)
-{
- /// \todo use map methods, instead of multimap JPR
- TagKey key = DictEntry::TranslateToKey(group, element);
- if ( !TagHT.count(key) )
- {
- return false;
- }
- if ( l % 2 )
- {
- l++; // length must be even
- }
- ( ((TagHT.equal_range(key)).first)->second )->SetLength(l);
-
- return true ;
-}
-
-/**
- * \brief Gets (from Header) the offset of a 'non string' element value
- * (LoadElementValues has already be executed)
- * @param group group number of the Entry
- * @param elem element number of the Entry
- * @return File Offset of the Element Value
- */
-size_t Document::GetEntryOffsetByNumber(uint16_t group, uint16_t elem)
-{
- DocEntry* entry = GetDocEntryByNumber(group, elem);
- if (!entry)
- {
- dbg.Verbose(1, "Document::GetDocEntryByNumber: no entry present.");
- return 0;
- }
- return entry->GetOffset();
-}
-
-/**
- * \brief Gets (from Header) a 'non string' element value
- * (LoadElementValues has already be executed)
- * @param group group number of the Entry
- * @param elem element number of the Entry
- * @return Pointer to the 'non string' area
- */
-void* Document::GetEntryBinAreaByNumber(uint16_t group, uint16_t elem)
-{
- DocEntry* entry = GetDocEntryByNumber(group, elem);
- if (!entry)
- {
- dbg.Verbose(1, "Document::GetDocEntryByNumber: no entry");
- return 0;
- }
- if ( BinEntry* binEntry = dynamic_cast<BinEntry*>(entry) )
- {
- return binEntry->GetBinArea();
- }
-
- return 0;
-}
-
-/**
- * \brief Loads (from disk) the element content
- * when a string is not suitable
- * @param group group number of the Entry
- * @param elem element number of the Entry
- */
-void* Document::LoadEntryBinArea(uint16_t group, uint16_t elem)
-{
- DocEntry *docElement = GetDocEntryByNumber(group, elem);
- if ( !docElement )
- {
- return NULL;
- }
- size_t o =(size_t)docElement->GetOffset();
- Fp->seekg( o, std::ios_base::beg);
- size_t l = docElement->GetLength();
- uint8_t* a = new uint8_t[l];
- if(!a)
- {
- dbg.Verbose(0, "Document::LoadEntryBinArea cannot allocate a");
- return NULL;
- }
- Fp->read((char*)a, l);
- if( Fp->fail() || Fp->eof() )//Fp->gcount() == 1
- {
- delete[] a;
- return NULL;
- }
- /// \todo Drop any already existing void area! JPR
- if( !SetEntryBinAreaByNumber( a, group, elem ) )
- {
- dbg.Verbose(0, "Document::LoadEntryBinArea setting failed.");
- }
- return a;
-}
-/**
- * \brief Loads (from disk) the element content
- * when a string is not suitable
- * @param element Entry whose binArea is going to be loaded
- */
-void* Document::LoadEntryBinArea(BinEntry* element)
-{
- size_t o =(size_t)element->GetOffset();
- Fp->seekg(o, std::ios_base::beg);
- size_t l = element->GetLength();
- uint8_t* a = new uint8_t[l];
- if( !a )
- {
- dbg.Verbose(0, "Document::LoadEntryBinArea cannot allocate a");
- return NULL;
- }
- element->SetBinArea((uint8_t*)a);
- /// \todo check the result
- Fp->read((char*)a, l);
- if( Fp->fail() || Fp->eof()) //Fp->gcount() == 1
- {
- delete[] a;
- return NULL;
- }