This guide shows you how to manage PDF passwords and access controls using the PDFluent SDK. It is for Rust developers who need to secure or modify existing PDF documents.
Encryption removal is part of the encryption feature.
# Cargo.toml
[dependencies]
pdfluent = "1.0.0"Protect a PDF with a user password and an owner password. Choose AES-256 and configure fine-grained permission flags.
use pdfluent::{PdfDocument, EncryptOptions};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("report.pdf")?;
let opts = EncryptOptions::aes256()
.with_user_password("open-sesame")
.with_owner_password("owner-secret");
doc.encrypt(opts)?;
doc.save("report-encrypted.pdf")?;
Ok(())
}Complete program, compiled against the published crate: Code on GitHub →
The same code, typed and run on screen. Watch on YouTube
Open the document you want to protect. Use a mutable binding.
use pdfluent::prelude::*;
let mut doc = PdfDocument::open("report.pdf")?;EncryptOptions::aes256() uses AES-256, which is the current best practice. EncryptOptions::aes128() is accepted but the 1.0 backend currently always emits AES-256 state; if strict 128-bit output matters for interop, track this as a known deviation.
let opts = EncryptOptions::aes256();The user password is required to open the document. The owner password unlocks all permissions. If you omit with_user_password, the document opens without a password but still enforces permission flags.
let opts = opts
.with_user_password("open_document_2025")
.with_owner_password("full_access_secret");Start from a preset (Permissions::full_access, print_only, read_only, or annotate) and opt out of individual rights with the with_*(false) builders: with_print, with_modify, with_copy, with_annotate, with_fill_forms, with_extract_accessibility, with_assemble, with_print_high_quality.
let perms = Permissions::full_access()
.with_modify(false)
.with_copy(false);
let opts = opts.with_permissions(perms);Call encrypt() then save() to write the protected file. encrypt takes EncryptOptions by value (no reference).
doc.encrypt(opts)?;
doc.save("report_protected.pdf")?;Open an encrypted PDF with the user or owner password and save an unprotected copy. Works with RC4-128 and AES-128/256 encrypted files.
use pdfluent::{PdfDocument, OpenOptions};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open_with(
"locked.pdf",
OpenOptions::new().with_password("s3cret"),
)?;
doc.decrypt("s3cret")?;
doc.save("decrypted.pdf")?;
Ok(())
}Use PdfDocument::open_with() and provide the user or owner password. The method returns an error if the password is wrong.
use pdfluent::{PdfDocument, OpenOptions};
let mut doc = PdfDocument::open_with("protected.pdf", OpenOptions::new().with_password("open123"))?;Match on Error::WrongPassword to give the user a clear error instead of a generic failure.
use pdfluent::{PdfDocument, OpenOptions};
let result = PdfDocument::open_with("protected.pdf", OpenOptions::new().with_password("guess"));
match result {
Ok(doc) => { /* proceed */ }
Err(_) => eprintln!("Could not open - wrong or missing password."),
}After opening, inspect the encryption type and permission flags that were set by the document author.
// Strip the encryption so the saved copy opens without a password
doc.decrypt("open123")?;Call decrypt(password) to strip all password protection and permission flags from the document in memory.
doc.save("decrypted.pdf")?;Save to a new path. The saved file will open in any PDF reader without a password.
// Verify by re-opening without a password
let check = PdfDocument::open("decrypted.pdf")?;
println!("Re-opened without a password: {} pages", check.page_count());Protect a PDF with a password so readers must enter it to open the file, or set an owner password to restrict editing.
use pdfluent::{PdfDocument, EncryptOptions};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("invoice.pdf")?;
doc.encrypt(EncryptOptions::aes256().with_user_password("s3cret"))?;
doc.save("invoice-protected.pdf")?;
Ok(())
}The encryption feature includes AES-128 and AES-256 support.
# Cargo.toml
[dependencies]
pdfluent = "1.0.0"Open the PDF you want to protect. If it is already encrypted, open it with the current owner password first.
use pdfluent::PdfDocument;
let mut doc = PdfDocument::open("report.pdf")?;The first argument to aes256() is the owner password. The second is the user password. Set the user password to an empty string to allow opening without a password while still restricting permissions.
use pdfluent::EncryptOptions;
// Both passwords set - readers must enter user-pw to open
let opts = EncryptOptions::aes256().with_owner_password("owner-pw-123").with_user_password("user-pw-456");
// Owner password only - file opens without a password, but editing is restricted
let opts = EncryptOptions::aes256().with_owner_password("owner-pw-123").with_user_password("");doc.encrypt() modifies the in-memory document. The protection is written to disk on save.
doc.encrypt(opts)?;
doc.save("report-protected.pdf")?;
println!("Saved report-protected.pdf");Re-open the saved file and confirm it requires a password.
let result = PdfDocument::open("report-protected.pdf");
match result {
Err(_) => println!("Correct: file requires a password to open."),
Ok(_) => println!("Warning: file opened without a password."),
}Decrypt a password-protected PDF and save a clean, unencrypted copy. Requires the owner password.
use pdfluent::{PdfDocument, OpenOptions};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open_with(
"locked.pdf",
OpenOptions::new().with_password("s3cret"),
)?;
doc.decrypt("s3cret")?;
doc.save("unlocked.pdf")?;
Ok(())
}You need the owner password to remove encryption. The user password only grants read access. If you only have the user password, you cannot remove or modify the encryption.
use pdfluent::{PdfDocument, OpenOptions};
let mut doc = PdfDocument::open_with(
"protected.pdf",
OpenOptions::new().with_password("owner-pw-123"),
)?;open_with() returns Error::WrongPassword if the password is incorrect. Handle this in a pipeline to log failures without crashing.
use pdfluent::{PdfDocument, OpenOptions};
match PdfDocument::open_with("protected.pdf", OpenOptions::new().with_password("owner-pw-123")) {
Ok(doc) => println!("Opened successfully"),
Err(_) => eprintln!("Wrong or missing password."),
}decrypt(password) strips all encryption dictionaries from the document. The saved file is a plain PDF with no password required.
doc.decrypt("owner-pw-123")?;
doc.save("unprotected.pdf")?;
// Verify by opening without a password
let _check = PdfDocument::open("unprotected.pdf")?;
println!("Saved an unprotected copy.");Combine with a directory scan to decrypt a folder of PDFs at once.
use std::fs;
use pdfluent::{PdfDocument, OpenOptions};
let password = "owner-pw-123";
for entry in fs::read_dir("./encrypted")?.filter_map(|e| e.ok()) {
let path = entry.path();
if path.extension().map_or(false, |e| e == "pdf") {
match PdfDocument::open_with(&path, OpenOptions::new().with_password(password)) {
Ok(mut doc) => {
doc.decrypt(password)?;
let out = std::path::Path::new("./decrypted").join(path.file_name().unwrap());
doc.save(out)?;
println!("Decrypted: {}", path.display());
}
Err(_) => eprintln!("Could not open: {}", path.display()),
}
}
}Pass a user or owner password through OpenOptions to decrypt a PDF at load time, then work with it like any other document.
use pdfluent::{PdfDocument, OpenOptions};
fn main() -> pdfluent::Result<()> {
let doc = PdfDocument::open_with(
"locked.pdf",
OpenOptions::new().with_password("s3cret"),
)?;
println!("{} pages", doc.page_count());
Ok(())
}Complete program, compiled against the published crate: Code on GitHub →
The same code, typed and run on screen. Watch on YouTube
PdfDocument::open_with takes a path and an OpenOptions. Chain .with_password(...) to pre-provision the user or owner password. Owner passwords grant full access regardless of permissions.
use pdfluent::prelude::*;
let opts = OpenOptions::new().with_password("s3cret");
let doc = PdfDocument::open_with("locked.pdf", opts)?;A wrong password returns Error::DecryptionFailed with reason WrongPassword. Match on the stable error code to prompt the user or fall back to a password-recovery flow.
use pdfluent::prelude::*;
match PdfDocument::open_with(
"locked.pdf",
OpenOptions::new().with_password("maybe_wrong"),
) {
Ok(doc) => println!("opened with {} pages", doc.page_count()),
Err(e) if e.code() == "E-SECURITY-DECRYPTION-FAILED" => {
eprintln!("wrong password — try again");
}
Err(e) => return Err(e),
}Don't hard-code secrets. Read the password from your runtime's secret manager — environment variable, vault, or a config file loaded through your existing secret-management layer.
use pdfluent::prelude::*;
let pw = std::env::var("PDF_OPEN_PASSWORD")
.expect("PDF_OPEN_PASSWORD env var not set");
let doc = PdfDocument::open_with(
"locked.pdf",
OpenOptions::new().with_password(pw),
)?;If the PDF is already in memory (blob from a database, response from an API), use from_bytes_with with the same OpenOptions. No filesystem roundtrip required.
use pdfluent::prelude::*;
let bytes: Vec<u8> = fetch_from_backend()?;
let doc = PdfDocument::from_bytes_with(
&bytes,
OpenOptions::new().with_password("s3cret"),
)?;1.0 does not report a document's permission bits. It answers whether the file is protected and whether your password opens it -- and it sets permissions when you write one.
use pdfluent::error::DecryptionFailureReason;
use pdfluent::{Error, OpenOptions, PdfDocument};
fn main() -> pdfluent::Result<()> {
// 1.0 does not report the permission bits of a document it opened. What it
// does answer is whether the file is protected at all, and whether the
// password you have is the one it wants.
match PdfDocument::open("document.pdf") {
Ok(doc) => println!("no password needed: {} pages", doc.page_count()),
Err(Error::DecryptionFailed { reason, .. }) => {
// Without a password there is only one answer worth giving, and the
// library gives that one: PasswordRequired. WrongPassword can only
// come back from an open that carried a password.
println!("protected ({reason:?}); trying the one we have");
match PdfDocument::open_with(
"document.pdf",
OpenOptions::new().with_password("user-password"),
) {
Ok(doc) => println!("opened: {} pages", doc.page_count()),
Err(Error::DecryptionFailed { reason, .. }) => match reason {
DecryptionFailureReason::WrongPassword => println!("that is not the password"),
other => println!("cannot open it: {other:?}"),
},
Err(e) => return Err(e),
}
}
Err(e) => return Err(e),
}
Ok(())
}An encrypted document opened without a password fails with Error::DecryptionFailed and the reason PasswordRequired. That is deliberate: an open that offered no password has no typo to report, so the library does not say WrongPassword to someone who never gave one.
use pdfluent::error::DecryptionFailureReason;
use pdfluent::{Error, PdfDocument};
match PdfDocument::open("document.pdf") {
Ok(doc) => println!("open: {} pages", doc.page_count()),
Err(Error::DecryptionFailed { reason, .. }) => match reason {
DecryptionFailureReason::PasswordRequired => println!("a password is required"),
other => println!("cannot decrypt: {other:?}"),
},
Err(e) => return Err(e),
}Supply it through OpenOptions. This is the only shape in which WrongPassword can come back, so it is where a wrong candidate is told apart from a file that simply needs one. The user password is enough to read the document; the owner password is what you need to change how it is protected.
use pdfluent::error::DecryptionFailureReason;
use pdfluent::{Error, OpenOptions, PdfDocument};
match PdfDocument::open_with(
"protected.pdf",
OpenOptions::new().with_password("user-password"),
) {
Ok(doc) => println!("{} pages", doc.page_count()),
Err(Error::DecryptionFailed { reason, .. }) => match reason {
DecryptionFailureReason::WrongPassword => println!("wrong password"),
other => println!("cannot open it: {other:?}"),
},
Err(e) => return Err(e),
}Permissions are something you write, not something you read back: build them and hand them to encrypt(). Permissions::print_only, read_only, annotate and full_access are the presets, with_print and friends the fine control.
use pdfluent::{EncryptOptions, Permissions};
let mut doc = PdfDocument::open("document.pdf")?;
doc.encrypt(
EncryptOptions::aes256()
.with_user_password("user-password")
.with_owner_password("owner-password")
.with_permissions(Permissions::print_only()),
)?;
doc.save("restricted.pdf")?;Lock down what readers can do with your PDF. Restrict printing, copying text, editing, and form filling using the PDFluent SDK.
use pdfluent::{PdfDocument, EncryptOptions, Permissions};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("report.pdf")?;
let opts = EncryptOptions::aes256()
.with_owner_password("owner-secret")
.with_permissions(Permissions::print_only());
doc.encrypt(opts)?;
doc.save("report-restricted.pdf")?;
Ok(())
}Open the PDF you want to restrict. If it is already encrypted you must supply the owner password.
let mut doc = PdfDocument::open("input.pdf")?;
// Or, if already encrypted:
// let mut doc = PdfDocument::open_with("input.pdf", OpenOptions::new().with_password("current-owner-pw"))?;Use the Permissions builder to choose what is allowed. All operations default to denied when you use the builder.
use pdfluent::Permissions;
let permissions = Permissions::full_access()
.with_print(false) // block all printing
.with_print_high_quality(false)
.with_copy(false) // block text/image copying
.with_modify(false) // block page editing
.with_annotate(false) // block adding comments
.with_fill_forms(true); // allow form data entryPair the permission set with an encryption config. The owner password lets you change permissions later. The user password is what readers enter to open the file.
use pdfluent::EncryptOptions;
let opts = EncryptOptions::aes256().with_owner_password("owner-secret").with_user_password("user-secret")
.with_permissions(permissions);
doc.encrypt(opts)?;Write the encrypted PDF to disk. The original file is not modified.
doc.save("locked.pdf")?;
println!("Saved locked.pdf");