Een AcroForm-actie is een dictionary die aan een widget is gekoppeld en de viewer vertelt wat te doen als er iets met die widget gebeurt. Klik op een knop en de viewer leest zijn actie-dictionary: een URI-actie opent een webadres, een JavaScript-actie voert script uit, een SubmitForm-actie verzendt de verzamelde veldwaarden naar een endpoint, een ResetForm-actie wist ze terug naar de standaardwaarden. De actie is data, geen gedrag dat in het bestand is gebakken. ISO 32000-1 §12.6 definieert de vorm van de dictionary; de viewer levert de engine die deze interpreteert. Die scheiding is belangrijk, want een actie die perfect in de PDF is geschreven doet nog steeds niets als de reader aan de andere kant geen engine ervoor heeft, en veel AcroForm-ellende herleidt zich tot die kloof in plaats van naar een verkeerd gevormd veld
HotPDF schrijft die dictionaries direct vanuit Delphi en C++Builder, naast de veld-widgets waaraan ze hangen. Voor elk interactief formulier zijn twee structuren in het spel: de widget die de gebruiker op de pagina ziet, en het veld plus de actie-machinerie eronder die de data en de bedrading draagt. Ze worden onafhankelijk bewerkt, en beide kunnen fout zijn terwijl de andere er goed uitziet. De onderstaande secties werken veldnaamgeving af, de knopacties zelf, JavaScript op veldniveau, en de klasse van defect die een visuele controle overleeft omdat het geheel in de tweede structuur leeft
Veldnamen zijn routesleutels, geen bijschriften
Elk AcroForm-veld draagt een volledig gekwalificeerde naam. ISO 32000-1 §12.7.3 maakt die naam, niet het zichtbare bijschrift, tot de sleutel waaronder de waarde van het veld reist wanneer het formulier wordt geëxporteerd of verzonden. Ontwikkelaars die vanuit VCL-ontwerp komen, zijn geneigd de naam van een control te behandelen als een privé-code-identifier, en dat is het hier niet. Het is het wire-format
Het eerste wat daaruit volgt, is dat twee velden met dezelfde volledig gekwalificeerde naam geen twee velden zijn. PDF behandelt ze als twee widget-annotaties van één veld, die één waarde delen, dus typen in de ene werkt de andere direct bij. Dat is precies wat u wilt als een klantnaam op elke pagina van een contract moet herhalen. Het is een bug als een generatielus per ongeluk 'Field1' over drie pagina's hergebruikt. Geen visuele inspectie vangt het tweede geval. Elke pagina tekent nog steeds zijn eigen vak, en de koppeling komt pas aan het licht als iemand begint te typen
Met punten genoteerde namen zoals applicant.email bouwen een hiërarchie op. Het bovenliggende knooppunt applicant groepeert zijn onderliggende elementen, wat een reset of submit slechts een deel van een formulier laat targeten. Velden vanaf het begin op deze manier benoemen kost niets, en het verdient zich terug de eerste keer dat het ontvangende systeem om alleen het applicant-blok vraagt
Radioknoppen hebben een eigen regel. Knoppen die samen moeten schakelen moeten een groepsnaam delen. In HotPDF koppelen AddRadioButton-aanroepen die dezelfde groepsnaam doorgeven hun widgets aan één bovenliggend veld, en de exportwaarde van elke knop ('basic' of 'full') identificeert de gekozen optie. Geef elke knop een eigen naam en u krijgt een rij onafhankelijke aan/uit-schakelaars in plaats van één wederzijds uitsluitende groep, die identiek rendert en verkeerd gedraagt
De veldenset pagina voor pagina aanmaken
HotPDF plaatst velden via THPDFPage-methoden, dus elk veld behoort tot het pagina-object dat het heeft aangemaakt. De sequentieval waar u op moet letten is AddPage. Het wijst CurrentPage opnieuw naar de nieuwe pagina op het moment dat het terugkeert, dus elke veldaanroep erna landt op de nieuwe pagina, zelfs als het veld logisch bij de pagina hoorde die u zojuist verliet. Maak elke pagina af, getekende content en velden samen, voordat u AddPage aanroept
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 wijst nu naar pagina 2
Pdf.CurrentPage.AddListBox('riders', 'None',
['None', 'Flood', 'Earthquake'], Rect(50, 500, 200, 600));
end;
Coördinaten gebruiken de PDF-conventie, met de oorsprong in de hoek linksonder van de pagina. Dit is dezelfde oorsprong die TextOut gebruikt voor getekende tekst, dus Rect(50, 100, 200, 120) zit vlak bij de onderkant van een Letter-pagina, niet de bovenkant. VCL plaatst Y bovenaan en laat deze naar beneden groeien, dus een lay-outtabel die rechtstreeks wordt overgezet, komt verticaal gespiegeld naar buiten, elk veld omgekeerd naar het verkeerde uiteinde van de pagina. Doe de conversie eenmaal in een gedeelde helper in plaats van op elke aanroepplaats, en één correctie herstelt het hele formulier
Knoppen verbinden met URI-, JavaScript- en submit-acties
Een drukknop is inert totdat er een actie aan is gekoppeld. HotPDF brengt de actietypen uit ISO 32000-1 §12.6.4 naar boven via de enumeratie THPDFButtonAction (baURI, baJavaScript, baSubmitURL, baResetForm, baHide, baShow, baNamed), en biedt twee methoden die de knop aanmaken en de actie in één aanroep binden
// Open een helppagina in de systeembrowser
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);
// Verstuur als XFDF en houd lege velden in de payload
Pdf.CurrentPage.AddPushButtonWithSubmitAction('btnSubmit', 'Submit claim',
'https://api.example.com/claims', Rect(320, 620, 420, 650),
[sffXFDF, sffIncludeNoValueFields]);
De submit-vlaggen verdienen meer aandacht dan ze meestal krijgen. AddPushButtonWithSubmitAction neemt een THPDFSubmitFormFlags-set, en een lege set produceert een gewone url-encoded post, wat het formaat is dat veel voorbeeld-endpoints accepteren en veel productie-endpoints afwijzen. sffXFDF toevoegen schakelt de payload om naar XFDF. sffGetMethod wijzigt het HTTP-werkwoord. sffIncludeNoValueFields behoudt lege velden in de payload in plaats van ze stilletjes weg te laten, wat uitmaakt zodra de consument 'afwezig' onderscheidt van 'leeg'. De vlaggenset is deel van uw interfacecontract met het ontvangende endpoint, dus leg het vast met het team dat de inzending parst, niet na de eerste afgewezen batch
JavaScript op veldniveau: keystroke, format, validate
Knopklikken zijn niet de enige plek waar acties leven. HotPDF koppelt JavaScript ook aan de veldspecifieke events die script-capabele viewers afvuren terwijl een gebruiker data invoert. Er zijn drie triggers, en ze vuren op verschillende momenten in de invoerlevenscyclus. Een keystroke-actie draait terwijl elk teken aankomt, en opnieuw bij commit. Een format-actie herschrijft de weergegeven waarde nadat een wijziging is vastgelegd, puur voor presentatie. Een validate-actie heeft het laatste woord, en accepteert of weigert de vastgelegde waarde voordat deze de waarde van het veld wordt
// Weiger vastgelegde waarden die geen plausibel e-mailadres zijn
Pdf.AttachFieldKeyStrokeAction('applicant.email',
'if (event.willCommit && !/^[\w.-]+@[\w.-]+\.\w+$/.test(event.value)) event.rc = false;');
// Toon Amerikaanse telefoonnummers als (NNN) NNN-NNNN
Pdf.AttachFieldFormatAction('applicant.phone',
'event.value = event.value.replace(/(\d{3})(\d{3})(\d{4})/, "($1) $2-$3");');
// Wijs aanvragers onder de 18 af op het moment van commit
Pdf.AttachFieldValidateAction('applicant.age',
'if (parseInt(event.value) < 18) event.rc = false;');
event.rc = false instellen binnen een keystroke- of validate-script vertelt de viewer de invoer af te wijzen. De hak is dat niets hiervan draait tenzij de viewer een JavaScript-engine levert. Acrobat en een paar desktopproducten hebben er een. De meeste mobiele readers, in de browser ingebedde renderers en print-pijplijnen niet, en ze droppen de scripts zonder klagen op de vloer. Dus veldscripts verbeteren de datakwaliteit voor de subset gebruikers wiens reader ze uitvoert, en dat is alles wat ze doen. Ze vormen geen beveiligingsgrens. Elke ingediende waarde moet nog steeds op de server worden gevalideerd zodra deze aankomt, omdat u niet kunt aannemen dat de client iets controleerde
Defecten die een visuele beoordeling doorstaan
De AcroForm-defecten die het moeilijkst te vangen zijn, zijn degenen die in de datastructuur leven in plaats van in de rendering, omdat het bestand openen en ernaar kijken u niets vertelt. Vier komen vaak genoeg voor om het benoemen waard te zijn, en elk heeft een mechanische test die het vóór release vindt
- Exportwaarde-drift. Een selectievakje aangemaakt als
AddCheckBox('consent', 'Yes', ...)verzendtYes. Een consument die matcht opYwijst elke inzending af terwijl de pagina er perfect uitziet. Vul het formulier, exporteer het als XFDF vanuit Acrobat, en vergelijk de waarden met het schema dat de consument daadwerkelijk verwacht - Per ongeluk waarden spiegelen. Twee velden die een volledig gekwalificeerde naam delen, smelten samen tot één. Het symptoom verschijnt op het moment van data-invoer en nooit op het moment van generatie, dus de test is om in het formulier te typen, niet om het te renderen en het resultaat visueel te beoordelen
- Combowaarden buiten de optielijst. Wanneer de huidige waarde die aan
AddComboBoxwordt doorgegeven niet een van de vermelde opties is, zijn viewers het oneens over of deze te tonen, leeg te maken of te markeren. Houd de standaardwaarde binnen de lijst en de onenigheid verdwijnt - Velden nog bewerkbaar nadat de workflow is gesloten. HotPDF heeft geen appearance-flattening-aanroep voor AcroForm-velden. De ondersteunde manier om een voltooid formulier te bevriezen is de velden aan te maken met de vlag
ffReadOnly, die de waarde zichtbaar houdt via de eigen appearance-stream van het veld terwijl het bewerkingen weigert. Het veld blijft een live formulierobject, wat stroomafwaartse assemblage- en ondertekeningsgereedschappen verwachten aan te treffen
Eén viewer-side gedrag is een regressienotitie waard, ook al verhelpt geen codewijziging het. Enterprise-Acrobat-implementaties kunnen JavaScript uitschakelen of submit-doelen per beleid beperken, dus een actie die door elke ontwikkelingsbuild werkte, kan dood zitten op een afgesloten klantdesktop. Plan een zichtbare fallback voor het geval dat de knop niets doet, zelfs als die fallback slechts een afgedrukte instructie is die de gebruiker vertelt wat in plaats daarvan te doen
Waar formulierwerk aansluit op de rest van het document
Een handtekeningveld is zelf een AcroForm-veldtype. Een formulier dat later wordt gecertificeerd of tegenondertekend, is beter af dat veld tijdens de generatie te reserveren dan het achteraf te patchen, en de byte-niveau-redenen waarom staan in het begeleidende artikel over digitale handtekeningen en PAdES-ondertekening met HotPDF. Invoeren die als XFA-pakketten aankomen in plaats van native AcroForm zijn een andere situatie: XFA flattening naar AcroForm-velden is een eigen workflow met een eigen verliesmodel, omdat de twee formuliertechnologieën niet in één bestand kunnen samenbestaan
De veld-, actie- en triggermethoden die hier worden getoond, maken deel uit van de standaard HotPDF Delphi Component-API voor Delphi en C++Builder; de productpagina linkt de volledige referentie, inclusief de veldvlag-overloads en de complete submit-vlag-enumeratie