#![doc(html_root_url = "https://docs.rs/zathura-plugin/0.4.0")]
#![warn(missing_debug_implementations, rust_2018_idioms)]
mod document;
mod error;
mod page;
pub use {
self::{document::*, error::*, page::*},
zathura_plugin_sys as sys,
};
#[doc(hidden)]
pub use pkg_version::{pkg_version_major, pkg_version_minor, pkg_version_patch};
#[derive(Debug)]
pub struct DocumentInfo<P: ZathuraPlugin + ?Sized> {
pub page_count: u32,
pub plugin_data: P::DocumentData,
}
#[derive(Debug)]
pub struct PageInfo<P: ZathuraPlugin + ?Sized> {
pub width: f64,
pub height: f64,
pub plugin_data: P::PageData,
}
pub trait ZathuraPlugin {
type DocumentData;
type PageData;
fn document_open(doc: DocumentRef<'_>) -> Result<DocumentInfo<Self>, PluginError>;
fn document_free(
doc: DocumentRef<'_>,
doc_data: &mut Self::DocumentData,
) -> Result<(), PluginError> {
let _ = (doc, doc_data);
Ok(())
}
fn page_init(
page: PageRef<'_>,
doc_data: &mut Self::DocumentData,
) -> Result<PageInfo<Self>, PluginError>;
fn page_free(
page: PageRef<'_>,
doc_data: &mut Self::DocumentData,
page_data: &mut Self::PageData,
) -> Result<(), PluginError> {
let _ = (page, doc_data, page_data);
Ok(())
}
fn page_render(
page: PageRef<'_>,
doc_data: &mut Self::DocumentData,
page_data: &mut Self::PageData,
cairo: &mut cairo::Context,
printing: bool,
) -> Result<(), PluginError>;
}
#[doc(hidden)]
pub mod wrapper {
use {
crate::{sys::*, *},
cairo,
std::{
ffi::c_void,
panic::{catch_unwind, AssertUnwindSafe},
},
};
trait ResultExt {
fn to_zathura(self) -> zathura_error_t;
}
impl ResultExt for Result<(), PluginError> {
fn to_zathura(self) -> zathura_error_t {
match self {
Ok(()) => 0,
Err(e) => e as zathura_error_t,
}
}
}
fn wrap(f: impl FnOnce() -> Result<(), PluginError>) -> Result<(), PluginError> {
match catch_unwind(AssertUnwindSafe(f)) {
Ok(r) => r,
Err(_) => Err(PluginError::Unknown),
}
}
pub unsafe extern "C" fn document_open<P: ZathuraPlugin>(
document: *mut zathura_document_t,
) -> zathura_error_t {
wrap(|| {
let doc = DocumentRef::from_raw(document);
let info = P::document_open(doc)?;
let mut doc = DocumentRef::from_raw(document);
doc.set_plugin_data(Box::into_raw(Box::new(info.plugin_data)) as *mut _);
doc.set_page_count(info.page_count);
Ok(())
})
.to_zathura()
}
pub unsafe extern "C" fn document_free<P: ZathuraPlugin>(
document: *mut zathura_document_t,
_data: *mut c_void,
) -> zathura_error_t {
wrap(|| {
let doc = DocumentRef::from_raw(document);
let doc_data = &mut *(doc.plugin_data() as *mut P::DocumentData);
let result = P::document_free(doc, doc_data);
let doc = DocumentRef::from_raw(document);
let plugin_data = doc.plugin_data() as *mut P::DocumentData;
drop(Box::from_raw(plugin_data));
result
})
.to_zathura()
}
pub unsafe extern "C" fn page_init<P: ZathuraPlugin>(
page: *mut zathura_page_t,
) -> zathura_error_t {
wrap(|| {
let mut p = PageRef::from_raw(page);
let doc_data = p.document().plugin_data() as *mut P::DocumentData;
let info = P::page_init(p, &mut *doc_data)?;
let mut p = PageRef::from_raw(page);
p.set_width(info.width);
p.set_height(info.height);
p.set_plugin_data(Box::into_raw(Box::new(info.plugin_data)) as *mut _);
Ok(())
})
.to_zathura()
}
pub unsafe extern "C" fn page_clear<P: ZathuraPlugin>(
page: *mut zathura_page_t,
_data: *mut c_void,
) -> zathura_error_t {
wrap(|| {
let result = {
let mut p = PageRef::from_raw(page);
let doc_data = &mut *(p.document().plugin_data() as *mut P::DocumentData);
let page_data = &mut *(p.plugin_data() as *mut P::PageData);
P::page_free(p, doc_data, page_data)
};
let p = PageRef::from_raw(page);
let plugin_data = p.plugin_data() as *mut P::PageData;
drop(Box::from_raw(plugin_data));
result
})
.to_zathura()
}
pub unsafe extern "C" fn page_render_cairo<P: ZathuraPlugin>(
page: *mut zathura_page_t,
_data: *mut c_void,
cairo: *mut sys::cairo_t,
printing: bool,
) -> zathura_error_t {
wrap(|| {
let mut p = PageRef::from_raw(page);
let page_data = &mut *(p.plugin_data() as *mut P::PageData);
let doc_data = &mut *(p.document().plugin_data() as *mut P::DocumentData);
let mut cairo = cairo::Context::from_raw_borrow(cairo as *mut _);
P::page_render(p, doc_data, page_data, &mut cairo, printing)
})
.to_zathura()
}
}
#[macro_export]
macro_rules! plugin_entry {
(
$name:literal,
$plugin_ty:ty,
[
$($mime:literal),+
$(,)?
]
) => {
#[doc(hidden)]
#[repr(transparent)]
#[allow(warnings)]
pub struct __AssertSync<T>(T);
unsafe impl<T> Sync for __AssertSync<T> {}
#[doc(hidden)]
#[no_mangle]
pub static mut zathura_plugin_3_4:
__AssertSync<$crate::sys::zathura_plugin_definition_t> = __AssertSync({
use $crate::sys::*;
use $crate::wrapper::*;
zathura_plugin_definition_t {
name: concat!($name, "\0").as_ptr() as *const _,
version: zathura_plugin_version_t {
major: $crate::pkg_version_major!(),
minor: $crate::pkg_version_minor!(),
rev: $crate::pkg_version_patch!(),
},
mime_types_size: {
0 $(+ {
$mime;
1
})+
},
mime_types: [
$(
concat!($mime, "\0").as_ptr() as *const _,
)+
].as_ptr() as *mut _,
functions: zathura_plugin_functions_t {
document_open: Some(document_open::<$plugin_ty>),
document_free: Some(document_free::<$plugin_ty>),
document_index_generate: None,
document_save_as: None,
document_attachments_get: None,
document_attachment_save: None,
document_get_information: None,
page_init: Some(page_init::<$plugin_ty>),
page_clear: Some(page_clear::<$plugin_ty>),
page_search_text: None,
page_links_get: None,
page_form_fields_get: None,
page_images_get: None,
page_image_get_cairo: None,
page_get_text: None,
page_render: None,
page_render_cairo: Some(page_render_cairo::<$plugin_ty>),
page_get_label: None,
},
}
});
};
}
#[cfg(feature = "testplugin")]
struct TestPlugin;
#[cfg(feature = "testplugin")]
impl ZathuraPlugin for TestPlugin {
type DocumentData = ();
type PageData = ();
fn document_open(doc: DocumentRef<'_>) -> Result<DocumentInfo<Self>, PluginError> {
println!("open: {:?}", doc.basename_utf8());
println!("path: {:?}", doc.path_utf8());
println!("url: {:?}", doc.uri_utf8());
println!("{} pages", doc.page_count());
Ok(DocumentInfo {
page_count: 5,
plugin_data: (),
})
}
fn document_free(doc: DocumentRef<'_>, _doc_data: &mut ()) -> Result<(), PluginError> {
println!("free! {:?}", doc);
Ok(())
}
fn page_init(page: PageRef<'_>, _doc_data: &mut ()) -> Result<PageInfo<Self>, PluginError> {
println!("page init: {:?}", page);
Ok(PageInfo {
width: 75.0,
height: 100.0,
plugin_data: (),
})
}
fn page_free(
page: PageRef<'_>,
_doc_data: &mut (),
_page_data: &mut (),
) -> Result<(), PluginError> {
println!("page free: {:?}", page);
Ok(())
}
fn page_render(
mut page: PageRef<'_>,
_doc_data: &mut Self::DocumentData,
_page_data: &mut Self::PageData,
cairo: &mut cairo::Context,
printing: bool,
) -> Result<(), PluginError> {
println!(
"render! {:?}, index={:?}, {}x{}, {:?}",
page,
page.index(),
page.width(),
page.height(),
printing
);
{
let doc = page.document();
println!(
"doc: zoom={}, scale={}, rotation={}°, ppi={}, scale={:?}, cell-size={:?}",
doc.zoom(),
doc.scale(),
doc.rotation(),
doc.viewport_ppi(),
doc.scaling_factors(),
doc.cell_size(),
);
}
println!(
"cairo: scale={:?}, 50,50={:?}",
cairo.get_target().get_device_scale(),
cairo.user_to_device(50.0, 50.0),
);
if page.index() == 0 {
cairo.move_to(10.0, 10.0);
cairo.show_text("Wello!");
cairo.set_source_rgb(0.0, 1.0, 1.0);
cairo.set_line_width(1.0);
cairo.move_to(0.0, 0.0);
cairo.line_to(10.5, 50.5);
cairo.stroke();
}
Ok(())
}
}
#[cfg(feature = "testplugin")]
plugin_entry!("TestPlugin", TestPlugin, ["text/plain"]);