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