A practical guide for Rust developers. Learn how to add encryption with permissions, perform proper redaction, apply digital signatures, and process files on your server.
Apply 128-bit RC4 or 256-bit AES encryption. Set user and owner passwords. Restrict printing, copying, and editing per PDF spec.
rust
use pdfluent::{PdfDocument, EncryptOptions, Permissions};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("contract.pdf")?;
let opts = EncryptOptions::aes256()
.with_user_password("view-only")
.with_owner_password("owner-secret-42")
.with_permissions(Permissions::print_only());
doc.encrypt(opts)?;
doc.save("contract-encrypted.pdf")?;
println!("Document encrypted with AES-256");
Ok(())
}
Which encryption algorithm should I use?
Use AES-256 unless you need to support PDF viewers older than Acrobat 9 (2008). RC4-128 is provided for compatibility only. AES-256 is the current PDF spec recommendation and is required for PDF 2.0 compliance.
Does encryption affect file size?
Minimally. The encrypted content is the same size as the unencrypted content because AES-256 in PDF operates in CBC mode with no padding overhead per stream. The /Encrypt dictionary and key derivation data add roughly 200-400 bytes total.
Can I remove encryption from a document I own?
Yes. Open the document with PdfDocument::open_with(path, OpenOptions::new().with_password(...)) using the owner password, then save with SaveOptions that omit encryption. The saved file will have no /Encrypt dictionary.
Are permission flags enforced by PDFluent?
PDFluent writes the correct permission bits into the PDF. Enforcement is the responsibility of the PDF viewer. Well-behaved viewers like Adobe Acrobat and most commercial readers honor these flags. PDFluent itself does not block operations based on permissions when you have the owner password.
Redact it right. Not just visually.
True PDF redaction that permanently removes content from the content stream — not just the visible layer.
rust
use pdfluent::{PdfDocument, RedactOptions};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("sensitive.pdf")?;
doc.redact("CONFIDENTIAL", RedactOptions::new())?;
doc.redact(r"\d{3}-\d{2}-\d{4}", RedactOptions::new().regex(true))?;
doc.save("redacted.pdf")?;
Ok(())
}
Why is content stream redaction important?
Most PDF redaction tools place a black rectangle over content — the underlying text, images, and metadata remain in the file and can be recovered. PDFluent operates on the content stream, cryptographically erasing the original data. The redacted content is genuinely gone.
Can embedded images be redacted?
Yes. PDFluent's redaction removes embedded images that intersect with redacted regions. The remove_embedded_images option ensures that content in images is also permanently removed.
What metadata is stripped?
PDFluent strips all document metadata: author, creator, modification date, PDF comments, XMP metadata, embedded files, and thumbnail images. The redaction report documents what was removed for compliance purposes.
Can I batch-redact thousands of documents?
Yes. PDFluent supports parallel batch processing with regex patterns applied across thousands of documents. Each redaction generates a per-document audit report in JSON format.
Digital signatures. That actually verify.
Sign with a PKCS#12 identity and verify the signatures already on a document. The signer is a trait, so the private key can stay wherever you keep it.
rust
use pdfluent::{PdfDocument, Pkcs12Signer, SignOptions};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("contract.pdf")?;
let signer = Pkcs12Signer::from_pfx_file("signing.p12", "cert-password")?;
let opts = SignOptions::new()
.reason("Approved")
.location("Amsterdam, NL")
.contact_info("[email protected]");
doc.sign(&signer, opts)?;
doc.save("contract-signed.pdf")?;
let report = doc.verify_signatures()?;
println!("All signatures valid: {}", report.all_valid());
Ok(())
}
Which PAdES profile does a signature get?
PAdES B-B — a basic CAdES-detached signature, which is what SignOptions::new() defaults to. B-T and B-LT are produced as well, and both need a DER-encoded RFC 3161 timestamp token passed to SignOptions::timestamp_token: there is no HTTP client in the signing path, so PDFluent never collects one itself. Ask for one of those without a token and the signature is refused rather than written as B-B under a name it has not earned.
What happens when a signing certificate expires?
A B-B signature carries no embedded revocation data or timestamp, so validation after expiry depends on the verifier's own trust store and on evidence gathered elsewhere. B-LT is the profile that fixes it: supply a timestamp token and the revocation evidence travels inside the file.
Can the private key stay in an HSM?
PdfSigner is a trait: implement it against an HSM, a cloud KMS or a PKCS#11 token and the key material never reaches the SDK, because sign() takes any implementation. Pkcs12Signer, which loads a .p12 or .pfx from disk, is the one implementation that ships — there is no built-in integration with a specific HSM or key vault.
Can multiple people sign the same document?
Yes. Each call to sign() adds a signature to the document as it stands, so parties can sign one after another, and verify_signatures() reports on every signature it finds.
PDF processing on your own server.
PDFluent runs entirely in-process. No HTTP calls to external services unless you turn on an optional cloud OCR or timestamping feature, no telemetry, no license server checks at runtime. Your documents stay inside your infrastructure.
rust
use pdfluent::prelude::*;
fn main() -> Result<()> {
// On-premise setup: no licence key, no phone-home, no telemetry.
// All processing is fully in-process.
// Licensing: free and complete under the AGPLv3; a yearly licence
// covers closed-source use. https://pdfluent.com/docs/licensing
let doc = PdfDocument::open("invoice.pdf")?;
println!("Opened document with {} pages", doc.page_count());
Ok(())
}
Does PDFluent phone home?
No. PDFluent makes zero network calls at runtime. No outbound request is made during document processing. You can verify this by running PDFluent in a network-namespace-isolated process or behind a firewall that logs all outbound connections.
Can I use PDFluent in an air-gapped network?
Yes. The binary has no dependency on external network resources, update servers, or telemetry endpoints. All functionality is available without any internet access.
Does it work in a Docker container with no internet access?
Yes. Start the container with --network none and PDFluent works normally. There is no licence key file to mount and no check to satisfy: nothing in the SDK looks for a licence at runtime.
How does this differ from PDF SaaS APIs like Adobe PDF Services or pdfRest?
SaaS PDF APIs process your documents on their servers. Your PDF bytes travel over the internet to their infrastructure, are stored temporarily during processing, and may be logged for debugging. With PDFluent, your documents never leave your process. There is no third-party server involved at any point.
Does PDFluent collect telemetry or crash reports?
No. PDFluent does not collect usage telemetry, crash reports, feature usage statistics, or any other data. This is verifiable by inspecting network traffic or reviewing the source of the SDK components.
What happens if my server has no outbound internet access at all?
Nothing changes. PDFluent operates entirely within the process. There is no background thread attempting to reach an update server or license endpoint. Processing is fully local.
The PDF library written in Rust.
US and EU regulators are actively pushing organizations away from C-based PDF dependencies. PDFluent is written in Rust.
rust
use pdfluent::PdfDocument;
// The Result type makes error handling explicit — no segfaults possible.
// Buffer overflows and use-after-free bugs are eliminated by the Rust compiler.
fn process_pdf(path: &str) -> anyhow::Result<()> {
// open() returns Result<Document, Error> — never a null pointer.
let doc = PdfDocument::open(path)?;
let page_count = doc.page_count();
let title = doc.metadata().title().unwrap_or("(no title)");
println!("Opened: {} ({} pages)", title, page_count);
// doc is dropped here — memory is freed deterministically.
// No garbage collector, no memory leak, no use-after-free.
Ok(())
}
What is the EU Cyber Resilience Act and why does it matter for PDF libraries?
The EU Cyber Resilience Act (CRA) is an EU regulation that entered into force in December 2024, with main obligations applying from December 2027. It requires software products placed on the EU market to be "secure by design" — meaning manufacturers must apply security engineering practices throughout the product lifecycle, including selecting memory-safe languages where feasible. If your application processes PDFs as part of a product sold in the EU, your PDF library choice is part of your CRA compliance posture. A C-based PDF library with an active CVE history is a harder argument to make to auditors than a library written in Rust.
Which US government guidance recommends memory-safe languages?
Multiple US government bodies have issued guidance on memory-safe languages. CISA and NSA jointly recommended transitioning away from C and C++ to memory-safe languages beginning in 2022. The White House Office of the National Cyber Director (ONCD) published "Back to the Building Blocks: A Path Toward Cybersecure Software" in February 2024, explicitly naming Rust as a memory-safe language and calling on the software industry to adopt such languages. CISA published a further joint guide on memory-safe languages in June 2025. PDFluent is written in Rust.
Do C-based PDF libraries have a CVE history?
Yes. libpoppler, MuPDF, Ghostscript, and PDFium all have CVE records on file — including buffer overflows, use-after-free vulnerabilities, and heap corruption bugs that are direct consequences of manual memory management in C and C++. These vulnerability classes are ruled out in safe Rust, where the compiler rejects the underlying bug patterns.
Does this mean PDFluent is completely secure?
No. Memory safety eliminates the most common class of critical vulnerabilities in PDF processors — buffer overflows, use-after-free, and null pointer dereferences — but it does not eliminate all security risks. Logic bugs, incorrect cryptography, or improper input validation can still exist in any library regardless of language. PDFluent still needs to be used correctly, kept up to date, and integrated with proper input validation in your application. What Rust eliminates is an entire category of low-level memory bugs that have historically been responsible for remote code execution CVEs in C-based PDF libraries.