Skip to main content

skia_safe/core/
document.rs

1use std::{fmt, ptr};
2
3use skia_bindings::{self as sb, SkDocument, SkRefCntBase};
4
5use crate::{interop::RustWStream, prelude::*, Canvas, Rect, Size};
6
7pub struct Document<'a, State = state::Open> {
8    // Order matters here, first the document must be dropped _and then_ the stream.
9    document: RCHandle<SkDocument>,
10    stream: RustWStream<'a>,
11
12    state: State,
13}
14
15require_type_equality!(sb::SkDocument_INHERITED, sb::SkRefCnt);
16
17impl NativeRefCountedBase for SkDocument {
18    type Base = SkRefCntBase;
19}
20
21impl<State: fmt::Debug> fmt::Debug for Document<'_, State> {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        f.debug_struct("Document")
24            .field("state", &self.state)
25            .finish()
26    }
27}
28
29pub mod state {
30    use std::{fmt, ptr};
31
32    use skia_bindings::SkCanvas;
33
34    use crate::Canvas;
35
36    /// Document is currently open. May contain several pages.
37    #[derive(Debug)]
38    pub struct Open {
39        pub(crate) pages: usize,
40    }
41
42    /// Document is currently on a page and can be drawn onto.
43    pub struct OnPage {
44        pub(crate) page: usize,
45        pub(crate) canvas: ptr::NonNull<SkCanvas>,
46    }
47
48    impl fmt::Debug for OnPage {
49        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50            f.debug_struct("OnPage")
51                .field("page", &self.page)
52                .field(
53                    "canvas",
54                    Canvas::borrow_from_native(unsafe { self.canvas.as_ref() }),
55                )
56                .finish()
57        }
58    }
59}
60
61impl<State> Document<'_, State> {
62    pub fn abort(mut self) {
63        unsafe { self.document.native_mut().abort() }
64        drop(self)
65    }
66}
67
68impl<'a> Document<'a, state::Open> {
69    #[allow(unused)]
70    pub(crate) fn new(stream: RustWStream<'a>, document: RCHandle<SkDocument>) -> Self {
71        Document {
72            document,
73            stream,
74            state: state::Open { pages: 0 },
75        }
76    }
77
78    /// The number of pages in this document.
79    pub fn pages(&self) -> usize {
80        self.state.pages
81    }
82
83    // This function consumes the document and returns a document containing a
84    // canvas that represents the page it's currently drawing on.
85    pub fn begin_page(
86        mut self,
87        size: impl Into<Size>,
88        content: Option<&Rect>,
89    ) -> Document<'a, state::OnPage> {
90        let size = size.into();
91        let canvas = unsafe {
92            self.document.native_mut().beginPage(
93                size.width,
94                size.height,
95                content.native_ptr_or_null(),
96            )
97        };
98
99        Document {
100            stream: self.stream,
101            document: self.document,
102            state: state::OnPage {
103                canvas: ptr::NonNull::new(canvas).unwrap(),
104                page: self.state.pages + 1,
105            },
106        }
107    }
108
109    /// Close the document and return the encoded representation.
110    ///
111    /// This function consumes and drops the document.
112    pub fn close(mut self) {
113        unsafe {
114            self.document.native_mut().close();
115        };
116    }
117}
118
119impl<'a> Document<'a, state::OnPage> {
120    /// The current page we are currently drawing on.
121    pub fn page(&self) -> usize {
122        self.state.page
123    }
124
125    /// Borrows the canvas for the current page on the document.
126    pub fn canvas(&mut self) -> &Canvas {
127        Canvas::borrow_from_native(unsafe { self.state.canvas.as_ref() })
128    }
129
130    /// Ends the page.
131    ///
132    /// This function consumes the document and returns a new open document that
133    /// contains the pages drawn so far.
134    pub fn end_page(mut self) -> Document<'a> {
135        unsafe {
136            self.document.native_mut().endPage();
137        }
138
139        Document {
140            stream: self.stream,
141            document: self.document,
142            state: state::Open {
143                pages: self.state.page,
144            },
145        }
146
147        // TODO: Think about providing a close() function that implicitly ends the page
148        //       and calls close() on the Open document.
149        // TODO: Think about providing a begin_page() function that implicitly ends the
150        //       current page.
151    }
152}