§HOW-TO GUIDE · C# · .NET 8 & 10

Redact PDF text with a regular expression in C#

PdfRedactionManager finds content by area, by text or by regular expression and deletes it from the file — text, images, links and form fields underneath are removed, not hidden behind a box.

Compiled and run: Linux ARM64 · Linux x64 · macOS ARM64 · Windows x64

Video walkthrough · 2:29 · Watch on YouTube · Short · 0:30
§STEP BY STEP

Build it step by step.

  1. 1Install the NuGet packages

    SelectPdf.Universal is the library. The native package carries the Chromium engine for one platform: add the one for the platform you run on.

    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.win-x64
    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.linux-x64
    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.linux-arm64
    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.osx-arm64
  2. 2Create a console app

    Any .NET 8 or .NET 10 project works. To follow along, create a console app, run the install commands above inside its folder, then replace Program.cs with the code below.

    terminal
    dotnet new console -n PdfSample
    cd PdfSample
  3. 3Import the namespaces

    Program.cs · lines 1–2
    using System.Text.RegularExpressions;
    using SelectPdf.Universal;

    Regex comes from System.Text.RegularExpressions; everything else is in SelectPdf.Universal.

  4. 4Create a PDF to redact

    Program.cs · lines 4–12
    // 1. Make a statement that contains card numbers.
    HtmlToPdf converter = new HtmlToPdf();
    PdfDocument statement = converter.ConvertHtmlString("""
        <h1>Card statement</h1>
        <p>Primary card: 4111-1111-1111-1111</p>
        <p>Backup card: 5500-0000-0000-0004</p>
        """);
    statement.Save("statement.pdf");
    statement.Close();

    To keep the sample self-contained, HtmlToPdf converts a small statement with two card numbers into statement.pdf. In your application this is the PDF you already have — an upload or a report you generated earlier.

  5. 5Load it into a PdfRedactionManager

    Program.cs · lines 14–16
    // 2. Remove every card number from the file.
    var redactor = new PdfRedactionManager();
    redactor.Load("statement.pdf");

    PdfRedactionManager works on an existing PDF. Load opens the file you want to clean.

  6. 6Describe what to remove

    Program.cs · lines 17–17
    redactor.AddRedaction(new Regex(@"\d{4}-\d{4}-\d{4}-\d{4}"));

    AddRedaction with a Regex marks every match in the document. The pattern \d{4}-\d{4}-\d{4}-\d{4} matches four groups of four digits separated by dashes — the format of the two card numbers. Adjust it to your data: national IDs, account numbers, e-mail addresses.

  7. 7Mark where content was removed (optional)

    Program.cs · lines 18–18
    redactor.OverlayText = "REDACTED";

    OverlayText writes a label over each redacted area so readers can see that something was taken out. Leave it unset for a plain box.

  8. 8Apply, save and close

    Program.cs · lines 19–21
    redactor.Redact();
    redactor.Save("statement-redacted.pdf");
    redactor.Close();

    Redact applies all redactions. Save writes the result to a new file, so the original stays untouched, and Close releases the document.

  9. 9Run it

    terminal
    dotnet run

    The program writes statement-redacted.pdf to the working folder. Without a license key, every page carries a trial watermark; set GlobalProperties.LicenseKey before the first conversion to remove it.

§COMPLETE PROGRAM

The complete Program.cs.

This is the exact file our samples pipeline compiled and ran on Linux ARM64 and Linux x64 and macOS ARM64 and Windows x64 to produce the output on this page.

Program.cs
using System.Text.RegularExpressions;
using SelectPdf.Universal;

// 1. Make a statement that contains card numbers.
HtmlToPdf converter = new HtmlToPdf();
PdfDocument statement = converter.ConvertHtmlString("""
    <h1>Card statement</h1>
    <p>Primary card: 4111-1111-1111-1111</p>
    <p>Backup card: 5500-0000-0000-0004</p>
    """);
statement.Save("statement.pdf");
statement.Close();

// 2. Remove every card number from the file.
var redactor = new PdfRedactionManager();
redactor.Load("statement.pdf");
redactor.AddRedaction(new Regex(@"\d{4}-\d{4}-\d{4}-\d{4}"));
redactor.OverlayText = "REDACTED";
redactor.Redact();
redactor.Save("statement-redacted.pdf");
redactor.Close();

Open the project on GitHub → this program and the variations below, ready to dotnet run

§OUTPUT

The PDF it produces.

First page of statement-redacted.pdf, the PDF this sample produces
statement-redacted.pdf, page 1

Download statement-redacted.pdf ↓ the file this sample produced, unedited

§VARIATIONS

Variations you will need next.

Each variation is a separate file in the same project, compiled and run with the program above.

Redact a known name or phrase

AddRedaction(text, caseSensitive, wholeWordsOnly) marks every occurrence of an exact text. FillColor changes the box color from the default black. Redact returns a PdfRedactionResult for each area it removed, with the page and the text, which you can write to an audit log.

RedactKnownText.cs
using SelectPdf.Universal;

static class RedactKnownText
{
    public static void Run()
    {
        HtmlToPdf converter = new HtmlToPdf();
        PdfDocument contract = converter.ConvertHtmlString("""
            <h1>Supply agreement</h1>
            <p>Between Contoso Ltd and Fabrikam Inc.</p>
            <p>Contoso Ltd delivers 400 units per month.</p>
            """);
        contract.Save("contract.pdf");
        contract.Close();

        var redactor = new PdfRedactionManager();
        redactor.Load("contract.pdf");
        redactor.FillColor = new PdfColor(128, 128, 128);

        // exact text: case-sensitive, whole words only
        redactor.AddRedaction("Contoso Ltd", true, true);

        // Redact returns what was removed, for an audit log
        foreach (PdfRedactionResult r in redactor.Redact())
            Console.WriteLine($"page {r.PageIndex + 1}: {r.Text}");

        redactor.Save("contract-redacted.pdf");
        redactor.Close();
    }
}

Redact a fixed area and work in memory

When the position is known, such as a signature box on a fixed form, AddRedaction(pageIndex, rectangle) removes everything inside that rectangle. Coordinates are in points from the top-left corner of the page. Save() without a file name returns the redacted PDF as bytes.

RedactArea.cs
using SelectPdf.Universal;
using SelectPdf.Universal.Drawing;

static class RedactArea
{
    public static void Run()
    {
        HtmlToPdf converter = new HtmlToPdf();
        PdfDocument form = converter.ConvertHtmlString("""
            <h1>Application form</h1>
            <p>Name: Jane Doe</p>
            <p>Signature: ____________________</p>
            """);
        form.Save("form.pdf");
        form.Close();

        var redactor = new PdfRedactionManager();
        redactor.Load("form.pdf");

        // a fixed area on page 1 (x, y, width, height in points from the top-left corner)
        redactor.AddRedaction(0, new RectangleF(0, 60, 300, 50));
        redactor.Redact();

        // in memory: Save() returns the redacted PDF as bytes
        byte[] pdf = redactor.Save();
        redactor.Close();
        File.WriteAllBytes("form-redacted.pdf", pdf);
    }
}
§TROUBLESHOOTING

Common problems and fixes.

The pattern matches nothing

Text is matched as it is stored in the PDF. Test the pattern against the output of the PDF to text converter first. Spacing and line breaks can differ from what you see on screen.

Only some pages should change

Pass page indexes: AddRedaction(regex, new[] { 0, 2 }). Without them the redaction applies to every page and to what the document stores off its pages (properties, bookmarks, annotations, form field text).

The source PDF is password-protected

Use the Load(fileName, password) overload. Load also accepts a stream, a byte array or an open PdfDocument.

§GOOD TO KNOW

Good to know.

  • Redaction removes text, images, links and form fields under the redaction from the file; there is nothing left to copy or extract. Source
  • Redactions can be found by area, by text or by regular expression, with optional overlay text. Source
  • Redaction is part of the commercial SelectPdf.Universal package. Source
?FAQ

Questions, answered.

Short answers about this guide. The SelectPdf.Universal documentation covers every class and option.

It removes it. Text, images, vector graphics, links and form fields inside the redacted areas are deleted from the file, and the area is painted with a fill color. Nothing is left to copy or extract.
Yes. PdfRedactionManager.AddRedaction(Regex) marks every match on every page, for values that follow a pattern such as card numbers, account numbers or e-mail addresses.
Yes. Redact() returns an array of PdfRedactionResult objects with the page index, bounds and text of each removed area.
No. Redaction is part of the commercial SelectPdf.Universal library. The Community Edition is HTML to PDF only.