dicom_object/file.rs
1use dicom_core::{DataDictionary, Tag};
2use dicom_dictionary_std::StandardDataDictionary;
3use dicom_encoding::transfer_syntax::TransferSyntaxIndex;
4use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
5
6// re-export from dicom_parser
7pub use dicom_parser::dataset::read::OddLengthStrategy;
8
9use crate::{DefaultDicomObject, ReadError};
10use std::io::Read;
11use std::path::Path;
12
13pub type Result<T, E = ReadError> = std::result::Result<T, E>;
14
15/// Create a DICOM object by reading from a byte source.
16///
17/// This function assumes the standard file encoding structure without the
18/// preamble: file meta group, followed by the rest of the data set.
19pub fn from_reader<F>(file: F) -> Result<DefaultDicomObject>
20where
21 F: Read,
22{
23 OpenFileOptions::new().from_reader(file)
24}
25
26/// Create a DICOM object by reading from a file.
27///
28/// This function assumes the standard file encoding structure: 128-byte
29/// preamble, file meta group, and the rest of the data set.
30pub fn open_file<P>(path: P) -> Result<DefaultDicomObject>
31where
32 P: AsRef<Path>,
33{
34 OpenFileOptions::new().open_file(path)
35}
36
37/// A builder type for opening a DICOM file with additional options.
38///
39/// This builder exposes additional properties
40/// to configure the reading of a DICOM file.
41///
42/// # Example
43///
44/// Create a `OpenFileOptions`,
45/// call adaptor methods in a chain,
46/// and finish the operation with
47/// either [`open_file()`](OpenFileOptions::open_file)
48/// or [`from_reader()`](OpenFileOptions::from_reader).
49///
50/// ```no_run
51/// # use dicom_object::OpenFileOptions;
52/// let file = OpenFileOptions::new()
53/// .read_until(dicom_dictionary_std::tags::PIXEL_DATA)
54/// .open_file("path/to/file.dcm")?;
55/// # Result::<(), Box<dyn std::error::Error>>::Ok(())
56/// ```
57#[derive(Debug, Default, Clone)]
58#[non_exhaustive]
59pub struct OpenFileOptions<D = StandardDataDictionary, T = TransferSyntaxRegistry> {
60 data_dictionary: D,
61 ts_index: T,
62 read_until: Option<Tag>,
63 read_preamble: ReadPreamble,
64 odd_length: OddLengthStrategy,
65}
66
67impl OpenFileOptions {
68 pub fn new() -> Self {
69 OpenFileOptions::default()
70 }
71}
72
73impl<D, T> OpenFileOptions<D, T> {
74 /// Set the operation to read only until the given tag is found.
75 ///
76 /// The reading process ends immediately after this tag,
77 /// or any other tag that is next in the standard DICOM tag ordering,
78 /// is found in the object's root data set.
79 /// An element with the exact tag will be excluded from the output.
80 pub fn read_until(mut self, tag: Tag) -> Self {
81 self.read_until = Some(tag);
82 self
83 }
84
85 /// Set the operation to read all elements of the data set to the end.
86 ///
87 /// This is the default behavior.
88 pub fn read_all(mut self) -> Self {
89 self.read_until = None;
90 self
91 }
92
93 /// Set whether to read the 128-byte DICOM file preamble.
94 pub fn read_preamble(mut self, option: ReadPreamble) -> Self {
95 self.read_preamble = option;
96 self
97 }
98
99 /// Set how data elements with an odd length should be handled.
100 pub fn odd_length_strategy(mut self, option: OddLengthStrategy) -> Self {
101 self.odd_length = option;
102 self
103 }
104
105 /// Set the transfer syntax index to use when reading the file.
106 pub fn transfer_syntax_index<Tr>(self, ts_index: Tr) -> OpenFileOptions<D, Tr>
107 where
108 Tr: TransferSyntaxIndex,
109 {
110 OpenFileOptions {
111 data_dictionary: self.data_dictionary,
112 read_until: self.read_until,
113 read_preamble: self.read_preamble,
114 ts_index,
115 odd_length: self.odd_length,
116 }
117 }
118
119 /// Set the transfer syntax index to use when reading the file.
120 #[deprecated(since="0.8.1", note="please use `transfer_syntax_index` instead")]
121 pub fn tranfer_syntax_index<Tr>(self, ts_index: Tr) -> OpenFileOptions<D, Tr>
122 where
123 Tr: TransferSyntaxIndex,
124 {
125 self.transfer_syntax_index(ts_index)
126 }
127
128 /// Set the data element dictionary to use when reading the file.
129 pub fn dictionary<Di>(self, dict: Di) -> OpenFileOptions<Di, T>
130 where
131 Di: DataDictionary,
132 Di: Clone,
133 {
134 OpenFileOptions {
135 data_dictionary: dict,
136 read_until: self.read_until,
137 read_preamble: self.read_preamble,
138 ts_index: self.ts_index,
139 odd_length: self.odd_length,
140 }
141 }
142
143 /// Open the file at the given path.
144 pub fn open_file<P>(self, path: P) -> Result<DefaultDicomObject<D>>
145 where
146 P: AsRef<Path>,
147 D: DataDictionary,
148 D: Clone,
149 T: TransferSyntaxIndex,
150 {
151 DefaultDicomObject::open_file_with_all_options(
152 path,
153 self.data_dictionary,
154 self.ts_index,
155 self.read_until,
156 self.read_preamble,
157 self.odd_length,
158 )
159 }
160
161 /// Obtain a DICOM object by reading from a byte source.
162 ///
163 /// This method assumes
164 /// the standard file encoding structure without the preamble:
165 /// file meta group, followed by the rest of the data set.
166 pub fn from_reader<R>(self, from: R) -> Result<DefaultDicomObject<D>>
167 where
168 R: Read,
169 D: DataDictionary,
170 D: Clone,
171 T: TransferSyntaxIndex,
172 {
173 DefaultDicomObject::from_reader_with_all_options(
174 from,
175 self.data_dictionary,
176 self.ts_index,
177 self.read_until,
178 self.read_preamble,
179 self.odd_length,
180 )
181 }
182}
183
184/// An enumerate of supported options for
185/// whether to read the 128-byte DICOM file preamble.
186#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)]
187pub enum ReadPreamble {
188 /// Try to detect the presence of the preamble automatically.
189 /// If detection fails, it will revert to always reading the preamble
190 /// when opening a file by path,
191 /// and not reading it when reading from a byte source.
192 #[default]
193 Auto,
194 /// Never read the preamble,
195 /// thus assuming that the original source does not have it.
196 Never,
197 /// Always read the preamble first,
198 /// thus assuming that the original source always has it.
199 Always,
200}