Tehnički članak

Dodavanje AcroForm polja u učitan PDF u Delphiju

Imate predložak fakture od treće strane ili arhivirani ugovor koji je neko generisao pre mnogo godina u softveru koji više niko ne može da pronađe, a zahtev je da ga učinite interaktivnim: ubacite polje za potpis u ugao, dodajte nekoliko tekst polja, pretvorite ravnu čeklistu u prava checkbox polja. Kvaka je u tome što ovaj PDF ne pravite od nule. Već postoji, već ima stranice i content stream-ove i fontove nad kojima nemate kontrolu, i potrebno je da na taj objektni graf nadogradite AcroForm widgete bez ponovnog građenja dokumenta. To je drugačiji problem od pravljenja forme na novom dokumentu, a deo koji ljude obično zbuni ostaje nevidljiv sve dok ne otvorite rezultat u viewer-u i polja koja ste upravo upisali nisu nigde na stranici

HotPDF je nativna VCL PDF komponenta za Delphi i C++Builder, i od verzije v2.247.0 nudi namensku grupu metoda baš za ovo: da svih šest standardnih tipova polja direktno napravi na dokumentu učitanom sa LoadFromFile. Ovaj članak objašnjava šta te metode rade, koju ISO 32000-1 strukturu rečnika grade, i koja je jedina zastavica bez koje ceo poduhvat neprimetno završava fajlom koji izgleda prazno

Zašto je kreiranje polja na učitanom dokumentu posebna putanja koda

Kada PDF pravite od nule, HotPDF upravlja celim objekt modelom. Svaka stranica je upisivi THPDFPage omotač, a dodavanje tekst polja preko AddTextField povezuje novi widget sa objektom anotacije stranice, objektom stranice i kolekcijom form polja, a zatim generiše appearance stream iz resursa fontova dokumenta. Appearance stream je vidljiva površina widgeta, okvir i ivica i svaki podrazumevani tekst, iscrtani kao PDF crtačke naredbe koje viewer prikazuje doslovno

U učitanom dokumentu nemate ništa od te pomoćne infrastrukture. Stranice su ušle kao sirovi rečnici; ne postoji upisivi THPDFPage omotač na koji bi se widget nakačio, a još važnije, nema ni spremnog lanca resursa fontova koji bi crtao appearance stream-ove. Zato učitana putanja ide drugim putem. Ona upisuje rečnike polja direktno u parsirani objektni graf i stranice adresira po nultom indeksu, a ne preko objekta stranice. Tipovi polja i bitovi zastavica potpuno su isti kao na putanji od nule, tako da je Text polje i dalje Text polje; menja se cevovod ispod toga i, što je ključno, način na koji se površina widgeta crta

/NeedAppearances zastavica ovde nije opcionalna

Ovo je jedina činjenica koja odlučuje hoće li se vaš rad videti. Pošto učitana putanja ne generiše appearance stream-ove, tek dodati widget stiže u viewer bez /AP unosa: polje bez opisanog izgleda. Mnogi viewer-i, kada treba da prikažu widget koji nema appearance i nema instrukciju da ga sami izgrade, ne nacrtaju ništa. Polje jeste u fajlu, strukturno ispravno, dostupno alatu za popunjavanje formulara i potpuno nevidljivo čoveku

Zaobilazno rešenje je definisano u ISO 32000-1 §12.7.3: AcroForm rečnik nosi /NeedAppearances logičku vrednost, a kada je true uključen, usklađeni čitač je dužan da sam konstruiše nedostajuće appearance stream-ove iz stringa /DA(default appearance) i vrednosti svakog polja. HotPDF to radi umesto vas. Prvi put kada dodate bilo koje polje u učitani dokument, EnsureLoadedAcroForm se izvršava: ako katalog nema /AcroForm, on ga pravi, ako nema /Fields niza, pravi i njega, i forsira /NeedAppearances true. Ne pozivate ga direktno, ali znanje da postoji objašnjava ponašanje. Takođe objašnjava i napomenu za primenu koju vredi jasno reći: nekolicina minimalističkih ili neusklađenih viewer-a i dalje ignoriše /NeedAppearances i ipak ne prikazuje ništa. Za glavne čitače zastavica radi svoj posao, ali ako vaša publika koristi neuobičajen ugrađeni renderer, testirajte tamo pre nego što bilo šta obećate

Dodavanje šest tipova polja

Svaka metoda prati isti obrazac. Prosleđujete nulti indeks stranice, četiri ugla pravougaonika widgeta u PDF korisničkom koordinatnom prostoru, ime polja i sve dodatne argumente koje taj tip traži. Pravougaonik je X1, Y1, X2, Y2 sa PDF ishodištem u donjem levom uglu stranice, pa veće Y vrednosti stoje više; to je koordinatna konvencija samog formata, a ne ekran sa ishodištem u gornjem levom uglu, i pogrešno tumačenje je druga najčešća greška posle zaboravljanja zastavice. Svaki poziv vraća nulti indeks novog polja, ili -1 ako je indeks stranice van opsega ili objekat stranice nije mogao da se razreši

var
  Pdf: THotPDF;
  Idx: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('contract.pdf') <= 0 then Exit;

    // Text field: name, initial value, max length (0 = unlimited)
    Idx := Pdf.AddLoadedTextField(0, 72, 680, 320, 700, 'FullName', '', 0);

    // CheckBox: export value, initial checked state
    Pdf.AddLoadedCheckBox(0, 72, 640, 90, 658, 'AgreeTerms', 'Yes', False);

    // Signature field: just a name and a rectangle
    Pdf.AddLoadedSignatureField(0, 360, 72, 540, 132, 'ApproverSig');

    if Idx >= 0 then
      Pdf.SaveLoadedDocument('contract-interactive.pdf');
  finally
    Pdf.Free;
  end;
end;

Treći i četvrti string argument tekst polja su ime polja i njegova početna /V vrednost; ceo broj je /MaxLen, upisuje se samo kada je veći od nule. HotPDF svakom izmenljivom polju daje podrazumevani appearance string /Helv 12 Tf 0 0 0 rg, koji viewer koji poštuje /NeedAppearances čita da bi odlučio kojim fontom i kojom bojom iscrtava vrednost. Checkbox prima export value, string koji forma šalje kada je polje čekirano, plus logičku vrednost za početno stanje; interno upisuje odgovarajuće /V, /AS, i /DV nazive unosa tako da je stanje uključeno/isključeno dosledno u trenutku kada se fajl otvori. Prazan export value podrazumevano postaje Yes, uobičajeno ime checkboxa za stanje uključeno

Polja izbora i /Ff bit-zastavice

ComboBox i ListBox su oba polja izbora, tip polja /Ch u ISO 32000-1 §12.7.4. Razlika između padajuće liste i liste za skrolovanje je jedan bit u celobrojnoj vrednosti zastavica polja /Ff: bit 18, Combo zastavica, vrednost $40000. HotPDF postavlja taj bit za AddLoadedComboBox i ostavlja ga isključenim za AddLoadedListBox; inače su ta dva tipa identična, i oba svoje izbore uzimaju kao otvoreni niz stringova upisanih u /Opt unos

// Dropdown (Combo flag set internally) with an initial selection
Pdf.AddLoadedComboBox(0, 72, 600, 300, 620, 'Country', 'Canada',
  ['United States', 'Canada', 'Mexico']);

// Scrolling list, no initial value
Pdf.AddLoadedListBox(0, 72, 520, 300, 590, 'Priority', '',
  ['Low', 'Normal', 'High']);

// Push button with a caption drawn through /MK
Pdf.AddLoadedPushButton(0, 360, 600, 480, 626, 'SubmitBtn', 'Submit');

Dve napomene o listi opcija. HotPDF svaki /Opt unos zapisuje kao običan string, gde su export value i prikazana oznaka isti tekst. ISO 32000-1 §12.7.4.4 takođe dozvoljava dvodelni [export display] oblik kada želite da poslata vrednost bude drugačija od onoga što korisnik čita; metode za kreiranje na učitanom dokumentu koriste jednostavniji jednostruki string oblik, pa ako vam trebaju različite export i prikazane vrednosti, sami biste ih postavili u rezultujući rečnik. A vrednost koju prosledite kao trenutno izabranu za polje treba da bude jedna od opcija koje ste dali, jer je viewer poredi sa listom

PushButton je drugi slučaj vođen zastavicom: tip polja /Btn sa bitom 17, PushButton zastavicom, vrednost $10000. Taj bit razlikuje klikabilno dugme od checkboxa, koji je takođe /Btn polje ali bez njega. Natpis koji prosledite upisuje se u rečnik karakteristika izgleda /MK kao normalni natpis /CA. Vredi biti iskren oko opsega: dugme se pravi sa svojom etiketom i pravougaonikom, ali metoda za kreiranje na učitanom dokumentu ne priključuje akciju, pa je samo po sebi dugme koje izgleda ispravno i ne radi ništa kada se klikne. Povezivanje submit, reset ili JavaScript akcija je zasebna tema; za stranu autorstva od nule, tok polje-plus-akcija pokriven je u izradi AcroForm polja i akcija u Delphiju, što je prava tačka poređenja za ono što učitana putanja namerno izostavlja

Rečnik koji svako polje deli

Ispod svih šest metoda nalazi se jedan zajednički graditelj koji konstruiše widget anotaciju i registruje je na dva mesta. On upisuje /Type /Annot i /Subtype /Widget, /Rect niz iz vaših četiri koordinata, zastavice anotacije /F 4 koje postavljaju Print bit tako da se polje pojavljuje i na papiru i na ekranu, ime polja /T, tip polja /FT, zastavice /Ff, i /P povratnu vezu ka objektu stranice. Zatim dodaje novo polje u AcroForm-ov /Fields niz i u /Annots niz te stranice, razrešavajući indirektne reference usput tako da proširuje stvarne nizove umesto da widget ostavi kao siroče

Ta dvostruka registracija je važna jer je widget koji postoji samo u jednoj od dve liste na suptilan način pokvaren. Polje prisutno u /Fields ali odsutno iz stranice /Annots poznato je formi ali se nikada ne iscrtava; obrnuto se iscrtava ali je nepoznato form logici. HotPDF drži oba dela sinhronizovana pri svakom dodavanju, što je vrsta administracije koju biste inače morali da izvedete potpuno tačno ručno prema specifikaciji

Nekoliko iskrenih ograničenja

Postavite očekivanja pre nego što na ovome izgradite radni tok. Ponašanje flatten-and-regenerate zavisi od toga da li viewer poštuje /NeedAppearances, što pokriva Acrobat, moderne browser PDF engine-ove i uobičajene desktop čitače, ali nije tvrda garancija za svaki renderer u praksi. Ako morate da proizvedete fajl čija polja svuda izgledaju identično, uključujući i viewer-e koji ignorišu zastavicu, onda ste u appearance-stream teritoriji i putanja od nule koja vam iscrtava /AP je bolji izbor. Polje za potpis se, isto tako, pravi kao prazni signature widget spreman za potpisivanje; postavljanje polja nije isto što i primena kriptografskog potpisa

Za menjanje onoga što već postoji umesto dodavanja, srodna operacija je flattening forme, gde interaktivna polja utiskujete nazad u statički sadržaj stranice tako da vrednosti postanu trajne i neizmenljive; taj povratni krug, uključujući i način na koji se obrađuju forme sa XFA podacima, opisan je u izravnavanju XFA i AcroForm polja u Delphiju. Dodavanje polja i izravnavanje polja su dva kraja istog životnog ciklusa: ovaj članak je način da interaktivnost dovedete na dokument koji je nije imao, a izravnavanje je način da je skinete kada forma posluži svojoj svrsi

API za forme na učitanom dokumentu prikazan ovde isporučuje se kao deo standardnog HotPDF Component za Delphi i C++Builder, uz potpunu referencu za zastavice polja, obradu appearance-a i ostatak AcroForm modela