mupdf 0.6.0

Safe Rust wrapper to MuPDF
Documentation
use std::convert::TryFrom;

use mupdf_sys::*;

use crate::{context, from_enum, Error};

from_enum! { fz_linecap => fz_linecap,
    #[derive(Debug, Default, Clone, Copy, PartialEq)]
    pub enum LineCap {
        #[default]
        Butt = FZ_LINECAP_BUTT,
        Round = FZ_LINECAP_ROUND,
        Square = FZ_LINECAP_SQUARE,
        Triangle = FZ_LINECAP_TRIANGLE,
    }
}

from_enum! { fz_linejoin => fz_linejoin,
    #[derive(Debug, Default, Clone, Copy, PartialEq)]
    pub enum LineJoin {
        #[default]
        Miter = FZ_LINEJOIN_MITER,
        Round = FZ_LINEJOIN_ROUND,
        Bevel = FZ_LINEJOIN_BEVEL,
        MiterXps = FZ_LINEJOIN_MITER_XPS,
    }
}

#[derive(Debug)]
pub struct StrokeState {
    pub(crate) inner: *mut fz_stroke_state,
}

impl StrokeState {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        start_cap: LineCap,
        dash_cap: LineCap,
        end_cap: LineCap,
        line_join: LineJoin,
        line_width: f32,
        miter_limit: f32,
        dash_phase: f32,
        dash: &[f32],
    ) -> Result<Self, Error> {
        let dash_len = dash.len() as i32;
        unsafe {
            ffi_try!(mupdf_new_stroke_state(
                context(),
                start_cap.into(),
                dash_cap.into(),
                end_cap.into(),
                line_join.into(),
                line_width,
                miter_limit,
                dash_phase,
                dash.as_ptr(),
                dash_len
            ))
        }
        .map(|inner| Self { inner })
    }

    pub fn try_clone(&self) -> Result<Self, Error> {
        let start_cap = self.start_cap();
        let dash_cap = self.dash_cap();
        let end_cap = self.end_cap();
        let line_join = self.line_join();
        let line_width = self.line_width();
        let miter_limit = self.miter_limit();
        let dash_phase = self.dash_phase();
        let dashes = self.dashes();
        Self::new(
            start_cap,
            dash_cap,
            end_cap,
            line_join,
            line_width,
            miter_limit,
            dash_phase,
            &dashes,
        )
    }

    pub fn start_cap(&self) -> LineCap {
        LineCap::try_from(unsafe { (*self.inner).start_cap }).unwrap()
    }

    pub fn dash_cap(&self) -> LineCap {
        LineCap::try_from(unsafe { (*self.inner).dash_cap }).unwrap()
    }

    pub fn end_cap(&self) -> LineCap {
        LineCap::try_from(unsafe { (*self.inner).end_cap }).unwrap()
    }

    pub fn line_join(&self) -> LineJoin {
        LineJoin::try_from(unsafe { (*self.inner).linejoin }).unwrap()
    }

    pub fn line_width(&self) -> f32 {
        unsafe { (*self.inner).linewidth }
    }

    pub fn miter_limit(&self) -> f32 {
        unsafe { (*self.inner).miterlimit }
    }

    pub fn dash_phase(&self) -> f32 {
        unsafe { (*self.inner).dash_phase }
    }

    pub fn dashes(&self) -> Vec<f32> {
        unsafe {
            let dash_len = (*self.inner).dash_len as usize;
            let dash_ptr = (*self.inner).dash_list.as_ptr();
            let mut dash_list = Vec::with_capacity(dash_len);
            dash_list.extend_from_slice(std::slice::from_raw_parts(dash_ptr, dash_len));
            dash_list
        }
    }
}

impl Drop for StrokeState {
    fn drop(&mut self) {
        if !self.inner.is_null() {
            unsafe {
                fz_drop_stroke_state(context(), self.inner);
            }
        }
    }
}

impl Clone for StrokeState {
    fn clone(&self) -> StrokeState {
        self.try_clone().unwrap()
    }
}

impl Default for StrokeState {
    fn default() -> Self {
        let inner = unsafe { mupdf_default_stroke_state(context()) };
        Self { inner }
    }
}

#[cfg(test)]
mod test {
    use super::StrokeState;

    #[test]
    fn test_default_stroke_state() {
        let _stroke = StrokeState::default();
    }
}