diff --git a/Cargo.lock b/Cargo.lock index 98834c6d4..4fcff22bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -607,6 +607,8 @@ dependencies = [ "byteordered", "dicom-core", "dicom-encoding", + "dicom-object", + "dicom-pixeldata", "dicom-test-files", "flate2", "jpeg-decoder", diff --git a/object/src/lib.rs b/object/src/lib.rs index 66aff8026..b41242a6f 100644 --- a/object/src/lib.rs +++ b/object/src/lib.rs @@ -151,6 +151,7 @@ pub use dicom_dictionary_std::StandardDataDictionary; pub type DefaultDicomObject = FileDicomObject>; use dicom_core::header::Header; +use dicom_encoding::Codec; use dicom_encoding::adapters::{PixelDataObject, RawPixelData}; use dicom_encoding::transfer_syntax::TransferSyntaxIndex; use dicom_parser::dataset::{DataSetWriter, IntoTokens}; @@ -424,21 +425,34 @@ where .with_context(|| WriteUnsupportedTransferSyntaxSnafu { uid: self.meta.transfer_syntax.clone(), })?; - let mut dset_writer = DataSetWriter::with_ts(to, ts).context(CreatePrinterSnafu)?; + if let Codec::Dataset(Some(adapter))= ts.codec() { + let adapter = adapter.adapt_writer(Box::new(to)); + let mut dset_writer = DataSetWriter::with_ts(adapter, ts).context(CreatePrinterSnafu)?; - // We use the default options, because only the inner object knows if something needs to change - dset_writer - .write_sequence((&self.obj).into_tokens()) - .context(PrintDataSetSnafu)?; + // write object + dset_writer + .write_sequence((&self.obj).into_tokens()) + .context(PrintDataSetSnafu)?; - Ok(()) + Ok(()) + + } else { + let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).context(CreatePrinterSnafu)?; + + // write object + dset_writer + .write_sequence((&self.obj).into_tokens()) + .context(PrintDataSetSnafu)?; + + Ok(()) + } } /// Write the entire object as a DICOM file /// into the given writer. /// Preamble, magic code, and file meta group will be included /// before the inner object. - pub fn write_all(&self, to: W) -> Result<(), WriteError> { + pub fn write_all(&self, to: W) -> Result<(), WriteError> { let mut to = BufWriter::new(to); // write preamble @@ -456,14 +470,27 @@ where .with_context(|| WriteUnsupportedTransferSyntaxSnafu { uid: self.meta.transfer_syntax.clone(), })?; - let mut dset_writer = DataSetWriter::with_ts(to, ts).context(CreatePrinterSnafu)?; + if let Codec::Dataset(Some(adapter))= ts.codec() { + let adapter = adapter.adapt_writer(Box::new(to)); + let mut dset_writer = DataSetWriter::with_ts(adapter, ts).context(CreatePrinterSnafu)?; + + // write object + dset_writer + .write_sequence((&self.obj).into_tokens()) + .context(PrintDataSetSnafu)?; + + Ok(()) - // We use the default options, because only the inner object knows if something needs to change - dset_writer - .write_sequence((&self.obj).into_tokens()) - .context(PrintDataSetSnafu)?; + } else { + let mut dset_writer = DataSetWriter::with_ts(to, ts).context(CreatePrinterSnafu)?; - Ok(()) + // write object + dset_writer + .write_sequence((&self.obj).into_tokens()) + .context(PrintDataSetSnafu)?; + + Ok(()) + } } /// Write the file meta group set into the given writer. @@ -477,7 +504,7 @@ where /// without preamble, magic code, nor file meta group. /// /// The transfer syntax is selected from the file meta table. - pub fn write_dataset(&self, to: W) -> Result<(), WriteError> { + pub fn write_dataset(&self, to: W) -> Result<(), WriteError> { let to = BufWriter::new(to); // prepare encoder @@ -486,14 +513,27 @@ where .with_context(|| WriteUnsupportedTransferSyntaxSnafu { uid: self.meta.transfer_syntax.clone(), })?; - let mut dset_writer = DataSetWriter::with_ts(to, ts).context(CreatePrinterSnafu)?; + if let Codec::Dataset(Some(adapter))= ts.codec() { + let adapter = adapter.adapt_writer(Box::new(to)); + let mut dset_writer = DataSetWriter::with_ts(adapter, ts).context(CreatePrinterSnafu)?; + + // write object + dset_writer + .write_sequence((&self.obj).into_tokens()) + .context(PrintDataSetSnafu)?; - // write object - dset_writer - .write_sequence((&self.obj).into_tokens()) - .context(PrintDataSetSnafu)?; + Ok(()) - Ok(()) + } else { + let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).context(CreatePrinterSnafu)?; + + // write object + dset_writer + .write_sequence((&self.obj).into_tokens()) + .context(PrintDataSetSnafu)?; + + Ok(()) + } } } diff --git a/object/src/mem.rs b/object/src/mem.rs index f42660273..626a8055f 100644 --- a/object/src/mem.rs +++ b/object/src/mem.rs @@ -236,7 +236,7 @@ impl InMemDicomObject { cs: SpecificCharacterSet, ) -> Result where - S: Read, + S: Read + 'static, { Self::read_dataset_with_dict_ts_cs(from, StandardDataDictionary, ts, cs) } @@ -250,7 +250,7 @@ impl InMemDicomObject { #[inline] pub fn read_dataset_with_ts(from: S, ts: &TransferSyntax) -> Result where - S: Read, + S: Read + 'static, { Self::read_dataset_with_dict_ts_cs( from, @@ -624,7 +624,7 @@ where ts: &TransferSyntax, ) -> Result where - S: Read, + S: Read + 'static, D: DataDictionary, { Self::read_dataset_with_dict_ts_cs(from, dict, ts, SpecificCharacterSet::default()) @@ -644,12 +644,19 @@ where cs: SpecificCharacterSet, ) -> Result where - S: Read, + S: Read + 'static, D: DataDictionary, { let from = BufReader::new(from); - let mut dataset = DataSetReader::new_with_ts_cs(from, ts, cs).context(CreateParserSnafu)?; - InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED, None) + if let Codec::Dataset(Some(adapter)) = ts.codec() { + let adapter = adapter.adapt_reader(Box::new(from)); + let mut dataset = + DataSetReader::new_with_ts_cs(adapter, ts, cs).context(CreateParserSnafu)?; + InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED, None) + } else { + let mut dataset = DataSetReader::new_with_ts_cs(from, ts, cs).context(CreateParserSnafu)?; + InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED, None) + } } // Standard methods follow. They are not placed as a trait implementation @@ -1564,7 +1571,9 @@ where /// in which then that character set will be used. /// /// Note: [`write_dataset_with_ts`] and [`write_dataset_with_ts_cs`] - /// may be easier to use. + /// may be easier to use and _will_ apply a dataset adapter (such as + /// DeflatedExplicitVRLittleEndian (1.2.840.10008.1.2.99)) whereas this + /// method will _not_ /// /// [`write_dataset_with_ts`]: #method.write_dataset_with_ts /// [`write_dataset_with_ts_cs`]: #method.write_dataset_with_ts_cs @@ -1598,18 +1607,30 @@ where cs: SpecificCharacterSet, ) -> Result<(), WriteError> where - W: Write, + W: Write + 'static, { - // prepare data set writer - let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).context(CreatePrinterSnafu)?; - let required_options = IntoTokensOptions::new(self.charset_changed); + if let Codec::Dataset(Some(adapter)) = ts.codec() { + let adapter = adapter.adapt_writer(Box::new(to)); + // prepare data set writer + let mut dset_writer = DataSetWriter::with_ts(adapter, ts).context(CreatePrinterSnafu)?; - // write object - dset_writer - .write_sequence(self.into_tokens_with_options(required_options)) - .context(PrintDataSetSnafu)?; + // write object + dset_writer + .write_sequence(self.into_tokens()) + .context(PrintDataSetSnafu)?; - Ok(()) + Ok(()) + } else { + // prepare data set writer + let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).context(CreatePrinterSnafu)?; + + // write object + dset_writer + .write_sequence(self.into_tokens()) + .context(PrintDataSetSnafu)?; + + Ok(()) + } } /// Write this object's data set into the given writer, @@ -1621,7 +1642,7 @@ where /// after which the text encoder is overridden accordingly. pub fn write_dataset_with_ts(&self, to: W, ts: &TransferSyntax) -> Result<(), WriteError> where - W: Write, + W: Write + 'static, { self.write_dataset_with_ts_cs(to, ts, SpecificCharacterSet::default()) } diff --git a/transfer-syntax-registry/Cargo.toml b/transfer-syntax-registry/Cargo.toml index fcda2beca..838881cb8 100644 --- a/transfer-syntax-registry/Cargo.toml +++ b/transfer-syntax-registry/Cargo.toml @@ -70,3 +70,5 @@ features = ["native"] [dev-dependencies] dicom-test-files = "0.3" +dicom-object = { path = "../object" } +dicom-pixeldata = { path = "../pixeldata" } diff --git a/transfer-syntax-registry/tests/deflate.rs b/transfer-syntax-registry/tests/deflate.rs index b7efd400d..f184d769c 100644 --- a/transfer-syntax-registry/tests/deflate.rs +++ b/transfer-syntax-registry/tests/deflate.rs @@ -1,20 +1,38 @@ -use std::{io::BufReader, fs::File}; +use std::{io::BufReader, fs::{metadata, File}}; -// use dicom_object::OpenFileOptions; -// use dicom_pixeldata::PixelDecoder; -// #[test] -// fn test_read_data_with_preamble() { -// let path = dicom_test_files::path("pydicom/image_dfl.dcm").expect("test DICOM file should exist"); -// let source = BufReader::new(File::open(path).unwrap()); +use dicom_object::OpenFileOptions; +use dicom_pixeldata::PixelDecoder; -// // should read preamble even though it's from a reader -// let object = OpenFileOptions::new() -// .from_reader(source) -// .expect("Should read from source successfully"); +#[test] +fn test_read_data_deflated() { + let path = dicom_test_files::path("pydicom/image_dfl.dcm").expect("test DICOM file should exist"); + let source = BufReader::new(File::open(path).unwrap()); -// let res = object.decode_pixel_data(); -// println!("{:?}", res); + // should read preamble even though it's from a reader + let object = OpenFileOptions::new() + .from_reader(source) + .expect("Should read from source successfully"); -// } \ No newline at end of file + let res = object.decode_pixel_data().unwrap(); + assert_eq!(( + res.rows() as usize * + res.columns() as usize * + res.number_of_frames() as usize), res.data().len() as usize); +} + +#[test] +fn write_deflated(){ + let path = dicom_test_files::path("pydicom/image_dfl.dcm").expect("test DICOM file should exist"); + let source = BufReader::new(File::open(path.clone()).unwrap()); + + // should read preamble even though it's from a reader + let object = OpenFileOptions::new() + .from_reader(source) + .expect("Should read from source successfully"); + + let mut buf = Vec::::new(); + object.write_all(&mut buf).unwrap(); + assert_eq!(buf.len(), metadata(path).unwrap().len() as usize); +} \ No newline at end of file