Pritisnite Tab u PDF obrascu koji je stvorio vaš kod, i pokazivač će sletjeti dva polja dalje od mjesta na kojem bi trebao biti, ili će potpuno preskočiti drugi stupac, ili će se nakon trećeg polja vratiti na vrh umjesto na četvrto. Osoba koja popunjava račun u vašem pregledniku očekuje da se tipkovnica kreće kroz obrazac na isti način na koji se kreće kroz svaki web obrazac koji su ikada koristili. Kada se to ne dogodi, oni posežu za mišem, traže sljedeći okvir i tiho zaključuju da je vaš alat nedovršen. Predvidljivo kretanje kroz polja čini razliku između preglednika za unos podataka koji ljudi toleriraju i onoga kojem vjeruju, a to je gotovo u potpunosti stvar korištenja ispravnog API-ja za fokus umjesto lažiranja unosa s tipkovnice pomoću simuliranih klikova.
Primjeri u nastavku koriste PDFium komponentu (PDFium Component), VCL/LCL komponentu temeljenju na PDFiumu za Delphi, C++Builder i Lazarus. Navigacija je jedna od tri stvari koje preglednik obrazaca mora ispravno odraditi; druge dvije, ispravno otvaranje obrasca i spremanje popunjenih vrijednosti kako bi se one stvarno prikazale, mjesta su na kojima se krije najviše iznenađenja, pa su sve tri teme pokrivene u nastavku.
Otvaranje obrasca: FormFill, FormType, i pitanje XFA-a
Pristup poljima zahtijeva da se podsustav za popunjavanje obrazaca, kojim upravlja svojstvo FormFill, omogući prije otvaranja dokumenta. Nakon što je aktivan, FormType vam govori s kojom vrstom obrasca se suočavate, a odgovor mijenja skup značajki koje možete obećati:
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 tog prebacivanja proizlaze dvije praktične napomene. AcroForm je standardni model obrazaca prema ISO 32000 i cilj je svakog API-ja ovdje. XFA dokumenti ugrađuju vlastitu XML arhitekturu obrazaca, pa je obećanje korisniku o potpunom uređivanju XFA-a nakon brze demonstracije AcroForma obveza zbog koje ćete požaliti. Druga napomena odnosi se na nuspojave: postavljanje svojstva FormFill na True također pokreće JavaScript u dokumentu. U pregledniku za unos podataka to je točno ono što želite, cuz skripte za izračun su one koje održavaju tekući zbroj točnim dok korisnik tipka. U prozoru za pregled datoteka nepoznatog podrijetla to je potpuno pogrešno. Članak o sigurnom pregledu PDF-a pokriva drugu stranu tog kompromisa s postavkama FormFill := False.
Kretanje tipkom Tab koje završava tamo gdje korisnici očekuju
Vratimo se na problem s tipkovnicom s početka. Iskušenje je lažirati pritisak na Tab sintetiziranjem klika mišem na pravokutnik sljedećeg widgeta, što se prekida u trenutku kada se polje pomakne izvan zaslona ili se dva widgeta preklapaju. Umjesto toga, API za fokus izravno pomiče fokus samog obrasca, bez ikakvog nagađanja o geometriji. Pet poziva pokriva to: FocusFormField prema indeksu, FocusNextFormField i FocusPreviousFormField za korake, FocusedFormFieldIndex za čitanje trenutne pozicije i ClearFormFieldFocus za potpuno uklanjanje 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;
Jedan detalj u ponašanju koji zbunjuje ljude je omatanje (wrap). Kretanje radi kroz redoslijed tabulatora trenutne stranice i kruži unutar njega: zakoračite nakon zadnjeg polja i vraćate se na prvo. Obje funkcije kretanja vraćaju indeks novog polja, ili -1 kada stranica uopće ne sadrži polja. To kruženje je po stranici, a ne po dokumentu, što znači da je prijelaz na sljedeću stranicu vaš posao, a ne posao knjižnice. Usporedite vraćeni indeks s onim od kojeg ste krenuli, uočite kada se omotao i sami promijenite PageNumber ako je obrazac zamišljen da se čita kao jedan neprekinuti niz. Preskočite tu provjeru i obrazac na dvije stranice tiho će zarobiti pokazivač na prvoj stranici, što je posebna verzija pritužbe na neispravan Tab.
Kretanje postaje korisno kada ostatak korisničkog sučelja reagira na njega. Događaj OnFormFieldEnter se aktivira kako fokus stiže, a na pregledniku OnFormFieldFocusChange javlja indeks novog polja, tako da bočna ploča može ostati usklađena sa svime što je tipkovnica upravo odabrala. Kada trebate obrnuto mapiranje, s pozicije zaslona na polje, indeksirano svojstvo FormFieldAt provodi testiranje pogotka za preglede opisa (tooltips) i ploče za klik-za-uređivanje. U svemu tome postoji i tiha korist za pristupačnost: budući da fokus prati vlastiti redoslijed polja u dokumentu, put koji postavite za tipku Tab isti je put koji čitač zaslona najavljuje, bez ikakvog dodatnog rada.
Prikazivanje naziva polja umjesto sirovih indeksnih brojeva zahtijeva još jedno svojstvo. FormFieldInfo[] vraća zapis TPdfFormFieldInfo po indeksu, koji sadrži naziv polja, vrstu, veličinu fonta, označeno stanje, izvoznu vrijednost i članstvo u grupi, što je ono što bi navigacijski popis trebao prikazati (npr. "Polje 4 od 17: InvoiceDate" umjesto "4"). Radio grupe su slučaj koji zaslužuje posebnu testnu datoteku. Nekoliko widgeta može dijeliti jedan naziv polja, pa popis sastavljen naivan način iz widgeta prikazuje istu grupu nekoliko puta i zbunjuje svakoga tko ga čita.
Zašto popunjene vrijednosti ispadaju prazne i poziv koji to rješava
Druga pritužba koja puni redove podrške alarmantnija je od lošeg ponašanja tipke Tab: obrazac se popunjava programski, korisnik ga otvara u Acrobatu i svako polje izgleda prazno. Kliknite u polje i njegova vrijednost skoči u vidno polje. Podaci su u datoteci cijelo vrijeme. Ono što nedostaje je slika podataka, a razlog vrijedi jednom razumjeti jer objašnjava cijelu obitelj pogrešaka.
Tekstualno polje AcroForma pohranjuje svoju vrijednost u unos /V rječnika polja (ISO 32000-1 §12.7.3.3). Ono što preglednik zapravo iscrtava je nešto zasebno: strujanje izgleda widgeta pod /AP (§12.5.5), mali unaprijed iscrtani isječak sadržaja. Upišite /V i ostavite /AP na miru, i to dvoje će se razići. Vrijednost je tu; njezina iscrtana verzija je zastarjela ili odsutna. Acrobat ponovno izgrađuje izgled polja kada ono dobije fokus, što je cijelo objašnjenje za vrijednosti koje se pojavljuju tek na klik. Stara zastavica NeedAppearances, koja je tražila od preglednika da sami regeneriraju izgled, nikada nije radila jednoliko i zastarjela je u PDF-u 2.0, a poslužitelji za ispis i generatori sličica je potpuno zanemaruju. Oni iscrtavaju /AP i ništa drugo, pa ako je /AP prazan, ispisuju prazan okvir.
Dodjeljivanje vrijednosti putem FormField[i] upisuje samo /V. Zato je popunjavanje obrasca slijed od tri koraka, a korak koji razvojni timovi ispuštaju je onaj srednji:
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;
GenerateFormAppearances je cjelokupno rješenje. Ono ponovno gradi strujanje izgleda svakog widgeta na temelju trenutnih vrijednosti, fontova i poravnanja, pa preglednik koji nikada ne pokreće događaj fokusa, poslužitelj za ispis ili generator sličica svejedno iscrtava popunjeno stanje. Pozovite ga jednom nakon grupe dodjela, a ne jednom po polju. Generiranje izgleda obavlja stvarni posao rasporeda, a pozivi po svakom polju uzalud ga umnožavaju na velikom obrascu.
Regeneriranje izgleda je također trenutak u kojem fontovi i poravnanje dolaze do izražaja, što je izvor sekundarnog iznenađenja. Novo strujanje postavlja svaku vrijednost unutar pravokutnika widgeta koristeći font, veličinu i poravnanje polja. Vrijednost koja udobno stane u vaš testni obrazac može se odrezati ili smanjiti u korisnikovoj kopiji gdje je isto polje uže. Polja s automatskom veličinom (veličina fonta nula) smanjuju tekst kako bi stao; polja s fiksnom veličinom ga jednostavno izrezuju. Oba su načina dopuštena, a jedini pošteni način da saznate što određeni obrazac radi jest da pogledate regenerirani izlaz, a ne niz koji ste napisali. Kada netko prijava kvara da je tekst odsječen na rubu okvira, to je gotovo uvijek razlog.
Tretirajte provjeru kao dio završetka posla, a ne kao naknadnu misao. Otvorite spremljenu datoteku u Acrobatu i potvrdite da su vrijednosti vidljive prije nego što dodirnete bilo koje polje. Zatim je ispišite u PDF ili u sliku iz drugog preglednika, onog koji u potpunosti zanemaruje logiku obrasca, i potvrdite da vrijednosti preživljavaju i taj put. Između njih, te dvije provjere hvataju svaku varijantu razilaženja između /V i /AP.
Konfiguracije polja koje prolaze demonstraciju, a ne uspijevaju u praksi
Čisti demonstracijski obrasci skrivaju niz graničnih slučajeva koje korisničke datoteke ne skrivaju. Četiri od njih objašnjavaju većinu izvješća tipa "radilo je na mom računalu".
- Izvozne vrijednosti potvrdnih okvira. Stanje "uključeno" nije uvijek
Yes. Obrazac može slobodno definirati vlastitu izvoznu vrijednost, a upisivanje pogrešnog niza ostavlja okvir vizualno neoznačenim dok je vaš kod uvjeren da ga je postavio. Pročitajte izvoznu vrijednost izFormFieldInfo[]radije nego da je pretpostavljate. - Radio grupe s dijeljenim nazivom. Jedno polje, nekoliko widgeta. Vrijednost koju dodijelite određuje koji se widget čita kao odabran, pa kod korisničkog sučelja koji pretpostavlja da se jedno ime mapira na jedan pravokutnik na kraju crta fokusni prsten na pogrešnom gumbu.
- Izračunata polja. Ukupni iznosi koje održava JavaScript dokumenta ažuriraju se kao odgovor na događaje u polju. Programsko popunjavanje koje zaobilazi te događaje mora ili pokrenuti ponovni izračun ili izravno prepisati izračunata polja. Obrazac u kojem se stavke i ukupni iznos ne podudaraju lošiji je od bilo kojeg od ova dva rješenja.
- Skrivena obavezna polja. Uvjetni obrasci skrivaju polja koja su još uvijek označena kao obavezna. Odlučite unaprijed poštuje li vaša provjera valjanosti vidljivost ili sirovu zastavicu obaveznosti, a zatim zapišite tu odluku negdje gdje je podrška može pronaći.
Jednu razliku vrijedi razjasniti prije nego što vas iznenadi: generiranje izgleda nije spljoštavanje (flattening). GenerateFormAppearances čini vrijednosti vidljivima svugdje, ostavljajući polja uredivima. Spljoštavanje ugrađuje izgled u statički sadržaj stranice i trajno uklanja interaktivnost, što je ispravno za arhivsku kopiju, a pogrešno za obrazac koji sljedeća osoba još mora popuniti. Ako FormType javlja ftXfaFull umjesto ftAcroForm, ništa od ovdje opisanog sučelja za uređivanje ionako se ne može čisto primijeniti, budući da se dokument iscrtava iz vlastitog XML predloška; otkrijte taj slučaj i obavijestite korisnika, umjesto da ih pustite da sami pronađu to ograničenje.
Podsustav za popunjavanje obrazaca, kretanje fokusom i generiranje izgleda prikazani ovdje dio su PDFium komponente za Delphi, C++Builder i Lazarus/FPC. Ako vaš preglednik također rukuje recenzentskim oznakama uz podatke obrasca, članak o pregledu anotacija pokriva taj susjedni model.