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