1 // Take a look at the license at the top of the repository in the LICENSE file.
2 
3 use crate::error::Error;
4 use crate::{Surface, UserDataKey};
5 use ffi::cairo_status_t;
6 
7 use libc::{c_double, c_uchar, c_uint, c_void};
8 use std::any::Any;
9 use std::cell::{Cell, RefCell};
10 use std::io;
11 use std::panic::AssertUnwindSafe;
12 use std::ptr;
13 use std::rc::Rc;
14 
15 macro_rules! for_stream_constructors {
16     ($constructor_ffi: ident) => {
17         /// Takes full ownership of the output stream,
18         /// which is not allowed to borrow any lifetime shorter than `'static`.
19         ///
20         /// Because the underlying `cairo_surface_t` is reference-counted,
21         /// a lifetime parameter in a Rust wrapper type would not be enough to track
22         /// how long it can keep writing to the stream.
23         pub fn for_stream<W: io::Write + 'static>(
24             width: f64,
25             height: f64,
26             stream: W,
27         ) -> Result<Self, crate::error::Error> {
28             Ok(Self(Surface::_for_stream(
29                 ffi::$constructor_ffi,
30                 width,
31                 height,
32                 stream,
33             )?))
34         }
35 
36         /// Allows writing to a borrowed stream. The lifetime of the borrow is not tracked.
37         ///
38         /// # Safety
39         ///
40         /// The value that `stream` points to must live at least until the underlying `cairo_surface_t`
41         /// (which maybe be longer then the Rust `PdfSurface` wrapper, because of reference-counting),
42         /// or until the output stream is removed from the surface with [`Surface::finish_output_stream`].
43         ///
44         /// Since the former is hard to track for sure, the latter is strongly recommended.
45         /// The concrete type behind the `Box<dyn Any>` value returned by `finish_output_stream`
46         /// is private, so you won’t be able to downcast it.
47         /// But removing it anyway ensures that later writes do not go through a dangling pointer.
48         pub unsafe fn for_raw_stream<W: io::Write + 'static>(
49             width: f64,
50             height: f64,
51             stream: *mut W,
52         ) -> Result<Self, crate::error::Error> {
53             Ok(Self(Surface::_for_raw_stream(
54                 ffi::$constructor_ffi,
55                 width,
56                 height,
57                 stream,
58             )?))
59         }
60     };
61 }
62 
63 impl Surface {
_for_stream<W: io::Write + 'static>( constructor: Constructor, width: f64, height: f64, stream: W, ) -> Result<Self, Error>64     pub(crate) fn _for_stream<W: io::Write + 'static>(
65         constructor: Constructor,
66         width: f64,
67         height: f64,
68         stream: W,
69     ) -> Result<Self, Error> {
70         let env_rc = Rc::new(CallbackEnvironment {
71             mutable: RefCell::new(MutableCallbackEnvironment {
72                 stream: Some((Box::new(stream), None)),
73                 unwind_payload: None,
74             }),
75             saw_already_borrowed: Cell::new(false),
76         });
77         let env: *const CallbackEnvironment = &*env_rc;
78         unsafe {
79             let ptr = constructor(Some(write_callback::<W>), env as *mut c_void, width, height);
80             let surface = Surface::from_raw_full(ptr)?;
81             surface.set_user_data(&STREAM_CALLBACK_ENVIRONMENT, env_rc)?;
82             Ok(surface)
83         }
84     }
85 
_for_raw_stream<W: io::Write + 'static>( constructor: Constructor, width: f64, height: f64, stream: *mut W, ) -> Result<Self, Error>86     pub(crate) unsafe fn _for_raw_stream<W: io::Write + 'static>(
87         constructor: Constructor,
88         width: f64,
89         height: f64,
90         stream: *mut W,
91     ) -> Result<Self, Error> {
92         Self::_for_stream(
93             constructor,
94             width,
95             height,
96             RawStream(ptr::NonNull::new(stream).expect("NULL stream passed")),
97         )
98     }
99 
100     /// Finish the surface, then remove and return the output stream if any.
101     ///
102     /// This calls [`Surface::finish`], to make sure pending writes are done.
103     ///
104     /// This is relevant for surfaces created for example with [`PdfSurface::for_stream`].
105     ///
106     /// Use [`Box::downcast`] to recover the concrete stream type.
107     ///
108     /// # Panics
109     ///
110     /// This method panics if:
111     ///
112     /// * This method was already called for this surface, or
113     /// * This surface was not created with an output stream in the first place, or
114     /// * A previous write to this surface panicked, or
115     /// * A previous write happened while another write was ongoing, or
116     /// * A write is ongoing now.
117     ///
118     /// The latter two cases can only occur with a pathological output stream type
119     /// that accesses the same surface again from `Write::write_all`.
finish_output_stream(&self) -> Result<Box<dyn Any>, StreamWithError>120     pub fn finish_output_stream(&self) -> Result<Box<dyn Any>, StreamWithError> {
121         self.finish();
122 
123         let env = self
124             .user_data_ptr(&STREAM_CALLBACK_ENVIRONMENT)
125             .expect("surface without an output stream");
126 
127         // Safety: since `STREAM_CALLBACK_ENVIRONMENT` is private and we never
128         // call `set_user_data` again or `remove_user_data` with it,
129         // the contract of `get_user_data_ptr` says that the user data entry
130         // lives as long as the underlying `cairo_surface_t`
131         // which is at least as long as `self`.
132         let env = unsafe { &*env.as_ptr() };
133 
134         if env.saw_already_borrowed.get() {
135             panic!("The output stream’s RefCell was already borrowed when cairo attempted a write")
136         }
137 
138         let mut mutable = env.mutable.borrow_mut();
139         if let Some(payload) = mutable.unwind_payload.take() {
140             std::panic::resume_unwind(payload)
141         }
142 
143         let (stream, io_error) = mutable
144             .stream
145             .take()
146             .expect("output stream was already taken");
147         if let Some(error) = io_error {
148             Err(StreamWithError { stream, error })
149         } else {
150             Ok(stream)
151         }
152     }
153 }
154 
155 pub struct StreamWithError {
156     pub stream: Box<dyn Any>,
157     pub error: io::Error,
158 }
159 
160 impl std::fmt::Debug for StreamWithError {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result161     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
162         self.error.fmt(f)
163     }
164 }
165 
166 impl std::fmt::Display for StreamWithError {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result167     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
168         self.error.fmt(f)
169     }
170 }
171 
172 impl From<StreamWithError> for io::Error {
from(e: StreamWithError) -> Self173     fn from(e: StreamWithError) -> Self {
174         e.error
175     }
176 }
177 
178 pub(crate) type Constructor = unsafe extern "C" fn(
179     ffi::cairo_write_func_t,
180     *mut c_void,
181     c_double,
182     c_double,
183 ) -> *mut ffi::cairo_surface_t;
184 
185 static STREAM_CALLBACK_ENVIRONMENT: UserDataKey<CallbackEnvironment> = UserDataKey::new();
186 
187 struct CallbackEnvironment {
188     mutable: RefCell<MutableCallbackEnvironment>,
189     saw_already_borrowed: Cell<bool>,
190 }
191 
192 struct MutableCallbackEnvironment {
193     stream: Option<(Box<dyn Any>, Option<io::Error>)>,
194     unwind_payload: Option<Box<dyn Any + Send + 'static>>,
195 }
196 
197 // Safety: unwinding into C is undefined behavior (https://github.com/rust-lang/rust/issues/58794)
198 // so code outside of the `catch_unwind` call must never panic.
write_callback<W: io::Write + 'static>( env: *mut c_void, data: *mut c_uchar, length: c_uint, ) -> cairo_status_t199 extern "C" fn write_callback<W: io::Write + 'static>(
200     env: *mut c_void,
201     data: *mut c_uchar,
202     length: c_uint,
203 ) -> cairo_status_t {
204     // This is consistent with the type of `env` in `Surface::_for_stream`.
205     let env: *const CallbackEnvironment = env as _;
206 
207     // Safety: the user data entry keeps `Rc<CallbackEnvironment>` alive
208     // until the surface is destroyed.
209     // If this is called by cairo, the surface is still alive.
210     let env: &CallbackEnvironment = unsafe { &*env };
211 
212     if let Ok(mut mutable) = env.mutable.try_borrow_mut() {
213         if let MutableCallbackEnvironment {
214             stream:
215                 Some((
216                     stream,
217                     // Don’t attempt another write, if a previous one errored or panicked:
218                     io_error @ None,
219                 )),
220             unwind_payload: unwind_payload @ None,
221         } = &mut *mutable
222         {
223             // Safety: `write_callback<W>` was instantiated in `Surface::_for_stream`
224             // with a W parameter consistent with the box that was unsized to `Box<dyn Any>`.
225             let stream = unsafe { stream.downcast_mut_unchecked::<W>() };
226             // Safety: this is the callback contract from cairo’s API
227             let data = unsafe { std::slice::from_raw_parts(data, length as usize) };
228             // Because `<W as Write>::write_all` is a generic,
229             // we must conservatively assume that it can panic.
230             let result = std::panic::catch_unwind(AssertUnwindSafe(|| stream.write_all(data)));
231             match result {
232                 Ok(Ok(())) => return ffi::STATUS_SUCCESS,
233                 Ok(Err(error)) => {
234                     *io_error = Some(error);
235                 }
236                 Err(payload) => {
237                     *unwind_payload = Some(payload);
238                 }
239             }
240         }
241     } else {
242         env.saw_already_borrowed.set(true)
243     }
244     Error::WriteError.into()
245 }
246 
247 struct RawStream<W>(ptr::NonNull<W>);
248 
249 impl<W: io::Write> io::Write for RawStream<W> {
write(&mut self, buf: &[u8]) -> io::Result<usize>250     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
251         unsafe { (*self.0.as_ptr()).write(buf) }
252     }
flush(&mut self) -> io::Result<()>253     fn flush(&mut self) -> io::Result<()> {
254         unsafe { (*self.0.as_ptr()).flush() }
255     }
write_all(&mut self, buf: &[u8]) -> io::Result<()>256     fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
257         unsafe { (*self.0.as_ptr()).write_all(buf) }
258     }
259 }
260 
261 trait AnyExt {
262     /// Any::downcast_mut, but YOLO
downcast_mut_unchecked<T>(&mut self) -> &mut T263     unsafe fn downcast_mut_unchecked<T>(&mut self) -> &mut T {
264         let ptr = self as *mut Self as *mut T;
265         &mut *ptr
266     }
267 }
268 impl AnyExt for dyn Any {}
269