pub mod boundaries;
pub mod link;
pub mod object;
pub mod pages;
pub mod range;
pub mod render;
pub mod text;
use crate::{
PdfiumDocument, PdfiumPageObject, PdfiumTextPage,
error::{PdfiumError, PdfiumResult},
lib,
page::{boundaries::PdfiumPageBoundaries, object::objects::PdfiumPageObjects},
pdfium_constants::FALSE,
pdfium_types::{FPDF_PAGE, Handle, PageHandle},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum PdfiumRotation {
None = 0,
Cw90 = 1,
Cw180 = 2,
Cw270 = 3,
}
impl PdfiumRotation {
pub fn needs_transpose(&self) -> bool {
*self == PdfiumRotation::Cw90 || *self == PdfiumRotation::Cw270
}
}
impl From<i32> for PdfiumRotation {
fn from(value: i32) -> Self {
match value % 4 {
1 => Self::Cw90,
2 => Self::Cw180,
3 => Self::Cw270,
_ => Self::None,
}
}
}
impl std::ops::Add for PdfiumRotation {
type Output = PdfiumRotation;
fn add(self, rhs: Self) -> Self::Output {
Self::Output::from(self as i32 + rhs as i32)
}
}
#[derive(Debug, Clone)]
pub struct PdfiumPage {
handle: PageHandle,
owner: Option<PdfiumDocument>,
}
impl PdfiumPage {
pub(crate) fn new_from_handle(handle: FPDF_PAGE) -> PdfiumResult<Self> {
if handle.is_null() {
Err(PdfiumError::NullHandle)
} else {
Ok(Self {
handle: Handle::new(handle, Some(close_page)),
owner: None,
})
}
}
pub(crate) fn set_owner(&mut self, owner: PdfiumDocument) {
self.owner = Some(owner);
}
pub fn boundaries(&self) -> PdfiumPageBoundaries<'_> {
PdfiumPageBoundaries::new(self)
}
pub fn object_count(&self) -> i32 {
lib().FPDFPage_CountObjects(self)
}
pub fn object(&self, index: i32) -> PdfiumResult<PdfiumPageObject> {
let mut object = lib().FPDFPage_GetObject(self, index)?;
object.set_owner(self.clone());
Ok(object)
}
pub fn objects(&self) -> PdfiumPageObjects<'_> {
PdfiumPageObjects::new(self)
}
pub fn text(&self) -> PdfiumResult<PdfiumTextPage> {
lib().FPDFText_LoadPage(self)
}
pub fn rotation(&self) -> PdfiumRotation {
PdfiumRotation::from(lib().FPDFPage_GetRotation(self))
}
pub fn set_rotation(&self, rotate: PdfiumRotation) {
lib().FPDFPage_SetRotation(self, rotate as i32)
}
pub fn height(&self) -> f32 {
lib().FPDF_GetPageHeightF(self)
}
pub fn width(&self) -> f32 {
lib().FPDF_GetPageWidthF(self)
}
pub fn is_landscape(&self) -> bool {
self.width() > self.height()
}
pub fn is_portrait(&self) -> bool {
self.width() < self.height()
}
pub fn has_transparency(&self) -> bool {
lib().FPDFPage_HasTransparency(self) != FALSE
}
}
impl From<&PdfiumPage> for FPDF_PAGE {
#[inline]
fn from(page: &PdfiumPage) -> Self {
page.handle.handle()
}
}
fn close_page(page: FPDF_PAGE) {
lib().FPDF_ClosePage(page);
}
#[cfg(test)]
mod tests {
use crate::{PdfiumDocument, PdfiumRotation};
#[test]
fn test_rotations() {
assert_eq!(
PdfiumRotation::Cw90 + PdfiumRotation::Cw90,
PdfiumRotation::Cw180
);
assert_eq!(
PdfiumRotation::Cw90 + PdfiumRotation::Cw270,
PdfiumRotation::None
);
assert_eq!(
PdfiumRotation::Cw270 + PdfiumRotation::Cw270,
PdfiumRotation::Cw180
);
}
#[test]
fn test_sequential_page_access() {
let document = PdfiumDocument::new_from_path("resources/groningen.pdf", None).unwrap();
let _: Vec<_> = (0..8)
.map(|i| {
let page = document.page(i % 2);
assert!(page.is_ok());
})
.collect();
}
#[test]
fn test_concurrent_page_access() {
use std::thread;
let handles: Vec<_> = (0..8)
.map(|i| {
thread::spawn(move || {
let document =
PdfiumDocument::new_from_path("resources/groningen.pdf", None).unwrap();
let page = document.page(i % 2);
assert!(page.is_ok());
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
}
#[test]
fn test_load_pages_out_of_range() {
let document = PdfiumDocument::new_from_path("resources/groningen.pdf", None).unwrap();
let page = document.page(-1);
assert!(page.is_err());
let page = document.page(2);
assert!(page.is_err());
}
}