use crate::document::PdfDocument;
use crate::error::{Error, Result};
use crate::sys;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PageSize {
pub width: f32,
pub height: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Rotation {
#[default]
None,
Clockwise90,
Rotate180,
Clockwise270,
}
impl Rotation {
pub(crate) fn from_raw(raw: i32) -> Rotation {
match raw.rem_euclid(4) {
1 => Rotation::Clockwise90,
2 => Rotation::Rotate180,
3 => Rotation::Clockwise270,
_ => Rotation::None,
}
}
pub(crate) fn as_raw(self) -> i32 {
match self {
Rotation::None => 0,
Rotation::Clockwise90 => 1,
Rotation::Rotate180 => 2,
Rotation::Clockwise270 => 3,
}
}
pub fn swaps_axes(self) -> bool {
matches!(self, Rotation::Clockwise90 | Rotation::Clockwise270)
}
}
pub struct PdfPage<'doc> {
doc: &'doc PdfDocument,
handle: sys::FPDF_PAGE,
index: usize,
size: PageSize,
rotation: Rotation,
forms_active: bool,
}
unsafe impl Send for PdfPage<'_> {}
unsafe impl Sync for PdfPage<'_> {}
impl std::fmt::Debug for PdfPage<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PdfPage")
.field("index", &self.index)
.field("size", &self.size)
.field("rotation", &self.rotation)
.finish_non_exhaustive()
}
}
impl<'doc> PdfPage<'doc> {
pub(crate) fn open(doc: &'doc PdfDocument, index: usize) -> Result<PdfPage<'doc>> {
let forms_active = doc.form_env().is_some();
let loaded = doc.pdfium().ffi(|b| {
let handle = unsafe { b.FPDF_LoadPage(doc.handle(), index as i32) };
if handle.is_null() {
return None;
}
if forms_active {
if let Some(env) = doc.form_env() {
unsafe { b.FORM_OnAfterLoadPage(handle, env.handle()) };
}
}
let width = unsafe { b.FPDF_GetPageWidthF(handle) };
let height = unsafe { b.FPDF_GetPageHeightF(handle) };
let rotation = unsafe { b.FPDFPage_GetRotation(handle) };
Some((handle, width, height, rotation))
});
match loaded {
Some((handle, width, height, rotation)) => Ok(PdfPage {
doc,
handle,
index,
size: PageSize { width, height },
rotation: Rotation::from_raw(rotation),
forms_active,
}),
None => Err(Error::PageLoadFailed { index }),
}
}
pub fn index(&self) -> usize {
self.index
}
pub fn size(&self) -> PageSize {
self.size
}
pub fn width(&self) -> f32 {
self.size.width
}
pub fn height(&self) -> f32 {
self.size.height
}
pub fn rotation(&self) -> Rotation {
self.rotation
}
pub fn bounding_box(&self) -> Result<crate::PageRect> {
let mut rect = sys::FS_RECTF::default();
let ok = self.ffi(|b| unsafe { b.FPDF_GetPageBoundingBox(self.handle, &mut rect) });
if ok != 0 {
Ok(crate::PageRect::new(
rect.left as f64,
rect.bottom as f64,
rect.right as f64,
rect.top as f64,
))
} else {
Err(Error::PageLoadFailed { index: self.index })
}
}
pub fn has_transparency(&self) -> bool {
self.ffi(|b| unsafe { b.FPDFPage_HasTransparency(self.handle) }) != 0
}
pub(crate) fn document(&self) -> &'doc PdfDocument {
self.doc
}
pub(crate) fn handle(&self) -> sys::FPDF_PAGE {
self.handle
}
pub(crate) fn ffi<R>(&self, f: impl FnOnce(&sys::Bindings) -> R) -> R {
self.doc.pdfium().ffi(f)
}
}
impl Drop for PdfPage<'_> {
fn drop(&mut self) {
self.ffi(|b| {
if self.forms_active {
if let Some(env) = self.doc.form_env() {
unsafe { b.FORM_OnBeforeClosePage(self.handle, env.handle()) };
}
}
unsafe { b.FPDF_ClosePage(self.handle) };
});
}
}