Documents opened with PdfOpenOptions.AllowEditing = true can have their form fields filled and their annotations baked into the page. Both work with Save and SaveIncremental, and on encrypted documents (new objects are encrypted like the rest). Field scripts (/AA calculate, format, validate, keystroke) are data: they never run, and values are stored exactly as given.
using var doc = PdfDocument.Open("form.pdf", new PdfOpenOptions { AllowEditing = true }); PdfAcroForm form = doc.Form!; form.SetValue("applicant.name", "Ada Lovelace"); // text form.SetValue("country", "Norway"); // combo or list: export value or display text form.SetValues("interests", ["art", "music"]); // multi-select list box form.SetChecked("agree", true); // check box form.SetValue("size", "M"); // radio group: on-state name or /Opt export value doc.SaveIncremental(File.Create("filled.pdf")); doc.Flatten(); // bake everything into the page, drop the form doc.Save("flat.pdf");
Filling
PdfFormField.SetValue, SetValues, SetChecked and Reset, and the same by fully qualified name on PdfAcroForm (plus ResetAll and GenerateMissingAppearances). A value that cannot be stored throws PdfFormValueException (an ArgumentException) with a Reason and changes nothing:
| Reason | When |
|---|---|
UnknownField | no field has that fully qualified name |
WrongType | the call does not fit the field (a check for a text field, a value for a push button or signature) |
ReadOnly | the field has the read-only flag; PdfFillOptions.OverrideReadOnly fills it anyway |
NotInOptions | the value is not in /Opt (combo, list), or not an on-state of the widgets (check box, radio) |
TooLong | a text value is longer than /MaxLen |
NotMultiSelect | several values for a field that is not a multi-select list box |
NotTerminal | the field has child fields; fill the leaves |
A document that is not editable throws InvalidOperationException; a value larger than PdfLimits.MaxMetadataBytes throws PdfLimitException.
- Text (ISO 32000-2 12.7.4.3):
/Vbecomes a text string (PDFDocEncoding when possible, else UTF-16BE) and a stale/RVis removed./MaxLencounts Unicode code points; a longer value is refused, or cut to the first/MaxLencode points withTruncateToMaxLength. Comb fields, multi-line, password and/Qalignment are drawn from the flags by the appearance generator. A value ofnullremoves/V. - Combo and list boxes (12.7.4.4): the value is matched against the export values of
/Opt, then against display texts;/Vstores the export value. An editable combo box (flag bit 19) accepts any text. A multi-select list box takes several values:/Vis an array and/Ithe selected indices, both in option order, duplicates dropped. Entries of/Optthat are neither strings nor two-string arrays are ignored. - Check boxes (12.7.4.2.3): the value is an on-state name (a key of a widget's
/AP /Nother thanOff),Off, or null;SetChecked(true)uses the first widget's on-state, orYeswhen no appearance names one./Vand every widget's/ASare set (widgets that have the state go on, others off). - Radio buttons (12.7.4.2.4): as for check boxes, by state name; when
/Optis present the state names are indices and the option text is accepted as the value. Only the first widget with the state turns on, unless the radios-in-unison flag (bit 26) is set, which turns on every widget that has it.OfforSetChecked(false)turns all off. - Appearances: for each widget the normal appearance is built by the appearance generator (
docs/appearance-generation.md) and stored as a Form XObject in/AP /N(text and choice: replaced; buttons: added under the on-state when the stored dictionary has no stream for it; stored art for the state is kept). Fonts named by/DAcome from/DR; built-in fonts are stored once per form as indirect objects.BBoxandMatrixare rounded to four decimals like the generated content.PdfFillOptions.GenerateAppearances = falsechanges only/Vand/AS. /NeedAppearancesis cleared only when every widget has a normal appearance (a button that is off needs none). When that moment arrives, text and choice fields not filled by the caller are rebuilt first, so clearing the flag never makes an old appearance authoritative.GenerateMissingAppearances()stores whatever is missing and applies the same rule.- Reset sets
/Vto the inherited/DV(or removes it; off for buttons). A default that is not among the options is stored as it is.ResetAllskips read-only fields unless overridden, push buttons and signatures. - Pages,
Formand other views taken before an edit are not refreshed by value changes to the same objects;FlattenrebuildsPagesandForm, so take them again afterwards.
Flattening
PdfDocument.Flatten(PdfFlattenOptions?) draws the normal appearance of widgets (and, with IncludeMarkup, of every annotation except links and pop-ups) into the page and removes the annotations.
- Placement (12.5.5): the appearance's
/Matrix-transformed/BBoxis fitted onto/Rect; the result isq a b c d e f cm /Name Do Qin a content stream appended to the page. The original content is wrapped inq…Qso a leaked graphics state cannot move the art. A stored appearance stream is used as the XObject itself (/Subtype /Formis added when missing); a missing one is generated in memory, exactly aspage.Interpretdoes (including the/NeedAppearancesrule), and stored.NoRotateon a rotated page keeps the art upright, pinned at the upper left corner of/Rect;NoZoomhas no meaning in a page. - Visibility:
Visibility = View(default) draws what is not Hidden and not NoView;Printdraws what has the Print flag and is not Hidden. Annotations hidden by optional content are not drawn. Annotations not drawn are still removed. - Selection:
Fields(default true) andFieldNames(fully qualified names, each with the fields under it; null means all, including widgets outside the field tree).IncludeMarkupadds the other annotation types; hidden ones are removed too. - Fields: flattened leaves leave the field tree and
/CO; parents left without children go too;/AcroFormis dropped when no field is left (PdfFlattenResult.FormRemoved). Signed signature fields stay, with their widgets (SignedKept). - Pop-ups go with their parent annotation. Structure-tree entries pointing at removed widgets are left as they are.
- The flattened page interprets as the original page with its annotations, except that flattened art is drawn under annotations that are kept, and text extraction now includes the field values.
Limits
Everything is bounded before work: values by PdfLimits.MaxMetadataBytes, value lists and widget counts by PdfLimits.MaxObjects, drawn text and items by the generator's limits (MaxAppearanceText, MaxAppearanceItems); the field tree is walked iteratively with cycle detection and the nesting limit; copying of font dictionaries is iterative and counted. Names are looked up in a dictionary built once, so filling a form of 100,000 fields is linear.