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