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    pub(crate) fn new(stream: RustWStream<'a>, document: RCHandle<SkDocument>) -> Self {
70        Document {
71            document,
72            stream,
73            state: state::Open { pages: 0 },
74        }
75    }
76
77    /// The number of pages in this document.
78    pub fn pages(&self) -> usize {
79        self.state.pages
80    }
81
82    // This function consumes the document and returns a document containing a
83    // canvas that represents the page it's currently drawing on.
84    pub fn begin_page(
85        mut self,
86        size: impl Into<Size>,
87        content: Option<&Rect>,
88    ) -> Document<'a, state::OnPage> {
89        let size = size.into();
90        let canvas = unsafe {
91            self.document.native_mut().beginPage(
92                size.width,
93                size.height,
94                content.native_ptr_or_null(),
95            )
96        };
97
98        Document {
99            stream: self.stream,
100            document: self.document,
101            state: state::OnPage {
102                canvas: ptr::NonNull::new(canvas).unwrap(),
103                page: self.state.pages + 1,
104            },
105        }
106    }
107
108    /// Close the document and return the encoded representation.
109    ///
110    /// This function consumes and drops the document.
111    pub fn close(mut self) {
112        unsafe {
113            self.document.native_mut().close();
114        };
115    }
116}
117
118impl<'a> Document<'a, state::OnPage> {
119    /// The current page we are currently drawing on.
120    pub fn page(&self) -> usize {
121        self.state.page
122    }
123
124    /// Borrows the canvas for the current page on the document.
125    pub fn canvas(&mut self) -> &Canvas {
126        Canvas::borrow_from_native(unsafe { self.state.canvas.as_ref() })
127    }
128
129    /// Ends the page.
130    ///
131    /// This function consumes the document and returns a new open document that
132    /// contains the pages drawn so far.
133    pub fn end_page(mut self) -> Document<'a> {
134        unsafe {
135            self.document.native_mut().endPage();
136        }
137
138        Document {
139            stream: self.stream,
140            document: self.document,
141            state: state::Open {
142                pages: self.state.page,
143            },
144        }
145
146        // TODO: Think about providing a close() function that implicitly ends the page
147        //       and calls close() on the Open document.
148        // TODO: Think about providing a begin_page() function that implicitly ends the
149        //       current page.
150    }
151}