PDF-lomakekenttä itsessään on vain laatikko, joka sisältää arvon. Se, mikä saa lomakkeen toimimaan kuin pieni sovellus, on siihen liitetty toiminto: napsautus, joka piilottaa osion, hakee tallennetut arvot tiedostosta, siirtyy viimeiselle sivulle tai suorittaa skriptin, joka laskee sarakkeen summan. Mikään tästä ei sijaitse itse kentässä. Se sijaitsee toimintosanakirjassa, ja ISO 32000-1 määrittelee koko perheen kohdassa §12.6. Tässä artikkelissa käydään läpi toiminnot, joita Delphi-ohjelma tarvitsee useimmiten, ja näytetään, miten PDFlibPas kytkee kunkin toiminnon kenttään tai linkkiin.
Hyvä mielentilaan sopiva malli on, että kenttä ja toiminto ovat erillisiä objekteja, joita yhdistää viite. Widget-annotointi tai linkkiannotointi kuljettaa toimintoa /A-merkinnässään. Toiminto viittaa kenttään sen nimen, ei indeksin, perusteella, joten kentälle antamasi nimi on kahva, jota jokainen myöhempi toiminto käyttää sen löytämiseen. Kun tämä ero on selvä, API ei enää vaikuta satunnaiselta kokoelmalta kutsuja, vaan yhdeltä mallilta, jota sovelletaan neljään eri toimintoon.
Named-toiminnot: navigointi ilman sivunumeroa
Yksinkertaisimmat toiminnot eivät sisällä lainkaan parametreja. ISO 32000-1 §12.6.4.11, taulukko 194, määrittelee nimetyt toiminnot: katseluohjelma tulkitsee symbolisen nimen suorituksen aikana tallennetun kohteen seuraamisen sijaan. Neljä nimeä on yleisesti tuettuja, ja ne ovat juuri niitä, joita lukija odottaa työkaluriviltä: NextPage, PrevPage, FirstPage ja LastPage. Koska kohde on suhteellinen siihen sivuun, jota katseluohjelma parhaillaan näyttää, tällä tavalla rakennettu Seuraava-painike toimii jokaisella sivulla ilman, että sinun tarvitsee laskea kohdetta.
In PDFlibPas a named action is attached to a hotspot rectangle on the current page. The fourth and fifth integer arguments select the verb and the appearance.
// 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
Ei ole synkronoitavaa kohdetta, mikä on koko homman ydin. Nimetty toiminto säilyy sivujen lisäämisen ja poistamisen yli, koska se ei koskaan nimeä sivua alun alkaenkaan. Vertaa tätä yksityiskohtaiseen siirtymislinkkiin, joka tallentaa kohdesivun indeksin, joka on numeroitava uudelleen heti, kun dokumentti kasvaa.
Hide-toiminto ja sen taulukkoansa
Hide-toiminto, ISO 32000-1 §12.6.4.10, taulukko 196, vaihtaa yhden tai useamman kentän näkyvyyttä. Se on siistein tapa rakentaa näyttö- ja piilotustoimintoja ilman skriptausta, ja se on juuri sitä, mitä haluat Näytä tiedot -linkille tai kahdelle toisensa poissulkevalle paneelille, joissa toisen näyttäminen piilottaa toisen. Toiminto kuljettaa kohdetta /T-merkinnässään ja boolean-arvoa /H, joka päättää suunnan: piilota, kun tosi, näytä, kun epätosi.
Hienovaraisuus on täysin siinä, miten kohde koodataan, ja se on sellainen yksityiskohta, joka tuottaa lomakkeen, joka toimii omalla koneellasi mutta epäonnistuu asiakkaan koneella. Kun toiminto nimeää yhden kentän, /T kirjoitetaan yhtenä tekstimerkkijonona. Kun se nimeää useita, /T kirjoitetaan tekstimerkkijonojen taulukkona. Vanhemmat katseluohjelmat eivät käsittele yhden alkion taulukkoa samalla tavalla kuin pelkkää merkkijonoa, joten koodauksen on haarauduttava määrän mukaan: yksittäinen nimi on vietävä merkkijonona, ei yhden alkion taulukkona, jotta mahdollisimman monet lukijat kunnioittaisivat sitä. PDFlibPas tekee tämän päätöksen puolestasi. Välität kenttien nimet pilkuilla, puolipisteillä tai rivinvaihdoilla erotettuina, ja kirjoittaja luo yhden merkkijonon yhdelle nimelle ja taulukon kahdelle tai useammalle.
// 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);
Koska toiminto ei viittaa mihinkään ulkoiseen resurssiin, se pysyy yhteensopivana PDF/A-standardin kanssa. Välittämäsi nimet ovat täysin määriteltyjä kenttien nimiä, minkä vuoksi ryhmän sisällä olevaa alikenttää on osoitettava sen koko pisteillä erotetun polun kautta pelkän lehtinimen sijaan.
ImportData: esitäyttö FDF-tiedostosta
Siinä missä Hide-toiminto järjestää uudelleen sen, mikä on jo sivulla, ImportData-toiminto tuo arvot sen ulkopuolelta. ISO 32000-1 §12.6.4.8, taulukko 198, määrittelee sen toiminnoksi, joka täyttää AcroForm-lomakkeen levyllä olevasta Forms Data Format -tiedostosta. Tämä toiminto on Palauta oletukset -ohjaimen takana, jossa FDF-tiedosto toimitetaan PDF-tiedoston vieressä ja se sisältää kanoniset kenttien arvot. Kutsu vastaa muita: se ottaa hotspot-suorakulmion, polun FDF-tiedostoon ja ulkoasun bittimaskin: Pdf.AddLinkToImportData(40, 660, 120, 18, 'defaults.fdf', 1). Tiedoston ei tarvitse olla olemassa PDF-tiedostoa rakennettaessa, mutta sen on oltava paikalla, kun käyttäjä napsauttaa, ja kaikki polun kenoviivat korvataan puolestasi PDF-kanonisella vinoviivalla.
Yksi rajoitus on syytä sanoa suoraan, koska se on yleinen yllätys. ImportData-toiminto osoittaa ulkoiseen tiedostoon, joten se ei ole sallittu PDF/A-standardissa. Kun dokumentti on PDF/A-tilassa, kutsu palauttaa nollan eikä lisää mitään sen sijaan, että se tuottaisi tiedoston, joka epäonnistuu validoinnissa. Jos putkesi tähtää arkistoitavaan tulosteeseen, esitäytön on tapahduttava luontiaikana kirjoittamalla kenttien arvot suoraan, eikä siirtämällä sitä napsautuksen varaan.
JavaScript: globaalit paketit ja toimintokohtaiset skriptit
Logiikassa, joka menee näyttämistä, piilottamista ja tuontia pidemmälle, toimintoperhe ulottuu dokumenttitason JavaScriptiin. Skripti voi sijaita kahdessa eri paikassa, ja erolla on merkitystä. Dokumenttitason JavaScript-paketti tallennetaan kerran koko tiedostolle ja se suoritetaan, kun dokumentti avataan, mikä tekee siitä oikean paikan funktioiden määrittelyille ja jaetulle tilalle. Toimintokohtainen skripti on liitetty yhteen linkkiin tai kenttään ja se suoritetaan vain, kun kyseinen objekti aktivoidaan, mikä tekee siitä oikean paikan sille yhdelle riville, joka kutsuu paketin jo määrittelemää funktiota.
PDFlibPas paljastaa molemmat. AddGlobalJavaScript tallentaa nimetyn paketin dokumenttitasolla; nimen käyttäminen uudelleen korvaa sen, mitä sen alle oli tallennettu. AddLinkToJavaScript liittää skriptin hotspotiin, jotta napsautus suorittaa sen.
// 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);
Funktion pitäminen globaalissa paketissa ja kutsun pitäminen linkissä ei ole tyylimieltymys. Se välttää saman rungon monistamisen jokaisessa sitä tarvitsevassa ohjaimessa, ja se tarkoittaa, että katseluohjelma, jossa skriptaus on poistettu käytöstä, ei yksinkertaisesti tee mitään napsautettaessa sen sijaan, että se tukehtuisi virheelliseen sisäiseen blobiin. Se myös pitää toimintokohtaiset merkinnät pieninä, mikä pitää tiedoston luettavana, kun tarkastelet sitä myöhemmin.
Kentät, alikentät ja tuloksen litistäminen
Toiminnot tarvitsevat kenttiä, joilla toimia, joten on hyödyllistä nähdä, miten kenttä syntyy. NewFormField luo kentän nykyiselle sivulle ja palauttaa sen indeksin; kokonaislukutyyppi valitsee tyypin, jossa 1 on teksti, 2 on painike, 3 on valintaruutu, 4 on valintapainike, 5 on valinta, 6 on allekirjoitus ja 7 on vanhempi, joka omistaa lapsia mutta ei piirrä itse mitään. Välittämäsi nimi ei voi sisältää pistettä, koska piste on erotin täysin määritellyissä nimissä, joita toiminnot käyttävät lapsiin viittaamiseen.
Valintaryhmät ja hierarkkiset lomakkeet rakennetaan antamalla vanhempikentälle lapsia. NewChildFormField lisää lapsen nimetyn vanhemman alle, ja radio- ja valintatapauksissa AddFormFieldSub lisää yksittäiset vaihtoehdot ja antaa takaisin väliaikaisen indeksin, jota käytät kunkin sijoittamiseen. Kun vuorovaikutteinen vaihe on ohi ja haluat litistää kentän niin, että sen nykyisestä ulkoasusta tulee pysyvää sivun sisältöä, FlattenFormField piirtää kentän sivulle ja poistaa sen lomakkeesta. Litistämisen jälkeen myöhempien kenttien indeksit siirtyvät yhdellä alaspäin, mikä on ainoa asia, joka on muistettava, jos litistät useita kenttiä silmukassa.
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;
Litistyskutsu on kommentoitu pois tarkoituksella. Jätä se pois, ja dokumentti toimitetaan elävänä lomakkeena, jonka toiminnot käynnistyvät lukijassa. Ota se käyttöön, ja kenttä renderöidään staattisiksi merkeiksi, mikä on se, mitä haluat, kun lomake on täytetty ja tuloksen pitäisi siirtyä kiinteänä tietueena. Sama kenttä, sama koodi, kaksi hyvin erilaista dokumenttia riippuen siitä, litistätkö sen.
Oikean toiminnon valinta
Neljä toimintoa jakautuvat selkeästi sen mukaan, mihin ne koskettavat. Nimetty toiminto siirtää katselunäkymää eikä tarvitse kenttää. Hide-toiminto muuttaa näkyvyyttä ja tarvitsee kenttien nimiä, ja merkkijono-vs-taulukko-koodaus käsitellään puolestasi. ImportData-toiminto tavoittaa levyllä olevan tiedoston ja on siksi kielletty PDF/A-standardissa. JavaScript-toiminto ajaa mielivaltaista logiikkaa, ja se on parasta jakaa globaaliin funktiopakettiin ja pieniin toimintokohtaisiin kutsuihin. Tartu yksinkertaisimpaan, joka tekee työn: Hide-toiminto on siirrettävämpi kuin skripti, joka asettaa piilotetun lipun, ja nimetty toiminto on kestävämpi kuin tallennettu sivukohde, koska ylläpidettävää sivunumeroa ei ole.
Tästä kaksi naapuriaihetta viimeistelevät kuvan. Jos lomake on osa saavutettavaa dokumenttia, rakenne-puu, jota ruudunlukijat käyvät läpi, käsitellään artikkelissamme merkityistä PDF-tiedostoista ja saavutettavuusrakenteesta. Kun valmis lomake on lukittava ja allekirjoitettava, työnkulku kuvataan yhteensopivuuden ja allekirjoittamisen työpöydän esittelyssä. Kaikki kolme rakentuvat samalle moottorille, joka toimitetaan Delphin PDF-kirjastona luonti-, lomake- ja allekirjoitus-API:en rinnalla, joita käsitellään muualla tässä blogissa.