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
24pub 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}