Redact PDF text, images, and patterns in Rust

This guide shows you how to use the PDFluent SDK to permanently remove sensitive content from your PDF documents. It is for developers who need to implement secure and compliant redaction.

Add PDFluent to your project

Add the pdfluent crate to Cargo.toml. No native dependencies are required.

toml
[dependencies]
pdfluent = "1.0.0-beta.18"

Permanently redact text and images from a PDF in Rust

Remove sensitive content from PDF files with black-box redaction. PDFluent removes the underlying text and image data, not just paints over it.

rust
use pdfluent::{PdfDocument, RedactOptions};

fn main() -> pdfluent::Result<()> {
    let mut doc = PdfDocument::open("contract.pdf")?;
    doc.redact("CONFIDENTIAL", RedactOptions::new())?;
    doc.save("redacted.pdf")?;
    Ok(())
}
  1. Open the PDF document

    Load the PDF you want to redact. Use open() for files on disk or from_bytes() for in-memory data.

    rust
    use pdfluent::PdfDocument;
    
    let mut doc = PdfDocument::open("sensitive_report.pdf")?;
  2. Mark text regions for redaction

    Call redact_text() with the exact string to remove. PDFluent searches all pages and marks every occurrence. The text is not yet removed at this step.

    rust
    use pdfluent::RedactOptions;
    
    // Remove specific strings (search + redact across all pages)
    doc.redact("Alice Johnson", RedactOptions::new())?;
    doc.redact("account: 4111-1111-1111-1111", RedactOptions::new())?;
    
    // Redact a page region by coordinates [x1, y1, x2, y2] (points, from bottom-left)
    doc.redact_region(0, [50.0, 700.0, 300.0, 720.0])?;
  3. Configure the redaction fill

    The default fill is solid black. You can change the fill colour or add a label such as "REDACTED" over the box.

    rust
    use pdfluent::RedactOptions;
    
    // Scope a redaction to specific pages, case-insensitive
    let opts = RedactOptions::new().case_sensitive(false).on_pages(&[0, 1]);
    
    doc.redact("SSN: 123-45-6789", opts)?;
  4. Apply redactions and save

    Call apply_redactions() to permanently remove the marked content from the data stream. Then save the file. After this step the content cannot be recovered.

    rust
    // redact() / redact_region() apply in place — write the sanitised file
    doc.save("report_redacted.pdf")?;
    
    println!("All redactions applied and saved.");
  • apply_redactions() removes text from the content stream. Simply drawing a black box does not remove the underlying text.
  • Redaction applies to all pages unless you use redact_text_on_page() to target a specific page index.
  • Scanned PDFs contain images, not text. Use redact_region() with pixel coordinates for those pages.
  • Always verify the output in a PDF viewer before distributing redacted documents.

Redact text matching a regex pattern from a PDF in Rust

Use regular expressions to find and permanently remove credit card numbers, SSNs, email addresses, or any structured data from a PDF.

rust
use pdfluent::{PdfDocument, RedactOptions};

fn main() -> pdfluent::Result<()> {
    let mut doc = PdfDocument::open("data.pdf")?;
    doc.redact(r"\d{3}-\d{2}-\d{4}", RedactOptions::new().regex(true))?;
    doc.save("redacted.pdf")?;
    Ok(())
}
  1. Open the PDF

    Load the file from disk or from an in-memory buffer.

    rust
    use pdfluent::PdfDocument;
    
    let mut doc = PdfDocument::open("customer_data.pdf")?;
  2. Define your regex patterns

    Write patterns for the data types you want to remove. PDFluent uses the Rust regex crate syntax.

    rust
    // Credit card: Visa, Mastercard, Amex formats
    let cc_pattern = r"\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13})\b";
    
    // Email addresses
    let email_pattern = r"[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}";
    
    // US phone numbers
    let phone_pattern = r"\b\(?\d{3}\)?[\s.\-]?\d{3}[\s.\-]?\d{4}\b";
  3. Apply pattern-based redaction

    Call redact_pattern() for each regex. You can chain multiple patterns before calling apply_redactions().

    rust
    use pdfluent::RedactOptions;
    
    let opts = RedactOptions::new().regex(true);
    
    doc.redact(cc_pattern, opts.clone())?;
    doc.redact(email_pattern, opts.clone())?;
    doc.redact(phone_pattern, opts)?;
  4. Apply and save

    apply_redactions() permanently removes all matched text from the content stream.

    rust
    // Pattern redactions apply in place — write the cleaned file
    doc.save("customer_data_clean.pdf")?;
    
    println!("Pattern redaction complete.");
  • PDFluent uses the Rust regex crate. Patterns are case-sensitive by default. Use (?i) for case-insensitive matching.
  • Text in PDFs may include ligatures or kerning gaps. If a pattern does not match expected text, extract the raw text first to inspect the actual character sequence.
  • Pattern redaction works on text content streams only. Text inside images requires OCR before redaction.
  • Call get_redaction_marks() after redact_pattern() to preview what will be removed before applying.

GDPR-compliant PDF redaction in Rust

Permanently remove personal data from PDF content streams — not just visually. A black-box overlay leaves text extractable; PDFluent removes it from the object layer.

rust
use pdfluent::{PdfDocument, RedactOptions};

fn main() -> pdfluent::Result<()> {
    let mut doc = PdfDocument::open("records.pdf")?;
    doc.redact("[email protected]", RedactOptions::new())?;
    doc.redact_region(0, [72.0, 700.0, 320.0, 720.0])?;
    doc.save("redacted.pdf")?;
    Ok(())
}
  1. Identify what needs to be redacted

    Determine which personal data is present in the document. PDFluent supports regex-based pattern matching, manual region selection, and full-text search. Common patterns include social security numbers, names, email addresses, phone numbers, and financial identifiers.

    rust
    use pdfluent::{PdfDocument, RedactOptions};
    
    let mut doc = PdfDocument::open("contract_with_pii.pdf")?;
    
    // Regex match — redact all occurrences across all pages
    let re = RedactOptions::new().regex(true);
    doc.redact(r"\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b", re.clone())?; // email
    doc.redact(r"\b\d{3}-\d{2}-\d{4}\b", re.clone())?; // US SSN
    doc.redact(r"\b\d{4}[\s\-]?\d{4}[\s\-]?\d{4}[\s\-]?\d{4}\b", re)?; // credit card
  2. Mark regions for redaction

    Marking creates redaction annotations over the identified content. At this stage the content is still present — marks are reversible until you call apply_redactions(). This lets you review and adjust the selection before committing.

    rust
    // Redact a specific region by coordinates [x1, y1, x2, y2] (page index first)
    doc.redact_region(0, [72.0, 600.0, 300.0, 620.0])?;
    
    // Or redact an exact text string
    doc.redact("Jan de Vries", pdfluent::RedactOptions::new())?;
  3. Apply redactions — permanently removes content from the content stream

    apply_redactions() modifies the PDF content stream directly. Text and image data in marked regions is removed from the object layer, not just hidden. After this step the content cannot be recovered, even by a PDF parser operating on the raw bytes.

    rust
    use pdfluent::RedactOptions;
    
    // RedactOptions controls matching. redact() / redact_region() mutate the
    // document in place — there is no separate "apply" step.
    let _opts = RedactOptions::new().case_sensitive(false).regex(true);
  4. Verify: confirm the content is gone

    After applying redactions, extract the text and check that the personal data is absent. This step is important for audit purposes and to confirm the redaction worked as intended before delivering or storing the document.

    rust
    // Verify: extracted text must not contain the redacted values
    let text = doc.text()?;
    assert!(!text.contains("Jan de Vries"), "Redaction failed: name still present");
    assert!(!text.contains("123-45-6789"), "Redaction failed: SSN still present");
    
    println!("Verification passed — PII removed from content stream");
  5. Clear metadata and save to a new file

    PDF metadata (Author, Title, Subject, Keywords) and XMP metadata may contain PII independently of the page content. Clear it before saving. Keep the original file for your audit trail if required by your data retention policy.

    rust
    // Clear document Info metadata that may carry PII
    doc.metadata_mut().set_title("").set_author("").set_subject("").commit()?;
    
    // Save to a new path — keep the original for audit
    doc.save("contract_redacted.pdf")?;
    
    println!("Redacted document saved.");
  • Redaction via apply_redactions() is irreversible. Always work on a copy of the original document.
  • A black rectangle drawn as a PDF annotation or content stream overlay does NOT constitute GDPR-compliant redaction. The underlying text remains in the content stream and can be extracted with any PDF parser.
  • Annotations, bookmarks, form fields, and JavaScript may also contain PII. Review these separately after redacting page content.
  • GDPR Article 17 requires erasure "without undue delay" — in practice, supervisory authorities interpret this as within one month of a valid request.
  • For CCPA compliance (California Consumer Privacy Act), the same principle applies: visual hiding is not deletion. Use apply_redactions() to permanently remove data from the content layer.