From 39d52597691c3faf13572a05bf4586affe1cc92d Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Thu, 30 Jan 2025 21:56:23 +0100 Subject: [PATCH 01/11] Start implementing custom devices --- mupdf-sys/src/lib.rs | 10 ++ src/device.rs | 7 ++ src/device/custom.rs | 225 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- 4 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 src/device/custom.rs diff --git a/mupdf-sys/src/lib.rs b/mupdf-sys/src/lib.rs index b35149f..e794ec3 100644 --- a/mupdf-sys/src/lib.rs +++ b/mupdf-sys/src/lib.rs @@ -4,3 +4,13 @@ #![allow(clippy::all)] include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +pub unsafe fn mupdf_new_derived_device( + ctx: *mut fz_context, + label: *const std::ffi::c_char, +) -> *mut T { + let size = std::ffi::c_int::try_from(size_of::()).unwrap(); + let device = fz_new_device_of_size(ctx, size); + let label = Memento_label(device.cast(), label); + label.cast() +} diff --git a/src/device.rs b/src/device.rs index 76f79f9..cd80465 100644 --- a/src/device.rs +++ b/src/device.rs @@ -8,6 +8,9 @@ use crate::{ Shade, StrokeState, Text, TextPage, TextPageOptions, }; +mod custom; +pub use custom::CustomDevice; + #[derive(Debug, Clone, Copy, PartialEq)] #[repr(C)] pub enum BlendMode { @@ -42,6 +45,10 @@ impl Device { Self { dev, list } } + pub fn from_custom(device: D) -> Self { + custom::create(device) + } + pub fn from_pixmap_with_clip(pixmap: &Pixmap, clip: IRect) -> Result { let dev = unsafe { ffi_try!(mupdf_new_draw_device(context(), pixmap.inner, clip.into())) }; Ok(Self { diff --git a/src/device/custom.rs b/src/device/custom.rs new file mode 100644 index 0000000..32a94f1 --- /dev/null +++ b/src/device/custom.rs @@ -0,0 +1,225 @@ +use std::{ffi::c_int, mem, ptr, slice}; + +use mupdf_sys::*; + +use crate::{context, ColorParams, Colorspace, Device, Matrix, Path, StrokeState, Text}; + +#[allow(unused_variables)] +pub trait CustomDevice { + fn close_device(&mut self) {} + + fn fill_path( + &mut self, + path: &Path, + even_odd: bool, + cmt: Matrix, + color_space: Colorspace, + color: &[f32], + alpha: f32, + cp: ColorParams, + ) { + } + + fn stroke_path( + &mut self, + path: &Path, + stroke_state: &StrokeState, + cmt: Matrix, + color_space: Colorspace, + color: &[f32], + alpha: f32, + cp: ColorParams, + ) { + } + + fn fill_text( + &mut self, + text: &Text, + cmt: Matrix, + color_space: Colorspace, + color: &[f32], + alpha: f32, + cp: ColorParams, + ) { + } +} + +impl CustomDevice for &mut T { + fn close_device(&mut self) { + (**self).close_device(); + } + + fn fill_path( + &mut self, + path: &Path, + even_odd: bool, + cmt: Matrix, + color_space: Colorspace, + color: &[f32], + alpha: f32, + cp: ColorParams, + ) { + (**self).fill_path(path, even_odd, cmt, color_space, color, alpha, cp); + } + + fn stroke_path( + &mut self, + path: &Path, + stroke_state: &StrokeState, + cmt: Matrix, + color_space: Colorspace, + color: &[f32], + alpha: f32, + cp: ColorParams, + ) { + (**self).stroke_path(path, stroke_state, cmt, color_space, color, alpha, cp); + } + + fn fill_text( + &mut self, + text: &Text, + cmt: Matrix, + color_space: Colorspace, + color: &[f32], + alpha: f32, + cp: ColorParams, + ) { + (**self).fill_text(text, cmt, color_space, color, alpha, cp); + } +} + +pub(crate) fn create(device: D) -> Device { + let d = unsafe { + let c_device = mupdf_new_derived_device::>(context(), c"RustDevice".as_ptr()); + ptr::write(&raw mut (*c_device).rust_device, device); + + (*c_device).base.close_device = Some(close_device::); + (*c_device).base.drop_device = Some(drop_device::); + (*c_device).base.fill_path = Some(fill_path::); + (*c_device).base.stroke_path = Some(stroke_path::); + (*c_device).base.fill_text = Some(fill_text::); + + Device::from_raw(c_device.cast(), ptr::null_mut()) + }; + d +} + +#[repr(C)] +struct CDevice { + base: fz_device, + rust_device: D, +} + +unsafe fn with_rust_device<'a, D: CustomDevice>(dev: *mut fz_device, f: impl FnOnce(&mut D)) { + let c_device: *mut CDevice = dev.cast(); + let rust_device = &mut (*c_device).rust_device; + f(rust_device); + let _ = rust_device; +} + +unsafe extern "C" fn close_device(_ctx: *mut fz_context, dev: *mut fz_device) { + with_rust_device::(dev, |dev| dev.close_device()); +} + +unsafe extern "C" fn drop_device(_ctx: *mut fz_context, dev: *mut fz_device) { + let c_device: *mut CDevice = dev.cast(); + let rust_device = &raw mut (*c_device).rust_device; + + ptr::drop_in_place(rust_device); +} + +unsafe extern "C" fn fill_path( + _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, +) { + with_rust_device::(dev, |dev| { + let cs = Colorspace::from_raw(color_space); + let cs_n = cs.n() as usize; + + let path = Path::from_raw(path.cast_mut()); + + dev.fill_path( + &path, + even_odd != 0, + cmt.into(), + cs, + slice::from_raw_parts(color, cs_n), + alpha, + color_params.into(), + ); + + mem::forget(path); + }); +} + +unsafe extern "C" fn stroke_path( + _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, +) { + with_rust_device::(dev, |dev| { + let cs = Colorspace::from_raw(color_space); + let cs_n = cs.n() as usize; + + let path = Path::from_raw(path.cast_mut()); + let stroke_state = StrokeState { + inner: stroke_state.cast_mut(), + }; + + dev.stroke_path( + &path, + &stroke_state, + cmt.into(), + cs, + slice::from_raw_parts(color, cs_n), + alpha, + color_params.into(), + ); + + mem::forget(stroke_state); + mem::forget(path); + }); +} + +unsafe extern "C" fn fill_text( + _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, +) { + with_rust_device::(dev, |dev| { + let text = Text { + inner: text.cast_mut(), + }; + + let cs = Colorspace::from_raw(color_space); + let cs_n = cs.n() as usize; + dev.fill_text( + &text, + cmt.into(), + cs, + slice::from_raw_parts(color, cs_n), + alpha, + color_params.into(), + ); + + mem::forget(text); + }); +} diff --git a/src/lib.rs b/src/lib.rs index 5b18e53..38fb1bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,7 +81,7 @@ pub(crate) use context::context; pub use context::Context; pub use cookie::Cookie; pub use destination::{Destination, DestinationKind}; -pub use device::{BlendMode, Device}; +pub use device::{BlendMode, CustomDevice, Device}; pub use display_list::DisplayList; pub use document::{Document, MetadataName}; pub use document_writer::DocumentWriter; From 1c7099d9df03d42ffdb7a37b55170d6f2a8c5b66 Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Thu, 6 Feb 2025 04:33:35 +0100 Subject: [PATCH 02/11] Add clip functions to custom device --- src/device/custom.rs | 221 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 220 insertions(+), 1 deletion(-) diff --git a/src/device/custom.rs b/src/device/custom.rs index 32a94f1..8ad60c3 100644 --- a/src/device/custom.rs +++ b/src/device/custom.rs @@ -2,7 +2,7 @@ use std::{ffi::c_int, mem, ptr, slice}; use mupdf_sys::*; -use crate::{context, ColorParams, Colorspace, Device, Matrix, Path, StrokeState, Text}; +use crate::{context, ColorParams, Colorspace, Device, Matrix, Path, Rect, StrokeState, Text}; #[allow(unused_variables)] pub trait CustomDevice { @@ -32,6 +32,17 @@ pub trait CustomDevice { ) { } + fn clip_path(&mut self, path: &Path, even_odd: bool, cmt: Matrix, scissor: Rect) {} + + fn clip_stroke_path( + &mut self, + path: &Path, + stroke_state: &StrokeState, + cmt: Matrix, + scissor: Rect, + ) { + } + fn fill_text( &mut self, text: &Text, @@ -42,6 +53,31 @@ pub trait CustomDevice { cp: ColorParams, ) { } + + fn stroke_text( + &mut self, + text: &Text, + stroke_state: &StrokeState, + cmt: Matrix, + color_space: Colorspace, + color: &[f32], + alpha: f32, + cp: ColorParams, + ) { + } + + fn clip_text(&mut self, text: &Text, cmt: Matrix, scissor: Rect) {} + + fn clip_stroke_text( + &mut self, + text: &Text, + stroke_state: &StrokeState, + cmt: Matrix, + scissor: Rect, + ) { + } + + fn ignore_text(&mut self, text: &Text, cmt: Matrix) {} } impl CustomDevice for &mut T { @@ -75,6 +111,20 @@ impl CustomDevice for &mut T { (**self).stroke_path(path, stroke_state, cmt, color_space, color, alpha, cp); } + fn clip_path(&mut self, path: &Path, even_odd: bool, cmt: Matrix, scissor: Rect) { + (**self).clip_path(path, even_odd, cmt, scissor); + } + + fn clip_stroke_path( + &mut self, + path: &Path, + stroke_state: &StrokeState, + cmt: Matrix, + scissor: Rect, + ) { + (**self).clip_stroke_path(path, stroke_state, cmt, scissor) + } + fn fill_text( &mut self, text: &Text, @@ -86,6 +136,37 @@ impl CustomDevice for &mut T { ) { (**self).fill_text(text, cmt, color_space, color, alpha, cp); } + + fn stroke_text( + &mut self, + text: &Text, + stroke_state: &StrokeState, + cmt: Matrix, + color_space: Colorspace, + color: &[f32], + alpha: f32, + cp: ColorParams, + ) { + (**self).stroke_text(text, stroke_state, cmt, color_space, color, alpha, cp); + } + + fn clip_text(&mut self, text: &Text, cmt: Matrix, scissor: Rect) { + (**self).clip_text(text, cmt, scissor); + } + + fn clip_stroke_text( + &mut self, + text: &Text, + stroke_state: &StrokeState, + cmt: Matrix, + scissor: Rect, + ) { + (**self).clip_stroke_text(text, stroke_state, cmt, scissor); + } + + fn ignore_text(&mut self, text: &Text, cmt: Matrix) { + (**self).ignore_text(text, cmt); + } } pub(crate) fn create(device: D) -> Device { @@ -97,7 +178,13 @@ pub(crate) fn create(device: D) -> Device { (*c_device).base.drop_device = Some(drop_device::); (*c_device).base.fill_path = Some(fill_path::); (*c_device).base.stroke_path = Some(stroke_path::); + (*c_device).base.clip_path = Some(clip_path::); + (*c_device).base.clip_stroke_path = Some(clip_stroke_path::); (*c_device).base.fill_text = Some(fill_text::); + (*c_device).base.stroke_text = Some(stroke_text::); + (*c_device).base.clip_text = Some(clip_text::); + (*c_device).base.clip_stroke_text = Some(clip_stroke_text::); + (*c_device).base.ignore_text = Some(ignore_text::); Device::from_raw(c_device.cast(), ptr::null_mut()) }; @@ -194,6 +281,44 @@ unsafe extern "C" fn stroke_path( }); } +unsafe extern "C" fn clip_path( + _ctx: *mut fz_context, + dev: *mut fz_device, + path: *const fz_path, + even_odd: c_int, + cmt: fz_matrix, + scissor: fz_rect, +) { + with_rust_device::(dev, |dev| { + let path = Path::from_raw(path.cast_mut()); + + dev.clip_path(&path, even_odd != 0, cmt.into(), scissor.into()); + + mem::forget(path); + }); +} + +unsafe extern "C" fn clip_stroke_path( + _ctx: *mut fz_context, + dev: *mut fz_device, + path: *const fz_path, + stroke_state: *const fz_stroke_state, + cmt: fz_matrix, + scissor: fz_rect, +) { + with_rust_device::(dev, |dev| { + let path = Path::from_raw(path.cast_mut()); + let stroke_state = StrokeState { + inner: stroke_state.cast_mut(), + }; + + dev.clip_stroke_path(&path, &stroke_state, cmt.into(), scissor.into()); + + mem::forget(stroke_state); + mem::forget(path); + }); +} + unsafe extern "C" fn fill_text( _ctx: *mut fz_context, dev: *mut fz_device, @@ -223,3 +348,97 @@ unsafe extern "C" fn fill_text( mem::forget(text); }); } + +unsafe extern "C" fn stroke_text( + _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, +) { + with_rust_device::(dev, |dev| { + let text = Text { + inner: text.cast_mut(), + }; + let stroke_state = StrokeState { + inner: stroke_state.cast_mut(), + }; + + let cs = Colorspace::from_raw(color_space); + let cs_n = cs.n() as usize; + dev.stroke_text( + &text, + &stroke_state, + cmt.into(), + cs, + slice::from_raw_parts(color, cs_n), + alpha, + color_params.into(), + ); + + mem::forget(stroke_state); + mem::forget(text); + }); +} + +unsafe extern "C" fn clip_text( + _ctx: *mut fz_context, + dev: *mut fz_device, + text: *const fz_text, + cmt: fz_matrix, + scissor: fz_rect, +) { + with_rust_device::(dev, |dev| { + let text = Text { + inner: text.cast_mut(), + }; + + dev.clip_text(&text, cmt.into(), scissor.into()); + + mem::forget(text); + }); +} + +unsafe extern "C" fn clip_stroke_text( + _ctx: *mut fz_context, + dev: *mut fz_device, + text: *const fz_text, + stroke_state: *const fz_stroke_state, + cmt: fz_matrix, + scissor: fz_rect, +) { + with_rust_device::(dev, |dev| { + let text = Text { + inner: text.cast_mut(), + }; + let stroke_state = StrokeState { + inner: stroke_state.cast_mut(), + }; + + dev.clip_stroke_text(&text, &stroke_state, cmt.into(), scissor.into()); + + mem::forget(stroke_state); + mem::forget(text); + }); +} + +unsafe extern "C" fn ignore_text( + _ctx: *mut fz_context, + dev: *mut fz_device, + text: *const fz_text, + cmt: fz_matrix, +) { + with_rust_device::(dev, |dev| { + let text = Text { + inner: text.cast_mut(), + }; + + dev.ignore_text(&text, cmt.into()); + + mem::forget(text); + }); +} From 034b14426a47363fcc04c006c71616929a63733b Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Sun, 16 Feb 2025 21:17:01 +0100 Subject: [PATCH 03/11] Add more functions to custom device --- mupdf-sys/build.rs | 4 +- mupdf-sys/wrapper.c | 4 +- src/device.rs | 55 +++++---- src/device/custom.rs | 276 ++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 2 +- 5 files changed, 315 insertions(+), 26 deletions(-) diff --git a/mupdf-sys/build.rs b/mupdf-sys/build.rs index 6e2add5..31eab88 100644 --- a/mupdf-sys/build.rs +++ b/mupdf-sys/build.rs @@ -118,8 +118,8 @@ fn build_libmupdf() { ]; // this may be unused if none of the features below are enabled - #[allow(unused_variables)] - let add_lib = |cflags_name: &'static str, pkgcfg_name: &'static str| { + #[allow(unused_variables, unused_mut)] + let mut add_lib = |cflags_name: &'static str, pkgcfg_name: &'static str| { make_flags.push(format!( "SYS_{cflags_name}_CFLAGS={}", pkg_config::probe_library(pkgcfg_name) diff --git a/mupdf-sys/wrapper.c b/mupdf-sys/wrapper.c index 78d68ba..8b1b3b8 100644 --- a/mupdf-sys/wrapper.c +++ b/mupdf-sys/wrapper.c @@ -2875,11 +2875,11 @@ void mupdf_begin_mask(fz_context *ctx, fz_device *device, fz_rect area, bool lum } } -void mupdf_end_mask(fz_context *ctx, fz_device *device, mupdf_error_t **errptr) +void mupdf_end_mask(fz_context *ctx, fz_device *device, fz_function *fn, mupdf_error_t **errptr) { fz_try(ctx) { - fz_end_mask(ctx, device); + fz_end_mask_tr(ctx, device, fn); } fz_catch(ctx) { diff --git a/src/device.rs b/src/device.rs index cd80465..02904cb 100644 --- a/src/device.rs +++ b/src/device.rs @@ -2,6 +2,7 @@ use std::ffi::CString; use std::ptr; use mupdf_sys::*; +use num_enum::TryFromPrimitive; use crate::{ context, ColorParams, Colorspace, DisplayList, Error, IRect, Image, Matrix, Path, Pixmap, Rect, @@ -11,27 +12,37 @@ use crate::{ mod custom; pub use custom::CustomDevice; -#[derive(Debug, Clone, Copy, PartialEq)] -#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, TryFromPrimitive)] +#[repr(u32)] pub enum BlendMode { /* PDF 1.4 -- standard separable */ - Normal = 0, - Multiply = 1, - Screen = 2, - Overlay = 3, - Darken = 4, - Lighten = 5, - ColorDodge = 6, - ColorBurn = 7, - HardLight = 8, - SoftLight = 9, - Difference = 10, - Exclusion = 11, + Normal = FZ_BLEND_NORMAL as u32, + Multiply = FZ_BLEND_MULTIPLY as u32, + Screen = FZ_BLEND_SCREEN as u32, + Overlay = FZ_BLEND_OVERLAY as u32, + Darken = FZ_BLEND_DARKEN as u32, + Lighten = FZ_BLEND_LIGHTEN as u32, + ColorDodge = FZ_BLEND_COLOR_DODGE as u32, + ColorBurn = FZ_BLEND_COLOR_BURN as u32, + HardLight = FZ_BLEND_HARD_LIGHT as u32, + SoftLight = FZ_BLEND_SOFT_LIGHT as u32, + Difference = FZ_BLEND_DIFFERENCE as u32, + Exclusion = FZ_BLEND_EXCLUSION as u32, /* PDF 1.4 -- standard non-separable */ - Hue = 12, - Saturation = 13, - Color = 14, - Luminosity = 15, + Hue = FZ_BLEND_HUE as u32, + Saturation = FZ_BLEND_SATURATION as u32, + Color = FZ_BLEND_COLOR as u32, + Luminosity = FZ_BLEND_LUMINOSITY as u32, +} + +pub struct Function { + pub(crate) inner: *mut fz_function, +} + +impl Drop for Function { + fn drop(&mut self) { + unsafe { fz_drop_function(context(), self.inner) } + } } #[derive(Debug)] @@ -359,9 +370,13 @@ impl Device { Ok(()) } - pub fn end_mask(&self) -> Result<(), Error> { + pub fn end_mask(&self, f: Option<&Function>) -> Result<(), Error> { unsafe { - ffi_try!(mupdf_end_mask(context(), self.dev)); + ffi_try!(mupdf_end_mask( + context(), + self.dev, + f.map_or(ptr::null_mut(), |f| f.inner) + )); } Ok(()) } diff --git a/src/device/custom.rs b/src/device/custom.rs index 8ad60c3..a79ecea 100644 --- a/src/device/custom.rs +++ b/src/device/custom.rs @@ -2,7 +2,10 @@ use std::{ffi::c_int, mem, ptr, slice}; use mupdf_sys::*; -use crate::{context, ColorParams, Colorspace, Device, Matrix, Path, Rect, StrokeState, Text}; +use crate::{ + context, BlendMode, ColorParams, Colorspace, Device, Function, Image, Matrix, Path, Rect, + Shade, StrokeState, Text, +}; #[allow(unused_variables)] pub trait CustomDevice { @@ -78,6 +81,50 @@ pub trait CustomDevice { } fn ignore_text(&mut self, text: &Text, cmt: Matrix) {} + + fn fill_shade(&mut self, shade: &Shade, cmt: Matrix, alpha: f32, cp: ColorParams) {} + + fn fill_image(&mut self, img: &Image, cmt: Matrix, alpha: f32, cp: ColorParams) {} + + fn fill_image_mask( + &mut self, + img: &Image, + cmt: Matrix, + color_space: Colorspace, + color: &[f32], + alpha: f32, + cp: ColorParams, + ) { + } + + fn clip_image_mask(&mut self, img: &Image, cmt: Matrix, scissor: Rect) {} + + fn pop_clip(&mut self) {} + + fn begin_mask( + &mut self, + area: Rect, + luminosity: bool, + color_space: Colorspace, + color: &[f32], + cp: ColorParams, + ) { + } + + fn end_mask(&mut self, f: &Function) {} + + fn begin_group( + &mut self, + area: Rect, + cs: Colorspace, + isolated: bool, + knockout: bool, + blendmode: BlendMode, + alpha: f32, + ) { + } + + fn end_group(&mut self) {} } impl CustomDevice for &mut T { @@ -167,6 +214,65 @@ impl CustomDevice for &mut T { fn ignore_text(&mut self, text: &Text, cmt: Matrix) { (**self).ignore_text(text, cmt); } + + fn fill_shade(&mut self, shade: &Shade, cmt: Matrix, alpha: f32, cp: ColorParams) { + (**self).fill_shade(shade, cmt, alpha, cp); + } + + fn fill_image(&mut self, img: &Image, cmt: Matrix, alpha: f32, cp: ColorParams) { + (**self).fill_image(img, cmt, alpha, cp); + } + + fn fill_image_mask( + &mut self, + img: &Image, + cmt: Matrix, + color_space: Colorspace, + color: &[f32], + alpha: f32, + cp: ColorParams, + ) { + (**self).fill_image_mask(img, cmt, color_space, color, alpha, cp); + } + + fn clip_image_mask(&mut self, img: &Image, cmt: Matrix, scissor: Rect) { + (**self).clip_image_mask(img, cmt, scissor); + } + + fn pop_clip(&mut self) { + (**self).pop_clip(); + } + + fn begin_mask( + &mut self, + area: Rect, + luminosity: bool, + color_space: Colorspace, + color: &[f32], + cp: ColorParams, + ) { + (**self).begin_mask(area, luminosity, color_space, color, cp); + } + + fn end_mask(&mut self, f: &Function) { + (**self).end_mask(f); + } + + fn begin_group( + &mut self, + area: Rect, + cs: Colorspace, + isolated: bool, + knockout: bool, + blendmode: BlendMode, + alpha: f32, + ) { + (**self).begin_group(area, cs, isolated, knockout, blendmode, alpha); + } + + fn end_group(&mut self) { + (**self).end_group(); + } } pub(crate) fn create(device: D) -> Device { @@ -176,16 +282,30 @@ pub(crate) fn create(device: D) -> Device { (*c_device).base.close_device = Some(close_device::); (*c_device).base.drop_device = Some(drop_device::); + (*c_device).base.fill_path = Some(fill_path::); (*c_device).base.stroke_path = Some(stroke_path::); (*c_device).base.clip_path = Some(clip_path::); (*c_device).base.clip_stroke_path = Some(clip_stroke_path::); + (*c_device).base.fill_text = Some(fill_text::); (*c_device).base.stroke_text = Some(stroke_text::); (*c_device).base.clip_text = Some(clip_text::); (*c_device).base.clip_stroke_text = Some(clip_stroke_text::); (*c_device).base.ignore_text = Some(ignore_text::); + (*c_device).base.fill_shade = Some(fill_shade::); + (*c_device).base.fill_image = Some(fill_image::); + (*c_device).base.fill_image_mask = Some(fill_image_mask::); + (*c_device).base.clip_image_mask = Some(clip_image_mask::); + + (*c_device).base.pop_clip = Some(pop_clip::); + + (*c_device).base.begin_mask = Some(begin_mask::); + (*c_device).base.end_mask = Some(end_mask::); + (*c_device).base.begin_group = Some(begin_group::); + (*c_device).base.end_group = Some(end_group::); + Device::from_raw(c_device.cast(), ptr::null_mut()) }; d @@ -442,3 +562,157 @@ unsafe extern "C" fn ignore_text( mem::forget(text); }); } + +unsafe extern "C" fn fill_shade( + _ctx: *mut fz_context, + dev: *mut fz_device, + shd: *mut fz_shade, + ctm: fz_matrix, + alpha: f32, + color_params: fz_color_params, +) { + with_rust_device::(dev, |dev| { + let shade = Shade { inner: shd }; + + dev.fill_shade(&shade, ctm.into(), alpha, color_params.into()); + + mem::forget(shade); + }); +} + +unsafe extern "C" fn fill_image( + _ctx: *mut fz_context, + dev: *mut fz_device, + img: *mut fz_image, + ctm: fz_matrix, + alpha: f32, + color_params: fz_color_params, +) { + with_rust_device::(dev, |dev| { + let img = Image::from_raw(img); + + dev.fill_image(&img, ctm.into(), alpha, color_params.into()); + + mem::forget(img); + }); +} + +unsafe extern "C" fn fill_image_mask( + _ctx: *mut fz_context, + dev: *mut fz_device, + img: *mut fz_image, + ctm: fz_matrix, + color_space: *mut fz_colorspace, + color: *const f32, + alpha: f32, + color_params: fz_color_params, +) { + with_rust_device::(dev, |dev| { + let cs = Colorspace::from_raw(color_space); + let cs_n = cs.n() as usize; + + let img = Image::from_raw(img); + + dev.fill_image_mask( + &img, + ctm.into(), + cs, + slice::from_raw_parts(color, cs_n), + alpha, + color_params.into(), + ); + + mem::forget(img); + }); +} + +unsafe extern "C" fn clip_image_mask( + _ctx: *mut fz_context, + dev: *mut fz_device, + img: *mut fz_image, + cmt: fz_matrix, + scissor: fz_rect, +) { + with_rust_device::(dev, |dev| { + let img = Image::from_raw(img); + + dev.clip_image_mask(&img, cmt.into(), scissor.into()); + + mem::forget(img); + }); +} + +unsafe extern "C" fn pop_clip(_ctx: *mut fz_context, dev: *mut fz_device) { + with_rust_device::(dev, |dev| { + dev.pop_clip(); + }); +} + +unsafe extern "C" fn begin_mask( + _ctx: *mut fz_context, + dev: *mut fz_device, + area: fz_rect, + luminosity: c_int, + color_space: *mut fz_colorspace, + color: *const f32, + color_params: fz_color_params, +) { + with_rust_device::(dev, |dev| { + let cs = Colorspace::from_raw(color_space); + let cs_n = cs.n() as usize; + + dev.begin_mask( + area.into(), + luminosity != 0, + cs, + slice::from_raw_parts(color, cs_n), + color_params.into(), + ); + }); +} + +unsafe extern "C" fn end_mask( + _ctx: *mut fz_context, + dev: *mut fz_device, + f: *mut fz_function, +) { + with_rust_device::(dev, |dev| { + let f = Function { inner: f }; + + dev.end_mask(&f); + + mem::forget(f); + }); +} + +unsafe extern "C" fn begin_group( + _ctx: *mut fz_context, + dev: *mut fz_device, + area: fz_rect, + color_space: *mut fz_colorspace, + isolated: c_int, + knockout: c_int, + blendmode: c_int, + alpha: f32, +) { + with_rust_device::(dev, |dev| { + let cs = Colorspace::from_raw(color_space); + + let blendmode = BlendMode::try_from(blendmode as u32).unwrap(); + + dev.begin_group( + area.into(), + cs, + isolated != 0, + knockout != 0, + blendmode, + alpha, + ); + }); +} + +unsafe extern "C" fn end_group(_ctx: *mut fz_context, dev: *mut fz_device) { + with_rust_device::(dev, |dev| { + dev.end_group(); + }); +} diff --git a/src/lib.rs b/src/lib.rs index 38fb1bc..3ca479a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,7 +81,7 @@ pub(crate) use context::context; pub use context::Context; pub use cookie::Cookie; pub use destination::{Destination, DestinationKind}; -pub use device::{BlendMode, CustomDevice, Device}; +pub use device::{BlendMode, CustomDevice, Device, Function}; pub use display_list::DisplayList; pub use document::{Document, MetadataName}; pub use document_writer::DocumentWriter; From 0b231cc3abd16353ca6a96f32a699a572dca12a5 Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Sun, 16 Feb 2025 22:12:36 +0100 Subject: [PATCH 04/11] Rename to NativeDevice --- src/device.rs | 18 +-- src/device/{custom.rs => native.rs} | 163 +++++++++++++++++++--------- src/lib.rs | 2 +- 3 files changed, 123 insertions(+), 60 deletions(-) rename src/device/{custom.rs => native.rs} (80%) diff --git a/src/device.rs b/src/device.rs index 02904cb..dc22030 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,5 +1,5 @@ -use std::ffi::CString; use std::ptr; +use std::{ffi::CString, num::NonZero}; use mupdf_sys::*; use num_enum::TryFromPrimitive; @@ -9,8 +9,8 @@ use crate::{ Shade, StrokeState, Text, TextPage, TextPageOptions, }; -mod custom; -pub use custom::CustomDevice; +mod native; +pub use native::NativeDevice; #[derive(Debug, Clone, Copy, PartialEq, TryFromPrimitive)] #[repr(u32)] @@ -56,8 +56,8 @@ impl Device { Self { dev, list } } - pub fn from_custom(device: D) -> Self { - custom::create(device) + pub fn from_native(device: D) -> Self { + native::create(device) } pub fn from_pixmap_with_clip(pixmap: &Pixmap, clip: IRect) -> Result { @@ -419,8 +419,8 @@ impl Device { xstep: f32, ystep: f32, ctm: &Matrix, - id: i32, - ) -> Result { + id: Option>, + ) -> Result>, Error> { let i = unsafe { ffi_try!(mupdf_begin_tile( context(), @@ -430,10 +430,10 @@ impl Device { xstep, ystep, ctm.into(), - id + id.map_or(0, NonZero::get) )) }; - Ok(i) + Ok(NonZero::new(i)) } pub fn end_tile(&self) -> Result<(), Error> { diff --git a/src/device/custom.rs b/src/device/native.rs similarity index 80% rename from src/device/custom.rs rename to src/device/native.rs index a79ecea..79073b9 100644 --- a/src/device/custom.rs +++ b/src/device/native.rs @@ -1,4 +1,4 @@ -use std::{ffi::c_int, mem, ptr, slice}; +use std::{ffi::c_int, mem, num::NonZero, ptr, slice}; use mupdf_sys::*; @@ -7,8 +7,8 @@ use crate::{ Shade, StrokeState, Text, }; -#[allow(unused_variables)] -pub trait CustomDevice { +#[allow(unused_variables, clippy::too_many_arguments)] +pub trait NativeDevice { fn close_device(&mut self) {} fn fill_path( @@ -125,9 +125,23 @@ pub trait CustomDevice { } fn end_group(&mut self) {} + + fn begin_tile( + &mut self, + area: Rect, + view: Rect, + x_step: f32, + y_step: f32, + ctm: Matrix, + id: Option>, + ) -> Option> { + None + } + + fn end_tile(&mut self) {} } -impl CustomDevice for &mut T { +impl NativeDevice for &mut T { fn close_device(&mut self) { (**self).close_device(); } @@ -273,10 +287,26 @@ impl CustomDevice for &mut T { fn end_group(&mut self) { (**self).end_group(); } + + fn begin_tile( + &mut self, + area: Rect, + view: Rect, + x_step: f32, + y_step: f32, + ctm: Matrix, + id: Option>, + ) -> Option> { + (**self).begin_tile(area, view, x_step, y_step, ctm, id) + } + + fn end_tile(&mut self) { + (**self).end_tile(); + } } -pub(crate) fn create(device: D) -> Device { - let d = unsafe { +pub(crate) fn create(device: D) -> Device { + unsafe { let c_device = mupdf_new_derived_device::>(context(), c"RustDevice".as_ptr()); ptr::write(&raw mut (*c_device).rust_device, device); @@ -306,9 +336,11 @@ pub(crate) fn create(device: D) -> Device { (*c_device).base.begin_group = Some(begin_group::); (*c_device).base.end_group = Some(end_group::); + (*c_device).base.begin_tile = Some(begin_tile::); + (*c_device).base.end_tile = Some(end_tile::); + Device::from_raw(c_device.cast(), ptr::null_mut()) - }; - d + } } #[repr(C)] @@ -317,25 +349,27 @@ struct CDevice { rust_device: D, } -unsafe fn with_rust_device<'a, D: CustomDevice>(dev: *mut fz_device, f: impl FnOnce(&mut D)) { +unsafe fn with_rust_device( + dev: *mut fz_device, + f: impl FnOnce(&mut D) -> T, +) -> T { let c_device: *mut CDevice = dev.cast(); let rust_device = &mut (*c_device).rust_device; - f(rust_device); - let _ = rust_device; + f(rust_device) } -unsafe extern "C" fn close_device(_ctx: *mut fz_context, dev: *mut fz_device) { - with_rust_device::(dev, |dev| dev.close_device()); +unsafe extern "C" fn close_device(_ctx: *mut fz_context, dev: *mut fz_device) { + with_rust_device::(dev, |dev| dev.close_device()); } -unsafe extern "C" fn drop_device(_ctx: *mut fz_context, dev: *mut fz_device) { +unsafe extern "C" fn drop_device(_ctx: *mut fz_context, dev: *mut fz_device) { let c_device: *mut CDevice = dev.cast(); let rust_device = &raw mut (*c_device).rust_device; ptr::drop_in_place(rust_device); } -unsafe extern "C" fn fill_path( +unsafe extern "C" fn fill_path( _ctx: *mut fz_context, dev: *mut fz_device, path: *const fz_path, @@ -346,7 +380,7 @@ unsafe extern "C" fn fill_path( alpha: f32, color_params: fz_color_params, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let cs = Colorspace::from_raw(color_space); let cs_n = cs.n() as usize; @@ -366,7 +400,7 @@ unsafe extern "C" fn fill_path( }); } -unsafe extern "C" fn stroke_path( +unsafe extern "C" fn stroke_path( _ctx: *mut fz_context, dev: *mut fz_device, path: *const fz_path, @@ -377,7 +411,7 @@ unsafe extern "C" fn stroke_path( alpha: f32, color_params: fz_color_params, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let cs = Colorspace::from_raw(color_space); let cs_n = cs.n() as usize; @@ -401,7 +435,7 @@ unsafe extern "C" fn stroke_path( }); } -unsafe extern "C" fn clip_path( +unsafe extern "C" fn clip_path( _ctx: *mut fz_context, dev: *mut fz_device, path: *const fz_path, @@ -409,7 +443,7 @@ unsafe extern "C" fn clip_path( cmt: fz_matrix, scissor: fz_rect, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let path = Path::from_raw(path.cast_mut()); dev.clip_path(&path, even_odd != 0, cmt.into(), scissor.into()); @@ -418,7 +452,7 @@ unsafe extern "C" fn clip_path( }); } -unsafe extern "C" fn clip_stroke_path( +unsafe extern "C" fn clip_stroke_path( _ctx: *mut fz_context, dev: *mut fz_device, path: *const fz_path, @@ -426,7 +460,7 @@ unsafe extern "C" fn clip_stroke_path( cmt: fz_matrix, scissor: fz_rect, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let path = Path::from_raw(path.cast_mut()); let stroke_state = StrokeState { inner: stroke_state.cast_mut(), @@ -439,7 +473,7 @@ unsafe extern "C" fn clip_stroke_path( }); } -unsafe extern "C" fn fill_text( +unsafe extern "C" fn fill_text( _ctx: *mut fz_context, dev: *mut fz_device, text: *const fz_text, @@ -449,7 +483,7 @@ unsafe extern "C" fn fill_text( alpha: f32, color_params: fz_color_params, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let text = Text { inner: text.cast_mut(), }; @@ -469,7 +503,7 @@ unsafe extern "C" fn fill_text( }); } -unsafe extern "C" fn stroke_text( +unsafe extern "C" fn stroke_text( _ctx: *mut fz_context, dev: *mut fz_device, text: *const fz_text, @@ -480,7 +514,7 @@ unsafe extern "C" fn stroke_text( alpha: f32, color_params: fz_color_params, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let text = Text { inner: text.cast_mut(), }; @@ -505,14 +539,14 @@ unsafe extern "C" fn stroke_text( }); } -unsafe extern "C" fn clip_text( +unsafe extern "C" fn clip_text( _ctx: *mut fz_context, dev: *mut fz_device, text: *const fz_text, cmt: fz_matrix, scissor: fz_rect, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let text = Text { inner: text.cast_mut(), }; @@ -523,7 +557,7 @@ unsafe extern "C" fn clip_text( }); } -unsafe extern "C" fn clip_stroke_text( +unsafe extern "C" fn clip_stroke_text( _ctx: *mut fz_context, dev: *mut fz_device, text: *const fz_text, @@ -531,7 +565,7 @@ unsafe extern "C" fn clip_stroke_text( cmt: fz_matrix, scissor: fz_rect, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let text = Text { inner: text.cast_mut(), }; @@ -546,13 +580,13 @@ unsafe extern "C" fn clip_stroke_text( }); } -unsafe extern "C" fn ignore_text( +unsafe extern "C" fn ignore_text( _ctx: *mut fz_context, dev: *mut fz_device, text: *const fz_text, cmt: fz_matrix, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let text = Text { inner: text.cast_mut(), }; @@ -563,7 +597,7 @@ unsafe extern "C" fn ignore_text( }); } -unsafe extern "C" fn fill_shade( +unsafe extern "C" fn fill_shade( _ctx: *mut fz_context, dev: *mut fz_device, shd: *mut fz_shade, @@ -571,7 +605,7 @@ unsafe extern "C" fn fill_shade( alpha: f32, color_params: fz_color_params, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let shade = Shade { inner: shd }; dev.fill_shade(&shade, ctm.into(), alpha, color_params.into()); @@ -580,7 +614,7 @@ unsafe extern "C" fn fill_shade( }); } -unsafe extern "C" fn fill_image( +unsafe extern "C" fn fill_image( _ctx: *mut fz_context, dev: *mut fz_device, img: *mut fz_image, @@ -588,7 +622,7 @@ unsafe extern "C" fn fill_image( alpha: f32, color_params: fz_color_params, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let img = Image::from_raw(img); dev.fill_image(&img, ctm.into(), alpha, color_params.into()); @@ -597,7 +631,7 @@ unsafe extern "C" fn fill_image( }); } -unsafe extern "C" fn fill_image_mask( +unsafe extern "C" fn fill_image_mask( _ctx: *mut fz_context, dev: *mut fz_device, img: *mut fz_image, @@ -607,7 +641,7 @@ unsafe extern "C" fn fill_image_mask( alpha: f32, color_params: fz_color_params, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let cs = Colorspace::from_raw(color_space); let cs_n = cs.n() as usize; @@ -626,14 +660,14 @@ unsafe extern "C" fn fill_image_mask( }); } -unsafe extern "C" fn clip_image_mask( +unsafe extern "C" fn clip_image_mask( _ctx: *mut fz_context, dev: *mut fz_device, img: *mut fz_image, cmt: fz_matrix, scissor: fz_rect, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let img = Image::from_raw(img); dev.clip_image_mask(&img, cmt.into(), scissor.into()); @@ -642,13 +676,13 @@ unsafe extern "C" fn clip_image_mask( }); } -unsafe extern "C" fn pop_clip(_ctx: *mut fz_context, dev: *mut fz_device) { - with_rust_device::(dev, |dev| { +unsafe extern "C" fn pop_clip(_ctx: *mut fz_context, dev: *mut fz_device) { + with_rust_device::(dev, |dev| { dev.pop_clip(); }); } -unsafe extern "C" fn begin_mask( +unsafe extern "C" fn begin_mask( _ctx: *mut fz_context, dev: *mut fz_device, area: fz_rect, @@ -657,7 +691,7 @@ unsafe extern "C" fn begin_mask( color: *const f32, color_params: fz_color_params, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let cs = Colorspace::from_raw(color_space); let cs_n = cs.n() as usize; @@ -671,12 +705,12 @@ unsafe extern "C" fn begin_mask( }); } -unsafe extern "C" fn end_mask( +unsafe extern "C" fn end_mask( _ctx: *mut fz_context, dev: *mut fz_device, f: *mut fz_function, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let f = Function { inner: f }; dev.end_mask(&f); @@ -685,7 +719,7 @@ unsafe extern "C" fn end_mask( }); } -unsafe extern "C" fn begin_group( +unsafe extern "C" fn begin_group( _ctx: *mut fz_context, dev: *mut fz_device, area: fz_rect, @@ -695,7 +729,7 @@ unsafe extern "C" fn begin_group( blendmode: c_int, alpha: f32, ) { - with_rust_device::(dev, |dev| { + with_rust_device::(dev, |dev| { let cs = Colorspace::from_raw(color_space); let blendmode = BlendMode::try_from(blendmode as u32).unwrap(); @@ -711,8 +745,37 @@ unsafe extern "C" fn begin_group( }); } -unsafe extern "C" fn end_group(_ctx: *mut fz_context, dev: *mut fz_device) { - with_rust_device::(dev, |dev| { +unsafe extern "C" fn end_group(_ctx: *mut fz_context, dev: *mut fz_device) { + with_rust_device::(dev, |dev| { dev.end_group(); }); } + +unsafe extern "C" fn begin_tile( + _ctx: *mut fz_context, + dev: *mut fz_device, + area: fz_rect, + view: fz_rect, + xstep: f32, + ystep: f32, + ctm: fz_matrix, + id: c_int, +) -> c_int { + let i = with_rust_device::(dev, |dev| { + dev.begin_tile( + area.into(), + view.into(), + xstep, + ystep, + ctm.into(), + NonZero::new(id as i32), + ) + }); + i.map_or(0, NonZero::get) as c_int +} + +unsafe extern "C" fn end_tile(_ctx: *mut fz_context, dev: *mut fz_device) { + with_rust_device::(dev, |dev| { + dev.end_tile(); + }); +} diff --git a/src/lib.rs b/src/lib.rs index 3ca479a..d87e9a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,7 +81,7 @@ pub(crate) use context::context; pub use context::Context; pub use cookie::Cookie; pub use destination::{Destination, DestinationKind}; -pub use device::{BlendMode, CustomDevice, Device, Function}; +pub use device::{BlendMode, Device, Function, NativeDevice}; pub use display_list::DisplayList; pub use document::{Document, MetadataName}; pub use document_writer::DocumentWriter; From 5dd9938dbea83945d47e6444fdd03349e37fe7de Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Mon, 17 Feb 2025 10:11:17 +0100 Subject: [PATCH 05/11] Add render_flags and layer --- src/device.rs | 49 +++++++++++++++++++++++++ src/device/native.rs | 85 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/src/device.rs b/src/device.rs index dc22030..e46ca01 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,6 +1,7 @@ use std::ptr; use std::{ffi::CString, num::NonZero}; +use bitflags::bitflags; use mupdf_sys::*; use num_enum::TryFromPrimitive; @@ -35,6 +36,54 @@ pub enum BlendMode { Luminosity = FZ_BLEND_LUMINOSITY as u32, } +bitflags! { + pub struct DeviceFlag: u32 { + const MASK = FZ_DEVFLAG_MASK as _; + const COLOR = FZ_DEVFLAG_COLOR as _; + const UNCACHEABLE = FZ_DEVFLAG_UNCACHEABLE as _; + const FILLCOLOR_UNDEFINED = FZ_DEVFLAG_FILLCOLOR_UNDEFINED as _; + const STROKECOLOR_UNDEFINED = FZ_DEVFLAG_STROKECOLOR_UNDEFINED as _; + const STARTCAP_UNDEFINED = FZ_DEVFLAG_STARTCAP_UNDEFINED as _; + const DASHCAP_UNDEFINED = FZ_DEVFLAG_DASHCAP_UNDEFINED as _; + const ENDCAP_UNDEFINED = FZ_DEVFLAG_ENDCAP_UNDEFINED as _; + const LINEJOIN_UNDEFINED = FZ_DEVFLAG_LINEJOIN_UNDEFINED as _; + const MITERLIMIT_UNDEFINED = FZ_DEVFLAG_MITERLIMIT_UNDEFINED as _; + const LINEWIDTH_UNDEFINED = FZ_DEVFLAG_LINEWIDTH_UNDEFINED as _; + const BBOX_DEFINED = FZ_DEVFLAG_BBOX_DEFINED as _; + const GRIDFIT_AS_TILED = FZ_DEVFLAG_GRIDFIT_AS_TILED as _; + // will probably be released in 1.26.x + // const DASH_PATTERN_UNDEFINED = FZ_DEVFLAG_DASH_PATTERN_UNDEFINED as _; + } +} + +pub struct DefaultColorspaces { + pub(crate) inner: *mut fz_default_colorspaces, +} + +impl DefaultColorspaces { + pub fn gray(&self) -> Colorspace { + unsafe { Colorspace::from_raw((*self.inner).gray) } + } + + pub fn rgb(&self) -> Colorspace { + unsafe { Colorspace::from_raw((*self.inner).rgb) } + } + + pub fn cmyk(&self) -> Colorspace { + unsafe { Colorspace::from_raw((*self.inner).cmyk) } + } + + pub fn oi(&self) -> Colorspace { + unsafe { Colorspace::from_raw((*self.inner).oi) } + } +} + +impl Drop for DefaultColorspaces { + fn drop(&mut self) { + unsafe { fz_drop_default_colorspaces(context(), self.inner) } + } +} + pub struct Function { pub(crate) inner: *mut fz_function, } diff --git a/src/device/native.rs b/src/device/native.rs index 79073b9..56aafa4 100644 --- a/src/device/native.rs +++ b/src/device/native.rs @@ -1,4 +1,9 @@ -use std::{ffi::c_int, mem, num::NonZero, ptr, slice}; +use std::{ + ffi::{c_char, c_int, CStr}, + mem, + num::NonZero, + ptr, slice, +}; use mupdf_sys::*; @@ -7,6 +12,8 @@ use crate::{ Shade, StrokeState, Text, }; +use super::{DefaultColorspaces, DeviceFlag}; + #[allow(unused_variables, clippy::too_many_arguments)] pub trait NativeDevice { fn close_device(&mut self) {} @@ -139,6 +146,14 @@ pub trait NativeDevice { } fn end_tile(&mut self) {} + + fn render_flags(&mut self, set: DeviceFlag, clear: DeviceFlag) {} + + fn set_default_colorspaces(&mut self, default_cs: &DefaultColorspaces) {} + + fn begin_layer(&mut self, name: &str) {} + + fn end_layer(&mut self) {} } impl NativeDevice for &mut T { @@ -303,6 +318,22 @@ impl NativeDevice for &mut T { fn end_tile(&mut self) { (**self).end_tile(); } + + fn render_flags(&mut self, set: DeviceFlag, clear: DeviceFlag) { + (**self).render_flags(set, clear); + } + + fn set_default_colorspaces(&mut self, default_cs: &DefaultColorspaces) { + (**self).set_default_colorspaces(default_cs); + } + + fn begin_layer(&mut self, name: &str) { + (**self).begin_layer(name); + } + + fn end_layer(&mut self) { + (**self).end_layer(); + } } pub(crate) fn create(device: D) -> Device { @@ -339,6 +370,12 @@ pub(crate) fn create(device: D) -> Device { (*c_device).base.begin_tile = Some(begin_tile::); (*c_device).base.end_tile = Some(end_tile::); + (*c_device).base.render_flags = Some(render_flags::); + (*c_device).base.set_default_colorspaces = Some(set_default_colorspaces::); + + (*c_device).base.begin_layer = Some(begin_layer::); + (*c_device).base.end_layer = Some(end_layer::); + Device::from_raw(c_device.cast(), ptr::null_mut()) } } @@ -779,3 +816,49 @@ unsafe extern "C" fn end_tile(_ctx: *mut fz_context, dev: *mut dev.end_tile(); }); } + +unsafe extern "C" fn render_flags( + _ctx: *mut fz_context, + dev: *mut fz_device, + set: c_int, + clear: c_int, +) { + with_rust_device::(dev, |dev| { + dev.render_flags( + DeviceFlag::from_bits(set as u32).unwrap(), + DeviceFlag::from_bits(clear as u32).unwrap(), + ); + }); +} + +unsafe extern "C" fn set_default_colorspaces( + _ctx: *mut fz_context, + dev: *mut fz_device, + dcs: *mut fz_default_colorspaces, +) { + with_rust_device::(dev, |dev| { + let dcs = DefaultColorspaces { inner: dcs }; + + dev.set_default_colorspaces(&dcs); + + mem::forget(dcs); + }); +} + +unsafe extern "C" fn begin_layer( + _ctx: *mut fz_context, + dev: *mut fz_device, + layer_name: *const c_char, +) { + with_rust_device::(dev, |dev| { + let name = unsafe { CStr::from_ptr(layer_name) }.to_str().unwrap(); + + dev.begin_layer(name); + }); +} + +unsafe extern "C" fn end_layer(_ctx: *mut fz_context, dev: *mut fz_device) { + with_rust_device::(dev, |dev| { + dev.end_layer(); + }); +} From 962c8dc28a8913a61d73de502d8dff497b53cd45 Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Mon, 17 Feb 2025 11:33:16 +0100 Subject: [PATCH 06/11] Replace `mem::forget` with `ManuallyDrop` --- src/device.rs | 18 ----- src/device/native.rs | 154 +++++++++++++++++-------------------------- 2 files changed, 61 insertions(+), 111 deletions(-) diff --git a/src/device.rs b/src/device.rs index e46ca01..e76d324 100644 --- a/src/device.rs +++ b/src/device.rs @@ -60,24 +60,6 @@ pub struct DefaultColorspaces { pub(crate) inner: *mut fz_default_colorspaces, } -impl DefaultColorspaces { - pub fn gray(&self) -> Colorspace { - unsafe { Colorspace::from_raw((*self.inner).gray) } - } - - pub fn rgb(&self) -> Colorspace { - unsafe { Colorspace::from_raw((*self.inner).rgb) } - } - - pub fn cmyk(&self) -> Colorspace { - unsafe { Colorspace::from_raw((*self.inner).cmyk) } - } - - pub fn oi(&self) -> Colorspace { - unsafe { Colorspace::from_raw((*self.inner).oi) } - } -} - impl Drop for DefaultColorspaces { fn drop(&mut self) { unsafe { fz_drop_default_colorspaces(context(), self.inner) } diff --git a/src/device/native.rs b/src/device/native.rs index 56aafa4..15385d5 100644 --- a/src/device/native.rs +++ b/src/device/native.rs @@ -1,6 +1,6 @@ use std::{ ffi::{c_char, c_int, CStr}, - mem, + mem::ManuallyDrop, num::NonZero, ptr, slice, }; @@ -23,7 +23,7 @@ pub trait NativeDevice { path: &Path, even_odd: bool, cmt: Matrix, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], alpha: f32, cp: ColorParams, @@ -35,7 +35,7 @@ pub trait NativeDevice { path: &Path, stroke_state: &StrokeState, cmt: Matrix, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], alpha: f32, cp: ColorParams, @@ -57,7 +57,7 @@ pub trait NativeDevice { &mut self, text: &Text, cmt: Matrix, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], alpha: f32, cp: ColorParams, @@ -69,7 +69,7 @@ pub trait NativeDevice { text: &Text, stroke_state: &StrokeState, cmt: Matrix, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], alpha: f32, cp: ColorParams, @@ -97,7 +97,7 @@ pub trait NativeDevice { &mut self, img: &Image, cmt: Matrix, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], alpha: f32, cp: ColorParams, @@ -112,7 +112,7 @@ pub trait NativeDevice { &mut self, area: Rect, luminosity: bool, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], cp: ColorParams, ) { @@ -123,7 +123,7 @@ pub trait NativeDevice { fn begin_group( &mut self, area: Rect, - cs: Colorspace, + cs: &Colorspace, isolated: bool, knockout: bool, blendmode: BlendMode, @@ -166,7 +166,7 @@ impl NativeDevice for &mut T { path: &Path, even_odd: bool, cmt: Matrix, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], alpha: f32, cp: ColorParams, @@ -179,7 +179,7 @@ impl NativeDevice for &mut T { path: &Path, stroke_state: &StrokeState, cmt: Matrix, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], alpha: f32, cp: ColorParams, @@ -205,7 +205,7 @@ impl NativeDevice for &mut T { &mut self, text: &Text, cmt: Matrix, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], alpha: f32, cp: ColorParams, @@ -218,7 +218,7 @@ impl NativeDevice for &mut T { text: &Text, stroke_state: &StrokeState, cmt: Matrix, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], alpha: f32, cp: ColorParams, @@ -256,7 +256,7 @@ impl NativeDevice for &mut T { &mut self, img: &Image, cmt: Matrix, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], alpha: f32, cp: ColorParams, @@ -276,7 +276,7 @@ impl NativeDevice for &mut T { &mut self, area: Rect, luminosity: bool, - color_space: Colorspace, + color_space: &Colorspace, color: &[f32], cp: ColorParams, ) { @@ -290,7 +290,7 @@ impl NativeDevice for &mut T { fn begin_group( &mut self, area: Rect, - cs: Colorspace, + cs: &Colorspace, isolated: bool, knockout: bool, blendmode: BlendMode, @@ -418,22 +418,20 @@ unsafe extern "C" fn fill_path( color_params: fz_color_params, ) { with_rust_device::(dev, |dev| { - let cs = Colorspace::from_raw(color_space); + let cs = ManuallyDrop::new(Colorspace::from_raw(color_space)); let cs_n = cs.n() as usize; - let path = Path::from_raw(path.cast_mut()); + let path = ManuallyDrop::new(Path::from_raw(path.cast_mut())); dev.fill_path( &path, even_odd != 0, cmt.into(), - cs, + &cs, slice::from_raw_parts(color, cs_n), alpha, color_params.into(), ); - - mem::forget(path); }); } @@ -449,26 +447,23 @@ unsafe extern "C" fn stroke_path( color_params: fz_color_params, ) { with_rust_device::(dev, |dev| { - let cs = Colorspace::from_raw(color_space); + let cs = ManuallyDrop::new(Colorspace::from_raw(color_space)); let cs_n = cs.n() as usize; - let path = Path::from_raw(path.cast_mut()); - let stroke_state = StrokeState { + let path = ManuallyDrop::new(Path::from_raw(path.cast_mut())); + let stroke_state = ManuallyDrop::new(StrokeState { inner: stroke_state.cast_mut(), - }; + }); dev.stroke_path( &path, &stroke_state, cmt.into(), - cs, + &cs, slice::from_raw_parts(color, cs_n), alpha, color_params.into(), ); - - mem::forget(stroke_state); - mem::forget(path); }); } @@ -481,11 +476,9 @@ unsafe extern "C" fn clip_path( scissor: fz_rect, ) { with_rust_device::(dev, |dev| { - let path = Path::from_raw(path.cast_mut()); + let path = ManuallyDrop::new(Path::from_raw(path.cast_mut())); dev.clip_path(&path, even_odd != 0, cmt.into(), scissor.into()); - - mem::forget(path); }); } @@ -498,15 +491,12 @@ unsafe extern "C" fn clip_stroke_path( scissor: fz_rect, ) { with_rust_device::(dev, |dev| { - let path = Path::from_raw(path.cast_mut()); - let stroke_state = StrokeState { + let path = ManuallyDrop::new(Path::from_raw(path.cast_mut())); + let stroke_state = ManuallyDrop::new(StrokeState { inner: stroke_state.cast_mut(), - }; + }); dev.clip_stroke_path(&path, &stroke_state, cmt.into(), scissor.into()); - - mem::forget(stroke_state); - mem::forget(path); }); } @@ -521,22 +511,21 @@ unsafe extern "C" fn fill_text( color_params: fz_color_params, ) { with_rust_device::(dev, |dev| { - let text = Text { + let cs = ManuallyDrop::new(Colorspace::from_raw(color_space)); + let cs_n = cs.n() as usize; + + let text = ManuallyDrop::new(Text { inner: text.cast_mut(), - }; + }); - let cs = Colorspace::from_raw(color_space); - let cs_n = cs.n() as usize; dev.fill_text( &text, cmt.into(), - cs, + &cs, slice::from_raw_parts(color, cs_n), alpha, color_params.into(), ); - - mem::forget(text); }); } @@ -552,27 +541,25 @@ unsafe extern "C" fn stroke_text( color_params: fz_color_params, ) { with_rust_device::(dev, |dev| { - let text = Text { + let cs = ManuallyDrop::new(Colorspace::from_raw(color_space)); + let cs_n = cs.n() as usize; + + let text = ManuallyDrop::new(Text { inner: text.cast_mut(), - }; - let stroke_state = StrokeState { + }); + let stroke_state = ManuallyDrop::new(StrokeState { inner: stroke_state.cast_mut(), - }; + }); - let cs = Colorspace::from_raw(color_space); - let cs_n = cs.n() as usize; dev.stroke_text( &text, &stroke_state, cmt.into(), - cs, + &cs, slice::from_raw_parts(color, cs_n), alpha, color_params.into(), ); - - mem::forget(stroke_state); - mem::forget(text); }); } @@ -584,13 +571,11 @@ unsafe extern "C" fn clip_text( scissor: fz_rect, ) { with_rust_device::(dev, |dev| { - let text = Text { + let text = ManuallyDrop::new(Text { inner: text.cast_mut(), - }; + }); dev.clip_text(&text, cmt.into(), scissor.into()); - - mem::forget(text); }); } @@ -603,17 +588,14 @@ unsafe extern "C" fn clip_stroke_text( scissor: fz_rect, ) { with_rust_device::(dev, |dev| { - let text = Text { + let text = ManuallyDrop::new(Text { inner: text.cast_mut(), - }; - let stroke_state = StrokeState { + }); + let stroke_state = ManuallyDrop::new(StrokeState { inner: stroke_state.cast_mut(), - }; + }); dev.clip_stroke_text(&text, &stroke_state, cmt.into(), scissor.into()); - - mem::forget(stroke_state); - mem::forget(text); }); } @@ -624,13 +606,11 @@ unsafe extern "C" fn ignore_text( cmt: fz_matrix, ) { with_rust_device::(dev, |dev| { - let text = Text { + let text = ManuallyDrop::new(Text { inner: text.cast_mut(), - }; + }); dev.ignore_text(&text, cmt.into()); - - mem::forget(text); }); } @@ -643,11 +623,9 @@ unsafe extern "C" fn fill_shade( color_params: fz_color_params, ) { with_rust_device::(dev, |dev| { - let shade = Shade { inner: shd }; + let shade = ManuallyDrop::new(Shade { inner: shd }); dev.fill_shade(&shade, ctm.into(), alpha, color_params.into()); - - mem::forget(shade); }); } @@ -660,11 +638,9 @@ unsafe extern "C" fn fill_image( color_params: fz_color_params, ) { with_rust_device::(dev, |dev| { - let img = Image::from_raw(img); + let img = ManuallyDrop::new(Image::from_raw(img)); dev.fill_image(&img, ctm.into(), alpha, color_params.into()); - - mem::forget(img); }); } @@ -679,21 +655,19 @@ unsafe extern "C" fn fill_image_mask( color_params: fz_color_params, ) { with_rust_device::(dev, |dev| { - let cs = Colorspace::from_raw(color_space); + let cs = ManuallyDrop::new(Colorspace::from_raw(color_space)); let cs_n = cs.n() as usize; - let img = Image::from_raw(img); + let img = ManuallyDrop::new(Image::from_raw(img)); dev.fill_image_mask( &img, ctm.into(), - cs, + &cs, slice::from_raw_parts(color, cs_n), alpha, color_params.into(), ); - - mem::forget(img); }); } @@ -705,11 +679,9 @@ unsafe extern "C" fn clip_image_mask( scissor: fz_rect, ) { with_rust_device::(dev, |dev| { - let img = Image::from_raw(img); + let img = ManuallyDrop::new(Image::from_raw(img)); dev.clip_image_mask(&img, cmt.into(), scissor.into()); - - mem::forget(img); }); } @@ -729,13 +701,13 @@ unsafe extern "C" fn begin_mask( color_params: fz_color_params, ) { with_rust_device::(dev, |dev| { - let cs = Colorspace::from_raw(color_space); + let cs = ManuallyDrop::new(Colorspace::from_raw(color_space)); let cs_n = cs.n() as usize; dev.begin_mask( area.into(), luminosity != 0, - cs, + &cs, slice::from_raw_parts(color, cs_n), color_params.into(), ); @@ -748,11 +720,9 @@ unsafe extern "C" fn end_mask( f: *mut fz_function, ) { with_rust_device::(dev, |dev| { - let f = Function { inner: f }; + let f = ManuallyDrop::new(Function { inner: f }); dev.end_mask(&f); - - mem::forget(f); }); } @@ -767,13 +737,13 @@ unsafe extern "C" fn begin_group( alpha: f32, ) { with_rust_device::(dev, |dev| { - let cs = Colorspace::from_raw(color_space); + let cs = ManuallyDrop::new(Colorspace::from_raw(color_space)); let blendmode = BlendMode::try_from(blendmode as u32).unwrap(); dev.begin_group( area.into(), - cs, + &cs, isolated != 0, knockout != 0, blendmode, @@ -837,11 +807,9 @@ unsafe extern "C" fn set_default_colorspaces( dcs: *mut fz_default_colorspaces, ) { with_rust_device::(dev, |dev| { - let dcs = DefaultColorspaces { inner: dcs }; + let dcs = ManuallyDrop::new(DefaultColorspaces { inner: dcs }); dev.set_default_colorspaces(&dcs); - - mem::forget(dcs); }); } From fcf6753d66f0689d52ad13200fc27f685e061d5c Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Mon, 17 Feb 2025 12:08:05 +0100 Subject: [PATCH 07/11] Add structure and metatext --- src/device.rs | 95 ++++++++++++++++++++++++++++++++++++++++++++ src/device/native.rs | 73 +++++++++++++++++++++++++++++++++- 2 files changed, 167 insertions(+), 1 deletion(-) diff --git a/src/device.rs b/src/device.rs index e76d324..1a95a50 100644 --- a/src/device.rs +++ b/src/device.rs @@ -56,6 +56,101 @@ bitflags! { } } +#[derive(Debug, PartialEq, Eq, Clone, Copy, TryFromPrimitive)] +#[repr(i32)] +pub enum Structure { + Invalid = fz_structure_FZ_STRUCTURE_INVALID as _, + + /* Grouping elements (PDF 1.7 - Table 10.20) */ + Document = fz_structure_FZ_STRUCTURE_DOCUMENT as _, + Part = fz_structure_FZ_STRUCTURE_PART as _, + Art = fz_structure_FZ_STRUCTURE_ART as _, + Sect = fz_structure_FZ_STRUCTURE_SECT as _, + Div = fz_structure_FZ_STRUCTURE_DIV as _, + BlockQuote = fz_structure_FZ_STRUCTURE_BLOCKQUOTE as _, + Caption = fz_structure_FZ_STRUCTURE_CAPTION as _, + TOC = fz_structure_FZ_STRUCTURE_TOC as _, + TOCI = fz_structure_FZ_STRUCTURE_TOCI as _, + Index = fz_structure_FZ_STRUCTURE_INDEX as _, + NonStruct = fz_structure_FZ_STRUCTURE_NONSTRUCT as _, + Private = fz_structure_FZ_STRUCTURE_PRIVATE as _, + /* Grouping elements (PDF 2.0 - Table 364) */ + DocumentFragment = fz_structure_FZ_STRUCTURE_DOCUMENTFRAGMENT as _, + /* Grouping elements (PDF 2.0 - Table 365) */ + Aside = fz_structure_FZ_STRUCTURE_ASIDE as _, + /* Grouping elements (PDF 2.0 - Table 366) */ + Title = fz_structure_FZ_STRUCTURE_TITLE as _, + FENote = fz_structure_FZ_STRUCTURE_FENOTE as _, + /* Grouping elements (PDF 2.0 - Table 367) */ + Sub = fz_structure_FZ_STRUCTURE_SUB as _, + + /* Paragraphlike elements (PDF 1.7 - Table 10.21) */ + P = fz_structure_FZ_STRUCTURE_P as _, + H = fz_structure_FZ_STRUCTURE_H as _, + H1 = fz_structure_FZ_STRUCTURE_H1 as _, + H2 = fz_structure_FZ_STRUCTURE_H2 as _, + H3 = fz_structure_FZ_STRUCTURE_H3 as _, + H4 = fz_structure_FZ_STRUCTURE_H4 as _, + H5 = fz_structure_FZ_STRUCTURE_H5 as _, + H6 = fz_structure_FZ_STRUCTURE_H6 as _, + + /* List elements (PDF 1.7 - Table 10.23) */ + List = fz_structure_FZ_STRUCTURE_LIST as _, + ListItem = fz_structure_FZ_STRUCTURE_LISTITEM as _, + Label = fz_structure_FZ_STRUCTURE_LABEL as _, + ListBody = fz_structure_FZ_STRUCTURE_LISTBODY as _, + + /* Table elements (PDF 1.7 - Table 10.24) */ + Table = fz_structure_FZ_STRUCTURE_TABLE as _, + TR = fz_structure_FZ_STRUCTURE_TR as _, + TH = fz_structure_FZ_STRUCTURE_TH as _, + TD = fz_structure_FZ_STRUCTURE_TD as _, + THead = fz_structure_FZ_STRUCTURE_THEAD as _, + TBody = fz_structure_FZ_STRUCTURE_TBODY as _, + TFoot = fz_structure_FZ_STRUCTURE_TFOOT as _, + + /* Inline elements (PDF 1.7 - Table 10.25) */ + Span = fz_structure_FZ_STRUCTURE_SPAN as _, + Quote = fz_structure_FZ_STRUCTURE_QUOTE as _, + Note = fz_structure_FZ_STRUCTURE_NOTE as _, + Reference = fz_structure_FZ_STRUCTURE_REFERENCE as _, + Bibentry = fz_structure_FZ_STRUCTURE_BIBENTRY as _, + Code = fz_structure_FZ_STRUCTURE_CODE as _, + Link = fz_structure_FZ_STRUCTURE_LINK as _, + Annot = fz_structure_FZ_STRUCTURE_ANNOT as _, + /* Inline elements (PDF 2.0 - Table 368) */ + Em = fz_structure_FZ_STRUCTURE_EM as _, + Strong = fz_structure_FZ_STRUCTURE_STRONG as _, + + /* Ruby inline element (PDF 1.7 - Table 10.26) */ + Ruby = fz_structure_FZ_STRUCTURE_RUBY as _, + RB = fz_structure_FZ_STRUCTURE_RB as _, + RT = fz_structure_FZ_STRUCTURE_RT as _, + RP = fz_structure_FZ_STRUCTURE_RP as _, + + /* Warichu inline element (PDF 1.7 - Table 10.26) */ + Warichu = fz_structure_FZ_STRUCTURE_WARICHU as _, + WT = fz_structure_FZ_STRUCTURE_WT as _, + WP = fz_structure_FZ_STRUCTURE_WP as _, + + /* Illustration elements (PDF 1.7 - Table 10.27) */ + Figure = fz_structure_FZ_STRUCTURE_FIGURE as _, + Formula = fz_structure_FZ_STRUCTURE_FORMULA as _, + Form = fz_structure_FZ_STRUCTURE_FORM as _, + + /* Artifact structure type (PDF 2.0 - Table 375) */ + Artifact = fz_structure_FZ_STRUCTURE_ARTIFACT as _, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, TryFromPrimitive)] +#[repr(u32)] +pub enum Metatext { + ActualText = fz_metatext_FZ_METATEXT_ACTUALTEXT as _, + Alt = fz_metatext_FZ_METATEXT_ALT as _, + Abbreviation = fz_metatext_FZ_METATEXT_ABBREVIATION as _, + Title = fz_metatext_FZ_METATEXT_TITLE as _, +} + pub struct DefaultColorspaces { pub(crate) inner: *mut fz_default_colorspaces, } diff --git a/src/device/native.rs b/src/device/native.rs index 15385d5..39da22b 100644 --- a/src/device/native.rs +++ b/src/device/native.rs @@ -12,7 +12,7 @@ use crate::{ Shade, StrokeState, Text, }; -use super::{DefaultColorspaces, DeviceFlag}; +use super::{DefaultColorspaces, DeviceFlag, Metatext, Structure}; #[allow(unused_variables, clippy::too_many_arguments)] pub trait NativeDevice { @@ -154,6 +154,14 @@ pub trait NativeDevice { fn begin_layer(&mut self, name: &str) {} fn end_layer(&mut self) {} + + fn begin_structure(&mut self, standard: Structure, raw: &str, idx: i32) {} + + fn end_structure(&mut self) {} + + fn begin_metatext(&mut self, meta: Metatext, text: &str) {} + + fn end_metatext(&mut self) {} } impl NativeDevice for &mut T { @@ -334,6 +342,22 @@ impl NativeDevice for &mut T { fn end_layer(&mut self) { (**self).end_layer(); } + + fn begin_structure(&mut self, standard: Structure, raw: &str, idx: i32) { + (**self).begin_structure(standard, raw, idx); + } + + fn end_structure(&mut self) { + (**self).end_structure(); + } + + fn begin_metatext(&mut self, meta: Metatext, text: &str) { + (**self).begin_metatext(meta, text); + } + + fn end_metatext(&mut self) { + (**self).end_metatext(); + } } pub(crate) fn create(device: D) -> Device { @@ -376,6 +400,12 @@ pub(crate) fn create(device: D) -> Device { (*c_device).base.begin_layer = Some(begin_layer::); (*c_device).base.end_layer = Some(end_layer::); + (*c_device).base.begin_structure = Some(begin_structure::); + (*c_device).base.end_structure = Some(end_structure::); + + (*c_device).base.begin_metatext = Some(begin_metatext::); + (*c_device).base.end_metatext = Some(end_metatext::); + Device::from_raw(c_device.cast(), ptr::null_mut()) } } @@ -830,3 +860,44 @@ unsafe extern "C" fn end_layer(_ctx: *mut fz_context, dev: *mut dev.end_layer(); }); } + +unsafe extern "C" fn begin_structure( + _ctx: *mut fz_context, + dev: *mut fz_device, + standard: fz_structure, + raw: *const c_char, + idx: c_int, +) { + with_rust_device::(dev, |dev| { + let standard = Structure::try_from(standard as i32).unwrap(); + let raw = unsafe { CStr::from_ptr(raw) }.to_str().unwrap(); + + dev.begin_structure(standard, raw, idx as i32); + }); +} + +unsafe extern "C" fn end_structure(_ctx: *mut fz_context, dev: *mut fz_device) { + with_rust_device::(dev, |dev| { + dev.end_structure(); + }); +} + +unsafe extern "C" fn begin_metatext( + _ctx: *mut fz_context, + dev: *mut fz_device, + meta: fz_metatext, + text: *const c_char, +) { + with_rust_device::(dev, |dev| { + let meta = Metatext::try_from(meta as u32).unwrap(); + let text = unsafe { CStr::from_ptr(text) }.to_str().unwrap(); + + dev.begin_metatext(meta, text); + }); +} + +unsafe extern "C" fn end_metatext(_ctx: *mut fz_context, dev: *mut fz_device) { + with_rust_device::(dev, |dev| { + dev.end_metatext(); + }); +} From 452b97e9a3b343343d453ab1f233f754ba0f1493 Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Mon, 17 Feb 2025 13:15:55 +0100 Subject: [PATCH 08/11] Start implementing tests --- mupdf-sys/wrapper.c | 48 ++++++++++++++++++++++++ src/device.rs | 43 ++++++++++++++++++++- src/device/native.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+), 1 deletion(-) diff --git a/mupdf-sys/wrapper.c b/mupdf-sys/wrapper.c index 8b1b3b8..952f63d 100644 --- a/mupdf-sys/wrapper.c +++ b/mupdf-sys/wrapper.c @@ -2863,6 +2863,54 @@ void mupdf_end_layer(fz_context *ctx, fz_device *device, mupdf_error_t **errptr) } } +void mupdf_begin_structure(fz_context *ctx, fz_device *device, fz_structure standard, const char *raw, int idx, mupdf_error_t **errptr) +{ + fz_try(ctx) + { + fz_begin_structure(ctx, device, standard, raw, idx); + } + fz_catch(ctx) + { + mupdf_save_error(ctx, errptr); + } +} + +void mupdf_end_structure(fz_context *ctx, fz_device *device, mupdf_error_t **errptr) +{ + fz_try(ctx) + { + fz_end_structure(ctx, device); + } + fz_catch(ctx) + { + mupdf_save_error(ctx, errptr); + } +} + +void mupdf_begin_metatext(fz_context *ctx, fz_device *device, fz_metatext meta, const char *text, mupdf_error_t **errptr) +{ + fz_try(ctx) + { + fz_begin_metatext(ctx, device, meta, text); + } + fz_catch(ctx) + { + mupdf_save_error(ctx, errptr); + } +} + +void mupdf_end_metatext(fz_context *ctx, fz_device *device, mupdf_error_t **errptr) +{ + fz_try(ctx) + { + fz_end_metatext(ctx, device); + } + fz_catch(ctx) + { + mupdf_save_error(ctx, errptr); + } +} + void mupdf_begin_mask(fz_context *ctx, fz_device *device, fz_rect area, bool luminosity, fz_colorspace *cs, const float *color, fz_color_params cp, mupdf_error_t **errptr) { fz_try(ctx) diff --git a/src/device.rs b/src/device.rs index 1a95a50..488dee7 100644 --- a/src/device.rs +++ b/src/device.rs @@ -114,7 +114,7 @@ pub enum Structure { Quote = fz_structure_FZ_STRUCTURE_QUOTE as _, Note = fz_structure_FZ_STRUCTURE_NOTE as _, Reference = fz_structure_FZ_STRUCTURE_REFERENCE as _, - Bibentry = fz_structure_FZ_STRUCTURE_BIBENTRY as _, + BibEntry = fz_structure_FZ_STRUCTURE_BIBENTRY as _, Code = fz_structure_FZ_STRUCTURE_CODE as _, Link = fz_structure_FZ_STRUCTURE_LINK as _, Annot = fz_structure_FZ_STRUCTURE_ANNOT as _, @@ -583,6 +583,47 @@ impl Device { } Ok(()) } + + pub fn begin_structure(&self, standard: Structure, raw: &str, idx: i32) -> Result<(), Error> { + let c_raw = CString::new(raw)?; + unsafe { + ffi_try!(mupdf_begin_structure( + context(), + self.dev, + standard as _, + c_raw.as_ptr(), + idx as _ + )); + } + Ok(()) + } + + pub fn end_structure(&self) -> Result<(), Error> { + unsafe { + ffi_try!(mupdf_end_structure(context(), self.dev)); + } + Ok(()) + } + + pub fn begin_metatext(&self, meta: Metatext, text: &str) -> Result<(), Error> { + let c_text = CString::new(text)?; + unsafe { + ffi_try!(mupdf_begin_metatext( + context(), + self.dev, + meta as _, + c_text.as_ptr() + )); + } + Ok(()) + } + + pub fn end_metatext(&self) -> Result<(), Error> { + unsafe { + ffi_try!(mupdf_end_metatext(context(), self.dev)); + } + Ok(()) + } } impl Drop for Device { diff --git a/src/device/native.rs b/src/device/native.rs index 39da22b..fd2829d 100644 --- a/src/device/native.rs +++ b/src/device/native.rs @@ -901,3 +901,92 @@ unsafe extern "C" fn end_metatext(_ctx: *mut fz_context, dev: * dev.end_metatext(); }); } + +#[cfg(test)] +mod test { + use std::rc::Rc; + + use crate::{ + device::{Metatext, Structure}, + Device, NativeDevice, + }; + + #[test] + fn native_device() { + #[derive(Default)] + struct Test { + begin_layer: u8, + end_layer: u8, + begin_structure: u8, + end_structure: u8, + begin_metatext: u8, + end_metatext: u8, + } + + impl NativeDevice for Test { + fn begin_layer(&mut self, name: &str) { + assert_eq!(name, "begin layer name"); + self.begin_layer += 1; + } + + fn end_layer(&mut self) { + self.end_layer += 1; + } + + fn begin_structure(&mut self, standard: Structure, raw: &str, idx: i32) { + assert_eq!(standard, Structure::Div); + assert_eq!(raw, "div"); + assert_eq!(idx, 5); + self.begin_structure += 1; + } + + fn end_structure(&mut self) { + self.end_structure += 1; + } + + fn begin_metatext(&mut self, meta: Metatext, text: &str) { + assert_eq!(meta, Metatext::Title); + assert_eq!(text, "some text"); + self.begin_metatext += 1; + } + + fn end_metatext(&mut self) { + self.end_metatext += 1; + } + } + + let mut ndev = Test::default(); + let dev = Device::from_native(&mut ndev); + + dev.begin_layer("begin layer name").unwrap(); + assert_eq!(ndev.begin_layer, 1); + dev.end_layer().unwrap(); + assert_eq!(ndev.end_layer, 1); + + dev.begin_structure(Structure::Div, "div", 5).unwrap(); + assert_eq!(ndev.begin_structure, 1); + dev.end_structure().unwrap(); + assert_eq!(ndev.end_structure, 1); + + dev.begin_metatext(Metatext::Title, "some text").unwrap(); + assert_eq!(ndev.begin_metatext, 1); + dev.end_metatext().unwrap(); + assert_eq!(ndev.end_metatext, 1); + } + + #[test] + fn native_device_drop() { + struct DropDevice(#[allow(dead_code)] Rc<()>); + + impl NativeDevice for DropDevice {} + + let proof = Rc::new(()); + let detector = proof.clone(); + assert_eq!(Rc::strong_count(&proof), 2, "setup failed"); + + let dev = Device::from_native(DropDevice(detector)); + assert_eq!(Rc::strong_count(&proof), 2, "dropped too early"); + drop(dev); + assert_eq!(Rc::strong_count(&proof), 1, "not dropped"); + } +} From 6dfc28dd3be2b5b092a58c07849fb0421a3159ab Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Tue, 18 Feb 2025 21:34:02 +0100 Subject: [PATCH 09/11] Add safety comment and error handling --- mupdf-sys/src/lib.rs | 56 ++++++++++++++++++++++++++++++++++++++++---- mupdf-sys/wrapper.c | 14 +++++++++++ src/device.rs | 2 +- src/device/native.rs | 18 +++++++------- 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/mupdf-sys/src/lib.rs b/mupdf-sys/src/lib.rs index e794ec3..7704dc7 100644 --- a/mupdf-sys/src/lib.rs +++ b/mupdf-sys/src/lib.rs @@ -5,12 +5,60 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +use core::{ + ffi::{c_int, CStr}, + ptr, +}; + +/// This function allocates a new device and returns a pointer to it if no error occured. For the +/// required structure of `T` check the example below. +/// +/// The pointer `errptr` points to is expected to be null at the time this function is called. +/// If an error occurs this inner pointer will be set to the error and null returned from this +/// function. Was this inner pointer non-null before this function was called a new device will be +/// allocated but the function will assume that an error has occured and will return null. This device +/// will not be freed. It will, however, not cause unsafe behavior either. +/// +/// # Safety +/// +/// The caller must ensure `ctx` and `errptr` to be a valid pointers. +/// +/// It must also ensure `T` to be a type that starts with `fz_device`. Memory will be allocated for +/// a new instance of `T`, but only the `fz_device` portion will be initialized. The rest is +/// currently being zero-initialized, but this might change in the future. +/// +/// # Example +/// +/// This is how a compliant `T` might look like. The `repr(C)` is necessary as `repr(Rust)` does +/// not guarantee stable field orderings. +/// +/// ```rust +/// use mupdf_sys::fz_device; +/// +/// #[repr(C)] +/// struct MyDevice { +/// base: fz_device, +/// foo: u32, +/// } +/// ``` pub unsafe fn mupdf_new_derived_device( ctx: *mut fz_context, - label: *const std::ffi::c_char, + label: &'static CStr, + errptr: *mut *mut mupdf_error_t, ) -> *mut T { - let size = std::ffi::c_int::try_from(size_of::()).unwrap(); - let device = fz_new_device_of_size(ctx, size); - let label = Memento_label(device.cast(), label); + let SIZE: c_int = const { + if (c_int::MAX as usize) < size_of::() { + panic!("device too big") + } else { + size_of::() as c_int + } + }; + + let device = mupdf_new_device_of_size(ctx, SIZE, errptr); + if !(*errptr).is_null() { + return ptr::null_mut(); + } + + let label = Memento_label(device.cast(), label.as_ptr()); label.cast() } diff --git a/mupdf-sys/wrapper.c b/mupdf-sys/wrapper.c index 952f63d..ab70349 100644 --- a/mupdf-sys/wrapper.c +++ b/mupdf-sys/wrapper.c @@ -2640,6 +2640,20 @@ fz_device *mupdf_new_draw_device(fz_context *ctx, fz_pixmap *pixmap, fz_irect cl return device; } +fz_device *mupdf_new_device_of_size(fz_context *ctx, int size, mupdf_error_t **errptr) +{ + fz_device *device = NULL; + fz_try(ctx) + { + device = fz_new_device_of_size(ctx, size); + } + fz_catch(ctx) + { + mupdf_save_error(ctx, errptr); + } + return device; +} + fz_device *mupdf_new_display_list_device(fz_context *ctx, fz_display_list *list, mupdf_error_t **errptr) { fz_device *device = NULL; diff --git a/src/device.rs b/src/device.rs index 488dee7..eeaa2e2 100644 --- a/src/device.rs +++ b/src/device.rs @@ -182,7 +182,7 @@ impl Device { Self { dev, list } } - pub fn from_native(device: D) -> Self { + pub fn from_native(device: D) -> Result { native::create(device) } diff --git a/src/device/native.rs b/src/device/native.rs index fd2829d..7780b96 100644 --- a/src/device/native.rs +++ b/src/device/native.rs @@ -8,8 +8,8 @@ use std::{ use mupdf_sys::*; use crate::{ - context, BlendMode, ColorParams, Colorspace, Device, Function, Image, Matrix, Path, Rect, - Shade, StrokeState, Text, + context, BlendMode, ColorParams, Colorspace, Device, Error, Function, Image, Matrix, Path, + Rect, Shade, StrokeState, Text, }; use super::{DefaultColorspaces, DeviceFlag, Metatext, Structure}; @@ -360,9 +360,10 @@ impl NativeDevice for &mut T { } } -pub(crate) fn create(device: D) -> Device { - unsafe { - let c_device = mupdf_new_derived_device::>(context(), c"RustDevice".as_ptr()); +pub(crate) fn create(device: D) -> Result { + let ret = unsafe { + let c_device: *mut CDevice = + ffi_try!(mupdf_new_derived_device(context(), c"RustDevice")); ptr::write(&raw mut (*c_device).rust_device, device); (*c_device).base.close_device = Some(close_device::); @@ -407,7 +408,8 @@ pub(crate) fn create(device: D) -> Device { (*c_device).base.end_metatext = Some(end_metatext::); Device::from_raw(c_device.cast(), ptr::null_mut()) - } + }; + Ok(ret) } #[repr(C)] @@ -956,7 +958,7 @@ mod test { } let mut ndev = Test::default(); - let dev = Device::from_native(&mut ndev); + let dev = Device::from_native(&mut ndev).unwrap(); dev.begin_layer("begin layer name").unwrap(); assert_eq!(ndev.begin_layer, 1); @@ -984,7 +986,7 @@ mod test { let detector = proof.clone(); assert_eq!(Rc::strong_count(&proof), 2, "setup failed"); - let dev = Device::from_native(DropDevice(detector)); + let dev = Device::from_native(DropDevice(detector)).unwrap(); assert_eq!(Rc::strong_count(&proof), 2, "dropped too early"); drop(dev); assert_eq!(Rc::strong_count(&proof), 1, "not dropped"); From d619c6b22f4347cb5e018175e4f53c2a353fa44b Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Tue, 18 Feb 2025 21:47:20 +0100 Subject: [PATCH 10/11] Simplify function requirements --- mupdf-sys/src/lib.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/mupdf-sys/src/lib.rs b/mupdf-sys/src/lib.rs index 7704dc7..285817d 100644 --- a/mupdf-sys/src/lib.rs +++ b/mupdf-sys/src/lib.rs @@ -11,13 +11,8 @@ use core::{ }; /// This function allocates a new device and returns a pointer to it if no error occured. For the -/// required structure of `T` check the example below. -/// -/// The pointer `errptr` points to is expected to be null at the time this function is called. -/// If an error occurs this inner pointer will be set to the error and null returned from this -/// function. Was this inner pointer non-null before this function was called a new device will be -/// allocated but the function will assume that an error has occured and will return null. This device -/// will not be freed. It will, however, not cause unsafe behavior either. +/// required structure of `T` check the example below. If an error occurs the pointer `errptr` points +/// to will be set to to a pointer pointing to the error and null returned from this function. /// /// # Safety /// @@ -55,10 +50,6 @@ pub unsafe fn mupdf_new_derived_device( }; let device = mupdf_new_device_of_size(ctx, SIZE, errptr); - if !(*errptr).is_null() { - return ptr::null_mut(); - } - let label = Memento_label(device.cast(), label.as_ptr()); label.cast() } From b20aee568bda8760a190011640a029b49d68fdc8 Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Tue, 18 Feb 2025 22:01:13 +0100 Subject: [PATCH 11/11] Remove unused import --- mupdf-sys/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mupdf-sys/src/lib.rs b/mupdf-sys/src/lib.rs index 285817d..d64e578 100644 --- a/mupdf-sys/src/lib.rs +++ b/mupdf-sys/src/lib.rs @@ -5,10 +5,7 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); -use core::{ - ffi::{c_int, CStr}, - ptr, -}; +use core::ffi::{c_int, CStr}; /// This function allocates a new device and returns a pointer to it if no error occured. For the /// required structure of `T` check the example below. If an error occurs the pointer `errptr` points