Dejanje AcroForm je slovar, priložen gradniku, ki pregledovalniku sporoči, kaj mora storiti, ko se s tem gradnikom nekaj zgodi. Kliknite gumb in pregledovalnik prebere njegov slovar dejanj: dejanje URI odpre spletni naslov, dejanje JavaScript zažene skript, dejanje SubmitForm pošlje zbrane vrednosti polj na končno točko, dejanje ResetForm pa jih ponastavi na privzete vrednosti. Dejanje predstavlja podatke, ne pa obnašanja, ki bi bilo zapečateno v sami datoteki. Standard ISO 32000-1 §12.6 določa obliko slovarja; pregledovalnik pa zagotavlja pogon, ki jo interpretira. Ta ločitev je pomembna, saj popolno zapisano dejanje v PDF-ju ne bo naredilo ničesar, če bralnik na drugi strani nima ustreznega pogona zanj. Večina težav z AcroForm izhaja prav iz te vrzeli in ne iz napačno oblikovanega polja.
HotPDF zapisuje te slovarje neposredno iz okolij Delphi in C++Builder, skupaj z gradniki polj, na katere so vezani. Pri vsakem interaktivnem obrazcu sta v igri dve strukturi: gradnik, ki ga uporabnik vidi na strani, ter polje z mehanizmom dejanj v ozadju, ki prenaša podatke in povezave. Urejata se neodvisno in ena je lahko napačna, medtem ko je druga videti povsem v redu. V spodnjih poglavjih bomo obravnavali poimenovanje polj, dejanja gumbov, JavaScript na ravni polj ter vrsto napak, ki preživijo vizualni pregled, ker obstajajo izključno v ozadju.
Imena polj so ključi za usmerjanje, ne napisi
Vsako polje AcroForm nosi polno kvalificirano ime. Standard ISO 32000-1 §12.7.3 določa, da je to ime, in ne vidni napis, ključ, pod katerim se vrednost polja prenaša ob izvozu ali oddaji obrazca. Razvijalci, ki prihajajo iz sveta oblikovanja VCL, ime kontrole pogosto obravnavajo kot zasebni identifikator v kodi, vendar tukaj ni tako. Gre za format prenosa podatkov.
Prva posledica tega je, da dve polji z enakim polno kvalificiranim imenom nista dve ločeni polji. PDF ju obravnava kot dve gradniški priponki istega polja, ki si delita isto vrednost, zato vnos v eno polje takoj posodobi drugo. To je natanko tisto, kar želite, ko se mora ime stranke ponoviti na vsaki strani obrazca ali pogodbe. Gre pa za napako, ko zanka za ustvarjanje pomotoma ponovno uporabi ime 'Field1' na treh straneh. Noben vizualni pregled ne bo zaznal tega drugega primera. Vsaka stran še vedno izriše svoj okvir, povezava pa se pokaže šele, ko nekdo začne vnašati podatke.
Imena s pikami, kot je applicant.email, gradijo hierarhijo. Nadrejeno vozlišče applicant združuje svoje podrejene elemente, kar omogoča ponastavitev ali oddajo le dela obrazca. Takšno poimenovanje polj že od začetka ne stane ničesar, povrne pa se takoj, ko prejemni sistem zahteva le blok s podatki o prijavitelju.
Izbirni gumbi (radio buttons) imajo svoje pravilo. Gumbi, ki naj bi delovali usklajeno, si morajo deliti ime skupine. V knjižnici HotPDF klici funkcije AddRadioButton, ki prenesejo isto ime skupine, pripnejo svoje gradnike k enemu nadrejenemu polju, izvozna vrednost vsakega gumba (npr. 'basic' ali 'full') pa določa izbrano možnost. Če daste vsakemu gumbu edinstveno ime, boste dobili vrsto neodvisnih stikal za vklop/izklop namesto skupine z medsebojno izključujočimi možnostmi, kar se izriše enako, vendar se obnaša napačno.
Ustvarjanje nabora polj stran za stranjo
HotPDF postavlja polja prek metod razreda THPDFPage, zato vsako polje pripada objektu strani, ki ga je ustvaril. Past pri vrstnem redu, na katero morate paziti, je klic AddPage. Ta takoj preusmeri CurrentPage na novo stran, zato bodo vsi klici za polja po tem pristali na novi strani, čeprav je polje logično pripadalo strani, ki ste jo pravkar zapustili. Dokončajte vsako stran, vključno z izrisano vsebino in polji, preden pokličete 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;
Koordinate uporabljajo konvencijo PDF z izhodiščem v spodnjem levem kotu strani. To je isto izhodišče, ki ga uporablja TextOut za izrisano besedilo, zato se Rect(50, 100, 200, 120) nahaja blizu dna strani Letter in ne na vrhu. VCL postavi Y na vrh in ga povečuje navzgol, zato se tabela postavitve, prenesena neposredno, izpiše navpično zrcalno, vsako polje pa se obrne na napačen del strani. Pretvorbo opravite enkrat v skupni pomožni funkciji namesto na vsakem mestu klica in en sam popravek bo uredil celoten obrazec.
Povezovanje gumbov z dejanji URI, JavaScript in pošiljanjem
Gumb je neaktiven, dokler nanj ne pripnete dejanja. HotPDF ponuja vrste dejanj iz standarda ISO 32000-1 §12.6.4 prek enumeracije THPDFButtonAction (baURI, baJavaScript, baSubmitURL, baResetForm, baHide, baShow, baNamed) in zagotavlja dve metodi, ki ustvarita gumb in povežeta njegovo dejanje v enem samem klicu.
// 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]);
Zastavice za oddajo obrazca (submit flags) si zaslužijo več pozornosti, kot jo običajno prejmejo. Metoda AddPushButtonWithSubmitAction sprejme nabor zastavic THPDFSubmitFormFlags. Prazen nabor ustvari navaden zahtavek POST s kodiranjem url-encoded, kajti to je format, ki ga sprejemajo številne testne točke, produkcijske končne točke pa ga pogosto zavrnejo. Dodajanje zastavice sffXFDF preklopi vsebino prenosa v format XFDF. Zastavica sffGetMethod spremeni glagol HTTP. Zastavica sffIncludeNoValueFields ohrani prazna polja v vsebini oddaje, namesto da bi jih tiho izpustila, kar postane pomembno takoj, ko prejemni sistem ločuje med »odsotnim« in »praznim« poljem. Nabor zastavic je del vaše pogodbe o vmesniku s prejemno končno točko, zato ga uskladite z ekipo, ki analizira oddane podatke, in ne šele po prvi zavrnjeni seriji.
JavaScript na ravni polj: pritisk tipke, formatiranje, validacija
Kliki gumbov niso edino mesto, kjer živijo dejanja. HotPDF omogoča tudi pripenjanje kode JavaScript na dogodke posameznih polj, ki jih pregledovalniki s podporo za skripte sprožijo med uporabnikovim vnašanjem podatkov. Na voljo so trije sprožilci, ki se zaženejo na različnih točkah življenjskega cikla vnosa. Dejanje ob pritisku tipke (keystroke) se izvede ob vsakem vnesenem znaku in ponovno ob potrditvi. Dejanje formatiranja (format) prepiše prikazano vrednost po potrditvi spremembe, izključno zaradi vizualnega prikaza. Dejanje potrjevanja (validate) pa ima zadnjo besedo: sprejme ali zavrne potrjeno vrednost, preden ta dejansko postane vrednost polja.
// 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;');
Nastavitev event.rc = false znotraj skripta za pritisk tipke ali validacijo pregledovalniku sporoči, naj zavrne vnos. Težava pa je v tem, da se nič od tega ne izvede, razen če pregledovalnik vsebuje pogon za JavaScript. Acrobat in nekateri namizni izdelki ga imajo. Večina mobilnih bralnikov, pregledovalnikov v brskalnikih in tiskalnih sistemov pa ga nima, zato te skripte tiho prezrejo. Skripte polj torej izboljšajo kakovost podatkov le za tisti del uporabnikov, katerih bralnik jih podpira. Niso pa varnostna meja. Vsako oddano vrednost je treba ob prihodu še vedno preveriti na strežniku, saj ne morete predpostaviti, da je odjemalec karkoli preveril.
Napake, ki preživijo vizualni pregled
Najtežje zaznavne napake AcroForm so tiste, ki živijo v podatkovni strukturi in ne v izrisu, saj vam odpiranje datoteke in vizualno opazovanje ne povesta ničesar. Štiri se pojavljajo dovolj pogosto, da jih je vredno izpostaviti, in vsaka ima mehanski test, s katerim jo lahko odkrijemo pred izdajo.
- Odstopanje izvoznih vrednosti. Potrditveno polje, ustvarjeno z
AddCheckBox('consent', 'Yes', ...), pošlje vrednostYes. Prejemni sistem, ki pričakujeY, bo zavrnil vsako oddajo, čeprav je stran videti popolna. Izpolnite obrazec, ga izvozite kot XFDF iz programa Acrobat in primerjajte vrednosti s shemo, ki jo prejemnik dejansko pričakuje. - Nenamerno zrcaljenje vrednosti. Dve polji, ki si delita isto polno kvalificirano ime, se združita v eno. Simptom se pokaže ob vnašanju podatkov in nikoli ob ustvarjanju datoteke, zato je test v tem, da vnesete podatke v obrazec, ne pa le, da ga izrišete in vizualno pregledate.
- Vrednosti seznama izven nabora možnosti. Ko privzeta vrednost, poslana v
AddComboBox, ni ena izmed navedenih možnosti na seznamu, se pregledovalniki razlikujejo glede tega, ali jo prikažejo, pustijo polje prazno ali sprožijo opozorilo. Privzeto vrednost obdržite znotraj seznama možnosti in ta težava bo izginila. - Polja so še vedno uredljiva po zaključku delovnega toka. HotPDF nima funkcije za sploščitev (flattening) polj AcroForm. Podprt način za zaklepanje dokončanega obrazca je ustvarjanje polj z zastavico
ffReadOnly, relativno ohranja vrednost vidno prek lastnega toka videza polja, hkrati pa preprečuje urejanje. Polje ostane aktivni objekt obrazca, kar pričakujejo orodja za nadaljnje zlaganje in podpisovanje.
Eno od obnašanj na strani pregledovalnika je vredno zabeležiti, čeprav ga nobena sprememba kode ne rešuje. Namestitve programa Acrobat v podjetjih lahko zaradi pravilnika onemogočijo JavaScript ali omejijo cilje za oddajo obrazcev, zato lahko dejanje, ki je delovalo v vseh razvojnih različicah, na zaklenjenem namizju stranke ostane neaktivno. Načrtujte vidno nadomestno možnost za primer, ko gumb ne naredi ničesar, pa čeprav je to le natisnjeno navodilo uporabniku, kaj naj stori namesto tega.
Kje se delo z obrazci povezuje s preostalim dokumentom
Polje za podpis je prav tako vrsta polja AcroForm. Pri obrazcu, ki bo pozneje certificiran ali sopodpisan, je bolje rezervirati to polje že med ustvarjanjem dokumenta, kot pa ga dodajati naknadno. Razlogi na ravni bajtov so opisani v spremljajočem članku o digitalnih podpisih in podpisovanju PAdES s HotPDF. Vnosi, ki pridejo kot paketi XFA namesto kot izvorni AcroForm, pa predstavljajo drugačno situacijo: sploščitev XFA v polja AcroForm je svoj potek dela z lastnim modelom izgube podatkov, saj ti dve tehnologiji obrazcev ne moreta soobstajati v isti datoteki.
Metode za polja, dejanja in sprožilce, ki so prikazane tukaj, so del standardnega vmesnika API za komponento HotPDF Component za Delphi in C++Builder; spletna stran izdelka vsebuje povezavo do celotne referenčne dokumentacije, vključno s preobremenitvami zastavic polj in celotno enumeracijo zastavic za oddajo obrazca.