Et PDF-skjemafelt i seg selv er bare en boks som inneholder en verdi. Det som får et skjema til å oppføre seg som en liten applikasjon er handlingen som er knyttet til det: et klikk som skjuler en seksjon, henter lagrede verdier fra en fil, hopper til siste side eller kjører et skript som summerer en kolonne. Ingenting av dette lever i feltet. Det lever i en handlingsordbok, og ISO 32000-1 organiserer hele familien i §12.6. Denne artikkelen går gjennom handlingene et Delphi-program oftest bruker, og viser hvordan PDFlibPas kobler hver av dem til et felt eller en lenke.
Den mentale modellen som er verdt å huske på, er at et felt og en handling er separate objekter som er knyttet sammen med en referanse. En skjermelement-annotering eller en lenke-annotering har en handling i sin /A-oppføring. Handlingen navngir feltet den opererer på med tittel, ikke etter indeks, så tittelen du gir til et felt er referansen hver senere handling bruker for å finne det. Når denne oppdelingen er tydelig, slutter API-et å se ut som en tilfeldig samling av kall og begynner å se ut som ett mønster brukt på fire typer verb.
Navngitte handlinger: navigasjon uten et sidenummer
De enkleste handlingene har ingen parametere i det hele tatt. ISO 32000-1 §12.6.4.11, tabell 194, definerer navngitte handlinger: visningsprogrammet tolker et symbolsk navn ved kjøretid i stedet for å følge en lagret destinasjon. Fire navn støttes universelt, og de er akkurat de en leser forventer fra en verktøylinje: NextPage, PrevPage, FirstPage og LastPage. Fordi destinasjonen er relativ til siden visningsprogrammet viser for øyeblikket, fungerer en Neste-knapp bygget på denne måten på hver side uten at du må beregne et mål.
I PDFlibPas er en navngitt handling knyttet til et aktivt rektangel på den gjeldende siden. Det fjerde og femte heltallsargumentet velger verbet og utseendet.
// 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
Det er ingen destinasjon å holde synkronisert, noe som er hele poenget. En navngitt handling overlever innsetting og sletting av sider fordi den aldri navngir en side i utgangspunktet. Kontraster det med en eksplisitt go-to-lenke, som lagrer en målsideindeks som du må omnummerere i det øyeblikket dokumentet vokser.
Hide-handlingen og dens array-fallgruve
Hide-handlingen, ISO 32000-1 §12.6.4.10, tabell 196, veksler synligheten til ett eller flere felt. Det er den reneste måten å bygge vis- og skjul-adferd på uten skripting, og det er det du vil ha for en Vis detaljer-lenke eller for to gjensidig utelukkende paneler der visning av det ene skjuler det andre. Handlingen har et mål i sin /T-oppføring og en boolsk /H som bestemmer retningen: skjul når true, vis når false.
Spissfindigheten ligger helt i hvordan dette målet er kodet, og det er typen detalj som produserer et skjema som fungerer på din maskin, men feiler hos en kunde. Når handlingen navngir et enkelt felt, skrives /T som én tekststreng. Når den navngir flere, skrives /T som et array av tekststrenger. Eldre visningsprogrammer behandler ikke et array med ett element på samme måte som de behandler en ren streng, så kodingen må forgrene seg basert på antallet: et enkelt navn må skrives som en streng, ikke som et array med lengde én, for at det bredeste utvalget av lesere skal godta det. PDFlibPas tar denne avgjørelsen for deg. Du sender feltnavn separert med komma, semikolon eller linjeskift, og skriveren sender ut en enkelt streng for ett navn og et array for to eller flere.
// 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);
Fordi handlingen ikke refererer til noen ekstern ressurs, forblir den kompatibel med PDF/A. Navnene du oppgir er fullt kvalifiserte felttitler, og det er derfor et barnefelt i en gruppe må adresseres via sin fulle punkt-separerte sti i stedet for bare sitt eget navn.
ImportData: forhåndsutfylling fra FDF
Der Hide-handlingen omorganiserer det som allerede er på siden, henter import-data-handlingen inn verdier utenfra. ISO 32000-1 §12.6.4.8, tabell 198, definerer det som en handling som populere AcroForm-skjemaet fra en Forms Data Format-fil på disken. Dette er handlingen bak en Last inn eksempeldata- eller Nullstill til standardverdier-kontroll, der en FDF-fil leveres ved siden av PDF-en og inneholder de kanoniske feltverdiene. Kallet speiler de andre, og tar det aktive rektangelet, banen til FDF-filen og en utseendebitmaske: Pdf.AddLinkToImportData(40, 660, 120, 18, 'defaults.fdf', 1). Filen trenger ikke eksistere når PDF-en bygges, men den må være til stede når brukeren klikker, og alle omvendte skråstreker i banen blir omskrevet til PDF-kanonisk skråstrekform for deg.
En begrensning er verdt å nevne tydelig fordi den ofte kommer som en overraskelse. En import-data-handling peker på en ekstern fil, så den er ikke tillatt i PDF/A. Når dokumentet er i PDF/A-modus, returnerer kallet null og legger ikke to noe, i stedet for å produsere en fil som feiler valideringen. Hvis produksjonslinjen din retter seg mot arkivering, må forhåndsutfylling skje under generering ved å skrive feltverdiene direkte, ikke ved å utsette dem til et klikk.
JavaScript: globale pakker og skripter per handling
For logikk som går utover vis, skjul og import, handlingsfamilien strekker seg inn i JavaScript på dokumentnivå. Det er to forskjellige steder et skript kan leve, og forskjellen betyr noe. En JavaScript-pakke på dokumentnivå lagres én gang for hele filen og kjører når dokumentet åpnes, noe som gjør det til det rette hjemmet for funksjonsdefinisjoner og delt tilstand. Et skript per handling er knyttet til én lenke eller ett felt, og kjører bare når dette objektet aktiveres, noe som gjør det til det rette hjemmet for den ene linjen som kaller en funksjon som pakken allerede har definert.
PDFlibPas eksponerer begge. AddGlobalJavaScript lagrer en navngitt pakke på dokumentnivå; gjenbruk av et navn erstatter det som var lagret under det. AddLinkToJavaScript knytter et skript til et aktivt område slik at et klikk kjører det.
// 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);
Å holde funksjonen i den globale pakken og kallet i lenken er ikke et stilvalg. Det unngår å duplisere den samme koden på hver kontroll som trenger den, og det betyr at et visningsprogram med skripting deaktivert ganske enkelt ikke gjør noe ved klikk, i stedet for å krasje på en feilutformet innebygd blokk. Det holder også oppføringene per handling små, noe som holder filen lesbar når du inspiserer den senere.
Felt, barnefelt og frysing av resultatet
Handlinger trenger felt å operere på, så det hjelper å se hvordan et felt blir til. NewFormField oppretter et felt på den gjeldende siden og returnerer dets indeks; heltallstypen velger typen, der 1 is Text, 2 is Pushbutton, 3 is Checkbox, 4 is Radiobutton, 5 is Choice, 6 is Signature, og 7 er en Parent som eier barn, men ikke tegner noe selv. Tittelen du oppgir kan ikke inneholde et punktum, fordi punktum er separatoren i de fullt kvalifiserte navnene som handlinger bruker til å adressere barn.
Radiogrupper og hierarkiske skjemaer bygges ved å gi et foreldrefelt barn. NewChildFormField legger til et barn under en navngitt forelder, og for radio- og valg-tilfellene legger AddFormFieldSub legger til de individuelle alternativene og gir tilbake en midlertidig indeks du bruker til å plassere hver av dem. Når den interaktive fasen er over og du vil fryse et felt slik at dets nåværende utseende blir permanent sideinnhold, tegner FlattenFormField feltet på siden og fjerner det fra skjemaet. Etter en flating vil indeksene til senere felt skiftes ned med én, noe som er det eneste du må huske på hvis du flater ut flere felt i en løkke.
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;
Flating-kallet er kommentert ut med vilje. Utelat det, og dokumentet leveres som et levende skjema der handlingene utløses i leseren. Aktiver det, og feltet blir gjengitt som statiske tegn på siden, noe som er det du ønsker når skjemaet er ferdigstilt og resultatet skal sendes som en fast oppføring. Det samme feltet, den samme koden, to svært forskjellige dokumenter avhengig av om du fryser det.
Å velge riktig verb
De fire handlingene deler seg tydelig etter hva de påvirker. En navngitt handling flytter visningsområdet og trenger ikke noe felt. En Hide-handling endrer synlighet og trenger felttitler, der streng-mot-array-kodingen håndteres for deg. En import-data-handling når en fil på disken og er derfor forbudt i PDF/A. En JavaScript-handling kjører vilkårlig logikk og er best delt mellom en global pakke med funksjoner og små kall per handling. Velg den enkleste som gjør jobben: en Hide-handling er mer portabel enn et skript som setter et skjul-flagg, og en navngitt handling er mer holdbar enn en lagret sidedestinasjon fordi det ikke er noe sidenummer å vedlikeholde.
Herfra fullfører to relaterte emner bildet. Hvis skjemaet er en del av et universelt utformet dokument, er strukturtreet som skjermlesere går gjennom dekket i vår artikkel om tagget PDF og tilgjengelighetsstruktur. Når det utfylte skjemaet må låses og signeres, er arbeidsflyten beskrevet i gjennomgangen av samsvars- og signeringsverktøyet. Alle tre bygger på den samme motoren, som leveres som PDF-biblioteket for Delphi sammen med opprettelses-, skjema- og signerings-API-ene som er dekket andre steder på denne bloggen.