To flatten a PDF form in C#, open the file with AllowEditing, call doc.Flatten() and save. OnePDF draws each field’s appearance into the page content and removes the fields, and once no fields are left the form itself goes. The values then look the same as before, but nobody can edit them in a viewer, and text extraction reads them like any other text on the page. OnePDF is pure C# with no native binaries, licensed MIT OR Apache-2.0. This page flattens a form you fill, a form someone else already filled, only some fields, and a form that also carries XFA, then shows how to check the result. Every sample was compiled and run against OnePDF 1.11.0 on .NET 10.
Before you start
dotnet new console -o PdfFlatten cd PdfFlatten dotnet add package RecursiveAI.OnePdf --version 1.11.0
OnePDF targets .NET 10 (net10.0). The samples use OPM’s SF 2823, a three-page US government form with 61 fields (text, check boxes, radio buttons, a combo box, signature fields and Print, Save and Clear buttons), and the IRS Form W-9. Save them next to the program as sf2823.pdf and fw9.pdf.
Fill and flatten a form
using OnePdf; // Fill a few fields, then flatten: the values become page content and the form goes. using (PdfDocument doc = PdfDocument.Open("sf2823.pdf", new PdfOpenOptions { AllowEditing = true })) { PdfAcroForm form = doc.Form!; form.SetValue("Name", "Lovelace, Ada"); form.SetValue("DOB", "12/10/1985"); form.SetValue("Type of Insured", "Employee"); PdfFlattenResult result = doc.Flatten(); Console.WriteLine($"drawn: {result.Drawn}, fields removed: {result.FieldsRemoved}, form removed: {result.FormRemoved}"); doc.Save("sf2823-flat.pdf"); } using (PdfDocument flat = PdfDocument.Open("sf2823-flat.pdf")) { Console.WriteLine($"form: {(flat.Form is null ? "none" : "still there")}"); Console.WriteLine($"page 1 has the name: {flat.Pages[0].ExtractText().Text.Contains("Lovelace, Ada")}"); }
drawn: 64, fields removed: 61, form removed: True form: none page 1 has the name: True
Drawncounts widgets, the boxes on the page: a radio group has one per button, so 61 fields have 64 widgets here. Every one was drawn, filled or empty, so the page looks as it did in a viewer.FormRemovedis true because no field was left, so the document’s/AcroFormis gone.- The selected radio button (“an employee”) is drawn checked, and the text values sit in the page content where text extraction, search and indexing find them.
The fill calls are covered in detail in fill PDF form fields in C#.
Flatten a form that is already filled
The form may arrive filled in, from a viewer or another program. Flattening it needs no field names: open, flatten, save. Check for a form first, because a file without one has nothing to flatten:
using OnePdf; // A form that someone already filled in, in a viewer or another program. using PdfDocument doc = PdfDocument.Open("filled.pdf", new PdfOpenOptions { AllowEditing = true }); if (doc.Form is null) { Console.WriteLine("Nothing to flatten: the file has no form."); return; } doc.Flatten(); doc.Save("filled-flat.pdf"); Console.WriteLine($"saved, form left: {doc.Form is not null}");
saved, form left: FalseSome programs fill a form without drawing the values. They set the form’s NeedAppearances flag and leave the drawing to the viewer, so a flattener that only copies stored appearances would bake in empty boxes. OnePDF generates the missing appearances while it flattens, the same way it draws a page, so the values are drawn anyway. To flatten a PDF that arrives as an upload, open it from its bytes with PdfDocument.Open(bytes, options) or TryOpen, as in the merge article’s ASP.NET Core sample.
Flatten only some fields
Pass the fully qualified names of the fields to bake in. The rest of the form stays fillable:
using OnePdf; using PdfDocument doc = PdfDocument.Open("sf2823.pdf", new PdfOpenOptions { AllowEditing = true }); doc.Form!.SetValue("Name", "Lovelace, Ada"); doc.Form!.SetValue("DOB", "12/10/1985"); // Lock the insured's details; leave the rest of the form fillable. PdfFlattenResult result = doc.Flatten(new PdfFlattenOptions { FieldNames = ["Name", "DOB"] }); doc.Save("sf2823-partly-flat.pdf"); Console.WriteLine($"fields removed: {result.FieldsRemoved}, form removed: {result.FormRemoved}"); Console.WriteLine($"fields left: {doc.Form!.TerminalFields.Count()}, Name still a field: {doc.Form!.FindField("Name") is not null}");
fields removed: 2, form removed: False fields left: 59, Name still a field: False
A name also covers every field below it in the field tree, and an unknown name throws PdfFormValueException. doc.Form is rebuilt by Flatten, so read it again afterwards, as the last line does.
Leave out Print, Save and Clear buttons
Flattening draws what a viewer shows, and SF 2823 shows Print, Save and Clear buttons at the bottom of page 1. Once flattened, they would be pictures of buttons that do nothing. SF 2823 doesn’t mark these buttons for printing, so flattening for print leaves them out:
using OnePdf; using PdfDocument doc = PdfDocument.Open("sf2823.pdf", new PdfOpenOptions { AllowEditing = true }); doc.Form!.SetValue("Name", "Lovelace, Ada"); // Draw only what the form marks as printable: on-screen buttons such as Print and Clear are removed, not drawn. PdfFlattenResult result = doc.Flatten(new PdfFlattenOptions { Visibility = PdfAnnotationMode.Print }); doc.Save("sf2823-flat-print.pdf"); Console.WriteLine($"drawn: {result.Drawn}, removed: {result.Removed}");
drawn: 61, removed: 64All 64 widgets are removed, but only the 61 marked for printing are drawn: the three buttons are gone from the page. The default, PdfAnnotationMode.View, draws what is visible on screen.
Forms that also contain XFA
Some forms, the IRS W-9 among them, carry two descriptions of the same form: AcroForm fields and an XFA form. OnePDF fills and flattens the AcroForm fields. When flattening removes the last field, the whole form goes, XFA included, so no viewer can show a stale XFA version of the form:
using OnePdf; using PdfDocument doc = PdfDocument.Open("fw9.pdf", new PdfOpenOptions { AllowEditing = true }); Console.WriteLine($"XFA before: {doc.Form!.HasXfa}"); doc.Form!.SetValue("topmostSubform[0].Page1[0].f1_01[0]", "Example Supplies LLC"); doc.Flatten(); doc.Save("fw9-flat.pdf"); Console.WriteLine($"form after: {(doc.Form is null ? "none" : "still there")}");
XFA before: True form after: none
XFA-only forms, with no AcroForm fields, can’t be filled or flattened with OnePDF.
What flattening does and doesn’t do
- Signed signature fields are kept with their widgets, since flattening them would delete the signature.
PdfFlattenResult.SignedKeptcounts them. Empty signature fields are flattened like any other field. - Comments and stamps stay as annotations by default. Set
IncludeMarkup = trueinPdfFlattenOptionsto draw them into the page too. Links and pop-ups are never flattened. - Encrypted documents can be flattened. New objects are encrypted like the rest.
- Scripts never run. A calculated field keeps the value it has. OnePDF doesn’t recompute totals.
- It is not rasterizing. Text stays text and vector art stays vector. If you need pages turned into images, render them with OneImage.Pdf.
- It can’t be undone. A flattened field is page content, and there is no field left to restore. Keep the fillable original if you may need to edit it again.
Every rule, including how each appearance is placed on rotated pages, is in forms and appearance generation.
Check that the form is flat
$ pdfinfo sf2823-flat.pdf | grep Form Form: none $ pdftotext -f 1 -l 1 sf2823-flat.pdf - | grep Lovelace Lovelace, Ada $ qpdf --check sf2823-flat.pdf | grep "No syntax" No syntax or stream encoding errors found; the file may still contain
pdfinfo reports Form: AcroForm or Form: XFA for a file that still has a form, and none once it is flat.
The same with iText or PDFsharp
In iText, PdfAcroForm.FlattenFields “flattens interactive form fields in the document”, and PartialFormFlattening(name) restricts it to the fields you name. Written from iText’s API reference and run with iText 9.8.0 (AGPL or commercial):
using iText.Forms; using iText.Kernel.Pdf; using PdfDocument pdf = new PdfDocument(new PdfReader("sf2823.pdf"), new PdfWriter("flat-itext.pdf")); PdfAcroForm form = PdfAcroForm.GetAcroForm(pdf, true); form.GetField("Name").SetValue("Lovelace, Ada"); form.GetField("DOB").SetValue("12/10/1985"); form.FlattenFields(); // or PartialFormFlattening("Name") first, to flatten only that field
The output has no form left, and page 1’s text includes both values. For licensing, see iText license cost and AGPL terms.
PDFsharp’s documentation doesn’t describe a flatten call, and its FAQ says “There is limited support for AcroForms included with PDFsharp 6.1.” If you’re moving PDFsharp code, see migrating from PDFsharp and PDFsharp license changes.
Common questions
How do I fully flatten a PDF?
Flatten the form fields and the other annotations: doc.Flatten(new PdfFlattenOptions { IncludeMarkup = true }). Everything except links, pop-ups and signed signatures becomes page content.
Can a flattened PDF be unflattened?
No. After flattening, the values are drawn into the page and the fields no longer exist. Keep the original if you might need the fillable version.
Should I flatten before merging forms?
If the merged file is for printing or archiving, yes: flatten each copy, then merge. If it has to stay fillable, merge without flattening. OnePDF renames clashing field names so each copy keeps its own values (see merge PDF files in C#).
Where next
- Fill and flatten form fields, the cookbook recipe.
- Fill PDF form fields in C#: every field kind, and catching bad values.
- Sign and verify a flattened document, or save it as PDF/A for archiving.