extracts pdf items using mupdf-sys directly
Some checks failed
/ test (push) Failing after 24s

This commit is contained in:
Jacob Lifshay 2026-01-06 08:00:38 -08:00
parent a677cd8a33
commit 040afcc435
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
2 changed files with 373 additions and 274 deletions

View file

@ -1,9 +1,14 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::quad_tree::QuadTree;
use crate::{
mupdf_ffi::{
WriteMode, add_points, point_max_components, point_min_components, transform_vector,
},
quad_tree::QuadTree,
};
use indexmap::IndexSet;
use mupdf_sys::{FZ_STEXT_BOLD, fz_matrix};
use mupdf_sys::{fz_matrix, fz_point, fz_text_item};
use non_nan_float::NonNaNF32;
use std::{
backtrace::Backtrace,
@ -135,6 +140,12 @@ macro_rules! make_enum_font {
panic!("invalid font name with id")
}
}
const fn known_from_name_with_tag(font_name_with_tag: &str, size: NonNaNF32) -> Option<Self> {
match size.get() {
$($($known_font_size if str_eq(font_name_with_tag, $known_font_name_with_tag) => Some(Self::$KnownFont),)*)*
_ => None,
}
}
const fn new_known(font_name: &str, size: NonNaNF32) -> Option<Self> {
match size.get() {
$($($known_font_size if str_eq(font_name, const {
@ -3167,25 +3178,32 @@ impl<'ctx> Parser<'ctx> {
}
}
#[derive(Clone, Debug)]
struct MyDevice {
#[derive(Debug)]
struct MyDevice<'a> {
page_num: u32,
qt: Rc<RefCell<BTreeMap<TextSection, QuadTree<PageItem>>>>,
qt: RefCell<BTreeMap<TextSection, QuadTree<PageItem>>>,
unprocessed_chars:
Rc<RefCell<BTreeMap<TextSection, Rc<RefCell<BTreeMap<Font, IndexSet<Char>>>>>>>,
unprocessed_non_text: Rc<RefCell<IndexSet<LineOrRect>>>,
first_seen_fonts: RefCell<&'a mut BTreeMap<String, BTreeSet<NonNaNF32>>>,
error: RefCell<Result<(), Box<dyn Error>>>,
}
impl MyDevice {
fn new(page_num: u32) -> Self {
impl<'a> MyDevice<'a> {
fn new(page_num: u32, first_seen_fonts: &'a mut BTreeMap<String, BTreeSet<NonNaNF32>>) -> Self {
Self {
page_num,
qt: Default::default(),
unprocessed_chars: Default::default(),
unprocessed_non_text: Default::default(),
first_seen_fonts: RefCell::new(first_seen_fonts),
error: RefCell::new(Ok(())),
}
}
fn path(&self, path: &mupdf_ffi::Path<'_>, cmt: fz_matrix) {
fn path(&self, path: &mupdf_ffi::Path<'_>, ctm: fz_matrix) {
if self.error.borrow().is_err() {
return;
}
enum Walker {
Empty,
Moved { x: f32, y: f32 },
@ -3262,19 +3280,19 @@ impl MyDevice {
p1_y,
}) => {
let mupdf_sys::fz_point { x: p0_x, y: p0_y } =
mupdf_ffi::transform_point_xy(p0_x.get(), p0_y.get(), cmt);
mupdf_ffi::transform_point_xy(p0_x.get(), p0_y.get(), ctm);
let mupdf_sys::fz_point { x: p1_x, y: p1_y } =
mupdf_ffi::transform_point_xy(p1_x.get(), p1_y.get(), cmt);
mupdf_ffi::transform_point_xy(p1_x.get(), p1_y.get(), ctm);
let Some(line) = new_line(p0_x, p0_y, p1_x, p1_y) else {
return;
};
LineOrRect::Line(line)
}
Walker::Rect { x1, y1, x2, y2 } => {
let p1 = mupdf_ffi::transform_point_xy(x1, y1, cmt);
let p2 = mupdf_ffi::transform_point_xy(x2, y1, cmt);
let p3 = mupdf_ffi::transform_point_xy(x2, y2, cmt);
let p4 = mupdf_ffi::transform_point_xy(x1, y2, cmt);
let p1 = mupdf_ffi::transform_point_xy(x1, y1, ctm);
let p2 = mupdf_ffi::transform_point_xy(x2, y1, ctm);
let p3 = mupdf_ffi::transform_point_xy(x2, y2, ctm);
let p4 = mupdf_ffi::transform_point_xy(x1, y2, ctm);
let min_x = NonNaNF32::new(p1.x.min(p2.x).min(p3.x).min(p4.x));
let max_x = NonNaNF32::new(p1.x.max(p2.x).max(p3.x).max(p4.x));
let min_y = NonNaNF32::new(p1.y.min(p2.y).min(p3.y).min(p4.y));
@ -3317,65 +3335,192 @@ impl MyDevice {
);
}
}
fn text(&self, text: &mupdf_ffi::Text<'_>, cmt: fz_matrix) {
fn text(&self, text: &mupdf_ffi::Text<'_>, ctm: fz_matrix) {
if self.error.borrow().is_err() {
return;
}
let mut first_seen_fonts = self.first_seen_fonts.borrow_mut();
for span in text.spans() {
let mupdf_sys::fz_text_span { trm, .. } = span.get();
let mupdf_sys::fz_font {
refs,
name,
buffer,
flags,
ft_face,
shaper_data,
t3matrix,
t3resources,
t3procs,
t3lists,
t3widths,
t3flags,
t3doc,
t3run,
t3freeres,
bbox,
ascender,
descender,
glyph_count,
bbox_table,
use_glyph_bbox,
width_count,
width_default,
width_table,
advance_cache,
encoding_cache,
has_digest,
digest,
subfont,
} = *span.font().get();
for item in span.items() {
todo!()
let tm = span.trm();
const ROUND_FACTOR: f32 = 1000.0;
let font_size = (mupdf_ffi::matrix_expansion(tm) * ROUND_FACTOR).round() / ROUND_FACTOR;
let Some(font_size) = NonNaNF32::new(font_size) else {
continue;
};
let font_name_with_tag = span.font().name();
let font_name_with_tag = match font_name_with_tag {
"CGMSHV+DejaVuSansCondensed-Obli" => "CGMSHV+DejaVuSansCondensed-Oblique",
"YDJYQV+DejaVuSansCondensed-Bold" => "YDJYQV+DejaVuSansCondensed-BoldOblique",
"NHUPPK+DejaVuSansCondensed-Bold" => "NHUPPK+DejaVuSansCondensed-Bold",
_ if font_name_with_tag.len() == 31 => {
let _ = self.error.replace(Err(format!(
"probably truncated font name: {font_name_with_tag:?}"
)
.into()));
return;
}
_ => font_name_with_tag,
};
for &fz_text_item {
x,
y,
adv,
gid,
ucs,
cid: _,
} in span.items()
{
let adv = if gid >= 0 { adv } else { 0.0 };
let tm = fz_matrix { e: x, f: y, ..tm };
let trm = mupdf_ffi::concat(tm, ctm);
let dir = match span.write_mode() {
WriteMode::Horizontal => fz_point { x: 1.0, y: 0.0 },
WriteMode::Vertical => fz_point { x: 0.0, y: -1.0 },
};
let dir = mupdf_ffi::transform_vector(dir, trm);
let glyph_start;
let glyph_stop;
let glyph_ascender;
let glyph_descender;
match span.write_mode() {
WriteMode::Horizontal => {
glyph_start = fz_point { x: trm.e, y: trm.f };
glyph_stop = fz_point {
x: trm.e + adv * dir.x,
y: trm.f + adv * dir.y,
};
glyph_ascender = fz_point {
x: 0.0,
y: span.font().ascender(),
};
glyph_descender = fz_point {
x: 0.0,
y: span.font().descender(),
};
}
WriteMode::Vertical => {
glyph_start = fz_point {
x: trm.e - adv * dir.x,
y: trm.f - adv * dir.y,
};
glyph_stop = fz_point { x: trm.e, y: trm.f };
glyph_ascender = fz_point { x: 1.0, y: 0.0 };
glyph_descender = fz_point { x: 0.0, y: 0.0 };
}
};
let glyph_ascender = transform_vector(glyph_ascender, trm);
let glyph_descender = transform_vector(glyph_descender, trm);
let points = [
add_points(glyph_start, glyph_descender),
add_points(glyph_start, glyph_ascender),
add_points(glyph_stop, glyph_descender),
add_points(glyph_stop, glyph_ascender),
];
let min = point_min_components(
point_min_components(point_min_components(points[0], points[1]), points[2]),
points[3],
);
let max = point_max_components(
point_max_components(point_max_components(points[0], points[1]), points[2]),
points[3],
);
let Some(ch) = u32::try_from(ucs).ok().and_then(|v| char::try_from(v).ok()) else {
continue;
};
let text = String::from(ch);
if text.trim().is_empty() {
continue;
}
let font = Font::known_from_name_with_tag(font_name_with_tag, font_size)
.unwrap_or_else(|| Font::Other {
font_name: font_name_with_tag.into(),
size: font_size,
});
let Some(text_section) = TextSection::for_position(
self.page_num,
(min.x + max.x) * 0.5,
(min.y + max.y) * 0.5,
) else {
if PAGE_BODY_MIN_Y <= min.y && min.y <= PAGE_BODY_MAX_Y {
if self.page_num != 1072 {
// page 1072 has characters in the margins
let _ = self.error.replace(Err(format!(
"char not in text section: {text:?}\npage_num={}",
self.page_num,
)
.into()));
return;
}
}
continue;
};
let (Some(min_x), Some(min_y), Some(max_x), Some(max_y)) = (
NonNaNF32::new(min.x),
NonNaNF32::new(min.y),
NonNaNF32::new(max.x),
NonNaNF32::new(max.y),
) else {
let _ = self
.error
.replace(Err("char position shouldn't be NaN".into()));
return;
};
let char = Char {
font,
text,
min_x,
min_y,
max_x,
max_y,
};
let set = match first_seen_fonts.get_mut(font_name_with_tag) {
Some(v) => v,
None => first_seen_fonts
.entry(String::from(font_name_with_tag))
.or_default(),
};
if set.insert(font_size) {
println!(
"first seen font: {font_name_with_tag:?} {font_size}: page {} {char:?}",
self.page_num,
);
}
self.qt
.borrow_mut()
.entry(text_section)
.or_default()
.insert(min_x.get(), min_y.get(), PageItem::Char(char.clone()));
self.unprocessed_chars
.borrow_mut()
.entry(text_section)
.or_default()
.borrow_mut()
.entry(char.font.clone())
.or_default()
.insert(char);
}
}
}
}
impl<'ctx> mupdf_ffi::DeviceCallbacks<'ctx> for MyDevice {
impl<'ctx> mupdf_ffi::DeviceCallbacks<'ctx> for MyDevice<'_> {
fn fill_path(
&self,
_ctx: mupdf_ffi::ContextRef<'ctx>,
path: &mupdf_ffi::Path<'ctx>,
_even_odd: bool,
cmt: fz_matrix,
ctm: fz_matrix,
) {
self.path(path, cmt);
self.path(path, ctm);
}
fn stroke_path(
&self,
_ctx: mupdf_ffi::ContextRef<'ctx>,
path: &mupdf_ffi::Path<'ctx>,
cmt: fz_matrix,
ctm: fz_matrix,
) {
self.path(path, cmt);
self.path(path, ctm);
}
fn clip_path(
@ -3383,67 +3528,67 @@ impl<'ctx> mupdf_ffi::DeviceCallbacks<'ctx> for MyDevice {
_ctx: mupdf_ffi::ContextRef<'ctx>,
path: &mupdf_ffi::Path<'ctx>,
_even_odd: bool,
cmt: fz_matrix,
ctm: fz_matrix,
_scissor: mupdf_sys::fz_rect,
) {
self.path(path, cmt);
self.path(path, ctm);
}
fn clip_stroke_path(
&self,
_ctx: mupdf_ffi::ContextRef<'ctx>,
path: &mupdf_ffi::Path<'ctx>,
cmt: fz_matrix,
ctm: fz_matrix,
_scissor: mupdf_sys::fz_rect,
) {
self.path(path, cmt);
self.path(path, ctm);
}
fn fill_text(
&self,
_ctx: mupdf_ffi::ContextRef<'ctx>,
text: &mupdf_ffi::Text<'ctx>,
cmt: fz_matrix,
ctm: fz_matrix,
) {
self.text(text, cmt);
self.text(text, ctm);
}
fn stroke_text(
&self,
_ctx: mupdf_ffi::ContextRef<'ctx>,
text: &mupdf_ffi::Text<'ctx>,
cmt: fz_matrix,
ctm: fz_matrix,
) {
self.text(text, cmt);
self.text(text, ctm);
}
fn clip_text(
&self,
_ctx: mupdf_ffi::ContextRef<'ctx>,
text: &mupdf_ffi::Text<'ctx>,
cmt: fz_matrix,
ctm: fz_matrix,
_scissor: mupdf_sys::fz_rect,
) {
self.text(text, cmt);
self.text(text, ctm);
}
fn clip_stroke_text(
&self,
_ctx: mupdf_ffi::ContextRef<'ctx>,
text: &mupdf_ffi::Text<'ctx>,
cmt: fz_matrix,
ctm: fz_matrix,
_scissor: mupdf_sys::fz_rect,
) {
self.text(text, cmt);
self.text(text, ctm);
}
fn ignore_text(
&self,
_ctx: mupdf_ffi::ContextRef<'ctx>,
text: &mupdf_ffi::Text<'ctx>,
cmt: fz_matrix,
ctm: fz_matrix,
) {
self.text(text, cmt);
self.text(text, ctm);
}
}
@ -3521,9 +3666,12 @@ impl 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);
let device = mupdf_ffi::Device::new(
page.ctx(),
Box::new(MyDevice::new(page_num, first_seen_fonts)),
)?;
page.run(
&mupdf_ffi::Device::new(page.ctx(), Box::new(device.clone()))?,
&device,
fz_matrix {
a: 1.0,
b: 0.0,
@ -3534,94 +3682,14 @@ impl Page {
},
)?;
let MyDevice {
page_num,
page_num: _,
qt,
unprocessed_chars,
unprocessed_non_text,
} = device;
let mut qt = Rc::try_unwrap(qt)
.ok()
.expect("already dropped all other references")
.into_inner();
// we convert to xml and parse that becuase the mupdf rust crate doesn't include all the API surface we need.
let xml: String = todo!("page.to_xml()?");
let MuPdfXml::Page(xml_page) = quick_xml::de::from_str(&xml)?;
for xml_block in xml_page.block {
for xml_line in xml_block.line {
for xml_font in xml_line.font {
const ROUND_FACTOR: f32 = 1000.0;
let font_size = (xml_font.size * ROUND_FACTOR).round() / ROUND_FACTOR;
let font_size = NonNaNF32::new(font_size).ok_or("font size must not be NaN")?;
for xml_char in xml_font.char {
if xml_char.c.trim().is_empty() {
continue;
}
let font_name = match &*xml_font.name {
"DejaVuSansCondensed-Obli" => {
if (xml_char.flags & FZ_STEXT_BOLD) != 0 {
"DejaVuSansCondensed-BoldOblique"
} else {
"DejaVuSansCondensed-Oblique"
}
}
font_name => font_name,
};
let font = Font::new(font_name, font_size);
let [x0, y0, x1, y1, x2, y2, x3, y3] = xml_char.quad;
let min_x = x0.min(x1).min(x2).min(x3);
let max_x = x0.max(x1).max(x2).max(x3);
let min_y = y0.min(y1).min(y2).min(y3);
let max_y = y0.max(y1).max(y2).max(y3);
let Some(text_section) = TextSection::for_position(
page_num,
(min_x + max_x) * 0.5,
(min_y + max_y) * 0.5,
) else {
if PAGE_BODY_MIN_Y <= min_y && min_y <= PAGE_BODY_MAX_Y {
if page_num != 1072 {
// page 1072 has characters in the margins
return Err(
format!("char not in text section: {xml_char:?}\npage_num={page_num}").into(),
);
}
}
continue;
};
let char = Char {
font: font.clone(),
text: xml_char.c.into_owned(),
min_x: NonNaNF32::new(min_x).ok_or("char position shouldn't be NaN")?,
min_y: NonNaNF32::new(min_y).ok_or("char position shouldn't be NaN")?,
max_x: NonNaNF32::new(max_x).ok_or("char position shouldn't be NaN")?,
max_y: NonNaNF32::new(max_y).ok_or("char position shouldn't be NaN")?,
};
let set = match first_seen_fonts.get_mut(font_name) {
Some(v) => v,
None => first_seen_fonts.entry(String::from(font_name)).or_default(),
};
if set.insert(font_size) {
println!(
"first seen font: {font_name:?} {font_size}: page {page_num} {char:?} {:x}",
xml_char.flags,
);
}
qt.entry(text_section).or_default().insert(
min_x,
min_y,
PageItem::Char(char.clone()),
);
unprocessed_chars
.borrow_mut()
.entry(text_section)
.or_default()
.borrow_mut()
.entry(char.font.clone())
.or_default()
.insert(char);
}
}
}
}
first_seen_fonts: _,
error,
} = device.get();
error.replace(Ok(()))?;
for (text_section, i) in unprocessed_chars.borrow_mut().iter_mut() {
for chars in i.borrow_mut().values_mut() {
chars.sort_by_key(Char::top_down_left_to_right_sort_key);
@ -3656,9 +3724,9 @@ impl Page {
}
Ok(Self {
page_num,
qt,
unprocessed_chars,
unprocessed_non_text,
qt: qt.take(),
unprocessed_chars: unprocessed_chars.clone(),
unprocessed_non_text: unprocessed_non_text.clone(),
})
}
}

View file

@ -2,16 +2,17 @@
// 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_font, fz_matrix, fz_page, fz_path, fz_path_walker, fz_point,
fz_rect, fz_stroke_state, fz_text, fz_text_item, fz_text_span, 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,
fz_clone_context, fz_color_params, fz_colorspace, fz_concat, 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_font, fz_font_ascender, fz_font_descender,
fz_font_is_bold, fz_font_is_italic, fz_font_name, fz_matrix, fz_matrix_expansion, fz_page,
fz_path, fz_path_walker, fz_point, fz_rect, fz_stroke_state, fz_text, fz_text_item,
fz_text_span, fz_transform_point, fz_transform_point_xy, fz_transform_vector, 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,
cell::{Cell, UnsafeCell},
ffi::{CStr, c_int, c_void},
fmt,
marker::PhantomData,
@ -104,7 +105,7 @@ impl Context {
CTX.with(f)
}
pub(crate) fn as_ref(&self) -> ContextRef<'_> {
ContextRef(self.0, PhantomData)
unsafe { ContextRef::from_ptr(self.0.as_ptr()) }
}
}
@ -117,7 +118,13 @@ impl Drop for Context {
}
#[derive(Clone, Copy)]
pub(crate) struct ContextRef<'ctx>(NonNull<fz_context>, PhantomData<&'ctx Context>);
pub(crate) struct ContextRef<'ctx>(&'ctx UnsafeCell<fz_context>);
impl<'ctx> ContextRef<'ctx> {
unsafe fn from_ptr(ptr: *mut fz_context) -> Self {
Self(unsafe { &*ptr.cast() })
}
}
impl<'ctx> From<&'ctx Context> for ContextRef<'ctx> {
fn from(value: &'ctx Context) -> Self {
@ -137,13 +144,13 @@ impl<'ctx> Document<'ctx> {
) -> Result<Document<'ctx>, MuPdfError> {
let ctx = ctx.into();
unsafe {
mupdf_try(|errptr| mupdf_open_document(ctx.0.as_ptr(), file_name.as_ptr(), errptr))
mupdf_try(|errptr| mupdf_open_document(ctx.0.get(), 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))?
mupdf_try(|errptr| mupdf_document_page_count(self.ctx.0.get(), self.ptr, errptr))?
.try_into()
.map_err(MuPdfError::new_generic)
}
@ -151,7 +158,7 @@ impl<'ctx> Document<'ctx> {
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))
mupdf_try(|errptr| mupdf_load_page(self.ctx.0.get(), self.ptr, page, errptr))
.map(|ptr| Page { ptr, ctx: self.ctx })
}
}
@ -160,7 +167,7 @@ impl<'ctx> Document<'ctx> {
impl<'ctx> Drop for Document<'ctx> {
fn drop(&mut self) {
unsafe {
fz_drop_document(self.ctx.0.as_ptr(), self.ptr);
fz_drop_document(self.ctx.0.get(), self.ptr);
}
}
}
@ -182,7 +189,7 @@ impl<'ctx> Page<'ctx> {
unsafe {
mupdf_try(|errptr| {
mupdf_run_page(
self.ctx.0.as_ptr(),
self.ctx.0.get(),
self.ptr,
device.dev,
ctm,
@ -197,7 +204,7 @@ impl<'ctx> Page<'ctx> {
impl<'ctx> Drop for Page<'ctx> {
fn drop(&mut self) {
unsafe {
fz_drop_page(self.ctx.0.as_ptr(), self.ptr);
fz_drop_page(self.ctx.0.get(), self.ptr);
}
}
}
@ -209,34 +216,34 @@ pub(crate) struct Device<'ctx, T: 'ctx> {
}
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 fill_path(&self, ctx: ContextRef<'ctx>, path: &Path<'ctx>, even_odd: bool, ctm: fz_matrix);
fn stroke_path(&self, ctx: ContextRef<'ctx>, path: &Path<'ctx>, ctm: fz_matrix);
fn clip_path(
&self,
ctx: ContextRef<'ctx>,
path: &Path<'ctx>,
even_odd: bool,
cmt: fz_matrix,
ctm: fz_matrix,
scissor: fz_rect,
);
fn clip_stroke_path(
&self,
ctx: ContextRef<'ctx>,
path: &Path<'ctx>,
cmt: fz_matrix,
ctm: 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 fill_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, ctm: fz_matrix);
fn stroke_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, ctm: fz_matrix);
fn clip_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, ctm: fz_matrix, scissor: fz_rect);
fn clip_stroke_text(
&self,
ctx: ContextRef<'ctx>,
text: &Text<'ctx>,
cmt: fz_matrix,
ctm: fz_matrix,
scissor: fz_rect,
);
fn ignore_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, cmt: fz_matrix);
fn ignore_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, ctm: fz_matrix);
}
impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
@ -245,7 +252,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
unsafe {
let dev_ptr = mupdf_try(|errptr| {
mupdf_new_derived_device::<DeviceStruct<T>>(
ctx.0.as_ptr(),
ctx.0.get(),
c"parse_powerisa_pdf::mupdf_ffi::Device",
errptr,
)
@ -295,16 +302,13 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device,
path: *const fz_path,
even_odd: c_int,
cmt: fz_matrix,
ctm: 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 ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.fill_path(
ctx,
@ -313,7 +317,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ctx,
}),
even_odd != 0,
cmt,
ctm,
);
}
unsafe extern "C" fn stroke_path_fn(
@ -321,16 +325,13 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device,
path: *const fz_path,
_stroke_state: *const fz_stroke_state,
cmt: fz_matrix,
ctm: 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 ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.stroke_path(
ctx,
@ -338,7 +339,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: path.cast_mut(),
ctx,
}),
cmt,
ctm,
);
}
unsafe extern "C" fn clip_path_fn(
@ -346,13 +347,10 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device,
path: *const fz_path,
even_odd: ::std::os::raw::c_int,
cmt: fz_matrix,
ctm: fz_matrix,
scissor: fz_rect,
) {
let Some(ctx) = NonNull::new(ctx) else {
return;
};
let ctx = ContextRef(ctx, PhantomData);
let ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.clip_path(
ctx,
@ -361,7 +359,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ctx,
}),
even_odd != 0,
cmt,
ctm,
scissor,
);
}
@ -370,13 +368,10 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device,
path: *const fz_path,
_stroke_state: *const fz_stroke_state,
cmt: fz_matrix,
ctm: fz_matrix,
scissor: fz_rect,
) {
let Some(ctx) = NonNull::new(ctx) else {
return;
};
let ctx = ContextRef(ctx, PhantomData);
let ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.clip_stroke_path(
ctx,
@ -384,7 +379,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: path.cast_mut(),
ctx,
}),
cmt,
ctm,
scissor,
);
}
@ -392,16 +387,13 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ctx: *mut fz_context,
dev: *mut fz_device,
text: *const fz_text,
cmt: fz_matrix,
ctm: 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 ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.fill_text(
ctx,
@ -409,7 +401,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: text.cast_mut(),
ctx,
}),
cmt,
ctm,
);
}
unsafe extern "C" fn stroke_text_fn(
@ -417,16 +409,13 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device,
text: *const fz_text,
_stroke_state: *const fz_stroke_state,
cmt: fz_matrix,
ctm: 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 ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.stroke_text(
ctx,
@ -434,20 +423,17 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: text.cast_mut(),
ctx,
}),
cmt,
ctm,
);
}
unsafe extern "C" fn clip_text_fn(
ctx: *mut fz_context,
dev: *mut fz_device,
text: *const fz_text,
cmt: fz_matrix,
ctm: fz_matrix,
scissor: fz_rect,
) {
let Some(ctx) = NonNull::new(ctx) else {
return;
};
let ctx = ContextRef(ctx, PhantomData);
let ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.clip_text(
ctx,
@ -455,7 +441,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: text.cast_mut(),
ctx,
}),
cmt,
ctm,
scissor,
);
}
@ -464,13 +450,10 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device,
text: *const fz_text,
_stroke_state: *const fz_stroke_state,
cmt: fz_matrix,
ctm: fz_matrix,
scissor: fz_rect,
) {
let Some(ctx) = NonNull::new(ctx) else {
return;
};
let ctx = ContextRef(ctx, PhantomData);
let ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.clip_stroke_text(
ctx,
@ -478,7 +461,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: text.cast_mut(),
ctx,
}),
cmt,
ctm,
scissor,
);
}
@ -486,12 +469,9 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ctx: *mut fz_context,
dev: *mut fz_device,
text: *const fz_text,
cmt: fz_matrix,
ctm: fz_matrix,
) {
let Some(ctx) = NonNull::new(ctx) else {
return;
};
let ctx = ContextRef(ctx, PhantomData);
let ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.ignore_text(
ctx,
@ -499,7 +479,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: text.cast_mut(),
ctx,
}),
cmt,
ctm,
);
}
}
@ -508,8 +488,8 @@ 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);
// fz_close_device(self.ctx.0.get(), self.dev);
fz_drop_device(self.ctx.0.get(), self.dev);
}
}
}
@ -583,7 +563,7 @@ 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.ctx.0.get(),
self.ptr,
const {
&fz_path_walker {
@ -607,10 +587,7 @@ impl<'ctx> Path<'ctx> {
x: f32,
y: f32,
) {
let Some(ctx) = NonNull::new(ctx) else {
return;
};
let ctx = ContextRef(ctx, PhantomData);
let ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut *arg.cast::<W>() };
this.move_to(ctx, x, y);
}
@ -620,10 +597,7 @@ impl<'ctx> Path<'ctx> {
x: f32,
y: f32,
) {
let Some(ctx) = NonNull::new(ctx) else {
return;
};
let ctx = ContextRef(ctx, PhantomData);
let ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut *arg.cast::<W>() };
this.line_to(ctx, x, y);
}
@ -637,10 +611,7 @@ impl<'ctx> Path<'ctx> {
x3: f32,
y3: f32,
) {
let Some(ctx) = NonNull::new(ctx) else {
return;
};
let ctx = ContextRef(ctx, PhantomData);
let ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut *arg.cast::<W>() };
this.curve_to(ctx, x1, y1, x2, y2, x3, y3);
}
@ -648,10 +619,7 @@ impl<'ctx> Path<'ctx> {
ctx: *mut fz_context,
arg: *mut c_void,
) {
let Some(ctx) = NonNull::new(ctx) else {
return;
};
let ctx = ContextRef(ctx, PhantomData);
let ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut *arg.cast::<W>() };
this.close_path(ctx);
}
@ -663,10 +631,7 @@ impl<'ctx> Path<'ctx> {
x2: f32,
y2: f32,
) {
let Some(ctx) = NonNull::new(ctx) else {
return;
};
let ctx = ContextRef(ctx, PhantomData);
let ctx = unsafe { ContextRef::from_ptr(ctx) };
let this = unsafe { &mut *arg.cast::<W>() };
this.rect_to(ctx, x1, y1, x2, y2);
}
@ -675,7 +640,7 @@ impl<'ctx> Path<'ctx> {
impl<'ctx> Drop for Path<'ctx> {
fn drop(&mut self) {
unsafe {
fz_drop_path(self.ctx.0.as_ptr(), self.ptr);
fz_drop_path(self.ctx.0.get(), self.ptr);
}
}
}
@ -688,7 +653,7 @@ pub(crate) struct Text<'ctx> {
impl<'ctx> Drop for Text<'ctx> {
fn drop(&mut self) {
unsafe {
fz_drop_text(self.ctx.0.as_ptr(), self.ptr);
fz_drop_text(self.ctx.0.get(), self.ptr);
}
}
}
@ -696,7 +661,7 @@ impl<'ctx> Drop for Text<'ctx> {
impl<'ctx> Text<'ctx> {
pub(crate) fn spans<'a>(&'a self) -> TextSpanIter<'a, 'ctx> {
TextSpanIter {
ptr: unsafe { NonNull::new((*self.ptr).head) },
ptr: unsafe { NonNull::new((*self.ptr).head).map(|ptr| &*ptr.as_ptr().cast()) },
ctx: self.ctx,
_phantom: PhantomData,
}
@ -705,7 +670,7 @@ impl<'ctx> Text<'ctx> {
#[derive(Clone)]
pub(crate) struct TextSpanIter<'a, 'ctx> {
ptr: Option<NonNull<fz_text_span>>,
ptr: Option<&'a UnsafeCell<fz_text_span>>,
ctx: ContextRef<'ctx>,
_phantom: PhantomData<&'a Text<'ctx>>,
}
@ -715,9 +680,9 @@ impl<'a, 'ctx> Iterator for TextSpanIter<'a, 'ctx> {
fn next(&mut self) -> Option<Self::Item> {
let ptr = self.ptr?;
self.ptr = NonNull::new(unsafe { ptr.as_ref().next });
self.ptr = unsafe { NonNull::new((*ptr.get()).next).map(|ptr| &*ptr.as_ptr().cast()) };
Some(TextSpanRef {
ptr: unsafe { &*ptr.as_ptr() },
ptr,
ctx: self.ctx,
_phantom: PhantomData,
})
@ -726,42 +691,75 @@ impl<'a, 'ctx> Iterator for TextSpanIter<'a, 'ctx> {
#[derive(Copy, Clone)]
pub(crate) struct TextSpanRef<'a, 'ctx> {
ptr: &'a fz_text_span,
ptr: &'a UnsafeCell<fz_text_span>,
ctx: ContextRef<'ctx>,
_phantom: PhantomData<&'a Text<'ctx>>,
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub(crate) enum WriteMode {
Horizontal,
Vertical,
}
impl<'a, 'ctx> TextSpanRef<'a, 'ctx> {
pub(crate) fn get(self) -> &'a fz_text_span {
pub(crate) fn get(self) -> &'a UnsafeCell<fz_text_span> {
self.ptr
}
pub(crate) fn font(self) -> FontRef<'a, 'ctx> {
FontRef {
ptr: unsafe { &*self.ptr.font },
ptr: unsafe { &*(*self.ptr.get()).font.cast::<UnsafeCell<fz_font>>() },
ctx: self.ctx,
_phantom: PhantomData,
}
}
pub(crate) fn trm(self) -> fz_matrix {
unsafe { (*self.ptr.get()).trm }
}
pub(crate) fn write_mode(self) -> WriteMode {
if unsafe { (*self.ptr.get()).wmode() != 0 } {
WriteMode::Vertical
} else {
WriteMode::Horizontal
}
}
pub(crate) fn items(self) -> &'a [fz_text_item] {
let len = self.ptr.len as usize;
let len = unsafe { (*self.ptr.get()).len } as usize;
if len == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(self.ptr.items, len) }
unsafe { std::slice::from_raw_parts((*self.ptr.get()).items, len) }
}
}
#[derive(Clone, Copy)]
pub(crate) struct FontRef<'a, 'ctx> {
ptr: &'a fz_font,
ptr: &'a UnsafeCell<fz_font>,
ctx: ContextRef<'ctx>,
_phantom: PhantomData<&'a Text<'ctx>>,
}
impl<'a, 'ctx> FontRef<'a, 'ctx> {
pub(crate) fn get(self) -> &'a fz_font {
pub(crate) fn get(self) -> &'a UnsafeCell<fz_font> {
self.ptr
}
pub(crate) fn name(self) -> &'a str {
unsafe { CStr::from_ptr(fz_font_name(self.ctx.0.get(), self.ptr.get())) }
.to_str()
.expect("font name isn't valid UTF-8")
}
pub(crate) fn is_bold(self) -> bool {
unsafe { fz_font_is_bold(self.ctx.0.get(), self.ptr.get()) != 0 }
}
pub(crate) fn is_italic(self) -> bool {
unsafe { fz_font_is_italic(self.ctx.0.get(), self.ptr.get()) != 0 }
}
pub(crate) fn ascender(self) -> f32 {
unsafe { fz_font_ascender(self.ctx.0.get(), self.ptr.get()) }
}
pub(crate) fn descender(self) -> f32 {
unsafe { fz_font_descender(self.ctx.0.get(), self.ptr.get()) }
}
}
pub(crate) fn transform_point(point: fz_point, m: fz_matrix) -> fz_point {
@ -771,3 +769,36 @@ pub(crate) fn transform_point(point: fz_point, m: fz_matrix) -> fz_point {
pub(crate) fn transform_point_xy(x: f32, y: f32, m: fz_matrix) -> fz_point {
unsafe { fz_transform_point_xy(x, y, m) }
}
pub(crate) fn transform_vector(vector: fz_point, m: fz_matrix) -> fz_point {
unsafe { fz_transform_vector(vector, m) }
}
pub(crate) fn matrix_expansion(m: fz_matrix) -> f32 {
unsafe { fz_matrix_expansion(m) }
}
pub(crate) fn concat(left: fz_matrix, right: fz_matrix) -> fz_matrix {
unsafe { fz_concat(left, right) }
}
pub(crate) fn add_points(a: fz_point, b: fz_point) -> fz_point {
fz_point {
x: a.x + b.x,
y: a.y + b.y,
}
}
pub(crate) fn point_min_components(a: fz_point, b: fz_point) -> fz_point {
fz_point {
x: a.x.min(b.x),
y: a.y.min(b.y),
}
}
pub(crate) fn point_max_components(a: fz_point, b: fz_point) -> fz_point {
fz_point {
x: a.x.max(b.x),
y: a.y.max(b.y),
}
}