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

Flatten a PDF form in C#

Load the form with PdfFormManager, fill the fields, set Flatten to true and save: the values become ordinary page content and the form fields are removed, so the result can no longer be edited in a PDF viewer.

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

Video walkthrough · 3:33 · Watch on YouTube · Short · 1:03
§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. 3Create a fillable form from HTML

    Program.cs · lines 3–15
    // 1. Create a fillable form: CreateFormFields turns the HTML controls into PDF form fields.
    HtmlToPdf converter = new HtmlToPdf();
    converter.Options.CreateFormFields = true;
    PdfDocument doc = converter.ConvertHtmlString("""
        <h1>Membership application</h1>
        <form>
          <p>Name: <input name="name" style="width:300px"></p>
          <p>Email: <input type="email" name="email" style="width:300px"></p>
          <p>Plan: <select name="plan">
            <option>Basic</option><option>Premium</option></select></p>
          <p><input type="checkbox" name="terms"> I accept the terms</p>
        </form>
        """);

    Options.CreateFormFields = true turns each HTML control into a PDF form field named after the control: two text boxes, a combo box and a check box. In your application the form is usually a PDF you already have; the last variation below loads one from a file.

  4. 4Fill the fields

    Program.cs · lines 17–23
    // 2. Fill the fields by name.
    PdfFormManager form = new PdfFormManager();
    form.Load(doc);
    ((PdfFormFieldTextBox)form.Fields["name"]).Text = "Jane Doe";
    ((PdfFormFieldTextBox)form.Fields["email"]).Text = "jane.doe@example.com";
    ((PdfFormFieldComboBox)form.Fields["plan"]).SelectedValue = "Premium";
    ((PdfFormFieldCheckBox)form.Fields["terms"]).Checked = true;

    PdfFormManager.Load accepts a PdfDocument, a file, a stream or a byte array. Fields["name"] finds a field by name; cast it to its type to set Text, SelectedValue or Checked.

  5. 5Flatten and save

    Program.cs · lines 25–30
    // 3. Flatten the whole form and write a new, compact file.
    form.Flatten = true;
    form.CompactSave = true;
    form.Save("application-flattened.pdf");
    form.Close();
    doc.Close();

    Flatten = true turns every field into page content when the document is saved. CompactSave = true writes a new file rather than appending the changes to the original, so the earlier, fillable version of the form is not kept inside the file.

  6. 6Check the result

    Program.cs · lines 32–36
    // 4. Check: the flattened file has no form fields left.
    PdfFormManager check = new PdfFormManager();
    check.Load("application-flattened.pdf");
    Console.WriteLine($"Form fields left: {check.Fields.Count}");
    check.Close();

    Loading the flattened file again prints Form fields left: 0: a flattened document has no form, and Fields is an empty collection. The values are still on the page, as text.

  7. 7Run it

    terminal
    dotnet run

    The program writes application-flattened.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 SelectPdf.Universal;

// 1. Create a fillable form: CreateFormFields turns the HTML controls into PDF form fields.
HtmlToPdf converter = new HtmlToPdf();
converter.Options.CreateFormFields = true;
PdfDocument doc = converter.ConvertHtmlString("""
    <h1>Membership application</h1>
    <form>
      <p>Name: <input name="name" style="width:300px"></p>
      <p>Email: <input type="email" name="email" style="width:300px"></p>
      <p>Plan: <select name="plan">
        <option>Basic</option><option>Premium</option></select></p>
      <p><input type="checkbox" name="terms"> I accept the terms</p>
    </form>
    """);

// 2. Fill the fields by name.
PdfFormManager form = new PdfFormManager();
form.Load(doc);
((PdfFormFieldTextBox)form.Fields["name"]).Text = "Jane Doe";
((PdfFormFieldTextBox)form.Fields["email"]).Text = "jane.doe@example.com";
((PdfFormFieldComboBox)form.Fields["plan"]).SelectedValue = "Premium";
((PdfFormFieldCheckBox)form.Fields["terms"]).Checked = true;

// 3. Flatten the whole form and write a new, compact file.
form.Flatten = true;
form.CompactSave = true;
form.Save("application-flattened.pdf");
form.Close();
doc.Close();

// 4. Check: the flattened file has no form fields left.
PdfFormManager check = new PdfFormManager();
check.Load("application-flattened.pdf");
Console.WriteLine($"Form fields left: {check.Fields.Count}");
check.Close();

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

§OUTPUT

The PDF it produces.

First page of application-flattened.pdf, the PDF this sample produces
application-flattened.pdf, page 1

Download application-flattened.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.

Flatten only some fields

Set Flatten on individual fields instead of on the whole form. Here the member's name and number are filled in and flattened, so they can no longer be changed, while the plan and the terms check box stay fillable for the member to complete.

FlattenSomeFields.cs
using SelectPdf.Universal;

static class FlattenSomeFields
{
    public static void Run()
    {
        HtmlToPdf converter = new HtmlToPdf();
        converter.Options.CreateFormFields = true;
        PdfDocument doc = converter.ConvertHtmlString("""
            <h1>Membership renewal</h1>
            <form>
              <p>Member: <input name="name" style="width:300px"></p>
              <p>Member number: <input name="number" style="width:300px"></p>
              <p>Plan: <select name="plan">
                <option>Basic</option><option>Premium</option></select></p>
              <p><input type="checkbox" name="terms"> I accept the terms</p>
            </form>
            """);

        PdfFormManager form = new PdfFormManager();
        form.Load(doc);

        // fill in what you already know and flatten only those fields
        PdfFormField name = form.Fields["name"];
        ((PdfFormFieldTextBox)name).Text = "Jane Doe";
        name.Flatten = true;

        PdfFormField number = form.Fields["number"];
        ((PdfFormFieldTextBox)number).Text = "M-10442";
        number.Flatten = true;

        // "plan" and "terms" stay fillable for the member
        form.Save("renewal-prefilled.pdf");
        form.Close();
        doc.Close();
    }
}

Flatten the form, then protect the file

GetDocument returns the flattened form as a PdfDocument, so security settings apply to it before it is saved. An owner password with CanEditContent = false turns off editing in viewers that honour the permissions, and setting a password is enough to encrypt the file with AES-256. Add a UserPassword as well if the content must stay confidential.

FlattenAndProtect.cs
using SelectPdf.Universal;

static class FlattenAndProtect
{
    public static void Run()
    {
        HtmlToPdf converter = new HtmlToPdf();
        converter.Options.CreateFormFields = true;
        PdfDocument doc = converter.ConvertHtmlString("""
            <h1>Expense claim</h1>
            <form>
              <p>Employee: <input name="employee" style="width:300px"></p>
              <p>Amount: <input name="amount" style="width:150px"></p>
            </form>
            """);

        PdfFormManager form = new PdfFormManager();
        form.Load(doc);
        ((PdfFormFieldTextBox)form.Fields["employee"]).Text = "Jane Doe";
        ((PdfFormFieldTextBox)form.Fields["amount"]).Text = "412.50";
        form.Flatten = true;

        // GetDocument returns the flattened result as a PdfDocument, ready for security settings
        PdfDocument flattened = form.GetDocument();
        string ownerPassword = Environment.GetEnvironmentVariable("PDF_OWNER_PASSWORD") ?? "owner-change-me";
        flattened.Security.OwnerPassword = ownerPassword;
        flattened.Security.CanEditContent = false;
        flattened.Save("expense-claim-protected.pdf");

        flattened.Close();
        form.Close();
        doc.Close();
    }
}

Fill and flatten an existing PDF file

Load takes the path of a fillable PDF you already have. Fill it, set Flatten and CompactSave, and save the result under a new name, which leaves the original form untouched for the next use.

FlattenExistingFile.cs
using SelectPdf.Universal;

static class FlattenExistingFile
{
    public static void Run()
    {
        // a fillable PDF that already exists (here made on the spot)
        HtmlToPdf converter = new HtmlToPdf();
        converter.Options.CreateFormFields = true;
        PdfDocument blank = converter.ConvertHtmlString("""
            <h1>Delivery note</h1>
            <form>
              <p>Received by: <input name="receiver" style="width:300px"></p>
              <p><input type="checkbox" name="complete"> Delivery complete</p>
            </form>
            """);
        blank.Save("delivery-note.pdf");
        blank.Close();

        // load the file, fill it and flatten it into a new file
        PdfFormManager form = new PdfFormManager();
        form.Load("delivery-note.pdf");
        ((PdfFormFieldTextBox)form.Fields["receiver"]).Text = "John Smith";
        ((PdfFormFieldCheckBox)form.Fields["complete"]).Checked = true;
        form.Flatten = true;
        form.CompactSave = true;
        form.Save("delivery-note-final.pdf");
        form.Close();
    }
}
§TROUBLESHOOTING

Common problems and fixes.

The PDF created from HTML has no form fields

Set converter.Options.CreateFormFields = true before converting; without it the controls are printed as they look and nothing can be filled in. The controls are read by a script, so JavaScriptEnabled must stay on. Hidden, file, image and button inputs never become fields.

Fields["..."] does not find a field

A converted field takes the name of its control, or its id when the control has no name, and a name used twice gets a suffix. Enumerate form.Fields and print each field's Name to see the names in your document.

The form was digitally signed

CompactSave writes a new document, and a signature the document carried no longer verifies. Leave CompactSave off when a signature has to survive: the default incremental save keeps the signed bytes.

The values must not be editable, but the form must remain a form

Set ReadOnly on the fields, or on the whole PdfFormManager, instead of flattening. Read-only fields keep their values and stay form fields.

§GOOD TO KNOW

Good to know.

  • By default the controls of an HTML form are printed as they look on the page. With CreateFormFields set to true, every form control becomes a fillable PDF form field placed over the control as it was printed. Source
  • Each converted field takes the name of its control, or its id when the control has no name. Hidden, file, image and button inputs are not converted. Source
  • PdfFormManager.Flatten flattens the whole form on save; PdfFormField.Flatten flattens a single field. Source
  • After the form is flattened and saved, the document has no AcroForm, and Fields returns an empty collection rather than null. Source
  • By default Save performs an incremental update: the loaded document is kept unchanged and the changes are appended after it. CompactSave writes a new document instead, with only what is still reachable. Source
  • GetDocument returns the current form state as a new PdfDocument for further processing, such as adding pages, merging or applying security. Source
?FAQ

Questions, answered.

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

Load the PDF with PdfFormManager, set Flatten to true and call Save. The values of the fields become page content and the form fields are removed. Set CompactSave to true to write a new file rather than appending the changes to the original.
Yes. Set Flatten on the individual PdfFormField objects you want to flatten, such as form.Fields["name"].Flatten = true. Fields you leave alone stay fillable.
Load the saved file with PdfFormManager and read Fields.Count. A flattened document has no form, so Fields is an empty collection and Count is 0.
Yes. Set converter.Options.CreateFormFields to true: text inputs, text areas, check boxes, radio buttons and select lists become fillable PDF form fields, which PdfFormManager can then fill and flatten.