Du har en fakturaskabelon fra en tredjepart eller en arkiveret kontrakt, som nogen genererede for år tilbage i software, ingen længere kan finde, og kravet er at gøre den interaktiv: sæt en signaturboks i hjørnet, tilføj et tekstfelt til kundenavn, måske et afkrydsningsfelt til accept og en rullemenu til land, og gør det uden at bygge siden om i hånden
HotPDF er en native VCL PDF-komponent til Delphi og C++Builder, og fra v2.247.0 eksponerer den en dedikeret metodefamilie til netop dette: at bygge alle seks standardfelttyper direkte på et dokument, der er indlæst med LoadFromFile. Denne artikel gennemgår, hvad metoderne gør, den ISO 32000-1-ordbog de konstruerer, og den ene flagbit, uden hvilken hele øvelsen lydløst producerer en fil, der ser tom ud
Hvorfor feltoprettelse på indlæste dokumenter er sin egen kodevej
Når du bygger en PDF fra bunden, ejer HotPDF hele objektmodellen. Hver side er en skrivbar THPDFPage-wrapper, og når du tilføjer et tekstfelt via AddTextField, kobler det nye widget ind i sidens annotation-objekt, sideobjektet og formularens feltkollektion, og derefter genereres en appearance-strøm ud fra dokumentets skrifttype-ressourcer. Appearance-strømmen er widgettens synlige overflade, boksen, rammen og al standardtekst, malet som PDF-tegneoperatører som fremviseren gengiver ordret
Et indlæst dokument giver dig intet af det stillads. Siderne kom ind som rå ordbøger; der er ingen skrivbar THPDFPage-wrapper at hægte et widget på, og endnu vigtigere er der ingen skrifttypepipeline klar til at male appearance-strømme. Den indlæste vej tager derfor en anden rute. Den skriver feltordbøgerne direkte ind i den parse-de objektgraf og adresserer sider ved nulbaseret indeks i stedet for via sideobjektet. Felttyperne og flagbittene matcher den fra-bunden-vej præcist, så et tekstfelt er et tekstfelt uanset hvad; det, der ændrer sig, er plumbingen underneden og, afgørende, hvordan widgettens overflade bliver tegnet
/NeedAppearances-flaget er ikke valgfrit her
Dette er den ene faktor, der afgør, om dit arbejde kan ses. Fordi den indlæste vej ikke genererer appearance-strømme, ankommer et nytilføjet widget til fremviseren uden en /AP-post: et felt uden beskrevet overflade. Mange fremvisere, der bliver bedt om at gengive et widget, som hverken har appearance eller instruktion om at bygge en, tegner slet intet. Feltet findes i filen, er strukturelt gyldigt, kan adresseres af et formularudfyldningsværktøj, og er helt usynligt for et menneske
Udkørslen er defineret i ISO 32000-1 §12.7.3: AcroForm-ordbogen bærer en /NeedAppearances-boolsk værdi, og når den er true, skal en conforming reader selv konstruere de manglende appearance-strømme ud fra hvert felts /DA-streng for standardudseende og værdi. HotPDF sætter dette for dig. Første gang du tilføjer et felt til et indlæst dokument, kører EnsureLoadedAcroForm: hvis catalog ikke har nogen /AcroForm, opretter den en, hvis der ikke findes en /Fields-array, opretter den også den, og den tvinger /NeedAppearances true. Du kalder den ikke direkte, men det forklarer adfærden. Det forklarer også en vigtig distributionsdetalje: nogle få minimale eller ikke-konforme fremvisere ignorerer /NeedAppearances og viser stadig intet. For almindelige læsere gør flaget sit arbejde, men hvis dit publikum bruger en usædvanlig indlejret renderer, så test dér, før du lover noget
Tilføjelse af de seks felttyper
Hver metode følger samme form. Du sender sidens nulbaserede indeks, widget-rectanglets fire hjørner i PDF user-space-koordinater, feltnavnet og de ekstra argumenter, typen kræver. Rectanglet er X1, Y1, X2, Y2 med PDF-udgangspunktet nederst til venstre på siden, så større Y-værdier ligger højere oppe; det er koordinatkonventionen fra filformatet, ikke skærmens top-venstre-konvention, og at få det vendt forkert er den næstmest almindelige fejl efter at glemme flaget. Hvert kald returnerer det nye felts nulbaserede indeks, eller -1 hvis sideindekset lå uden for området, eller sideobjektet ikke kunne løses
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;
Tekstfeltets tredje og fjerde strengargument er feltnavnet og dets indledende /V-værdi; heltallet er /MaxLen, som kun skrives, når det er større end nul. HotPDF giver hvert redigerbart felt en standard-appearance-streng på /Helv 12 Tf 0 0 0 rg, og det er den, en /NeedAppearances-respekterende fremviser læser for at afgøre, hvilken skrifttype og farve den skal male værdien med. Afkrydsningsfeltet tager en eksportværdi, altså den streng formularen sender, når boksen er markeret, plus en boolsk værdi for starttilstanden; internt skriver det de matchende /V-, /AS- og /DV-navneposter, så on/off-tilstanden er konsistent i det øjeblik filen åbnes. En tom eksportværdi falder tilbage til Yes, det konventionelle "tændt"-navn for checkbox
Valgfelter og /Ff-flagbittene
ComboBox og ListBox er begge valgfelter, feltype /Ch i ISO 32000-1 §12.7.4. Forskellen mellem en dropdown og en rullende liste er én bit i feltflagenes heltal /Ff: bit 18, Combo-flaget, værdien $40000. HotPDF sætter den bit for AddLoadedComboBox og lader den være klar for AddLoadedListBox; ellers er de to identiske, og begge tager deres valg som et åbent array af strenge, der skrives til /Opt-posten
// 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');
To noter om valglister. HotPDF skriver hver /Opt-post som en simpel streng, hvor eksportværdien og den viste etiket er den samme tekst. ISO 32000-1 §12.7.4.4 tillader også formen med to elementer, [export display], når du har brug for, at den indsendte værdi skal være forskellig fra det, brugeren læser; de indlæste oprettelsesmetoder bruger den enklere strengform, så hvis du har brug for forskellige eksport- og visningsværdier, skal du sætte dem på den resulterende ordbog selv. Og den værdi, du sender som feltets aktuelle valg, bør være en af de valgmuligheder, du leverede, eftersom fremviseren matcher den mod listen
Trykknappen er det andet flagstyrede tilfælde: felttype /Btn med bit 17, PushButton-flaget, værdien $10000. Den bit er det, der adskiller en klikbar knap fra en checkbox, som også er et /Btn-felt, bare uden den bit. Den caption, du sender, skrives ind i appearance-egenskabsordbogen /MK som den normale caption /CA. Det er værd at være ærlig om omfanget her: knappen oprettes med sin label og sit rectangle, men den indlæste oprettelsesmetode tilknytter ingen action, så den er alene en knap, der ser rigtig ud og ikke gør noget, når den klikkes. At koble submit-, reset- eller JavaScript-actions på er en separat opgave; på den fra-bunden-side er felt-plus-action-arbejdsgangen dækket i building AcroForm fields and actions in Delphi, som er det rigtige sammenligningspunkt for det, den indlæste vej bevidst lader være
Den ordbog hvert felt deler
Under alle seks metoder ligger én fælles builder, som konstruerer widget-annotationen og registrerer den to steder. Den skriver /Type /Annot og /Subtype /Widget, /Rect-arrayet ud fra dine fire koordinater, annotationsflagen /F 4, som sætter Print-biten, så feltet vises på papir såvel som på skærm, feltnavnet /T, felt-typen /FT, flagene /Ff og en /P-tilbage-reference til sideobjektet. Derefter tilføjer den det nye felt til AcroForms /Fields-array og til sidens /Annots-array, mens den løser indirekte referencer undervejs, så den udvider de rigtige arrays i stedet for at gøre widgetten forældreløs
Den dobbelte registrering betyder noget, fordi et widget, der kun lever i én af de to lister, er defekt på en subtil måde. Et felt, der findes i /Fields, men mangler fra sidens /Annots, er kendt af formularen, men bliver aldrig tegnet; det omvendte bliver tegnet, men er ukendt for formularlogikken. HotPDF holder begge synkroniserede ved hvert eneste add, og det er den slags bogføring, du ellers selv skulle have gjort helt korrekt i hånden efter specifikationen
Et par ærlige begrænsninger
Sæt forventningerne, før du bygger en arbejdsgang på dette. Flatten-and-regenerate-adfærden afhænger af, at fremviseren respekterer /NeedAppearances, hvilket dækker Acrobat, moderne browser-PDF-motorer og de almindelige desktop-læsere, men ikke er en hård garanti på tværs af alle renderere i det fri. Hvis du skal levere en fil, hvor felterne gengives identisk overalt, også i fremvisere der ignorerer flaget, er du ovre i appearance-strømme, og den fra-bunden-oprettelsesvej, som maler /AP for dig, er det bedre valg. Signaturfeltet bliver ligeledes oprettet som en tom signatur-widget klar til at blive underskrevet; at placere feltet er ikke det samme som at anvende en kryptografisk signatur
For at ændre noget, der allerede findes, i stedet for at lægge noget til, er den relaterede operation formular-flattening, hvor du bager interaktive felter tilbage til statisk sideindhold, så værdierne bliver permanente og ikke kan redigeres; den rundtur, inklusive hvordan XFA-bærende formularer håndteres, er beskrevet i flattening XFA and AcroForm fields in Delphi. At tilføje felter og at flattene felter er to ender af den samme livscyklus: denne artikel er måden, du får interaktivitet ind på et dokument, der manglede den, og flattening er måden, du tager den af igen, når formularen har tjent sit formål
Den viste formular-API til indlæste dokumenter følger som en del af den standard HotPDF Component til Delphi og C++Builder, sammen med hele referencen for feltflaggene, appearance-håndtering og resten af AcroForm-modellen