Anda punya template invoice dari pihak ketiga, atau kontrak lama yang dibuat bertahun-tahun lalu dengan software yang sekarang sudah tidak ada lagi, lalu diminta membuatnya interaktif: menaruh kotak tanda tangan di sudut, menambah beberapa field teks, atau mengubah checklist sederhana menjadi CheckBox yang sebenarnya. Tantangannya adalah Anda tidak membuat PDF itu dari awal. File sudah ada, lengkap dengan halaman, content stream, dan font yang tidak Anda kontrol, lalu Anda harus menempelkan widget AcroForm ke graph objek itu tanpa membuat ulang seluruh struktur. Ini berbeda dengan membuat form pada dokumen baru, dan penyebab umum kegagalan biasanya baru terlihat setelah Anda membuka hasil di viewer dan field yang ditulis tidak muncul di halaman.
HotPDF, komponen VCL PDF asli untuk Delphi dan C++Builder, mulai v2.247.0 menyediakan keluarga method khusus untuk kasus ini: membangun enam tipe field standar langsung pada dokumen yang dimuat melalui LoadFromFile. Artikel ini menjelaskan cara kerja tiap method, dictionary ISO 32000-1 yang dihasilkan, dan satu flag yang jika hilang akan membuat output terlihat kosong.
Mengapa pembuatan field pada loaded document memiliki jalur sendiri
Saat membuat PDF dari nol, HotPDF mengendalikan seluruh object model. Setiap halaman adalah THPDFPage wrapper yang dapat ditulis, dan penambahan AddTextField mengikat widget baru ke objek anotasi halaman, objek halaman, dan kumpulan field form, lalu menghasilkan stream appearance dari resource font dokumen. Stream appearance adalah permukaan visual dari widget, termasuk border, kotak, dan teks default, yang dirender viewer persis sesuai operator gambar PDF.
Pada dokumen yang sudah dimuat Anda tidak mendapat scaffolding tersebut. Halaman datang sebagai raw dictionary, tidak ada THPDFPage wrapper untuk menempelkan widget, dan yang lebih penting tidak ada alur resource font yang siap untuk mewarnai appearance stream. Karena itu jalur loaded document berbeda. Ia menulis dictionary field langsung ke graph objek yang sudah diparse dan mengakses halaman lewat indeks nol, bukan lewat objek halaman. Jenis field dan bit flag sama dengan jalur dari-scratch, sehingga Text field tetap Text field; yang berubah adalah plumbing di bawahnya, dan terutama bagaimana permukaan widget digambar.
Flag /NeedAppearances tidak opsional di sini
Ini adalah poin kunci yang menentukan apakah karya Anda terlihat. Karena jalur loaded tidak membuat appearance streams, widget yang baru ditambahkan masuk ke viewer tanpa entri /AP. Hasilnya, widget tidak punya surface, dan banyak viewer tidak akan merender apa pun karena tidak ada instruksi untuk membangunnya. File tetap valid secara struktural dan dapat ditarget form-filling tool, tetapi tidak terlihat manusia.
Pelengkapnya dijelaskan di ISO 32000-1 §12.7.3: dictionary AcroForm memiliki boolean /NeedAppearances. Jika nilainya true, reader yang sesuai akan membangun appearance stream yang hilang dari setiap nilai /DA (default appearance) dan value field. HotPDF menangani ini untuk Anda. Saat Anda menambahkan field pertama di dokumen loaded, EnsureLoadedAcroForm berjalan: jika Catalog belum punya /AcroForm maka dibuat, jika belum ada array /Fields maka dibuat juga, dan /NeedAppearances true dipaksa aktif. Anda tidak memanggilnya secara langsung, namun pemahamannya penting untuk perilaku yang muncul. Harus diingat juga ada beberapa viewer minimalis atau tidak sesuai standar yang mengabaikan /NeedAppearances sehingga tetap tidak render apa pun. Untuk pembaca umum flag ini bekerja, tetapi jika audience Anda memakai renderer tersemat yang jarang dipakai, uji di sana sebelum membuat komitmen fungsional.
Menambahkan enam jenis field
Setiap method memakai bentuk pemanggilan yang sama. Anda memberi indeks halaman nol-based, empat sudut rectangle widget di koordinat user-space PDF, nama field, dan argumen tambahan sesuai tipe. Koordinatnya adalah X1, Y1, X2, Y2 dengan origin PDF di kiri bawah, sehingga Y yang lebih besar berarti posisi lebih tinggi. Ini mengikuti konvensi format file, bukan konvensi layar kiri atas, dan membaliknya adalah kesalahan yang sering terjadi setelah lupa flag ini. Setiap pemanggilan mengembalikan indeks field baru berbasis nol, atau -1 jika indeks halaman di luar rentang atau objek halaman tidak dapat diresolusi.
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;
Argumen ketiga dan keempat pada field teks adalah nama field serta nilai awal /V. Integer keempatnya adalah /MaxLen, dan ditulis hanya jika lebih dari nol. HotPDF memberi setiap field editable default appearance string /Helv 12 Tf 0 0 0 rg, yang dipakai viewer yang menghormati /NeedAppearances untuk menentukan font dan warna saat menampilkan nilai. Checkbox menerima export value, yaitu string yang di-submit saat box dicentang, plus boolean untuk status awal. Di dalamnya ditulis entri /V, /AS, dan /DV agar state on dan off konsisten begitu file dibuka. Export value kosong akan memakai nilai default Yes, nama "on" yang umum untuk checkbox.
Choice field dan bit flag /Ff
ComboBox dan ListBox termasuk choice fields dengan tipe /Ch menurut ISO 32000-1 §12.7.4. Perbedaannya terletak pada satu bit di integer flag /Ff: bit 18 yaitu Combo dengan nilai $40000. HotPDF mengaktifkan bit itu untuk AddLoadedComboBox dan membiarkannya kosong pada AddLoadedListBox. Secara struktur keduanya identik, serta daftar pilihan diterima sebagai array terbuka string yang ditulis ke entri /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');
Catatan untuk opsi. HotPDF menulis setiap entri /Opt sebagai string sederhana, sehingga nilai export dan label tampil sama. ISO 32000-1 §12.7.4.4 juga mengizinkan format dua elemen [export display] jika value yang dikirim berbeda dari nilai tampilan, tetapi pada method loaded Anda memakai bentuk string tunggal, jadi jika butuh perbedaan Anda harus atur sendiri pada dictionary hasilnya. Nilai pilihan saat ini juga harus termasuk dalam opsi yang Anda kirim, karena viewer memeriksanya terhadap daftar.
Push button adalah kasus lain yang didorong flag. Tipe /Btn dengan bit 17 yaitu PushButton, bernilai $10000. Bit ini yang membedakan tombol klik dari checkbox yang juga /Btn tetapi tanpa bit itu. Caption yang Anda kirim masuk ke /MK sebagai caption normal /CA. Jujur saja, method ini membuat tombol lengkap dengan label dan rectangle, namun tidak membuat action. Kalau diklik sendiri, tombol hanya tampil benar tetapi tidak melakukan apa pun. Menyambungkan action submit, reset, atau JavaScript adalah urusan terpisah. Workflow field plus action untuk jalur from-scratch dijelaskan di building AcroForm fields and actions in Delphi, yang membantu membandingkan apa saja yang sengaja tidak disertakan pada jalur loaded.
Dictionary yang dipakai setiap field
Di balik enam method ini ada satu builder bersama yang membuat anotasi widget dan mendaftarkannya di dua tempat. Ia menulis /Type /Annot dan /Subtype /Widget, array /Rect dari empat koordinat Anda, flag anotasi /F 4 yang mengatur bit Print agar field juga terlihat saat dicetak, /T untuk nama field, /FT untuk tipe field, /Ff, dan /P sebagai referensi balik ke page object. Kemudian field baru ditambahkan ke array /Fields AcroForm dan array /Annots halaman, dengan penyelesaian referensi tidak langsung agar array yang dipakai benar-benar diperpanjang.
Duplikasi pendaftaran ini penting. Widget yang hanya ada di salah satu dari dua list akan rusak secara halus. Field yang ada di /Fields tapi hilang di /Annots dikenali form tetapi tidak pernah dirender, sebaliknya ada di /Annots tetapi tidak di /Fields akan terlihat tapi tidak dikenal logic form. HotPDF menyinkronkan keduanya di setiap operasi tambah, jadi Anda tidak perlu menanganinya manual satu per satu.
Beberapa batasan yang harus diketahui
Siapkan ekspektasi sebelum membuat workflow. Perilaku flatten-and-regenerate ini bergantung pada viewer yang menghormati /NeedAppearances, termasuk Acrobat, engine PDF modern, dan pembaca desktop umum, tetapi tidak berlaku untuk semua renderer. Jika Anda harus menghasilkan file yang rendernya identik di setiap viewer termasuk yang mengabaikan flag, Anda sebaiknya menggunakan jalur appearance stream saat membuat dari awal karena di jalur loaded Anda tidak membuat /AP sendiri. Signature field juga dibuat sebagai signature widget kosong yang siap ditandatangani; menambahkan field belum sama dengan menerapkan tanda tangan kriptografi.
Untuk skenario yang mengubah field yang sudah ada daripada menambah baru, ada proses flattening. Pada proses ini field interaktif dibentuk kembali menjadi konten halaman statis agar nilai menjadi permanen dan tidak dapat diedit. Siklus tersebut, termasuk penanganan XFA AcroForm, dibahas di flattening XFA and AcroForm fields in Delphi. Menambah field dan men-flatten field adalah dua sisi dari satu siklus, yakni menambah interaktivitas lalu mengambilnya kembali setelah dipakai.
API loaded-document form yang dibahas di sini termasuk dalam komponen standar HotPDF Component untuk Delphi dan C++Builder, bersama referensi lengkap untuk flag field, pengelolaan appearance, dan model AcroForm.