Initial Functional Plugin
This commit is contained in:
167
src/lib.rs
Normal file
167
src/lib.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
use std::{
|
||||
ffi::{CStr, OsStr, c_void},
|
||||
os::unix::ffi::OsStrExt,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use cairo::{Format, ImageSurface};
|
||||
|
||||
use crate::zathura::{
|
||||
ZathuraResult, cairo_t, zathura_document_get_data, zathura_document_get_path,
|
||||
zathura_document_s, zathura_document_set_data, zathura_document_set_number_of_pages,
|
||||
zathura_page_get_document, zathura_page_get_index, zathura_page_set_height,
|
||||
zathura_page_set_width, zathura_page_t,
|
||||
};
|
||||
|
||||
mod typst;
|
||||
mod zathura;
|
||||
use typst::TypstDocument;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub static zathura_plugin_6_7: zathura::zathura_plugin_definition_s =
|
||||
zathura::zathura_plugin_definition_s {
|
||||
name: c"zathura-typst".as_ptr(),
|
||||
version: zathura::zathura_plugin_version_s {
|
||||
major: 0,
|
||||
minor: 1,
|
||||
rev: 0,
|
||||
},
|
||||
functions: zathura::zathura_plugin_functions_s {
|
||||
document_open: Some(document_open),
|
||||
document_free: Some(document_free),
|
||||
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),
|
||||
page_clear: Some(page_clear),
|
||||
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_get_selection: None,
|
||||
page_render_cairo: Some(page_render_cairo),
|
||||
page_get_label: None,
|
||||
page_get_signatures: None,
|
||||
},
|
||||
mime_types_size: 1,
|
||||
mime_types: {
|
||||
static MIMETYPES: &[&u8] = &[c"text/vnd.typst".to_bytes_with_nul().first().unwrap()];
|
||||
MIMETYPES.as_ptr() as *mut _
|
||||
},
|
||||
};
|
||||
|
||||
unsafe extern "C" fn document_open(doc: *mut zathura_document_s) -> ZathuraResult {
|
||||
let path = unsafe {
|
||||
Path::new(OsStr::from_bytes(
|
||||
CStr::from_ptr(zathura_document_get_path(doc)).to_bytes(),
|
||||
))
|
||||
};
|
||||
let Ok(document) = TypstDocument::load(path) else {
|
||||
return ZathuraResult::InvalidArguments;
|
||||
};
|
||||
let document = Box::new(document);
|
||||
|
||||
unsafe {
|
||||
zathura_document_set_number_of_pages(doc, document.doc.pages.len() as _);
|
||||
zathura_document_set_data(doc, Box::into_raw(document) as _);
|
||||
}
|
||||
|
||||
ZathuraResult::OK
|
||||
}
|
||||
|
||||
unsafe extern "C" fn document_free(_: *mut zathura_document_s, data: *mut c_void) -> ZathuraResult {
|
||||
if !data.is_null() {
|
||||
drop(unsafe { Box::<TypstDocument>::from_raw(data as _) });
|
||||
}
|
||||
ZathuraResult::OK
|
||||
}
|
||||
|
||||
unsafe extern "C" fn page_init(page: *mut zathura_page_t) -> ZathuraResult {
|
||||
let pageno = unsafe { zathura_page_get_index(page) };
|
||||
let doc = unsafe { zathura_page_get_document(page) };
|
||||
let typst_doc = unsafe { &mut *(zathura_document_get_data(doc) as *mut TypstDocument) };
|
||||
let typst_page = &mut typst_doc.doc.pages[pageno as usize];
|
||||
|
||||
unsafe {
|
||||
zathura_page_set_width(page, typst_page.frame.width().to_pt());
|
||||
zathura_page_set_height(page, typst_page.frame.height().to_pt());
|
||||
};
|
||||
|
||||
ZathuraResult::OK
|
||||
}
|
||||
|
||||
unsafe extern "C" fn page_clear(_: *mut zathura_page_t, _: *mut c_void) -> ZathuraResult {
|
||||
ZathuraResult::OK
|
||||
}
|
||||
|
||||
unsafe extern "C" fn page_render_cairo(
|
||||
page: *mut zathura_page_t,
|
||||
_: *mut c_void,
|
||||
cairo: *mut cairo_t,
|
||||
_printing: bool,
|
||||
) -> ZathuraResult {
|
||||
let pageno = unsafe { zathura_page_get_index(page) };
|
||||
let doc = unsafe { zathura_page_get_document(page) };
|
||||
let typst_doc = unsafe { &mut *(zathura_document_get_data(doc) as *mut TypstDocument) };
|
||||
let typst_page = &mut typst_doc.doc.pages[pageno as usize];
|
||||
|
||||
let context = unsafe { cairo::Context::from_raw_none(cairo) };
|
||||
let surface = context.target();
|
||||
if surface.status().is_err() {
|
||||
return ZathuraResult::Unknown;
|
||||
}
|
||||
let Ok(surface) = ImageSurface::try_from(surface) else {
|
||||
return ZathuraResult::InvalidArguments;
|
||||
};
|
||||
|
||||
let scale = {
|
||||
let wscale = surface.width() as f32 / typst_page.frame.width().to_pt() as f32;
|
||||
let vscale = surface.height() as f32 / typst_page.frame.height().to_pt() as f32;
|
||||
if wscale < vscale { wscale } else { vscale } // min, but floats aren't Ord
|
||||
};
|
||||
|
||||
let format = surface.format();
|
||||
|
||||
let pixels_rgba = typst_render::render(typst_page, scale).take();
|
||||
|
||||
let (pixels_rgba, _) = pixels_rgba.as_chunks::<4>(); // This will always be exact anyway
|
||||
|
||||
let buf = unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
cairo::ffi::cairo_image_surface_get_data(surface.to_raw_none()),
|
||||
surface.stride() as usize * surface.height() as usize,
|
||||
)
|
||||
};
|
||||
|
||||
if !matches!(format, Format::ARgb32 | Format::Rgb24) {
|
||||
return ZathuraResult::Unknown;
|
||||
}
|
||||
|
||||
pixels_rgba
|
||||
.iter()
|
||||
.zip(buf.chunks_exact_mut(4))
|
||||
.for_each(|(&[r, g, b, a], dst)| {
|
||||
dst.copy_from_slice(&u32::from_be_bytes([a, r, g, b]).to_ne_bytes());
|
||||
});
|
||||
|
||||
surface.mark_dirty();
|
||||
|
||||
ZathuraResult::OK
|
||||
}
|
||||
// TODO: handle compile errors
|
||||
// TODO: render warnings
|
||||
// TODO: text/link/... handling
|
||||
// TODO: nixify compilation of magicdb (file -Cm ~/.magic:/nix/store/vi7ya34k19nid2m0dmkljqip5572g0bi-file-5.45/share/misc/magic.mgc)
|
||||
/*
|
||||
0 string/cW /*\ doc:\ typst\ */ typst text document
|
||||
!:ext typ
|
||||
!:mime text/vnd.typst
|
||||
0 string #import typst text document
|
||||
!:ext typ
|
||||
!:mime text/vnd.typst
|
||||
*/
|
||||
Reference in New Issue
Block a user