Table of Contents

Interactive form fields

A PDF does not have to be read-only. TTMSFNCPDFLib can place AcroForm fields on a page — text edits, check boxes, radio groups, combo and list boxes — so the generated document can be filled in by the recipient in any standard reader and returned. Use it for application forms, expense claims, consent sheets, and anything where the alternative is printing, filling by hand, and scanning back.

Note

Form fields are supported on Windows and Linux only. On other platforms the document still generates; the fields are simply not produced, so do not rely on them for a cross-platform deliverable without testing the target.

Adding fields

Add form fields via p.FormFields:

Type Method
Text edit AddEdit
Multiline text edit AddMemo
Password edit AddPasswordEdit
Check box AddCheckBox
Radio group AddRadioGroup
Radio button AddRadioButton
Combo box AddComboBox
List box AddListBox

Each field requires a unique name — it is the key the reader submits the value under, so duplicates silently collapse into one value. All fields except radio groups also require a TRectF position, in the same page coordinates as any drawing call.

p.FormFields.AddEdit('Textfield1', RectF(10, 10, 210, 32), 'Default text', gtaLeading);
p.FormFields.AddCheckBox('Checkbox1', RectF(10, 50, 30, 70), False);
p.FormFields.AddComboBox('Combobox1', RectF(10, 90, 70, 110), ['item1', 'item2', 'item3'], 1);

Mutually exclusive radio buttons

A radio group is the one field type that does not take a rectangle, because it is a logical container rather than a drawn control: create the group by name with AddRadioGroup, then add each AddRadioButton into it with its own rectangle. Buttons in the same group are mutually exclusive; buttons that accidentally end up in different groups are not, which is the usual cause of "my radio buttons can all be selected at once".

Field fonts

FormFields.Font sets the default font for all fields, and individual fields can override it. Two switches are worth knowing:

Property Effect
FontAutoSize When True, scales the font to fit the field bounds
IgnoreFonts When True, suppresses font embedding for form fields

FontAutoSize is the practical answer to a field whose default text is clipped: rather than shrinking the font globally, let each field size its own. IgnoreFonts reduces file size when the reader's standard fonts are acceptable for the fillable areas — the rest of the document keeps its embedded fonts.

Labelling fields

Form fields carry no caption of their own. Draw the label as ordinary text next to the field rectangle, the same way you would label anything else on the page — see Text and fonts. Keep the label and the field rectangle in one place in the code so they cannot drift apart when the layout changes.

Common mistakes

  • Reusing a field name. Names must be unique across the document; a duplicate makes two controls share one value.
  • Expecting fields on macOS/iOS/Android. Windows and Linux only — see the note above.
  • Radio buttons that are not mutually exclusive. They are only exclusive within one AddRadioGroup.
  • Clipped default text. Set FontAutoSize := True on the field rather than reducing the document font.
  • Expecting a caption. Fields are unlabelled; draw the text yourself.

Putting it together

A complete fillable page: a shared field font, drawn labels beside every control, and one of each field family — edit, combo box, memo, check box, and a radio group with two buttons.

procedure TForm1.BuildFormPage(const AFileName: string);
var
  p: TTMSFNCPDFLib;
begin
  p := TTMSFNCPDFLib.Create;
  try
    p.Header := '';
    p.Footer := '';
    p.BeginDocument(AFileName);
    try
      p.NewPage;

      { One font for every field; individual fields may still override it.
        FontAutoSize keeps long default text from being clipped. }
      p.FormFields.Font.Name := 'Segoe UI';
      p.FormFields.Font.Size := 10;
      p.FormFields.FontAutoSize := True;

      p.Graphics.Font.Name := 'Segoe UI';
      p.Graphics.Font.Size := 14;
      p.Graphics.DrawText('Expense claim', PointF(40, 40));
      p.Graphics.Font.Size := 10;

      { Fields carry no caption of their own - the label is ordinary drawn
        text, kept next to the field it belongs to. }
      p.Graphics.DrawText('Employee', PointF(40, 88));
      p.FormFields.AddEdit('Employee', RectF(140, 84, 400, 106), '', gtaLeading);

      p.Graphics.DrawText('Cost centre', PointF(40, 120));
      p.FormFields.AddComboBox('CostCentre', RectF(140, 116, 300, 138),
        ['Engineering', 'Sales', 'Support', 'Operations'], 0);

      p.Graphics.DrawText('Description', PointF(40, 152));
      p.FormFields.AddMemo('Description', RectF(140, 148, 400, 220), '');

      p.Graphics.DrawText('Receipt attached', PointF(40, 236));
      p.FormFields.AddCheckBox('ReceiptAttached', RectF(140, 232, 160, 252), False);

      { A radio group is a logical container with no rectangle of its own: it
        is declared with its own field name AND a group id, and every button
        then joins that group id. Each button still needs its own unique field
        name - buttons are only mutually exclusive within one group id. }
      p.Graphics.DrawText('Payment', PointF(40, 268));
      p.FormFields.AddRadioGroup('Payment', 'PaymentGroup');
      p.FormFields.AddRadioButton('PaymentTransfer', RectF(140, 264, 160, 284),
        'PaymentGroup', True);
      p.Graphics.DrawText('Bank transfer', PointF(166, 268));
      p.FormFields.AddRadioButton('PaymentCash', RectF(300, 264, 320, 284),
        'PaymentGroup', False);
      p.Graphics.DrawText('Cash', PointF(326, 268));
    finally
      p.EndDocument;
    end;
  finally
    p.Free;
  end;
end;

See also