Lasku- tai kuluraportti-PDF, jossa rivikohdat laskevat itsensä yhteen ja päivämäärä muotoilee itsensä, vaatii kolme asiaa kiinnitettäväksi sen AcroFormiin. HotPDF lisää kaikki kolme ladattuun tiedostoon Delphissä: ApplyLoadedFieldCalculation AFSimple_Calculate-yhteissummille, ApplyLoadedFieldValidation Acrobatin AF-muotoilu- ja vahvistusapulaisille ja EnsureLoadedFieldAppearanceStream leipomaan arvon, jonka katseluohjelma todellisuudessa piirtää
HotPDF on natiivi VCL PDF -komponentti Delphille ja C++Builderille, ja nämä kolme menetelmää toimivat asiakirjalle, jonka avasit LoadFromFile-kutsulla, sen sijaan että rakentaisit sen tyhjästä. Jokainen niistä ottaa vastaan nollaperusteisen kenttäindeksin AcroFormin /Fields-taulukkoon — samaan järjestykseen, jonka GetLoadedFormFieldNames palauttaa — ja kirjoittaa suoraan kenttäsanakirjaan, joten koko homma on lataaminen, indeksointi, liittäminen ja SaveLoadedDocument. Itse kenttäjoukon rakentaminen on erillinen tehtävä, jota käsitellään artikkelissa AcroForm-kenttien lisääminen ladattuun PDF-tiedostoon Delphissä, kun taas tämä sivu käsittelee aritmetiikan ja muotoilun antamista olemassa olevalle kenttäjoukolle
Miksi PDF-lomake tarvitsee skriptejä laskemaan omat kenttänsä?
Koska PDF-muodossa ei ole taulukkolaskentakerrosta. Tekstikenttä sisältää merkkijonon, ei kaavaa, eikä mikään tiedostossa laske summaa uudelleen, kun rivikohta muuttuu, ellei siihen ole liitetty skriptiä sitä tekemään. Mekanismi on kentän lisätoimintojen sanakirja, /AA (ISO 32000-1 §12.7.5), joka mäppää liipaisintapahtumat (trigger events) toimintoihin: näppäilyliipaisin (K) merkkien saapuessa, muotoiluliipaisin näyttämistä varten, vahvistusliipaisin (V) hyväksymisen yhteydessä ja laskentaliipaisin (C), joka käynnistyy aina, kun jokin lomakkeen kenttä muuttuu. Jokainen toiminto on JavaScript-toiminto, /S /JavaScript (ISO 32000-1 §12.6.4.14), ja sen suorittama skripti on se, joka todellisuudessa suorittaa summan tai uudelleenmuotoilun. Acrobat toimittaa valmiiden apureiden kirjaston — AFSimple_Calculate, AFNumber_Format, AFDate_FormatEx ja muut —, joten liitetty skripti on yleensä yhden rivin kutsu tähän kirjastoon mittatilauskoodin sijaan
Yksi yksityiskohta ratkaisee, antaako monivaiheinen lomake oikean vastauksen: laskentajärjestys. Acrobat suorittaa jokaisen kentän, joka sisältää laskentatoiminnon, ja se suorittaa ne siinä järjestyksessä kuin AcroFormin /CO-taulukko ne luettelee, ei sivujärjestyksessä. ApplyLoadedFieldCalculation liittää laskentatoiminnon nimeämääsi kenttään. Kun yksi laskettu kenttä syöttää toista — esim. välisumma, jota verorivi lukee, ja sitten loppusumma, joka lukee molempia —, sinun on varmistettava, että /CO-järjestys luettelee kunkin syötteen ennen sitä kuluttavaa kenttää, tai muuten loppusumma lukee vanhentuneen välisumman ja laahaa yhden muokkauksen jäljessä
Miten PDF-lomake saadaan laskemaan summat automaattisesti?
ApplyLoadedFieldCalculation ottaa vastaan kohdekentän indeksin, operaation ja niiden lähdekenttien nimet, jotka sen pitäisi agregoida. HotPDF kirjoittaa AFSimple_Calculate("SUM", ["lineTotal.1", "lineTotal.2", ...]) kohdekentän laskentatoimintoon, ja THPDFFieldCalcOp-luettelo valitsee funktion: fccSum, fccAverage, fccProduct, fccMinimum ja fccMaximum mäppäävät Acrobatin funktioihin SUM, AVG, PRD, MIN ja MAX. Lähdenimet ovat AcroForm-kenttien nimiä, samoja merkkijonoja, joita GetLoadedFormFieldNames antaa takaisin, joten kolmen rivikohdan ja toimituskulun summa on yksi ainoa kutsu
var
Pdf: THotPDF;
Names: THPDFAnsiStringArray;
i, TotalIdx: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('invoice.pdf');
Names := Pdf.GetLoadedFormFieldNames;
TotalIdx := -1;
for i := 0 to High(Names) do
if Names[i] = 'grandTotal' then TotalIdx := i;
if TotalIdx >= 0 then
begin
Pdf.ApplyLoadedFieldCalculation(TotalIdx, fccSum,
['lineTotal.1', 'lineTotal.2', 'lineTotal.3', 'shipping']);
Pdf.EnsureLoadedFieldAppearanceStream(TotalIdx);
end;
Pdf.SaveLoadedDocument('invoice-live.pdf');
finally
Pdf.Free;
end;
end;
Loppusummalle tehty EnsureLoadedFieldAppearanceStream-kutsu ei ole kosmeettinen. Laskenta suoritetaan katseluohjelmassa, joten ennen kuin skriptejä tukeva lukija avaa tiedoston, loppusummakentällä ei ole omaa renderöityä sisältöä. Ulkoasuvirran leipominen antaa sille näytettävän luvun sillä välin, minkä tulostustyö tai pikkukuvageneraattori poimii, kun se ei koskaan suorita skriptiä
Kenttien muotoilu ja vahvistus AF-apulaisohjelmistolla
ApplyLoadedFieldValidation liittää muotoilu-, näppäily- ja vahvistusskriptit yhdellä kutsulla, ja välittämäsi THPDFFieldValidationKind määrittää, minkä Acrobatin apulaisen se kytkee. Välitä fvkCurrency ja HotPDF kirjoittaa AFNumber_Format(2,0,0,0,"$ ",true) muotoiluskriptiksi vastaavan AFNumber_Keystroke-kutsun kanssa. fvkNumber käyttää "." erotinarvona, fvkPercentage tuottaa AFPercent_Format-kutsun ja fvkDate tuottaa AFDate_FormatEx("dd/mm/yyyy")-kutsun, ellet välitä omaa maskiasi DateFormat-argumentissa. Erikoismuodot — fvkZipCode, fvkSSN ja fvkPhone — mäppäävät AFSpecial_Format-kutsuun sillä koodilla, jonka Acrobat varaa kullekin, ja fvkArbitraryMask ottaa vastaan maskimerkkijonon Mask-argumentin kautta kaikelle sille, mitä nämä esiasetukset eivät kata. Numeerisille tyypeille HotPDF liittää myös AFRange_Validate-skriptin, jotta alueen ulkopuolella oleva syöte hylätään hyväksymisen yhteydessä
// Muotoile laskun päivämäärä ISO-muotoon, summa valuutaksi ja peitä verotunnus
Pdf.ApplyLoadedFieldValidation(DateIdx, fvkDate, 'yyyy-mm-dd', '');
Pdf.ApplyLoadedFieldValidation(AmountIdx, fvkCurrency, '', '');
Pdf.ApplyLoadedFieldValidation(TaxIdIdx, fvkArbitraryMask, '', '999-99-9999');
Pdf.EnsureLoadedFieldAppearanceStream(AmountIdx);
Miksi ulkoasuvirta täytyy luoda uudelleen?
Koska arvon merkkijono ja pikselit, jotka katseluohjelma piirtää, ovat kaksi eri asiaa PDF:ssä, eikä toisen muuttaminen muuta toista. Kenttä tallentaa arvonsa polkuun /V, mutta se, minkä lukija renderöi, tulee kentän ulkoasuvirrasta, /AP /N. Muokkaa arvoa /V objektigraafin kautta, ja tiukka katseluohjelma näyttää silti vanhan ulkoasun, kunnes jokin luo sen uudelleen. Lomakesanakirja sisältää /NeedAppearances-lipun, joka pyytää katseluohjelmaa rakentamaan jokaisen ulkoasun uudelleen avattaessa, ja Acrobat noudattaa sitä — mutta selaimeen upotetut renderöijät, mobiililukijat, tulostuspalvelimet ja pikkukuvageneraattorit ohittavat sen usein ja piirtävät sen virran, joka on jo valmiina, tai eivät mitään. EnsureLoadedFieldAppearanceStream sulkee tämän aukon rakentamalla /AP /N Form XObjectin itse kenttien /DA, /V ja /Rect perusteella, jotta renderöity arvo kulkee tiedoston mukana sen sijaan, että oltaisiin riippuvaisia siitä, että katseluohjelma piirtää sen uudelleen
// Leivo ulkoasu jokaiselle kentälle ennen tallennusta, jotta arvot renderöityvät kaikkialla
Names := Pdf.GetLoadedFormFieldNames;
for i := 0 to High(Names) do
Pdf.EnsureLoadedFieldAppearanceStream(i);
Pdf.SaveLoadedDocument('invoice-flat-appearance.pdf');
Missä AF-skriptit lakkaavat toimimasta
Jokainen AF*-skripti riippuu siitä, että katseluohjelmassa on JavaScript-moottori, ja useimmissa katseluohjelmissa sitä ei ole. Acrobat ja kourallinen työpöytätuotteita suorittavat näitä apulaisia; Chromeen, Edgeen, Firefoxiin ja tärkeimpiin mobiilialustoihin sisäänrakennetut PDF-katseluohjelmat sekä palvelinpuolen rasteroijat ja tulostusputket eivät aja lainkaan lomake-JavaScriptiä. Näissä lukijoissa laskenta ei koskaan käynnisty eikä muotoilu koskaan päde — kenttä näyttää yksinkertaisesti sen ulkoasuvirran, jolla se viimeksi tallennettiin, minkä vuoksi /AP /N-virran leipominen on niin tärkeää. Käsittele AF*-kerrosta mukavuutena sille käyttäjäryhmälle, joka käyttää skriptejä tukevaa lukijaa, älä koskaan takuuna. Kaikki summat, joiden on oltava oikein palvelimella — laskutussumma, veroluku jne. —, on laskettava uudelleen sinun puolellasi sen jälkeen, kun lomake palautuu, koska et voi olettaa, että sitä täyttänyt lukija suoritti riviäkään skriptistäsi
Yksi tapaus on vielä nimeämisen arvoinen: lomakkeet, jotka saapuivat XFA-muodossa. Dynaaminen XFA-lomake kuljettaa omaa laskentamoottoriaan XML-hyötykuormassa, josta AF*-apurit eivät tiedä mitään, joten AFSimple_Calculate-funktion liittäminen XFA-kenttään ei johda mihinkään. Litistä XFA-lomake ensin tavalliseksi AcroFormiksi, kuten kuvataan artikkelissa XFA:n litistäminen AcroFormiksi Delphissä, ja tässä esitetyt kolme menetelmää soveltuvat sen jälkeen tuloksena oleviin AcroForm-kenttiin täsmälleen samalla tavalla kuin muihunkin ladattuihin lomakkeisiin
Automaattisesti laskevat ja itsestään muotoilevat AcroFormit vaativat vain kolme kutsua ladatulle asiakirjalle — liitä laskenta, liitä muotoilu ja vahvistus, leivo ulkoasu — ja sen jälkeen SaveLoadedDocument. Tässä esitetty ladattujen lomakkeiden API on osa Delphille ja C++Builderille tarkoitettua HotPDF-komponenttia, joka dokumentoi täydelliset THPDFFieldValidationKind- ja THPDFFieldCalcOp-luettelot muun AcroForm-pinnan ohella