pdfium_render/pdf/document/page/
boundaries.rs

1//! Defines the [PdfPageBoundaries] struct, exposing functionality related to the
2//! boundary boxes of a single [PdfPage].
3
4use crate::bindgen::{FPDF_BOOL, FPDF_PAGE, FS_RECTF};
5use crate::bindings::PdfiumLibraryBindings;
6use crate::error::PdfiumError;
7use crate::pdf::rect::PdfRect;
8use std::os::raw::c_float;
9
10#[cfg(doc)]
11use crate::pdf::document::page::PdfPage;
12
13/// The box type of a single boundary box in a [PdfPage].
14#[derive(Debug, Copy, Clone, PartialEq)]
15pub enum PdfPageBoundaryBoxType {
16    Media,
17    Art,
18    Bleed,
19    Trim,
20    Crop,
21    Bounding,
22}
23
24/// The type and bounds of a single boundary box in a [PdfPage].
25#[derive(Debug, Copy, Clone, PartialEq)]
26pub struct PdfPageBoundaryBox {
27    pub box_type: PdfPageBoundaryBoxType,
28    pub bounds: PdfRect,
29}
30
31impl PdfPageBoundaryBox {
32    #[inline]
33    pub(crate) fn new(boundary: PdfPageBoundaryBoxType, bounds: PdfRect) -> Self {
34        Self {
35            box_type: boundary,
36            bounds,
37        }
38    }
39}
40
41/// The page boundaries of a single [PdfPage].
42///
43/// The content of a page can be bounded by up to six different boxes:
44///
45/// * Media box: the full page size, equivalent to the target paper size when the document is printed.
46///   All other page boundaries must fit inside the Media box.
47/// * Art box: the maximum extent of out-of-bleed page art when offset printing.
48///   Typically cropped out when viewing the document on-screen.
49/// * Bleed box: the maximum extent of outside-trim page bleeds when offset printing.
50///   Typically cropped out when viewing the document on-screen.
51/// * Trim box: the maximum extent of page trims when offset printing.
52///   Typically cropped out when viewing the document on-screen.
53/// * Crop box: the maximum extent of user-visible content when viewing the document on-screen.
54/// * Bounding box ("BBox"): the smallest rectangle that can enclose all the content contained in the page.
55///
56/// These boundaries are concentric, i.e. the Bounding box must fit within the Crop box,
57/// which must fit within the Trim box, and so on. The Media box therefore contains all other boxes.
58/// Not all boxes are guaranteed to exist for all pages.
59///
60/// For more information, see section 10.10.1 on page 962 of the PDF Reference Manual version 1.7,
61/// or visit: <https://www.pdfscripting.com/public/PDF-Page-Coordinates.cfm#UserSpace>
62pub struct PdfPageBoundaries<'a> {
63    page_handle: FPDF_PAGE,
64    bindings: &'a dyn PdfiumLibraryBindings,
65}
66
67impl<'a> PdfPageBoundaries<'a> {
68    #[inline]
69    pub(crate) fn from_pdfium(
70        page_handle: FPDF_PAGE,
71        bindings: &'a dyn PdfiumLibraryBindings,
72    ) -> Self {
73        Self {
74            page_handle,
75            bindings,
76        }
77    }
78
79    /// Returns the [PdfiumLibraryBindings] used by this [PdfPageBoundaries] collection.
80    #[inline]
81    pub fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
82        self.bindings
83    }
84
85    /// Returns the boundary box defined for the containing [PdfPage] matching the
86    /// given [PdfPageBoundaryBoxType], if any.
87    #[inline]
88    pub fn get(&self, boundary: PdfPageBoundaryBoxType) -> Result<PdfPageBoundaryBox, PdfiumError> {
89        match boundary {
90            PdfPageBoundaryBoxType::Media => self.media(),
91            PdfPageBoundaryBoxType::Art => self.art(),
92            PdfPageBoundaryBoxType::Bleed => self.bleed(),
93            PdfPageBoundaryBoxType::Trim => self.trim(),
94            PdfPageBoundaryBoxType::Crop => self.crop(),
95            PdfPageBoundaryBoxType::Bounding => self.bounding(),
96        }
97    }
98
99    /// Sets the boundary box matching the given [PdfPageBoundaryBoxType] to the given [PdfRect]
100    /// for the containing [PdfPage].
101    #[inline]
102    pub fn set(
103        &mut self,
104        box_type: PdfPageBoundaryBoxType,
105        rect: PdfRect,
106    ) -> Result<(), PdfiumError> {
107        match box_type {
108            PdfPageBoundaryBoxType::Media => self.set_media(rect),
109            PdfPageBoundaryBoxType::Art => self.set_art(rect),
110            PdfPageBoundaryBoxType::Bleed => self.set_bleed(rect),
111            PdfPageBoundaryBoxType::Trim => self.set_trim(rect),
112            PdfPageBoundaryBoxType::Crop => self.set_crop(rect),
113            PdfPageBoundaryBoxType::Bounding => Ok(()), // The bounding box is implicit and cannot be set directly.
114        }
115    }
116
117    /// Returns the Media boundary box defined for the containing [PdfPage], if any.
118    /// The Media box is the full page size, equivalent to the target paper size when the document
119    /// is printed.
120    #[inline]
121    pub fn media(&self) -> Result<PdfPageBoundaryBox, PdfiumError> {
122        self.get_bounding_box_rect(|page, left, bottom, right, top| {
123            self.bindings
124                .FPDFPage_GetMediaBox(page, left, bottom, right, top)
125        })
126        .map(|rect| PdfPageBoundaryBox::new(PdfPageBoundaryBoxType::Media, rect))
127    }
128
129    /// Sets the Media boundary box for the containing [PdfPage] to the given [PdfRect].
130    pub fn set_media(&mut self, rect: PdfRect) -> Result<(), PdfiumError> {
131        self.bindings.FPDFPage_SetMediaBox(
132            self.page_handle,
133            rect.left().value,
134            rect.bottom().value,
135            rect.right().value,
136            rect.top().value,
137        );
138
139        Ok(())
140    }
141
142    /// Returns the Art boundary box defined for the containing [PdfPage], if any.
143    /// The Art box is the maximum extent of out-of-bleed page art when offset printing.
144    /// It is typically cropped out when viewing the document on-screen.
145    #[inline]
146    pub fn art(&self) -> Result<PdfPageBoundaryBox, PdfiumError> {
147        self.get_bounding_box_rect(|page, left, bottom, right, top| {
148            self.bindings
149                .FPDFPage_GetArtBox(page, left, bottom, right, top)
150        })
151        .map(|rect| PdfPageBoundaryBox::new(PdfPageBoundaryBoxType::Art, rect))
152    }
153
154    /// Sets the Art boundary box for the containing [PdfPage] to the given [PdfRect].
155    pub fn set_art(&mut self, rect: PdfRect) -> Result<(), PdfiumError> {
156        self.bindings.FPDFPage_SetArtBox(
157            self.page_handle,
158            rect.left().value,
159            rect.bottom().value,
160            rect.right().value,
161            rect.top().value,
162        );
163
164        Ok(())
165    }
166
167    /// Returns the Bleed boundary box defined for the containing [PdfPage], if any.
168    /// The Bleed box is the maximum extent of outside-trim page bleeds when offset printing.
169    /// It is typically cropped out when viewing the document on-screen.
170    #[inline]
171    pub fn bleed(&self) -> Result<PdfPageBoundaryBox, PdfiumError> {
172        self.get_bounding_box_rect(|page, left, bottom, right, top| {
173            self.bindings
174                .FPDFPage_GetBleedBox(page, left, bottom, right, top)
175        })
176        .map(|rect| PdfPageBoundaryBox::new(PdfPageBoundaryBoxType::Bleed, rect))
177    }
178
179    /// Sets the Bleed boundary box for the containing [PdfPage] to the given [PdfRect].
180    pub fn set_bleed(&mut self, rect: PdfRect) -> Result<(), PdfiumError> {
181        self.bindings.FPDFPage_SetBleedBox(
182            self.page_handle,
183            rect.left().value,
184            rect.bottom().value,
185            rect.right().value,
186            rect.top().value,
187        );
188
189        Ok(())
190    }
191
192    /// Returns the Trim boundary box defined for the containing [PdfPage], if any.
193    /// The Trim box is the maximum extent of page trims when offset printing.
194    /// It is typically cropped out when viewing the document on-screen.
195    #[inline]
196    pub fn trim(&self) -> Result<PdfPageBoundaryBox, PdfiumError> {
197        self.get_bounding_box_rect(|page, left, bottom, right, top| {
198            self.bindings
199                .FPDFPage_GetTrimBox(page, left, bottom, right, top)
200        })
201        .map(|rect| PdfPageBoundaryBox::new(PdfPageBoundaryBoxType::Trim, rect))
202    }
203
204    /// Sets the Trim boundary box for the containing [PdfPage] to the given [PdfRect].
205    pub fn set_trim(&mut self, rect: PdfRect) -> Result<(), PdfiumError> {
206        self.bindings.FPDFPage_SetTrimBox(
207            self.page_handle,
208            rect.left().value,
209            rect.bottom().value,
210            rect.right().value,
211            rect.top().value,
212        );
213
214        Ok(())
215    }
216
217    /// Returns the Crop boundary box defined for the containing [PdfPage], if any.
218    /// The Crop box is the maximum extent of user-visible content when viewing the document on-screen.
219    #[inline]
220    pub fn crop(&self) -> Result<PdfPageBoundaryBox, PdfiumError> {
221        self.get_bounding_box_rect(|page, left, bottom, right, top| {
222            self.bindings
223                .FPDFPage_GetCropBox(page, left, bottom, right, top)
224        })
225        .map(|rect| PdfPageBoundaryBox::new(PdfPageBoundaryBoxType::Crop, rect))
226    }
227
228    /// Sets the Crop boundary box for the containing [PdfPage] to the given [PdfRect].
229    pub fn set_crop(&mut self, rect: PdfRect) -> Result<(), PdfiumError> {
230        self.bindings.FPDFPage_SetCropBox(
231            self.page_handle,
232            rect.left().value,
233            rect.bottom().value,
234            rect.right().value,
235            rect.top().value,
236        );
237
238        Ok(())
239    }
240
241    /// Returns the Bounding box ("BBox") defined for the containing [PdfPage], if any.
242    /// The BBox is the smallest rectangle that can enclose all the content contained in the page.
243    /// Unlike other boundary boxes, the BBox is computed dynamically on request and cannot
244    /// be set explicitly.
245    #[inline]
246    pub fn bounding(&self) -> Result<PdfPageBoundaryBox, PdfiumError> {
247        let mut rect = FS_RECTF {
248            left: 0.0,
249            top: 0.0,
250            right: 0.0,
251            bottom: 0.0,
252        };
253
254        let result = self
255            .bindings
256            .FPDF_GetPageBoundingBox(self.page_handle, &mut rect);
257
258        PdfRect::from_pdfium_as_result(result, rect, self.bindings)
259            .map(|rect| PdfPageBoundaryBox::new(PdfPageBoundaryBoxType::Bounding, rect))
260    }
261
262    /// Returns the [PdfRect] obtained from calling the given `FPDF_*Box()` function.
263    #[inline]
264    fn get_bounding_box_rect<F>(&self, f: F) -> Result<PdfRect, PdfiumError>
265    where
266        F: FnOnce(FPDF_PAGE, *mut c_float, *mut c_float, *mut c_float, *mut c_float) -> FPDF_BOOL,
267    {
268        let mut left = 0_f32;
269        let mut bottom = 0_f32;
270        let mut right = 0_f32;
271        let mut top = 0_f32;
272
273        let result = f(
274            self.page_handle,
275            &mut left,
276            &mut bottom,
277            &mut right,
278            &mut top,
279        );
280
281        PdfRect::from_pdfium_as_result(
282            result,
283            FS_RECTF {
284                left,
285                top,
286                right,
287                bottom,
288            },
289            self.bindings,
290        )
291    }
292
293    /// Returns an iterator over all defined [PdfPageBoundaryBox] boxes in the containing `PdfPage`.
294    /// Not all boxes are guaranteed to exist for all pages, but where they are defined they will
295    /// be returned strictly in enclosing order from outermost to innermost:
296    /// Media, Art, Bleed, Trim, Crop, Bounding.
297    pub fn iter(&'a self) -> PageBoundaryIterator<'a> {
298        PageBoundaryIterator::new(self)
299    }
300}
301
302/// An iterator over all the [PdfPageBoundaryBox] objects defined for a [PdfPage].
303/// Not all boxes are guaranteed to exist for all pages, but where they are defined they will
304/// be returned strictly in enclosing order from outermost to innermost:
305/// Media, Art, Bleed, Trim, Crop, Bounding.
306pub struct PageBoundaryIterator<'a> {
307    boundaries: &'a PdfPageBoundaries<'a>,
308    next_index: usize,
309}
310
311impl<'a> PageBoundaryIterator<'a> {
312    #[inline]
313    pub(crate) fn new(boundaries: &'a PdfPageBoundaries<'a>) -> Self {
314        Self {
315            boundaries,
316            next_index: 0,
317        }
318    }
319}
320
321impl<'a> Iterator for PageBoundaryIterator<'a> {
322    type Item = PdfPageBoundaryBox;
323
324    fn next(&mut self) -> Option<Self::Item> {
325        let mut next = None;
326
327        while self.next_index < 5 && next.is_none() {
328            next = match self.next_index {
329                0 => self.boundaries.get(PdfPageBoundaryBoxType::Media).ok(),
330                1 => self.boundaries.get(PdfPageBoundaryBoxType::Art).ok(),
331                2 => self.boundaries.get(PdfPageBoundaryBoxType::Bleed).ok(),
332                3 => self.boundaries.get(PdfPageBoundaryBoxType::Trim).ok(),
333                4 => self.boundaries.get(PdfPageBoundaryBoxType::Crop).ok(),
334                5 => self.boundaries.get(PdfPageBoundaryBoxType::Bounding).ok(),
335                _ => None,
336            };
337
338            self.next_index += 1;
339        }
340
341        next
342    }
343}