Tehnični članak

Dodajanje polj AcroForm v naložen PDF v Delphiju

Imate predlogo računa tretje osebe ali arhivirano pogodbo, ki jo je nekdo ustvaril pred leti v programski opremi, ki je danes nihče več ne najde, zahteva pa je, da dokument postane interaktiven: v kot dodati polje za podpis, dodati nekaj besedilnih polj in navaden kontrolni seznam spremeniti v prave potrditvene kvadratke. Težava je v tem, da tega PDF-ja ne ustvarjate od začetka. Dokument že obstaja, že ima strani, vsebinske tokove in pisave, ki jih ne nadzorujete, vi pa morate na ta graf objektov pripeti gradnike AcroForm, ne da bi ga znova zgradili. To je drugačen problem kot ustvarjanje obrazca v novem dokumentu, del, ki uporabnike največkrat ujame, pa ostane neviden, dokler rezultata ne odprete v pregledovalniku in ugotovite, da polj, ki ste jih pravkar zapisali, nikjer ni na strani

HotPDF je izvorna komponenta VCL PDF za Delphi in C++Builder, od različice v2.247.0 pa ponuja namensko družino metod prav za to: gradnjo vseh šestih standardnih vrst polj neposredno v dokumentu, naloženem z LoadFromFile. Ta članek razloži, kaj te metode počnejo, kakšen slovar po ISO 32000-1 sestavijo in katera zastavica je tista, brez katere celotna operacija potiho ustvari datoteko, ki je videti prazna

Zakaj je ustvarjanje polj v naloženem dokumentu ločena izvajalna pot

Ko PDF zgradite iz nič, HotPDF obvladuje celoten model objektov. Vsaka stran je zapisljiv ovoj THPDFPage, dodajanje besedilnega polja prek AddTextField pa nov gradnik poveže z anotacijskim objektom strani, objektom strani in zbirko polj obrazca, nato pa iz virov pisav dokumenta ustvari appearance stream. Appearance stream je vidna površina gradnika: okvir, obroba in morebitno privzeto besedilo, zapisani kot operatorji risanja PDF, ki jih pregledovalnik izriše dobesedno

Naložen dokument vam ne da nič od tega ogrodja. Strani pridejo kot surovi slovarji, ni zapisljivega ovoja THPDFPage, na katerega bi obesili gradnik, še pomembneje pa ni pripravljene poti z viri pisav, ki bi narisala appearance streame. Naložena pot zato ubere drugo smer. Slovarje polj zapiše neposredno na razčlenjen graf objektov, strani pa naslavlja z ničelnim indeksom namesto prek objekta strani. Vrste polj in biti zastavic so popolnoma enaki kot pri poti iz nič, zato je polje Text v obeh primerih še vedno polje Text. Kar se spremeni, je podlaga pod njim in predvsem način, kako se izriše vidna površina gradnika

Zastavica /NeedAppearances tukaj ni izbirna

To je edino dejstvo, ki odloča, ali bo vaše delo sploh vidno. Ker naložena pot ne ustvarja appearance streamov, sveže dodan gradnik prispe v pregledovalnik brez vnosa /AP: polje torej nima opisane površine. Mnogi pregledovalniki, ko morajo izrisati gradnik brez appearance in brez navodila, naj jo sami zgradijo, ne narišejo ničesar. Polje je v datoteki, strukturno veljavno, dosegljivo orodju za izpolnjevanje obrazcev in človeku povsem nevidno

Izhod v sili določa ISO 32000-1 §12.7.3: slovar AcroForm vsebuje logično vrednost /NeedAppearances, in kadar je true, mora skladen bralnik manjkajoče appearance streame sam zgraditi iz niza in vrednosti /DA posameznega polja. HotPDF to nastavi namesto vas. Ko v naložen dokument prvič dodate katero koli polje, se zažene EnsureLoadedAcroForm: če katalog nima /AcroForm, ga ustvari, če ni polja /Fields, ustvari tudi tega, nato pa prisili še /NeedAppearances true. Tega ne kličete neposredno, vendar poznavanje njegovega obstoja razloži vedenje. Hkrati razloži še opozorilo za produkcijsko rabo, ki ga je vredno povedati naravnost: nekaj minimalističnih ali neskladnih pregledovalnikov /NeedAppearances prezre in še vedno ne izriše ničesar. Pri običajnih bralnikih zastavica opravi svoje delo, če pa vaše občinstvo uporablja nenavaden vgrajeni izrisovalnik, to preverite tam, še preden karkoli obljubite

Dodajanje šestih vrst polj

Vsaka metoda sledi isti obliki. Podate ničelni indeks strani, štiri kote pravokotnika gradnika v koordinatah uporabniškega prostora PDF, ime polja in vse dodatne argumente, ki jih ta vrsta potrebuje. Pravokotnik je X1, Y1, X2, Y2, pri čemer je izhodišče PDF v spodnjem levem kotu strani, zato večje vrednosti Y ležijo višje. To je koordinatna konvencija datotečnega formata, ne zaslonska konvencija z zgornjim levim kotom, in napačna usmeritev je druga najpogostejša napaka takoj za pozabljeno zastavico. Vsak klic vrne ničelni indeks novega polja ali -1, če je indeks strani zunaj obsega ali pa objekta strani ni bilo mogoče razrešiti

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;

Tretji in četrti nizovni argument besedilnega polja sta ime polja in začetna vrednost /V, celo število pa je /MaxLen, ki se zapiše samo, kadar je večje od nič. HotPDF vsakemu polju, ki ga je mogoče urejati, dodeli privzeti niz appearance /Helv 12 Tf 0 0 0 rg, po katerem pregledovalnik, ki spoštuje /NeedAppearances, določi pisavo in barvo za izris vrednosti. CheckBox prejme izvoženo vrednost, torej niz, ki ga obrazec odda, ko je polje označeno, ter logično vrednost za začetno stanje. Interno pa zapiše ustrezne vnose imen /V, /AS in /DV, tako da je stanje vklopljeno ali izklopljeno usklajeno že v trenutku, ko se datoteka odpre. Prazna izvozna vrednost privzeto pomeni Yes, kar je običajno ime za označeno stanje potrditvenega polja

Izbirna polja in biti zastavic /Ff

ComboBox in ListBox sta oba izbirni polji, vrsta polja /Ch po ISO 32000-1 §12.7.4. Razlika med spustnim seznamom in drsečim seznamom je en sam bit v celoštevilčni zastavici polja /Ff: bit 18, zastavica Combo, z vrednostjo $40000. HotPDF ta bit nastavi pri AddLoadedComboBox, pri AddLoadedListBox pa ga pusti izklopljenega. Sicer sta polji enaki, obe pa svoje možnosti prejmeta kot odprto polje nizov, zapisano v vnos /Opt

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

O seznamu možnosti velja povedati dve stvari. HotPDF vsak vnos /Opt zapiše kot navaden niz, pri katerem sta izvozna vrednost in prikazana oznaka enako besedilo. ISO 32000-1 §12.7.4.4 dopušča tudi dvodelno obliko [export display], kadar mora biti oddana vrednost drugačna od tega, kar vidi uporabnik. Metode za ustvarjanje v naloženem dokumentu uporabljajo preprostejšo enonizovno obliko, zato morate, če potrebujete ločeni izvozni in prikazni vrednosti, to nastaviti sami na nastalem slovarju. Vrednost, ki jo podate kot trenutno izbiro polja, pa naj bo ena od ponujenih možnosti, saj jo pregledovalnik primerja s seznamom

Push button je drugi primer, ki ga določa zastavica: vrsta polja /Btn z bitom 17, zastavico PushButton, vrednosti $10000. Prav ta bit loči klikljiv gumb od potrditvenega polja, ki je prav tako polje /Btn, le brez tega bita. Oznaka, ki jo podate, se zapiše v slovar značilnosti appearance /MK kot običajni napis /CA. Tu je pošteno jasno določiti obseg: gumb se ustvari z oznako in pravokotnikom, vendar metoda za ustvarjanje v naloženem dokumentu ne pripne nobenega dejanja, zato je sam po sebi to gumb, ki je videti prav, a ob kliku ne naredi ničesar. Povezovanje dejanj submit, reset ali JavaScript je ločena skrb. Na strani ustvarjanja iz nič je potek polje plus dejanje opisan v gradnji polj in dejanj AcroForm v Delphiju, kar je prava primerjalna točka za to, kar naložena pot namenoma pusti ob strani

Slovar, ki si ga delijo vsa polja

Pod vseh šest metod leži en skupni graditelj, ki sestavi anotacijo gradnika in jo registrira na dveh mestih. Zapiše /Type /Annot in /Subtype /Widget, polje /Rect iz vaših štirih koordinat, zastavice anotacije /F 4, ki nastavijo bit Print, tako da se polje pojavi tako na papirju kot na zaslonu, ime polja /T, vrsto polja /FT, zastavice /Ff in povratno referenco /P na objekt strani. Nato novo polje doda v polje /Fields slovarja AcroForm in v polje /Annots te strani, pri čemer razrešuje posredne reference, da razširi resnična polja in gradnika ne pusti osirotelega

Ta dvojna registracija je pomembna, ker je gradnik, ki živi samo v enem od obeh seznamov, pokvarjen na subtilen način. Polje, prisotno v /Fields, vendar manjkajoče v /Annots strani, je obrazcu znano, vendar se nikoli ne izriše. Obratni primer se izriše, obrazčna logika pa ga ne pozna. HotPDF ob vsakem dodajanju ohranja obe strani usklajeni, kar je prav tista vrsta knjigovodstva, ki bi jo sicer morali proti specifikaciji zadeti popolnoma natančno ročno

Nekaj pošteno povedanih omejitev

Pri takem poteku si pravilno nastavite pričakovanja. Mehanizem flatten-and-regenerate je odvisen od tega, da pregledovalnik spoštuje /NeedAppearances, kar velja za Acrobat, sodobne brskalniške pogone PDF in običajne namizne bralnike, ni pa trdno zagotovilo za vsak izrisovalnik v naravi. Če morate ustvariti datoteko, v kateri so polja povsod videti enako, tudi v pregledovalnikih, ki zastavico ignorirajo, ste že na področju appearance streamov in pot ustvarjanja iz nič, ki vam sama nariše /AP, je boljša izbira. Tudi polje za podpis se ustvari kot prazen podpisni gradnik, pripravljen za podpisovanje. Postavitev polja ni isto kot uporaba kriptografskega podpisa

Za spreminjanje že obstoječega namesto dodajanja novega je soroden postopek flattening obrazca, kjer interaktivna polja spečete nazaj v statično vsebino strani, tako da njihove vrednosti postanejo trajne in jih ni več mogoče urejati. Ta povratna pot, vključno z ravnanjem z obrazci, ki vsebujejo XFA, je opisana v flatteningu polj XFA in AcroForm v Delphiju. Dodajanje polj in flattening polj sta dve skrajnosti istega življenjskega cikla: ta članek pokaže, kako dokumentu, ki interaktivnosti ni imel, to dodate, flattening pa, kako jo po opravljenem delu spet odstranite

API za obrazce v naloženem dokumentu, prikazan tukaj, je del standardnega paketa HotPDF Component za Delphi in C++Builder, skupaj s celotno referenco za zastavice polj, ravnanje z appearance in preostali model AcroForm