# How to Handle Password-Protected PDFs with pdf-inspector: A Complete Guide

> Easily handle password-protected PDFs with pdf-inspector. Learn how to pass passwords using PdfOptions for seamless decryption in this complete guide.

- Repository: [Firecrawl/pdf-inspector](https://github.com/firecrawl/pdf-inspector)
- Tags: how-to-guide
- Published: 2026-08-13

---

**Pass the password via the `PdfOptions` builder when calling `process_pdf_with_options` or `process_pdf_mem_with_options`, and pdf-inspector will automatically decrypt the document using lopdf, falling back to an empty password for owner-only encryption.**

Handling encrypted PDFs in Rust requires careful integration with low-level document parsers. The **pdf-inspector** library from the firecrawl organization simplifies this by providing a high-level API that accepts passwords through a builder pattern and handles decryption transparently. This guide demonstrates how to handle password-protected PDFs with pdf-inspector using both file-based and in-memory workflows.

## Setting the Password via PdfOptions

The decryption workflow begins with the `PdfOptions` struct defined in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs). This builder exposes an optional `password` field that accepts the encryption key as a `String`.

```rust
pub struct PdfOptions {
    ...
    /// Password for decrypting an encrypted PDF. `None` falls back to the
    /// empty password (owner‑only encryption).
    pub password: Option<String>,
}

```

*(see [src/lib.rs L185-L188](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs#L185-L188))*

When you instantiate `PdfOptions` using `PdfOptions::new()`, you can chain the `.password()` method to supply the decryption key. The library passes this value downstream to the document loader, ensuring the password is available before any parsing begins.

## Decryption Architecture and Implementation

Once you invoke a processing function, pdf-inspector routes the password through internal loader functions located in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs). Every high-level entry point—including `process_pdf_with_options` and `process_pdf_mem_with_options`—delegates to either `load_document_from_path_with_password` or `load_document_from_mem_with_password`.

```rust
pub(crate) fn load_document_from_path_with_password<P: AsRef<Path>>(
    path: P,
    password: Option<&str>,
) -> Result<(Document, u32), PdfError> { ... }

```

*(see [src/lib.rs L75-L78](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs#L75-L78))*

These helpers forward the optional password to `load_document_bytes`, where the actual decryption logic resides.

### The Decryption Routine

Inside `load_document_bytes`, the code detects encryption and calls `decrypt_document_bytes` (lines 40-48). This function invokes **lopdf**’s `Document::load_mem_with_options` with the supplied password. If authentication fails, the routine automatically retries with an empty string, handling the common "owner-only" protection scenario without requiring user intervention.

```rust
fn decrypt_document_bytes(buf: &[u8], password: Option<&str>) -> Result<Document, lopdf::Error> {
    let pw = password.unwrap_or("");
    match Document::load_mem_with_options(buf, lopdf::LoadOptions::with_password(pw)) {
        Ok(doc) => Ok(doc),
        Err(inner) if !pw.is_empty() => {
            Document::load_mem_with_options(buf, lopdf::LoadOptions::with_password(""))
                .map_err(|_| inner)
        }
        Err(inner) => Err(inner),
    }
}

```

*(see [src/lib.rs L40-L48](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs#L40-L48))*

## Processing Password-Protected Files and Buffers

After supplying the password via the builder, the remainder of the pipeline—detection, extraction, and markdown generation—operates identically to unencrypted documents. The password is never exposed in log output because `PdfOptions` implements a custom `Debug` trait that redacts sensitive fields.

### From Disk

Use `process_pdf_with_options` to read a password-protected file from the filesystem:

```rust
use pdf_inspector::{process_pdf_with_options, PdfOptions};

fn main() -> Result<(), pdf_inspector::PdfError> {
    let opts = PdfOptions::new()
        .password("my-secret-pw");

    let result = process_pdf_with_options("protected.pdf", opts)?;
    println!("PDF type: {:?}, pages: {}", result.pdf_type, result.page_count);
    
    if let Some(md) = result.markdown {
        println!("{}", md);
    }
    Ok(())
}

```

### From Memory

For in-memory workflows—such as processing downloaded bytes—use `process_pdf_mem_with_options`:

```rust
let data = std::fs::read("protected.pdf")?;
let opts = PdfOptions::new().password("my-secret-pw");
let result = pdf_inspector::process_pdf_mem_with_options(&data, opts)?;

```

Both variants return the same result structure containing the decrypted document metadata and extracted content.

## Summary

- **Supply passwords** using `PdfOptions::new().password("your-key")` before calling any processing function.
- **Automatic fallback** to empty password handles owner-only encryption without manual retry logic.
- **Secure by default**—the `Debug` implementation for `PdfOptions` redacts passwords to prevent accidental logging.
- **Unified API**—once decrypted, detection and extraction pipelines work identically for both encrypted and unencrypted PDFs.
- **Core implementation** resides in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs), specifically lines 40-48 for decryption and lines 185-188 for the options builder.

## Frequently Asked Questions

### What happens if I provide the wrong password?

If the supplied password fails to decrypt the document, `decrypt_document_bytes` attempts to open the PDF with an empty password as a fallback. If both attempts fail, the function returns the original `lopdf::Error`, and pdf-inspector propagates this error to the caller, allowing you to handle invalid credentials appropriately.

### Does pdf-inspector support owner-only encrypted PDFs?

Yes. Owner-only encryption—where the document opens without a user password but restricts permissions—is handled automatically. When you provide `None` or the wrong password, the fallback logic in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) lines 40-48 retries with an empty string, which successfully decrypts owner-protected files that do not require an open password.

### Can I process password-protected PDFs from memory instead of disk?

Absolutely. Use the `process_pdf_mem_with_options` function with a byte slice and the same `PdfOptions` builder pattern. The internal `load_document_from_mem_with_password` function handles the decryption identically to the file-based variant, making it ideal for web servers or streaming applications.

### Is the password visible in logs or debug output?

No. The `PdfOptions` struct implements a custom `Debug` trait that redacts the `password` field. When you debug-print the options or encounter errors, the password is replaced with a placeholder, ensuring sensitive credentials never appear in application logs or panic traces.