Skip to main content

pdfium_render/
lib.rs

1#![doc = include_str!("../README.md")]
2#![allow(clippy::doc_nested_refdefs)]
3#![allow(dead_code)]
4#![allow(deprecated)]
5
6mod bindgen {
7    #![allow(non_upper_case_globals)]
8    #![allow(non_camel_case_types)]
9    #![allow(non_snake_case)]
10    #![allow(dead_code)]
11
12    include!("bindgen/pdfium_7678.rs");
13
14    pub(crate) type size_t = usize;
15}
16
17mod bindings;
18mod error;
19mod font_provider;
20mod pdf;
21mod pdfium;
22mod utils;
23
24/// A prelude for conveniently importing public `pdfium-render` definitions.
25///
26/// Only re-exports types actually used by the xberg crate.
27/// Removed modules: form fields (field/), appearance_mode, annotation subtypes
28/// popup/redacted/widget/xfa_widget/variable_text. Form highlighting functions
29/// (highlight_*_form_fields) removed from PdfRenderConfig.
30///
31/// Usage:
32/// ```
33/// use pdfium_render::prelude::*;
34/// ```
35pub mod prelude {
36    pub use crate::{
37        bindings::*,
38        error::*,
39        font_provider::FontDescriptor,
40        pdf::bitmap::*,
41        pdf::color::*,
42        pdf::color_space::*,
43        pdf::document::fonts::*,
44        pdf::document::metadata::*,
45        pdf::document::page::annotation::attachment_points::*,
46        pdf::document::page::annotation::circle::*,
47        pdf::document::page::annotation::free_text::*,
48        pdf::document::page::annotation::highlight::*,
49        pdf::document::page::annotation::ink::*,
50        pdf::document::page::annotation::link::*,
51        pdf::document::page::annotation::objects::*,
52        pdf::document::page::annotation::square::*,
53        pdf::document::page::annotation::squiggly::*,
54        pdf::document::page::annotation::stamp::*,
55        pdf::document::page::annotation::strikeout::*,
56        pdf::document::page::annotation::text::*,
57        pdf::document::page::annotation::underline::*,
58        pdf::document::page::annotation::unsupported::*,
59        pdf::document::page::annotation::{PdfPageAnnotation, PdfPageAnnotationCommon, PdfPageAnnotationType},
60        pdf::document::page::annotations::*,
61        pdf::document::page::boundaries::*,
62        pdf::document::page::extraction::*,
63        pdf::document::page::links::*,
64        pdf::document::page::object::content_mark::*,
65        pdf::document::page::object::content_marks::*,
66        pdf::document::page::object::group::*,
67        pdf::document::page::object::image::*,
68        pdf::document::page::object::path::*,
69        pdf::document::page::object::shading::*,
70        pdf::document::page::object::text::*,
71        pdf::document::page::object::unsupported::*,
72        pdf::document::page::object::x_object_form::*,
73        pdf::document::page::object::{
74            PdfPageObject, PdfPageObjectBlendMode, PdfPageObjectCommon, PdfPageObjectLineCap, PdfPageObjectLineJoin,
75            PdfPageObjectType,
76        },
77        pdf::document::page::objects::common::*,
78        pdf::document::page::objects::*,
79        pdf::document::page::paragraph::*,
80        pdf::document::page::render_config::*,
81        pdf::document::page::size::*,
82        pdf::document::page::struct_element::*,
83        pdf::document::page::struct_tree::*,
84        pdf::document::page::text::char::*,
85        pdf::document::page::text::chars::*,
86        pdf::document::page::text::search::*,
87        pdf::document::page::text::segment::*,
88        pdf::document::page::text::segments::*,
89        pdf::document::page::text::*,
90        pdf::document::page::{PdfPage, PdfPageContentRegenerationStrategy, PdfPageOrientation, PdfPageRenderRotation},
91        pdf::document::pages::*,
92        pdf::document::permissions::*,
93        pdf::document::{PdfDocument, PdfDocumentVersion},
94        pdf::font::glyph::*,
95        pdf::font::glyphs::*,
96        pdf::font::*,
97        pdf::link::*,
98        pdf::matrix::*,
99        pdf::path::clip_path::*,
100        pdf::path::segment::*,
101        pdf::path::segments::*,
102        pdf::points::*,
103        pdf::quad_points::*,
104        pdf::rect::*,
105        pdfium::*,
106    };
107}
108
109#[cfg(test)]
110mod tests {
111    use crate::prelude::*;
112    use crate::utils::test::{test_bind_to_pdfium, test_fixture_path};
113    use image_025::ImageFormat;
114    use std::fs::File;
115    use std::path::Path;
116
117    #[test]
118    #[cfg(not(pdfium_use_static))]
119    fn test_readme_example() -> Result<(), PdfiumError> {
120        fn export_pdf_to_jpegs(path: &impl AsRef<Path>, password: Option<&str>) -> Result<(), PdfiumError> {
121            let pdfium = Pdfium;
122
123            let document = pdfium.load_pdf_from_file(path, password)?;
124
125            let render_config = PdfRenderConfig::new()
126                .set_target_width(2000)
127                .set_maximum_height(2000)
128                .rotate_if_landscape(PdfPageRenderRotation::Degrees90, true);
129
130            for (index, page) in document.pages().iter().enumerate() {
131                page.render_with_config(&render_config)?
132                    .as_image()?
133                    .into_rgb8()
134                    .save_with_format(format!("test-page-{}.jpg", index), ImageFormat::Jpeg)
135                    .map_err(|_| PdfiumError::ImageError)?;
136            }
137
138            Ok(())
139        }
140
141        export_pdf_to_jpegs(&test_fixture_path("export-test.pdf"), None)
142    }
143
144    #[test]
145    #[cfg(not(pdfium_use_static))]
146    fn test_dynamic_bindings() -> Result<(), PdfiumError> {
147        let pdfium = Pdfium;
148
149        let document = pdfium.load_pdf_from_file(&test_fixture_path("form-test.pdf"), None)?;
150
151        let render_config = PdfRenderConfig::new()
152            .set_target_width(2000)
153            .set_maximum_height(2000)
154            .rotate_if_landscape(PdfPageRenderRotation::Degrees90, true)
155            .render_form_data(true)
156            .render_annotations(true);
157
158        for (index, page) in document.pages().iter().enumerate() {
159            let result = page
160                .render_with_config(&render_config)?
161                .as_image()?
162                .into_rgb8()
163                .save_with_format(format!("form-test-page-{}.jpg", index), ImageFormat::Jpeg);
164
165            assert!(result.is_ok());
166        }
167
168        Ok(())
169    }
170
171    #[test]
172    #[cfg(pdfium_use_static)]
173    fn test_static_bindings() {
174        Pdfium::bind_to_statically_linked_library().unwrap();
175    }
176
177    #[test]
178    fn test_reader_lifetime() -> Result<(), PdfiumError> {
179        let pdfium = test_bind_to_pdfium();
180
181        let filenames = ["form-test.pdf", "annotations-test.pdf"];
182
183        for filename in filenames {
184            let path = test_fixture_path(filename);
185            let page_count = {
186                let reader = File::open(&path).map_err(PdfiumError::IoError)?;
187
188                let document = pdfium.load_pdf_from_reader(reader, None)?;
189
190                document.pages().len()
191            };
192
193            println!("{} has {} pages", path.display(), page_count);
194        }
195
196        Ok(())
197    }
198
199    #[test]
200    #[cfg(not(pdfium_use_static))]
201    fn test_custom_font_paths_with_text_rendering() -> Result<(), PdfiumError> {
202        let config = PdfiumConfig::new().set_user_font_paths(vec!["/usr/share/fonts/truetype/".to_string()]);
203
204        let bindings = Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path("./"))
205            .or_else(|_| Pdfium::bind_to_system_library());
206
207        match bindings {
208            Ok(bindings) => {
209                let pdfium = Pdfium::new_with_config(bindings, &config);
210
211                let mut document = pdfium.create_new_pdf()?;
212                let mut page = document.pages_mut().create_page_at_end(PdfPagePaperSize::a4())?;
213
214                let font = document.fonts_mut().helvetica();
215                let _text_obj = page.objects_mut().create_text_object(
216                    PdfPoints::new(100.0),
217                    PdfPoints::new(700.0),
218                    "Testing custom font paths",
219                    font,
220                    PdfPoints::new(12.0),
221                )?;
222
223                assert!(page.objects().iter().count() > 0);
224
225                Ok(())
226            }
227            Err(PdfiumError::PdfiumLibraryBindingsAlreadyInitialized) => Ok(()),
228            Err(e) => Err(e),
229        }
230    }
231
232    #[test]
233    #[cfg(not(pdfium_use_static))]
234    fn test_empty_font_paths() -> Result<(), PdfiumError> {
235        let config = PdfiumConfig::new();
236
237        let bindings = Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path("./"))
238            .or_else(|_| Pdfium::bind_to_system_library());
239
240        match bindings {
241            Ok(bindings) => {
242                let pdfium = Pdfium::new_with_config(bindings, &config);
243                let document = pdfium.create_new_pdf()?;
244                assert_eq!(document.pages().len(), 0);
245                Ok(())
246            }
247            Err(PdfiumError::PdfiumLibraryBindingsAlreadyInitialized) => Ok(()),
248            Err(e) => Err(e),
249        }
250    }
251
252    #[test]
253    #[cfg(not(pdfium_use_static))]
254    fn test_font_paths_with_null_bytes() -> Result<(), PdfiumError> {
255        let config = PdfiumConfig::new().set_user_font_paths(vec![
256            "/usr/share\0/fonts".to_string(),
257            "/usr/share/fonts/truetype/".to_string(),
258        ]);
259
260        let bindings = Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path("./"))
261            .or_else(|_| Pdfium::bind_to_system_library());
262
263        match bindings {
264            Ok(bindings) => {
265                let pdfium = Pdfium::new_with_config(bindings, &config);
266                let document = pdfium.create_new_pdf()?;
267                assert_eq!(document.pages().len(), 0);
268                Ok(())
269            }
270            Err(PdfiumError::PdfiumLibraryBindingsAlreadyInitialized) => Ok(()),
271            Err(e) => Err(e),
272        }
273    }
274
275    #[test]
276    #[cfg(not(pdfium_use_static))]
277    fn test_font_paths_nonexistent() -> Result<(), PdfiumError> {
278        let config = PdfiumConfig::new().set_user_font_paths(vec![
279            "/this/path/does/not/exist".to_string(),
280            "/another/fake/path".to_string(),
281        ]);
282
283        let bindings = Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path("./"))
284            .or_else(|_| Pdfium::bind_to_system_library());
285
286        match bindings {
287            Ok(bindings) => {
288                let pdfium = Pdfium::new_with_config(bindings, &config);
289                let document = pdfium.create_new_pdf()?;
290                assert_eq!(document.pages().len(), 0);
291                Ok(())
292            }
293            Err(PdfiumError::PdfiumLibraryBindingsAlreadyInitialized) => Ok(()),
294            Err(e) => Err(e),
295        }
296    }
297
298    #[test]
299    #[cfg(not(pdfium_use_static))]
300    fn test_default_config_uses_simple_initialization() -> Result<(), PdfiumError> {
301        let config = PdfiumConfig::new();
302
303        let bindings = Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path("./"))
304            .or_else(|_| Pdfium::bind_to_system_library());
305
306        match bindings {
307            Ok(bindings) => {
308                let pdfium = Pdfium::new_with_config(bindings, &config);
309                let document = pdfium.create_new_pdf()?;
310                assert_eq!(document.pages().len(), 0);
311                Ok(())
312            }
313            Err(PdfiumError::PdfiumLibraryBindingsAlreadyInitialized) => Ok(()),
314            Err(e) => Err(e),
315        }
316    }
317}