1 | use std::{fmt, ptr}; |
2 | |
3 | use skia_bindings::{self as sb, SkDocument, SkRefCntBase}; |
4 | |
5 | use crate::{interop::RustWStream, prelude::*, Canvas, Rect, Size}; |
6 | |
7 | pub 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 | |
15 | require_type_equality!(sb::SkDocument_INHERITED, sb::SkRefCnt); |
16 | |
17 | impl NativeRefCountedBase for SkDocument { |
18 | type Base = SkRefCntBase; |
19 | } |
20 | |
21 | impl<State: fmt::Debug> fmt::Debug for Document<'_, State> { |
22 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
23 | f&mut DebugStruct<'_, '_>.debug_struct("Document" ) |
24 | .field(name:"state" , &self.state) |
25 | .finish() |
26 | } |
27 | } |
28 | |
29 | pub 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 | |
61 | impl<State> Document<'_, State> { |
62 | pub fn abort(mut self) { |
63 | unsafe { self.document.native_mut().abort() } |
64 | drop(self) |
65 | } |
66 | } |
67 | |
68 | impl<'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 | |
118 | impl<'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 | } |
152 | |