Pritisnite Tab v obrazcu PDF, ki ga je zgradila vaša koda, in kazalec bo pristal dve polji stran od mesta, kjer bi moral, ali pa bo popolnoma preskočil drugi stolpec oziroma skočil nazaj na vrh po tretjem polju namesto po četrtem. Oseba, ki v vašem pregledovalniku izpolnjuje račun, pričakuje, da se bo tipkovnica premikala po obrazcu enako kot po vsakem spletnem obrazcu, ki ga je kdaj uporabila. Če se to ne zgodi, poseže po miški, išče naslednje polje in potihem oceni, da je vaše orodje nedokončano. Predvidljiva navigacija po poljih je tisto, kar loči pregledovalnik za vnos podatkov, ki ga ljudje le prenašajo, od tistega, ki mu zaupajo. To pa je skoraj v celoti odvisno od uporabe pravega vmesnika API za fokusiranje, namesto simuliranja vnosa s tipkovnice z navideznimi kliki z miško.
Spodnji primeri uporabljajo komponento PDFium Component, ki je VCL/LCL komponenta na osnovi PDFium za Delphi, C++Builder in Lazarus. Navigacija je ena od treh stvari, ki jih mora pregledovalnik obrazcev pravilno izvesti; preostali dve (pravilno odpiranje obrazca in shranjevanje izpolnjenih vrednosti, da so dejansko vidne) sta področji, kjer se skriva največ presenečenj, zato so v nadaljevanju opisane vse tri.
Odpiranje obrazca: FormFill, FormType, in vprašanje o XFA
Dostop do polij zahteva, da je podsistem za izpolnjevanje obrazcev, ki ga nadzoruje lastnost FormFill, omogočen pred odpiranjem dokumenta. Ko je aktiven, lastnost FormType pove, s kakšno vrsto obrazca imate opravka, odgovor pa spremeni nabor funkcij, ki jih lahko zagotovite:
Pdf.FileName := FormPath;
Pdf.FormFill := True; // enable before Active; required for any field access
Pdf.Active := True;
case Pdf.FormType of
ftNone:
DisableFormPanel('This document has no interactive form');
ftAcroForm:
BuildFieldList; // full field navigation and editing available
ftXfaFull:
ShowXfaNotice; // XFA renders from its own XML template;
// treat field editing as limited
end;
Iz te preklapitve izhajata dve praktični opozorila. AcroForm is standardni model obrazcev ISO 32000 in je tarča vseh tukaj uporabljenih API-jev. Dokumenti XFA vsebujejo lastno arhitekturo obrazcev XML, zato je obljubljanje popolnega urejanja XFA stranki po hitri predstavitvi AcroForm obveza, ki jo boste obžalovali. Drugo opozorilo se nanaša na stranske učinke: nastavitev FormFill na True inicializira tudi dokumentni JavaScript. V pregledovalniku za vnos podatkov je to povsem pravilno, saj skripte za izračun sproti posodabljajo skupne vsote, ko uporabnik tipka. V predoglednem oknu za datoteke neznanega izvora pa je to povsem napačno. Članek o varnem predogledu dokumentov PDF obravnava nasprotno stran te kompenzacije z nastavitvijo FormFill := False.
Navigacija s tipko Tab, ki pristane tam, kjer uporabniki pričakujejo
Vrnimo se k težavi s tipkovnico z začetka. Obstaja mik, da bi tipko Tab simulirali s poustvarjanjem klika z miško na pravokotnik naslednjega gradnika, kar pa se zalomi takoj, ko se polje pomakne izven zaslona ali ko se dva gradnika prekrivata. API za fokusiranje namesto tega premakne fokus samega obrazca neposredno, brez ugibanja geometrije. Za to je na voljo pet klicev: FocusFormField po indeksu, FocusNextFormField in FocusPreviousFormField za pomikanje, FocusedFormFieldIndex za branje trenutnega stanja ter ClearFormFieldFocus za popoln umik fokusa.
procedure TFormViewer.HandleTabKey(Shift: TShiftState);
begin
if ssShift in Shift then
PdfView.FocusPreviousFormField
else
PdfView.FocusNextFormField;
UpdateFieldStatus; // e.g. "Field 4 of 17: InvoiceDate"
end;
Edino obnašanje, ki lahko zmede razvijalce, je kroženje. Pomikanje poteka po vrstnem redu zavihkov trenutne strani in kroži znotraj nje: če stopite čez zadnje polje, se vrnete na prvo. Obe funkciji za pomikanje vrneta indeks novega polja ali -1, če stran sploh ne vsebuje polj. To kroženje poteka na ravni posamezne strani in ne celotnega dokumenta, kar pomeni, da je prehod na naslednjo stran vaša naloga in ne naloga knjižnice. Primerjajte vrnjeni indeks z začetnim indeksom, zaznajte, kdaj je prišlo do kroženja, in sami povečajte PageNumber, če je obrazec zasnovan kot eno neprekinjeno zaporedje. Če to preverjanje izpustite, bo dvo-stranski obrazec tiho ujel kazalec na prvi strani, kar je še ena različica težav z nedelujočo tipko Tab.
Krmarjenje postane uporabno šele, ko nanj reagira preostali del uporabniškega vmesnika. Dogodek OnFormFieldEnter se sproži ob prejemu fokusa, na pregledovalniku pa OnFormFieldFocusChange poroča o novem indeksu polja, tako da lahko stranska plošča sledi tistemu, kar je uporabnik izbral s tipkovnico. Ko potrebujete obratno preslikavo, iz zaslonskega položaja v polje, indeksirana lastnost FormFieldAt izvede testiranje zadetkov (hit-testing) za predoglede orodnih namigov in plošč za urejanje s klikom. V vsem tem je tudi skrita prednost za dostopnost: ker fokus sledi vrstnemu redu polj v samem dokumentu, je pot, ki jo pripravite za tipko Tab, enaka poti, ki jo napove bralnik zaslona, in to brez dodatnega dela.
Prikazovanje imen polj namesto surovih indeksnih številk zahteva još eno lastnost. FormFieldInfo[] vrne zapis TPdfFormFieldInfo za vsak indeks, ki vsebuje ime polja, vrsto, velikost pisave, označeno stanje, izvozno vrednost in pripadnost skupini, kar je natanko tisto, kar mora prikazati navigacijski seznam (npr. »Polje 4 od 17: InvoiceDate« in ne le »4«). Izbirni gumbi (radio gumbi) so primer, ki si zasluži namensko testno datoteko. Več gradnikov si lahko deli isto ime polja, zato seznam, sestavljen neposredno iz gradnikov, večkrat prikaže isto skupino in zmede uporabnika.
Zakaj so izpolnjene vrednosti videti prazne in klic, ki to odpravi
Druga pritožba, ki polni podporne vrste, je še bolj skrb vzbujajoča kot nepravilno delovanje tipke Tab: obrazec je izpolnjen programsko, stranka ga odpre v programu Acrobat, vsa polja pa so videti prazna. Kliknite v polje in njegova vrednost se nenadoma prikaže. Podatki so ves čas v datoteki. Manjka le slika podatkov, razlog za to pa je vredno razumeti enkrat za vselej, saj pojasnjuje celotno družino sorodnih napak.
Besedilno polje AcroForm shrani svojo vrednost v vnos /V v slovarju polja (ISO 32000-1 §12.7.3.3). Pregledovalnik pa dejansko izriše nekaj drugega: tok videza gradnika pod /AP (§12.5.5), kar je kratek vnaprej pripravljen del vsebine. Če zapišete /V in pustite /AP pri miru, se ti dve vrednosti razideta. Vrednost obstaja, vendar je njena izrisana različica zastarela ali pa je sploh ni. Acrobat ob prejemu fokusa ponovno zgradi videz polja, kar pojasnjuje, zakaj se vrednosti prikažejo šele ob kliku. Stara zastavica NeedAppearances, ki je od pregledovalnikov zahtevala, da sami poustvarijo videz, nikoli ni delovala enotno in je v PDF 2.0 opuščena, tiskalni strežniki in generatorji sličic pa jo popolnoma prezrejo. Izrišejo le /AP in nič drugega, zato v primeru praznega /AP natisnejo prazno polje.
Dodelitev vrednosti prek FormField[i] spremeni le vnos /V. Zato je izpolnjevanje obrazca zaporedje treh korakov, razvijalci pa najpogosteje izpustijo prav vmesnega:
procedure TFormViewer.FillAndSave(const Values: array of WString;
const OutputPath: string);
var
i: Integer;
begin
for i := 0 to Pdf.FormFieldCount - 1 do
Pdf.FormField[i] := Values[i]; // writes /V only
// Rebuild the /AP appearance streams; without this the form
// looks blank in Acrobat until each field is clicked
Pdf.GenerateFormAppearances;
Pdf.SaveAs(OutputPath);
end;
Metoda GenerateFormAppearances je rešitev za to težavo. Ponovno zgradi tok videza vsakega gradnika na podlagi trenutnih vrednosti, pisav in poravnave, tako da pregledovalnik, ki nikoli ne sproži dogodka fokusiranja (kot sta tiskalni strežnik ali generator sličic), kljub temu izriše izpolnjeno stanje. Pokličite jo enkrat po celotni seriji dodelitev in ne za vsako polje posebej. Generiranje videza opravi dejansko delo postavitve, zato bi klici za vsako polje posebej po nepotrebnem obremenili proces pri velikih obrazcih.
Ponovna izgradnja videza je tudi trenutek, ko se uveljavijo pisave in poravnave, kar prinaša dodatna presenečenja. Nov tok razporedi vsako vrednost znotraj pravokotnika gradnika z uporabo pisave, velikosti in poravnave polja. Vrednost, ki se v vašem testnem obrazcu lepo prilega, se lahko v strankini kopiji odreže ali skrči, če je isto polje tam ožje. Polja s samodejno velikostjo (velikost pisave nič) skrčijo besedilo, da se prilega; polja s fiksno velikostjo pa ga preprosto odrežejo. Obe možnosti sta tehnično pravilni, edini zanesljiv način, da ugotovite, katero možnost uporablja določen obrazec, pa je ogled ponovno ustvarjenega izhoda in ne preverjanje niza, ki ste ga zapisali. Če nekdo poroča o odrezanem besedilu na robu polja, je razlog gotovo v tem.
Preverjanje obravnavate kot del zaključka dela in ne kot naknadno misel. Odprite shranjeno datoteko v programu Acrobat in potrdite, da so vrednosti vidne, preden se dotaknete katerega koli polja. Nato jo natisnite v PDF ali izvozite v sliko iz drugega pregledovalnika (takšnega, ki popolnoma prezre logiko obrazca), in potrdite, da so vrednosti vidne tudi tam. Ti dve preverjanji bosta skupaj ulovili vsako odstopanje med vnosoma /V in /AP.
Konfiguracije polj, ki uspešno prestanejo predstavitev, a spodletijo v praksi
Čisti predstavitveni obrazci pogosto prikrivajo vrsto robnih primerov, ki jih strankine datoteke vsebujejo. Štiri izmed njih so odgovorni za večino poročil tipa »na mojem računalniku je delovalo«.
- Izvozne vrednosti potrditvenih polj. Stanje vključenosti ni vedno
Yes. Obrazec lahko poljubno definira svojo izvozno vrednost. Če zapišete napačen niz, bo polje ostalo vizualno neoznačeno, čeprav bo vaša koda prepričana, da ga je nastavila. Preberite izvozno vrednost izFormFieldInfo[], namesto da jo predvidevate. - Skupine izbirnih gumbov z enakim imenom. Eno polje, več gradnikov. Vrednost, ki jo dodelite, odloča o tem, kateri gradnik bo označen kot izbran. Koda uporabniškega vmesnika, ki predvideva preslikavo enega imena v en pravokotnik, bo na koncu izrisala fokusni okvir na napačnem gumbu.
- Izračunana polja. Skupne vsote, ki jih upravlja dokumentni JavaScript, se posodabljajo kot odziv na dogodke v poljih. Programsko izpolnjevanje, ki zaobide te dogodke, mora bodisi sprožiti ponovni izračun ali pa neposredno prepisati izračunana polja. Obrazec, v katerem se posamezne postavke in končna vsota ne ujemajo, je slabša možnost od katere koli rešitve.
- Skrita obvezna polja. Pogojni obrazci pogosto skrijejo polja, ki so še vedno označena kot obvezna. Vnaprej se odločite, ali bo vaše preverjanje upoštevalo vidnost ali zgolj surovo zastavico obveznega polja, to odločitev pa zapišite nekam, kjer jo podporna ekipa lahko najde.
Eno razliko je vredno razčistiti, preden povzroči težave: generiranje videza ni enako sploščitvi (flattening). Metoda GenerateFormAppearances naredi vrednosti vidne povsod, polja pa pusti uredljiva. Sploščitev zapeče videz v statično vsebino strani in trajno odstrani interaktivnost, kar je primerno za arhivsko kopijo, a napačno za obrazec, ki ga mora naslednja oseba še izpolniti. Če FormType vrne ftXfaFull namesto ftAcroForm, nobeno od tukaj opisanih urejanj ne bo delovalo pravilno, saj se dokument izrisuje iz lastne predloge XML; zaznajte ta primer in o tem obvestite uporabnika, namesto da sami naletijo na to omejitev.
Podsistem za izpolnjevanje obrazcev, premikanje fokusa in generiranje videza, prikazani tukaj, so del paketa PDFium Component za Delphi, C++Builder in Lazarus/FPC. Če vaš pregledovalnik poleg podatkov o obrazcih obravnava tudi pregledovalčeve označbe, članek o pregledu anotacij opisuje ta sorodni model.