This commit is contained in:
parent
a677cd8a33
commit
040afcc435
2 changed files with 373 additions and 274 deletions
384
src/main.rs
384
src/main.rs
|
|
@ -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(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
263
src/mupdf_ffi.rs
263
src/mupdf_ffi.rs
|
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue