+
+/**
+ * \brief anonymize a File (remove Patient's personal info passed with
+ * AddAnonymizeElement()
+ * \note You cannot Anonymize a DataEntry (to be fixed)
+ */
+bool File::AnonymizeFile()
+{
+ // If Anonymisation list is empty, let's perform some basic anonymization
+ if ( UserAnonymizeList.begin() == UserAnonymizeList.end() )
+ {
+ // If exist, replace by spaces
+ SetEntryString(" ",0x0010, 0x2154); // Telephone
+ SetEntryString(" ",0x0010, 0x1040); // Adress
+ SetEntryString(" ",0x0010, 0x0020); // Patient ID
+
+ DocEntry *patientNameHE = GetDocEntry (0x0010, 0x0010);
+
+ if ( patientNameHE ) // we replace it by Study Instance UID (why not ?)
+ {
+ std::string studyInstanceUID = GetEntryString (0x0020, 0x000d);
+ if ( studyInstanceUID != GDCM_UNFOUND )
+ {
+ SetEntryString(studyInstanceUID, 0x0010, 0x0010);
+ }
+ else
+ {
+ SetEntryString("anonymized", 0x0010, 0x0010);
+ }
+ }
+ }
+ else
+ {
+ gdcm::DocEntry *d;
+ for (ListElements::iterator it = UserAnonymizeList.begin();
+ it != UserAnonymizeList.end();
+ ++it)
+ {
+ d = GetDocEntry( (*it).Group, (*it).Elem);
+
+ if ( d == NULL)
+ continue;
+
+ if ( dynamic_cast<SeqEntry *>(d) )
+ {
+ gdcmWarningMacro( "You cannot 'Anonymize' a SeqEntry ");
+ continue;
+ }
+
+ if ( dynamic_cast<DataEntry *>(d) )
+ {
+ gdcmWarningMacro( "To 'Anonymize' a DataEntry, better use AnonymizeNoLoad (FIXME) ");
+ continue;
+ }
+ else
+ SetEntryString ((*it).Value, (*it).Group, (*it).Elem);
+ }
+}
+
+ // In order to make definitively impossible any further identification
+ // remove or replace all the stuff that contains a Date
+
+//0008 0012 DA ID Instance Creation Date
+//0008 0020 DA ID Study Date
+//0008 0021 DA ID Series Date
+//0008 0022 DA ID Acquisition Date
+//0008 0023 DA ID Content Date
+//0008 0024 DA ID Overlay Date
+//0008 0025 DA ID Curve Date
+//0008 002a DT ID Acquisition Datetime
+//0018 9074 DT ACQ Frame Acquisition Datetime
+//0018 9151 DT ACQ Frame Reference Datetime
+//0018 a002 DT ACQ Contribution Date Time
+//0020 3403 SH REL Modified Image Date (RET)
+//0032 0032 DA SDY Study Verified Date
+//0032 0034 DA SDY Study Read Date
+//0032 1000 DA SDY Scheduled Study Start Date
+//0032 1010 DA SDY Scheduled Study Stop Date
+//0032 1040 DA SDY Study Arrival Date
+//0032 1050 DA SDY Study Completion Date
+//0038 001a DA VIS Scheduled Admission Date
+//0038 001c DA VIS Scheduled Discharge Date
+//0038 0020 DA VIS Admitting Date
+//0038 0030 DA VIS Discharge Date
+//0040 0002 DA PRC Scheduled Procedure Step Start Date
+//0040 0004 DA PRC Scheduled Procedure Step End Date
+//0040 0244 DA PRC Performed Procedure Step Start Date
+//0040 0250 DA PRC Performed Procedure Step End Date
+//0040 2004 DA PRC Issue Date of Imaging Service Request
+//0040 4005 DT PRC Scheduled Procedure Step Start Date and Time
+//0040 4011 DT PRC Expected Completion Date and Time
+//0040 a030 DT PRC Verification Date Time
+//0040 a032 DT PRC Observation Date Time
+//0040 a120 DT PRC DateTime
+//0040 a121 DA PRC Date
+//0040 a13a DT PRC Referenced Datetime
+//0070 0082 DA ??? Presentation Creation Date
+//0100 0420 DT ??? SOP Autorization Date and Time
+//0400 0105 DT ??? Digital Signature DateTime
+//2100 0040 DA PJ Creation Date
+//3006 0008 DA SSET Structure Set Date
+//3008 0024 DA ??? Treatment Control Point Date
+//3008 0054 DA ??? First Treatment Date
+//3008 0056 DA ??? Most Recent Treatment Date
+//3008 0162 DA ??? Safe Position Exit Date
+//3008 0166 DA ??? Safe Position Return Date
+//3008 0250 DA ??? Treatment Date
+//300a 0006 DA RT RT Plan Date
+//300a 022c DA RT Air Kerma Rate Reference Date
+//300e 0004 DA RT Review Date
+
+ return true;
+}
+
+/**
+ * \brief Performs some consistency checking on various 'File related'
+ * (as opposed to 'DicomDir related') entries
+ * then writes in a file all the (Dicom Elements) included the Pixels
+ * @param fileName file name to write to
+ * @param writetype type of the file to be written
+ * (ACR, ExplicitVR, ImplicitVR)
+ */
+bool File::Write(std::string fileName, FileType writetype)
+{
+ std::ofstream *fp = new std::ofstream(fileName.c_str(),
+ std::ios::out | std::ios::binary);
+ if (*fp == NULL)
+ {
+ gdcmWarningMacro("Failed to open (write) File: " << fileName.c_str());
+ return false;
+ }
+
+ // Entry : 0002|0000 = group length -> recalculated
+ DataEntry *e0000 = GetDataEntry(0x0002,0x0000);
+ if ( e0000 )
+ {
+ std::ostringstream sLen;
+ sLen << ComputeGroup0002Length( );
+ e0000->SetString(sLen.str());
+ }
+
+ // Derma?.dcm does not have it...let's remove it FIXME FIXME
+ if( writetype != JPEG )
+ {
+ int i_lgPix = GetEntryLength(GrPixel, NumPixel);
+ if (i_lgPix != -2)
+ {
+ // no (GrPixel, NumPixel) element
+ std::string s_lgPix = Util::Format("%d", i_lgPix+12);
+ s_lgPix = Util::DicomString( s_lgPix.c_str() );
+ InsertEntryString(s_lgPix,GrPixel, 0x0000);
+ }
+ }
+
+ Document::WriteContent(fp, writetype);
+
+ fp->close();
+ delete fp;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Protected
+
+
+//-----------------------------------------------------------------------------
+// Private
+/**
+ * \brief Parse pixel data from disk of [multi-]fragment RLE encoding.
+ * Compute the RLE extra information and store it in \ref RLEInfo
+ * for later pixel retrieval usage.
+ */
+void File::ComputeRLEInfo()
+{
+ std::string ts = GetTransferSyntax();
+ if ( !Global::GetTS()->IsRLELossless(ts) )
+ {
+ return;
+ }
+
+ // Encoded pixel data: for the time being we are only concerned with
+ // Jpeg or RLE Pixel data encodings.
+ // As stated in PS 3.5-2003, section 8.2 p44:
+ // "If sent in Encapsulated Format (i.e. other than the Native Format) the
+ // value representation OB is used".
+ // Hence we expect an OB value representation. Concerning OB VR,
+ // the section PS 3.5-2003, section A.4.c p 58-59, states:
+ // "For the Value Representations OB and OW, the encoding shall meet the
+ // following specifications depending on the Data element tag:"
+ // [...snip...]
+ // - the first item in the sequence of items before the encoded pixel
+ // data stream shall be basic offset table item. The basic offset table
+ // item value, however, is not required to be present"
+ ReadEncapsulatedBasicOffsetTable();
+
+ // Encapsulated RLE Compressed Images (see PS 3.5-2003, Annex G)
+ // Loop on the individual frame[s] and store the information
+ // on the RLE fragments in a RLEFramesInfo.
+ // Note: - when only a single frame is present, this is a
+ // classical image.
+ // - when more than one frame are present, then we are in
+ // the case of a multi-frame image.
+ long frameLength;
+ int i=0;
+ uint32_t sum = 0;
+ while ( (frameLength = ReadTagLength(0xfffe, 0xe000)) != 0 )
+ {
+ // Since we have read the basic offset table, let's check the value were correct
+ // or else produce a warning:
+ if ( BasicOffsetTableItemValue )
+ {
+ // If a BasicOffsetTableItemValue was read
+ uint32_t individualLength = BasicOffsetTableItemValue[i];
+ assert( individualLength == sum ); // REMOVE that if this is a problem
+ if( individualLength != sum )
+ {
+ gdcmWarningMacro( "BasicOffsetTableItemValue differs from the fragment lenght" );
+ }
+ sum += frameLength + 8;
+ i++;
+ }
+ // Parse the RLE Header and store the corresponding RLE Segment
+ // Offset Table information on fragments of this current Frame.
+ // Note that the fragment pixels themselves are not loaded
+ // (but just skipped).
+ long frameOffset = Fp->tellg();
+
+ uint32_t nbRleSegments = ReadInt32();
+ if ( nbRleSegments > 16 )
+ {
+ // There should be at most 15 segments (refer to RLEFrame class)
+ gdcmWarningMacro( "Too many segments.");
+ }
+
+ uint32_t rleSegmentOffsetTable[16];
+ for( int k = 1; k <= 15; k++ )
+ {
+ rleSegmentOffsetTable[k] = ReadInt32();
+ }
+
+ // Deduce from both RLE Header and frameLength
+ // the fragment length, and again store this info
+ // in a RLEFramesInfo.
+ long rleSegmentLength[15];
+ // skipping (not reading) RLE Segments
+ if ( nbRleSegments > 1)
+ {
+ for(unsigned int k = 1; k <= nbRleSegments-1; k++)
+ {
+ rleSegmentLength[k] = rleSegmentOffsetTable[k+1]
+ - rleSegmentOffsetTable[k];
+ SkipBytes(rleSegmentLength[k]);
+ }
+ }
+
+ rleSegmentLength[nbRleSegments] = frameLength
+ - rleSegmentOffsetTable[nbRleSegments];
+ SkipBytes(rleSegmentLength[nbRleSegments]);
+
+ // Store the collected info
+ RLEFrame *newFrame = new RLEFrame;
+ newFrame->SetNumberOfFragments(nbRleSegments);
+ for( unsigned int uk = 1; uk <= nbRleSegments; uk++ )
+ {
+ newFrame->SetOffset(uk,frameOffset + rleSegmentOffsetTable[uk]);
+ newFrame->SetLength(uk,rleSegmentLength[uk]);
+ }
+ RLEInfo->AddFrame(newFrame);
+ }
+
+ // Make sure that we encounter a 'Sequence Delimiter Item'
+ // at the end of the item :
+ if ( !ReadTag(0xfffe, 0xe0dd) )
+ {
+ gdcmWarningMacro( "No sequence delimiter item at end of RLE item sequence");
+ }
+}
+
+/**
+ * \brief Parse pixel data from disk of [multi-]fragment Jpeg encoding.
+ * Compute the jpeg extra information (fragment[s] offset[s] and
+ * length) and store it[them] in \ref JPEGInfo for later pixel
+ * retrieval usage.
+ */
+void File::ComputeJPEGFragmentInfo()
+{
+ // If you need to, look for comments of ComputeRLEInfo().
+ std::string ts = GetTransferSyntax();
+ if ( ! Global::GetTS()->IsJPEG(ts) )
+ {
+ return;
+ }
+
+ ReadEncapsulatedBasicOffsetTable();
+
+ // Loop on the fragments[s] and store the parsed information in a
+ // JPEGInfo.
+ long fragmentLength;
+ int i=0;
+ uint32_t sum = 0;
+ while ( (fragmentLength = ReadTagLength(0xfffe, 0xe000)) != 0 )
+ {
+ // Since we have read the basic offset table, let's check the value were correct
+ // or else produce a warning:
+ // A.4 Transfer syntaxes for encapsulation of encoded pixel data:
+ // When the Item Value is present, the Basic Offset Table Item Value shall contain
+ // concatenated 32-bit unsigned integer values that are byte offsets to the first
+ // byte of the Item Tag of the first fragment for each frame in the Sequence of
+ // Items. These offsets are measured from the first byte of the first Item Tag
+ // following the Basic Offset Table item (See Table A.4-2).
+
+ if ( BasicOffsetTableItemValue )
+ {
+ // If a BasicOffsetTableItemValue was read
+ uint32_t individualLength = BasicOffsetTableItemValue[i];
+ //assert( individualLength == sum ); // Seems like 00191113.dcm is off by one ??
+ if( individualLength != sum )
+ {
+ gdcmWarningMacro( "BasicOffsetTableItemValue differs from the fragment lenght:" <<
+ individualLength << " != " << sum );
+ }
+ sum += fragmentLength + 8;
+ i++;
+ }
+
+ long fragmentOffset = Fp->tellg();
+ // Store the collected info
+ JPEGFragment *newFragment = new JPEGFragment;
+ newFragment->SetOffset(fragmentOffset);
+ newFragment->SetLength(fragmentLength);
+ JPEGInfo->AddFragment(newFragment);
+
+ SkipBytes(fragmentLength);
+ }
+
+ // Make sure that we encounter a 'Sequence Delimiter Item'
+ // at the end of the item :
+ if ( !ReadTag(0xfffe, 0xe0dd) )
+ {
+ gdcmWarningMacro( "No sequence delimiter item at end of JPEG item sequence");
+ }
+}
+
+/**
+ * \brief Assuming the internal file pointer \ref Document::Fp
+ * is placed at the beginning of a tag, check whether this
+ * tag is (TestGroup, TestElem).
+ * \warning On success the internal file pointer \ref Document::Fp
+ * is modified to point after the tag.
+ * On failure (i.e. when the tag wasn't the expected tag
+ * (TestGroup, TestElem) the internal file pointer
+ * \ref Document::Fp is restored to it's original position.
+ * @param testGroup The expected group of the tag.
+ * @param testElem The expected Element of the tag.
+ * @return True on success, false otherwise.
+ */
+bool File::ReadTag(uint16_t testGroup, uint16_t testElem)
+{
+ long positionOnEntry = Fp->tellg();
+ long currentPosition = Fp->tellg(); // On debugging purposes
+
+ // Read the Item Tag group and element, and make
+ // sure they are what we expected:
+ uint16_t itemTagGroup;
+ uint16_t itemTagElem;
+ try
+ {
+ itemTagGroup = ReadInt16();
+ itemTagElem = ReadInt16();
+ }
+ catch ( FormatError )
+ {
+ gdcmErrorMacro( "Can not read tag for "
+ << " We should have found tag ("
+ << DictEntry::TranslateToKey(testGroup,testElem) << ")"
+ ) ;
+
+ return false;
+ }
+ if ( itemTagGroup != testGroup || itemTagElem != testElem )
+ {
+ // in order not to pollute output we don't warn on 'delimitors'
+ if (itemTagGroup != 0xfffe || testGroup != 0xfffe )
+ gdcmErrorMacro( "Wrong Item Tag found:"
+ << " We should have found tag ("
+ << DictEntry::TranslateToKey(testGroup,testElem) << ")" << std::endl
+ << " but instead we encountered tag ("
+ << DictEntry::TranslateToKey(itemTagGroup,itemTagElem) << ")"
+ << " at address: " << " 0x(" << std::hex
+ << (unsigned int)currentPosition << std::dec << ")"
+ ) ;
+ Fp->seekg(positionOnEntry, std::ios::beg);
+
+ return false;
+ }
+ return true;
+}
+
+/**
+ * \brief Assuming the internal file pointer \ref Document::Fp
+ * is placed at the beginning of a tag (TestGroup, TestElement),
+ * read the length associated to the Tag.
+ * \warning On success the internal file pointer \ref Document::Fp
+ * is modified to point after the tag and it's length.
+ * On failure (i.e. when the tag wasn't the expected tag
+ * (TestGroup, TestElement) the internal file pointer
+ * \ref Document::Fp is restored to it's original position.
+ * @param testGroup The expected Group of the tag.
+ * @param testElem The expected Element of the tag.
+ * @return On success returns the length associated to the tag. On failure
+ * returns 0.
+ */
+uint32_t File::ReadTagLength(uint16_t testGroup, uint16_t testElem)
+{
+
+ if ( !ReadTag(testGroup, testElem) )
+ {
+ // Avoid polutting output
+ if ( testGroup != 0xfffe )
+ gdcmErrorMacro( "ReadTag did not succeed for ("
+ << DictEntry::TranslateToKey(testGroup,testElem)
+ << ")..." );
+ return 0;
+ }
+
+ //// Then read the associated Item Length
+ long currentPosition = Fp->tellg();
+ uint32_t itemLength = ReadInt32();
+ gdcmDebugMacro( "Basic Item Length is: " << itemLength
+ << " at address: " << std::hex << (unsigned int)currentPosition);
+ return itemLength;
+}
+
+/**
+ * \brief When parsing the Pixel Data of an encapsulated file, read
+ * the basic offset table (when present, and BTW dump it).
+ */
+void File::ReadEncapsulatedBasicOffsetTable()
+{
+ //// Read the Basic Offset Table Item Tag length...
+ uint32_t itemLength = ReadTagLength(0xfffe, 0xe000);
+
+ // When present, read the basic offset table itself.
+ // Notes: - since the presence of this basic offset table is optional
+ // we can't rely on it for the implementation, and we will simply
+ // trash it's content (when present).
+ // - still, when present, we could add some further checks on the
+ // lengths, but we won't bother with such fuses for the time being.
+ if ( itemLength != 0 )
+ {
+ char *charBasicOffsetTableItemValue = new char[itemLength];
+ Fp->read(charBasicOffsetTableItemValue, itemLength);
+ unsigned int nbEntries = itemLength/4;
+ assert( nbEntries*4 == itemLength); // Make sure this is a multiple
+ BasicOffsetTableItemValue = new uint32_t[nbEntries];
+
+ for (unsigned int i=0; i < nbEntries; i++ )
+ {
+ BasicOffsetTableItemValue[i] = *((uint32_t*)(&charBasicOffsetTableItemValue[4*i]));
+#if defined(GDCM_WORDS_BIGENDIAN) || defined(GDCM_FORCE_BIGENDIAN_EMULATION)
+ uint32_t val = BasicOffsetTableItemValue[i];
+ BasicOffsetTableItemValue[i]
+ = ( (val<<24) | ((val<<8) & 0x00ff0000) |
+ ((val>>8) & 0x0000ff00) | (val>>24) );
+#endif
+ gdcmWarningMacro( "Read one length for: " <<
+ std::hex << BasicOffsetTableItemValue[i] );
+ }
+
+ delete[] charBasicOffsetTableItemValue;
+ }
+}
+
+// These are the deprecated method that one day should be removed (after the next release)
+
+#ifndef GDCM_LEGACY_REMOVE
+/*
+ * \ brief Loader. (DEPRECATED : temporaryly kept not to break the API)
+ * @ param fileName file to be open for parsing
+ * @ return false if file cannot be open or no swap info was found,
+ * or no tag was found.
+ * @ deprecated Use the Load() [ + SetLoadMode() ] + SetFileName() functions instead
+ */
+bool File::Load( std::string const &fileName )
+{
+ GDCM_LEGACY_REPLACED_BODY(File::Load(std::string), "1.2",
+ File::Load());
+ SetFileName( fileName );
+ if ( ! this->Document::Load( ) )
+ return false;
+
+ return DoTheLoadingJob( );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Print
+
+//-----------------------------------------------------------------------------
+} // end namespace gdcm