nitf_rs/
nitf.rs

1use log::{debug, trace};
2use std::fmt::Display;
3use std::io::{Read, Seek, Write};
4
5use crate::headers::file_hdr::Segment::*;
6use crate::headers::*;
7use crate::types::NitfSegment;
8use crate::NitfResult;
9
10// Convenience type-defs
11pub type ImageSegment = NitfSegment<ImageHeader>;
12pub type GraphicSegment = NitfSegment<GraphicHeader>;
13pub type TextSegment = NitfSegment<TextHeader>;
14pub type DataExtensionSegment = NitfSegment<DataExtensionHeader>;
15pub type ReservedExtensionSegment = NitfSegment<ReservedExtensionHeader>;
16
17/// Top level NITF interface
18#[derive(Default, Debug)]
19pub struct Nitf {
20    /// Nitf file header.
21    pub nitf_header: NitfHeader,
22    /// Vector of image segments.
23    pub image_segments: Vec<ImageSegment>,
24    /// Vector of graphics segments.
25    pub graphic_segments: Vec<GraphicSegment>,
26    /// Vector of text segments.
27    pub text_segments: Vec<TextSegment>,
28    /// Vector of data extension segments.
29    pub data_extension_segments: Vec<DataExtensionSegment>,
30    /// Vector of reserved extension segments.
31    pub reserved_extension_segments: Vec<ReservedExtensionSegment>,
32}
33
34impl Nitf {
35    /// Construct a [Nitf] from a reader
36    pub fn from_reader(reader: &mut (impl Read + Seek)) -> NitfResult<Self> {
37        let mut nitf = Nitf::default();
38
39        debug!("Reading NITF file header");
40        nitf.nitf_header.read(reader)?;
41
42        let mut n_seg = nitf.nitf_header.numi.val as usize;
43        for i_seg in 0..n_seg {
44            let seg_info = &nitf.nitf_header.imheaders[i_seg];
45            let data_size = seg_info.item_size.val;
46            let seg = ImageSegment::read(reader, data_size)?;
47            nitf.image_segments.push(seg);
48        }
49
50        n_seg = nitf.nitf_header.nums.val as usize;
51        for i_seg in 0..n_seg {
52            let seg_info = &nitf.nitf_header.graphheaders[i_seg];
53            let data_size: u64 = seg_info.item_size.val;
54            let seg = GraphicSegment::read(reader, data_size)?;
55            nitf.graphic_segments.push(seg);
56        }
57
58        n_seg = nitf.nitf_header.numt.val as usize;
59        for i_seg in 0..n_seg {
60            let seg_info = &nitf.nitf_header.textheaders[i_seg];
61            let data_size: u64 = seg_info.item_size.val;
62            let seg = TextSegment::read(reader, data_size)?;
63            nitf.text_segments.push(seg);
64        }
65
66        n_seg = nitf.nitf_header.numdes.val as usize;
67        for i_seg in 0..n_seg {
68            let seg_info = &nitf.nitf_header.dextheaders[i_seg];
69            let data_size: u64 = seg_info.item_size.val;
70            let seg = DataExtensionSegment::read(reader, data_size)?;
71            nitf.data_extension_segments.push(seg);
72        }
73
74        n_seg = nitf.nitf_header.numres.val as usize;
75        for i_seg in 0..n_seg {
76            let seg_info = &nitf.nitf_header.resheaders[i_seg];
77            let data_size = seg_info.item_size.val;
78            let seg = ReservedExtensionSegment::read(reader, data_size)?;
79            nitf.reserved_extension_segments.push(seg);
80        }
81        Ok(nitf)
82    }
83
84    /// Write the header information for all segments to a file
85    pub fn write_headers(&mut self, writer: &mut (impl Write + Seek)) -> NitfResult<usize> {
86        let mut bytes_written = 0;
87
88        let file_length = self.length() as u64;
89        bytes_written += self.nitf_header.write_header(writer, file_length)?;
90        for seg in self.image_segments.iter_mut() {
91            bytes_written += seg.write_header(writer)?;
92        }
93        for seg in self.graphic_segments.iter_mut() {
94            bytes_written += seg.write_header(writer)?;
95        }
96        for seg in self.text_segments.iter_mut() {
97            bytes_written += seg.write_header(writer)?;
98        }
99        for seg in self.data_extension_segments.iter_mut() {
100            bytes_written += seg.write_header(writer)?;
101        }
102        for seg in self.reserved_extension_segments.iter_mut() {
103            bytes_written += seg.write_header(writer)?;
104        }
105        Ok(bytes_written)
106    }
107
108    /// Get the length of the [Nitf] file in bytes
109    pub fn length(&self) -> usize {
110        let mut length = 0;
111        length += self.nitf_header.length();
112        length += self
113            .image_segments
114            .iter()
115            .map(|seg| seg.length())
116            .sum::<usize>();
117        length += self
118            .graphic_segments
119            .iter()
120            .map(|seg| seg.length())
121            .sum::<usize>();
122        length += self
123            .text_segments
124            .iter()
125            .map(|seg| seg.length())
126            .sum::<usize>();
127        length += self
128            .data_extension_segments
129            .iter()
130            .map(|seg| seg.length())
131            .sum::<usize>();
132        length += self
133            .reserved_extension_segments
134            .iter()
135            .map(|seg| seg.length())
136            .sum::<usize>();
137        length
138    }
139
140    /// Update file header and offsets when necessary.
141    fn update_offsets(&mut self) {
142        let mut offset = self.nitf_header.length();
143        let mut trace_string = "Updated offsets: \n".to_string();
144        trace_string += &format!("\tFile Header length: {offset}\n");
145        for (i_seg, seg) in self.image_segments.iter_mut().enumerate() {
146            seg.header_offset = offset as u64;
147            offset += seg.header.length();
148            trace_string += &format!(
149                "\tImage segment {i_seg} header offset: {}\n",
150                seg.header_offset
151            );
152            trace_string += &format!(
153                "\tImage segment {i_seg} header length: {}\n",
154                seg.header.length()
155            );
156            seg.data_offset = offset as u64;
157            offset += seg.data_size as usize;
158            trace_string += &format!("\tImage segment {i_seg} data offset: {}\n", seg.data_offset);
159            trace_string += &format!("\tImage segment {i_seg} data length: {}\n", seg.data_size);
160        }
161        for (i_seg, seg) in self.graphic_segments.iter_mut().enumerate() {
162            seg.header_offset = offset as u64;
163            offset += seg.header.length();
164            trace_string += &format!(
165                "\tGraphic segment {i_seg} header offset: {}\n",
166                seg.header_offset
167            );
168            trace_string += &format!(
169                "\tGraphic segment {i_seg} header length: {}\n",
170                seg.header.length()
171            );
172            seg.data_offset = offset as u64;
173            offset += seg.data_size as usize;
174            trace_string += &format!(
175                "\tGraphic segment {i_seg} data offset: {}\n",
176                seg.data_offset
177            );
178            trace_string += &format!("\tGraphic segment {i_seg} data length: {}\n", seg.data_size);
179        }
180        for (i_seg, seg) in self.text_segments.iter_mut().enumerate() {
181            seg.header_offset = offset as u64;
182            offset += seg.header.length();
183            trace_string += &format!(
184                "\tText segment {i_seg} header offset: {}\n",
185                seg.header_offset
186            );
187            trace_string += &format!(
188                "\tText segment {i_seg} header length: {}\n",
189                seg.header.length()
190            );
191            seg.data_offset = offset as u64;
192            offset += seg.data_size as usize;
193            trace_string += &format!("\tText segment {i_seg} data offset: {}\n", seg.data_offset);
194            trace_string += &format!("\tText segment {i_seg} data length: {}\n", seg.data_size);
195        }
196        for (i_seg, seg) in self.data_extension_segments.iter_mut().enumerate() {
197            seg.header_offset = offset as u64;
198            offset += seg.header.length();
199            trace_string += &format!(
200                "\tData Extension segment {i_seg} header offset: {}\n",
201                seg.header_offset
202            );
203            trace_string += &format!(
204                "\tData Extension segment {i_seg} header length: {}\n",
205                seg.header.length()
206            );
207            seg.data_offset = offset as u64;
208            offset += seg.data_size as usize;
209            trace_string += &format!(
210                "\tData Extension segment {i_seg} data offset: {}\n",
211                seg.data_offset
212            );
213            trace_string += &format!(
214                "\tData Extension segment {i_seg} data length: {}\n",
215                seg.data_size
216            );
217        }
218        for (i_seg, seg) in self.reserved_extension_segments.iter_mut().enumerate() {
219            seg.header_offset = offset as u64;
220            offset += seg.header.length();
221            trace_string += &format!(
222                "\tReserved Extension segment {i_seg} header offset: {}\n",
223                seg.header_offset
224            );
225            trace_string += &format!(
226                "\tReserved Extension segment {i_seg} header length: {}\n",
227                seg.header.length()
228            );
229            seg.data_offset = offset as u64;
230            offset += seg.data_size as usize;
231            trace_string += &format!(
232                "\tReserved Extension segment {i_seg} data offset: {}\n",
233                seg.data_offset
234            );
235            trace_string += &format!(
236                "\tReserved Extension segment {i_seg} data length: {}\n",
237                seg.data_size
238            );
239        }
240        trace!("{trace_string}");
241    }
242
243    /// Add a [ImageSegment] to the object.
244    ///
245    /// Takes ownership of the segment to indicate that the metadata should not
246    /// be extensively modified after adding. Some fields can be changed without
247    /// adverse affect, but it should be done with moderate prejudice.
248    pub fn add_im(&mut self, seg: ImageSegment) {
249        let segment_type = Image;
250        let subheader_size = seg.header.length() as u32;
251        let item_size = seg.data_size;
252        self.nitf_header
253            .add_subheader(segment_type, subheader_size, item_size);
254        self.image_segments.push(seg);
255        self.update_offsets();
256        debug!("Added Image Segment to NITF");
257    }
258
259    /// Add a [GraphicSegment] to the object.
260    ///
261    /// Takes ownership of the segment to indicate that the metadata should not
262    /// be extensively modified after adding. Some fields can be changed without
263    /// adverse affect, but it should be done with moderate prejudice.
264    pub fn add_sy(&mut self, seg: GraphicSegment) {
265        let segment_type = Graphic;
266        let subheader_size = seg.header.length() as u32;
267        let item_size = seg.data_size;
268        self.nitf_header
269            .add_subheader(segment_type, subheader_size, item_size);
270        self.graphic_segments.push(seg);
271        self.update_offsets();
272        debug!("Added Graphic Segment to NITF");
273    }
274
275    /// Add a [TextSegment] to the object.
276    ///
277    /// Takes ownership of the segment to indicate that the metadata should not
278    /// be extensively modified after adding. Some fields can be changed without
279    /// adverse affect, but it should be done with moderate prejudice.
280    pub fn add_te(&mut self, seg: TextSegment) {
281        let segment_type = Text;
282        let subheader_size = seg.header.length() as u32;
283        let item_size = seg.data_size;
284        self.nitf_header
285            .add_subheader(segment_type, subheader_size, item_size);
286        self.text_segments.push(seg);
287        self.update_offsets();
288        debug!("Added Text Segment to NITF");
289    }
290
291    /// Add a [DataExtensionSegment] to the object.
292    ///
293    /// Takes ownership of the segment to indicate that the metadata should not
294    /// be extensively modified after adding. Some fields can be changed without
295    /// adverse affect, but it should be done with moderate prejudice.
296    pub fn add_de(&mut self, seg: DataExtensionSegment) {
297        let segment_type = DataExtension;
298        let subheader_size = seg.header.length() as u32;
299        let item_size = seg.data_size;
300        self.nitf_header
301            .add_subheader(segment_type, subheader_size, item_size);
302        self.data_extension_segments.push(seg);
303        self.update_offsets();
304        debug!("Added Data Extension Segment to NITF");
305    }
306
307    /// Add a [ReservedExtensionSegment] to the object.
308    ///
309    /// Takes ownership of the segment to indicate that the metadata should not
310    /// be extensively modified after adding. Some fields can be changed without
311    /// adverse affect, but it should be done with moderate prejudice.
312    pub fn add_re(&mut self, seg: ReservedExtensionSegment) {
313        let segment_type = ReservedExtension;
314        let subheader_size = seg.header.length() as u32;
315        let item_size = seg.data_size;
316        self.nitf_header
317            .add_subheader(segment_type, subheader_size, item_size);
318        self.reserved_extension_segments.push(seg);
319        self.update_offsets();
320        debug!("Added Reserved Extension Segment to NITF");
321    }
322}
323
324// Trait implementations
325impl Display for Nitf {
326    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
327        let mut out_str = String::default();
328        out_str += format!("{}", self.nitf_header).as_ref();
329        for segment in &self.image_segments {
330            out_str += format!("{segment}, ").as_ref();
331        }
332        for segment in &self.graphic_segments {
333            out_str += format!("{segment}, ").as_ref();
334        }
335        for segment in &self.text_segments {
336            out_str += format!("{segment}, ").as_ref();
337        }
338        for segment in &self.data_extension_segments {
339            out_str += format!("{segment}, ").as_ref();
340        }
341        for segment in &self.reserved_extension_segments {
342            out_str += format!("{segment}, ").as_ref();
343        }
344        write!(f, "{out_str}",)
345    }
346}
347impl PartialEq for Nitf {
348    fn eq(&self, other: &Self) -> bool {
349        self.nitf_header == other.nitf_header
350            && self.image_segments == other.image_segments
351            && self.graphic_segments == other.graphic_segments
352            && self.text_segments == other.text_segments
353            && self.data_extension_segments == other.data_extension_segments
354            && self.reserved_extension_segments == other.reserved_extension_segments
355    }
356}
357impl Eq for Nitf {}