1 // Take a look at the license at the top of the repository in the LICENSE file.
2 
3 use crate::utils::status_to_result;
4 use std::any::Any;
5 use std::io::{self, Read, Write};
6 use std::panic::AssertUnwindSafe;
7 use std::slice;
8 
9 use libc::{c_uint, c_void};
10 
11 use crate::error::{Error, IoError};
12 use crate::ImageSurface;
13 use ffi::cairo_status_t;
14 
15 struct ReadEnv<'a, R: 'a + Read> {
16     reader: &'a mut R,
17     io_error: Option<io::Error>,
18     unwind_payload: Option<Box<dyn Any + Send + 'static>>,
19 }
20 
read_func<R: Read>( closure: *mut c_void, data: *mut u8, len: c_uint, ) -> cairo_status_t21 unsafe extern "C" fn read_func<R: Read>(
22     closure: *mut c_void,
23     data: *mut u8,
24     len: c_uint,
25 ) -> cairo_status_t {
26     let read_env: &mut ReadEnv<R> = &mut *(closure as *mut ReadEnv<R>);
27 
28     // Don’t attempt another read, if a previous one errored or panicked:
29     if read_env.io_error.is_some() || read_env.unwind_payload.is_some() {
30         return Error::ReadError.into();
31     }
32 
33     let buffer = slice::from_raw_parts_mut(data, len as usize);
34     let result = std::panic::catch_unwind(AssertUnwindSafe(|| read_env.reader.read_exact(buffer)));
35     match result {
36         Ok(Ok(())) => ffi::STATUS_SUCCESS,
37         Ok(Err(error)) => {
38             read_env.io_error = Some(error);
39             Error::ReadError.into()
40         }
41         Err(payload) => {
42             read_env.unwind_payload = Some(payload);
43             Error::ReadError.into()
44         }
45     }
46 }
47 
48 struct WriteEnv<'a, W: 'a + Write> {
49     writer: &'a mut W,
50     io_error: Option<io::Error>,
51     unwind_payload: Option<Box<dyn Any + Send + 'static>>,
52 }
53 
write_func<W: Write>( closure: *mut c_void, data: *mut u8, len: c_uint, ) -> cairo_status_t54 unsafe extern "C" fn write_func<W: Write>(
55     closure: *mut c_void,
56     data: *mut u8,
57     len: c_uint,
58 ) -> cairo_status_t {
59     let write_env: &mut WriteEnv<W> = &mut *(closure as *mut WriteEnv<W>);
60 
61     // Don’t attempt another write, if a previous one errored or panicked:
62     if write_env.io_error.is_some() || write_env.unwind_payload.is_some() {
63         return Error::WriteError.into();
64     }
65 
66     let buffer = slice::from_raw_parts(data, len as usize);
67     let result = std::panic::catch_unwind(AssertUnwindSafe(|| write_env.writer.write_all(buffer)));
68     match result {
69         Ok(Ok(())) => ffi::STATUS_SUCCESS,
70         Ok(Err(error)) => {
71             write_env.io_error = Some(error);
72             Error::WriteError.into()
73         }
74         Err(payload) => {
75             write_env.unwind_payload = Some(payload);
76             Error::WriteError.into()
77         }
78     }
79 }
80 
81 impl ImageSurface {
82     #[doc(alias = "cairo_image_surface_create_from_png_stream")]
create_from_png<R: Read>(stream: &mut R) -> Result<ImageSurface, IoError>83     pub fn create_from_png<R: Read>(stream: &mut R) -> Result<ImageSurface, IoError> {
84         let mut env = ReadEnv {
85             reader: stream,
86             io_error: None,
87             unwind_payload: None,
88         };
89         unsafe {
90             let raw_surface = ffi::cairo_image_surface_create_from_png_stream(
91                 Some(read_func::<R>),
92                 &mut env as *mut ReadEnv<R> as *mut c_void,
93             );
94 
95             let surface = ImageSurface::from_raw_full(raw_surface)?;
96 
97             if let Some(payload) = env.unwind_payload {
98                 std::panic::resume_unwind(payload)
99             }
100 
101             match env.io_error {
102                 None => Ok(surface),
103                 Some(err) => Err(IoError::Io(err)),
104             }
105         }
106     }
107 
108     #[doc(alias = "cairo_surface_write_to_png_stream")]
write_to_png<W: Write>(&self, stream: &mut W) -> Result<(), IoError>109     pub fn write_to_png<W: Write>(&self, stream: &mut W) -> Result<(), IoError> {
110         let mut env = WriteEnv {
111             writer: stream,
112             io_error: None,
113             unwind_payload: None,
114         };
115         let status = unsafe {
116             ffi::cairo_surface_write_to_png_stream(
117                 self.to_raw_none(),
118                 Some(write_func::<W>),
119                 &mut env as *mut WriteEnv<W> as *mut c_void,
120             )
121         };
122 
123         if let Some(payload) = env.unwind_payload {
124             std::panic::resume_unwind(payload)
125         }
126 
127         match env.io_error {
128             None => match status_to_result(status) {
129                 Err(err) => Err(IoError::Cairo(err)),
130                 Ok(_) => Ok(()),
131             },
132             Some(err) => Err(IoError::Io(err)),
133         }
134     }
135 }
136 
137 #[cfg(test)]
138 mod tests {
139     use super::*;
140     use crate::enums::Format;
141     use std::io::ErrorKind;
142 
143     struct IoErrorReader;
144 
145     // A reader that always returns an error
146     impl Read for IoErrorReader {
read(&mut self, _: &mut [u8]) -> Result<usize, io::Error>147         fn read(&mut self, _: &mut [u8]) -> Result<usize, io::Error> {
148             Err(io::Error::new(ErrorKind::Other, "yikes!"))
149         }
150     }
151 
152     #[test]
valid_png_reads_correctly()153     fn valid_png_reads_correctly() {
154         // A 1x1 PNG, RGB, no alpha, with a single pixel with (42, 42, 42) values
155         let png_data: Vec<u8> = vec![
156             0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48,
157             0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00,
158             0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08,
159             0xd7, 0x63, 0xd0, 0xd2, 0xd2, 0x02, 0x00, 0x01, 0x00, 0x00, 0x7f, 0x09, 0xa9, 0x5a,
160             0x4d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
161         ];
162 
163         let r = ImageSurface::create_from_png(&mut &png_data[..]);
164         assert!(r.is_ok());
165 
166         let mut surface = r.unwrap();
167         assert_eq!(surface.width(), 1);
168         assert_eq!(surface.height(), 1);
169         assert_eq!(surface.format(), Format::Rgb24);
170 
171         let data = surface.data().unwrap();
172         assert!(data.len() >= 3);
173 
174         let slice = &data[0..3];
175         assert_eq!(slice[0], 42);
176         assert_eq!(slice[1], 42);
177         assert_eq!(slice[2], 42);
178     }
179 
180     #[cfg(not(target_os = "macos"))]
181     #[test]
invalid_png_yields_error()182     fn invalid_png_yields_error() {
183         let png_data: Vec<u8> = vec![
184             //      v--- this byte is modified
185             0x89, 0x40, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48,
186             0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00,
187             0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08,
188             0xd7, 0x63, 0xd0, 0xd2, 0xd2, 0x02, 0x00, 0x01, 0x00, 0x00, 0x7f, 0x09, 0xa9, 0x5a,
189             0x4d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
190         ];
191 
192         match ImageSurface::create_from_png(&mut &png_data[..]) {
193             Err(IoError::Cairo(_)) => (),
194             _ => unreachable!(),
195         }
196     }
197 
198     #[cfg(not(target_os = "macos"))]
199     #[test]
io_error_yields_cairo_read_error()200     fn io_error_yields_cairo_read_error() {
201         let mut r = IoErrorReader;
202 
203         match ImageSurface::create_from_png(&mut r) {
204             Err(IoError::Cairo(Error::ReadError)) => (),
205             _ => unreachable!(),
206         }
207     }
208 }
209