725 lines
20 KiB
Rust
725 lines
20 KiB
Rust
// 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_path_walker, fz_point, fz_rect,
|
|
fz_stroke_state, fz_text, fz_transform_point, fz_transform_point_xy, fz_walk_path,
|
|
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, c_void},
|
|
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) trait PathWalker<'ctx> {
|
|
fn move_to(&mut self, ctx: ContextRef<'ctx>, x: f32, y: f32);
|
|
fn line_to(&mut self, ctx: ContextRef<'ctx>, x: f32, y: f32);
|
|
fn curve_to(
|
|
&mut self,
|
|
ctx: ContextRef<'ctx>,
|
|
x1: f32,
|
|
y1: f32,
|
|
x2: f32,
|
|
y2: f32,
|
|
x3: f32,
|
|
y3: f32,
|
|
);
|
|
fn close_path(&mut self, ctx: ContextRef<'ctx>);
|
|
fn rect_to(&mut self, ctx: ContextRef<'ctx>, x1: f32, y1: f32, x2: f32, y2: f32) {
|
|
self.move_to(ctx, x1, y1);
|
|
self.move_to(ctx, x2, y1);
|
|
self.move_to(ctx, x2, y2);
|
|
self.move_to(ctx, x1, y2);
|
|
self.close_path(ctx);
|
|
}
|
|
}
|
|
|
|
impl<'ctx, T: ?Sized + PathWalker<'ctx>> PathWalker<'ctx> for &'_ mut T {
|
|
fn move_to(&mut self, ctx: ContextRef<'ctx>, x: f32, y: f32) {
|
|
T::move_to(self, ctx, x, y);
|
|
}
|
|
|
|
fn line_to(&mut self, ctx: ContextRef<'ctx>, x: f32, y: f32) {
|
|
T::line_to(self, ctx, x, y);
|
|
}
|
|
|
|
fn curve_to(
|
|
&mut self,
|
|
ctx: ContextRef<'ctx>,
|
|
x1: f32,
|
|
y1: f32,
|
|
x2: f32,
|
|
y2: f32,
|
|
x3: f32,
|
|
y3: f32,
|
|
) {
|
|
T::curve_to(self, ctx, x1, y1, x2, y2, x3, y3);
|
|
}
|
|
|
|
fn close_path(&mut self, ctx: ContextRef<'ctx>) {
|
|
T::close_path(self, ctx);
|
|
}
|
|
|
|
fn rect_to(&mut self, ctx: ContextRef<'ctx>, x1: f32, y1: f32, x2: f32, y2: f32) {
|
|
T::rect_to(self, ctx, x1, y1, x2, y2);
|
|
}
|
|
}
|
|
|
|
pub(crate) struct Path<'ctx> {
|
|
ptr: *mut fz_path,
|
|
ctx: ContextRef<'ctx>,
|
|
}
|
|
|
|
impl<'ctx> Path<'ctx> {
|
|
pub(crate) fn walk<W: PathWalker<'ctx>>(&self, mut walker: W) {
|
|
unsafe {
|
|
fz_walk_path(
|
|
self.ctx.0.as_ptr(),
|
|
self.ptr,
|
|
const {
|
|
&fz_path_walker {
|
|
moveto: Some(Self::move_to_fn::<W>),
|
|
lineto: Some(Self::line_to_fn::<W>),
|
|
curveto: Some(Self::curve_to_fn::<W>),
|
|
closepath: Some(Self::close_path_fn::<W>),
|
|
quadto: None,
|
|
curvetov: None,
|
|
curvetoy: None,
|
|
rectto: Some(Self::rect_to_fn::<W>),
|
|
}
|
|
},
|
|
(&raw mut walker).cast(),
|
|
);
|
|
}
|
|
}
|
|
unsafe extern "C" fn move_to_fn<W: PathWalker<'ctx>>(
|
|
ctx: *mut fz_context,
|
|
arg: *mut c_void,
|
|
x: f32,
|
|
y: f32,
|
|
) {
|
|
let Some(ctx) = NonNull::new(ctx) else {
|
|
return;
|
|
};
|
|
let ctx = ContextRef(ctx, PhantomData);
|
|
let this = unsafe { &mut *arg.cast::<W>() };
|
|
this.move_to(ctx, x, y);
|
|
}
|
|
unsafe extern "C" fn line_to_fn<W: PathWalker<'ctx>>(
|
|
ctx: *mut fz_context,
|
|
arg: *mut c_void,
|
|
x: f32,
|
|
y: f32,
|
|
) {
|
|
let Some(ctx) = NonNull::new(ctx) else {
|
|
return;
|
|
};
|
|
let ctx = ContextRef(ctx, PhantomData);
|
|
let this = unsafe { &mut *arg.cast::<W>() };
|
|
this.line_to(ctx, x, y);
|
|
}
|
|
unsafe extern "C" fn curve_to_fn<W: PathWalker<'ctx>>(
|
|
ctx: *mut fz_context,
|
|
arg: *mut c_void,
|
|
x1: f32,
|
|
y1: f32,
|
|
x2: f32,
|
|
y2: f32,
|
|
x3: f32,
|
|
y3: f32,
|
|
) {
|
|
let Some(ctx) = NonNull::new(ctx) else {
|
|
return;
|
|
};
|
|
let ctx = ContextRef(ctx, PhantomData);
|
|
let this = unsafe { &mut *arg.cast::<W>() };
|
|
this.curve_to(ctx, x1, y1, x2, y2, x3, y3);
|
|
}
|
|
unsafe extern "C" fn close_path_fn<W: PathWalker<'ctx>>(
|
|
ctx: *mut fz_context,
|
|
arg: *mut c_void,
|
|
) {
|
|
let Some(ctx) = NonNull::new(ctx) else {
|
|
return;
|
|
};
|
|
let ctx = ContextRef(ctx, PhantomData);
|
|
let this = unsafe { &mut *arg.cast::<W>() };
|
|
this.close_path(ctx);
|
|
}
|
|
unsafe extern "C" fn rect_to_fn<W: PathWalker<'ctx>>(
|
|
ctx: *mut fz_context,
|
|
arg: *mut c_void,
|
|
x1: f32,
|
|
y1: f32,
|
|
x2: f32,
|
|
y2: f32,
|
|
) {
|
|
let Some(ctx) = NonNull::new(ctx) else {
|
|
return;
|
|
};
|
|
let ctx = ContextRef(ctx, PhantomData);
|
|
let this = unsafe { &mut *arg.cast::<W>() };
|
|
this.rect_to(ctx, x1, y1, x2, y2);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn transform_point(point: fz_point, m: fz_matrix) -> fz_point {
|
|
unsafe { fz_transform_point(point, m) }
|
|
}
|
|
|
|
pub(crate) fn transform_point_xy(x: f32, y: f32, m: fz_matrix) -> fz_point {
|
|
unsafe { fz_transform_point_xy(x, y, m) }
|
|
}
|