cairo/
context.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3#[cfg(feature = "use_glib")]
4use std::marker::PhantomData;
5use std::{ffi::CString, fmt, mem::MaybeUninit, ops, ptr, slice};
6
7#[cfg(feature = "use_glib")]
8use glib::translate::*;
9
10use crate::{
11    Antialias, Content, Error, FillRule, FontExtents, FontFace, FontOptions, FontSlant, FontWeight,
12    Glyph, LineCap, LineJoin, Matrix, Operator, Path, Pattern, Rectangle, ScaledFont, Surface,
13    TextCluster, TextClusterFlags, TextExtents, ffi, utils::status_to_result,
14};
15
16pub struct RectangleList {
17    ptr: *mut ffi::cairo_rectangle_list_t,
18}
19
20impl ops::Deref for RectangleList {
21    type Target = [Rectangle];
22
23    #[inline]
24    fn deref(&self) -> &[Rectangle] {
25        unsafe {
26            let ptr = (*self.ptr).rectangles as *mut Rectangle;
27            let len = (*self.ptr).num_rectangles;
28
29            if ptr.is_null() || len == 0 {
30                &[]
31            } else {
32                slice::from_raw_parts(ptr, len as usize)
33            }
34        }
35    }
36}
37
38impl Drop for RectangleList {
39    #[inline]
40    fn drop(&mut self) {
41        unsafe {
42            ffi::cairo_rectangle_list_destroy(self.ptr);
43        }
44    }
45}
46
47impl fmt::Debug for RectangleList {
48    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49        use std::ops::Deref;
50        f.debug_tuple("RectangleList").field(&self.deref()).finish()
51    }
52}
53
54#[derive(Debug)]
55#[repr(transparent)]
56#[doc(alias = "cairo_t")]
57pub struct Context(ptr::NonNull<ffi::cairo_t>);
58
59#[cfg(feature = "use_glib")]
60#[cfg_attr(docsrs, doc(cfg(feature = "use_glib")))]
61impl IntoGlibPtr<*mut ffi::cairo_t> for Context {
62    #[inline]
63    fn into_glib_ptr(self) -> *mut ffi::cairo_t {
64        (&*std::mem::ManuallyDrop::new(self)).to_glib_none().0
65    }
66}
67
68#[cfg(feature = "use_glib")]
69#[cfg_attr(docsrs, doc(cfg(feature = "use_glib")))]
70impl<'a> ToGlibPtr<'a, *mut ffi::cairo_t> for &'a Context {
71    type Storage = PhantomData<&'a Context>;
72
73    #[inline]
74    fn to_glib_none(&self) -> Stash<'a, *mut ffi::cairo_t, &'a Context> {
75        Stash(self.0.as_ptr(), PhantomData)
76    }
77
78    #[inline]
79    fn to_glib_full(&self) -> *mut ffi::cairo_t {
80        unsafe { ffi::cairo_reference(self.0.as_ptr()) }
81    }
82}
83
84#[cfg(feature = "use_glib")]
85#[cfg_attr(docsrs, doc(cfg(feature = "use_glib")))]
86impl FromGlibPtrNone<*mut ffi::cairo_t> for Context {
87    #[inline]
88    unsafe fn from_glib_none(ptr: *mut ffi::cairo_t) -> Context {
89        unsafe { Self::from_raw_none(ptr) }
90    }
91}
92
93#[cfg(feature = "use_glib")]
94#[cfg_attr(docsrs, doc(cfg(feature = "use_glib")))]
95impl FromGlibPtrBorrow<*mut ffi::cairo_t> for Context {
96    #[inline]
97    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_t) -> crate::Borrowed<Context> {
98        unsafe { Self::from_raw_borrow(ptr) }
99    }
100}
101
102#[cfg(feature = "use_glib")]
103#[cfg_attr(docsrs, doc(cfg(feature = "use_glib")))]
104impl FromGlibPtrFull<*mut ffi::cairo_t> for Context {
105    #[inline]
106    unsafe fn from_glib_full(ptr: *mut ffi::cairo_t) -> Context {
107        unsafe { Self::from_raw_full(ptr) }
108    }
109}
110
111#[cfg(feature = "use_glib")]
112gvalue_impl!(
113    Context,
114    ffi::cairo_t,
115    ffi::gobject::cairo_gobject_context_get_type
116);
117
118impl Clone for Context {
119    #[inline]
120    fn clone(&self) -> Context {
121        unsafe { Self::from_raw_none(self.to_raw_none()) }
122    }
123}
124
125impl Drop for Context {
126    #[inline]
127    fn drop(&mut self) {
128        unsafe {
129            ffi::cairo_destroy(self.0.as_ptr());
130        }
131    }
132}
133
134impl Context {
135    #[inline]
136    pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_t) -> Context {
137        unsafe {
138            debug_assert!(!ptr.is_null());
139            ffi::cairo_reference(ptr);
140            Context(ptr::NonNull::new_unchecked(ptr))
141        }
142    }
143
144    #[inline]
145    pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_t) -> crate::Borrowed<Context> {
146        unsafe {
147            debug_assert!(!ptr.is_null());
148            crate::Borrowed::new(Context(ptr::NonNull::new_unchecked(ptr)))
149        }
150    }
151
152    #[inline]
153    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_t) -> Context {
154        unsafe {
155            debug_assert!(!ptr.is_null());
156            Context(ptr::NonNull::new_unchecked(ptr))
157        }
158    }
159
160    #[inline]
161    pub fn to_raw_none(&self) -> *mut ffi::cairo_t {
162        self.0.as_ptr()
163    }
164
165    #[doc(alias = "cairo_status")]
166    #[inline]
167    pub fn status(&self) -> Result<(), Error> {
168        let status = unsafe { ffi::cairo_status(self.0.as_ptr()) };
169        status_to_result(status)
170    }
171
172    pub fn new(target: impl AsRef<Surface>) -> Result<Context, Error> {
173        let ctx = unsafe { Self::from_raw_full(ffi::cairo_create(target.as_ref().to_raw_none())) };
174        ctx.status().map(|_| ctx)
175    }
176
177    #[doc(alias = "cairo_save")]
178    pub fn save(&self) -> Result<(), Error> {
179        unsafe { ffi::cairo_save(self.0.as_ptr()) }
180        self.status()
181    }
182
183    #[doc(alias = "cairo_restore")]
184    pub fn restore(&self) -> Result<(), Error> {
185        unsafe { ffi::cairo_restore(self.0.as_ptr()) }
186        self.status()
187    }
188
189    #[doc(alias = "get_target")]
190    #[doc(alias = "cairo_get_target")]
191    pub fn target(&self) -> Surface {
192        unsafe { Surface::from_raw_none(ffi::cairo_get_target(self.0.as_ptr())) }
193    }
194
195    #[doc(alias = "cairo_push_group")]
196    pub fn push_group(&self) {
197        unsafe { ffi::cairo_push_group(self.0.as_ptr()) }
198    }
199
200    #[doc(alias = "cairo_push_group_with_content")]
201    pub fn push_group_with_content(&self, content: Content) {
202        unsafe { ffi::cairo_push_group_with_content(self.0.as_ptr(), content.into()) }
203    }
204
205    #[doc(alias = "cairo_pop_group")]
206    pub fn pop_group(&self) -> Result<Pattern, Error> {
207        let pattern = unsafe { Pattern::from_raw_full(ffi::cairo_pop_group(self.0.as_ptr())) };
208        self.status().map(|_| pattern)
209    }
210
211    #[doc(alias = "cairo_pop_group_to_source")]
212    pub fn pop_group_to_source(&self) -> Result<(), Error> {
213        unsafe { ffi::cairo_pop_group_to_source(self.0.as_ptr()) };
214        self.status()
215    }
216
217    #[doc(alias = "get_group_target")]
218    #[doc(alias = "cairo_get_group_target")]
219    pub fn group_target(&self) -> Surface {
220        unsafe { Surface::from_raw_none(ffi::cairo_get_group_target(self.0.as_ptr())) }
221    }
222
223    #[doc(alias = "cairo_set_source_rgb")]
224    pub fn set_source_rgb(&self, red: f64, green: f64, blue: f64) {
225        unsafe { ffi::cairo_set_source_rgb(self.0.as_ptr(), red, green, blue) }
226    }
227
228    #[doc(alias = "cairo_set_source_rgba")]
229    pub fn set_source_rgba(&self, red: f64, green: f64, blue: f64, alpha: f64) {
230        unsafe { ffi::cairo_set_source_rgba(self.0.as_ptr(), red, green, blue, alpha) }
231    }
232
233    #[doc(alias = "cairo_set_source")]
234    pub fn set_source(&self, source: impl AsRef<Pattern>) -> Result<(), Error> {
235        let source = source.as_ref();
236        source.status()?;
237        unsafe {
238            ffi::cairo_set_source(self.0.as_ptr(), source.to_raw_none());
239        }
240        self.status()
241    }
242
243    #[doc(alias = "get_source")]
244    #[doc(alias = "cairo_get_source")]
245    pub fn source(&self) -> Pattern {
246        unsafe { Pattern::from_raw_none(ffi::cairo_get_source(self.0.as_ptr())) }
247    }
248
249    #[doc(alias = "cairo_set_source_surface")]
250    pub fn set_source_surface(
251        &self,
252        surface: impl AsRef<Surface>,
253        x: f64,
254        y: f64,
255    ) -> Result<(), Error> {
256        let surface = surface.as_ref();
257        surface.status()?;
258        unsafe {
259            ffi::cairo_set_source_surface(self.0.as_ptr(), surface.to_raw_none(), x, y);
260        }
261        self.status()
262    }
263
264    #[doc(alias = "cairo_set_antialias")]
265    pub fn set_antialias(&self, antialias: Antialias) {
266        unsafe { ffi::cairo_set_antialias(self.0.as_ptr(), antialias.into()) }
267    }
268
269    #[doc(alias = "get_antialias")]
270    #[doc(alias = "cairo_get_antialias")]
271    pub fn antialias(&self) -> Antialias {
272        unsafe { Antialias::from(ffi::cairo_get_antialias(self.0.as_ptr())) }
273    }
274
275    #[doc(alias = "cairo_set_dash")]
276    pub fn set_dash(&self, dashes: &[f64], offset: f64) {
277        unsafe {
278            ffi::cairo_set_dash(
279                self.0.as_ptr(),
280                dashes.as_ptr(),
281                dashes.len() as i32,
282                offset,
283            )
284        }
285    }
286
287    #[doc(alias = "get_dash_count")]
288    #[doc(alias = "cairo_get_dash_count")]
289    pub fn dash_count(&self) -> i32 {
290        unsafe { ffi::cairo_get_dash_count(self.0.as_ptr()) }
291    }
292
293    #[doc(alias = "get_dash")]
294    #[doc(alias = "cairo_get_dash")]
295    pub fn dash(&self) -> (Vec<f64>, f64) {
296        let dash_count = self.dash_count() as usize;
297        let mut dashes: Vec<f64> = Vec::with_capacity(dash_count);
298        let mut offset: f64 = 0.0;
299
300        unsafe {
301            ffi::cairo_get_dash(self.0.as_ptr(), dashes.as_mut_ptr(), &mut offset);
302            dashes.set_len(dash_count);
303            (dashes, offset)
304        }
305    }
306
307    #[doc(alias = "get_dash_dashes")]
308    pub fn dash_dashes(&self) -> Vec<f64> {
309        let (dashes, _) = self.dash();
310        dashes
311    }
312
313    #[doc(alias = "get_dash_offset")]
314    pub fn dash_offset(&self) -> f64 {
315        let (_, offset) = self.dash();
316        offset
317    }
318
319    #[doc(alias = "cairo_set_fill_rule")]
320    pub fn set_fill_rule(&self, fill_rule: FillRule) {
321        unsafe {
322            ffi::cairo_set_fill_rule(self.0.as_ptr(), fill_rule.into());
323        }
324    }
325
326    #[doc(alias = "get_fill_rule")]
327    #[doc(alias = "cairo_get_fill_rule")]
328    pub fn fill_rule(&self) -> FillRule {
329        unsafe { FillRule::from(ffi::cairo_get_fill_rule(self.0.as_ptr())) }
330    }
331
332    #[doc(alias = "cairo_set_line_cap")]
333    pub fn set_line_cap(&self, arg: LineCap) {
334        unsafe { ffi::cairo_set_line_cap(self.0.as_ptr(), arg.into()) }
335    }
336
337    #[doc(alias = "get_line_cap")]
338    #[doc(alias = "cairo_get_line_cap")]
339    pub fn line_cap(&self) -> LineCap {
340        unsafe { LineCap::from(ffi::cairo_get_line_cap(self.0.as_ptr())) }
341    }
342
343    #[doc(alias = "cairo_set_line_join")]
344    pub fn set_line_join(&self, arg: LineJoin) {
345        unsafe { ffi::cairo_set_line_join(self.0.as_ptr(), arg.into()) }
346    }
347
348    #[doc(alias = "get_line_join")]
349    #[doc(alias = "cairo_get_line_join")]
350    pub fn line_join(&self) -> LineJoin {
351        unsafe { LineJoin::from(ffi::cairo_get_line_join(self.0.as_ptr())) }
352    }
353
354    #[doc(alias = "cairo_set_line_width")]
355    pub fn set_line_width(&self, arg: f64) {
356        unsafe { ffi::cairo_set_line_width(self.0.as_ptr(), arg) }
357    }
358
359    #[doc(alias = "get_line_width")]
360    #[doc(alias = "cairo_get_line_width")]
361    pub fn line_width(&self) -> f64 {
362        unsafe { ffi::cairo_get_line_width(self.0.as_ptr()) }
363    }
364
365    #[cfg(feature = "v1_18")]
366    #[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
367    #[doc(alias = "cairo_set_hairline")]
368    pub fn set_hairline(&self, set_hairline: bool) {
369        unsafe { ffi::cairo_set_hairline(self.0.as_ptr(), set_hairline.into()) }
370    }
371
372    #[cfg(feature = "v1_18")]
373    #[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
374    #[doc(alias = "get_hairline")]
375    #[doc(alias = "cairo_get_hairline")]
376    pub fn hairline(&self) -> bool {
377        unsafe { ffi::cairo_get_hairline(self.0.as_ptr()) }.as_bool()
378    }
379
380    #[doc(alias = "cairo_set_miter_limit")]
381    pub fn set_miter_limit(&self, arg: f64) {
382        unsafe { ffi::cairo_set_miter_limit(self.0.as_ptr(), arg) }
383    }
384
385    #[doc(alias = "get_miter_limit")]
386    #[doc(alias = "cairo_get_miter_limit")]
387    pub fn miter_limit(&self) -> f64 {
388        unsafe { ffi::cairo_get_miter_limit(self.0.as_ptr()) }
389    }
390
391    #[doc(alias = "cairo_set_operator")]
392    pub fn set_operator(&self, op: Operator) {
393        unsafe {
394            ffi::cairo_set_operator(self.0.as_ptr(), op.into());
395        }
396    }
397
398    #[doc(alias = "get_operator")]
399    #[doc(alias = "cairo_get_operator")]
400    pub fn operator(&self) -> Operator {
401        unsafe { Operator::from(ffi::cairo_get_operator(self.0.as_ptr())) }
402    }
403
404    #[doc(alias = "cairo_set_tolerance")]
405    pub fn set_tolerance(&self, arg: f64) {
406        unsafe { ffi::cairo_set_tolerance(self.0.as_ptr(), arg) }
407    }
408
409    #[doc(alias = "get_tolerance")]
410    #[doc(alias = "cairo_get_tolerance")]
411    pub fn tolerance(&self) -> f64 {
412        unsafe { ffi::cairo_get_tolerance(self.0.as_ptr()) }
413    }
414
415    #[doc(alias = "cairo_clip")]
416    pub fn clip(&self) {
417        unsafe { ffi::cairo_clip(self.0.as_ptr()) }
418    }
419
420    #[doc(alias = "cairo_clip_preserve")]
421    pub fn clip_preserve(&self) {
422        unsafe { ffi::cairo_clip_preserve(self.0.as_ptr()) }
423    }
424
425    #[doc(alias = "cairo_clip_extents")]
426    pub fn clip_extents(&self) -> Result<(f64, f64, f64, f64), Error> {
427        let mut x1: f64 = 0.0;
428        let mut y1: f64 = 0.0;
429        let mut x2: f64 = 0.0;
430        let mut y2: f64 = 0.0;
431
432        unsafe {
433            ffi::cairo_clip_extents(self.0.as_ptr(), &mut x1, &mut y1, &mut x2, &mut y2);
434        }
435        self.status().map(|_| (x1, y1, x2, y2))
436    }
437
438    #[doc(alias = "cairo_in_clip")]
439    pub fn in_clip(&self, x: f64, y: f64) -> Result<bool, Error> {
440        let in_clip = unsafe { ffi::cairo_in_clip(self.0.as_ptr(), x, y).as_bool() };
441        self.status().map(|_| in_clip)
442    }
443
444    #[doc(alias = "cairo_reset_clip")]
445    pub fn reset_clip(&self) {
446        unsafe { ffi::cairo_reset_clip(self.0.as_ptr()) }
447    }
448
449    #[doc(alias = "cairo_copy_clip_rectangle_list")]
450    pub fn copy_clip_rectangle_list(&self) -> Result<RectangleList, Error> {
451        unsafe {
452            let rectangle_list = ffi::cairo_copy_clip_rectangle_list(self.0.as_ptr());
453
454            status_to_result((*rectangle_list).status)?;
455
456            Ok(RectangleList {
457                ptr: rectangle_list,
458            })
459        }
460    }
461
462    #[doc(alias = "cairo_fill")]
463    pub fn fill(&self) -> Result<(), Error> {
464        unsafe { ffi::cairo_fill(self.0.as_ptr()) };
465        self.status()
466    }
467
468    #[doc(alias = "cairo_fill_preserve")]
469    pub fn fill_preserve(&self) -> Result<(), Error> {
470        unsafe { ffi::cairo_fill_preserve(self.0.as_ptr()) };
471        self.status()
472    }
473
474    #[doc(alias = "cairo_fill_extents")]
475    pub fn fill_extents(&self) -> Result<(f64, f64, f64, f64), Error> {
476        let mut x1: f64 = 0.0;
477        let mut y1: f64 = 0.0;
478        let mut x2: f64 = 0.0;
479        let mut y2: f64 = 0.0;
480
481        unsafe {
482            ffi::cairo_fill_extents(self.0.as_ptr(), &mut x1, &mut y1, &mut x2, &mut y2);
483        }
484        self.status().map(|_| (x1, y1, x2, y2))
485    }
486
487    #[doc(alias = "cairo_in_fill")]
488    pub fn in_fill(&self, x: f64, y: f64) -> Result<bool, Error> {
489        let in_fill = unsafe { ffi::cairo_in_fill(self.0.as_ptr(), x, y).as_bool() };
490        self.status().map(|_| in_fill)
491    }
492
493    #[doc(alias = "cairo_mask")]
494    pub fn mask(&self, pattern: impl AsRef<Pattern>) -> Result<(), Error> {
495        let pattern = pattern.as_ref();
496        pattern.status()?;
497        unsafe { ffi::cairo_mask(self.0.as_ptr(), pattern.to_raw_none()) };
498        self.status()
499    }
500
501    #[doc(alias = "cairo_mask_surface")]
502    pub fn mask_surface(&self, surface: impl AsRef<Surface>, x: f64, y: f64) -> Result<(), Error> {
503        let surface = surface.as_ref();
504        surface.status()?;
505        unsafe {
506            ffi::cairo_mask_surface(self.0.as_ptr(), surface.to_raw_none(), x, y);
507        };
508        self.status()
509    }
510
511    #[doc(alias = "cairo_paint")]
512    pub fn paint(&self) -> Result<(), Error> {
513        unsafe { ffi::cairo_paint(self.0.as_ptr()) };
514        self.status()
515    }
516
517    #[doc(alias = "cairo_paint_with_alpha")]
518    pub fn paint_with_alpha(&self, alpha: f64) -> Result<(), Error> {
519        unsafe { ffi::cairo_paint_with_alpha(self.0.as_ptr(), alpha) };
520        self.status()
521    }
522
523    #[doc(alias = "cairo_stroke")]
524    pub fn stroke(&self) -> Result<(), Error> {
525        unsafe { ffi::cairo_stroke(self.0.as_ptr()) };
526        self.status()
527    }
528
529    #[doc(alias = "cairo_stroke_preserve")]
530    pub fn stroke_preserve(&self) -> Result<(), Error> {
531        unsafe { ffi::cairo_stroke_preserve(self.0.as_ptr()) };
532        self.status()
533    }
534
535    #[doc(alias = "cairo_stroke_extents")]
536    pub fn stroke_extents(&self) -> Result<(f64, f64, f64, f64), Error> {
537        let mut x1: f64 = 0.0;
538        let mut y1: f64 = 0.0;
539        let mut x2: f64 = 0.0;
540        let mut y2: f64 = 0.0;
541
542        unsafe {
543            ffi::cairo_stroke_extents(self.0.as_ptr(), &mut x1, &mut y1, &mut x2, &mut y2);
544        }
545        self.status().map(|_| (x1, y1, x2, y2))
546    }
547
548    #[doc(alias = "cairo_in_stroke")]
549    pub fn in_stroke(&self, x: f64, y: f64) -> Result<bool, Error> {
550        let in_stroke = unsafe { ffi::cairo_in_stroke(self.0.as_ptr(), x, y).as_bool() };
551        self.status().map(|_| in_stroke)
552    }
553
554    #[doc(alias = "cairo_copy_page")]
555    pub fn copy_page(&self) -> Result<(), Error> {
556        unsafe { ffi::cairo_copy_page(self.0.as_ptr()) };
557        self.status()
558    }
559
560    #[doc(alias = "cairo_show_page")]
561    pub fn show_page(&self) -> Result<(), Error> {
562        unsafe { ffi::cairo_show_page(self.0.as_ptr()) };
563        self.status()
564    }
565
566    #[doc(alias = "get_reference_count")]
567    pub fn reference_count(&self) -> u32 {
568        unsafe { ffi::cairo_get_reference_count(self.0.as_ptr()) }
569    }
570
571    // transformations stuff
572
573    #[doc(alias = "cairo_translate")]
574    pub fn translate(&self, tx: f64, ty: f64) {
575        unsafe { ffi::cairo_translate(self.0.as_ptr(), tx, ty) }
576    }
577
578    #[doc(alias = "cairo_scale")]
579    pub fn scale(&self, sx: f64, sy: f64) {
580        unsafe { ffi::cairo_scale(self.0.as_ptr(), sx, sy) }
581    }
582
583    #[doc(alias = "cairo_rotate")]
584    pub fn rotate(&self, angle: f64) {
585        unsafe { ffi::cairo_rotate(self.0.as_ptr(), angle) }
586    }
587
588    #[doc(alias = "cairo_transform")]
589    pub fn transform(&self, matrix: Matrix) {
590        unsafe {
591            ffi::cairo_transform(self.0.as_ptr(), matrix.ptr());
592        }
593    }
594
595    #[doc(alias = "cairo_set_matrix")]
596    pub fn set_matrix(&self, matrix: Matrix) {
597        unsafe {
598            ffi::cairo_set_matrix(self.0.as_ptr(), matrix.ptr());
599        }
600    }
601
602    #[doc(alias = "get_matrix")]
603    #[doc(alias = "cairo_get_matrix")]
604    pub fn matrix(&self) -> Matrix {
605        let mut matrix = Matrix::null();
606        unsafe {
607            ffi::cairo_get_matrix(self.0.as_ptr(), matrix.mut_ptr());
608        }
609        matrix
610    }
611
612    #[doc(alias = "cairo_identity_matrix")]
613    pub fn identity_matrix(&self) {
614        unsafe { ffi::cairo_identity_matrix(self.0.as_ptr()) }
615    }
616
617    #[doc(alias = "cairo_user_to_device")]
618    pub fn user_to_device(&self, mut x: f64, mut y: f64) -> (f64, f64) {
619        unsafe {
620            ffi::cairo_user_to_device(self.0.as_ptr(), &mut x, &mut y);
621            (x, y)
622        }
623    }
624
625    #[doc(alias = "cairo_user_to_device_distance")]
626    pub fn user_to_device_distance(&self, mut dx: f64, mut dy: f64) -> Result<(f64, f64), Error> {
627        unsafe {
628            ffi::cairo_user_to_device_distance(self.0.as_ptr(), &mut dx, &mut dy);
629        };
630        self.status().map(|_| (dx, dy))
631    }
632
633    #[doc(alias = "cairo_device_to_user")]
634    pub fn device_to_user(&self, mut x: f64, mut y: f64) -> Result<(f64, f64), Error> {
635        unsafe {
636            ffi::cairo_device_to_user(self.0.as_ptr(), &mut x, &mut y);
637        }
638        self.status().map(|_| (x, y))
639    }
640
641    #[doc(alias = "cairo_device_to_user_distance")]
642    pub fn device_to_user_distance(&self, mut dx: f64, mut dy: f64) -> Result<(f64, f64), Error> {
643        unsafe {
644            ffi::cairo_device_to_user_distance(self.0.as_ptr(), &mut dx, &mut dy);
645        }
646        self.status().map(|_| (dx, dy))
647    }
648
649    // font stuff
650
651    #[doc(alias = "cairo_select_font_face")]
652    pub fn select_font_face(&self, family: &str, slant: FontSlant, weight: FontWeight) {
653        unsafe {
654            let family = CString::new(family).unwrap();
655            ffi::cairo_select_font_face(
656                self.0.as_ptr(),
657                family.as_ptr(),
658                slant.into(),
659                weight.into(),
660            )
661        }
662    }
663
664    #[doc(alias = "cairo_set_font_size")]
665    pub fn set_font_size(&self, size: f64) {
666        unsafe { ffi::cairo_set_font_size(self.0.as_ptr(), size) }
667    }
668
669    // FIXME probably needs a heap allocation
670    #[doc(alias = "cairo_set_font_matrix")]
671    pub fn set_font_matrix(&self, matrix: Matrix) {
672        unsafe { ffi::cairo_set_font_matrix(self.0.as_ptr(), matrix.ptr()) }
673    }
674
675    #[doc(alias = "get_font_matrix")]
676    #[doc(alias = "cairo_get_font_matrix")]
677    pub fn font_matrix(&self) -> Matrix {
678        let mut matrix = Matrix::null();
679        unsafe {
680            ffi::cairo_get_font_matrix(self.0.as_ptr(), matrix.mut_ptr());
681        }
682        matrix
683    }
684
685    #[doc(alias = "cairo_set_font_options")]
686    pub fn set_font_options(&self, options: &FontOptions) {
687        unsafe { ffi::cairo_set_font_options(self.0.as_ptr(), options.to_raw_none()) }
688    }
689
690    #[doc(alias = "get_font_options")]
691    #[doc(alias = "cairo_get_font_options")]
692    pub fn font_options(&self) -> Result<FontOptions, Error> {
693        let out = FontOptions::new()?;
694        unsafe {
695            ffi::cairo_get_font_options(self.0.as_ptr(), out.to_raw_none());
696        }
697        Ok(out)
698    }
699
700    #[doc(alias = "cairo_set_font_face")]
701    pub fn set_font_face(&self, font_face: &FontFace) {
702        unsafe { ffi::cairo_set_font_face(self.0.as_ptr(), font_face.to_raw_none()) }
703    }
704
705    #[doc(alias = "get_font_face")]
706    #[doc(alias = "cairo_get_font_face")]
707    pub fn font_face(&self) -> FontFace {
708        unsafe { FontFace::from_raw_none(ffi::cairo_get_font_face(self.0.as_ptr())) }
709    }
710
711    #[doc(alias = "cairo_set_scaled_font")]
712    pub fn set_scaled_font(&self, scaled_font: &ScaledFont) {
713        unsafe { ffi::cairo_set_scaled_font(self.0.as_ptr(), scaled_font.to_raw_none()) }
714    }
715
716    #[doc(alias = "get_scaled_font")]
717    #[doc(alias = "cairo_get_scaled_font")]
718    pub fn scaled_font(&self) -> ScaledFont {
719        unsafe { ScaledFont::from_raw_none(ffi::cairo_get_scaled_font(self.0.as_ptr())) }
720    }
721
722    #[doc(alias = "cairo_show_text")]
723    pub fn show_text(&self, text: &str) -> Result<(), Error> {
724        unsafe {
725            let text = CString::new(text).unwrap();
726            ffi::cairo_show_text(self.0.as_ptr(), text.as_ptr())
727        };
728        self.status()
729    }
730
731    #[doc(alias = "cairo_show_glyphs")]
732    pub fn show_glyphs(&self, glyphs: &[Glyph]) -> Result<(), Error> {
733        unsafe {
734            ffi::cairo_show_glyphs(
735                self.0.as_ptr(),
736                glyphs.as_ptr() as *const _,
737                glyphs.len() as _,
738            )
739        };
740        self.status()
741    }
742
743    #[doc(alias = "cairo_show_text_glyphs")]
744    pub fn show_text_glyphs(
745        &self,
746        text: &str,
747        glyphs: &[Glyph],
748        clusters: &[TextCluster],
749        cluster_flags: TextClusterFlags,
750    ) -> Result<(), Error> {
751        unsafe {
752            let text = CString::new(text).unwrap();
753            ffi::cairo_show_text_glyphs(
754                self.0.as_ptr(),
755                text.as_ptr(),
756                -1_i32, //NULL terminated
757                glyphs.as_ptr() as *const _,
758                glyphs.len() as _,
759                clusters.as_ptr() as *const _,
760                clusters.len() as _,
761                cluster_flags.into(),
762            )
763        };
764        self.status()
765    }
766
767    #[doc(alias = "cairo_font_extents")]
768    pub fn font_extents(&self) -> Result<FontExtents, Error> {
769        let mut extents = MaybeUninit::<FontExtents>::uninit();
770
771        unsafe {
772            ffi::cairo_font_extents(self.0.as_ptr(), extents.as_mut_ptr() as *mut _);
773            self.status().map(|_| extents.assume_init() as _)
774        }
775    }
776
777    #[doc(alias = "cairo_text_extents")]
778    pub fn text_extents(&self, text: &str) -> Result<TextExtents, Error> {
779        let mut extents = MaybeUninit::<TextExtents>::uninit();
780
781        unsafe {
782            let text = CString::new(text).unwrap();
783            ffi::cairo_text_extents(
784                self.0.as_ptr(),
785                text.as_ptr(),
786                extents.as_mut_ptr() as *mut _,
787            );
788            self.status().map(|_| extents.assume_init())
789        }
790    }
791
792    #[doc(alias = "cairo_glyph_extents")]
793    pub fn glyph_extents(&self, glyphs: &[Glyph]) -> Result<TextExtents, Error> {
794        let mut extents = MaybeUninit::<TextExtents>::uninit();
795
796        unsafe {
797            ffi::cairo_glyph_extents(
798                self.0.as_ptr(),
799                glyphs.as_ptr() as *const _,
800                glyphs.len() as _,
801                extents.as_mut_ptr() as *mut _,
802            );
803            self.status().map(|_| extents.assume_init())
804        }
805    }
806
807    // paths stuff
808
809    #[doc(alias = "cairo_copy_path")]
810    pub fn copy_path(&self) -> Result<Path, Error> {
811        let path = unsafe { Path::from_raw_full(ffi::cairo_copy_path(self.0.as_ptr())) };
812        self.status().map(|_| path)
813    }
814
815    #[doc(alias = "cairo_copy_path_flat")]
816    pub fn copy_path_flat(&self) -> Result<Path, Error> {
817        let path = unsafe { Path::from_raw_full(ffi::cairo_copy_path_flat(self.0.as_ptr())) };
818        self.status().map(|_| path)
819    }
820
821    #[doc(alias = "cairo_append_path")]
822    pub fn append_path(&self, path: &Path) {
823        unsafe { ffi::cairo_append_path(self.0.as_ptr(), path.as_ptr()) }
824    }
825
826    #[doc(alias = "cairo_has_current_point")]
827    pub fn has_current_point(&self) -> Result<bool, Error> {
828        let has_current_point = unsafe { ffi::cairo_has_current_point(self.0.as_ptr()).as_bool() };
829        self.status().map(|_| has_current_point)
830    }
831
832    #[doc(alias = "get_current_point")]
833    #[doc(alias = "cairo_get_current_point")]
834    pub fn current_point(&self) -> Result<(f64, f64), Error> {
835        unsafe {
836            let mut x = 0.0;
837            let mut y = 0.0;
838            ffi::cairo_get_current_point(self.0.as_ptr(), &mut x, &mut y);
839            self.status().map(|_| (x, y))
840        }
841    }
842
843    #[doc(alias = "cairo_new_path")]
844    pub fn new_path(&self) {
845        unsafe { ffi::cairo_new_path(self.0.as_ptr()) }
846    }
847
848    #[doc(alias = "cairo_new_sub_path")]
849    pub fn new_sub_path(&self) {
850        unsafe { ffi::cairo_new_sub_path(self.0.as_ptr()) }
851    }
852
853    #[doc(alias = "cairo_close_path")]
854    pub fn close_path(&self) {
855        unsafe { ffi::cairo_close_path(self.0.as_ptr()) }
856    }
857
858    #[doc(alias = "cairo_arc")]
859    pub fn arc(&self, xc: f64, yc: f64, radius: f64, angle1: f64, angle2: f64) {
860        unsafe { ffi::cairo_arc(self.0.as_ptr(), xc, yc, radius, angle1, angle2) }
861    }
862
863    #[doc(alias = "cairo_arc_negative")]
864    pub fn arc_negative(&self, xc: f64, yc: f64, radius: f64, angle1: f64, angle2: f64) {
865        unsafe { ffi::cairo_arc_negative(self.0.as_ptr(), xc, yc, radius, angle1, angle2) }
866    }
867
868    #[doc(alias = "cairo_curve_to")]
869    pub fn curve_to(&self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) {
870        unsafe { ffi::cairo_curve_to(self.0.as_ptr(), x1, y1, x2, y2, x3, y3) }
871    }
872
873    #[doc(alias = "cairo_line_to")]
874    pub fn line_to(&self, x: f64, y: f64) {
875        unsafe { ffi::cairo_line_to(self.0.as_ptr(), x, y) }
876    }
877
878    #[doc(alias = "cairo_move_to")]
879    pub fn move_to(&self, x: f64, y: f64) {
880        unsafe { ffi::cairo_move_to(self.0.as_ptr(), x, y) }
881    }
882
883    #[doc(alias = "cairo_rectangle")]
884    pub fn rectangle(&self, x: f64, y: f64, width: f64, height: f64) {
885        unsafe { ffi::cairo_rectangle(self.0.as_ptr(), x, y, width, height) }
886    }
887
888    #[doc(alias = "cairo_text_path")]
889    pub fn text_path(&self, str_: &str) {
890        unsafe {
891            let str_ = CString::new(str_).unwrap();
892            ffi::cairo_text_path(self.0.as_ptr(), str_.as_ptr())
893        }
894    }
895
896    #[doc(alias = "cairo_glyph_path")]
897    pub fn glyph_path(&self, glyphs: &[Glyph]) {
898        unsafe {
899            ffi::cairo_glyph_path(
900                self.0.as_ptr(),
901                glyphs.as_ptr() as *const _,
902                glyphs.len() as _,
903            )
904        }
905    }
906
907    #[doc(alias = "cairo_rel_curve_to")]
908    pub fn rel_curve_to(&self, dx1: f64, dy1: f64, dx2: f64, dy2: f64, dx3: f64, dy3: f64) {
909        unsafe { ffi::cairo_rel_curve_to(self.0.as_ptr(), dx1, dy1, dx2, dy2, dx3, dy3) }
910    }
911
912    #[doc(alias = "cairo_rel_line_to")]
913    pub fn rel_line_to(&self, dx: f64, dy: f64) {
914        unsafe { ffi::cairo_rel_line_to(self.0.as_ptr(), dx, dy) }
915    }
916
917    #[doc(alias = "cairo_rel_move_to")]
918    pub fn rel_move_to(&self, dx: f64, dy: f64) {
919        unsafe { ffi::cairo_rel_move_to(self.0.as_ptr(), dx, dy) }
920    }
921
922    #[doc(alias = "cairo_path_extents")]
923    pub fn path_extents(&self) -> Result<(f64, f64, f64, f64), Error> {
924        let mut x1: f64 = 0.0;
925        let mut y1: f64 = 0.0;
926        let mut x2: f64 = 0.0;
927        let mut y2: f64 = 0.0;
928
929        unsafe {
930            ffi::cairo_path_extents(self.0.as_ptr(), &mut x1, &mut y1, &mut x2, &mut y2);
931        }
932        self.status().map(|_| (x1, y1, x2, y2))
933    }
934
935    #[cfg(feature = "v1_16")]
936    #[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
937    #[doc(alias = "cairo_tag_begin")]
938    pub fn tag_begin(&self, tag_name: &str, attributes: &str) {
939        unsafe {
940            let tag_name = CString::new(tag_name).unwrap();
941            let attributes = CString::new(attributes).unwrap();
942            ffi::cairo_tag_begin(self.0.as_ptr(), tag_name.as_ptr(), attributes.as_ptr())
943        }
944    }
945
946    #[cfg(feature = "v1_16")]
947    #[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
948    #[doc(alias = "cairo_tag_end")]
949    pub fn tag_end(&self, tag_name: &str) {
950        unsafe {
951            let tag_name = CString::new(tag_name).unwrap();
952            ffi::cairo_tag_end(self.0.as_ptr(), tag_name.as_ptr())
953        }
954    }
955}
956
957#[cfg(test)]
958mod tests {
959    use float_eq::float_eq;
960
961    use super::*;
962    use crate::{enums::Format, image_surface::ImageSurface, patterns::LinearGradient};
963
964    fn create_ctx() -> Context {
965        let surface = ImageSurface::create(Format::ARgb32, 10, 10).unwrap();
966        Context::new(&surface).expect("Can't create a Cairo context")
967    }
968
969    #[test]
970    fn invalid_surface_cant_create_context() {
971        unsafe {
972            // The size here will create an image surface in an error state
973            let image_surf =
974                ffi::cairo_image_surface_create(Format::ARgb32.into(), 100_000, 100_000);
975
976            // from_raw_none() as from_raw_full() checks the surface status, and we *want*
977            // a surface in an error state.
978            let wrapped = Surface::from_raw_none(image_surf);
979
980            assert!(Context::new(&wrapped).is_err());
981
982            ffi::cairo_surface_destroy(image_surf);
983        }
984    }
985
986    #[test]
987    fn drop_non_reference_pattern_from_ctx() {
988        let ctx = create_ctx();
989        ctx.source();
990    }
991
992    #[test]
993    fn drop_non_reference_pattern() {
994        let ctx = create_ctx();
995        let pattern = LinearGradient::new(1.0f64, 2.0f64, 3.0f64, 4.0f64);
996        ctx.set_source(&pattern).expect("Invalid surface state");
997    }
998
999    #[test]
1000    fn clip_rectangle() {
1001        let ctx = create_ctx();
1002        let rect = ctx
1003            .copy_clip_rectangle_list()
1004            .expect("Failed to copy rectangle list");
1005        let first_rect = rect[0];
1006        assert!(float_eq!(first_rect.x(), 0.0, abs <= 0.000_1));
1007        assert!(float_eq!(first_rect.y(), 0.0, abs <= 0.000_1));
1008        assert!(float_eq!(first_rect.width(), 10.0, abs <= 0.000_1));
1009        assert!(float_eq!(first_rect.height(), 10.0, abs <= 0.000_1));
1010    }
1011}