Tehnički članak

Automatski izračun PDF obrazaca u Delphiju pomoću HotPDF-a

PDF računa ili troškova u kojem se stavke same zbrajaju, a datum se automatski oblikuje, zahtijeva tri stvari povezane sa svojim AcroForm obrascem, a HotPDF dodaje sve tri u učitanu datoteku u Delphiju: ApplyLoadedFieldCalculation za ukupne vrijednosti AFSimple_Calculate, ApplyLoadedFieldValidation za Acrobat AF format i pomoćnike za provjeru te EnsureLoadedFieldAppearanceStream za ugradnju vrijednosti koju preglednik stvarno crta

HotPDF je izvorna VCL PDF komponenta za Delphi i C++Builder, a ove tri metode rade na dokumentu koji ste otvorili pomoću LoadFromFile umjesto na dokumentu koji gradite ispočetka. Svaka prima indeks polja koji počinje od nule u AcroForm polju /Fields — istim redoslijedom koji vraća GetLoadedFormFieldNames — i piše izravno u rječnik polja, tako da se cijeli posao sastoji od učitavanja, indeksiranja, povezivanja i pozivanja SaveLoadedDocument. Sama izgradnja skupa polja zaseban je zadatak, obrađen u članku o dodavanju AcroForm polja u učitani PDF u Delphiju, dok se ova stranica bavi dodavanjem aritmetike i oblikovanja postojećem skupu polja

Zašto PDF obrazac treba skripte za zbrajanje vlastitih polja?

Zato što PDF nema sloj proračunske tablice. Tekstualno polje sadrži niz znakova, a ne formulu, i ništa u datoteci ne preračunava ukupni iznos kada se stavka promijeni osim ako nije priložena skripta koja to čini. Mehanizam je rječnik dodatnih radnji polja, /AA (ISO 32000-1 §12.7.5), koji mapira događaje pokretača na radnje: pokretač pritiska tipke (K) kako znakovi pristižu, pokretač formata za prikaz, pokretač provjere (V) pri potvrđivanju i pokretač izračuna (C) koji se aktivira kad god se bilo koje polje na obrascu promijeni. Svaka radnja je JavaScript radnja, /S /JavaScript (ISO 32000-1 §12.6.4.14), a skripta koju pokreće je ono što zapravo izvodi zbrajanje ili ponovno oblikovanje. Acrobat isporučuje biblioteku gotovih pomoćnika — AFSimple_Calculate, AFNumber_Format, AFDate_FormatEx i ostale — pa je priložena skripta obično poziv od jednog retka u tu biblioteku, a ne prilagođeni kôd

Jedan detalj odlučuje daje li obrazac s više koraka točan odgovor: redoslijed izračuna. Acrobat pokreće svako polje koje sadrži radnju izračuna i pokreće ih redoslijedom koji navodi AcroForm polje /CO, a ne redoslijedom stranica. ApplyLoadedFieldCalculation povezuje radnju izračuna s poljem koje imenujete; kada jedno izračunato polje hrani drugo — podzbroj koji čita porezna stavka, a zatim ukupni zbroj koji čita oboje — morate potvrditi da redoslijed u /CO navodi svaki ulaz prije polja koje ga troši, inače će ukupni zbroj pročitati zastarjeli podzbroj i kasniti jedan korak iza promjene

Kako postići da PDF obrazac automatski izračunava ukupne iznose?

ApplyLoadedFieldCalculation prima indeks ciljnog polja, operaciju i nazive izvornih polja koje treba agregirati. HotPDF zapisuje AFSimple_Calculate("SUM", ["lineTotal.1", "lineTotal.2", ...]) u radnju izračuna ciljnog polja, a nabrajanje THPDFFieldCalcOp odabire funkciju: fccSum, fccAverage, fccProduct, fccMinimum i fccMaximum mapiraju se na Acrobat SUM, AVG, PRD, MIN i MAX. Nazivi izvora su nazivi AcroForm polja, isti nizovi znakova koje vraća GetLoadedFormFieldNames, pa je ukupni zbroj za tri stavke i trošak dostave samo jedan poziv

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;

Oblikovanje i provjera polja pomoću AF paketa pomoćnika

ApplyLoadedFieldValidation povezuje skripte za format, pritisak tipke i provjeru u jednom pozivu, a THPDFFieldValidationKind koji proslijedite odlučuje koji će se Acrobat pomoćnik povezati. Proslijedite fvkCurrency i HotPDF zapisuje AFNumber_Format(2,0,0,0,"$ ",true) kao skriptu formata s odgovarajućom AFNumber_Keystroke; fvkNumber koristi "." za argument separatora, fvkPercentage ispisuje AFPercent_Format, and fvkDate ispisuje AFDate_FormatEx("dd/mm/yyyy") osim ako ne proslijedite vlastitu masku u argumentu DateFormat. Posebni formati — fvkZipCode, fvkSSN i fvkPhone — mapiraju se na AFSpecial_Format s kodom koji Acrobat rezervira za svaki, a fvkArbitraryMask prima niz maske kroz argument Mask za sve što ti predlošci ne pokrivaju. Za numeričke vrste HotPDF također prilaže skriptu AFRange_Validate tako da se unos izvan raspona odbija pri potvrđivanju

// 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);

Zašto morate ponovno generirati tok izgleda?

Zato što su niz vrijednosti i pikseli koje preglednik crta dvije različite stvari u PDF-u, a promjena jednog ne mijenja drugo. Polje pohranjuje svoju vrijednost u /V, ali ono što čitatelj prikazuje dolazi iz toka izgleda polja, /AP /N. Uredite /V kroz graf objekta i strogi preglednik i dalje prikazuje stari izgled dok ga nešto ne generira ponovno. Rječnik obrasca nosi zastavicu /NeedAppearances koja od preglednika traži da obnovi svaki izgled pri otvaranju, i Acrobat to poštuje — ali rendereri ugrađeni u preglednike, mobilni čitatelji, poslužitelji za ispis i generatori sličica često to zanemaruju i crtaju bilo koji tok koji je već prisutan, ili ne crtaju ništa. EnsureLoadedFieldAppearanceStream zatvara taj jaz tako što sam gradi /AP /N Form XObject iz polja /DA, /V i /Rect, pa iscrtana vrijednost putuje unutar datoteke umjesto da ovisi o pregledniku da je ponovno nacrta

Gdje AF skripte prestaju raditi

Svaka AF* skripta ovisi o tome ima li preglednik JavaScript mehanizam, a većina preglednika ga nema. Acrobat i nekoliko stolnih proizvoda pokreću ove pomoćnike; PDF preglednici ugrađeni u Chrome, Edge, Firefox i glavne mobilne platforme, zajedno s rasterizatorima na poslužitelju i cjevovodima za ispis, uopće ne pokreću JavaScript za obrasce. U tim se čitateljima izračun nikada ne pokreće i format se nikada ne primjenjuje — polje jednostavno prikazuje tok izgleda s kojim je zadnji put spremljeno, što je točno razlog zašto je ugradnja /AP /N važno. Tretirajte AF* sloj kao pogodnost za podskup korisnika na čitateljima koji podržavaju skripte, a nikada kao jamstvo. Svaki ukupni iznos koji mora biti točan na poslužitelju — iznos računa koji ćete naplatiti, porezna brojka koju ćete prijaviti — mora se ponovno izračunati na vašoj strani nakon što se obrazac vrati, jer ne možete pretpostaviti da je čitatelj koji ga je popunio pokrenuo ijedan redak vaše skripte

Vrijedi spomenuti još jedan slučaj: obrasce koji su stigli kao XFA. Dinamički XFA obrazac nosi vlastiti mehanizam za izračun u XML sadržaju o kojem AF* pomoćnici ne znaju ništa, pa povezivanje AFSimple_Calculate s XFA poljem ne postiže ništa. Najprije spljoštite XFA obrazac u običan AcroForm, kao što je opisano u članku o spljoštavanju XFA u AcroForm u Delphiju, a tri ovdje opisane metode potom se primjenjuju na rezultirajuća AcroForm polja točno onako kako se primjenjuju na bilo koji drugi učitani obrazac

Automatski izračun i samostalno oblikovanje AcroForm obrazaca svodi se na tri poziva na učitanom dokumentu — povezivanje izračuna, povezivanje oblikovanja i provjere, ugradnja izgleda — nakon čega slijedi SaveLoadedDocument. Ovdje prikazani API za učitani obrazac dio je komponente HotPDF Component za Delphi i C++Builder, koja dokumentira potpuna nabrajanja THPDFFieldValidationKind i THPDFFieldCalcOp uz ostatak AcroForm sučelja