Skip to content

Form JavaScript Actions

Dragon edited this page Jun 3, 2026 · 1 revision

Form JavaScript Actions

Fields can carry JavaScript behaviors via a FieldActions value object passed as the actions: parameter. Three categories of helpers are provided: Format (controls how a value is displayed after the user leaves the field), Calculate (computes a field value from other named fields), and Validate (rejects out-of-range input). Document-level scripts run once when the PDF opens and are registered with $pdf->addDocumentScript().

Important: these JavaScript behaviors execute only in Adobe Acrobat / Adobe Reader. Browser PDF viewers (Chrome, Firefox PDF.js) and most mobile viewers ignore them entirely. The field keeps its raw typed value in those environments.

Basic usage

use DragonOfMercy\PhpPdf\Form\TextField;
use DragonOfMercy\PhpPdf\Form\Action\{FieldActions, Format, Calculate, Validate};

// Range-validated, integer-formatted quantity
$page->field(new TextField(20, 20, 40, 8, name: 'qty',
    actions: FieldActions::new()
        ->format(Format::number(0))
        ->validate(Validate::range(0, 999))));

// Currency-formatted unit price
$page->field(new TextField(20, 32, 40, 8, name: 'price',
    actions: FieldActions::new()->format(Format::currency('EUR', 2))));

// Read-only total = qty * price, shown as currency. Recalculated by Acrobat.
$page->field(new TextField(20, 44, 40, 8, name: 'total', readOnly: true,
    actions: FieldActions::new()
        ->calculate(Calculate::product(['qty', 'price']))
        ->format(Format::currency('EUR', 2))));

// Script run when the document opens
$pdf->addDocumentScript('init', 'console.println("ready");');

FieldActions::new() returns an empty, immutable container. Each chained method (->format(), ->calculate(), ->validate()) returns a new instance with the trigger added. Setting the same trigger twice throws a PdfException.

Format

Format controls how a value is displayed after the user leaves the field. Calling ->format(Format::...) on a FieldActions attaches both the Keystroke (K) and Format (F) triggers simultaneously.

use DragonOfMercy\PhpPdf\Form\Action\Format;

Format::number(int $decimals = 2, bool $thousands = true, int $negStyle = 0)
Format::currency(string $symbol, int $decimals = 2, bool $prepend = false)
Format::percent(int $decimals = 2, bool $thousands = false)
Format::date(string $pattern = 'mm/dd/yyyy')
Format::time(string $pattern = 'HH:MM')
Format::custom(string $keystrokeJs, string $formatJs)
Method Description
Format::number(...) Numeric value; $decimals decimal places, optional thousands separator, $negStyle 0-3 for negative style
Format::currency(...) Like number but appends (or prepends with $prepend: true) a currency symbol such as 'EUR' or '$'
Format::percent(...) Percentage value; multiplies by 100 for display
Format::date(...) Date string; $pattern is an Adobe AFDate_FormatEx pattern (e.g. 'mm/dd/yyyy', 'dd.mm.yyyy')
Format::time(...) Time string; $pattern must be one of 'HH:MM', 'h:MM tt', 'HH:MM:ss', 'h:MM:ss tt'
Format::custom(...) Raw JavaScript for the Keystroke and Format triggers; both strings must be non-empty

Calculate

Calculate computes a field value from other named fields. Calculated fields are re-evaluated in the order they appear in the document whenever any field value changes.

use DragonOfMercy\PhpPdf\Form\Action\Calculate;

Calculate::sum(array $fields)
Calculate::product(array $fields)
Calculate::average(array $fields)
Calculate::min(array $fields)
Calculate::max(array $fields)
Calculate::custom(string $js)
Method Description
Calculate::sum($fields) Sum of the named fields
Calculate::product($fields) Product of the named fields
Calculate::average($fields) Arithmetic mean of the named fields
Calculate::min($fields) Minimum value across the named fields
Calculate::max($fields) Maximum value across the named fields
Calculate::custom($js) Arbitrary JavaScript; sets event.value

$fields is a list<string> of field names. Field names are not validated against the declared form; a missing name yields no value at run time, matching Adobe behavior. At least one field name is required.

Validate

Validate rejects a value when the user leaves the field if it does not satisfy the constraint.

use DragonOfMercy\PhpPdf\Form\Action\Validate;

Validate::range(?float $min, ?float $max)
Validate::custom(string $js)
Method Description
Validate::range($min, $max) Accepts values in [$min, $max]; pass null for an open-ended bound; at least one bound is required
Validate::custom($js) Arbitrary JavaScript; set event.rc = false to reject the value

Raw triggers

FieldActions exposes direct access to every PDF /AA trigger for cases where the structured helpers are not enough.

FieldActions::new()
    ->keystroke(string $js)       // K - each keystroke
    ->onMouseEnter(string $js)    // E - cursor enters the widget
    ->onMouseExit(string $js)     // X - cursor leaves the widget
    ->onMouseDown(string $js)     // D - mouse button pressed inside the widget
    ->onMouseUp(string $js)       // U - mouse button released inside the widget
    ->onFocus(string $js)         // Fo - field receives focus
    ->onBlur(string $js)          // Bl - field loses focus

The ->format(), ->calculate(), and ->validate() methods take the Format, Calculate, and Validate value objects above (not raw strings). Use the ::custom() escape hatch on each helper for arbitrary JavaScript.

Document-level scripts

$pdf->addDocumentScript(string $name, string $js) registers a script in the PDF /Names /JavaScript name tree. The script runs once when the document is opened.

$pdf->addDocumentScript('init', 'app.alert("Document loaded");');
$pdf->addDocumentScript('helpers', 'function fmt(v) { return v.toFixed(2); }');

Each call appends one entry to the name tree; duplicate names are not checked.

Field type restrictions

Value triggers (calculate, format, validate, keystroke) are only valid on text fields, comboboxes, and listboxes. Applying them to checkboxes, radio buttons, or push buttons throws a PdfException at Document::output() time.

Mouse and focus triggers (onMouseEnter, onMouseExit, onMouseDown, onMouseUp, onFocus, onBlur) are valid on any field type.

Acrobat / Reader only caveat

All JavaScript actions in this section are executed exclusively by Adobe Acrobat and Adobe Reader. Browser PDF viewers (Chrome built-in, Firefox PDF.js) and most mobile PDF apps ignore the /AA dictionary entirely. Do not rely on these behaviors for security-critical validation; always validate submitted data server-side.

See also

Clone this wiki locally