Recursive AI

Flatten a PDF in C#

All onepdf recipes

Open the PDF with editing allowed, call doc.Flatten() and save. Each field’s value is drawn into the page and the fields are removed, so the values look the same but can no longer be edited. The program below fills in and flattens the IRS Form W-9, a public fillable PDF you can download to try it.

Terminal
dotnet new console -o FlattenPdf && cd FlattenPdf
dotnet add package RecursiveAI.OnePdf --version 1.14.0
curl -LO https://www.irs.gov/pub/irs-pdf/fw9.pdf

Put this in Program.cs and run dotnet run:

C#
using OnePdf;
 
using (PdfDocument doc = PdfDocument.Open("fw9.pdf",
    new PdfOpenOptions { AllowEditing = true }))
{
    // Fill a field first, or flatten a form someone else filled: no names needed.
    doc.Form!.SetValue("topmostSubform[0].Page1[0].f1_01[0]", "Ada Lovelace");
 
    PdfFlattenResult result = doc.Flatten();
    doc.Save("fw9-flat.pdf");
    Console.WriteLine($"Drawn: {result.Drawn}, removed: {result.FieldsRemoved}");
}
 
using PdfDocument flat = PdfDocument.Open("fw9-flat.pdf");
Console.WriteLine($"Form left: {flat.Form is not null}");
string text = flat.Pages[0].ExtractText().Text;
Console.WriteLine($"Name in the page text: {text.Contains("Ada Lovelace")}");
Text
Drawn: 23, removed: 23
Form left: False
Name in the page text: True

Every field was drawn, filled or empty, so the page looks as it did in a viewer. With no fields left, the form itself is gone, including the W-9’s XFA copy. “Ada Lovelace” is now ordinary page text, which search and text extraction find.

Flatten only some fields

Pass the full names of the fields to lock. The rest stay fillable:

C#
using OnePdf;
 
using PdfDocument doc = PdfDocument.Open("fw9.pdf",
    new PdfOpenOptions { AllowEditing = true });
const string name = "topmostSubform[0].Page1[0].f1_01[0]";
doc.Form!.SetValue(name, "Ada Lovelace");
 
PdfFlattenResult result = doc.Flatten(new PdfFlattenOptions { FieldNames = [name] });
doc.Save("fw9-name-locked.pdf");
int left = doc.Form!.TerminalFields.Count();
Console.WriteLine($"Fields removed: {result.FieldsRemoved}, still fillable: {left}");
Text
Fields removed: 1, still fillable: 22

A name also covers the fields below it in the field tree, and an unknown name throws PdfFormValueException. Flatten rebuilds doc.Form, so read it again afterwards, as the last line does.

Other options

  • Comments and stamps too. new PdfFlattenOptions { IncludeMarkup = true } draws markup annotations into the page as well. Links and pop-ups are never flattened.
  • Only what prints. Visibility = PdfAnnotationMode.Print leaves out fields the form marks as screen-only, such as Print and Clear buttons.
  • Forms filled elsewhere. Some programs store a value without drawing it and leave that to the viewer. OnePDF draws the missing appearances while it flattens, so those values show too.

Limits

  • It can’t be undone. A flattened value is page content with no field left to restore. Keep the fillable original.
  • It isn’t an image. Text stays text and vector art stays vector. To turn pages into pictures, render them with OneImage.
  • Signed signature fields are kept, since flattening them would delete the signature. Flatten before you sign.
  • XFA-only forms have no AcroForm fields to flatten.
  • Scripts never run, so a calculated total keeps whatever value it has.

To fill the fields first, see Fill a PDF form. Every rule is in Forms.