losLab PDF Library leser og skriver gjentakende datanoder i dynamiske XFA-skjemaer gjennom XFA 3.3 SOM-baneuttrykk: GetXFAFormFieldValue og SetXFAFormFieldValue godtar røttene $data, $record og !data, [*]-forekomstjokere, etterkommer- og barn-jokerselektorer, egenskapen parent, #dataGroup/#dataValue-klasse-selektorer og enkle predikater i FormCalc-stil, slik at et Delphi-program kan oppdatere hver fakturalinje i et offentlig avgiftsskjema med ett enkelt kall. Denne artikkelen er referansen for adressering; for å flytte hele datasett mellom filer, se den tilhørende artikkelen om FDF-, XFDF- og XFA-skjemadatautveksling
Problemet oppstår i det øyeblikket et skjema slutter å være flatt. En mva-melding eller tollerklæring bygd som et dynamisk XFA-skjema har ikke tolv felt kalt Total_Price_1 til Total_Price_12; det har ett enkelt Detail-underskjema deklarert én gang i malens DOM, og instansiert så mange ganger som dataene krever. Verdiene lever i et annet tre, nemlig Data DOM-en inne i datasets-pakken, der hver linje er et gjentatt <Detail>-element under <Receipt>. Flate feltnavn kan ikke adressere "prisen på den tredje linjen" eller "hver linje over 200"; det er akkurat den jobben kapittelet om Scripting Object Model i spesifikasjonen for Adobe XFA 3.3 gir til SOM-uttrykk, og det er den syntaksen som losLab PDF Library-API-ene for XFA-feltverdier snakker
Hvordan adresserer du XFA-datanoder fra Delphi?
Hver datasets-bane starter fra en rot, og losLab PDF Library godtar standard skrivemåter om hverandre: $data er kortformen for xfa.datasets.data i XFA 3.3, !data er kortformen med rot i xfa.datasets, og i pakker som ikke bruker postbehandling løser $record seg opp til den ytre dataposten, det første elementet under datanoden. API-er på malsiden, som for eksempel SetXFAFormFieldAccess, godtar $template og xfa.template på samme måte. Segmenter kan være punktumdelte eller skråstrekdelte ($data.Receipt.Tax eller $data/Receipt/Tax), som har betydning fordi GetXFAFormFieldNames lister opp felt som skråstrekdelte baner som kan sendes direkte tilbake til verdi- og mal-API-ene. Forekomstindekser er nullbaserte i henhold til spesifikasjonen, så Detail[0] is den første raden. To regler for unnslipping (escaping) gjør at uvanlige navn forblir adresserbare: i en punktumdelt bane betegner \. et bokstavelig punktum (Line\.Item), mens skråstrekdelte baner behandler punktum som vanlige tegn. Forretningsdata som bærer sitt eget XML-navneromsprefiks, matches med lokalt navn, så <m:Receipt> vil fremdeles svare på 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;
Hvordan fyller du gjentakende rader i et XFA-skjema programmatisk?
Forekomstjokeren [*] er batch-verktøyet. Der en numerisk indeks velger ett enkelt søsken, velger [*] alle søsken med samme navn, slik at $data.Receipt.Detail[*].Total_Price adresserer prisfeltet på hver eneste linje samtidig. Ved lesing returnerer GetXFAFormFieldValue verdiene til alle samsvarende noder slått sammen med en |-separator; ved skriving oppdaterer SetXFAFormFieldValue hver samsvarende node med samme verdi og returnerer 1 ved suksess. En bane som ikke matcher noe, leses tilbake som en tom streng, som er en enkel måte å undersøke om en gren eksisterer før man skriver til den
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;
Etterkommer-, barn-joker- og foreldreselektorer
Tre strukturelle selektorer dekker tilfellene der du kjenner feltnavnet, men ikke dets nøyaktige dybde. Etterkommer-selektoren .. matcher på enhver dybde under den gjeldende noden: $data..Total_Price finner den første Total_Price hvor som helst under dataroten, og en skriving via en etterkommerbane oppdaterer den første samsvarende noden. Merk begrensningen: en forekomstindeks etter et etterkommertreff går ikke på tvers av søskengrener, så hvis $data..Total_Price[0] løser seg opp inne i den første Detail-raden, returnerer $data..Total_Price[1] tomt i stedet for å hoppe til neste rad; bruk Detail[*] når du vil ha alle. Barn-jokeren .* matcher hvert direkte barn og lar de gjenværende segmentene filtrere: $data.Receipt.*.Total_Price når Total_Price inni hver barnegren av Receipt uten å også velge et oppsummeringsfelt med samme navn som ligger direkte på selve Receipt. Til slutt klatrer egenskapen parent opp ett nivå, noe XFA 3.3 bevisst skiller fra ..: $data.Receipt.Detail[1].parent.Tax starter på den andre linjen, går opp to Receipt og ender på dens søskenverdi Tax
Klasseselektorer og predikater: #dataGroup, #dataValue, .[uttrykk]
Syntaksen #class adresserer Data DOM-noder etter objektklasse i stedet for etter navn, noe som er den praktiske veien når XML tagger inneholder tegn som er vanskelige å skrive som skriptnavn. losLab PDF Library kartlegger #dataGroup til elementer som har barneelementer, og #dataValue til bladelementer uten barneelementer, og begge kan kombineres med numeriske eller [*]-forekomster: $data.Receipt.#dataGroup[0].#dataValue[0] velger den første verdien inni den første gruppen under Receipt. Predikatselektoren .[uttrykk] filtrerer søsken med samme navn etter innhold. Støttede predikater er enkle sammenligninger av en barneverdi mot en bokstavelig verdi (literal), med relasjonelle operatorer som >, < og <=; når et predikat matcher flere rader, kommer lesinger tilbake slått sammen med | og skrivinger oppdaterer hvert treff
// 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]');
Hvilke XFA SOM-funksjoner støttes ikke?
SOM-støtten i losLab PDF Library er begrenset til datasets- og malfeltbaner, og grensene er eksplisitte fremfor at det gjøres et best-effort-forsøk. Å vite om dem på forhånd sparer deg for feilsøking av en bane som lydløst returnerer en tom streng
- Predikater utfører ikke vilkårlig FormCalc eller JavaScript: ingen funksjonskall (et
contains(...)-predikat returnerer tomt), ingen sammensatte boolske uttrykk, kun en enkelt sammenligning av typenchild op literal $recordfungerer kun i pakker uten postbehandling; dataWindow-post-paginering og rotasjon av postgrupper er ikke implementert- Oppløsning skjer direkte mot Data DOM og mal-DOM; Form DOM relativ oppløsning, den sammenslåtte visningen en viser bygger ved kjøring, utføres ikke
- Data DOM-attributtinnlastingssemantikk brukes ikke; biblioteket leser datasets-pakken som rå XML, så baner adresserer elementer, ikke attributt-noder
Disse grensene er sjelden et problem i batch-utfyllingsarbeid, fordi en utfyller adresserer data etter struktur fremfor skriptet logikk. Når de gjør det, er rømningsveien på pakkenivå: les hele datasets-XML-en, transformer den i Delphi, og skriv den tilbake
Skrive hele pakken med SetXFAFromString
SetXFAFromString er dette inngangspunktet på pakkenivå: det installerer en komplett XDP-streng, mal og datasets sammen, og oppretter AcroForm-beholderen i et nytt dokument når ingen eksisterer ennå, og det godtar UTF-16-kodede pakker med byte-rekkefølge-merker (BOM) så vel som UTF-8. En vanlig produksjonsform er å beholde den offentlig utstedte XDP-en som en fast ressurs, laste den med SetXFAFromString, og deretter kjøre SOM-adresserte SetXFAFormFieldValue-kall for de flyktige verdiene per faktura før lagring. Siden XFA er en av to skjemamodeller en PDF kan bære, har den klassiske AcroForm-siden sin egen automatiseringshistorie, som dekkes i artikkelen om interaktive skjemahandlinger og JavaScript
API-ene for XFA-feltverdier, oppregning og pakker som vises her, leveres i losLab PDF Library for Delphi, C# og VB.NET; produktsiden inneholder den fullstendige referansen for skjemahåndtering