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
use crate::interop::DynamicMemoryWStream;
use crate::prelude::*;
use crate::{Canvas, Data, Rect, Size};
use skia_bindings::{SkDocument, SkRefCntBase};
use std::pin::Pin;

pub struct Document<State = state::Open> {
    // note: order matters here, first the document must be
    // dropped _and then_ the stream.
    document: RCHandle<SkDocument>,
    stream: Pin<Box<DynamicMemoryWStream>>,

    state: State,
}

impl NativeRefCountedBase for SkDocument {
    type Base = SkRefCntBase;

    fn ref_counted_base(&self) -> &Self::Base {
        &self._base._base
    }
}

pub mod state {
    use skia_bindings::SkCanvas;

    /// Document is currently open. May contain several pages.
    pub struct Open {
        pub(crate) pages: usize,
    }

    /// Document is currently on a page and can be drawn onto.
    pub struct OnPage {
        pub(crate) canvas: *mut SkCanvas,
        pub(crate) page: usize,
    }
}

impl<S> Document<S> {
    pub fn abort(mut self) {
        unsafe { self.document.native_mut().abort() }
        drop(self)
    }
}

impl Document {
    pub(crate) fn new(
        stream: Pin<Box<DynamicMemoryWStream>>,
        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<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,
                page: self.state.pages + 1,
            },
        } as _
    }

    /// Close the document and return the encoded representation.
    /// This function consumes and drops the document.
    pub fn close(mut self) -> Data {
        unsafe {
            self.document.native_mut().close();
        };
        self.stream.detach_as_data()
    }
}

impl Document<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) -> &mut Canvas {
        Canvas::borrow_from_native(unsafe { &mut *self.state.canvas })
    }

    /// 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 {
        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.
    }
}