-
Notifications
You must be signed in to change notification settings - Fork 0
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.
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 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 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 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 |
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 focusThe ->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.
$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.
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.
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.
MIT licensed. Source on GitHub - if phppdf helps you, you can buy me a coffee.