1use crate::document::PdfDocument;
4use crate::error::{Error, Result};
5use crate::sys;
6
7#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct PageSize {
12 pub width: f32,
14 pub height: f32,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21pub enum Rotation {
22 #[default]
24 None,
25 Clockwise90,
27 Rotate180,
29 Clockwise270,
31}
32
33impl Rotation {
34 pub(crate) fn from_raw(raw: i32) -> Rotation {
35 match raw.rem_euclid(4) {
36 1 => Rotation::Clockwise90,
37 2 => Rotation::Rotate180,
38 3 => Rotation::Clockwise270,
39 _ => Rotation::None,
40 }
41 }
42
43 pub(crate) fn as_raw(self) -> i32 {
44 match self {
45 Rotation::None => 0,
46 Rotation::Clockwise90 => 1,
47 Rotation::Rotate180 => 2,
48 Rotation::Clockwise270 => 3,
49 }
50 }
51
52 pub fn swaps_axes(self) -> bool {
54 matches!(self, Rotation::Clockwise90 | Rotation::Clockwise270)
55 }
56}
57
58pub struct PdfPage<'doc> {
66 doc: &'doc PdfDocument,
67 handle: sys::FPDF_PAGE,
68 index: usize,
69 size: PageSize,
70 rotation: Rotation,
71 forms_active: bool,
72}
73
74unsafe impl Send for PdfPage<'_> {}
77unsafe impl Sync for PdfPage<'_> {}
79
80impl std::fmt::Debug for PdfPage<'_> {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 f.debug_struct("PdfPage")
83 .field("index", &self.index)
84 .field("size", &self.size)
85 .field("rotation", &self.rotation)
86 .finish_non_exhaustive()
87 }
88}
89
90impl<'doc> PdfPage<'doc> {
91 pub(crate) fn open(doc: &'doc PdfDocument, index: usize) -> Result<PdfPage<'doc>> {
92 let forms_active = doc.form_env().is_some();
93 let loaded = doc.pdfium().ffi(|b| {
94 let handle = unsafe { b.FPDF_LoadPage(doc.handle(), index as i32) };
96 if handle.is_null() {
97 return None;
98 }
99 if forms_active {
100 if let Some(env) = doc.form_env() {
101 unsafe { b.FORM_OnAfterLoadPage(handle, env.handle()) };
105 }
106 }
107 let width = unsafe { b.FPDF_GetPageWidthF(handle) };
109 let height = unsafe { b.FPDF_GetPageHeightF(handle) };
110 let rotation = unsafe { b.FPDFPage_GetRotation(handle) };
111 Some((handle, width, height, rotation))
112 });
113
114 match loaded {
115 Some((handle, width, height, rotation)) => Ok(PdfPage {
116 doc,
117 handle,
118 index,
119 size: PageSize { width, height },
120 rotation: Rotation::from_raw(rotation),
121 forms_active,
122 }),
123 None => Err(Error::PageLoadFailed { index }),
124 }
125 }
126
127 pub fn index(&self) -> usize {
129 self.index
130 }
131
132 pub fn size(&self) -> PageSize {
134 self.size
135 }
136
137 pub fn width(&self) -> f32 {
139 self.size.width
140 }
141
142 pub fn height(&self) -> f32 {
144 self.size.height
145 }
146
147 pub fn rotation(&self) -> Rotation {
150 self.rotation
151 }
152
153 pub fn bounding_box(&self) -> Result<crate::PageRect> {
156 let mut rect = sys::FS_RECTF::default();
157 let ok = self.ffi(|b| unsafe { b.FPDF_GetPageBoundingBox(self.handle, &mut rect) });
159 if ok != 0 {
160 Ok(crate::PageRect::new(
161 rect.left as f64,
162 rect.bottom as f64,
163 rect.right as f64,
164 rect.top as f64,
165 ))
166 } else {
167 Err(Error::PageLoadFailed { index: self.index })
168 }
169 }
170
171 pub fn has_transparency(&self) -> bool {
173 self.ffi(|b| unsafe { b.FPDFPage_HasTransparency(self.handle) }) != 0
175 }
176
177 pub(crate) fn document(&self) -> &'doc PdfDocument {
178 self.doc
179 }
180
181 pub(crate) fn handle(&self) -> sys::FPDF_PAGE {
182 self.handle
183 }
184
185 pub(crate) fn ffi<R>(&self, f: impl FnOnce(&sys::Bindings) -> R) -> R {
186 self.doc.pdfium().ffi(f)
187 }
188}
189
190impl Drop for PdfPage<'_> {
191 fn drop(&mut self) {
192 self.ffi(|b| {
193 if self.forms_active {
194 if let Some(env) = self.doc.form_env() {
195 unsafe { b.FORM_OnBeforeClosePage(self.handle, env.handle()) };
198 }
199 }
200 unsafe { b.FPDF_ClosePage(self.handle) };
202 });
203 }
204}