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_preamble: ReadPreamble,
65    odd_length: OddLengthStrategy,
66    charset_override: CharacterSetOverride,
67}
68
69impl OpenFileOptions {
70    pub fn new() -> Self {
71        OpenFileOptions::default()
72    }
73}
74
75impl<D, T> OpenFileOptions<D, T> {
76    /// Set the operation to read only until the given tag is found.
77    ///
78    /// The reading process ends immediately after this tag,
79    /// or any other tag that is next in the standard DICOM tag ordering,
80    /// is found in the object's root data set.
81    /// An element with the exact tag will be excluded from the output.
82    pub fn read_until(mut self, tag: Tag) -> Self {
83        self.read_until = Some(tag);
84        self
85    }
86
87    /// Set the operation to read all elements of the data set to the end.
88    ///
89    /// This is the default behavior.
90    pub fn read_all(mut self) -> Self {
91        self.read_until = None;
92        self
93    }
94
95    /// Set whether to read the 128-byte DICOM file preamble.
96    pub fn read_preamble(mut self, option: ReadPreamble) -> Self {
97        self.read_preamble = option;
98        self
99    }
100
101    /// Set how data elements with an odd length should be handled.
102    pub fn odd_length_strategy(mut self, option: OddLengthStrategy) -> Self {
103        self.odd_length = option;
104        self
105    }
106
107    /// Set an override on how text values are decoded.
108    pub fn charset_override(mut self, option: CharacterSetOverride) -> Self {
109        self.charset_override = option;
110        self
111    }
112
113    /// Set the transfer syntax index to use when reading the file.
114    pub fn transfer_syntax_index<Tr>(self, ts_index: Tr) -> OpenFileOptions<D, Tr>
115    where
116        Tr: TransferSyntaxIndex,
117    {
118        OpenFileOptions {
119            data_dictionary: self.data_dictionary,
120            read_until: self.read_until,
121            read_preamble: self.read_preamble,
122            ts_index,
123            odd_length: self.odd_length,
124            charset_override: self.charset_override,
125        }
126    }
127
128    /// Set the transfer syntax index to use when reading the file.
129    #[deprecated(since = "0.8.1", note = "please use `transfer_syntax_index` instead")]
130    pub fn tranfer_syntax_index<Tr>(self, ts_index: Tr) -> OpenFileOptions<D, Tr>
131    where
132        Tr: TransferSyntaxIndex,
133    {
134        self.transfer_syntax_index(ts_index)
135    }
136
137    /// Set the data element dictionary to use when reading the file.
138    pub fn dictionary<Di>(self, dict: Di) -> OpenFileOptions<Di, T>
139    where
140        Di: DataDictionary,
141        Di: Clone,
142    {
143        OpenFileOptions {
144            data_dictionary: dict,
145            read_until: self.read_until,
146            read_preamble: self.read_preamble,
147            ts_index: self.ts_index,
148            odd_length: self.odd_length,
149            charset_override: self.charset_override,
150        }
151    }
152
153    /// Open the file at the given path.
154    pub fn open_file<P>(self, path: P) -> Result<DefaultDicomObject<D>>
155    where
156        P: AsRef<Path>,
157        D: DataDictionary,
158        D: Clone,
159        T: TransferSyntaxIndex,
160    {
161        DefaultDicomObject::open_file_with_all_options(
162            path,
163            self.data_dictionary,
164            self.ts_index,
165            self.read_until,
166            self.read_preamble,
167            self.odd_length,
168            self.charset_override,
169        )
170    }
171
172    /// Obtain a DICOM object by reading from a byte source.
173    ///
174    /// This method assumes
175    /// the standard file encoding structure without the preamble:
176    /// file meta group, followed by the rest of the data set.
177    pub fn from_reader<R>(self, from: R) -> Result<DefaultDicomObject<D>>
178    where
179        R: Read,
180        D: DataDictionary,
181        D: Clone,
182        T: TransferSyntaxIndex,
183    {
184        DefaultDicomObject::from_reader_with_all_options(
185            from,
186            self.data_dictionary,
187            self.ts_index,
188            self.read_until,
189            self.read_preamble,
190            self.odd_length,
191            self.charset_override,
192        )
193    }
194}
195
196/// An enumerate of supported options for
197/// whether to read the 128-byte DICOM file preamble.
198#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)]
199pub enum ReadPreamble {
200    /// Try to detect the presence of the preamble automatically.
201    /// If detection fails, it will revert to always reading the preamble
202    /// when opening a file by path,
203    /// and not reading it when reading from a byte source.
204    #[default]
205    Auto,
206    /// Never read the preamble,
207    /// thus assuming that the original source does not have it.
208    Never,
209    /// Always read the preamble first,
210    /// thus assuming that the original source always has it.
211    Always,
212}