„AcroForm“ veiksmas yra prie valdiklio (widget) pridėtas žodynas, kuris nurodo peržiūros programai, ką daryti atlikus tam tikrą veiksmą su tuo valdikliu. Spustelėjus mygtuką, peržiūros programa perskaito jo veiksmų žodyną: URI veiksmas atidaro interneto adresą, „JavaScript“ veiksmas paleidžia scenarijų, „SubmitForm“ veiksmas išsiunčia surinktas laukų reikšmes į galinį tašką, o „ResetForm“ veiksmas grąžina numatytąsias reikšmes. Veiksmas yra duomenys, o ne faile įrašytas elgesys. ISO 32000-1 §12.6 apibrėžia šio žodyno formą, o peržiūros programa pateikia variklį, kuris jį interpretuoja. Šis atskyrimas yra svarbus, nes net ir puikiai įrašytas veiksmas PDF faile nieko nedarys, jei gavėjo peržiūros programa neturės tam skirto variklio, ir daugelis „AcroForm“ problemų kyla būtent dėl šios spragos, o ne dėl netinkamai suformuoto lauko.
HotPDF įrašo šiuos žodynus tiesiai iš Delphi ir C++Builder aplinkų kartu su laukų valdikliais, prie kurių jie yra prijungti. Kiekvienoje interaktyviojoje formoje veikia dvi struktūros: valdiklis, kurį vartotojas mato puslapyje, ir po juo esantis lauko bei veiksmų mechanizmas, kuris perneša duomenis ir jungtis. Jie redaguojami nepriklausomai, todėl vienas iš jų gali būti klaidingas, nors kitas atrodo gerai. Tolesniuose skyriuose aptariami laukų pavadinimai, mygtukų veiksmai, lauko lygio „JavaScript“ ir defektų klasė, kuri išlieka nepastebėta vizualinio patikrinimo metu, nes egzistuoja tik antrojoje struktūroje.
Laukų pavadinimai yra maršruto raktai, o ne etiketės
Kiekvienas „AcroForm“ laukas turi pilną pavadinimą (fully qualified name). ISO 32000-1 §12.7.3 reikalauja, kad eksportuojant arba pateikiant formą šis pavadinimas, o ne matoma etiketė (caption), būtų raktas, su kuriuo perduodama lauko reikšmė. Kūrėjai, atėję iš VCL aplinkos, yra linkę traktuoti valdiklio pavadinimą kaip privatų kodo identifikatorių, tačiau čia taip nėra. Tai yra perdavimo formatas.
Iš to seka, kad du laukai su tuo pačiu pilnu pavadinimu nėra du atskiri laukai. PDF juos traktuoja kaip dvi to paties lauko valdiklių anotacijas, kurios dalijasi viena reikšme, todėl rašant į vieną lauką, kitas atnaujinamas iš karto. Tai yra būtent tai, ko norite, kai kliento vardas turi kartotis kiekviename sutarties puslapyje. Tačiau tai yra klaida, jei generavimo ciklas netyčia pakartotinai naudoja pavadinimą 'Field1' trijuose puslapiuose. Joks vizualinis patikrinimas šio antrojo atvejo neatskleis. Kiekvienas puslapis vis tiek pieš savo laukelį, o ryšys išryškės tik tada, kai kas nors pradės rašyti.
Pavadinimai su taškais, pavyzdžiui, applicant.email, sukuria hierarchiją. Tėvinis mazgas applicant sugrupuoja savo vaikus, todėl formos išvalymo (reset) ar pateikimo (submit) veiksmas gali būti nukreiptas tik į dalį formos. Toks laukų pavadinimas nuo pat pradžių nieko nekainuoja, tačiau atsiperka jau pirmą kartą, kai gaunančioji sistema paprašo tik pareiškėjo bloko.
Akutėms (radio buttons) taikoma atskira taisyklė. Mygtukai, kurie turi persijungti kartu, privalo turėti bendrą grupės pavadinimą. HotPDF aplinkoje AddRadioButton iškvietimai, perduodantys tą patį grupės pavadinimą, priskiria savo valdiklius vienam tėviniam laukui, o kiekvieno mygtuko eksporto reikšmė (pavyzdžiui, 'basic' arba 'full') identifikuoja pasirinktą parinktį. Suteikite kiekvienam mygtukui skirtingą pavadinimą ir gausite nepriklausomų įjungimo / išjungimo jungiklių eilę, o ne vieną tarpusavyje susijusią grupę – vizualiai tai atrodys taip pat, tačiau veiks klaidingai.
Laukų rinkinio kūrimas puslapis po puslapio
HotPDF išdėsto laukus naudodamas THPDFPage metodus, todėl kiekvienas laukas priklauso jį sukūrusiam puslapio objektui. Eiliškumo spąstai, kurių reikia saugotis, yra AddPage. Šis metodas nukreipia CurrentPage į naują puslapį iškart, kai yra iškviečiamas, todėl bet koks lauko sukūrimas po jo bus patalpintas naujame puslapyje, net jei laukas logiškai priklausė puslapiui, kurį ką tik palikote. Užbaikite kiekvieną puslapį (tiek piešiamą turinį, tiek laukus) prieš iškviesdami 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;
Koordinatėms naudojamas PDF standartas, kur pradžios taškas (origin) yra apatiniame kairiajame puslapio kampe. Tai yra tas pats pradžios taškas, kurį TextOut naudoja piešiamam tekstui, todėl Rect(50, 100, 200, 120) bus arti „Letter“ formato puslapio apačios, o ne viršuje. VCL naudoja Y koordinatę viršuje ir didina ją žemyn, todėl tiesiogiai perkelta išdėstymo lentelė bus vertikaliai veidrodinė – kiekvienas laukas atsidurs klaidingoje puslapio vietoje. Atlikite konversiją bendroje pagalbinėje funkcijoje, o ne kiekviename iškvietime, ir viena korekcija sutvarkys visą formą.
Mygtukų sujungimas su URI, JavaScript ir pateikimo (submit) veiksmais
Mygtukas (push button) yra neaktyvus, kol prie jo nepriskiriamas veiksmas. HotPDF pateikia veiksmų tipus iš ISO 32000-1 §12.6.4 per THPDFButtonAction išvardijimą (baURI, baJavaScript, baSubmitURL, baResetForm, baHide, baShow, baNamed) ir siūlo du metodus, kurie sukuria mygtuką ir susieja jo veiksmą vienu iškvietimu.
// 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]);
Pateikimo vėliavėlės (submit flags) reikalauja daugiau dėmesio, nei joms paprastai skiriama. AddPushButtonWithSubmitAction priima THPDFSubmitFormFlags rinkinį. Tuščias rinkinys sukuria paprastą URL koduotą POST užklausą – tai formatas, kurį priima daugelis bandomųjų galinių taškų, tačiau atmeta gamybiniai taškai. Pridėjus sffXFDF, siunčiami duomenys pakeičiami į XFDF formatą. sffGetMethod pakeičia HTTP metodą. sffIncludeNoValueFields išlaiko tuščius laukus siunčiamuose duomenyse, užuot juos tyliai atmetus, o tai labai svarbu, jei gavėjas skiria būsenas „nėra lauko“ ir „tuščias laukas“. Šis vėliavėlių rinkinys yra jūsų integracijos sutarties dalis, todėl suderinkite jį su komanda, kuri apdoroja formos duomenis, o ne po pirmo atmesto duomenų paketo.
Lauko lygio JavaScript: keystroke, format, validate
Mygtukų paspaudimai nėra vienintelė vieta, kur gyvuoja veiksmai. HotPDF taip pat priskiria „JavaScript“ scenarijus konkrečių laukų įvykiams, kuriuos sužadina scenarijus palaikančios peržiūros programos, kai vartotojas įveda duomenis. Yra trys trigeriai, kurie suveikia skirtingais įvesties etapais. Veiksmas „keystroke“ vykdomas įvedant kiekvieną simbolį ir dar kartą patvirtinant pakeitimą. Veiksmas „format“ perrašo rodomą reikšmę po to, kai pakeitimas patvirtinamas (tik pateikimo tikslais). Veiksmas „validate“ turi paskutinį žodį – jis priima arba atmeta patvirtintą reikšmę prieš jai tampant lauko reikšme.
// 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;');
Nustačius event.rc = false scenarijuje „keystroke“ arba „validate“, peržiūros programai nurodoma atmesti įvestį. Tačiau visa tai neveiks, jei peržiūros programa neturi „JavaScript“ variklio. „Acrobat“ ir keletas kitų darbalaukio programų jį turi. Daugelis mobiliųjų skaitytuvų, naršyklėse įterptų atvaizdavimo įrankių ir spausdinimo procesų jo neturi ir tiesiog ignoruoja šiuos scenarijus be jokių pranešimų. Taigi lauko scenarijai pagerina duomenų kokybę tik tai vartotojų daliai, kurių skaitytuvas juos palaiko. Tai nėra saugumo garantas. Kiekviena pateikta reikšmė vis tiek privalo būti patikrinta serveryje, nes negalite daryti prielaidos, kad klientas ką nors patikrino.
Defektai, kurie praeina vizualinį patikrinimą
Sunkiausiai pastebimi „AcroForm“ defektai yra tie, kurie slypi duomenų struktūroje, o ne vizualiame atvaizdavime, nes failo atidarymas ir peržiūra nieko neatskleidžia. Keturi iš jų pasitaiko pakankamai dažnai, kad būtų verta juos paminėti, ir kiekvienas iš jų turi mechaninį testą, leidžiantį juos rasti prieš išleidimą.
- Eksporto reikšmių nukrypimas. Žymimasis langelis, sukurtas kaip
AddCheckBox('consent', 'Yes', ...), perduoda reikšmęYes. Gavėjas, tikintis gauti reikšmęY, atmes visus pateikimus, nors vizualiai puslapis atrodys nepriekaištingai. Užpildykite formą, eksportuokite ją kaip XFDF iš „Acrobat“ ir palyginkite reikšmes su schema, kurios tikisi gavėjas. - Atsitiktinis reikšmių dubliavimas (veidrodinis atspindys). Du laukai, turintys tą patį pilną pavadinimą, susijungia į vieną. Simptomas pasireiškia tik duomenų įvedimo metu, o ne generuojant failą, todėl testavimui reikia patiems įrašyti duomenis į formą, o ne tik vizualiai įvertinti rezultatą.
- Išskleidžiamojo sąrašo (combo) reikšmės už pasirinkimų ribų. Kai dabartinė reikšmė, perduota
AddComboBoxmetodui, nėra viena iš pateiktų parinkčių, peržiūros programos gali elgtis skirtingai: rodyti ją, palikti lauką tuščią arba pažymėti kaip klaidą. Laikykite numatytąją reikšmę sąrašo ribose, ir šis nesuderinamumas išnyks. - Laukai lieka redaguojami užbaigus procesą. HotPDF neturi parinkties, skirtos „AcroForm“ laukų suplokštinimui (flattening). Rekomenduojamas būdas užšaldyti užpildytą formą – sukurti laukus su
ffReadOnlyvėliavėle, kuri išlaiko reikšmę matomą per paties lauko atvaizdavimo srautą, tačiau neleidžia jos redaguoti. Laukelis lieka aktyviu formos objektu, ko ir tikisi tolesni dokumentų pasirašymo įrankiai.
Viena peržiūros programos elgsena yra verta pastabos, nors jokie kodo pakeitimai jos neišsprendžia. Įmonių „Acrobat“ diegimai gali išjungti „JavaScript“ arba apriboti pateikimo tikslus pagal politiką, todėl veiksmas, kuris veikė kūrimo metu, gali visiškai neveikti kliento kompiuteryje. Suplanuokite aiškiai matomą atsarginį variantą tam atvejui, kai mygtukas nieko nedaro, net jei tai būtų tik atspausdinta instrukcija, nurodanti vartotojui, ką daryti.
Kur formos darbas jungiasi su likusia dokumento dalimi
Parašo laukas pats savaime yra „AcroForm“ lauko tipas. Formoje, kuri bus sertifikuojama arba pasirašoma vėliau, geriau rezervuoti šį lauką generavimo metu, o ne įterpti jį vėliau. Baitų lygio priežastys paaiškintos susijusiame straipsnyje apie skaitmeninius parašus ir PAdES pasirašymą su HotPDF. Įvestys, kurios gaunamos kaip XFA paketai, o ne vietiniai „AcroForm“ laukai, yra kitokia situacija: XFA suplokštinimas į AcroForm laukus yra atskiras procesas su savo praradimo modeliu, nes šios dvi formų technologijos negali bendrai egzistuoti viename faile.
Čia parodyti laukų, veiksmų ir trigerių metodai yra standartinės HotPDF Component API dalis, skirta Delphi ir C++Builder; produkto puslapyje pateikiamos nuorodos į visą dokumentaciją, įskaitant laukų vėliavėlių perkrovas ir pilną pateikimo vėliavėlių sąrašą.