Pritisnite taster Tab u PDF obrascu koji je vaš kod napravio, i kursor će sleteti dva polja dalje od mesta gde bi trebalo, ili će potpuno preskočiti drugu kolonu, ili će se nakon trećeg polja vratiti na vrh umesto na četvrto. Osoba koja popunjava fakturu u vašem čitaču očekuje da se tastatura kreće kroz obrazac na isti način na koji se kreće kroz svaki veb obrazac koji je ikada koristila. Kada se to ne desi, oni posežu za mišem, traže sledeće polje i tiho odlučuju da je vaša alatka nedovršena. Predvidljivo kretanje kroz polja je razlika između čitača za unos podataka koji korisnici tolerišu i onog kojem veruju, a to je gotovo u potpunosti stvar korišćenja ispravnog API-ja za fokus umesto lažiranja unosa sa tastature simuliranim klikovima.
Primeri u nastavku koriste PDFium Component, VCL/LCL komponentu zasnovanu na PDFium-u za Delphi, C++Builder i Lazarus. Navigacija je jedna od tri stvari koje čitač obrazaca mora ispravno da uradi; druge dve, ispravno otvaranje obrasca i čuvanje popunjenih vrednosti tako da se one zapravo prikazuju, jesu mesta gde se krije većina iznenađenja, pa su sve tri pokrivene u nastavku.
Otvaranje obrasca: FormFill, FormType i pitanje XFA
Pristup poljima zahteva da podsistem za popunjavanje obrazaca, kontrolisan svojstvom FormFill, bude omogućen pre otvaranja dokumenta. Kada je aktivan, FormType vam govori sa kojom vrstom obrasca se suočavate, a odgovor menja skup funkcija 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 izbora proizilaze dve praktične napomene. AcroForm je standardni ISO 32000 model obrazaca i on je cilj svakog API-ja ovde. XFA dokumenti ugrađuju sopstvenu XML arhitekturu obrazaca, tako da je obećanje klijentu o potpunom uređivanju XFA obrazaca nakon brze AcroForm demonstracije obaveza koju ćete zažaliti. Druga napomena se odnosi na neželjene efekte: postavljanje FormFill na True takođe inicijalizuje JavaScript u dokumentu. U čitaču za unos podataka to je upravo ono što je potrebno, jer skripte za proračun održavaju tekući zbir ažurnim dok korisnik kuca. U prozoru za pregled datoteka nepoznatog porekla to je potpuno pogrešno. Članak o bezbednom pregledu PDF-a pokriva stranu tog kompromisa kada je FormFill := False.
Kretanje tasterom Tab koje vodi tamo gde korisnici očekuju
Nazad na problem sa tastaturom sa početka. Iskušenje je lažirati taster Tab sintetisanjem klika mišem na pravougaonik sledećeg vidžeta, što puca čim se polje skroluje van ekrana ili se dva vidžeta preklapaju. API za fokus umesto toga pomera fokus samog obrasca direktno, bez nagađanja geometrije. Pet poziva pokriva ovo: FocusFormField po 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 ponašanja koji zbunjuje ljude jeste prelamanje (wrap). Kretanje radi kroz redosled tabulatora trenutne stranice i ide u petlju unutar nje: zakoračite iza poslednjeg polja i vraćate se na prvo. Obe funkcije za koračanje vraćaju indeks novog polja, ili -1 kada stranica uopšte nema polja. To ponavljanje u petlji je po stranici, a ne po dokumentu, što znači da je prelazak na sledeću stranicu vaš posao, a ne posao biblioteke. Uporedite vraćeni indeks sa onim od kojeg ste počeli, primetite kada se vratio na početak i sami pomerite PageNumber ako je predviđeno da se obrazac čita kao jedan neprekidan niz. Preskočite tu proveru i obrazac od dve stranice tiho zarobljava kursor na prvoj stranici, što je posebna varijacija žalbe na neispravan Tab taster.
Kretanje postaje korisno tek kada ostatak korisničkog interfejsa reaguje na njega. Događaj OnFormFieldEnter se ispaljuje kada fokus stigne, a na čitaču OnFormFieldFocusChange prijavljuje novi indeks polja, tako da bočni panel može ostati usklađen sa onim što je tastatura upravo izabrala. Kada vam zatreba obrnuto mapiranje, sa pozicije na ekranu do polja, indeksirano svojstvo FormFieldAt vrši testiranje pogotka za preglede opisa (tooltips) i panele klikni-za-uređivanje. U svemu ovome postoji i tiha korist za pristupačnost: pošto fokus prati sopstveni redosled polja u dokumentu, putanja koju povežete za taster Tab jeste ista putanja koju najavljuje čitač ekrana, bez ikakvog dodatnog rada.
Prikazivanje imena polja umesto sirovih indeksnih brojeva zahteva još jedno svojstvo. FormFieldInfo[] vraća zapis TPdfFormFieldInfo po indeksu, koji sadrži ime polja, tip, veličinu fonta, stanje označenosti, izvoznu vrednost i članstvo u grupi, što je upravo ono što navigaciona lista treba da prikaže („Polje 4 od 17: InvoiceDate“ umesto samo „4“). Radio grupe su slučaj vredan namenske test datoteke. Nekoliko vidžeta može deliti jedno ime polja, tako da lista sastavljena naivno od vidžeta prikazuje istu grupu nekoliko puta i zbunjuje svakoga ko je čita.
Zašto unete vrednosti ispadaju prazne i poziv koji to rešava
Druga žalba koja puni redove podrške je alarmantnija od pogrešnog ponašanja tastera Tab: obrazac se popunjava programski, korisnik ga otvara u Acrobat-u, a svako polje izgleda prazno. Kliknite na polje i njegova vrednost se odmah pojavljuje. Podaci su u datoteci sve vreme. Ono što nedostaje jeste slika podataka, a razlog vredi razumeti jednom jer objašnjava čitavu familiju grešaka.
Tekstualno polje AcroForm-a čuva svoju vrednost u unosu /V u rečniku polja (ISO 32000-1 §12.7.3.3). Ono što čitač zapravo iscrtava je nešto odvojeno: tok izgleda vidžeta pod /AP (§12.5.5), mali unapred iscrtani isečak sadržaja. Upišite /V a ostavite /AP na miru, i to dvoje će se razići. Vrednost je tu; njena iscrtana verzija je zastarela ili nedostaje. Acrobat slučajno obnavlja izgled polja kada ono dobije fokus, što je jedino objašnjenje za vrednosti koje se pojavljuju tek na klik. Stara zastavica NeedAppearances, koja je tražila od čitača da generišu izglede za vas, nikada nije radila ujednačeno i zastarela je u PDF-u 2.0, a serveri za štampanje i generatori sličica je potpuno ignorišu. Oni iscrtavaju /AP i ništa drugo, pa ako je /AP prazan, štampaju prazan okvir.
Dodeljivanje vrednosti preko FormField[i] upisuje samo /V. Zato je popunjavanje obrasca sekvenca od tri koraka, a korak koji programeri ispuštaju je onaj u sredini:
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 kompletno rešenje. Ono ponovo gradi tok izgleda svakog vidžeta na osnovu trenutnih vrednosti, fontova i poravnanja, tako da čitač koji nikada ne pokreće događaj fokusa, server za štampanje ili generator sličica ipak iscrtava popunjeno stanje. Pozovite to jednom nakon grupe dodeljivanja vrednosti, a ne jednom po polju. Generisanje izgleda radi pravi posao rasporeda, a pozivi po polju bespotrebno množe taj rad na velikom obrascu.
Regenerisanje izgleda je takođe trenutak kada se nameću fontovi i poravnanje, što je izvor iznenađenja drugog reda. Novi tok raspoređuje svaku vrednost unutar pravougaonika vidžeta koristeći font, veličinu i poravnanje polja. Vrednost koja se udobno smestila u vaš test obrazac može se skratiti ili smanjiti u korisnikovoj kopiji gde je isto polje uže. Polja sa automatskom veličinom (veličina fonta nula) smanjuju tekst da bi se uklopio; polja fiksne veličine ga jednostavno skraćuju. Oba su legalna, i jedini pošten način da saznate šta određeni obrazac radi jeste da pogledate regenerisani izlaz, a ne string koji ste upisali. Kada neko prijavi da je tekst odsečen na ivici okvira, to je skoro uvek razlog.
Tretirajte verifikaciju kao deo završetka rada, a ne kao naknadnu misao. Otvorite sačuvanu datoteku u Acrobat-u i potvrdite da su vrednosti vidljive pre nego što dodirnete bilo koje polje. Zatim je odštampajte u PDF ili u sliku iz drugog čitača, onog koji potpuno ignoriše logiku obrazaca, i potvrdite da vrednosti preživljavaju i tu putanju. Između njih, te dve provere hvataju svaku varijaciju razilaženja između /V i /AP vrednosti.
Konfiguracije polja koje prolaze na demonstraciji, a padaju na terenu
Čisti demo obrasci kriju skup graničnih slučajeva koje korisničke datoteke ne kriju. Četiri od njih čine većinu izveštaja tipa „radilo je na mojoj mašini“.
- Izvozne vrednosti za potvrdu (Checkbox export values). Stanje „uključeno“ nije uvek
Yes. Obrazac je slobodan da definiše sopstvenu izvoznu vrednost, a upisivanje pogrešnog stringa ostavlja polje vizuelno neoznačeno dok je vaš kod uveren da ga je postavio. Pročitajte izvoznu vrednost izFormFieldInfo[]umesto da je pretpostavljate. - Radio grupe sa zajedničkim imenom. Jedno polje, nekoliko vidžeta. Vrednost koju dodelite određuje koji se vidžet očitava kao izabran, tako da kod korisničkog interfejsa koji pretpostavlja da se jedno ime mapira na jedan pravougaonik završava crtanjem prstena fokusa na pogrešnom dugmetu.
- Izračunata polja. Ukupni iznosi koje održava JavaScript u dokumentu ažuriraju se kao odgovor na događaje u poljima. Programsko popunjavanje koje zaobilazi te događaje mora ili da pokrene ponovni proračun ili da direktno prepiše izračunata polja. Obrazac u kojem se stavke i ukupan iznos ne slažu gori je od bilo kog rešenja.
- Skrivena obavezna polja. Uslovni obrasci kriju polja koja su i dalje označena kao obavezna. Odlučite unapred da li vaša validacija poštuje vidljivost ili sirovu zastavicu obaveznosti, a zatim zapišite tu odluku negde gde je podrška može pronaći.
Jednu razliku vredi razjasniti pre nego što vas ugrize: generisanje izgleda nije spljoštavanje (flattening). GenerateFormAppearances čini vrednosti vidljivim svuda, ostavljajući polja uredivim. Spljoštavanje peče izgled u statički sadržaj stranice i trajno uklanja interaktivnost, što je ispravno za arhivsku kopiju, a pogrešno za obrazac koji sledeća osoba još uvek mora da popuni. Ako FormType prijavljuje ftXfaFull umesto ftAcroForm, ništa od ovde opisanih funkcija za uređivanje se ne primenjuje čisto, pošto se dokument iscrtava iz sopstvenog XML šablona; detektujte taj slučaj i obavestite korisnika, umesto da ih pustite da sami pronađu to ograničenje.
Podsistem za popunjavanje obrazaca, kretanje kroz fokus i generisanje izgleda prikazani ovde deo su PDFium Component za Delphi, C++Builder i Lazarus/FPC. Ako vaš čitač takođe rukuje oznakama pregledača uporedo sa podacima obrasca, članak o pregledu anotacija pokriva taj susedni model.