Faktúra alebo príjmový doklad v PDF, kde sa položky riadkov samy sčítavajú a dátum sa sám formátuje, vyžaduje tri veci pripojené k svojmu AcroFormu. HotPDF pridáva všetky tri do načítaného súboru v Delphi: ApplyLoadedFieldCalculation pre súčty AFSimple_Calculate, ApplyLoadedFieldValidation pre formátovacie a validačné pomocné skripty Acrobat AF a EnsureLoadedFieldAppearanceStream na zapečenie hodnoty, ktorú prehliadač skutočne vykreslí
HotPDF je natívny VCL PDF komponent pre Delphi a C++Builder a tieto tri metódy pracujú s dokumentom, ktorý ste otvorili pomocou LoadFromFile, a nie s takým, ktorý vytvárate od začiatku. Každá z nich prijíma index poľa začínajúci od nuly v poli AcroForm /Fields — v rovnakom poradí, aké vracia GetLoadedFormFieldNames — a zapisuje priamo do slovníka poľa, takže celou úlohou je načítať, indexovať, pripojiť a vykonať SaveLoadedDocument. Samotné vytvorenie sady polí je samostatná úloha opísaná v časti pridávanie polí AcroForm do načítaného PDF v Delphi, zatiaľ čo táto stránka sa venuje pridaniu aritmetiky a formátovania do existujúcej sady polí
Prečo PDF formulár potrebuje skripty na sčítavanie vlastných polí?
Pretože PDF nemá tabuľkovú vrstvu. Textové pole obsahuje reťazec, nie vzorec, a nič v súbore neprepočíta celkovú sumu pri zmene položky riadku, pokiaľ na to nie je pripojený skript. Mechanizmom je slovník dodatočných akcií poľa, /AA (ISO 32000-1 §12.7.5), ktorý mapuje spúšťacie udalosti na akcie: stlačenie klávesu (K) pri príchode znakov, formátovanie pre zobrazenie, validáciu (V) pri potvrdení a výpočet (C), ktorý sa spustí vždy, keď sa zmení akékoľvek pole vo formulári. Každá akcia je akciou JavaScriptu, /S /JavaScript (ISO 32000-1 §12.6.4.14), a skript, ktorý spúšťa, je to, čo skutočne vykonáva sčítanie alebo zmenu formátu. Acrobat dodáva knižnicu pripravených pomocných skriptov — AFSimple_Calculate, AFNumber_Format, AFDate_FormatEx a ďalšie —, takže pripojený skript je zvyčajne len jednoradkovým volaním do tejto knižnice a nie vlastným kódom
Jeden detail rozhoduje o tom, či viacstupňový formulár poskytne správny výsledok: poradie výpočtov. Acrobat spúšťa každé pole, ktoré nesie akciu výpočtu, a spúšťa ich v poradí, ktoré uvádza pole AcroForm /CO, a nie v poradí stránok. Metóda ApplyLoadedFieldCalculation pripojí akciu výpočtu k poľu, ktoré pomenujete. Keď jedno vypočítané pole napája iné — medzisúčet, ktorý číta daňový riadok, a potom celkový súčet, ktorý číta oba —, musíte overiť, či poradie /CO uvádza každý vstup pred poľom, ktoré ho spotrebúva, inak celkový súčet načíta neaktuálny medzisúčet a bude o jednu úpravu pozadu
Ako nastaviť, aby PDF formulár automaticky počítal súčty?
Metóda ApplyLoadedFieldCalculation prijíma index cieľového poľa, operáciu a názvy zdrojových polí, ktoré má agregovať. HotPDF zapíše AFSimple_Calculate("SUM", ["lineTotal.1", "lineTotal.2", ...]) do akcie výpočtu cieľového poľa a enumerácia THPDFFieldCalcOp vyberie funkciu: fccSum, fccAverage, fccProduct, fccMinimum a fccMaximum sa mapujú na SUM, AVG, PRD, MIN a MAX v Acrobate. Názvy zdrojov sú názvami polí AcroForm, čo sú rovnaké reťazce, aké vracia GetLoadedFormFieldNames, takže súčet pre tri položky riadkov a poplatok za dopravu je záležitosťou jediného volania
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;
Volanie EnsureLoadedFieldAppearanceStream na celkovom súčte nie je len kozmetické. Výpočet prebieha vo vnútri prehliadača, takže kým súbor neotvorí čítačka podporujúca skripty, pole celkového súčtu nemá žiadny vlastný vykreslený obsah. Zapečenie toku vzhľadu mu medzitým poskytne číslo na zobrazenie, ktoré prevezme tlačová úloha alebo generátor náhľadov, keď nikdy nespustia skript
Formátovanie a validácia polí pomocou sady pomocníkov AF
Metóda ApplyLoadedFieldValidation pripojí formátovacie, klávesové a validačné skripty v jednom volaní a odovzdaná hodnota THPDFFieldValidationKind rozhodne, ktorého pomocníka z Acrobat u pripojí. Ak odovzdáte fvkCurrency, HotPDF zapíše AFNumber_Format(2,0,0,0,"$ ",true) ako formátovací skript so zodpovedajúcim AFNumber_Keystroke; fvkNumber použije "." pre argument oddeľovača, fvkPercentage emituje AFPercent_Format a fvkDate emituje AFDate_FormatEx("dd/mm/yyyy"), pokiaľ neodovzdáte vlastnú masku v argumente DateFormat. Špeciálne formáty — fvkZipCode, fvkSSN a fvkPhone — sa mapujú na AFSpecial_Format s kódom, ktorý Acrobat vyhradzuje pre každý z nich, a fvkArbitraryMask prijíma reťazec masky cez argument Mask pre čokoľvek, čo tieto predvoľby nepokrývajú. Pre číselné druhy HotPDF pripojí aj skript AFRange_Validate, takže vstup mimo rozsahu je pri potvrdení odmietnutý
// Format the invoice date as ISO, the amount as currency, and mask a tax id
Pdf.ApplyLoadedFieldValidation(DateIdx, fvkDate, 'yyyy-mm-dd', '');
Pdf.ApplyLoadedFieldValidation(AmountIdx, fvkCurrency, '', '');
Pdf.ApplyLoadedFieldValidation(TaxIdIdx, fvkArbitraryMask, '', '999-99-9999');
Pdf.EnsureLoadedFieldAppearanceStream(AmountIdx);
Pomocné skripty AF pokrývajú bežné tvary, takže pre ne nikdy nemusíte ručne písať JavaScript. Keď pole potrebuje logiku, ktorú žiadna predvoľba nevyjadruje — kontrolný súčet, medzipoľové pravidlo, vlastný regulárny výraz —, pripojte samotný nespracovaný skript pomocou metód akcií na úrovni polí opísaných v časti vytváranie polí a akcií AcroForm pomocou HotPDF, ktoré priamo prijímajú reťazec JavaScriptu. Obe cesty zapisujú do rovnakého slovníka /AA, takže predvolený formát meny na jednom poli a ručne napísaná validácia na druhom môžu koexistovať v tom istom dokumente
Prečo musíte znova vygenerovať tok vzhľadu?
Pretože reťazec hodnoty a pixely, ktoré prehliadač kreslí, sú v PDF dve rôzne veci a zmena jednej nezmení druhú. Pole ukladá svoju hodnotu v /V, ale to, čo čítačka vykresľuje, pochádza z toku vzhľadu poľa, /AP /N. Upravte /V prostredníctvom grafu objektov a prísny prehliadač bude stále zobrazovať starý vzhľad, kým ho niečo znova nevygeneruje. Slovník formulára nesie príznak /NeedAppearances, ktorý žiada prehliadač o prebudovanie každého vzhľadu pri otvorení, a Acrobat to rešpektuje — avšak vykresľovacie moduly zabudované v prehliadačoch, mobilné čítačky, tlačové servery a generátory náhľadov to často ignorujú a vykresľujú akýkoľvek tok, ktorý je už prítomný, alebo vôbec nič. Metóda EnsureLoadedFieldAppearanceStream vypĺňa túto medzeru tým, že sama zostaví Form XObject /AP /N z polí /DA, /V a /Rect, takže vykreslená hodnota putuje vo vnútri súboru a nezávisí od toho, či ju prehliadač znova prekreslí
// Bake an appearance for every field before saving, so values render everywhere
Names := Pdf.GetLoadedFormFieldNames;
for i := 0 to High(Names) do
Pdf.EnsureLoadedFieldAppearanceStream(i);
Pdf.SaveLoadedDocument('invoice-flat-appearance.pdf');
Kde skripty AF prestávajú fungovať
Každý skript AF* závisí od toho, či má prehliadač engine JavaScriptu, a väčšina prehliadačov ho nemá. Acrobat a hrsť stolových produktov tieto pomocné skripty spúšťajú; prehliadače PDF zabudované v Chrome, Edge, Firefoxe a hlavných mobilných platformách, spolu s rastrovacími modulmi na strane servera a tlačovými linkami, nespúšťajú vôbec žiadny JavaScript formulára. V týchto čítačkách sa výpočet nikdy nespustí a formát sa nikdy neaplikuje — pole jednoducho zobrazuje akýkoľvek tok vzhľadu, s ktorým bolo naposledy uložené, čo je presne dôvod, prečo na zapečení /AP /N záleží. Považujte vrstvu AF* za vymoženosť pre podmnožinu používateľov s čítačkou podporujúcou skripty, nikdy nie za záruku. Akýkoľvek celkový súčet, ktorý musí byť na serveri správny — suma faktúry, ktorú budete účtovať, daňové číslo, ktoré budete vykazovať —, musíte po vrátení formulára znova prepočítať na vašej strane, pretože nemôžete predpokladať, že čítačka, ktorá ho vyplnila, spustila jediný riadok vášho skriptu
Stojí za to uviesť ešte jeden prípad: formuláre, ktoré prišli ako XFA. Dynamický formulár XFA nesie svoj vlastný výpočtový engine v XML dátach, o ktorých pomocné skripty AF* nič nevedia, takže pripojenie AFSimple_Calculate k poľu XFA nič nedosiahne. Najprv zlúčte (flatten) formulár XFA na obyčajný AcroForm, ako je opísané v časti zlúčenie XFA na AcroForm v Delphi, a tri tu uvedené metódy sa potom použijú na výsledné polia AcroForm presne tak, ako na akýkoľvek iný načítaný formulár
Automaticky kalkulované a samovoľne sa formátujúce AcroFormy sa zredukujú na tri volania nad načítaným dokumentom — pripojenie výpočtu, pripojenie formátu a validácie, zapečenie vzhľadu — po ktorých nasleduje SaveLoadedDocument. Tu zobrazené API pre načítané formuláre je súčasťou komponentu HotPDF Component pre Delphi a C++Builder, ktorý dokumentuje kompletné enumerácie THPDFFieldValidationKind a THPDFFieldCalcOp spolu so zvyškom rozhrania AcroForm