XFA, XML Forms Architecture, kini sudah usang (deprecated). ISO 32000-1 mencantumkannya di §12.7 dengan catatan bahwa fitur ini dihapus dari PDF 2.0, dan pembaca dokumen modern mulai menghentikan dukungan untuk mesin XFA mereka satu per satu. Namun, semua itu tidak mengosongkan arsip yang ada. Formulir penerimaan pemerintah, aplikasi asuransi, dan laporan perbankan ditulis sebagai XFA selama hampir dua dekade, dan berkas-berkas tersebut masih tiba di kotak masuk dan sistem pemrosesan dokumen saat ini. Ketika pembaca dokumen yang biasa merendernya berhenti melakukan hal tersebut, formulir berubah menjadi halaman kosong dengan pesan penampung "harap buka di pembaca lain". Perbaikan yang tahan lama adalah meratakan (flatten) XFA menjadi konten PDF statis yang dapat digambar oleh pembaca apa pun.
Bagian tersulit dari perataan (flattening) tersebut bukanlah field-nya. Kotak teks dan kotak centang memetakan ke widget AcroForm dengan cukup bersih. Bagian yang sulit adalah teks kaya (rich text) yang disimpan XFA di dalam elemen gambar, dalam blok <exData contentType="text/html">. Blok tersebut adalah subset HTML dengan gaya inline (inline styling) dan sering kali berisi jangkar (anchors). Menampilkannya di halaman berarti memproduksi kembali teks bergaya dan tautan aktif, dan tautan aktif tersebut adalah tempat di mana sebagian besar implementasi menyerah secara diam-diam.
Seperti apa sebenarnya teks kaya XFA itu
Badan data exData adalah potongan kecil XHTML. Paragraf adalah <p>; rentang karakter bergaya (styled span) adalah <span> dengan CSS inline-nya sendiri untuk ketebalan, kemiringan, warna, dan ukuran; dan hiperlink adalah <a href="..."> yang membungkus teks yang terlihat. Satu baris dapat menampung beberapa rentang (span) berturut-turut, masing-masing dengan gaya berbeda, dan salah satunya bisa berupa jangkar (anchor). Gaya tersebut bukan sekadar dekorasi yang bisa dihilangkan. Klausul yang dirender dengan warna merah tebal karena merupakan peringatan hukum harus tetap tebal dan merah setelah diratakan, jika tidak, dokumen yang diratakan akan menyimpang dari dokumen aslinya.
Menata tata letak run dari kiri ke kanan
Penempatan posisi adalah tempat teks kaya berhenti menjadi masalah penguraian (parsing) dan mulai menjadi masalah tata letak huruf (typesetting). Bagian teks (run) berbagi baris yang sama, sehingga setiap bagian dimulai di tempat bagian sebelumnya berakhir. Tidak ada markup yang mencatat posisi tersebut; semuanya harus diukur. Rutinitas internal mesin LayoutRichText mengukur setiap bagian dengan metrik font yang sama yang nantinya akan mengecatnya, lalu menyetel pergeseran horizontal (horizontal offset) bagian tersebut ke jumlah akumulasi lebar semua bagian sebelumnya. Bagian pertama dimulai pada asal kotak gambar (draw box origin), bagian kedua dimulai pada lebar bagian pertama, bagian ketiga pada lebar gabungan dari dua bagian pertama, dan seterusnya di sepanjang baris.
Inilah mengapa penyelarasan font pengukuran sangat penting. Fase tata letak (layout pass) mengukur pergerakan maju (advances); fase render terpisah menggambar glif. Jika kedua fase tersebut tidak sepakat tentang font-nya, kotak yang dihitung oleh tata letak tidak akan berada di bawah glif yang digambar oleh perender. HotPDF menjaganya tetap selaras dengan memetakan gaya run yang diselesaikan ke spesifikasi font, melalui pembantu internal RunStyleToFontSpec, yang cocok dengan default perender itu sendiri yaitu Arial pada 10 poin. Pergerakan maju yang diukur dan teks yang digambar kemudian sesuai, dan kotak run yang dihitung benar-benar menutupi karakter yang dilihat pembaca.
// Conceptual shape of one laid-out run. The engine builds an array of these
// internally; you never construct them yourself, but the fields explain how a
// link's hit box is derived from measured geometry rather than from text.
type
TRichRunInfo = record
Dx, Dy : Double; // top-left, relative to the draw-box origin
W, H : Double; // measured run box (width from the layout pass)
Text : AnsiString; // the run's visible characters
Href : AnsiString; // URI target for an <a> run, '' otherwise
end;
Dari run jangkar ke anotasi Tautan PDF
Hiperlink dalam PDF yang sudah selesai bukan merupakan bagian dari konten halaman. Ini adalah objek terpisah, anotasi Tautan (Link annotation), yang dijelaskan dalam ISO 32000-1 §12.5.6.5. Anotasi tersebut memiliki /Rect yang mendefinisikan persegi panjang yang dapat diklik pada halaman dan tindakan yang aktif ketika persegi panjang tersebut diklik. Untuk tautan eksternal, tindakannya adalah tindakan URI: /S /URI dengan alamat target sebagai string /URI miliknya. Teks yang terlihat di bawahnya adalah konten halaman biasa; anotasi tersebut adalah zona aktif tidak terlihat yang diletakkan di atasnya.
Jalur perataan (flatten path) mengikuti model ini secara tepat. Ketika run membawa Href, HotPDF pertama-tama menggambar teks bergaya, lalu membangun anotasi Tautan di atas kotak run tersebut. Titik masuk publik untuk anotasi tersebut adalah metode halaman AddURILink, yang membuat objek /Type /Annot /Subtype /Link dengan tindakan /URI dan mengembalikan kamus anotasi. Persegi panjangnya adalah kotak run terukur, yang diterjemahkan dari koordinat lokal elemen gambar ke koordinat halaman. Hasilnya adalah tautan yang mendarat tepat di atas teks jangkar dan tidak di tempat lain.
// The same public API the flatten path uses for each anchor run. It produces
// an ISO 32000-1 12.5.6.5 Link annotation: /Subtype /Link with a /URI action
// over the given rectangle. The optional description fills /Contents so a
// screen reader can announce the target.
var
LinkRect: TRect;
Annot: THPDFDictionaryObject;
begin
LinkRect := Rect(72, 690, 268, 706); // page-space hit box for the run
Annot := Pdf.CurrentPage.AddURILink(LinkRect,
'https://www.example.gov/appeal', 'File an appeal online');
end;
Mengapa hit box harus berasal dari lebar terukur
Sangat menggoda untuk membayangkan menemukan tautan dengan mencari teks yang terlihat di halaman dan menggambar persegi panjang di sekitar apa pun yang ditemukan. Cara tersebut tidak berhasil, dan alasannya sangat mendasar pada bagaimana teks yang diratakan disimpan. Run bergaya dicat dengan subset font yang disematkan. Subset font menomori kembali glif yang disimpannya, sehingga aliran konten halaman menampung kode heksadesimal CID, bukan kode karakter asli. Byte pada halaman bukanlah huruf yang dibaca manusia, dan tidak dapat dicari sebagai teks. Pencarian untuk teks jangkar tidak menemukan apa-apa, karena teks tersebut tidak ada sebagai teks literal di mana pun dalam aliran konten.
Satu-satunya jangkar yang andal untuk persegi panjang tersebut adalah geometri yang telah dihasilkan oleh fase tata letak. Offset dan lebar terukur setiap run dihitung saat mengalirkan baris, sebelum glif apa pun dinomori ulang, dan mereka menggambarkan di mana teks akan muncul secara fisik. Oleh karena itu, HotPDF mengambil persegi panjang tautan langsung dari kotak run yang diletakkan alih-alih dari pencarian teks apa pun. Karena pengukuran menggunakan font render, kotak tersebut benar terlepas dari subsetting. Geometri bertahan dari pengodean; teks tidak. Itulah seluruh argumen untuk penempatan posisi berbasis lebar terukur, dan itulah mengapa perata yang mencoba menyesuaikan kembali tautan dengan pencarian teks menghasilkan zona hit (hit zones) yang bergeser atau hilang.
Menjalankan perataan dari kode Anda
Untuk PDF yang sudah berisi paket XFA, titik masuknya adalah FlattenLoadedXFA. Muat dokumen, panggil metode tersebut, dan simpan hasilnya. Parameter Editable memutuskan apa yang terjadi pada field formulir: teruskan True untuk menjaganya tetap sebagai widget AcroForm yang dapat diisi, atau False untuk menandai setiap widget sebagai baca-saja sehingga hasilnya berupa catatan yang dibekukan. Blok gambar teks kaya, dengan run bergaya dan anotasi tautannya, dibuat dalam kedua cara tersebut. Fungsi mengembalikan jumlah widget yang dikeluarkannya.
var
Pdf: THotPDF;
Emitted, i: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('xfa_appeal_form.pdf');
// True keeps fields fillable; False freezes them read-only.
Emitted := Pdf.FlattenLoadedXFA(True);
// Anything the engine could not map is reported, not raised.
for i := 0 to Pdf.XFAFlattenWarnings.Count - 1 do
Writeln('XFA warning: ', Pdf.XFAFlattenWarnings[i]);
Pdf.SaveLoadedDocument('appeal_form_flat.pdf');
Writeln('Widgets emitted: ', Emitted);
finally
Pdf.Free;
end;
end;
Selalu baca XFAFlattenWarnings setelah panggilan tersebut. Daftar dibersihkan di awal setiap perataan dan mengumpulkan baris untuk setiap elemen yang ditolak untuk dirender oleh mesin: jenis field yang tidak didukung, gambar elemen gambar yang tidak dapat didekodekan, blok exData tanpa rentang (spans) yang dapat digunakan. Tidak ada dari hal tersebut yang menimbulkan pengecualian, sehingga daftar peringatan yang kosong adalah bukti Anda bahwa semuanya terpetakan, dan daftar yang tidak kosong memberi tahu Anda dengan tepat berkas asli mana yang harus diperiksa. Ketika Anda memegang XFA mentah sebagai byte XDP alih-alih PDF yang dimuat, metode saudara ApplyXFAAsAcroForm mengambil byte tersebut secara langsung dan berbagi jalur kode serta perilaku peringatan yang sama. Metode pelengkap AddXFAPacket berjalan ke arah sebaliknya, menyematkan paket XFA ke dalam dokumen yang sedang Anda buat.
Mengonfirmasi hasil di pembaca
Buka berkas yang diratakan di Acrobat, atau pembaca dokumen terkini, dan periksa dua hal. Pertama, teks kaya dirender dengan gayanya yang utuh: run tebal tetap tebal, run berwarna membawa warnanya, dan spans berada dalam urutan yang benar pada baris alih-alih tumpang tindih atau keluar dari kotak. Kedua, hiperlink aktif. Arahkan kursor ke atas jangkar dan bilah status harus menunjukkan alamat target; klik dan tindakan URI harus membukanya. Gunakan inspektur anotasi pembaca untuk mengonfirmasi bahwa masing-masing adalah anotasi /Link asli yang /Rect-nya mendekap teks jangkar, berada di atas konten yang sekarang berupa glif cat biasa alih-alih XFA yang dirender formulir. Kombinasi tersebut, teks statis bergaya ditambah anotasi Tautan nyata pada persegi panjang yang tepat, adalah apa yang membuat dokumen yang diratakan bertahan lebih lama daripada mesin XFA yang tidak lagi dibutuhkannya.
Perataan field itu sendiri, kotak teks, kotak centang, dan daftar pilihan yang mengelilingi teks kaya ini, dibahas dalam panduan kami tentang perataan formulir XFA menjadi widget AcroForm. Untuk cerita yang lebih luas tentang pembuatan dan penempatan anotasi Tautan secara manual, di luar apa yang dihasilkan oleh jalur perataan, lihat bekerja dengan anotasi PDF di HotPDF. Keduanya dibangun di atas model anotasi dan formulir yang sama yang dikirimkan bersama dengan HotPDF Component untuk Delphi dan C++Builder.