1use image::codecs::png::PngDecoder;
14use image::error::{DecodingError, ImageFormatHint, UnsupportedError};
15use image::metadata::Orientation;
16use image::{ColorType, ExtendedColorType, ImageDecoder, ImageError, ImageResult, Limits};
17use ouroboros::self_referencing;
18use std::io::{self, BufReader, Read, Seek};
19use std::marker::PhantomData;
20use zip::read::{ZipArchive, ZipFile};
21
22pub struct OpenRasterDecoder<'a, R>
23where
24 R: Read + Seek + 'a,
25{
26 mergedimg_decoder: PngDecoder<BufReader<SeekableArchiveFile<'a, R>>>,
27}
28
29fn openraster_format_hint() -> ImageFormatHint {
30 ImageFormatHint::Name("OpenRaster".into())
31}
32
33fn set_ora_image_type(err: ImageError) -> ImageError {
35 match err {
36 ImageError::Decoding(e) => {
37 ImageError::Decoding(DecodingError::new(openraster_format_hint(), e))
40 }
41 ImageError::Encoding(_) => {
42 unreachable!();
44 }
45 ImageError::Parameter(e) => ImageError::Parameter(e),
46 ImageError::Limits(e) => ImageError::Limits(e),
47 ImageError::Unsupported(e) => ImageError::Unsupported(
48 UnsupportedError::from_format_and_kind(openraster_format_hint(), e.kind()),
49 ),
50 ImageError::IoError(e) => ImageError::IoError(e),
51 }
52}
53
54#[self_referencing]
55struct SeekableArchiveCore<'a, R: Read + Seek + 'a> {
56 archive: ZipArchive<R>,
57 #[covariant]
58 #[borrows(mut archive)]
59 file: ZipFile<'this, R>,
60 lifetime_helper: PhantomData<&'a R>,
61}
62
63struct SeekableArchiveFile<'a, R: Read + Seek + 'a> {
68 core: Option<SeekableArchiveCore<'a, R>>,
69 file_index: usize,
70 position: u64,
71 file_size: u64,
72}
73
74impl<'a, R: Read + Seek + 'a> SeekableArchiveFile<'a, R> {
75 fn new(
76 archive: ZipArchive<R>,
77 file_index: usize,
78 ) -> Result<SeekableArchiveFile<'a, R>, io::Error> {
79 let core = SeekableArchiveCore::try_new(archive, |x| x.by_index(file_index), PhantomData)
80 .map_err(|x| io::Error::other(format!("failed to open: {:?}", x)))?;
81 let file_size = core.with_file(|file| file.size());
82 Ok(SeekableArchiveFile {
83 core: Some(core),
84 file_index,
85 position: 0,
86 file_size,
87 })
88 }
89}
90
91impl<R: Read + Seek> Read for SeekableArchiveFile<'_, R> {
92 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
93 let res = self
94 .core
95 .as_mut()
96 .unwrap()
97 .with_file_mut(|file| file.read(buf));
98 let nread = res?;
99 self.position
100 .checked_add(nread as u64)
101 .ok_or_else(|| io::Error::other("seek position overflow"))?;
102 Ok(nread)
103 }
104}
105
106impl<R: Read + Seek> Seek for SeekableArchiveFile<'_, R> {
107 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
108 let target_pos = match pos {
109 io::SeekFrom::Start(offset) => offset,
110 io::SeekFrom::End(offset) => self
111 .file_size
112 .checked_add_signed(offset)
113 .ok_or_else(|| io::Error::other("seek position over or underflow"))?,
114 io::SeekFrom::Current(offset) => self
115 .position
116 .checked_add_signed(offset)
117 .ok_or_else(|| io::Error::other("seek position over or underflow"))?,
118 };
119
120 if target_pos < self.position {
121 let core = self.core.take();
122 let archive = core.unwrap().into_heads().archive;
123
124 self.core = Some(
125 SeekableArchiveCore::try_new(archive, |x| x.by_index(self.file_index), PhantomData)
126 .map_err(|x| io::Error::other(format!("failed to reopen: {:?}", x)))?,
127 );
128 }
129 while self.position < target_pos {
130 const TMP_LEN: usize = 1024;
131 let mut tmp = [0_u8; TMP_LEN];
132 let cur_pos = self.position;
133 let nr = self
134 .read(&mut tmp[..std::cmp::min(TMP_LEN as u64, target_pos - cur_pos) as usize])?;
135 if nr == 0 {
136 return Err(io::Error::other("unexpected eof when seeking"));
137 }
138 self.position += nr as u64;
139 }
140
141 Ok(0)
142 }
143}
144
145impl<'a, R> OpenRasterDecoder<'a, R>
146where
147 R: Read + Seek + 'a,
148{
149 pub fn with_limits(r: R, limits: Limits) -> Result<OpenRasterDecoder<'a, R>, ImageError> {
162 let mut archive = ZipArchive::new(r)
163 .map_err(|e| ImageError::Decoding(DecodingError::new(openraster_format_hint(), e)))?;
164
165 let mimetype_index = archive.index_for_name("mimetype").ok_or_else(|| {
167 ImageError::Decoding(DecodingError::new(
168 openraster_format_hint(),
169 "OpenRaster images should contain a mimetype subfile",
170 ))
171 })?;
172
173 let mut mimetype_file = archive
174 .by_index(mimetype_index)
175 .map_err(|x| ImageError::Decoding(DecodingError::new(openraster_format_hint(), x)))?;
176
177 const EXPECTED_MIMETYPE: &str = "image/openraster";
178 let mut tmp = [0u8; EXPECTED_MIMETYPE.len()];
179
180 mimetype_file.read_exact(&mut tmp)?;
181
182 if tmp != EXPECTED_MIMETYPE.as_bytes()
183 || mimetype_file.size() != EXPECTED_MIMETYPE.len() as u64
184 {
185 return Err(ImageError::Decoding(DecodingError::new(
186 openraster_format_hint(),
187 "Image did not have correct mimetype subentry to be identified as OpenRaster",
188 )));
189 }
190
191 drop(mimetype_file);
192
193 let mergedimage_index = archive.index_for_name("mergedimage.png").ok_or_else(|| {
194 ImageError::Decoding(DecodingError::new(
195 openraster_format_hint(),
196 "OpenRaster image missing mergedimage.png entry",
197 ))
198 })?;
199
200 let file = SeekableArchiveFile::new(archive, mergedimage_index)?;
201 let decoder =
202 PngDecoder::with_limits(BufReader::new(file), limits).map_err(set_ora_image_type)?;
203
204 Ok(OpenRasterDecoder {
205 mergedimg_decoder: decoder,
206 })
207 }
208}
209
210impl<'a, R: Read + Seek + 'a> ImageDecoder for OpenRasterDecoder<'a, R> {
211 fn dimensions(&self) -> (u32, u32) {
212 self.mergedimg_decoder.dimensions()
213 }
214
215 fn color_type(&self) -> ColorType {
216 self.mergedimg_decoder.color_type()
217 }
218
219 fn original_color_type(&self) -> ExtendedColorType {
220 self.mergedimg_decoder.original_color_type()
221 }
222
223 fn set_limits(&mut self, limits: Limits) -> ImageResult<()> {
224 self.mergedimg_decoder.set_limits(limits)
226 }
227
228 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
229 self.mergedimg_decoder.icc_profile()
230 }
231
232 fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
233 self.mergedimg_decoder.exif_metadata()
234 }
235
236 fn orientation(&mut self) -> ImageResult<Orientation> {
237 self.mergedimg_decoder.orientation()
238 }
239
240 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
241 self.mergedimg_decoder.read_image(buf)
242 }
243
244 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
245 (*self).read_image(buf)
246 }
247}