use std::path::{Path, PathBuf};
use firecrawl_pdfium::{Error, Pdfium, RenderConfig};
const FIXTURES: &[(&str, Option<&str>)] = &[
("empty.pdf", None),
("header_only.pdf", None),
("truncated.pdf", None),
("garbage.bin", None),
("bad_xref.pdf", None),
("encrypted_rc4.pdf", Some("userpw")),
("encrypted_aes256.pdf", Some("userpw")),
("owner_only.pdf", None),
];
fn main() {
if let Err(err) = run() {
eprintln!("error: {err}");
std::process::exit(1);
}
}
fn run() -> Result<(), Box<dyn std::error::Error>> {
let pdfium = Pdfium::load()?;
for (name, password) in FIXTURES {
println!("{name}:");
let bytes =
std::fs::read(fixture(name)).map_err(|e| format!("cannot read fixture {name}: {e}"))?;
try_open(&pdfium, name, &bytes, *password);
println!();
}
let aes_bytes = std::fs::read(fixture("encrypted_aes256.pdf"))
.map_err(|e| format!("cannot read fixture encrypted_aes256.pdf: {e}"))?;
println!("encrypted_aes256.pdf with a deliberately wrong password:");
match pdfium.load_document(aes_bytes.clone(), Some("definitely-wrong")) {
Err(err) => {
if matches!(err, Error::IncorrectPassword) {
println!(
" -> {err} (is_encryption_error: {})\n",
err.is_encryption_error()
);
} else {
return Err(format!("expected IncorrectPassword, got: {err}").into());
}
}
Ok(_) => return Err("expected IncorrectPassword, but the document opened".into()),
}
let doc = pdfium.load_document(aes_bytes, Some("userpw"))?;
println!("encrypted_aes256.pdf opened with the documented password:");
println!(" pages: {}", doc.page_count());
match doc.security_handler_revision() {
Some(rev) => println!(" security handler revision: {rev}"),
None => println!(" security handler revision: none"),
}
let page = doc.page(0)?;
let rendered = page.render(&RenderConfig::new().dpi(144.0))?;
println!(
" rendered page 0: {} x {} px, {:?}, {} bytes",
rendered.width(),
rendered.height(),
rendered.format(),
rendered.pixels().len(),
);
Ok(())
}
fn try_open(pdfium: &Pdfium, name: &str, bytes: &[u8], known_password: Option<&str>) {
match pdfium.load_document(bytes.to_vec(), None) {
Ok(doc) => match doc.security_handler_revision() {
Some(rev) => {
let perms = doc.permissions();
println!(
" opened without a password despite encryption (revision {rev}); \
permissions: print={} modify={} copy={}",
perms.can_print(),
perms.can_modify(),
perms.can_copy(),
);
}
None => println!(
" opened: {} page(s), not encrypted (PDFium repaired the file if needed)",
doc.page_count()
),
},
Err(err) => {
println!(" load failed: {err}");
println!(" is_encryption_error: {}", err.is_encryption_error());
match err {
Error::PasswordRequired => match known_password {
Some(pw) => match pdfium.load_document(bytes.to_vec(), Some(pw)) {
Ok(doc) => println!(
" retried with the correct password: opened, {} page(s)",
doc.page_count()
),
Err(retry) => println!(" retry with password failed: {retry}"),
},
None => println!(" no password known for {name}; giving up"),
},
Error::IncorrectPassword => {
println!(" the supplied password does not unlock this document");
}
Error::UnsupportedSecurity => {
println!(" encryption scheme not supported by this PDFium build");
}
Error::InvalidPdf => {
println!(" not a PDF, or too corrupt to open — nothing to retry");
}
Error::Pdfium { code } => {
println!(" PDFium reported an unclassified error code: {code}");
}
Error::Load(_) | Error::AlreadyLoaded { .. } => {
println!(" library-level failure (unexpected after a successful load)");
}
Error::Io(_) => {
println!(" I/O failure (unexpected: the bytes are already in memory)");
}
Error::PageIndexOutOfBounds { .. }
| Error::PageLoadFailed { .. }
| Error::TextLoadFailed { .. }
| Error::FormInitFailed
| Error::RenderTooLarge { .. }
| Error::RenderFailed { .. }
| Error::InvalidConfig(_) => {
println!(" not a document-load error (unexpected from load_document)");
}
other => println!(" unrecognized error variant: {other}"),
}
}
}
}
fn fixture(name: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures")
.join(name)
}