losLab PDF Library membaca dan menulis simpul data yang berulang (repeating data nodes) dalam formulir XFA dinamis melalui ekspresi jalur SOM XFA 3.3: GetXFAFormFieldValue dan SetXFAFormFieldValue menerima root $data, $record, dan !data, wildcard kemunculan [*], pemilih turunan (descendant) dan wildcard anak (child-wildcard), properti parent, pemilih kelas #dataGroup/#dataValue, dan predikat gaya FormCalc sederhana, sehingga program Delphi dapat memperbarui setiap baris faktur dalam formulir pajak pemerintah dengan satu panggilan. Artikel ini adalah referensi pengalamatan; untuk memindahkan seluruh kumpulan data antarberkas, lihat artikel pendamping tentang pertukaran data formulir FDF, XFDF, dan XFA
Masalah muncul saat formulir tidak lagi datar. SPT PPN atau deklarasi pabean yang dibangun sebagai formulir XFA dinamis tidak memiliki dua belas bidang bernama Total_Price_1 hingga Total_Price_12; melainkan memiliki satu subformulir Detail yang dideklarasikan sekali dalam DOM templat dan diinstansiasi sebanyak yang diminta oleh data. Nilai-nilai tersebut hidup di pohon kedua, Data DOM di dalam paket dataset, di mana setiap item baris adalah elemen <Detail> yang berulang di bawah <Receipt>. Nama bidang yang datar tidak dapat menunjukkan "harga pada baris ketiga" atau "setiap baris di atas 200"; itulah tugas yang diberikan bab Scripting Object Model dari spesifikasi Adobe XFA 3.3 kepada ekspresi SOM, dan merupakan sintaksis yang digunakan oleh API nilai bidang XFA losLab PDF Library
Bagaimana cara mengalamatkan simpul data XFA dari Delphi?
Setiap jalur dataset dimulai dari root, dan losLab PDF Library menerima penulisan standar secara bergantian: $data adalah bentuk singkat XFA 3.3 dari xfa.datasets.data, !data adalah bentuk singkat yang berakar di xfa.datasets, dan dalam paket yang tidak menggunakan pemrosesan rekaman (record processing) $record menyelesaikan ke rekaman data luar, elemen pertama di bawah simpul data. API sisi templat seperti SetXFAFormFieldAccess menerima $template dan xfa.template dengan cara yang sama. Segmen dapat dipisahkan dengan titik atau garis miring ($data.Receipt.Tax atau $data/Receipt/Tax), hal ini penting karena GetXFAFormFieldNames mengenumerasi bidang sebagai jalur yang dipisahkan garis miring yang langsung dimasukkan kembali ke API nilai dan templat. Indeks kemunculan berbasis nol sesuai dengan spesifikasi, sehingga Detail[0] adalah baris pertama. Dua aturan pelepasan (escaping) menjaga nama-nama yang tidak biasa tetap dapat dialamatkan: dalam jalur yang dipisahkan titik, \. menunjukkan titik harfiah (Line\.Item), sedangkan jalur yang dipisahkan garis miring memperlakukan titik sebagai karakter biasa. Data bisnis yang membawa awalan namespace XML-nya sendiri dicocokkan dengan nama lokalnya, sehingga <m:Receipt> tetap menjawab ke Receipt
var
Lib: TPDFlib;
Tax: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('vat-return.pdf', '');
// Alamat setara untuk simpul data yang sama
Tax := Lib.GetXFAFormFieldValue('Receipt.Tax');
Tax := Lib.GetXFAFormFieldValue('$data.Receipt.Tax');
Tax := Lib.GetXFAFormFieldValue('xfa.datasets.data.Receipt.Tax');
Tax := Lib.GetXFAFormFieldValue('$record.Tax');
// Bentuk garis miring dengan indeks kemunculan berbasis nol
Lib.SetXFAFormFieldValue('$record/Detail[0]/Total_Price', '251.00');
Lib.SaveToFile('vat-return-updated.pdf');
finally
Lib.Free;
end;
end;
Bagaimana cara mengisi baris berulang pada formulir XFA secara terprogram?
Wildcard kemunculan [*] adalah alat massal. Di mana indeks numerik memilih satu saudara kandung (sibling), [*] memilih semua saudara kandung dengan nama yang sama, sehingga $data.Receipt.Detail[*].Total_Price mengalamatkan bidang harga di setiap item baris sekaligus. Pada pembacaan, GetXFAFormFieldValue mengembalikan nilai dari semua simpul yang cocok yang digabungkan dengan pemisah |; pada penulisan, SetXFAFormFieldValue memperbarui setiap simpul yang cocok dengan nilai yang sama dan mengembalikan 1 jika berhasil. Jalur yang tidak menghasilkan kecocokan akan dibaca kembali sebagai string kosong, yang merupakan cara cepat untuk memeriksa apakah suatu cabang ada sebelum menulis ke dalamnya
var
Lib: TPDFlib;
Prices: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('invoice.pdf', '');
// Semua baris Detail sekaligus: '250.00|60.00'
Prices := Lib.GetXFAFormFieldValue('$data.Receipt.Detail[*].Total_Price');
// Atur ulang satu kolom di setiap baris berulang dalam satu panggilan tunggal
Lib.SetXFAFormFieldValue('$data.Receipt.Detail[*].Surcharge', '0.00');
Lib.SaveToFile('invoice-updated.pdf');
finally
Lib.Free;
end;
end;
Pemilih turunan, wildcard anak, dan induk
Tiga pemilih struktural mencakup kasus-kasuk di mana Anda mengetahui nama bidang tetapi tidak mengetahui kedalaman pastinya. Pemilih turunan (descendant) .. cocok pada kedalaman berapa pun di bawah simpul saat ini: $data..Total_Price menemukan Total_Price pertama di mana saja di bawah root data, dan penulisan melalui jalur turunan memperbarui simpul pertama yang cocok tersebut. Perhatikan batasan: indeks kemunculan setelah pencocokan turunan tidak melangkah melintasi cabang saudara (sibling), jadi jika $data..Total_Price[0] diselesaikan di dalam baris Detail pertama, $data..Total_Price[1] akan mengembalikan nilai kosong alih-alih melompat ke baris berikutnya; gunakan Detail[*] ketika Anda menginginkan semuanya. Wildcard anak (child wildcard) .* cocok dengan setiap anak langsung dan membiarkan segmen yang tersisa menyaring: $data.Receipt.*.Total_Price menjangkau Total_Price di dalam setiap cabang anak dari Receipt tanpa memilih bidang ringkasan dengan nama yang sama yang berada langsung di Receipt itu sendiri. Terakhir, properti parent naik satu tingkat, yang sengaja dipisahkan dari .. oleh XFA 3.3: $data.Receipt.Detail[1].parent.Tax dimulai pada item baris kedua, naik ke Receipt, dan mendarat pada nilai saudara kandungnya Tax
Pemilih kelas dan predikat: #dataGroup, #dataValue, .[ekspresi]
Sintaksis #class mengalamatkan simpul Data DOM berdasarkan kelas objek alih-alih berdasarkan nama, yang merupakan rute praktis ketika tag XML berisi karakter yang sulit ditulis sebagai nama skrip. losLab PDF Library memetakan #dataGroup ke elemen yang memiliki elemen anak dan #dataValue ke elemen daun (leaf) tanpa elemen anak, dan keduanya digabungkan dengan kemunculan numerik atau [*]: $data.Receipt.#dataGroup[0].#dataValue[0] memilih nilai pertama di dalam grup pertama di bawah Receipt. Pemilih predikat .[expression] menyaring saudara kandung (siblings) dengan nama yang sama berdasarkan konten. Predikat yang didukung adalah perbandingan sederhana dari nilai anak terhadap nilai harfiah (literal), dengan operator relasional seperti >, <, dan <=; ketika predikat cocok dengan beberapa baris, pembacaan dikembalikan digabungkan dengan | dan penulisan memperbarui setiap kecocokan
// Tandai setiap baris yang Total_Price-nya melebihi 200
Lib.SetXFAFormFieldValue(
'$data.Receipt.Detail.[Total_Price > 200].Review_Flag', '1');
// Deskripsi semua baris bernilai rendah, digabungkan dengan '|' jika beberapa cocok
Desc := Lib.GetXFAFormFieldValue(
'$data.Receipt.Detail.[Total_Price <= 200].Description');
// Pengalamatan berbasis kelas saat nama tag tidak ramah penulisan skrip
Total := Lib.GetXFAFormFieldValue(
'$data.Receipt.#dataGroup[0].#dataValue[0]');
Fitur XFA SOM mana yang tidak didukung?
Dukungan SOM di losLab PDF Library dibatasi pada kumpulan data (datasets) dan jalur bidang templat, dan batasannya dijelaskan secara eksplisit, bukan berdasarkan upaya terbaik. Mengetahuinya sejak awal akan menghemat waktu debugging jalur yang secara diam-diam mengembalikan string kosong
- Predikat tidak menjalankan FormCalc atau JavaScript sewenang-wenang: tidak ada panggilan fungsi (predikat
contains(...)mengembalikan nilai kosong), tidak ada ekspresi boolean majemuk, hanya perbandinganchild op literaltunggal $recordhanya berfungsi dalam paket non-pemrosesan rekaman; paging rekaman dataWindow dan rotasi grup-rekaman tidak diimplementasikan- Penyelesaian masalah dilakukan secara langsung terhadap Data DOM dan DOM templat; penyelesaian relatif DOM Formulir, yaitu tampilan gabungan yang dibangun viewer saat runtime, tidak dilakukan
- Semantik pemuatan atribut Data DOM tidak diterapkan; pustaka membaca paket dataset sebagai XML mentah, sehingga jalur mengalamatkan elemen, bukan atribut yang dipromosikan menjadi simpul
Batasan ini jarang menyulitkan dalam pekerjaan pengisian massal, karena pengisi mengalamatkan data berdasarkan struktur alih-alih logika skrip. Ketika batasan tersebut menjadi kendala, solusi daruratnya adalah di tingkat paket: baca seluruh XML dataset, transformasikan di Delphi, dan tulis kembali
Menulis seluruh paket dengan SetXFAFromString
SetXFAFromString adalah titik masuk tingkat paket tersebut: fungsi ini memasang string XDP lengkap, templat dan dataset bersama-sama, membuat kontainer AcroForm dalam dokumen baru ketika belum ada, dan menerima paket yang dikodekan UTF-16 dengan tanda urutan byte (BOM) serta UTF-8. Struktur produksi yang umum adalah menyimpan XDP yang diterbitkan instansi sebagai fixture, memuatnya dengan SetXFAFromString, lalu menjalankan panggilan SetXFAFormFieldValue yang beralamat SOM untuk nilai-nilai per faktur yang volatil sebelum disimpan. Karena XFA is salah satu dari dua model formulir yang dapat dibawa oleh PDF, sisi AcroForm klasik memiliki cerita automasinya sendiri, yang dibahas dalam artikel tentang tindakan formulir interaktif dan JavaScript
API nilai bidang, enumerasi, dan paket XFA yang ditampilkan di sini disertakan dalam losLab PDF Library untuk Delphi, C#, dan VB.NET; halaman produk menyediakan referensi penanganan formulir yang lengkap