Samotné pole PDF formulára je len políčko, ktoré obsahuje hodnotu. To, čo robí formulár podobným malej aplikácii, je k nemu pripojená akcia: kliknutie, ktoré skryje sekciu, načíta uložené hodnoty zo súboru, preskočí na poslednú stránku alebo spustí skript, ktorý spočíta stĺpec. Nič z toho nežije v samotnom poli. Žije to v slovníku akcií a norma ISO 32000-1 organizuje celú túto rodinu v §12.6. Tento článok prechádza akcie, po ktorých program v Delphi siaha najčastejšie, a ukazuje, ako PDFlibPas prepája každú z nich s poľom alebo odkazom.
Mentálny model, ktorý stojí za to si zapamätať, je, že pole a akcia sú samostatné objekty spojené odkazom. Anotácia widgetu alebo anotácia odkazu nesie akciu vo svojej položke /A. Akcia pomenúva pole, s ktorým pracuje, jeho názvom, nie indexom, takže názov, ktorý priradíte poľu, je identifikátorom, ktorý každá neskoršia akcia používa na jeho vyhľadanie. Akonáhle je toto rozdelenie jasné, API prestane vyzerať ako nesúrodá sada volaní a začne vyzerať ako jeden vzor aplikovaný na štyri druhy slovies.
Pomenované akcie: navigácia bez čísla stránky
Najjednoduchšie akcie nenesú vôbec žiadne parametre. ISO 32000-1 §12.6.4.11, tabuľka 194, definuje pomenované akcie: prehliadač interpretuje symbolický názov za behu namiesto sledovania uloženého cieľa. Univerzálne podporované sú štyri názvy, a sú to presne tie, ktoré čitateľ očakáva na paneli nástrojov: NextPage, PrevPage, FirstPage a LastPage. Keďže cieľ je relatívny k tomu, ktorú stránku prehliadač práve zobrazuje, tlačidlo Ďalší vytvorené týmto spôsobom funguje na každej stránke bez toho, aby ste museli počítať cieľovú stránku.
V PDFlibPas je pomenovaná akcia pripojená k aktívnemu obdĺžniku (hotspotu) na aktuálnej stránke. Štvrtý a piaty celočíselný argument vyberajú sloveso a vzhľad.
// NamedActionType: 0 = NextPage, 1 = PrevPage, 2 = FirstPage, 3 = LastPage
// Options bit 0 (value 1) draws a border around the hotspot
Pdf.AddLinkToNamedAction(500, 560, 60, 18, 0, 1); // Next
Pdf.AddLinkToNamedAction(40, 560, 60, 18, 1, 1); // Previous
Pdf.AddLinkToNamedAction(110, 560, 60, 18, 3, 1); // jump to last page
Neexistuje žiadny cieľ, ktorý by bolo potrebné synchronizovať, čo je hlavným zmyslom. Pomenovaná akcia prežije vloženie aj vymazanie stránky, pretože na prvom mieste nikdy nepomenúva konkrétnu stránku. Porovnajte to s explicitným odkazom prechodu (go-to link), ktorý ukladá index cieľovej stránky, ktorý musíte prečíslovať v momente, keď sa dokument rozrastie.
Akcia Hide a jej chyták s polom
Akcia Hide, ISO 32000-1 §12.6.4.10, tabuľka 196, prepína viditeľnosť jedného alebo viacerých polí. Je to najčistejší spôsob, ako vytvoriť správanie zobrazenia a skrytia bez skriptovania, a je to presne to, čo chcete pre odkaz typu Zobraziť podrobnosti alebo pre dva navzájom sa vylučujúce panely, kde odhalenie jedného skryje druhý. Akcia nesie cieľ vo svojej položke /T a boolovskú hodnotu /H, ktorá určuje smer: skryť pri hodnote true, zobraziť pri false.
Jemnosť spočíva výhradne v tom, ako je tento cieľ zakódovaný, a je to presne ten typ detailu, ktorý spôsobí, že formulár funguje na vašom počítači, ale zlyhá u zákazníka. Keď akcia pomenúva jediné pole, /T sa zapíše ako jeden textový reťazec. Keď ich pomenúva viacero, /T sa zapíše ako pole textových reťazcov. Staršie prehliadače nezaobchádzajú s jednoprvkovým polom rovnako ako s holým reťazcom, takže kódovanie sa musí vetviť podľa počtu: jeden názov must byť emitovaný ako reťazec, nie ako pole dĺžky jedna, ak ho má rešpektovať čo najširší okruh čítačiek. PDFlibPas robí toto rozhodnutie za vás. Názvy polí odovzdávate oddelené čiarkami, bodkočiarkami alebo zlomom riadku a zapisovač vygeneruje jeden reťazec pre jeden názov a pole pre dva alebo viac názvov.
// HideFlag non-zero hides the listed fields (/H true); zero shows them.
// One name -> /T is a text string. Two or more -> /T is an array of strings.
Pdf.AddLinkToHideField(40, 700, 90, 18, 'ShippingAddress', 1, 1);
Pdf.AddLinkToHideField(140, 700, 90, 18,
'ShippingName,ShippingAddress,ShippingZip', 1, 1);
Keďže akcia neodkazuje na žiadny externý zdroj, zostáva kompatibilná s PDF/A. Názvy, ktoré odovzdávate, sú plne kvalifikované názvy polí, a preto musí byť dcérske pole v skupine adresované cez svoju úplnú bodkovanú cestu, a nie len cez svoj samostatný názov listu.
ImportData: predvyplnenie z FDF
Zatiaľ čo akcia Hide mení usporiadanie toho, čo už na stránke je, akcia importu dát prináša hodnoty zvonku. ISO 32000-1 §12.6.4.8, tabuľka 198, ju definuje ako akciu, ktorá vypĺňa AcroForm zo súboru formátu FDF (Forms Data Format) na disku. Toto je akcia za ovládacími prvkami typu Znova načítať vzorové dáta alebo Obnoviť predvolené nastavenia, kde sa súbor FDF dodáva vedľa PDF a obsahuje kanonické hodnoty polí. Volanie zrkadlí ostatné, berie aktívny obdĺžnik, cestu k FDF a masku vzhľadu: Pdf.AddLinkToImportData(40, 660, 120, 18, 'defaults.fdf', 1). Súbor nemusí existovať pri vytváraní PDF, ale musí byť prítomný, keď používateľ klikne, a všetky spätné lomky v ceste sa automaticky prepíšu do kanonického tvaru lomky PDF.
Jedno obmedzenie stojí za to uviesť jasne, pretože je často prekvapením. Akcia importu dát ukazuje na externý súbor, takže v PDF/A nie je povolená. Keď je dokument v režime PDF/A, volanie vráti nulu a nepridá nič, namiesto toho, aby vytvorilo súbor, ktorý zlyhá pri validácii. Ak vaša spracovateľská reťaz mieri na archívny výstup, predvyplnenie sa musí uskutočniť v čase generovania priamym zápisom hodnôt polí, nie odložením na kliknutie.
JavaScript: globálne balíky a skripty pre jednotlivé akcie
Pre logiku, ktorá presahuje zobrazovanie, skrývanie a import, rodina akcií siaha po JavaScripte na úrovni dokumentu. Existujú dve odlišné miesta, kde môže skript žiť, a na tomto rozdiele záleží. Globálny JavaScriptový balík na úrovni dokumentu sa ukladá raz pre celý súbor a spúšťa sa pri otvorení dokumentu, vďaka čomu je správnym domovom pre definície funkcií a zdieľaný stav. Skript pre konkrétnu akciu je pripojený k jednému odkazu alebo polu a spúšťa sa len vtedy, keď je tento objekt aktivovaný, čo z neho robí správne miesto pre ten jeden riadok, ktorý volá funkciu, ktorú balík už definoval.
PDFlibPas sprístupňuje oboje. AddGlobalJavaScript ukladá pomenovaný balík na úrovni dokumentu; opätovné použitie názvu nahradí všetko, čo bolo pod ním uložené. AddLinkToJavaScript pripojí skript k hotspotu, takže kliknutie ho spustí.
// Document-level package: define a reusable function once.
Pdf.AddGlobalJavaScript('Totals',
'function recalcTotal() {' +
' var net = this.getField("Net").value;' +
' var tax = this.getField("Tax").value;' +
' this.getField("Gross").value = Number(net) + Number(tax);' +
'}');
// Per-action script on a link: just call the shared function.
Pdf.AddLinkToJavaScript(40, 620, 100, 18, 'recalcTotal();', 1);
Uchovávanie funkcie v globálnom balíku a volania v odkaze nie je otázkou štýlu. Zabraňuje to duplikovaniu rovnakého tela na každom ovládacom prvku, ktorý ho potrebuje, a znamená to, že prehliadač s vypnutým skriptovaním pri kliknutí jednoducho neurobí nič, namiesto toho, aby zlyhal na nesprávne naformátovanom inline objekte. Tiež to udržuje položky jednotlivých akcií malé, čo zachováva čitateľnosť súboru pri jeho neskoršom skúmaní.
Polia, dcérske polia a zmrazenie výsledku
Akcie potrebujú polia, na ktoré môžu pôsobiť, preto pomôže vidieť, ako pole vzniká. NewFormField vytvorí pole na aktuálnej stránke a vráti jeho index; celočíselný typ vyberá druh, kde 1 je Text, 2 je Pushbutton, 3 je Checkbox, 4 je Radiobutton, 5 je Choice, 6 je Signature a 7 je Parent, ktorý vlastní dcérske prvky, ale sám nič nevykresľuje. Názov, ktorý odovzdáte, nemôže obsahovať bodku, pretože bodka je oddeľovačom v plne kvalifikovaných názvoch, ktoré akcie používajú na adresovanie dcérskych prvkov.
Rádiové skupiny a hierarchické formuláre sa vytvárajú tak, že sa rodičovskému polu priradia dcérske polia. NewChildFormField pridá dcérske pole pod pomenovaného rodiča a v prípade prepínačov a výberov AddFormFieldSub pridá jednotlivé možnosti a vráti dočasný index, ktorý použijete na umiestnenie každej z nich. Keď sa interaktívna fáza skončí a vy chcete pole zmraziť, aby sa jeho aktuálny vzhľad stal trvalým obsahom stránky, FlattenFormField vykreslí pole na stránku a odstráni ho z formulára. Po sploštení (flatten) sa indexy neskorších polí posunú o jeden nadol, čo je jediná vec, na ktorú treba pamätať, ak splošťujete niekoľko polí v cykle.
var
Pdf: TPDFlib;
FldShip: Integer;
begin
Pdf := TPDFlib.Create;
try
Pdf.SetOrigin(1); // top-left origin
Pdf.SetPageSize('A4');
Pdf.NewPage;
// A text field the Hide action will target by its title.
FldShip := Pdf.NewFormField('ShippingAddress', 1);
Pdf.SetFormFieldBounds(FldShip, 40, 120, 240, 20);
Pdf.SetFormFieldValue(FldShip, '');
// Wire a Hide link and a navigation link to this page.
Pdf.DrawText(40, 110, 'Toggle shipping block:');
Pdf.AddLinkToHideField(220, 100, 70, 16, 'ShippingAddress', 1, 1);
Pdf.AddLinkToNamedAction(500, 800, 60, 18, 3, 1); // Last page
// A document-level script available to every event in the file.
Pdf.AddGlobalJavaScript('OnOpen',
'app.alert("Form ready", 3);');
// Freeze the field if the output should no longer be editable.
// Pdf.FlattenFormField(FldShip);
if Pdf.SaveToFile('form_actions.pdf') <> 1 then
raise Exception.Create('Save failed');
finally
Pdf.Free;
end;
end;
Volanie flatten je zakomentované úmyselne. Ak ho vynecháte, dokument sa odošle ako živý formulár, ktorého akcie sa spúšťajú v čítačke. Ak ho povolíte, pole sa vykreslí do statických značiek, čo je presne to, čo chcete, keď bol formulár dokončený a výsledok by mal cestovať ako pevný záznam. Rovnaké pole, rovnaký kód, dva veľmi odlišné dokumenty v závislosti od toho, či ho zmrazíte.
Výber správneho slovesa
Štyri akcie sa jasne delia podľa toho, čoho sa týkajú. Pomenovaná akcia posúva výrez zobrazenia a nepotrebuje žiadne pole. Akcia Hide mení viditeľnosť a vyžaduje názvy polí, pričom kódovanie reťazca versus poľa sa rieši za vás. Akcia importu dát pristupuje k súboru na disku, a preto je v PDF/A zakázaná. Akcia JavaScriptu spúšťa ľubovoľnú logiku a najlepšie je rozdeliť ju na globálny balík funkcií a malé volania pre jednotlivé akcie. Siahnite po najjednoduchšej z nich, ktorá splní úlohu: akcia Hide je prenosnejšia ako skript, ktorý nastavuje príznak skrytia, a pomenovaná akcia je odolnejšia ako uložený cieľ stránky, pretože netreba udržiavať žiadne číslo.
Odtiaľto obraz dopĺňajú dve susediace témy. Ak je formulár súčasťou prístupného dokumentu, strom štruktúry, ktorým prechádzajú čítačky obrazovky, je popísaný v našom články o tagovanom PDF a štruktúre prístupnosti. Keď musí byť dokončený formulár uzamknutý a podpísaný, pracovný postup je opísaný v sprievodcovi pracovným priestorom pre zhodu a podpisovanie. Všetky tri stavajú na rovnakom engine, ktorý sa dodáva ako PDF knižnica pre Delphi spolu s rozhraniami API pre vytváranie, formuláre a podpisy, ktoré sú popísané inde na tomto blogu.