Du har en tredjeparts fakturamal, eller en arkivert kontrakt noen genererte for mange år siden i en programvare ingen lenger kan finne, og kravet er å gjøre den interaktiv: plasser en signaturboks i hjørnet, legg til et par tekstfelt, og gjør en flat sjekkliste om til ekte avkrysningsbokser. Det vanskelige er at du ikke oppretter denne PDF-en fra bunnen av. Den eksisterer allerede, den har allerede sider, innholdsstrømmer og fonter du ikke kontrollerer, og du må pode AcroForm-skjemaelementer (widgets) på den objektsgrafen uten å bygge den opp på nytt. Dette er et annet problem enn å opprette et skjema i et nytt dokument, og den delen som vanligvis skaper problemer for folk er usynlig helt til du åpner resultatet i en viser, og feltene du akkurat skrev ikke er å se noen steder på siden
HotPDF er en innfødt VCL PDF-komponent for Delphi og C++Builder, og fra og med v2.247.0 eksponerer den en dedikert familie av metoder for akkurat dette: å bygge alle de seks standard felttypene direkte på et dokument lastet med LoadFromFile. Denne artikkelen går gjennom hva disse metodene gjør, ISO 32000-1-ordboken de konstruerer, og det ene flagget uten hvilket hele operasjonen lydløst produserer en tom fil
Hvorfor feltoppretting i innlastede dokumenter har en egen kodeflyt
Når du bygger en PDF fra ingenting, eier HotPDF hele objektmodellen. Hver side er en skrivbar THPDFPage-innpakning, og det å legge til et tekstfelt via AddTextField kobler det nye skjemaelementet inn i sidens annoteringsobjekt, sideobjektet og skjemaets feltsamling, og genererer deretter en utseendestrøm (appearance stream) fra dokumentets skriftressurser. Utseendestrømmen er den synlige overflaten til skjemaelementet, boksen og rammen samt eventuell standardtekst, tegnet som PDF-tegneoperatorer viseren rendrer ordrett
Et lastet dokument gir deg ingenting av dette rammeverket. Sidene kom inn som rå ordbøker (dictionaries); det er ingen skrivbar THPDFPage-innpakning å henge et skjemaelement på, og enda viktigere: det finnes ingen skriftressurs-rørledning klar til å tegne utseendestrømmer. Den innlastede banen tar derfor en annen vei. Den skriver felt-ordbøkene direkte på den tolkede objektgrafen og adresserer sider ved hjelp av en 0-basert indeks i stedet for sideobjekt. Felttypene og flaggbitene samsvarer nøyaktig med banen fra bunnen av, så et Text-felt er et Text-felt uansett; det som endres er rørleggerarbeidet under og, avgjørende, hvordan skjemaelementets overflate blir tegnet
/NeedAppearances-flagget er ikke valgfritt her
Dette er det eneste faktumet som avgjør om arbeidet ditt blir synlig. Fordi den innlastede banen ikke genererer utseendestrømmer, ankommer et nylig lagt til skjemaelement viseren uten noen /AP-oppføring: et felt uten beskrevet overflate. Mange visere, som blir bedt om å rendre et skjemaelement som ikke har noe utseende og ingen instruksjoner om å bygge et, tegner ingenting i det hele tatt. Feltet er i filen, strukturelt gyldig, adresserbart for et skjemafyllingsverktøy, og computationally usynlig for et menneske
Fluktveien er definert i ISO 32000-1 §12.7.3: AcroForm-ordboken bærer en boolsk verdi /NeedAppearances, og når denne er true er en kompatibel leser pålagt å konstruere de manglende utseendestrømmene selv ut fra hvert felts /DA-streng (standard utseende) og verdi. HotPDF setter dette for deg. Første gang du legger til et felt i et lastet dokument, kjøres EnsureLoadedAcroForm: hvis katalogen ikke har noen /AcroForm oppretter den en, hvis det ikke er noen /Fields-tabell oppretter den det, og den tvinger /NeedAppearances true. Du kaller den ikke direkte, men å vite at den eksisterer forklarer oppførselen. Det forklarer også et distribusjonsforbehold verdt å nevne tydelig: en håndfull minimale eller ikke-kompatible visere ignorerer /NeedAppearances og rendrer fremdeles ingenting. For vanlige lesere gjør flagget jobben sin, men hvis publikummet ditt kjører en uvanlig innebygd tegningsmotor, må du teste der før du lover noe
Slik legger du til de seks felttypene
Hver metode følger den samme formen. Du sender inn den 0-baserte sideindeksen, de fire hjørnene av skjemaelementets rektangel i PDF-brukerromskoordinater, feltnavnet og de ekstra argumentene typen trenger. Rektangelet er X1, Y1, X2, Y2 med PDF-origo nederst til venstre på siden, slik at større Y-verdier sitter høyere opp; dette er koordinatkonvensjonen fra filformatet, ikke konvensjonen med øverst til venstre på skjermen, og å snu dette bak-frem er den nest vanligste feilen etter å glemme flagget. Hvert kall returnerer det nye feltets 0-baserte indeks, eller -1 hvis sideindeksen var utenfor området eller sideobjektet ikke kunne løses opp
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 strengargumenter er feltnavnet og dets opprinnelige /V-verdi; heltallet er /MaxLen, som bare skrives når det er større enn null. HotPDF gir hvert redigerbare felt en standard utseendestreng på /Helv 12 Tf 0 0 0 rg, som er det en viser som respekterer /NeedAppearances leser for å bestemme skrifttypen og fargen den tegner verdien med. Avkrysningsboksen tar en eksportverdi, strengen skjemaet sender inn når boksen er krysset av, pluss en boolsk verdi for den opprinnelige tilstanden; internt skriver den de matchende /V-, /AS- og /DV-navneoppføringene slik at på/av-tilstanden er konsistent i det øyeblikket filen åpnes. En tom eksportverdi faller tilbake på Yes, det konvensjonelle navnet for avkrysningsboks "på"
Valgfelter og /Ff-bitflaggene
ComboBox og ListBox er begge valgfelt, felttype /Ch i ISO 32000-1 §12.7.4. Forskjellen mellom en rullegardinliste (dropdown) og en rullbar liste er én bit i feltflagg-heltallet /Ff: bit 18, Combo-flagget, verdi $40000. HotPDF setter den biten for AddLoadedComboBox og lar den være usatt for AddLoadedListBox; ellers er de to identiske, og begge tar valgene sine som en åpen tabell med strenger skrevet til /Opt-oppføringen
// 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 merknader om alternativlisten. HotPDF skriver hver /Opt-oppføring som en ren streng, der eksportverdien og den viste etiketten er samme tekst. ISO 32000-1 §12.7.4.4 tillater også formen med to elementer [export display] når du trenger at den innsendte verdien skal avvike fra det brukeren leser; de innlastede opprettelsesmetodene bruker den enklere formen med en enkelt streng, så hvis du trenger avvikende eksport- og visningsverdier, må du sette dem i den resulterende ordboken selv. Og verdien du sender inn som feltets gjeldende valg, bør være et av alternativene du oppga, ettersom viseren matcher den mot listen
Trykknappen er det andre flagg-drevne tilfellet: felttype /Btn med bit 17, PushButton-flagget, verdi $10000. Den biten er det som skiller en klikkbar knapp fra en avkrysningsboks, som også er et /Btn-felt, men uten denne biten. Teksten du sender inn skrives inn i ordboken for utseendekarakteristikker /MK som standardteksten /CA. Det er verdt å være ærlig om omfanget her: knappen opprettes med sin etikett og sitt rektangel, men den innlastede opprettelsesmetoden knytter ikke til noen handling, så i seg selv er det en knapp som ser riktig ut, men gjør ingenting når den klikkes. Å koble til handlinger for innsending, tilbakestilling eller JavaScript er en egen sak; for opprettelse fra bunnen av dekkes arbeidsflyten med felt-pluss-handling i bygging av AcroForm-felt og handlinger i Delphi, som er det rette sammenligningspunktet for hva den innlastede banen bevisst utelater
Ordboken alle felter deler
Under alle de seks metodene ligger en felles byggefunksjon som konstruerer skjemaelement-annoteringen og registrerer den på to steder. Den skriver /Type /Annot og /Subtype /Widget, /Rect-tabellen fra de fire koordinatene dine, annoteringsflaggene /F 4 som setter Print-biten slik at feltet vises på papir så vel som på skjerm, feltnavnet /T, felttypen /FT, flaggene /Ff og en /P-bakreferanse til sideobjektet. Deretter legger den det nye feltet til i AcroForms /Fields-tabell og i den sidens /Annots-tabell, og løser opp indirekte referanser underveis slik at den utvider de reelle tabellene i stedet for å etterlater elementet foreldreløst
Denne doble registreringen har betydning fordi et skjemaelement som bare lever in en av de to listene er ødelagt på en subtil måte. Et felt som er til stede i /Fields, men mangler i sidens /Annots, er kjent for skjemaet, men tegnes aldri; det motsatte blir tegnet, men er ukjent for skjemalogikken. HotPDF holder begge i synk ved hver innsetting, noe som er den typen bokføring du ellers måtte ha gjort nøyaktig riktig for hånd mot spesifikasjonen
Noen ærlige begrensninger
Sett forventningene før du bygger en arbeidsflyt på dette. Justerings- og gjenopprettingsoppførselen avhenger av at viseren respekterer /NeedAppearances, noe som dekker Acrobat, moderne PDF-motorer i nettlesere og de vanlige skrivebordsleserne, men er ikke en absolutt garanti på tvers av alle tegningsmotorer der ute. Hvis du må produsere en fil der feltene tegnes identisk overalt, inkludert i visere som ignorerer flagget, er du i utseendestrøm-territorium (appearance stream), og opprettelsesbanen fra bunnen av som tegner /AP for deg er et bedre valg. Signaturfeltet opprettes på samme måte som et tomt signaturelement klar til å signeres; det å plassere feltet er ikke det samme som å bruke en kryptografisk signatur
For å endre det som allerede eksisterer i stedet for å legge til nytt, er den relaterte operasjonen skjemautflating (form flattening), der du baker interaktive felt tilbake til statisk sideinnhold slik at verdiene blir permanente og uredigerbare; den rundreisen, inkludert hvordan XFA-bærende skjemaer håndteres, diskuteres i utflating av XFA- og AcroForm-felt i Delphi. Det å legge til felt og flate ut felt er to ender av samme livssyklus: denne artikkelen viser hvordan du får interaktivitet inn i et dokument som manglet det, og utflating viser hvordan du tar det bort igjen når skjemaet har tjent sitt formål
Skjema-API-et for innlastede dokumenter som vises her leveres som en del av standard HotPDF Component for Delphi og C++Builder, sammen med den fullstendige referansen for feltflagg, utseendebehandling og resten av AcroForm-modellen