1use std::convert::TryFrom;
2
3use mupdf_sys::*;
4
5use crate::{context, from_enum, Error};
6
7from_enum! { fz_linecap => fz_linecap,
8 #[derive(Debug, Default, Clone, Copy, PartialEq)]
9 pub enum LineCap {
10 #[default]
11 Butt = FZ_LINECAP_BUTT,
12 Round = FZ_LINECAP_ROUND,
13 Square = FZ_LINECAP_SQUARE,
14 Triangle = FZ_LINECAP_TRIANGLE,
15 }
16}
17
18from_enum! { fz_linejoin => fz_linejoin,
19 #[derive(Debug, Default, Clone, Copy, PartialEq)]
20 pub enum LineJoin {
21 #[default]
22 Miter = FZ_LINEJOIN_MITER,
23 Round = FZ_LINEJOIN_ROUND,
24 Bevel = FZ_LINEJOIN_BEVEL,
25 MiterXps = FZ_LINEJOIN_MITER_XPS,
26 }
27}
28
29#[derive(Debug)]
30pub struct StrokeState {
31 pub(crate) inner: *mut fz_stroke_state,
32}
33
34impl StrokeState {
35 #[allow(clippy::too_many_arguments)]
36 pub fn new(
37 start_cap: LineCap,
38 dash_cap: LineCap,
39 end_cap: LineCap,
40 line_join: LineJoin,
41 line_width: f32,
42 miter_limit: f32,
43 dash_phase: f32,
44 dash: &[f32],
45 ) -> Result<Self, Error> {
46 let dash_len = dash.len() as i32;
47 unsafe {
48 ffi_try!(mupdf_new_stroke_state(
49 context(),
50 start_cap.into(),
51 dash_cap.into(),
52 end_cap.into(),
53 line_join.into(),
54 line_width,
55 miter_limit,
56 dash_phase,
57 dash.as_ptr(),
58 dash_len
59 ))
60 }
61 .map(|inner| Self { inner })
62 }
63
64 pub fn try_clone(&self) -> Result<Self, Error> {
65 let start_cap = self.start_cap();
66 let dash_cap = self.dash_cap();
67 let end_cap = self.end_cap();
68 let line_join = self.line_join();
69 let line_width = self.line_width();
70 let miter_limit = self.miter_limit();
71 let dash_phase = self.dash_phase();
72 let dashes = self.dashes();
73 Self::new(
74 start_cap,
75 dash_cap,
76 end_cap,
77 line_join,
78 line_width,
79 miter_limit,
80 dash_phase,
81 &dashes,
82 )
83 }
84
85 pub fn start_cap(&self) -> LineCap {
86 LineCap::try_from(unsafe { (*self.inner).start_cap }).unwrap()
87 }
88
89 pub fn dash_cap(&self) -> LineCap {
90 LineCap::try_from(unsafe { (*self.inner).dash_cap }).unwrap()
91 }
92
93 pub fn end_cap(&self) -> LineCap {
94 LineCap::try_from(unsafe { (*self.inner).end_cap }).unwrap()
95 }
96
97 pub fn line_join(&self) -> LineJoin {
98 LineJoin::try_from(unsafe { (*self.inner).linejoin }).unwrap()
99 }
100
101 pub fn line_width(&self) -> f32 {
102 unsafe { (*self.inner).linewidth }
103 }
104
105 pub fn miter_limit(&self) -> f32 {
106 unsafe { (*self.inner).miterlimit }
107 }
108
109 pub fn dash_phase(&self) -> f32 {
110 unsafe { (*self.inner).dash_phase }
111 }
112
113 pub fn dashes(&self) -> Vec<f32> {
114 unsafe {
115 let dash_len = (*self.inner).dash_len as usize;
116 let dash_ptr = (*self.inner).dash_list.as_ptr();
117 let mut dash_list = Vec::with_capacity(dash_len);
118 dash_list.extend_from_slice(std::slice::from_raw_parts(dash_ptr, dash_len));
119 dash_list
120 }
121 }
122}
123
124impl Drop for StrokeState {
125 fn drop(&mut self) {
126 if !self.inner.is_null() {
127 unsafe {
128 fz_drop_stroke_state(context(), self.inner);
129 }
130 }
131 }
132}
133
134impl Clone for StrokeState {
135 fn clone(&self) -> StrokeState {
136 self.try_clone().unwrap()
137 }
138}
139
140impl Default for StrokeState {
141 fn default() -> Self {
142 let inner = unsafe { mupdf_default_stroke_state(context()) };
143 Self { inner }
144 }
145}
146
147#[cfg(test)]
148mod test {
149 use super::StrokeState;
150
151 #[test]
152 fn test_default_stroke_state() {
153 let _stroke = StrokeState::default();
154 }
155}