A claims team ships a Delphi-generated consent form that looks flawless in Acrobat: every checkbox renders, every caption aligns, the layout matches the paper original. Three weeks later the intake service starts rejecting a third of all submissions. The back-end expects the consent checkbox to post the value Y; the form exports Yes, because whoever built it assumed the caption and the export value were the same thing. Nothing on the rendered page hints at the difference. That is the defining property of AcroForm development: the visible widget and the data contract underneath it are separate structures, and only one of them is verified when somebody opens the file and looks at it.
UK teams should align this hotpdf acroform actions delphi fields workflow with local governance, audit, and data quality requirements before production release
HotPDF is a native VCL component that writes AcroForm field dictionaries directly from Delphi and C++Builder code, so both structures are under explicit programme control — and both can be wrong independently. The sections below cover field creation, button actions, and field-level JavaScript, with emphasis on the points where the page preview and the form data silently diverge.
Field names are routing keys, not captions
Every AcroForm field carries a fully qualified name, and ISO 32000-1 §12.7.3 defines that name — never the visible caption — as the key under which the field's value travels in FDF, XFDF, and HTTP form submissions. Two consequences follow that routinely surprise developers moving from VCL form design, where a control's name is little more than an identifier in code.
Firstly, two fields with the same fully qualified name are not two fields. The PDF model treats them as two widget annotations of a single field, sharing a single value: type into one and the other updates immediately. Repeating a customer name on every page of a contract is a legitimate use of this behaviour. Getting it by accident, because a generation loop reused 'Field1' across three pages, is a bug no visual review catches — each page still displays its own widget, and the mirroring only appears once a user starts typing.
Secondly, dotted names such as applicant.email build a hierarchy: the parent node applicant groups its children for reset and submit operations that target only part of a form. Naming fields hierarchically from the outset costs nothing and pays off the first time the receiving system asks for 'just the applicant block.'
Radio buttons add a third rule: buttons that should toggle as one group must share a group name. In HotPDF, AddRadioButton calls that use the same group name attach their widgets to one parent field, and each button's export value — 'basic', 'full' — identifies the selected option. Give every button a unique name instead and you get independent on/off switches instead of a mutually exclusive group.
Creating the field set page by page
HotPDF places fields through THPDFPage methods, so every field belongs to the page object that created it. One sequencing trap dominates here: AddPage immediately repoints CurrentPage at the new page, so any field call issued afterwards lands on the new page even if it logically belongs to the previous one. Build each page completely — drawn content and fields together — before calling AddPage.
procedure BuildClaimForm(Pdf: THotPDF);
begin
// Page 1: applicant block
Pdf.CurrentPage.AddTextField('applicant.name', '', Rect(50, 700, 300, 722));
Pdf.CurrentPage.AddTextField('applicant.email', '', Rect(50, 660, 300, 682));
Pdf.CurrentPage.AddCheckBox('consent', 'Y', Rect(50, 620, 70, 640), False);
Pdf.CurrentPage.AddRadioButton('coverage', 'basic', Rect(50, 580, 70, 600), True);
Pdf.CurrentPage.AddRadioButton('coverage', 'full', Rect(90, 580, 110, 600), False);
Pdf.CurrentPage.AddComboBox('plan', 'Standard',
['Basic', 'Standard', 'Premium'], Rect(50, 540, 200, 565));
Pdf.AddPage; // CurrentPage now points at page 2
Pdf.CurrentPage.AddListBox('riders', 'None',
['None', 'Flood', 'Earthquake'], Rect(50, 500, 200, 600));
end;
Coordinates follow the PDF convention: the origin is the bottom-left corner of the page, the same convention TextOut uses for drawn text. Rect(50, 100, 200, 120) therefore sits near the bottom of a Letter page, not the top. Teams porting layout tables from VCL, where Y grows downward from the top, reliably produce a first draft with every field vertically mirrored. Convert coordinates in one shared helper instead of at every call site, so the fix lands everywhere in one pass.
Wiring buttons to URI, JavaScript, and submit actions
A push button does nothing until an action is attached to it. HotPDF exposes the action types of ISO 32000-1 §12.6.4 through the THPDFButtonAction enumeration — baURI, baJavaScript, baSubmitURL, baResetForm, baHide, baShow, baNamed — plus two methods that create the button and bind its action in a single step.
// Open a help page in the system browser
Pdf.CurrentPage.AddPushButtonWithAction('btnHelp', 'Help',
'https://www.example.com/claims-help', Rect(320, 700, 420, 730), baURI);
// Run viewer-side JavaScript
Pdf.CurrentPage.AddPushButtonWithAction('btnRecalc', 'Recalculate',
'app.alert("Totals updated.");', Rect(320, 660, 420, 690), baJavaScript);
// Submit as XFDF and keep empty fields in the payload
Pdf.CurrentPage.AddPushButtonWithSubmitAction('btnSubmit', 'Submit claim',
'https://api.example.com/claims', Rect(320, 620, 420, 650),
[sffXFDF, sffIncludeNoValueFields]);
The submit flags deserve more design attention than they typically receive. AddPushButtonWithSubmitAction takes a THPDFSubmitFormFlags set, and an empty set produces a plain url-encoded post — the format many example endpoints accept and many production endpoints do not. Adding sffXFDF switches the payload to XFDF; sffGetMethod changes the HTTP verb; sffIncludeNoValueFields makes empty fields appear in the payload instead of being silently omitted, which matters whenever the receiving system distinguishes 'absent' from 'blank'. The flag set is effectively part of your interface contract with the receiving endpoint, so choose it together with the team that parses the submission — not after the first rejected batch.
Field-level JavaScript: keystroke, format, validate
Beyond button clicks, HotPDF attaches JavaScript to the per-field events that script-capable viewers fire during data entry. The three triggers map to different moments in the input lifecycle: keystroke actions run as characters arrive, format actions rewrite the displayed value after a change commits, and validate actions accept or reject the committed value.
// Reject committed values that are not plausible email addresses
Pdf.AttachFieldKeyStrokeAction('applicant.email',
'if (event.willCommit && !/^[\w.-]+@[\w.-]+\.\w+$/.test(event.value)) event.rc = false;');
// Display US phone numbers as (NNN) NNN-NNNN
Pdf.AttachFieldFormatAction('applicant.phone',
'event.value = event.value.replace(/(\d{3})(\d{3})(\d{4})/, "($1) $2-$3");');
// Refuse applicants under 18 at commit time
Pdf.AttachFieldValidateAction('applicant.age',
'if (parseInt(event.value) < 18) event.rc = false;');
Setting event.rc = false within a keystroke or validate script makes the viewer reject the input. The limitation to keep in view: this code executes only in viewers that ship a JavaScript engine. Acrobat and a handful of desktop products run it; most mobile readers, browser-embedded renderers, and print pipelines ignore it completely. Field scripts raise data quality for the users who get them — they are not a security boundary, and every submitted value still needs validation on the server when it arrives.
Defects that pass visual review
Some form defects are structurally invisible to an open-and-inspect check. These four account for most of the AcroForm escalations that reach support, and each one can be caught mechanically before release:
- Export value drift. A checkbox created as
AddCheckBox('consent', 'Yes', ...)postsYes; a consumer matching onYrejects every submission whilst the page looks perfect. Export the filled form as XFDF from Acrobat and diff the values against the receiving schema. - Accidental value mirroring. Duplicate fully qualified names merge fields into one. The symptom appears at data-entry time, never at generation time, so test by typing into the form — not by rendering it.
- Combo values outside the option list. If the current value passed to
AddComboBoxis not among the options, viewers disagree about whether to display it, blank it, or flag it. Keep the default within the list. - Fields still editable after the workflow closed. HotPDF has no appearance-flattening call for AcroForm fields; the supported way to freeze a completed form is creating fields with the
ffReadOnlyflag, which keeps the value rendered through the field's own appearance stream whilst blocking edits. A read-only field remains a live form object, which downstream assembly and signing tools handle predictably.
One viewer-side behaviour deserves a regression note even though no code change fixes it: enterprise Acrobat deployments can disable JavaScript or restrict submit targets by policy, so an action that worked throughout development may be inert on a customer desktop. Give submissions a visible fallback — at minimum a printed instruction — for the case where the button does nothing.
Where form work connects to the remainder of the document
A signature field is itself an AcroForm field type, so a form that will be certified or counter-signed later should reserve that field during generation instead of patching it in afterwards; the byte-level mechanics are covered by the companion article on digital signatures and PAdES signing with HotPDF. If your inputs arrive as XFA packages instead of native AcroForm, flattening XFA into AcroForm fields is a separate workflow with its own loss model, since the two form technologies are mutually exclusive within a single file.
The field, action, and trigger methods shown here are part of the standard HotPDF Component API for Delphi and C++Builder; the product page links the full reference, including the field-flag overloads and the complete submit-flag enumeration.