„losLab PDF Library“ skaito ir rašo pasikartojančius duomenų mazgus dinaminėse XFA formose per XFA 3.3 SOM kelio išraiškas: „GetXFAFormFieldValue“ ir „SetXFAFormFieldValue“ priima „$data“, „$record“ ir „!data“ šaknis, „[*]“ pasikartojimo pakaitos simbolius, palikuonių (descendant) ir vaikų pakaitos (child-wildcard) selektorius, savybę „parent“, „#dataGroup“/„#dataValue“ klasių selektorius ir paprastus „FormCalc“ stiliaus predikatus, todėl Delphi programa gali atnaujinti kiekvieną sąskaitos faktūros eilutę valstybinėje mokesčių formoje vienu iškvietimu. Šis straipsnis yra adresavimo vadovas; norėdami perkelti ištisus duomenų rinkinius tarp failų, skaitykite straipsnį apie FDF, XFDF ir XFA formų duomenų mainus
Problema iškyla tuo momentu, kai forma nustoja būti plokščia. PVM deklaracijoje ar muitinės deklaracijoje, sukurtoje kaip dinaminė XFA forma, nėra dvylikos laukų, pavadintų nuo „Total_Price_1“ iki „Total_Price_12“; joje yra viena subforma „Detail“, vieną kartą deklaruota šablono DOM ir sukurta tiek kartų, kiek reikalauja duomenys. Reikšmės gyvena antrame medyje — duomenų DOM (Data DOM) duomenų rinkinių pakete, kur kiekviena eilutė yra pasikartojantis „<Detail>“ elementas po „<Receipt>“. Plokšti laukų pavadinimai negali nurodyti „kainos trečiojoje eilutėje“ arba „kiekvienos eilutės virš 200“; tai yra būtent tas darbas, kurį „Adobe XFA 3.3“ specifikacijos „Scripting Object Model“ skyrius skiria SOM išraiškoms, ir tai yra sintaksė, kuria kalba „losLab PDF Library“ XFA laukų reikšmių API
Kaip pasiekti XFA duomenų mazgus iš Delphi?
Kiekvienas duomenų rinkinio kelias prasideda nuo šaknies, o „losLab PDF Library“ pakaitomis priima standartinę rašybą: „$data“ yra trumpoji XFA 3.3 forma iš „xfa.datasets.data“, „!data“ yra trumpoji forma su šaknimi ties „xfa.datasets“, o paketuose, nenaudojančiuose įrašų apdorojimo, „$record“ nurodo išorinį duomenų įrašą — pirmąjį elementą po duomenų mazgu. Šablono pusės API, tokios kaip „SetXFAFormFieldAccess“, taip pat priima „$template“ ir „xfa.template“. Segmentai gali būti atskirti taškais arba pasvirusiais brūkšniais („$data.Receipt.Tax“ arba „$data/Receipt/Tax“), kas yra svarbu, nes „GetXFAFormFieldNames“ išvardija laukus kaip keliais su pasvirusiais brūkšniais, kurie tiesiogiai perduodami atgal į reikšmių ir šablonų API. Pasikartojimo indeksai pagal specifikaciją prasideda nuo nulio, todėl „Detail[0]“ yra pirmoji eilutė. Dvi pabėgimo (escaping) taisyklės leidžia pasiekti neįprastus pavadinimus: taškais atskirtame kelyje „\.“ žymi tašką kaip tekstą („Line\.Item“), o pasvirusiais brūkšniais atskirtuose keliuose taškai laikomi įprastais simboliais. Verslo duomenys, turintys savo XML vardų srities priešdėlį, yra suderinami pagal vietinį pavadinimą, todėl „<m:Receipt>“ vis tiek atitinka „Receipt“
var
Lib: TPDFlib;
Tax: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('vat-return.pdf', '');
// Equivalent addresses for the same data node
Tax := Lib.GetXFAFormFieldValue('Receipt.Tax');
Tax := Lib.GetXFAFormFieldValue('$data.Receipt.Tax');
Tax := Lib.GetXFAFormFieldValue('xfa.datasets.data.Receipt.Tax');
Tax := Lib.GetXFAFormFieldValue('$record.Tax');
// Slash form with a zero-based occurrence index
Lib.SetXFAFormFieldValue('$record/Detail[0]/Total_Price', '251.00');
Lib.SaveToFile('vat-return-updated.pdf');
finally
Lib.Free;
end;
end;
Kaip programiškai užpildyti besikartojančias eilutes XFA formoje?
Pakaitos simbolis „[*]“ yra grupavimo įrankis. Kai skaitinis indeksas pasirenka vieną elementą, „[*]“ pasirenka visus to paties pavadinimo elementus, todėl „$data.Receipt.Detail[*].Total_Price“ vienu metu nurodo kainos lauką kiekvienoje eilutėje. Skaitant „GetXFAFormFieldValue“ grąžina visų atitinkančių mazgų reikšmes, sujungtas su „|“ skirtuku; rašant „SetXFAFormFieldValue“ atnaujina kiekvieną atitinkamą mazgą ta pačia reikšme ir sėkmės atveju grąžina 1. Kelias, kuris nieko neatitinka, nuskaitomas kaip tuščia eilutė — tai paprastas būdas patikrinti, ar šaka egzistuoja, prieš rašant į ją
var
Lib: TPDFlib;
Prices: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('invoice.pdf', '');
// All Detail rows at once: '250.00|60.00'
Prices := Lib.GetXFAFormFieldValue('$data.Receipt.Detail[*].Total_Price');
// Reset one column across every repeated row in a single call
Lib.SetXFAFormFieldValue('$data.Receipt.Detail[*].Surcharge', '0.00');
Lib.SaveToFile('invoice-updated.pdf');
finally
Lib.Free;
end;
end;
Palikuonių, vaikų pakaitos ir tėvinių elementų selektoriai
Trys struktūriniai selektoriai apima atvejus, kai žinote lauko pavadinimą, bet ne tikslų jo gylį. Palikuonių (descendant) selektorius „..“ atitinka bet kokiame gylyje po esamu mazgu: „$data..Total_Price“ suranda pirmąjį „Total_Price“ bet kur po duomenų šaknimi, o rašymas per palikuonių kelią atnaujina tą pirmąjį atitinkamą mazgą. Atkreipkite dėmesį į ribą: pasikartojimo indeksas po palikuonių atitikmens neperžengia broliškų šakų ribų, todėl jei „$data..Total_Price[0]“ išsprendžiamas pirmosios „Detail“ eilutės viduje, „$data..Total_Price[1]“ grąžina tuščią reikšmę, užuot peršokęs į kitą eilutę; naudokite „Detail[*]“, kai norite visų jų. Vaikų pakaitos (child-wildcard) selektorius „.*“ atitinka kiekvieną tiesioginį vaiką ir leidžia likusiems segmentams filtruoti: „$data.Receipt.*.Total_Price“ pasiekia „Total_Price“ kiekvienoje vaikų šakoje po „Receipt“, nepasirinkdamas to paties pavadinimo santraukos lauko, esančio tiesiogiai pačiame „Receipt“. Galiausiai savybė „parent“ pakyla vienu lygiu aukštyn, ką XFA 3.3 sąmoningai atskiria nuo „..“: „$data.Receipt.Detail[1].parent.Tax“ pradeda nuo antrosios eilutės, pakyla iki „Receipt“ ir patenka ant jo brolinės reikšmės „Tax“
Klasių selektoriai ir predikatai: #dataGroup, #dataValue, .[išraiška]
Sintaksė „#class“ pasiekia Data DOM mazgus pagal objekto klasę, o ne pagal pavadinimą — tai praktiškas kelias, kai XML gairėse (tags) yra simbolių, kuriuos nepatogu rašyti kaip skripto pavadinimus. „losLab PDF Library“ susieja „#dataGroup“ su elementais, turinčiais vaikų, o „#dataValue“ — su galiniais elementais be vaikų, ir abu dera su skaitiniais arba „[*]“ pasikartojimais: „$data.Receipt.#dataGroup[0].#dataValue[0]“ pasirenka pirmąją reikšmę pirmosios grupės viduje po „Receipt“. Predikato selektorius „.[išraiška]“ filtruoja to paties pavadinimo broliškus elementus pagal turinį. Palaikomi predikatai yra paprasti vaiko reikšmės palyginimai su tekstu, naudojant tokius santykio operatorius kaip „>“, „<“ ir „<=“; kai predikatas atitinka kelias eilutes, skaitant grąžinamos „|“ sujungtos reikšmės, o rašant atnaujinamas kiekvienas atitikmuo
// Flag every line whose Total_Price exceeds 200
Lib.SetXFAFormFieldValue(
'$data.Receipt.Detail.[Total_Price > 200].Review_Flag', '1');
// Descriptions of all low-value lines, '|'-joined when several match
Desc := Lib.GetXFAFormFieldValue(
'$data.Receipt.Detail.[Total_Price <= 200].Description');
// Class-based addressing when tag names resist script spelling
Total := Lib.GetXFAFormFieldValue(
'$data.Receipt.#dataGroup[0].#dataValue[0]');
Kurios XFA SOM funkcijos nepalaikomos?
SOM palaikymas „losLab PDF Library“ yra apribotas iki duomenų rinkinių ir šablonų laukų kelių, o ribos yra aiškios, o ne paremtos geriausiomis pastangomis. Šių ribų žinojimas iš anksto sutaupo laiko derinant kelią, kuris tyliai grąžina tuščią eilutę
- Predikatai nevykdo bet kokio „FormCalc“ ar „JavaScript“ kodo: jokių funkcijų iškvietimų (predikatas „contains(...)“ grąžina tuščią reikšmę), jokių sudėtinių loginių išraiškų — palaikomas tik vienas „child op literal“ palyginimas
- „$record“ veikia tik paketuose, kuriuose nenaudojamas įrašų apdorojimas; „dataWindow“ įrašų puslapiavimas ir įrašų grupių sukimasis nėra realizuoti
- Išsprendimas vyksta tiesiogiai pagal duomenų DOM (Data DOM) ir šablono DOM; santykinis „Form DOM“ išsprendimas — bendras vaizdas, kurį peržiūros programa sukuria vykdymo metu — nėra atliekamas
- Duomenų DOM atributų įkėlimo semantika netaikoma; biblioteka skaito duomenų rinkinių paketą kaip gryną XML, todėl keliai nurodo elementus, o ne atributus, paverstus mazgais
Šios ribos retai trukdo atliekant grupinį užpildymą, nes užpildytojas pasiekia duomenis pagal struktūrą, o ne pagal skripto logiką. Kai jos trukdo, išeitis yra paketo lygis: perskaitykite visą duomenų rinkinių XML, transformuokite jį Delphi aplinkoje ir įrašykite atgal
Viso paketo įrašymas naudojant SetXFAFromString
„SetXFAFromString“ yra tas paketo lygio pradžios taškas: ši funkcija įdiegia pilną XDP eilutę (šabloną ir duomenų rinkinius kartu), sukurdama „AcroForm“ konteinerį naujame dokumente, jei jo dar nėra, ir priima tiek UTF-16 koduotus paketus su baitų tvarkos žymomis (BOM), tiek UTF-8. Įprastas gamybinis šablonas yra išlaikyti agentūros išduotą XDP kaip nekintamą dalį, įkelti jį su „SetXFAFromString“, o prieš išsaugojant atlikti „SetXFAFormFieldValue“ iškvietimus su SOM adresais kintamoms sąskaitų reikšmėms. Kadangi XFA is vienas iš dviejų formų modelių, kuriuos gali turėti PDF failas, klasikinė „AcroForm“ pusė turi savo automatizavimo istoriją, aprašytą straipsnyje apie interaktyvius formos veiksmus ir „JavaScript“
Čia parodyti XFA lauko reikšmių, išvardijimo ir paketų API tiekiama standartinėje „losLab PDF Library“ Delphi, C# ir VB.NET aplinkoms; produkto puslapyje pateikiama visa formų tvarkymo dokumentacija