pdfium/page/
boundaries.rs

1// PDFium-rs -- Modern Rust interface to PDFium, the PDF library from Google
2//
3// Copyright (c) 2025 Martin van der Werff <github (at) newinnovations.nl>
4//
5// This file is part of PDFium-rs.
6//
7// PDFium-rs is free software: you can redistribute it and/or modify it under the terms of
8// the GNU General Public License as published by the Free Software Foundation, either version 3
9// of the License, or (at your option) any later version.
10//
11// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
12// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
13// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
14// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
15// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
16// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
17// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
20use crate::{error::PdfiumResult, lib, page::PdfiumPage, PdfiumRect};
21
22/// Rust interface to the boundary boxes of a page
23///
24/// PDF pages define multiple nested boundary boxes that serve different purposes in the
25/// document lifecycle, from creation to final output.
26///
27/// **MediaBox** The MediaBox represents the full physical size of a PDF page. It includes every
28/// possible part of the layout—bleed area, crop marks, and artwork. It’s essentially the outermost
29/// boundary and acts like the canvas that all other boxes sit on. Every PDF must have a MediaBox,
30/// and it's the largest of the five.
31///
32/// **CropBox** The CropBox defines the visible area of the page when viewed on a screen or printed
33/// by default. It acts as a “window” through which the page content is displayed in most PDF viewers.
34/// This box doesn’t usually affect professional printing, but it’s important for digital previews and
35/// general display.
36///
37/// **BleedBox** The BleedBox extends slightly beyond the final trim size to include artwork that
38/// "bleeds" off the edge of the page. This is especially important in printing, as it prevents unwanted
39/// white margins if the paper is cut slightly off-target. Designers typically allow 3 to 5 mm of bleed
40/// outside the TrimBox.
41///
42/// **TrimBox** The TrimBox defines the finished size of the printed page after it’s been trimmed. In
43/// professional printing workflows, this box is the most critical—it determines how the page will be cut
44/// and positioned. For example, if you're printing a business card or a poster, the TrimBox sets the
45/// final dimensions.
46///
47/// **ArtBox** The ArtBox outlines the area that contains the meaningful content—like text, logos, or
48/// illustrations. It's useful in cases where you want to mark a "safe zone" so that nothing important
49/// sits too close to the edge. While not used as often, it’s handy for laying out advertisements or design
50/// elements within a page.
51pub struct PdfiumPageBoundaries<'a> {
52    page: &'a PdfiumPage,
53}
54
55impl<'a> PdfiumPageBoundaries<'a> {
56    pub(crate) fn new(page: &'a PdfiumPage) -> PdfiumPageBoundaries<'a> {
57        Self { page }
58    }
59
60    /// Gets the "ArtBox" entry from the page dictionary.
61    ///
62    /// The ArtBox defines the extent of the page's meaningful content (including potential
63    /// white space) as intended by the page's creator. This is typically used by print
64    /// production software and represents the "artistic" boundary of the page content.
65    /// It should be contained within or equal to the CropBox.
66    #[inline]
67    pub fn art(&self) -> PdfiumResult<PdfiumRect> {
68        let mut rect = PdfiumRect::zero();
69        lib().FPDFPage_GetArtBox(
70            self.page,
71            &mut rect.left,
72            &mut rect.bottom,
73            &mut rect.right,
74            &mut rect.top,
75        )?;
76        Ok(rect)
77    }
78
79    /// Gets the "BleedBox" entry from the page dictionary.
80    ///
81    /// The BleedBox defines the region to which the contents of the page should be clipped
82    /// when output in a production environment. This may include any extra bleed area needed
83    /// to accommodate the physical limitations of cutting, folding, and trimming equipment.
84    /// The bleed box should be larger than or equal to the TrimBox.
85    #[inline]
86    pub fn bleed(&self) -> PdfiumResult<PdfiumRect> {
87        let mut rect = PdfiumRect::zero();
88        lib().FPDFPage_GetBleedBox(
89            self.page,
90            &mut rect.left,
91            &mut rect.bottom,
92            &mut rect.right,
93            &mut rect.top,
94        )?;
95        Ok(rect)
96    }
97
98    /// Gets the "CropBox" entry from the page dictionary.
99    ///
100    /// The CropBox defines the visible region of default user space. When the page is displayed
101    /// or printed, its contents should be clipped to this rectangle and then imposed on the
102    /// output medium. This is what viewers typically show as the "page" and defaults to the
103    /// MediaBox if not specified. The CropBox should be contained within the MediaBox.
104    #[inline]
105    pub fn crop(&self) -> PdfiumResult<PdfiumRect> {
106        let mut rect = PdfiumRect::zero();
107        lib().FPDFPage_GetCropBox(
108            self.page,
109            &mut rect.left,
110            &mut rect.bottom,
111            &mut rect.right,
112            &mut rect.top,
113        )?;
114        Ok(rect)
115    }
116
117    /// Gets the "MediaBox" entry from the page dictionary.
118    ///
119    /// The MediaBox defines the boundaries of the physical medium on which the page is to be
120    /// printed. This represents the largest possible page size and is required for every page.
121    /// All other page boundary boxes should be contained within or equal to the MediaBox.
122    /// This is typically the paper size (e.g., A4, Letter, etc.).
123    #[inline]
124    pub fn media(&self) -> PdfiumResult<PdfiumRect> {
125        let mut rect = PdfiumRect::zero();
126        lib().FPDFPage_GetMediaBox(
127            self.page,
128            &mut rect.left,
129            &mut rect.bottom,
130            &mut rect.right,
131            &mut rect.top,
132        )?;
133        Ok(rect)
134    }
135
136    /// Gets the "TrimBox" entry from the page dictionary.
137    ///
138    /// The TrimBox defines the intended dimensions of the finished page after trimming.
139    /// This represents the final size of the page as it will appear to the end user after
140    /// any production cutting/trimming processes. It should be contained within or equal to
141    /// the BleedBox and is commonly used in professional printing workflows.
142    #[inline]
143    pub fn trim(&self) -> PdfiumResult<PdfiumRect> {
144        let mut rect = PdfiumRect::zero();
145        lib().FPDFPage_GetTrimBox(
146            self.page,
147            &mut rect.left,
148            &mut rect.bottom,
149            &mut rect.right,
150            &mut rect.top,
151        )?;
152        Ok(rect)
153    }
154
155    /// Gets the default boundary for use by PDF viewers
156    ///
157    /// Returns the most appropriate boundary box for displaying the page in a PDF viewer
158    /// or similar application. This method implements a fallback hierarchy to determine
159    /// the best viewing area:
160    ///
161    /// 1. **CropBox** - The primary choice, as it defines the visible region intended
162    ///    for display and is what most PDF viewers show by default
163    /// 2. **TrimBox** - Used if CropBox is not available, representing the final
164    ///    page dimensions after trimming
165    /// 3. **MediaBox** - The fallback option, representing the full physical page size
166    ///
167    /// This method ensures that viewer applications always have a valid boundary to work
168    /// with, since MediaBox is required to exist in every PDF page. The returned rectangle
169    /// represents the area that should be visible to end users when viewing the document.
170    pub fn default(&self) -> PdfiumResult<PdfiumRect> {
171        self.crop()
172            .or_else(|_| self.trim())
173            .or_else(|_| self.media())
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use crate::document::PdfiumDocument;
180
181    #[test]
182    fn test_media_boundary() {
183        let document = PdfiumDocument::new_from_path("resources/groningen.pdf", None).unwrap();
184        let page = document.page(0).unwrap();
185        let boundary = page.boundaries().media().unwrap();
186
187        assert_eq!(boundary.left, 0.0);
188        assert_eq!(boundary.top, 841.92);
189        assert_eq!(boundary.bottom, 0.0);
190        assert_eq!(boundary.right, 594.95996);
191    }
192
193    #[test]
194    fn test_default_boundary() {
195        let document = PdfiumDocument::new_from_path("resources/groningen.pdf", None).unwrap();
196        let page = document.page(0).unwrap();
197        let boundary = page.boundaries().default().unwrap();
198
199        assert_eq!(boundary.left, 0.0);
200        assert_eq!(boundary.top, 841.92);
201        assert_eq!(boundary.bottom, 0.0);
202        assert_eq!(boundary.right, 594.95996);
203    }
204}