Open the form with editing allowed, call SetValue or SetChecked with each field’s name, and save. The program below fills in the IRS Form W-9, a public fillable PDF you can download to try it.
dotnet new console -o FillForm && cd FillForm 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:
using OnePdf; using PdfDocument doc = PdfDocument.Open("fw9.pdf", new PdfOpenOptions { AllowEditing = true }); PdfAcroForm form = doc.Form ?? throw new InvalidOperationException("No form here."); // Fields are found by their full names. Every field of the W-9 starts with this. const string page1 = "topmostSubform[0].Page1[0]."; // 1: name form.SetValue(page1 + "f1_01[0]", "Ada Lovelace"); // 3a: individual form.SetChecked(page1 + "Boxes3a-b_ReadOrder[0].c1_1[0]", true); // 5: address form.SetValue(page1 + "Address_ReadOrder[0].f1_07[0]", "100 Main Street"); form.SetValue(page1 + "Address_ReadOrder[0].f1_08[0]", "Springfield, IL 62701"); // A value that doesn't fit is refused, and nothing is changed. try { form.SetValue(page1 + "f1_11[0]", "1234"); // the first SSN box takes 3 digits } catch (PdfFormValueException e) { Console.WriteLine($"Refused: {e.Reason}"); } // The W-9 also carries an XFA copy of the form, which OnePDF doesn't fill. // Remove it, so viewers such as Adobe Reader show the values set above. form.Dictionary.RemoveEntry("XFA"); doc.Save("fw9-filled.pdf"); Console.WriteLine($"Name: {form.FindField(page1 + "f1_01[0]")!.Value}");
Refused: TooLong Name: Ada Lovelace
fw9-filled.pdf shows the name, the checked box and the address in any viewer, because OnePDF draws each filled field’s appearance and stores it in the file. It is still a form: anyone can change the values. To make them permanent, flatten it.
Find out which field is which
Field names are often codes like f1_01. List them, then fill every text field with its own name and open the copy: each box shows what to call it.
using OnePdf; using PdfDocument doc = PdfDocument.Open("fw9.pdf", new PdfOpenOptions { AllowEditing = true }); PdfAcroForm form = doc.Form!; foreach (PdfFormField field in form.TerminalFields) { string max = field.MaxLength is int n ? $", max {n}" : ""; Console.WriteLine($"{field.FullName} ({field.Kind}{max})"); if (field.Kind == PdfFieldKind.Text) { string label = field.PartialName.Replace("[0]", ""); field.SetValue(label.Length <= (field.MaxLength ?? 99) ? label : "#"); } } form.Dictionary.RemoveEntry("XFA"); doc.Save("fw9-names.pdf");
topmostSubform[0].Page1[0].f1_01[0] (Text) topmostSubform[0].Page1[0].f1_02[0] (Text) topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].c1_1[0] (CheckBox) topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].c1_1[1] (CheckBox) topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].c1_1[2] (CheckBox) topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].c1_1[3] (CheckBox) topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].c1_1[4] (CheckBox) topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].c1_1[5] (CheckBox) topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].f1_03[0] (Text, max 1) topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].c1_1[6] (CheckBox) topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].f1_04[0] (Text) topmostSubform[0].Page1[0].Boxes3a-b_ReadOrder[0].c1_2[0] (CheckBox) topmostSubform[0].Page1[0].f1_05[0] (Text) topmostSubform[0].Page1[0].f1_06[0] (Text) topmostSubform[0].Page1[0].Address_ReadOrder[0].f1_07[0] (Text) topmostSubform[0].Page1[0].Address_ReadOrder[0].f1_08[0] (Text) topmostSubform[0].Page1[0].f1_09[0] (Text) topmostSubform[0].Page1[0].f1_10[0] (Text) topmostSubform[0].Page1[0].f1_11[0] (Text, max 3) topmostSubform[0].Page1[0].f1_12[0] (Text, max 2) topmostSubform[0].Page1[0].f1_13[0] (Text, max 4) topmostSubform[0].Page1[0].f1_14[0] (Text, max 2) topmostSubform[0].Page1[0].f1_15[0] (Text, max 7)
In fw9-names.pdf, box 1 reads “f1_01”, box 5 “f1_07” and so on. A check box or radio button is set with SetChecked(name, true), or with SetValue(name, state) where the state is one of the names in its widgets’ /AP /N dictionary, here 1 to 7.
What each kind of field takes
| Field | Call | Value |
|---|---|---|
| Text | SetValue(name, text) | Any text up to its maximum length; null clears it. |
| Check box | SetChecked(name, true) | Or SetValue(name, state) with an on-state, or Off. |
| Radio group | SetValue(name, state) | One of the group’s on-states. |
| Combo or list box | SetValue(name, choice) | An option’s export value or display text. |
| Multi-select list | SetValues(name, choices) | Several options. |
A refused value throws PdfFormValueException with a Reason: UnknownField, WrongType, ReadOnly, NotInOptions, TooLong, NotMultiSelect or NotTerminal. PdfFillOptions can fill read-only fields or cut text to the maximum length instead.
Limits
- Scripts never run. Calculated fields aren’t recomputed and format scripts aren’t applied: values are stored exactly as you give them, so format dates and totals yourself.
- XFA isn’t filled. OnePDF fills AcroForm fields. A form that is XFA only, with no AcroForm fields, can’t be filled. On hybrid forms like the W-9, remove the XFA as above.
- No new fields. OnePDF fills the fields a form has; it doesn’t add fields.
- Signature fields are signed, not filled: see Sign a PDF.
Every rule for each field type is in Forms, and how values are drawn in Appearance generation.