switching to using mupdf-sys directly since mupdf hides all the necessary functionality
This commit is contained in:
parent
fcf1c63cb7
commit
f9a24f4c48
4 changed files with 666 additions and 138 deletions
138
src/main.rs
138
src/main.rs
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use crate::quad_tree::QuadTree;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use mupdf_sys::FZ_STEXT_BOLD;
|
||||
use mupdf_sys::{FZ_STEXT_BOLD, fz_matrix};
|
||||
use non_nan_float::NonNaNF32;
|
||||
use std::{
|
||||
backtrace::Backtrace,
|
||||
|
|
@ -19,6 +19,7 @@ use std::{
|
|||
sync::OnceLock,
|
||||
};
|
||||
|
||||
mod mupdf_ffi;
|
||||
mod quad_tree;
|
||||
mod xml_tree;
|
||||
|
||||
|
|
@ -1597,13 +1598,13 @@ struct Page {
|
|||
unprocessed_non_text: Rc<RefCell<IndexSet<LineOrRect>>>,
|
||||
}
|
||||
|
||||
struct Pages {
|
||||
pages_gen: Option<Box<dyn Iterator<Item = Result<Page, Box<dyn Error>>>>>,
|
||||
struct Pages<'ctx> {
|
||||
pages_gen: Option<Box<dyn Iterator<Item = Result<Page, Box<dyn Error>>> + 'ctx>>,
|
||||
pages: BTreeMap<u32, Rc<Page>>,
|
||||
max_page_num: u32,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Pages {
|
||||
impl<'ctx> fmt::Debug for Pages<'ctx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
pages_gen,
|
||||
|
|
@ -1621,8 +1622,10 @@ impl fmt::Debug for Pages {
|
|||
}
|
||||
}
|
||||
|
||||
impl Pages {
|
||||
fn new(pages_gen: Option<Box<dyn Iterator<Item = Result<Page, Box<dyn Error>>>>>) -> Self {
|
||||
impl<'ctx> Pages<'ctx> {
|
||||
fn new(
|
||||
pages_gen: Option<Box<dyn Iterator<Item = Result<Page, Box<dyn Error>>> + 'ctx>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
pages_gen,
|
||||
pages: BTreeMap::new(),
|
||||
|
|
@ -1997,8 +2000,8 @@ impl Insn {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Parser {
|
||||
pages: Pages,
|
||||
struct Parser<'ctx> {
|
||||
pages: Pages<'ctx>,
|
||||
text_section: TextSection,
|
||||
insns: Vec<Insn>,
|
||||
}
|
||||
|
|
@ -2042,7 +2045,7 @@ impl<E: fmt::Display> fmt::Display for ErrorWithNote<E> {
|
|||
|
||||
impl<E: fmt::Display + fmt::Debug> Error for ErrorWithNote<E> {}
|
||||
|
||||
impl Parser {
|
||||
impl<'ctx> Parser<'ctx> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
pages: Pages::new(None),
|
||||
|
|
@ -2068,33 +2071,40 @@ impl Parser {
|
|||
.clone())
|
||||
}
|
||||
fn pages_gen(
|
||||
ctx: impl Into<mupdf_ffi::ContextRef<'ctx>>,
|
||||
file: &str,
|
||||
page_numbers: Option<Vec<NonZero<u32>>>,
|
||||
) -> Result<Box<dyn Iterator<Item = Result<Page, Box<dyn Error>>>>, Box<dyn Error>> {
|
||||
) -> Result<Box<dyn Iterator<Item = Result<Page, Box<dyn Error>>> + 'ctx>, Box<dyn Error>> {
|
||||
let ctx = ctx.into();
|
||||
let page_indexes = page_numbers.map(|page_numbers| {
|
||||
let mut retval = Vec::from_iter(page_numbers.into_iter().map(|v| v.get() as usize - 1));
|
||||
retval.sort();
|
||||
retval
|
||||
});
|
||||
let document = mupdf::Document::open(file)?;
|
||||
let pages: Vec<mupdf::Page> = document.pages().and_then(|pages| pages.collect())?;
|
||||
let page_indexes = page_indexes.unwrap_or_else(|| (0..pages.len()).collect());
|
||||
let document = mupdf_ffi::Document::open(ctx, &std::ffi::CString::new(file)?)?;
|
||||
let page_count = document.page_count()?;
|
||||
let page_indexes = page_indexes.unwrap_or_else(|| (0..page_count).collect());
|
||||
let mut first_seen_fonts = BTreeMap::new();
|
||||
Ok(Box::new(page_indexes.into_iter().map(move |page_index| {
|
||||
let page_num = page_index as u32 + 1;
|
||||
println!("page {page_num}");
|
||||
let page = document
|
||||
.load_page(page_index)
|
||||
.map_err(|e| format!("error reading pdf page {page_num}: {e}"))?;
|
||||
Ok(
|
||||
Page::from_mupdf_page(page_num, &pages[page_index], &mut first_seen_fonts)
|
||||
Page::from_mupdf_page(page_num, &page, &mut first_seen_fonts)
|
||||
.map_err(|e| format!("error reading pdf page {page_num}: {e}"))?,
|
||||
)
|
||||
})))
|
||||
}
|
||||
fn parse_pdf<I: Iterator<Item = NonZero<u32>>>(
|
||||
&mut self,
|
||||
ctx: impl Into<mupdf_ffi::ContextRef<'ctx>>,
|
||||
file: &str,
|
||||
page_numbers: Option<I>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
self.pages = Pages::new(Some(Self::pages_gen(
|
||||
ctx,
|
||||
file,
|
||||
page_numbers.map(|v| v.into_iter().collect()),
|
||||
)?));
|
||||
|
|
@ -3160,7 +3170,7 @@ impl Parser {
|
|||
#[derive(Clone, Debug)]
|
||||
struct MyDevice {
|
||||
page_num: u32,
|
||||
qt: Rc<RefCell<BTreeMap<TextSection, QuadTree<PageItem>>>>,
|
||||
qt: BTreeMap<TextSection, QuadTree<PageItem>>,
|
||||
unprocessed_non_text: Rc<RefCell<IndexSet<LineOrRect>>>,
|
||||
}
|
||||
|
||||
|
|
@ -3172,7 +3182,7 @@ impl MyDevice {
|
|||
unprocessed_non_text: Default::default(),
|
||||
}
|
||||
}
|
||||
fn path(&mut self, path: &mupdf::Path, cmt: mupdf::Matrix) {
|
||||
fn path(&mut self, path: &mupdf_ffi::Path<'_>, cmt: fz_matrix) {
|
||||
enum Walker {
|
||||
Empty,
|
||||
Moved { x: f32, y: f32 },
|
||||
|
|
@ -3296,54 +3306,98 @@ impl MyDevice {
|
|||
);
|
||||
}
|
||||
}
|
||||
fn text(&mut self, text: &mupdf_ffi::Text<'_>, cmt: fz_matrix) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl mupdf::NativeDevice for MyDevice {
|
||||
impl<'ctx> mupdf_ffi::DeviceCallbacks<'ctx> for MyDevice {
|
||||
fn fill_path(
|
||||
&mut self,
|
||||
path: &mupdf::Path,
|
||||
_even_odd: bool,
|
||||
cmt: mupdf::Matrix,
|
||||
_color_space: &mupdf::Colorspace,
|
||||
_color: &[f32],
|
||||
_alpha: f32,
|
||||
_cp: mupdf::ColorParams,
|
||||
ctx: mupdf_ffi::ContextRef<'ctx>,
|
||||
path: &mupdf_ffi::Path<'ctx>,
|
||||
even_odd: bool,
|
||||
cmt: fz_matrix,
|
||||
) {
|
||||
self.path(path, cmt);
|
||||
}
|
||||
|
||||
fn stroke_path(
|
||||
&mut self,
|
||||
path: &mupdf::Path,
|
||||
_stroke_state: &mupdf::StrokeState,
|
||||
cmt: mupdf::Matrix,
|
||||
_color_space: &mupdf::Colorspace,
|
||||
_color: &[f32],
|
||||
_alpha: f32,
|
||||
_cp: mupdf::ColorParams,
|
||||
ctx: mupdf_ffi::ContextRef<'ctx>,
|
||||
path: &mupdf_ffi::Path<'ctx>,
|
||||
cmt: fz_matrix,
|
||||
) {
|
||||
self.path(path, cmt);
|
||||
}
|
||||
|
||||
fn clip_path(
|
||||
&mut self,
|
||||
path: &mupdf::Path,
|
||||
_even_odd: bool,
|
||||
cmt: mupdf::Matrix,
|
||||
_scissor: mupdf::Rect,
|
||||
ctx: mupdf_ffi::ContextRef<'ctx>,
|
||||
path: &mupdf_ffi::Path<'ctx>,
|
||||
even_odd: bool,
|
||||
cmt: fz_matrix,
|
||||
scissor: mupdf_sys::fz_rect,
|
||||
) {
|
||||
self.path(path, cmt);
|
||||
}
|
||||
|
||||
fn clip_stroke_path(
|
||||
&mut self,
|
||||
path: &mupdf::Path,
|
||||
_stroke_state: &mupdf::StrokeState,
|
||||
cmt: mupdf::Matrix,
|
||||
_scissor: mupdf::Rect,
|
||||
ctx: mupdf_ffi::ContextRef<'ctx>,
|
||||
path: &mupdf_ffi::Path<'ctx>,
|
||||
cmt: fz_matrix,
|
||||
scissor: mupdf_sys::fz_rect,
|
||||
) {
|
||||
self.path(path, cmt);
|
||||
}
|
||||
|
||||
fn fill_text(
|
||||
&mut self,
|
||||
ctx: mupdf_ffi::ContextRef<'ctx>,
|
||||
text: &mupdf_ffi::Text<'ctx>,
|
||||
cmt: fz_matrix,
|
||||
) {
|
||||
self.text(text, cmt);
|
||||
}
|
||||
|
||||
fn stroke_text(
|
||||
&mut self,
|
||||
ctx: mupdf_ffi::ContextRef<'ctx>,
|
||||
text: &mupdf_ffi::Text<'ctx>,
|
||||
cmt: fz_matrix,
|
||||
) {
|
||||
self.text(text, cmt);
|
||||
}
|
||||
|
||||
fn clip_text(
|
||||
&mut self,
|
||||
ctx: mupdf_ffi::ContextRef<'ctx>,
|
||||
text: &mupdf_ffi::Text<'ctx>,
|
||||
cmt: fz_matrix,
|
||||
scissor: mupdf_sys::fz_rect,
|
||||
) {
|
||||
self.text(text, cmt);
|
||||
}
|
||||
|
||||
fn clip_stroke_text(
|
||||
&mut self,
|
||||
ctx: mupdf_ffi::ContextRef<'ctx>,
|
||||
text: &mupdf_ffi::Text<'ctx>,
|
||||
cmt: fz_matrix,
|
||||
scissor: mupdf_sys::fz_rect,
|
||||
) {
|
||||
self.text(text, cmt);
|
||||
}
|
||||
|
||||
fn ignore_text(
|
||||
&mut self,
|
||||
ctx: mupdf_ffi::ContextRef<'ctx>,
|
||||
text: &mupdf_ffi::Text<'ctx>,
|
||||
cmt: fz_matrix,
|
||||
) {
|
||||
self.text(text, cmt);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
|
|
@ -3417,12 +3471,12 @@ struct MuPdfXmlChar<'a> {
|
|||
impl Page {
|
||||
fn from_mupdf_page(
|
||||
page_num: u32,
|
||||
page: &mupdf::Page,
|
||||
page: &mupdf_ffi::Page<'_>,
|
||||
first_seen_fonts: &mut BTreeMap<String, BTreeSet<NonNaNF32>>,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let device = MyDevice::new(page_num);
|
||||
page.run(
|
||||
&mupdf::Device::from_native(device.clone())?,
|
||||
&mupdf_ffi::Device::new(page.ctx(), Box::new(device))?,
|
||||
&mupdf::Matrix::IDENTITY,
|
||||
)?;
|
||||
let MyDevice {
|
||||
|
|
@ -3438,6 +3492,8 @@ impl Page {
|
|||
RefCell<BTreeMap<TextSection, Rc<RefCell<BTreeMap<Font, IndexSet<Char>>>>>>,
|
||||
> = Rc::default();
|
||||
// we convert to xml and parse that becuase the mupdf rust crate doesn't include all the API surface we need.
|
||||
let json = page.stext_page_as_json_from_page(1.0)?;
|
||||
todo!("{json}");
|
||||
let xml = page.to_xml()?;
|
||||
let MuPdfXml::Page(xml_page) = quick_xml::de::from_str(&xml)?;
|
||||
for xml_block in xml_page.block {
|
||||
|
|
|
|||
569
src/mupdf_ffi.rs
Normal file
569
src/mupdf_ffi.rs
Normal file
|
|
@ -0,0 +1,569 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use mupdf_sys::{
|
||||
fz_clone_context, fz_color_params, fz_colorspace, fz_context, fz_device, fz_document,
|
||||
fz_drop_context, fz_drop_device, fz_drop_document, fz_drop_page, fz_drop_path, fz_drop_text,
|
||||
fz_error_type_FZ_ERROR_GENERIC, fz_matrix, fz_page, fz_path, fz_rect, fz_stroke_state, fz_text,
|
||||
mupdf_document_page_count, mupdf_drop_error, mupdf_error_t, mupdf_load_page,
|
||||
mupdf_new_base_context, mupdf_new_derived_device, mupdf_open_document, mupdf_run_page,
|
||||
};
|
||||
use std::{
|
||||
cell::Cell,
|
||||
ffi::{CStr, CString, c_int},
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
mem::ManuallyDrop,
|
||||
ptr::{self, NonNull},
|
||||
sync::{Mutex, OnceLock},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MuPdfError {
|
||||
type_: c_int,
|
||||
message: CString,
|
||||
}
|
||||
|
||||
impl MuPdfError {
|
||||
fn new_generic(message: impl ToString) -> Self {
|
||||
Self {
|
||||
type_: fz_error_type_FZ_ERROR_GENERIC as _,
|
||||
message: message.try_into().expect("nul byte in message"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MuPdfError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"MuPDF error: type: {}, message: {}",
|
||||
self.type_, self.message
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for MuPdfError {}
|
||||
|
||||
struct OwnedMuPdfError(NonNull<mupdf_error_t>);
|
||||
|
||||
impl Drop for OwnedMuPdfError {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
mupdf_drop_error(self.0.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn mupdf_try<R>(f: impl FnOnce(&mut *mut mupdf_error_t) -> R) -> Result<R, MuPdfError> {
|
||||
let mut err = <*mut mupdf_error_t>::null_mut();
|
||||
let retval = f(&mut err);
|
||||
let Some(err) = NonNull::new(err).map(OwnedMuPdfError) else {
|
||||
return Ok(retval);
|
||||
};
|
||||
unsafe {
|
||||
Err(MuPdfError {
|
||||
type_: (*err.0).type_,
|
||||
message: CString::from(CStr::from_ptr((*err.0).message)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Context(NonNull<fz_context>);
|
||||
|
||||
impl Context {
|
||||
fn new() -> Self {
|
||||
struct BaseContext(NonNull<fz_context>);
|
||||
unsafe impl Send for BaseContext {}
|
||||
static CTX: OnceLock<Mutex<BaseContext>> = OnceLock::new();
|
||||
let base = CTX
|
||||
.get_or_init(|| {
|
||||
let ctx = unsafe { mupdf_new_base_context() };
|
||||
let Some(ctx) = NonNull::new(ctx).map(BaseContext) else {
|
||||
panic!("failed to allocate a MuPDF context");
|
||||
};
|
||||
Mutex::new(ctx)
|
||||
})
|
||||
.lock()
|
||||
.expect("not poisoned");
|
||||
let ctx = unsafe { fz_clone_context(base.0.as_ptr()) };
|
||||
let Some(ctx) = NonNull::new(ctx).map(Self) else {
|
||||
drop(base);
|
||||
panic!("failed to clone a MuPDF context");
|
||||
};
|
||||
ctx
|
||||
}
|
||||
pub(crate) fn with<R>(f: impl FnOnce(&Self) -> R) -> R {
|
||||
thread_local! {
|
||||
static CTX: Context = Context::new();
|
||||
}
|
||||
CTX.with(f)
|
||||
}
|
||||
pub(crate) fn as_ref(&self) -> ContextRef<'_> {
|
||||
ContextRef(self.0, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Context {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
fz_drop_context(self.0.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct ContextRef<'ctx>(NonNull<fz_context>, PhantomData<&'ctx Context>);
|
||||
|
||||
impl<'ctx> From<&'ctx Context> for ContextRef<'ctx> {
|
||||
fn from(value: &'ctx Context) -> Self {
|
||||
value.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Document<'ctx> {
|
||||
ptr: *mut fz_document,
|
||||
ctx: ContextRef<'ctx>,
|
||||
}
|
||||
|
||||
impl<'ctx> Document<'ctx> {
|
||||
pub(crate) fn open(
|
||||
ctx: impl Into<ContextRef<'ctx>>,
|
||||
file_name: &CStr,
|
||||
) -> Result<Document<'ctx>, MuPdfError> {
|
||||
let ctx = ctx.into();
|
||||
unsafe {
|
||||
mupdf_try(|errptr| mupdf_open_document(ctx.0.as_ptr(), file_name.as_ptr(), errptr))
|
||||
.map(|ptr| Document { ptr, ctx })
|
||||
}
|
||||
}
|
||||
pub(crate) fn page_count(&self) -> Result<usize, MuPdfError> {
|
||||
unsafe {
|
||||
mupdf_try(|errptr| mupdf_document_page_count(self.ctx.0.as_ptr(), self.ptr, errptr))?
|
||||
.try_into()
|
||||
.map_err(MuPdfError::new_generic)
|
||||
}
|
||||
}
|
||||
pub(crate) fn load_page(&self, page: usize) -> Result<Page<'ctx>, MuPdfError> {
|
||||
let page = page.try_into().map_err(MuPdfError::new_generic)?;
|
||||
unsafe {
|
||||
mupdf_try(|errptr| mupdf_load_page(self.ctx.0.as_ptr(), self.ptr, page, errptr))
|
||||
.map(|ptr| Page { ptr, ctx: self.ctx })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> Drop for Document<'ctx> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
fz_drop_document(self.ctx.0.as_ptr(), self.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Page<'ctx> {
|
||||
ptr: *mut fz_page,
|
||||
ctx: ContextRef<'ctx>,
|
||||
}
|
||||
|
||||
impl<'ctx> Page<'ctx> {
|
||||
pub(crate) fn ctx(&self) -> ContextRef<'ctx> {
|
||||
self.ctx
|
||||
}
|
||||
pub(crate) fn run<T>(
|
||||
&self,
|
||||
device: &Device<'ctx, T>,
|
||||
ctm: fz_matrix,
|
||||
) -> Result<(), MuPdfError> {
|
||||
unsafe {
|
||||
mupdf_try(|errptr| {
|
||||
mupdf_run_page(
|
||||
self.ctx.0.as_ptr(),
|
||||
self.ptr,
|
||||
device.dev,
|
||||
ctm,
|
||||
ptr::null_mut(),
|
||||
errptr,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> Drop for Page<'ctx> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
fz_drop_page(self.ctx.0.as_ptr(), self.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Device<'ctx, T: 'ctx> {
|
||||
dev: *mut fz_device,
|
||||
ctx: ContextRef<'ctx>,
|
||||
_phantom: PhantomData<Box<Cell<T>>>,
|
||||
}
|
||||
|
||||
pub(crate) trait DeviceCallbacks<'ctx> {
|
||||
fn fill_path(&self, ctx: ContextRef<'ctx>, path: &Path<'ctx>, even_odd: bool, cmt: fz_matrix);
|
||||
fn stroke_path(&self, ctx: ContextRef<'ctx>, path: &Path<'ctx>, cmt: fz_matrix);
|
||||
fn clip_path(
|
||||
&self,
|
||||
ctx: ContextRef<'ctx>,
|
||||
path: &Path<'ctx>,
|
||||
even_odd: bool,
|
||||
cmt: fz_matrix,
|
||||
scissor: fz_rect,
|
||||
);
|
||||
fn clip_stroke_path(
|
||||
&self,
|
||||
ctx: ContextRef<'ctx>,
|
||||
path: &Path<'ctx>,
|
||||
cmt: fz_matrix,
|
||||
scissor: fz_rect,
|
||||
);
|
||||
fn fill_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, cmt: fz_matrix);
|
||||
fn stroke_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, cmt: fz_matrix);
|
||||
fn clip_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, cmt: fz_matrix, scissor: fz_rect);
|
||||
fn clip_stroke_text(
|
||||
&self,
|
||||
ctx: ContextRef<'ctx>,
|
||||
text: &Text<'ctx>,
|
||||
cmt: fz_matrix,
|
||||
scissor: fz_rect,
|
||||
);
|
||||
fn ignore_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, cmt: fz_matrix);
|
||||
}
|
||||
|
||||
impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
|
||||
pub(crate) fn new(ctx: impl Into<ContextRef<'ctx>>, value: Box<T>) -> Result<Self, MuPdfError> {
|
||||
let ctx = ctx.into();
|
||||
unsafe {
|
||||
let dev_ptr = mupdf_try(|errptr| {
|
||||
mupdf_new_derived_device::<DeviceStruct<T>>(
|
||||
ctx.0.as_ptr(),
|
||||
c"parse_powerisa_pdf::mupdf_ffi::Device",
|
||||
errptr,
|
||||
)
|
||||
})?;
|
||||
let retval = Device {
|
||||
dev: dev_ptr.cast(),
|
||||
ctx,
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
(&raw mut (*dev_ptr).value).write(value);
|
||||
let fz_device {
|
||||
refs,
|
||||
hints,
|
||||
flags,
|
||||
close_device,
|
||||
drop_device,
|
||||
fill_path,
|
||||
stroke_path,
|
||||
clip_path,
|
||||
clip_stroke_path,
|
||||
fill_text,
|
||||
stroke_text,
|
||||
clip_text,
|
||||
clip_stroke_text,
|
||||
ignore_text,
|
||||
fill_shade,
|
||||
fill_image,
|
||||
fill_image_mask,
|
||||
clip_image_mask,
|
||||
pop_clip,
|
||||
begin_mask,
|
||||
end_mask,
|
||||
begin_group,
|
||||
end_group,
|
||||
begin_tile,
|
||||
end_tile,
|
||||
render_flags,
|
||||
set_default_colorspaces,
|
||||
begin_layer,
|
||||
end_layer,
|
||||
begin_structure,
|
||||
end_structure,
|
||||
begin_metatext,
|
||||
end_metatext,
|
||||
d1_rect,
|
||||
container_len,
|
||||
container_cap,
|
||||
container,
|
||||
} = &mut (*dev_ptr).base;
|
||||
*drop_device = Some(Self::drop_device_fn);
|
||||
*fill_path = Some(Self::fill_path_fn);
|
||||
*stroke_path = Some(Self::stroke_path_fn);
|
||||
*clip_path = Some(Self::clip_path_fn);
|
||||
*clip_stroke_path = Some(Self::clip_stroke_path_fn);
|
||||
*fill_text = Some(Self::fill_text_fn);
|
||||
*stroke_text = Some(Self::stroke_text_fn);
|
||||
*clip_text = Some(Self::clip_text_fn);
|
||||
*clip_stroke_text = Some(Self::clip_stroke_text_fn);
|
||||
*ignore_text = Some(Self::ignore_text_fn);
|
||||
Ok(retval)
|
||||
}
|
||||
}
|
||||
pub(crate) fn get(&self) -> &T {
|
||||
unsafe { &(*self.ptr.cast::<DeviceStruct<T>>()).value }
|
||||
}
|
||||
unsafe extern "C" fn drop_device_fn(_ctx: *mut fz_context, dev: *mut fz_device) {
|
||||
unsafe {
|
||||
(&raw mut (*dev.cast::<DeviceStruct<T>>()).value).drop_in_place();
|
||||
}
|
||||
}
|
||||
unsafe extern "C" fn fill_path_fn(
|
||||
ctx: *mut fz_context,
|
||||
dev: *mut fz_device,
|
||||
path: *const fz_path,
|
||||
even_odd: c_int,
|
||||
cmt: fz_matrix,
|
||||
color_space: *mut fz_colorspace,
|
||||
color: *const f32,
|
||||
alpha: f32,
|
||||
color_params: fz_color_params,
|
||||
) {
|
||||
let Some(ctx) = NonNull::new(ctx) else {
|
||||
return;
|
||||
};
|
||||
let ctx = ContextRef(ctx, PhantomData);
|
||||
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
|
||||
this.fill_path(
|
||||
ctx,
|
||||
&ManuallyDrop::new(Path {
|
||||
ptr: path.cast_mut(),
|
||||
ctx,
|
||||
}),
|
||||
even_odd != 0,
|
||||
cmt,
|
||||
);
|
||||
}
|
||||
unsafe extern "C" fn stroke_path_fn(
|
||||
ctx: *mut fz_context,
|
||||
dev: *mut fz_device,
|
||||
path: *const fz_path,
|
||||
stroke_state: *const fz_stroke_state,
|
||||
cmt: fz_matrix,
|
||||
color_space: *mut fz_colorspace,
|
||||
color: *const f32,
|
||||
alpha: f32,
|
||||
color_params: fz_color_params,
|
||||
) {
|
||||
let Some(ctx) = NonNull::new(ctx) else {
|
||||
return;
|
||||
};
|
||||
let ctx = ContextRef(ctx, PhantomData);
|
||||
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
|
||||
this.stroke_path(
|
||||
ctx,
|
||||
&ManuallyDrop::new(Path {
|
||||
ptr: path.cast_mut(),
|
||||
ctx,
|
||||
}),
|
||||
cmt,
|
||||
);
|
||||
}
|
||||
unsafe extern "C" fn clip_path_fn(
|
||||
ctx: *mut fz_context,
|
||||
dev: *mut fz_device,
|
||||
path: *const fz_path,
|
||||
even_odd: ::std::os::raw::c_int,
|
||||
cmt: fz_matrix,
|
||||
scissor: fz_rect,
|
||||
) {
|
||||
let Some(ctx) = NonNull::new(ctx) else {
|
||||
return;
|
||||
};
|
||||
let ctx = ContextRef(ctx, PhantomData);
|
||||
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
|
||||
this.clip_path(
|
||||
ctx,
|
||||
&ManuallyDrop::new(Path {
|
||||
ptr: path.cast_mut(),
|
||||
ctx,
|
||||
}),
|
||||
even_odd != 0,
|
||||
cmt,
|
||||
scissor,
|
||||
);
|
||||
}
|
||||
unsafe extern "C" fn clip_stroke_path_fn(
|
||||
ctx: *mut fz_context,
|
||||
dev: *mut fz_device,
|
||||
path: *const fz_path,
|
||||
stroke_state: *const fz_stroke_state,
|
||||
cmt: fz_matrix,
|
||||
scissor: fz_rect,
|
||||
) {
|
||||
let Some(ctx) = NonNull::new(ctx) else {
|
||||
return;
|
||||
};
|
||||
let ctx = ContextRef(ctx, PhantomData);
|
||||
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
|
||||
this.clip_stroke_path(
|
||||
ctx,
|
||||
&ManuallyDrop::new(Path {
|
||||
ptr: path.cast_mut(),
|
||||
ctx,
|
||||
}),
|
||||
cmt,
|
||||
scissor,
|
||||
);
|
||||
}
|
||||
unsafe extern "C" fn fill_text_fn(
|
||||
ctx: *mut fz_context,
|
||||
dev: *mut fz_device,
|
||||
text: *const fz_text,
|
||||
cmt: fz_matrix,
|
||||
color_space: *mut fz_colorspace,
|
||||
color: *const f32,
|
||||
alpha: f32,
|
||||
color_params: fz_color_params,
|
||||
) {
|
||||
let Some(ctx) = NonNull::new(ctx) else {
|
||||
return;
|
||||
};
|
||||
let ctx = ContextRef(ctx, PhantomData);
|
||||
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
|
||||
this.fill_text(
|
||||
ctx,
|
||||
&ManuallyDrop::new(Text {
|
||||
ptr: text.cast_mut(),
|
||||
ctx,
|
||||
}),
|
||||
cmt,
|
||||
);
|
||||
}
|
||||
unsafe extern "C" fn stroke_text_fn(
|
||||
ctx: *mut fz_context,
|
||||
dev: *mut fz_device,
|
||||
text: *const fz_text,
|
||||
stroke_state: *const fz_stroke_state,
|
||||
cmt: fz_matrix,
|
||||
color_space: *mut fz_colorspace,
|
||||
color: *const f32,
|
||||
alpha: f32,
|
||||
color_params: fz_color_params,
|
||||
) {
|
||||
let Some(ctx) = NonNull::new(ctx) else {
|
||||
return;
|
||||
};
|
||||
let ctx = ContextRef(ctx, PhantomData);
|
||||
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
|
||||
this.stroke_text(
|
||||
ctx,
|
||||
&ManuallyDrop::new(Text {
|
||||
ptr: text.cast_mut(),
|
||||
ctx,
|
||||
}),
|
||||
cmt,
|
||||
);
|
||||
}
|
||||
unsafe extern "C" fn clip_text_fn(
|
||||
ctx: *mut fz_context,
|
||||
dev: *mut fz_device,
|
||||
text: *const fz_text,
|
||||
cmt: fz_matrix,
|
||||
scissor: fz_rect,
|
||||
) {
|
||||
let Some(ctx) = NonNull::new(ctx) else {
|
||||
return;
|
||||
};
|
||||
let ctx = ContextRef(ctx, PhantomData);
|
||||
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
|
||||
this.clip_text(
|
||||
ctx,
|
||||
&ManuallyDrop::new(Text {
|
||||
ptr: text.cast_mut(),
|
||||
ctx,
|
||||
}),
|
||||
cmt,
|
||||
scissor,
|
||||
);
|
||||
}
|
||||
unsafe extern "C" fn clip_stroke_text_fn(
|
||||
ctx: *mut fz_context,
|
||||
dev: *mut fz_device,
|
||||
text: *const fz_text,
|
||||
stroke_state: *const fz_stroke_state,
|
||||
cmt: fz_matrix,
|
||||
scissor: fz_rect,
|
||||
) {
|
||||
let Some(ctx) = NonNull::new(ctx) else {
|
||||
return;
|
||||
};
|
||||
let ctx = ContextRef(ctx, PhantomData);
|
||||
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
|
||||
this.clip_stroke_text(
|
||||
ctx,
|
||||
&ManuallyDrop::new(Text {
|
||||
ptr: text.cast_mut(),
|
||||
ctx,
|
||||
}),
|
||||
cmt,
|
||||
scissor,
|
||||
);
|
||||
}
|
||||
unsafe extern "C" fn ignore_text_fn(
|
||||
ctx: *mut fz_context,
|
||||
dev: *mut fz_device,
|
||||
text: *const fz_text,
|
||||
cmt: fz_matrix,
|
||||
) {
|
||||
let Some(ctx) = NonNull::new(ctx) else {
|
||||
return;
|
||||
};
|
||||
let ctx = ContextRef(ctx, PhantomData);
|
||||
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
|
||||
this.ignore_text(
|
||||
ctx,
|
||||
&ManuallyDrop::new(Text {
|
||||
ptr: text.cast_mut(),
|
||||
ctx,
|
||||
}),
|
||||
cmt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx, T> Drop for Device<'ctx, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// FIXME: fz_close_device may throw exceptions
|
||||
// fz_close_device(self.ctx.0.as_ptr(), self.dev);
|
||||
fz_drop_device(self.ctx.0.as_ptr(), self.dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct DeviceStruct<T> {
|
||||
base: fz_device,
|
||||
value: Box<T>,
|
||||
}
|
||||
|
||||
pub(crate) struct Path<'ctx> {
|
||||
ptr: *mut fz_path,
|
||||
ctx: ContextRef<'ctx>,
|
||||
}
|
||||
|
||||
impl<'ctx> Drop for Path<'ctx> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
fz_drop_path(self.ctx.0.as_ptr(), self.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Text<'ctx> {
|
||||
ptr: *mut fz_text,
|
||||
ctx: ContextRef<'ctx>,
|
||||
}
|
||||
|
||||
impl<'ctx> Drop for Text<'ctx> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
fz_drop_text(self.ctx.0.as_ptr(), self.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue