Knjižnica losLab PDF Library bere in piše ponavljajoča se podatkovna vozlišča v dinamičnih obrazcih XFA prek izrazov poti XFA 3.3 SOM: metodi GetXFAFormFieldValue in SetXFAFormFieldValue sprejemata korene $data, $record in !data, nadomestne znake za pojavitve [*], selektorje potomcev in nadomestne znake za otroke, lastnost parent, selektorje razredov #dataGroup/#dataValue ter preproste predikate v slogu FormCalc, tako da lahko program v Delphiju z enim klicem posodobi vsako vrstico računa v državnem davčnem obrazcu. Ta članek je referenca za naslavljanje; za premikanje celotnih podatkovnih nizov med datotekami si oglejte spremljajoči članek o izmenjavi podatkov obrazcev FDF, XFDF in XFA
Težava se pojavi tisti trenutek, ko obrazec preneha biti raven. Obračun DDV ali carinska deklaracija, zgrajena kot dinamični obrazec XFA, nima dvanajstih polj z imeni od Total_Price_1 do Total_Price_12; ima en podobrazec (subform) Detail, ki je enkrat deklariran v predlogi DOM in instanciran tolikokrat, kolikor zahtevajo podatki. Vrednosti živijo v drugem drevesu, Data DOM znotraj paketa datasets, kjer je vsaka postavka ponavljajoči se element <Detail> pod <Receipt>. Ravna imena polj ne morejo nasloviti "cene v tretji vrstici" ali "vsake vrstice nad 200"; to je natanko naloga, ki jo poglavje o Scripting Object Model specifikacije Adobe XFA 3.3 dodeljuje izrazom SOM, in to je sintaksa, ki jo govorijo API-ji za vrednosti polj XFA v knjižnici losLab PDF Library
Kako nasloviti podatkovna vozlišča XFA iz Delphija?
Vsaka pot datasets se začne pri korenu (root) in knjižnica losLab PDF Library sprejema standardne zapise izmenično: $data je kratka oblika XFA 3.3 za xfa.datasets.data, !data je kratka oblika s korenino pri xfa.datasets, v paketih, ki ne uporabljajo obdelave zapisov, pa se $record razreši v zunanji podatkovni zapis (record), prvi element pod podatkovnim vozliščem. API-ji na strani predloge, kot je SetXFAFormFieldAccess, na enak način sprejemajo $template in xfa.template. Segmenti so lahko ločeni s piko ali poševnico ($data.Receipt.Tax ali $data/Receipt/Tax), kajti metoda GetXFAFormFieldNames našteva polja kot poti, ločene s poševnico, ki se neposredno prenašajo nazaj v API-je za vrednosti in predloge. Indeksi pojavitev so po specifikaciji 0-bazirani, tako da je Detail[0] prva vrstica. Dve pravili za izogibanje (escaping) ohranjata nenavadna imena naslovljiva: v poti, ločeni s pikami, \. označuje dobesedno piko (Line\.Item), medtem ko poti, ločene s poševnicami, obravnavajo pike kot običajne znake. Poslovni podatki, ki nosijo svojo predpono imenskega prostora XML, se ujemajo po lokalnem imenu, tako da se <m:Receipt> še vedno odziva na Receipt
var
Lib: TPDFlib;
Tax: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('vat-return.pdf', '');
// Enakovredni naslovi za isto podatkovno vozlišče
Tax := Lib.GetXFAFormFieldValue('Receipt.Tax');
Tax := Lib.GetXFAFormFieldValue('$data.Receipt.Tax');
Tax := Lib.GetXFAFormFieldValue('xfa.datasets.data.Receipt.Tax');
Tax := Lib.GetXFAFormFieldValue('$record.Tax');
// Oblika s poševnico z 0-baziranim indeksom pojavitve
Lib.SetXFAFormFieldValue('$record/Detail[0]/Total_Price', '251.00');
Lib.SaveToFile('vat-return-updated.pdf');
finally
Lib.Free;
end;
end;
Kako programsko napolniti ponavljajoče se vrstice v obrazcu XFA?
Nadomestni znak za pojavitve [*] je orodje za množično delo. Kjer številčni indeks izbere enega sorodnika (sibling), [*] izbere vse sorodnike z istim imenom, tako da pot $data.Receipt.Detail[*].Total_Price naslovi polje s ceno na vsaki postavki hkrati. Pri branju metoda GetXFAFormFieldValue vrne vrednosti vseh ujemajočih se vozlišč, združenih z ločilom |; pri pisanju pa SetXFAFormFieldValue posodobi vsako ujemajoče se vozlišče z isto vrednostjo in vrne 1 ob uspehu. Pot, ki se ne ujema z ničemer, se prebere kot prazen niz, kar je enostaven način za preverjanje, ali veja obstaja, preden vanjo vpišemo podatke
var
Lib: TPDFlib;
Prices: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('invoice.pdf', '');
// Vse vrstice Detail hkrati: '250.00|60.00'
Prices := Lib.GetXFAFormFieldValue('$data.Receipt.Detail[*].Total_Price');
// Ponastavitev enega stolpca v vsaki ponavljajoči se vrstici v enem klicu
Lib.SetXFAFormFieldValue('$data.Receipt.Detail[*].Surcharge', '0.00');
Lib.SaveToFile('invoice-updated.pdf');
finally
Lib.Free;
end;
end;
Selektorji potomcev, nadomestni znaki za otroke in starševski selektorji
Trije strukturni selektorji pokrivajo primere, ko poznate ime polja, ne pa njegove natančne globine. Selektor potomcev .. se ujema na kateri koli globini pod trenutnim vozliščem: $data..Total_Price najde prvi Total_Price kjer koli pod podatkovno korenino, pisanje prek poti potomca pa posodobi to prvo ujemajoče se vozlišče. Upoštevajte omejitev: indeks pojavitve po ujemu potomca ne prestopi čez sorodne veje, tako da če se $data..Total_Price[0] razreši znotraj prve vrstice Detail, $data..Total_Price[1] vrne prazno vrednost, namesto da bi skočil na naslednjo vrstico; uporabite Detail[*], ko želite vse. Nadomestni znak za otroke .* se ujema z vsakim neposrednim otrokom in omogoča filtriranje preostalih segmentov: $data.Receipt.*.Total_Price doseže Total_Price znotraj vsake otroške veje Receipt, ne da bi hkrati izbral povzetkovno polje z istim imenom, ki se nahaja neposredno na samem Receipt. Nazadnje, lastnost parent se povzpne za eno raven višje, kar specifikacija XFA 3.3 namerno ločuje od ..: pot $data.Receipt.Detail[1].parent.Tax se začne pri drugi postavki, se dvigne do Receipt in pristane na njegovi sorodni vrednosti Tax
Selektorji razredov in predikati: #dataGroup, #dataValue, .[expression]
Sintaksa #class naslavlja vozlišča Data DOM po razredu objekta in ne po imenu, kar je praktična pot, ko oznake XML vsebujejo znake, ki jih je težko zapisati kot imena v skriptih. Knjižnica losLab PDF Library preslika #dataGroup v elemente, ki imajo otroške elemente, in #dataValue v končne elemente (leaf) brez otroških elementov, oba pa se kombinirata s številčnimi pojavitvami ali pojavitvami [*]: $data.Receipt.#dataGroup[0].#dataValue[0] izbere prvo vrednost znotraj prve skupine pod Receipt. Predikatski selektor .[expression] filtrira sorodnike z istim imenom po vsebini. Podprti predikati so preproste primerjave vrednosti otroka z literalom z relacijskimi operaterji, kot so >, < in <=; ko se predikat ujema z več vrsticami, se branju vrnejo vrednosti, združene z ločilom |, pisanja pa posodobijo vsako ujemanje
// Označitev vsake vrstice, katere Total_Price presega 200
Lib.SetXFAFormFieldValue(
'$data.Receipt.Detail.[Total_Price > 200].Review_Flag', '1');
// Opisi vseh vrstic z nizko vrednostjo, združeni z '|', ko se jih ujema več
Desc := Lib.GetXFAFormFieldValue(
'$data.Receipt.Detail.[Total_Price <= 200].Description');
// Naslavljanje na podlagi razreda, ko se imena oznak upirajo črkovanju v skriptu
Total := Lib.GetXFAFormFieldValue(
'$data.Receipt.#dataGroup[0].#dataValue[0]');
Katere funkcije XFA SOM niso podprte?
Podpora za SOM v knjižnici losLab PDF Library je omejena na poti datasets in polja predlog, meje pa so eksplicitne in ne le po principu najboljšega truda. Poznavanje teh vnaprej prihrani odpravljanje napak na poti, ki tiho vrne prazen niz
- Predikati ne izvajajo poljubne kode FormCalc ali JavaScript: ni klicov funkcij (predikat
contains(...)vrne prazno vrednost), ni sestavljenih logičnih izrazov, temveč le ena primerjavachild op literal - Koren
$recorddeluje le v paketih, ki ne uporabljajo obdelave zapisov; stranjevanje zapisov dataWindow in rotacija skupin zapisov nista implementirana - Razreševanje poteka neposredno glede na Data DOM in template DOM; relativno razreševanje Form DOM, ki predstavlja združeni pogled, ki ga pregledovalnik zgradi ob zagonu, se ne izvaja
- Semantika nalaganja atributov Data DOM se ne uporablja; knjižnica bere paket datasets kot surov XML, zato poti naslavljajo elemente in ne atributov, povišanih v vozlišča
Te omejitve le redko ovirajo množično izpolnjevanje podatkov, saj izpolnjevalnik naslavlja podatke po strukturi in ne po skriptni logiki. Ko pa se to zgodi, je zasilni izhod na ravni paketa: preberite celoten XML datasets, ga preoblikujte v Delphiju in zapišite nazaj
Zapisovanje celotnega paketa z metodo SetXFAFromString
Metoda SetXFAFromString je ta vstopna točka na ravni paketa: namesti celoten niz XDP, predlogo in datasets skupaj, s čimer ustvari vsebnik AcroForm v novem dokumentu, ko ta še ne obstaja, ter sprejema pakete, kodirane v UTF-16 z oznakami vrstnega reda bajtov (BOM), kot tudi v UTF-8. Pogost proizvodni pristop je ohranjanje uradno izdanega XDP kot stalne komponente, njegovo nalaganje z SetXFAFromString, nato pa izvajanje klicov SetXFAFormFieldValue z naslovi SOM za nestanovitne posamezne vrednosti računov pred shranjevanjem. Ker je XFA eden izmed dveh modelov obrazcev, ki jih lahko prenaša PDF, ima klasična stran AcroForm svojo lastno zgodbo o avtomatizaciji, ki jo pokriva članek o interaktivnih akcijah obrazcev in JavaScriptu
API-ji za vrednosti polj XFA, naštevanje in pakete, prikazani tukaj, se prinašajo v knjižnici losLab PDF Library za Delphi, C# in VB.NET; stran izdelka vsebuje celotno referenco za delo z obrazci