1use std::{
4 ffi::{CStr, CString},
5 io, mem,
6 ops::Deref,
7 path::Path,
8 ptr,
9};
10
11#[cfg(feature = "use_glib")]
12use glib::translate::*;
13
14use crate::{ffi, Error, PsLevel, Surface, SurfaceType};
15
16impl PsLevel {
17 pub fn as_str(self) -> Option<&'static str> {
18 unsafe {
19 let res = ffi::cairo_ps_level_to_string(self.into());
20 res.as_ref()
21 .and_then(|cstr| CStr::from_ptr(cstr as _).to_str().ok())
22 }
23 }
24}
25
26declare_surface!(PsSurface, SurfaceType::Ps);
27
28impl PsSurface {
29 #[doc(alias = "cairo_ps_surface_create")]
30 pub fn new<P: AsRef<Path>>(width: f64, height: f64, path: P) -> Result<PsSurface, Error> {
31 let path = path.as_ref().to_string_lossy().into_owned();
32 let path = CString::new(path).unwrap();
33
34 unsafe { Self::from_raw_full(ffi::cairo_ps_surface_create(path.as_ptr(), width, height)) }
35 }
36
37 for_stream_constructors!(cairo_ps_surface_create_for_stream);
38
39 #[doc(alias = "cairo_ps_get_levels")]
40 #[doc(alias = "get_levels")]
41 pub fn levels() -> impl Iterator<Item = PsLevel> {
42 let lvls_slice = unsafe {
43 let mut vers_ptr = ptr::null_mut();
44 let mut num_vers = mem::MaybeUninit::uninit();
45 ffi::cairo_ps_get_levels(&mut vers_ptr, num_vers.as_mut_ptr());
46
47 let num_vers = num_vers.assume_init();
48 if num_vers == 0 {
49 &[]
50 } else {
51 std::slice::from_raw_parts(vers_ptr, num_vers as _)
52 }
53 };
54
55 lvls_slice.iter().map(|v| PsLevel::from(*v))
56 }
57
58 #[doc(alias = "cairo_ps_surface_restrict_to_level")]
59 pub fn restrict(&self, level: PsLevel) {
60 unsafe {
61 ffi::cairo_ps_surface_restrict_to_level(self.0.to_raw_none(), level.into());
62 }
63 }
64
65 #[doc(alias = "cairo_ps_surface_get_eps")]
66 #[doc(alias = "get_eps")]
67 pub fn is_eps(&self) -> bool {
68 unsafe { ffi::cairo_ps_surface_get_eps(self.0.to_raw_none()).as_bool() }
69 }
70
71 #[doc(alias = "cairo_ps_surface_set_eps")]
72 pub fn set_eps(&self, eps: bool) {
73 unsafe {
74 ffi::cairo_ps_surface_set_eps(self.0.to_raw_none(), eps.into());
75 }
76 }
77
78 #[doc(alias = "cairo_ps_surface_set_size")]
79 pub fn set_size(&self, width: f64, height: f64) {
80 unsafe {
81 ffi::cairo_ps_surface_set_size(self.0.to_raw_none(), width, height);
82 }
83 }
84
85 #[doc(alias = "cairo_ps_surface_dsc_begin_setup")]
86 pub fn dsc_begin_setup(&self) {
87 unsafe {
88 ffi::cairo_ps_surface_dsc_begin_setup(self.0.to_raw_none());
89 }
90 }
91
92 #[doc(alias = "cairo_ps_surface_dsc_begin_page_setup")]
93 pub fn begin_page_setup(&self) {
94 unsafe {
95 ffi::cairo_ps_surface_dsc_begin_page_setup(self.0.to_raw_none());
96 }
97 }
98
99 #[doc(alias = "cairo_ps_surface_dsc_comment")]
100 pub fn dsc_comment(&self, comment: &str) {
101 let comment = CString::new(comment).unwrap();
102 unsafe {
103 ffi::cairo_ps_surface_dsc_comment(self.0.to_raw_none(), comment.as_ptr());
104 }
105 }
106}
107
108#[cfg(test)]
109mod test {
110 use tempfile::tempfile;
111
112 use super::*;
113 use crate::context::*;
114
115 fn draw(surface: &Surface) {
116 let cr = Context::new(surface).expect("Can't create a Cairo context");
117
118 cr.set_line_width(25.0);
122
123 cr.set_source_rgb(1.0, 0.0, 0.0);
124 cr.line_to(0., 0.);
125 cr.line_to(100., 100.);
126 cr.stroke().expect("Surface on an invalid state");
127
128 cr.set_source_rgb(0.0, 0.0, 1.0);
129 cr.line_to(0., 100.);
130 cr.line_to(100., 0.);
131 cr.stroke().expect("Surface on an invalid state");
132 }
133
134 fn draw_in_buffer() -> Vec<u8> {
135 let buffer: Vec<u8> = vec![];
136
137 let surface = PsSurface::for_stream(100., 100., buffer).unwrap();
138 draw(&surface);
139 *surface.finish_output_stream().unwrap().downcast().unwrap()
140 }
141
142 #[test]
143 fn levels() {
144 assert!(PsSurface::levels().any(|v| v == PsLevel::_2));
145 }
146
147 #[test]
148 fn level_string() {
149 let ver_str = PsLevel::_2.as_str().unwrap();
150 assert_eq!(ver_str, "PS Level 2");
151 }
152
153 #[test]
154 fn eps() {
155 let buffer: Vec<u8> = vec![];
156 let surface = PsSurface::for_stream(100., 100., buffer).unwrap();
157 surface.set_eps(true);
158 assert!(surface.is_eps());
159 }
160
161 #[test]
162 #[cfg(unix)]
163 fn file() {
164 let surface = PsSurface::new(100., 100., "/dev/null").unwrap();
165 draw(&surface);
166 surface.finish();
167 }
168
169 #[test]
170 fn writer() {
171 let file = tempfile().expect("tempfile failed");
172 let surface = PsSurface::for_stream(100., 100., file).unwrap();
173
174 draw(&surface);
175 let stream = surface.finish_output_stream().unwrap();
176 let file = stream.downcast::<std::fs::File>().unwrap();
177
178 let buffer = draw_in_buffer();
179 let file_size = file.metadata().unwrap().len();
180 assert_eq!(file_size, buffer.len() as u64);
181 }
182
183 #[test]
184 fn ref_writer() {
185 let mut file = tempfile().expect("tempfile failed");
186 let surface = unsafe { PsSurface::for_raw_stream(100., 100., &mut file).unwrap() };
187
188 draw(&surface);
189 surface.finish_output_stream().unwrap();
190 }
191
192 #[test]
193 fn buffer() {
194 let buffer = draw_in_buffer();
195
196 let header = b"%!PS-Adobe";
197 assert_eq!(&buffer[..header.len()], header);
198 }
199
200 #[test]
201 fn custom_writer() {
202 struct CustomWriter(usize);
203
204 impl io::Write for CustomWriter {
205 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
206 self.0 += buf.len();
207 Ok(buf.len())
208 }
209
210 fn flush(&mut self) -> io::Result<()> {
211 Ok(())
212 }
213 }
214
215 let custom_writer = CustomWriter(0);
216
217 let surface = PsSurface::for_stream(20., 20., custom_writer).unwrap();
218 surface.set_size(100., 100.);
219 draw(&surface);
220 let stream = surface.finish_output_stream().unwrap();
221 let custom_writer = stream.downcast::<CustomWriter>().unwrap();
222
223 let buffer = draw_in_buffer();
224
225 assert_eq!(custom_writer.0, buffer.len());
226 }
227
228 fn with_panicky_stream() -> PsSurface {
229 struct PanicWriter;
230
231 impl io::Write for PanicWriter {
232 fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
233 panic!("panic in writer");
234 }
235 fn flush(&mut self) -> io::Result<()> {
236 Ok(())
237 }
238 }
239
240 let surface = PsSurface::for_stream(20., 20., PanicWriter).unwrap();
241 surface.finish();
242 surface
243 }
244
245 #[test]
246 #[should_panic]
247 fn finish_stream_propagates_panic() {
248 let _ = with_panicky_stream().finish_output_stream();
249 }
250}