Pro edition
Form — Deep Reference
At a glance
Section titled “At a glance”This page is the deep reference for the Pro Form module. It covers AcroForm value extraction, XFDF read and write, data binding, and XFA data extraction. The module consumes NextPDF\Form\FormField values produced by the Core form reader and adds serialization, parsing, and binding on top of them. XFA support is data-oriented: the parser structures the template and datasets packets. It does not execute XFA calculation scripts or render dynamic XFA layouts.
Availability & licensing
Section titled “Availability & licensing”This capability ships in NextPDF Pro (nextpdf/pro) and activates with a Pro-tier license envelope. A deployment without that entitlement does not load the capability’s classes. Compare editions and get a license.
No per-feature license flag exists. This is a Pro-edition capability.
Public API surface
Section titled “Public API surface”| Symbol | Parameters | Default behavior | Returns | Throws or fails with | Notes |
|---|---|---|---|---|---|
FormDataExtractor::extract | list<FormField> $fields | Reads each field’s name and value | XfdfData | — | Includes fields whose value is empty. |
FormDataExtractor::toArray | list<FormField> $fields | Builds a name-to-value string map | array<string, string> | — | A later duplicate name overwrites an earlier one. |
FormDataExtractor::toXfdf | list<FormField> $fields, ?string $pdfHref = null | Delegates to XfdfWriter::fromFields | string (XFDF XML) | — | Convenience path for one-call export. |
FormDataExtractor::extractNonEmpty | list<FormField> $fields | Skips fields whose value is the empty string | XfdfData | — | — |
FormDataExtractor::getEmptyFieldNames | list<FormField> $fields | Lists names of fields with no value set | list<string> | — | Complement of extractNonEmpty. |
XfdfWriter::fromFields | list<FormField> $fields, ?string $pdfHref = null | Collects name-to-value pairs, delegates to fromArray | string (XFDF XML) | — | — |
XfdfWriter::fromArray | array<string, string> $data, ?string $pdfHref = null | Wraps the map in XfdfData, delegates | string (XFDF XML) | — | — |
XfdfWriter::fromXfdfData | XfdfData $data, ?string $pdfHref = null | Serializes to XFDF; dot-notation names nest as hierarchical <field> elements | string (XFDF XML) | — | Strips XML-1.0-illegal control characters; see the behavior contract. |
XfdfParser::parse | string $xfdfXml | Loads the XML XXE-safe and flattens fields to dot notation | XfdfData | InvalidArgumentException | 10 MiB input ceiling; accepts namespaced and non-namespaced roots. |
XfdfParser::parseFile | string $filePath | Resolves the path, reads the file, delegates to parse | XfdfData | InvalidArgumentException | Missing, non-file, or unreadable paths raise. |
XfaParser::parse | string $pdfData | Marker check, XML extraction, packet parse | XfaFormData | InvalidArgumentException, XfaParseException | No /XFA marker returns an empty result, not an error. |
XfaParser::hasXfa | string $pdfData | Scans the bytes for the /XFA marker | bool | — | Byte-marker scan; any occurrence of the token matches. |
XfaParser::extractXfaXml | string $pdfData | Stream scan for XFA markers, then direct <xdp:xdp> search | string (XFA XML or '') | RuntimeException (declared) | Scans at most the first 50 MiB of the input. |
XfaParser::parseXml | string $xml | Extracts template and datasets packets, parses <field> elements | XfaFormData | XfaParseException | 10 MiB XML ceiling, enforced before DOM load. |
FormDataBinder::bind | list<FormField> $fields, XfdfData $data | Creates new FormField instances with bound values | FormDataBindResult | — | Originals are never modified; check boxes normalize to Yes/Off. |
FormDataBinder::fromXfdf | list<FormField> $fields, string $xfdfXml | Parses the XFDF, then binds | FormDataBindResult | InvalidArgumentException | Failure modes are those of XfdfParser::parse. |
FormDataBinder::fromArray | list<FormField> $fields, array<string, string> $data | Wraps the map in XfdfData, then binds | FormDataBindResult | — | — |
FormDataBindResult | isFullyBound, hasNoUnmatchedKeys, boundCount, fieldCount; readonly fields, boundFieldNames, unmatchedDataKeys, unboundFieldNames | Immutable bind diagnostics | per method | — | isFullyBound requires zero unmatched keys and zero unbound fields. |
XfdfData | hasField, getValue, count, isEmpty, getFieldNames, withField, withoutField, merge; readonly fields | Immutable name-to-value container | per method | — | with* and merge return new instances; merge prefers the argument’s values. |
XfaFormData | getField, hasField, count, fieldNames; readonly fields, templateXml, datasetsXml | Immutable XFA parse result | per method | — | Carries raw template and datasets packet XML for round-tripping. |
XfaFormField | readonly name, type, value, required, caption, options | Immutable single-field record | — | — | type is one of text, numeric, date, choice, button, signature. |
XfaPacket | enum cases Template, Datasets, Config, LocaleSet, ConnectionSet, Form; xmlNamespace() | String-backed packet enumeration | string from xmlNamespace() | — | Namespace URIs follow the XFA Specification 3.3. |
public static function extract(array $fields): XfdfDatapublic static function toArray(array $fields): arraypublic static function toXfdf(array $fields, ?string $pdfHref = null): stringpublic static function extractNonEmpty(array $fields): XfdfDatapublic static function getEmptyFieldNames(array $fields): arraypublic static function fromFields(array $fields, ?string $pdfHref = null): stringpublic static function fromArray(array $data, ?string $pdfHref = null): stringpublic static function fromXfdfData(XfdfData $data, ?string $pdfHref = null): stringpublic static function parse(string $xfdfXml): XfdfDatapublic static function parseFile(string $filePath): XfdfDatapublic function parse(string $pdfData): XfaFormDatapublic function hasXfa(string $pdfData): boolpublic function extractXfaXml(string $pdfData): stringpublic function parseXml(string $xml): XfaFormDatapublic static function bind(array $fields, XfdfData $data): FormDataBindResultpublic static function fromXfdf(array $fields, string $xfdfXml): FormDataBindResultpublic static function fromArray(array $fields, array $data): FormDataBindResultExceptions
Section titled “Exceptions”NextPDF\Pro\Form\Exception\XfaParseExceptionextendsRuntimeException— the XFA payload cannot be parsed into anXfaFormData. The subclassing is deliberate: existingcatch (RuntimeException $e)call sites keep working.- SPL
InvalidArgumentException— empty, oversized, malformed, or non-XFDF input toXfdfParser; empty PDF input toXfaParser::parse; unreadable paths inXfdfParser::parseFile.
Behavior contract
Section titled “Behavior contract”AcroForm extraction. FormDataExtractor walks the field list you pass and reads each field’s name and value. extract returns an XfdfData; toArray returns a plain name-to-value string map. extractNonEmpty drops fields whose value is the empty string; getEmptyFieldNames returns the complementary name list. Extraction never mutates the input fields.
XFDF write. XfdfWriter produces a document conforming to the ISO 19444-1:2019 structure. Output starts with the XFDF XML declaration and an xfdf root in the Adobe XFDF namespace (http://ns.adobe.com/xfdf/) with xml:space="preserve". A non-null pdfHref emits an <f href="..."/> reference back to the source PDF. Dot-notation field names (for example address.city) nest into a hierarchical <field> element tree. Values and attributes escape the five XML metacharacters. Field names, values, and the pdfHref are additionally normalized for well-formedness: the C0 control characters that XML 1.0 forbids are stripped, while TAB, LF, and CR are preserved. This normalization is lossy by design, so the writer always emits well-formed, re-parseable XFDF regardless of caller-supplied bytes.
XFDF read. XfdfParser accepts both namespaced and non-namespaced xfdf roots and matches the root name case-insensitively, because some producers emit an uppercase root element. Hierarchical <field> trees flatten back to dot-notation names, so write and read round-trip. All XML loading disables network access and external-entity resolution. parseFile adds path resolution and readability checks in front of the same parse.
Data binding. FormDataBinder::bind matches data keys against field names. Because FormField is immutable, binding creates new instances with updated values; the originals are never modified. The result reports three diagnostic sets: bound field names, data keys with no matching field, and fields that received no data. Check-box values normalize to the on/off state model of ISO 32000-2:2020, 12.7.5.2.3: case-insensitive yes, true, 1, and on map to Yes; every other value maps to Off.
XFA data extraction. XfaParser::parse accepts raw PDF bytes. It first scans for the /XFA marker; absent the marker it returns an empty XfaFormData. Extraction then tries two strategies: a scan of stream…endstream blocks for XFA XML indicators, then a direct search for a <xdp:xdp> document. A single xdp:xdp fragment is returned as-is; multiple fragments are concatenated into a synthesized xdp:xdp envelope. parseXml extracts the template and datasets packets and parses each template <field> element into an XfaFormField: the name attribute is required, the type derives from the field’s UI child element, the required flag derives from a validate element with nullTest set to error, and choice options come from items children.
XFA support is data-oriented. The parser structures the template and datasets packets. It does not execute XFA calculation scripts, render dynamic XFA layouts, or round-trip every packet type. Validate the parser against your specific document set before relying on it.
Edge cases & failure modes
Section titled “Edge cases & failure modes”XfdfParser::parse('')throwsInvalidArgumentException. Input above 10 MiB throwsInvalidArgumentExceptionnaming the ceiling.- Malformed XML throws
InvalidArgumentExceptioncarrying the collected libxml messages. A well-formed document whose root is notxfdfthrows and names the actual root element. - An XFDF document without a
<fields>element parses to an emptyXfdfData; that is not an error. - Field elements without a
nameattribute are skipped in both XFDF and XFA parsing. An XFDF field without a<value>child contributes no entry. XfaParser::parse('')throwsInvalidArgumentException. A PDF without the/XFAmarker, or one whose XFA XML cannot be located, returns an emptyXfaFormDatainstead of throwing.hasXfais a byte-marker scan: any/XFAtoken in the file matches, including one in an unused object. The subsequent extraction step decides whether usable XML exists.- XFA extraction examines at most the first 50 MiB of the PDF byte string; content beyond that bound is not scanned.
- XFA XML above 10 MiB throws
XfaParseExceptionbefore any DOM tree is materialized. Malformed XFA XML throwsXfaParseExceptionwith the libxml messages. - Check-box normalization never passes unrecognized values through; anything outside the accepted on-forms maps to
Off. - The writer’s control-character stripping is lossy: XML-1.0-illegal C0 bytes in names, values, or the
pdfHrefare dropped so the output stays well-formed. TAB, LF, and CR survive. - All XML parsing disables external-entity resolution and network access (XXE-safe).
- This module performs no cryptographic operations; FIPS mode does not change its behavior.
Conformance
Section titled “Conformance”| Behavior | Reference | Status |
|---|---|---|
| Interactive form / field dictionary model | ISO 32000-2:2020, 12.7 | Aligned (product-grounded) |
Check-box on/off state normalization (Yes/Off) | ISO 32000-2:2020, 12.7.5.2.3 | Aligned; clause cited in this page’s citation record |
| XFDF data-exchange structure | ISO 19444-1:2019 | Aligned (product-grounded) |
| XFA packet names and namespace URIs | XFA Specification 3.3 | Aligned (product-grounded) |
These statements describe capability against the referenced documents.
Development notes
Section titled “Development notes”- Every entry point except
XfaParseris static.XfaParseris instantiable and stateless; one instance is safe to reuse across documents. - The intended round-trip is: Core form reader produces
FormFieldvalues;FormDataExtractororXfdfWriterserializes them;XfdfParserreads the data back;FormDataBinderapplies it to a field list. Hierarchical names survive the round-trip through dot notation. - Use the
FormDataBindResultdiagnostics (isFullyBound,unmatchedDataKeys,unboundFieldNames) to detect drift between an XFDF data file and a revised PDF template before accepting a fill. XfdfDatais a value object:withField,withoutField, andmergereturn new instances. On key collisions,mergeprefers the argument’s values.XfaFormDataretains the raw template and datasets packet XML (templateXml,datasetsXml) so you can post-process packets the field model does not cover.- This module does not parse AcroForm dictionaries out of PDF bytes itself; it consumes fields produced by the Core form reader. Only
XfaParseroperates on raw PDF content.
Publication boundary
Section titled “Publication boundary”This page documents externally observable behavior and the supported public API surface only. Internal namespace paths, helper classes, mechanism tables, runbook filenames, and ticket prefixes are out of scope.