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 // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // 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 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 non_nan_float::NonNaNF32;
use std::{ use std::{
backtrace::Backtrace, backtrace::Backtrace,
@ -135,6 +140,12 @@ macro_rules! make_enum_font {
panic!("invalid font name with id") 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> { const fn new_known(font_name: &str, size: NonNaNF32) -> Option<Self> {
match size.get() { match size.get() {
$($($known_font_size if str_eq(font_name, const { $($($known_font_size if str_eq(font_name, const {
@ -3167,25 +3178,32 @@ impl<'ctx> Parser<'ctx> {
} }
} }
#[derive(Clone, Debug)] #[derive(Debug)]
struct MyDevice { struct MyDevice<'a> {
page_num: u32, page_num: u32,
qt: Rc<RefCell<BTreeMap<TextSection, QuadTree<PageItem>>>>, qt: RefCell<BTreeMap<TextSection, QuadTree<PageItem>>>,
unprocessed_chars: unprocessed_chars:
Rc<RefCell<BTreeMap<TextSection, Rc<RefCell<BTreeMap<Font, IndexSet<Char>>>>>>>, Rc<RefCell<BTreeMap<TextSection, Rc<RefCell<BTreeMap<Font, IndexSet<Char>>>>>>>,
unprocessed_non_text: Rc<RefCell<IndexSet<LineOrRect>>>, 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 { impl<'a> MyDevice<'a> {
fn new(page_num: u32) -> Self { fn new(page_num: u32, first_seen_fonts: &'a mut BTreeMap<String, BTreeSet<NonNaNF32>>) -> Self {
Self { Self {
page_num, page_num,
qt: Default::default(), qt: Default::default(),
unprocessed_chars: Default::default(), unprocessed_chars: Default::default(),
unprocessed_non_text: 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 { enum Walker {
Empty, Empty,
Moved { x: f32, y: f32 }, Moved { x: f32, y: f32 },
@ -3262,19 +3280,19 @@ impl MyDevice {
p1_y, p1_y,
}) => { }) => {
let mupdf_sys::fz_point { x: p0_x, y: p0_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 } = 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 { let Some(line) = new_line(p0_x, p0_y, p1_x, p1_y) else {
return; return;
}; };
LineOrRect::Line(line) LineOrRect::Line(line)
} }
Walker::Rect { x1, y1, x2, y2 } => { Walker::Rect { x1, y1, x2, y2 } => {
let p1 = mupdf_ffi::transform_point_xy(x1, y1, cmt); let p1 = mupdf_ffi::transform_point_xy(x1, y1, ctm);
let p2 = mupdf_ffi::transform_point_xy(x2, y1, cmt); let p2 = mupdf_ffi::transform_point_xy(x2, y1, ctm);
let p3 = mupdf_ffi::transform_point_xy(x2, y2, cmt); let p3 = mupdf_ffi::transform_point_xy(x2, y2, ctm);
let p4 = mupdf_ffi::transform_point_xy(x1, y2, cmt); 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 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 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)); 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() { for span in text.spans() {
let mupdf_sys::fz_text_span { trm, .. } = span.get(); let tm = span.trm();
let mupdf_sys::fz_font { const ROUND_FACTOR: f32 = 1000.0;
refs, let font_size = (mupdf_ffi::matrix_expansion(tm) * ROUND_FACTOR).round() / ROUND_FACTOR;
name, let Some(font_size) = NonNaNF32::new(font_size) else {
buffer, continue;
flags, };
ft_face, let font_name_with_tag = span.font().name();
shaper_data, let font_name_with_tag = match font_name_with_tag {
t3matrix, "CGMSHV+DejaVuSansCondensed-Obli" => "CGMSHV+DejaVuSansCondensed-Oblique",
t3resources, "YDJYQV+DejaVuSansCondensed-Bold" => "YDJYQV+DejaVuSansCondensed-BoldOblique",
t3procs, "NHUPPK+DejaVuSansCondensed-Bold" => "NHUPPK+DejaVuSansCondensed-Bold",
t3lists, _ if font_name_with_tag.len() == 31 => {
t3widths, let _ = self.error.replace(Err(format!(
t3flags, "probably truncated font name: {font_name_with_tag:?}"
t3doc, )
t3run, .into()));
t3freeres, return;
bbox, }
ascender, _ => font_name_with_tag,
descender, };
glyph_count, for &fz_text_item {
bbox_table, x,
use_glyph_bbox, y,
width_count, adv,
width_default, gid,
width_table, ucs,
advance_cache, cid: _,
encoding_cache, } in span.items()
has_digest, {
digest, let adv = if gid >= 0 { adv } else { 0.0 };
subfont, let tm = fz_matrix { e: x, f: y, ..tm };
} = *span.font().get(); let trm = mupdf_ffi::concat(tm, ctm);
for item in span.items() { let dir = match span.write_mode() {
todo!() 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( fn fill_path(
&self, &self,
_ctx: mupdf_ffi::ContextRef<'ctx>, _ctx: mupdf_ffi::ContextRef<'ctx>,
path: &mupdf_ffi::Path<'ctx>, path: &mupdf_ffi::Path<'ctx>,
_even_odd: bool, _even_odd: bool,
cmt: fz_matrix, ctm: fz_matrix,
) { ) {
self.path(path, cmt); self.path(path, ctm);
} }
fn stroke_path( fn stroke_path(
&self, &self,
_ctx: mupdf_ffi::ContextRef<'ctx>, _ctx: mupdf_ffi::ContextRef<'ctx>,
path: &mupdf_ffi::Path<'ctx>, path: &mupdf_ffi::Path<'ctx>,
cmt: fz_matrix, ctm: fz_matrix,
) { ) {
self.path(path, cmt); self.path(path, ctm);
} }
fn clip_path( fn clip_path(
@ -3383,67 +3528,67 @@ impl<'ctx> mupdf_ffi::DeviceCallbacks<'ctx> for MyDevice {
_ctx: mupdf_ffi::ContextRef<'ctx>, _ctx: mupdf_ffi::ContextRef<'ctx>,
path: &mupdf_ffi::Path<'ctx>, path: &mupdf_ffi::Path<'ctx>,
_even_odd: bool, _even_odd: bool,
cmt: fz_matrix, ctm: fz_matrix,
_scissor: mupdf_sys::fz_rect, _scissor: mupdf_sys::fz_rect,
) { ) {
self.path(path, cmt); self.path(path, ctm);
} }
fn clip_stroke_path( fn clip_stroke_path(
&self, &self,
_ctx: mupdf_ffi::ContextRef<'ctx>, _ctx: mupdf_ffi::ContextRef<'ctx>,
path: &mupdf_ffi::Path<'ctx>, path: &mupdf_ffi::Path<'ctx>,
cmt: fz_matrix, ctm: fz_matrix,
_scissor: mupdf_sys::fz_rect, _scissor: mupdf_sys::fz_rect,
) { ) {
self.path(path, cmt); self.path(path, ctm);
} }
fn fill_text( fn fill_text(
&self, &self,
_ctx: mupdf_ffi::ContextRef<'ctx>, _ctx: mupdf_ffi::ContextRef<'ctx>,
text: &mupdf_ffi::Text<'ctx>, text: &mupdf_ffi::Text<'ctx>,
cmt: fz_matrix, ctm: fz_matrix,
) { ) {
self.text(text, cmt); self.text(text, ctm);
} }
fn stroke_text( fn stroke_text(
&self, &self,
_ctx: mupdf_ffi::ContextRef<'ctx>, _ctx: mupdf_ffi::ContextRef<'ctx>,
text: &mupdf_ffi::Text<'ctx>, text: &mupdf_ffi::Text<'ctx>,
cmt: fz_matrix, ctm: fz_matrix,
) { ) {
self.text(text, cmt); self.text(text, ctm);
} }
fn clip_text( fn clip_text(
&self, &self,
_ctx: mupdf_ffi::ContextRef<'ctx>, _ctx: mupdf_ffi::ContextRef<'ctx>,
text: &mupdf_ffi::Text<'ctx>, text: &mupdf_ffi::Text<'ctx>,
cmt: fz_matrix, ctm: fz_matrix,
_scissor: mupdf_sys::fz_rect, _scissor: mupdf_sys::fz_rect,
) { ) {
self.text(text, cmt); self.text(text, ctm);
} }
fn clip_stroke_text( fn clip_stroke_text(
&self, &self,
_ctx: mupdf_ffi::ContextRef<'ctx>, _ctx: mupdf_ffi::ContextRef<'ctx>,
text: &mupdf_ffi::Text<'ctx>, text: &mupdf_ffi::Text<'ctx>,
cmt: fz_matrix, ctm: fz_matrix,
_scissor: mupdf_sys::fz_rect, _scissor: mupdf_sys::fz_rect,
) { ) {
self.text(text, cmt); self.text(text, ctm);
} }
fn ignore_text( fn ignore_text(
&self, &self,
_ctx: mupdf_ffi::ContextRef<'ctx>, _ctx: mupdf_ffi::ContextRef<'ctx>,
text: &mupdf_ffi::Text<'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<'_>, page: &mupdf_ffi::Page<'_>,
first_seen_fonts: &mut BTreeMap<String, BTreeSet<NonNaNF32>>, first_seen_fonts: &mut BTreeMap<String, BTreeSet<NonNaNF32>>,
) -> Result<Self, Box<dyn std::error::Error>> { ) -> 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( page.run(
&mupdf_ffi::Device::new(page.ctx(), Box::new(device.clone()))?, &device,
fz_matrix { fz_matrix {
a: 1.0, a: 1.0,
b: 0.0, b: 0.0,
@ -3534,94 +3682,14 @@ impl Page {
}, },
)?; )?;
let MyDevice { let MyDevice {
page_num, page_num: _,
qt, qt,
unprocessed_chars, unprocessed_chars,
unprocessed_non_text, unprocessed_non_text,
} = device; first_seen_fonts: _,
let mut qt = Rc::try_unwrap(qt) error,
.ok() } = device.get();
.expect("already dropped all other references") error.replace(Ok(()))?;
.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);
}
}
}
}
for (text_section, i) in unprocessed_chars.borrow_mut().iter_mut() { for (text_section, i) in unprocessed_chars.borrow_mut().iter_mut() {
for chars in i.borrow_mut().values_mut() { for chars in i.borrow_mut().values_mut() {
chars.sort_by_key(Char::top_down_left_to_right_sort_key); chars.sort_by_key(Char::top_down_left_to_right_sort_key);
@ -3656,9 +3724,9 @@ impl Page {
} }
Ok(Self { Ok(Self {
page_num, page_num,
qt, qt: qt.take(),
unprocessed_chars, unprocessed_chars: unprocessed_chars.clone(),
unprocessed_non_text, unprocessed_non_text: unprocessed_non_text.clone(),
}) })
} }
} }

View file

@ -2,16 +2,17 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use mupdf_sys::{ use mupdf_sys::{
fz_clone_context, fz_color_params, fz_colorspace, fz_context, fz_device, fz_document, fz_clone_context, fz_color_params, fz_colorspace, fz_concat, fz_context, fz_device,
fz_drop_context, fz_drop_device, fz_drop_document, fz_drop_page, fz_drop_path, fz_drop_text, fz_document, fz_drop_context, fz_drop_device, fz_drop_document, fz_drop_page, fz_drop_path,
fz_error_type_FZ_ERROR_GENERIC, fz_font, fz_matrix, fz_page, fz_path, fz_path_walker, fz_point, fz_drop_text, fz_error_type_FZ_ERROR_GENERIC, fz_font, fz_font_ascender, fz_font_descender,
fz_rect, fz_stroke_state, fz_text, fz_text_item, fz_text_span, fz_transform_point, fz_font_is_bold, fz_font_is_italic, fz_font_name, fz_matrix, fz_matrix_expansion, fz_page,
fz_transform_point_xy, fz_walk_path, mupdf_document_page_count, mupdf_drop_error, fz_path, fz_path_walker, fz_point, fz_rect, fz_stroke_state, fz_text, fz_text_item,
mupdf_error_t, mupdf_load_page, mupdf_new_base_context, mupdf_new_derived_device, fz_text_span, fz_transform_point, fz_transform_point_xy, fz_transform_vector, fz_walk_path,
mupdf_open_document, mupdf_run_page, 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::{ use std::{
cell::Cell, cell::{Cell, UnsafeCell},
ffi::{CStr, c_int, c_void}, ffi::{CStr, c_int, c_void},
fmt, fmt,
marker::PhantomData, marker::PhantomData,
@ -104,7 +105,7 @@ impl Context {
CTX.with(f) CTX.with(f)
} }
pub(crate) fn as_ref(&self) -> ContextRef<'_> { 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)] #[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> { impl<'ctx> From<&'ctx Context> for ContextRef<'ctx> {
fn from(value: &'ctx Context) -> Self { fn from(value: &'ctx Context) -> Self {
@ -137,13 +144,13 @@ impl<'ctx> Document<'ctx> {
) -> Result<Document<'ctx>, MuPdfError> { ) -> Result<Document<'ctx>, MuPdfError> {
let ctx = ctx.into(); let ctx = ctx.into();
unsafe { 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 }) .map(|ptr| Document { ptr, ctx })
} }
} }
pub(crate) fn page_count(&self) -> Result<usize, MuPdfError> { pub(crate) fn page_count(&self) -> Result<usize, MuPdfError> {
unsafe { 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() .try_into()
.map_err(MuPdfError::new_generic) .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> { pub(crate) fn load_page(&self, page: usize) -> Result<Page<'ctx>, MuPdfError> {
let page = page.try_into().map_err(MuPdfError::new_generic)?; let page = page.try_into().map_err(MuPdfError::new_generic)?;
unsafe { 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 }) .map(|ptr| Page { ptr, ctx: self.ctx })
} }
} }
@ -160,7 +167,7 @@ impl<'ctx> Document<'ctx> {
impl<'ctx> Drop for Document<'ctx> { impl<'ctx> Drop for Document<'ctx> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { 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 { unsafe {
mupdf_try(|errptr| { mupdf_try(|errptr| {
mupdf_run_page( mupdf_run_page(
self.ctx.0.as_ptr(), self.ctx.0.get(),
self.ptr, self.ptr,
device.dev, device.dev,
ctm, ctm,
@ -197,7 +204,7 @@ impl<'ctx> Page<'ctx> {
impl<'ctx> Drop for Page<'ctx> { impl<'ctx> Drop for Page<'ctx> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { 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> { pub(crate) trait DeviceCallbacks<'ctx> {
fn fill_path(&self, ctx: ContextRef<'ctx>, path: &Path<'ctx>, even_odd: bool, 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>, cmt: fz_matrix); fn stroke_path(&self, ctx: ContextRef<'ctx>, path: &Path<'ctx>, ctm: fz_matrix);
fn clip_path( fn clip_path(
&self, &self,
ctx: ContextRef<'ctx>, ctx: ContextRef<'ctx>,
path: &Path<'ctx>, path: &Path<'ctx>,
even_odd: bool, even_odd: bool,
cmt: fz_matrix, ctm: fz_matrix,
scissor: fz_rect, scissor: fz_rect,
); );
fn clip_stroke_path( fn clip_stroke_path(
&self, &self,
ctx: ContextRef<'ctx>, ctx: ContextRef<'ctx>,
path: &Path<'ctx>, path: &Path<'ctx>,
cmt: fz_matrix, ctm: fz_matrix,
scissor: fz_rect, scissor: fz_rect,
); );
fn fill_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, cmt: fz_matrix); fn fill_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, ctm: fz_matrix);
fn stroke_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, cmt: 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>, cmt: fz_matrix, scissor: fz_rect); fn clip_text(&self, ctx: ContextRef<'ctx>, text: &Text<'ctx>, ctm: fz_matrix, scissor: fz_rect);
fn clip_stroke_text( fn clip_stroke_text(
&self, &self,
ctx: ContextRef<'ctx>, ctx: ContextRef<'ctx>,
text: &Text<'ctx>, text: &Text<'ctx>,
cmt: fz_matrix, ctm: fz_matrix,
scissor: fz_rect, 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> { impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
@ -245,7 +252,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
unsafe { unsafe {
let dev_ptr = mupdf_try(|errptr| { let dev_ptr = mupdf_try(|errptr| {
mupdf_new_derived_device::<DeviceStruct<T>>( mupdf_new_derived_device::<DeviceStruct<T>>(
ctx.0.as_ptr(), ctx.0.get(),
c"parse_powerisa_pdf::mupdf_ffi::Device", c"parse_powerisa_pdf::mupdf_ffi::Device",
errptr, errptr,
) )
@ -295,16 +302,13 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device, dev: *mut fz_device,
path: *const fz_path, path: *const fz_path,
even_odd: c_int, even_odd: c_int,
cmt: fz_matrix, ctm: fz_matrix,
_color_space: *mut fz_colorspace, _color_space: *mut fz_colorspace,
_color: *const f32, _color: *const f32,
_alpha: f32, _alpha: f32,
_color_params: fz_color_params, _color_params: fz_color_params,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value }; let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.fill_path( this.fill_path(
ctx, ctx,
@ -313,7 +317,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ctx, ctx,
}), }),
even_odd != 0, even_odd != 0,
cmt, ctm,
); );
} }
unsafe extern "C" fn stroke_path_fn( unsafe extern "C" fn stroke_path_fn(
@ -321,16 +325,13 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device, dev: *mut fz_device,
path: *const fz_path, path: *const fz_path,
_stroke_state: *const fz_stroke_state, _stroke_state: *const fz_stroke_state,
cmt: fz_matrix, ctm: fz_matrix,
_color_space: *mut fz_colorspace, _color_space: *mut fz_colorspace,
_color: *const f32, _color: *const f32,
_alpha: f32, _alpha: f32,
_color_params: fz_color_params, _color_params: fz_color_params,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value }; let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.stroke_path( this.stroke_path(
ctx, ctx,
@ -338,7 +339,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: path.cast_mut(), ptr: path.cast_mut(),
ctx, ctx,
}), }),
cmt, ctm,
); );
} }
unsafe extern "C" fn clip_path_fn( unsafe extern "C" fn clip_path_fn(
@ -346,13 +347,10 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device, dev: *mut fz_device,
path: *const fz_path, path: *const fz_path,
even_odd: ::std::os::raw::c_int, even_odd: ::std::os::raw::c_int,
cmt: fz_matrix, ctm: fz_matrix,
scissor: fz_rect, scissor: fz_rect,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value }; let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.clip_path( this.clip_path(
ctx, ctx,
@ -361,7 +359,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ctx, ctx,
}), }),
even_odd != 0, even_odd != 0,
cmt, ctm,
scissor, scissor,
); );
} }
@ -370,13 +368,10 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device, dev: *mut fz_device,
path: *const fz_path, path: *const fz_path,
_stroke_state: *const fz_stroke_state, _stroke_state: *const fz_stroke_state,
cmt: fz_matrix, ctm: fz_matrix,
scissor: fz_rect, scissor: fz_rect,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value }; let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.clip_stroke_path( this.clip_stroke_path(
ctx, ctx,
@ -384,7 +379,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: path.cast_mut(), ptr: path.cast_mut(),
ctx, ctx,
}), }),
cmt, ctm,
scissor, scissor,
); );
} }
@ -392,16 +387,13 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ctx: *mut fz_context, ctx: *mut fz_context,
dev: *mut fz_device, dev: *mut fz_device,
text: *const fz_text, text: *const fz_text,
cmt: fz_matrix, ctm: fz_matrix,
_color_space: *mut fz_colorspace, _color_space: *mut fz_colorspace,
_color: *const f32, _color: *const f32,
_alpha: f32, _alpha: f32,
_color_params: fz_color_params, _color_params: fz_color_params,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value }; let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.fill_text( this.fill_text(
ctx, ctx,
@ -409,7 +401,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: text.cast_mut(), ptr: text.cast_mut(),
ctx, ctx,
}), }),
cmt, ctm,
); );
} }
unsafe extern "C" fn stroke_text_fn( unsafe extern "C" fn stroke_text_fn(
@ -417,16 +409,13 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device, dev: *mut fz_device,
text: *const fz_text, text: *const fz_text,
_stroke_state: *const fz_stroke_state, _stroke_state: *const fz_stroke_state,
cmt: fz_matrix, ctm: fz_matrix,
_color_space: *mut fz_colorspace, _color_space: *mut fz_colorspace,
_color: *const f32, _color: *const f32,
_alpha: f32, _alpha: f32,
_color_params: fz_color_params, _color_params: fz_color_params,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value }; let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.stroke_text( this.stroke_text(
ctx, ctx,
@ -434,20 +423,17 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: text.cast_mut(), ptr: text.cast_mut(),
ctx, ctx,
}), }),
cmt, ctm,
); );
} }
unsafe extern "C" fn clip_text_fn( unsafe extern "C" fn clip_text_fn(
ctx: *mut fz_context, ctx: *mut fz_context,
dev: *mut fz_device, dev: *mut fz_device,
text: *const fz_text, text: *const fz_text,
cmt: fz_matrix, ctm: fz_matrix,
scissor: fz_rect, scissor: fz_rect,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value }; let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.clip_text( this.clip_text(
ctx, ctx,
@ -455,7 +441,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: text.cast_mut(), ptr: text.cast_mut(),
ctx, ctx,
}), }),
cmt, ctm,
scissor, scissor,
); );
} }
@ -464,13 +450,10 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
dev: *mut fz_device, dev: *mut fz_device,
text: *const fz_text, text: *const fz_text,
_stroke_state: *const fz_stroke_state, _stroke_state: *const fz_stroke_state,
cmt: fz_matrix, ctm: fz_matrix,
scissor: fz_rect, scissor: fz_rect,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value }; let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.clip_stroke_text( this.clip_stroke_text(
ctx, ctx,
@ -478,7 +461,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: text.cast_mut(), ptr: text.cast_mut(),
ctx, ctx,
}), }),
cmt, ctm,
scissor, scissor,
); );
} }
@ -486,12 +469,9 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ctx: *mut fz_context, ctx: *mut fz_context,
dev: *mut fz_device, dev: *mut fz_device,
text: *const fz_text, text: *const fz_text,
cmt: fz_matrix, ctm: fz_matrix,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value }; let this = unsafe { &mut (*dev.cast::<DeviceStruct<T>>()).value };
this.ignore_text( this.ignore_text(
ctx, ctx,
@ -499,7 +479,7 @@ impl<'ctx, T: DeviceCallbacks<'ctx>> Device<'ctx, T> {
ptr: text.cast_mut(), ptr: text.cast_mut(),
ctx, ctx,
}), }),
cmt, ctm,
); );
} }
} }
@ -508,8 +488,8 @@ impl<'ctx, T> Drop for Device<'ctx, T> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
// FIXME: fz_close_device may throw exceptions // FIXME: fz_close_device may throw exceptions
// fz_close_device(self.ctx.0.as_ptr(), self.dev); // fz_close_device(self.ctx.0.get(), self.dev);
fz_drop_device(self.ctx.0.as_ptr(), 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) { pub(crate) fn walk<W: PathWalker<'ctx>>(&self, mut walker: W) {
unsafe { unsafe {
fz_walk_path( fz_walk_path(
self.ctx.0.as_ptr(), self.ctx.0.get(),
self.ptr, self.ptr,
const { const {
&fz_path_walker { &fz_path_walker {
@ -607,10 +587,7 @@ impl<'ctx> Path<'ctx> {
x: f32, x: f32,
y: f32, y: f32,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut *arg.cast::<W>() }; let this = unsafe { &mut *arg.cast::<W>() };
this.move_to(ctx, x, y); this.move_to(ctx, x, y);
} }
@ -620,10 +597,7 @@ impl<'ctx> Path<'ctx> {
x: f32, x: f32,
y: f32, y: f32,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut *arg.cast::<W>() }; let this = unsafe { &mut *arg.cast::<W>() };
this.line_to(ctx, x, y); this.line_to(ctx, x, y);
} }
@ -637,10 +611,7 @@ impl<'ctx> Path<'ctx> {
x3: f32, x3: f32,
y3: f32, y3: f32,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut *arg.cast::<W>() }; let this = unsafe { &mut *arg.cast::<W>() };
this.curve_to(ctx, x1, y1, x2, y2, x3, y3); this.curve_to(ctx, x1, y1, x2, y2, x3, y3);
} }
@ -648,10 +619,7 @@ impl<'ctx> Path<'ctx> {
ctx: *mut fz_context, ctx: *mut fz_context,
arg: *mut c_void, arg: *mut c_void,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut *arg.cast::<W>() }; let this = unsafe { &mut *arg.cast::<W>() };
this.close_path(ctx); this.close_path(ctx);
} }
@ -663,10 +631,7 @@ impl<'ctx> Path<'ctx> {
x2: f32, x2: f32,
y2: f32, y2: f32,
) { ) {
let Some(ctx) = NonNull::new(ctx) else { let ctx = unsafe { ContextRef::from_ptr(ctx) };
return;
};
let ctx = ContextRef(ctx, PhantomData);
let this = unsafe { &mut *arg.cast::<W>() }; let this = unsafe { &mut *arg.cast::<W>() };
this.rect_to(ctx, x1, y1, x2, y2); this.rect_to(ctx, x1, y1, x2, y2);
} }
@ -675,7 +640,7 @@ impl<'ctx> Path<'ctx> {
impl<'ctx> Drop for Path<'ctx> { impl<'ctx> Drop for Path<'ctx> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { 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> { impl<'ctx> Drop for Text<'ctx> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { 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> { impl<'ctx> Text<'ctx> {
pub(crate) fn spans<'a>(&'a self) -> TextSpanIter<'a, 'ctx> { pub(crate) fn spans<'a>(&'a self) -> TextSpanIter<'a, 'ctx> {
TextSpanIter { TextSpanIter {
ptr: unsafe { NonNull::new((*self.ptr).head) }, ptr: unsafe { NonNull::new((*self.ptr).head).map(|ptr| &*ptr.as_ptr().cast()) },
ctx: self.ctx, ctx: self.ctx,
_phantom: PhantomData, _phantom: PhantomData,
} }
@ -705,7 +670,7 @@ impl<'ctx> Text<'ctx> {
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct TextSpanIter<'a, 'ctx> { pub(crate) struct TextSpanIter<'a, 'ctx> {
ptr: Option<NonNull<fz_text_span>>, ptr: Option<&'a UnsafeCell<fz_text_span>>,
ctx: ContextRef<'ctx>, ctx: ContextRef<'ctx>,
_phantom: PhantomData<&'a Text<'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> { fn next(&mut self) -> Option<Self::Item> {
let ptr = self.ptr?; 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 { Some(TextSpanRef {
ptr: unsafe { &*ptr.as_ptr() }, ptr,
ctx: self.ctx, ctx: self.ctx,
_phantom: PhantomData, _phantom: PhantomData,
}) })
@ -726,42 +691,75 @@ impl<'a, 'ctx> Iterator for TextSpanIter<'a, 'ctx> {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub(crate) struct TextSpanRef<'a, 'ctx> { pub(crate) struct TextSpanRef<'a, 'ctx> {
ptr: &'a fz_text_span, ptr: &'a UnsafeCell<fz_text_span>,
ctx: ContextRef<'ctx>, ctx: ContextRef<'ctx>,
_phantom: PhantomData<&'a Text<'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> { 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 self.ptr
} }
pub(crate) fn font(self) -> FontRef<'a, 'ctx> { pub(crate) fn font(self) -> FontRef<'a, 'ctx> {
FontRef { FontRef {
ptr: unsafe { &*self.ptr.font }, ptr: unsafe { &*(*self.ptr.get()).font.cast::<UnsafeCell<fz_font>>() },
ctx: self.ctx, ctx: self.ctx,
_phantom: PhantomData, _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] { 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 { if len == 0 {
return &[]; 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)] #[derive(Clone, Copy)]
pub(crate) struct FontRef<'a, 'ctx> { pub(crate) struct FontRef<'a, 'ctx> {
ptr: &'a fz_font, ptr: &'a UnsafeCell<fz_font>,
ctx: ContextRef<'ctx>, ctx: ContextRef<'ctx>,
_phantom: PhantomData<&'a Text<'ctx>>, _phantom: PhantomData<&'a Text<'ctx>>,
} }
impl<'a, 'ctx> FontRef<'a, '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 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 { 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 { pub(crate) fn transform_point_xy(x: f32, y: f32, m: fz_matrix) -> fz_point {
unsafe { fz_transform_point_xy(x, y, m) } 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),
}
}