Skip to content

Interactive Forms

Dragon edited this page Jun 3, 2026 · 1 revision

Interactive Forms

PhpPdf supports AcroForm interactive forms. Fields are value objects placed on a page with $page->field(...). Every field type is immutable (final readonly); validation is eager in the constructor or deferred to Document::output().

Placing fields

use DragonOfMercy\PhpPdf\Form\{TextField, Checkbox, Radio, Combobox, Listbox, PushButton, ButtonAction, FieldAppearance};
use DragonOfMercy\PhpPdf\Color;

$pdf  = new \DragonOfMercy\PhpPdf\Document();
$page = $pdf->addPage();

// Single-line text input
$page->field(new TextField(50.0, 50.0, 80.0, 8.0, name: 'firstname', value: 'Bob', required: true));

// Multi-line text area
$page->field(new TextField(50.0, 65.0, 80.0, 24.0, name: 'comment', multiline: true));

// Password field (input masked in the reader)
$page->field(new TextField(x: 20, y: 20, width: 60, height: 8, name: 'pwd', password: true));

// Checkbox
$page->field(new Checkbox(50.0, 95.0, 5.0, 5.0, name: 'agree'));

// Radio group - widgets sharing the same group name form one logical field
$page->field(new Radio(50.0, 110.0, 5.0, 5.0, group: 'civility', value: 'mr',  checked: true));
$page->field(new Radio(50.0, 120.0, 5.0, 5.0, group: 'civility', value: 'mrs'));

// Combobox (dropdown)
$page->field(new Combobox(50.0, 135.0, 80.0, 8.0, name: 'country',
    options: ['fr' => 'France', 'ch' => 'Suisse'], value: 'ch'));

// Listbox (multi-select)
$page->field(new Listbox(50.0, 150.0, 80.0, 24.0, name: 'interests',
    options: ['music', 'sport', 'code'], value: ['sport'], multiSelect: true));

Coordinates x, y, width, and height are in the document's user unit with a top-down Y axis, the same convention as Page::cell().

Field types

TextField

new TextField(float $x, float $y, float $width, float $height, string $name, ...)
Parameter Type Description
value string Initial display value (default '')
multiline bool Multi-line text area; mutually exclusive with password
password bool Masked input; cannot be multiline or have a default value
required bool Field must be filled before submission
readOnly bool Value is locked in the viewer
maxLength ?int Maximum character count (positive)
tooltip ?string Tooltip shown on hover
appearance ?FieldAppearance Visual style
actions ?FieldActions JavaScript triggers
defaultValue ?string Value restored by a ResetForm button (/DV)

Checkbox

new Checkbox(float $x, float $y, float $width, float $height, string $name, ...)
Parameter Type Description
checked bool Initial checked state
required bool Field must be checked before submission
readOnly bool State cannot be changed
tooltip ?string Tooltip shown on hover
appearance ?FieldAppearance Visual style
actions ?FieldActions JavaScript triggers (mouse/focus only)
defaultValue ?bool State restored by a ResetForm button

Radio

new Radio(float $x, float $y, float $width, float $height, string $group, string $value, ...)

group is the shared field name; value is the export value (PDF /AS state name) for this widget. All Radio instances with the same group are emitted as one /Field with /Kids.

Combobox

new Combobox(float $x, float $y, float $width, float $height, string $name, array $options, ...)

$options accepts two shapes:

  • list<string> - ['France', 'Suisse', 'Belgique']
  • array<string, string> - ['fr' => 'France', 'ch' => 'Suisse'] (export value => display label)

value is the initial selection: a label (list shape) or an export key (map shape). Validation against $options is deferred to Document::output(). Set editable: true to allow the user to type a custom value.

Listbox

new Listbox(float $x, float $y, float $width, float $height, string $name, array $options, ...)

Same two-shape $options contract as Combobox. value accepts a string, a list<string>, or null. When multiSelect is false a list longer than one element is rejected at output time.

Styling

All field types accept an appearance: new FieldAppearance(...) parameter.

use DragonOfMercy\PhpPdf\Form\{TextField, FieldAppearance};
use DragonOfMercy\PhpPdf\Color;

$page->field(new TextField(50.0, 50.0, 80.0, 8.0,
    name: 'styled',
    appearance: new FieldAppearance(
        borderColor:     Color::rgb(255, 0, 0),
        borderWidth:     1.0,
        backgroundColor: Color::rgb(240, 240, 240),
    ),
));

FieldAppearance constructor parameters:

Parameter Type Description
borderColor ?Color Widget border color
borderWidth ?float Border width in points (non-negative)
backgroundColor ?Color Widget background fill
textColor ?Color Text color inside the widget
font ?Font Font family for the widget text
fontSize ?float Font size in points (positive)
align TextAlign Text alignment (default LEFT)
hidden bool See advanced options below
noExport bool See advanced options below
borderStyle ?FieldBorderStyle Border shape; see table below

All parameters are optional; null means "use the PDF viewer default."

Advanced field options

Border styles

FieldAppearance accepts a borderStyle: FieldBorderStyle parameter that controls the widget border shape. The five cases map to the PDF /BS /S name:

Case PDF /S Visual
FieldBorderStyle::SOLID /S Plain solid line (default when omitted)
FieldBorderStyle::DASHED /D Dashed line (3-unit dash array)
FieldBorderStyle::BEVELED /B Raised 3-D bevel
FieldBorderStyle::INSET /I Sunken 3-D inset
FieldBorderStyle::UNDERLINE /U Bottom edge only
use DragonOfMercy\PhpPdf\Form\{TextField, FieldAppearance, FieldBorderStyle};
use DragonOfMercy\PhpPdf\TabOrder;

$page->setTabOrder(TabOrder::ROW);

// Beveled border, excluded from form submission, with a default value
$page->field(new TextField(20, 20, 80, 8, name: 'amount', value: '0', defaultValue: '0',
    appearance: new FieldAppearance(
        borderWidth: 1.0,
        borderStyle: FieldBorderStyle::BEVELED,
        noExport: true,
    )));

// Hidden field - present in the form data but not rendered on screen
$page->field(new TextField(20, 40, 80, 8, name: 'token',
    appearance: new FieldAppearance(hidden: true)));

Visibility and submission flags

Two boolean flags on FieldAppearance control special behaviors:

  • hidden: true - the field is present in the PDF object graph and its value participates in the form data, but it is not rendered on screen or printed. Sets the /F annotation Hidden bit (bit 2).
  • noExport: true - the field is visible in the reader but its value is excluded from form submissions triggered by a ButtonAction::submit() button. Sets bit 3 of the /Ff AcroForm flags.

Default value

TextField, Combobox, Listbox, and Checkbox accept a defaultValue parameter. When provided it is stored as the /DV PDF entry and is the value restored when a ResetForm button is clicked, independently of the current display value. When omitted, defaultValue falls back to value (unchanged behavior).

Tab order

Page::setTabOrder(?TabOrder $order) writes a /Tabs entry on the page dictionary, telling compatible readers the order in which the Tab key moves through that page's fields. Pass null to clear the entry (reader default).

use DragonOfMercy\PhpPdf\TabOrder;

$page->setTabOrder(TabOrder::ROW);       // left-to-right, top-to-bottom
$page->setTabOrder(TabOrder::COLUMN);    // top-to-bottom, left-to-right
$page->setTabOrder(TabOrder::STRUCTURE); // document structure order
$page->setTabOrder(null);               // clear (reader default)

TabOrder lives in the root namespace DragonOfMercy\PhpPdf.

Linked fields

Place several fields with the same name and they become one logical field: editing one widget updates all others - the same mechanism radio buttons use to share a group value. The library emits a single parent /Field with one /Kids widget per placement.

use DragonOfMercy\PhpPdf\Form\TextField;

// One "customer" field shown at the top and repeated in the footer
$page->field(new TextField(20, 20,  80, 8, name: 'customer', value: 'ACME'));
$page->field(new TextField(20, 250, 80, 8, name: 'customer'));

Rules:

  • Linking applies to TextField, Checkbox, Combobox, and Listbox. Each placement keeps its own position and FieldAppearance.
  • Field-level properties (value, default value, flags, options, max length, tooltip, JavaScript actions) are taken from the first widget added; later same-name widgets contribute only their position and appearance.
  • Mixing field types under one name, repeating a PushButton name, or repeating a SignatureField name throws a PdfException.
  • Attaching actions: to anything other than the first widget of a linked group also throws.

Push buttons

Push buttons trigger an action on click and hold no value.

use DragonOfMercy\PhpPdf\Form\{PushButton, ButtonAction, FieldAppearance, SubmitFormat};
use DragonOfMercy\PhpPdf\Color;

// Reset every field in the form to its default value
$page->field(PushButton::of(
    x: 20, y: 20, width: 40, height: 10,
    name: 'reset', caption: 'Clear', action: ButtonAction::resetForm(),
));

// Open a URL in the browser
$page->field(PushButton::of(
    x: 70, y: 20, width: 40, height: 10,
    name: 'home', caption: 'Visit', action: ButtonAction::openUrl('https://example.com'),
));

// Submit the form data to a web endpoint (HTML POST)
$page->field(PushButton::of(
    x: 20, y: 35, width: 40, height: 10,
    name: 'send', caption: 'Submit',
    action: ButtonAction::submit('https://example.com/post', SubmitFormat::HTML),
));

// With appearance
$page->field(PushButton::of(
    x: 20, y: 50, width: 40, height: 10,
    name: 'styled_reset', caption: 'Reset',
    action: ButtonAction::resetForm(),
    appearance: new FieldAppearance(
        borderColor: Color::rgb(0, 0, 0),
        borderWidth: 0.5,
        backgroundColor: Color::rgb(230, 230, 230),
    ),
));

PushButton::of() is a named constructor that mirrors the regular new PushButton(...) call. Three ButtonAction constructors are available:

Named constructor Description
ButtonAction::resetForm() Resets all fields to their /DV default values
ButtonAction::openUrl(string $url) Opens a URL in the system browser
ButtonAction::submit(string $url, SubmitFormat $format, bool $get) Submits form data to $url

The format argument of ButtonAction::submit() selects the submission encoding:

SubmitFormat case Encoding
FDF Adobe FDF envelope (default)
HTML application/x-www-form-urlencoded
XFDF XML variant of FDF
PDF The entire PDF document is sent

Pass get: true for an HTTP GET request instead of the default POST. This is mainly useful with SubmitFormat::HTML.

Form submission is carried out by Adobe Acrobat / Adobe Reader. Browser PDF viewers (Chrome, Firefox PDF.js) generally do not submit forms.

Signature fields

A signature field marks the location (and optional appearance) of a digital signature. On its own it is an unsigned placeholder a human can sign later in a desktop reader. Combined with Document::sign() (see Digital Signatures) the library applies a real PKCS#7 / CMS cryptographic signature at output time.

Any document containing at least one signature field automatically emits /SigFlags 3 in the AcroForm dictionary, enabling the signing UI in compatible readers.

use DragonOfMercy\PhpPdf\Form\{SignatureField, FieldAppearance};
use DragonOfMercy\PhpPdf\Color;

// A visible signing box (bordered). Left unsigned - the reader signs it.
$page->field(SignatureField::visible(
    x: 20, y: 20, width: 80, height: 30, name: 'signature',
    appearance: new FieldAppearance(borderColor: Color::rgb(0, 0, 0), borderWidth: 0.5),
));

// An invisible signature field (no on-page rendering)
$page->field(SignatureField::invisible(name: 'approval'));

SignatureField::visible() places a rectangle at (x, y) with the given width and height in the document unit. SignatureField::invisible() creates a zero-size annotation (/Rect [0 0 0 0]) with no visual footprint - useful for metadata-level approval workflows.

Both named constructors accept optional required, readOnly, and tooltip parameters. SignatureField does not support JavaScript actions.

See also

Clone this wiki locally