Skip to main content

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}