Add basic text handling
This commit is contained in:
1
build.rs
1
build.rs
@@ -11,6 +11,7 @@ fn main() {
|
|||||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||||
.formatter(bindgen::Formatter::Prettyplease)
|
.formatter(bindgen::Formatter::Prettyplease)
|
||||||
.allowlist_item("zathura_.*")
|
.allowlist_item("zathura_.*")
|
||||||
|
.allowlist_item("girara_list_.*")
|
||||||
.blocklist_item("zathura_plugin_error_e")
|
.blocklist_item("zathura_plugin_error_e")
|
||||||
.blocklist_item("^cairo_.*")
|
.blocklist_item("^cairo_.*")
|
||||||
.generate()
|
.generate()
|
||||||
|
|||||||
168
src/lib.rs
168
src/lib.rs
@@ -1,14 +1,20 @@
|
|||||||
use std::{
|
use std::{
|
||||||
ffi::{CStr, OsStr, c_void},
|
ffi::{CStr, CString, OsStr, c_void},
|
||||||
|
mem::MaybeUninit,
|
||||||
os::unix::ffi::OsStrExt,
|
os::unix::ffi::OsStrExt,
|
||||||
path::Path,
|
path::Path,
|
||||||
|
slice,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ::typst::layout::Page;
|
use ::typst::layout::{Frame, FrameItem, GroupItem, Page, Point, Transform};
|
||||||
use cairo::{Format, ImageSurface};
|
use cairo::{Format, ImageSurface};
|
||||||
|
|
||||||
use crate::zathura::{
|
use crate::zathura::{
|
||||||
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_data, zathura_page_get_document, zathura_page_get_index, zathura_page_set_data, zathura_page_set_height, zathura_page_set_width, zathura_page_t, ZathuraResult
|
ZathuraResult, cairo_t, girara_list_append, girara_list_new_with_free, girara_list_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_data, zathura_page_set_height, zathura_page_set_width,
|
||||||
|
zathura_page_t, zathura_rectangle_s,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod typst;
|
mod typst;
|
||||||
@@ -40,8 +46,8 @@ pub static zathura_plugin_6_7: zathura::zathura_plugin_definition_s =
|
|||||||
page_form_fields_get: None,
|
page_form_fields_get: None,
|
||||||
page_images_get: None,
|
page_images_get: None,
|
||||||
page_image_get_cairo: None,
|
page_image_get_cairo: None,
|
||||||
page_get_text: None,
|
page_get_text: Some(page_get_text),
|
||||||
page_get_selection: None,
|
page_get_selection: Some(page_get_selection),
|
||||||
page_render_cairo: Some(page_render_cairo),
|
page_render_cairo: Some(page_render_cairo),
|
||||||
page_get_label: None,
|
page_get_label: None,
|
||||||
page_get_signatures: None,
|
page_get_signatures: None,
|
||||||
@@ -97,12 +103,12 @@ unsafe extern "C" fn page_clear(_: *mut zathura_page_t, _: *mut c_void) -> Zathu
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn page_render_cairo(
|
unsafe extern "C" fn page_render_cairo(
|
||||||
page: *mut zathura_page_t,
|
_page: *mut zathura_page_t,
|
||||||
_: *mut c_void,
|
typst_page: *mut c_void,
|
||||||
cairo: *mut cairo_t,
|
cairo: *mut cairo_t,
|
||||||
_printing: bool,
|
_printing: bool,
|
||||||
) -> ZathuraResult {
|
) -> ZathuraResult {
|
||||||
let typst_page: &Page = unsafe { &*(zathura_page_get_data(page) as *mut _)};
|
let typst_page: &Page = unsafe { &*(typst_page as *mut _) };
|
||||||
|
|
||||||
let context = unsafe { cairo::Context::from_raw_none(cairo) };
|
let context = unsafe { cairo::Context::from_raw_none(cairo) };
|
||||||
let surface = context.target();
|
let surface = context.target();
|
||||||
@@ -147,8 +153,152 @@ unsafe extern "C" fn page_render_cairo(
|
|||||||
|
|
||||||
ZathuraResult::OK
|
ZathuraResult::OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn page_get_text(
|
||||||
|
_page: *mut zathura_page_t,
|
||||||
|
data: *mut c_void,
|
||||||
|
rect: zathura_rectangle_s,
|
||||||
|
res: *mut ZathuraResult,
|
||||||
|
) -> *mut i8 {
|
||||||
|
let mut res_ = ZathuraResult::Unknown;
|
||||||
|
let res = unsafe {
|
||||||
|
&mut *(if res.is_null() { &raw mut res_ } else { res } as *mut MaybeUninit<ZathuraResult>)
|
||||||
|
}
|
||||||
|
.write(ZathuraResult::Unknown);
|
||||||
|
let typst_page: &Page = unsafe { &*(data as *mut _) };
|
||||||
|
|
||||||
|
let text = FrameItemIterator::new(&typst_page.frame)
|
||||||
|
.filter(|(Point { x, y }, _)| {
|
||||||
|
(rect.x1..=rect.x2).contains(&x.to_pt()) && (rect.y1..=rect.y2).contains(&y.to_pt())
|
||||||
|
})
|
||||||
|
.filter_map(|(_, item)| {
|
||||||
|
if let FrameItem::Text(item) = item {
|
||||||
|
Some(item.text.to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
*res = ZathuraResult::OK;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let str = slice::from_raw_parts_mut(
|
||||||
|
cairo::glib::ffi::g_malloc0_n(text.len() + 1, 1) as *mut u8,
|
||||||
|
text.len() + 1,
|
||||||
|
);
|
||||||
|
str[..text.len()].copy_from_slice(text.as_bytes());
|
||||||
|
|
||||||
|
str.as_mut_ptr() as _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn page_get_selection(
|
||||||
|
_page: *mut zathura_page_t,
|
||||||
|
data: *mut c_void,
|
||||||
|
rect: zathura_rectangle_s,
|
||||||
|
res: *mut ZathuraResult,
|
||||||
|
) -> *mut girara_list_t {
|
||||||
|
let mut res_ = ZathuraResult::Unknown;
|
||||||
|
let res = unsafe {
|
||||||
|
&mut *(if res.is_null() { &raw mut res_ } else { res } as *mut MaybeUninit<ZathuraResult>)
|
||||||
|
}
|
||||||
|
.write(ZathuraResult::Unknown);
|
||||||
|
let typst_page: &Page = unsafe { &*(data as *mut _) };
|
||||||
|
let min = |x, y| if x < y { x } else { y };
|
||||||
|
let max = |x, y| if x > y { x } else { y };
|
||||||
|
|
||||||
|
let rect = zathura_rectangle_s {
|
||||||
|
x1: min(rect.x1, rect.x2),
|
||||||
|
y1: min(rect.y1, rect.y2),
|
||||||
|
x2: max(rect.x1, rect.x2),
|
||||||
|
y2: max(rect.y1, rect.y2),
|
||||||
|
};
|
||||||
|
|
||||||
|
let rects = FrameItemIterator::new(&typst_page.frame)
|
||||||
|
.filter(|(Point { x, y }, _)| {
|
||||||
|
(rect.x1..=rect.x2).contains(&x.to_pt()) && (rect.y1..=rect.y2).contains(&y.to_pt())
|
||||||
|
})
|
||||||
|
.filter_map(|(_, item)| {
|
||||||
|
if let FrameItem::Text(item) = item {
|
||||||
|
Some(item.bbox())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe extern "C" fn drop_rect(data: *mut c_void) {
|
||||||
|
drop(unsafe { Box::from_raw(data as *mut zathura_rectangle_s) });
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let list = girara_list_new_with_free(Some(drop_rect));
|
||||||
|
|
||||||
|
for rect in rects {
|
||||||
|
girara_list_append(
|
||||||
|
list,
|
||||||
|
Box::into_raw(Box::new(zathura_rectangle_s {
|
||||||
|
x1: rect.min.x.to_pt(),
|
||||||
|
y1: rect.min.y.to_pt(),
|
||||||
|
x2: rect.max.x.to_pt(),
|
||||||
|
y2: rect.max.y.to_pt(),
|
||||||
|
})) as *mut _,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
*res = ZathuraResult::OK;
|
||||||
|
list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FrameItemIterator<'a> {
|
||||||
|
transform: Transform,
|
||||||
|
recur: Option<Box<FrameItemIterator<'a>>>,
|
||||||
|
current: slice::Iter<'a, (Point, FrameItem)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FrameItemIterator<'a> {
|
||||||
|
fn new(root: &'a Frame) -> Self {
|
||||||
|
Self::new_at(Default::default(), root)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_at(transform: Transform, root: &'a Frame) -> Self {
|
||||||
|
Self {
|
||||||
|
transform,
|
||||||
|
recur: None,
|
||||||
|
current: root.items(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for FrameItemIterator<'a> {
|
||||||
|
type Item = (Point, &'a FrameItem);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if let Some((position, item)) = self.recur.as_mut().and_then(|r| r.next()) {
|
||||||
|
return Some((position.transform(self.transform), item));
|
||||||
|
}
|
||||||
|
self.recur = None;
|
||||||
|
let (position, item) = self.current.next()?;
|
||||||
|
if let FrameItem::Group(GroupItem {
|
||||||
|
frame, transform, ..
|
||||||
|
}) = item
|
||||||
|
{
|
||||||
|
self.recur = Some(Box::new(Self::new_at(
|
||||||
|
transform
|
||||||
|
.invert()
|
||||||
|
.unwrap()
|
||||||
|
.post_concat(Transform::translate(position.x, position.y)),
|
||||||
|
frame,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Some((position.transform(self.transform), item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: render warnings
|
// TODO: render warnings
|
||||||
// TODO: text/link/... handling
|
// TODO: PDF as attachment
|
||||||
|
// TODO: link/... handling
|
||||||
// TODO: better caching
|
// TODO: better caching
|
||||||
// TODO: nixify compilation of magicdb (file -Cm ~/.magic:/nix/store/vi7ya34k19nid2m0dmkljqip5572g0bi-file-5.45/share/misc/magic.mgc)
|
// TODO: nixify compilation of magicdb (file -Cm ~/.magic:/nix/store/vi7ya34k19nid2m0dmkljqip5572g0bi-file-5.45/share/misc/magic.mgc)
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user