Tekninen artikkeli

FDF-, XFDF- ja XFA-lomaketietojen vaihto Delphissä

losLab PDF Library siirtää lomaketietoja PDF-tiedostoon ja sieltä pois kolmella eri tavalla: FDF ja XFDF AcroForm-kenttien arvoille, samat kaksi muotoa huomautuskommenteille ja kokonainen XFA XDP -paketti, joka kirjoitetaan suoraan lomakkeeseen. ExportFormDataToXFDF, ImportFormDataFromXFDF, ImportAnnotationsFromFDF, ExportAnnotationsToXFDF ja SetXFAFromString ovat aloituspisteitä, ja jokaisesta niistä on olemassa tiedosto- ja merkkijonovariantti

Syy moniin menetelmiin on se, että PDF-lomakkeen tiedot eivät ole vain yhtä asiaa. Täytetyllä AcroForm-lomakkeella on kenttäarvoja, se voi myös sisältää tarkistuskommentteja, ja vanha XFA-lomake sisältää kokonaisen XML-sovelluskuvauksen, jolla ei ole mitään tekemistä kummankaan edellisen kanssa. losLab PDF Library pitää nämä kolme asiaa erillisissä rajapinnoissa tarkoituksella, koska niiden yhdistäminen pakottaisi väärän tietomallin ainakin kahteen niistä. Päätös siitä, minkä tuoteperheen tarvitset, on ensimmäinen suunnitteluvalinta, ja se ratkeaa yleensä heti, kun tiedät, mitä vastaanottava järjestelmä toisella puolella todellisuudessa käyttää

Mitä eroa on FDF-, XFDF- ja XFA-lomaketiedoilla?

FDF ja XFDF kuljettavat samat tiedot kahdessa eri syntaksissa, ja XFA on aivan oma maailmansa. FDF on PDF-syntaksin mukainen pienoisasiakirja (ISO 32000-2 §12.7.8): /Fields-taulukko, joka koostuu << /T (nimi) /V (arvo) >> -sanakirjoista, joissa merkkijonot on suojattu (escape) täsmälleen samalla tavalla kuin merkkijonoliteraalit PDF-tiedoston sisällä. XFDF on saman tiedon XML-muoto (ISO 19444), <fields>-puu, jota Acrobat ja useimmat lomakkeen taustajärjestelmät lukevat ja kirjoittavat natiivisti. XFA ei ole kumpaakaan: se on XML Forms Architecture -malli ja sen tiedot, jotka on tallennettu XML Data Package (XDP) -muodossa, johon PDF viittaa polusta /AcroForm/XFA. Valitse FDF tai XFDF, when vaihdat kenttien arvoja muiden järjestelmien kanssa, ja turvaudu XFA:han vain silloin, kun ylläpidät asiakirjaa, joka on alun perin luotu XFA-lomakkeeksi

FDF:n ja XFDF:n sisällä losLab PDF Library vetää toisen rajan: kenttien arvot vastaan huomautuskommentit. Lomaketietoperhe (ExportFormDataToFDF, ImportFormDataFromFDF, ExportFormDataToXFDF, ImportFormDataFromXFDF) lukee ja kirjoittaa /Fields-alipuuta eikä koske mihinkään muuhun. Huomautusperhe (ExportAnnotationsToFDF, ImportAnnotationsFromFDF, ExportAnnotationsToXFDF, ImportAnnotationsFromXFDF) lukee ja kirjoittaa sen sijaan /Annots-alipuuta, jota Acrobat kutsuu nimellä 'Vie kommentit' (Export Comments). Nämä kaksi eivät koskaan mene päällekkäin, joten kenttien arvojen vieminen ei kerää mukaan irrallisia tarkistusmerkintöjä, eikä kommenttien tuominen häiritse arvoja, jotka käyttäjä on jo kirjoittanut. Se, mitä ohjausobjekti tekee, kun sitä napsautetaan tai se lasketaan uudelleen, on puolestaan kolmas asia, jota käsitellään oheisessa muistiinpanossa vuorovaikutteisista lomaketoiminnoista ja JavaScriptistä

Miten PDF-lomake täytetään FDF- tai XFDF-tiedostosta Delphissä?

Lataa asiakirja, kutsu yhtä tuontimenetelmää ja tallenna. ImportFormDataFromXFDF ja ImportFormDataFromFDF kukin jäsentävät saapuvan lomaketiedon, täsmäävät jokaisen merkinnän AcroForm-kenttään sen täysin määritellyn nimen (fully qualified name) perusteella, asettavat arvon ja palauttavat todellisuudessa päivitettyjen kenttien määrän. Molemmat menetelmät päivittävät vain kohde-PDF-tiedostossa jo olevia kenttiä; kumpikaan ei luo kenttää nimelle, jota lomake ei määrittele, mikä estää vierasta tai haitallista tietotiedostoa kasvattamasta lomakettasi salaa

var
  Lib: TPDFlib;
  FieldsSet: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('application-blank.pdf', '');
    // Asianhallintajärjestelmän tuottama XFDF, valmiiksi UTF-8-muodossa levyllä
    FieldsSet := Lib.ImportFormDataFromXFDF('applicant-1042.xfdf');
    if FieldsSet > 0 then
      Lib.SaveToFile('application-filled.pdf');
  finally
    Lib.Free;
  end;
end;

Kaksi yksityiskohtaa ratkaisee, selviääkö monimutkaisempi tieto matkasta. Ensimmäinen on suojaus (escaping): FDF tallentaa arvot PDF-merkkijonoliteraaleina, joten arvo, joka sisältää sulkumerkin, kenoviivan tai tulostumattoman tavun, saapuu käärittynä standardin ISO 32000-2 §7.3.4.2 mukaiseen oktaalisuojaukseen, ja losLab PDF Library purkaa tämän suojauksen tuotaessa, jotta sulut ja vinoviivat palautuvat literaalimerkeiksi, jotka käyttäjä kirjoitti. Toinen on koodaus: tiedostopohjaiset menetelmät kirjoittavat ja lukevat XFDF- ja FDF-tiedostoja UTF-8-muodossa, mitä XFDF-määrittely edellyttää ja mitä kaikki muut kuin ASCII-kenttäarvot (esim. painotetut merkit, valuuttasymbolit, CJK-osoitteet) tarvitsevat säilyäkseen muuttumattomina. Jos rakennat XFDF:n itse, määritä UTF-8 ja kirjoita UTF-8, jolloin tuonti toimii oikein

Hierarkkiset kentät, monivalinnat ja rikas teksti

Hierarkkiset kenttien nimet ovat ensimmäinen paikka, jossa yksinkertainen viejä rikkoutuu. AcroForm osoittaa sisäkkäistä kenttää pistevälitteisellä koko nimellä, kuten Applicant.FullName, mutta XFDF ei laita tätä pistemerkein eroteltua merkkijonoa yhteen nimen määritteeseen (name attribute); standardi ISO 19444 asettaa sen sisäkkäin, muodossa <field name="Applicant"><field name="FullName">. losLab PDF Library jakaa pistenimen sisäkkäisiksi <field>-elementeiksi vietäessä ja kokoaa ne takaisin koko nimeksi tuotaessa, jotta molemmat suunnat pysyvät symmetrisinä. Vietäessä se myös ohittaa ei-terminaaliset isäntäkentät, koska AcroFormin isäntäsolmu edustaa vain nimeämistasoa eikä sillä ole omaa arvoa; sen vieminen tuottaisi tyhjän <value></value>-elementin, joka ei vastaa todellista lomaketta. Monivalintaiset luetteloruudut ovat toinen sudenkuoppa: valintakenttä voi sisältää useita valittuja arvoja samanaikaisesti, minkä XFDF ilmaisee toistuvina <value>-elementteinä ja FDF /V-taulukkona, ja losLab PDF Library jakaa kentän useiksi arvoiksi vain silloin, kun kyseessä on aidosti monivalintainen kenttä, jotta tavallinen monirivinen tekstikenttä säilyttää rivinvaihtonsa eikä hajoa virheellisiksi arvoiksi

Rikas teksti (rich text) on kolmas ja se, minkä kehittäjät tekevät useimmiten väärin. Muotoiltu kenttä tallentaa merkintänsä RV-merkintään XHTML-alipuuna, ja XFDF kuljettaa sen elementissä <value-richtext>. losLab PDF Library kirjoittaa tämän alipuun elävänä XML-fragmenttina sen sijaan, että se suojaisi sen merkkijonoksi, jotta jatkotyökalu lukee todellista rikasta tekstiä eikä näkyviä tageja; tuotaessa se säilyttää raa'an RV-alipuun ja soveltaa silti pelkkää tekstiä sisältävän <value>-arvon kenttään V. Jos kenttä tarjoaa molemmat, pelkkä arvo menee kenttään V ja rikas teksti kulkee mukana kohdassa RV, mikä on se yhteensopivuussääntö, joka estää rikkaan tekstin tuontia ylikirjoittamasta hiljaisesti toisen työkalun jo asettamaa arvoa. Kun rikas teksti on olemassa välittämään asiakirjan rakennetta avustavalle teknologialle, käsittele sitä samalla tavalla kuin lukujärjestystä, jota käsitellään muistiinpanossa sivutetuista PDF-tiedostoista ja saavutettavuusrakenteesta

var
  Lib: TPDFlib;
const
  XFDF =
    '<?xml version="1.0" encoding="UTF-8"?>' +
    '<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">' +
    '<fields>' +
    '  <field name="Applicant">' +
    '    <field name="FullName"><value>Alice Example</value></field>' +
    '  </field>' +
    '  <field name="Skills">' +
    '    <value>Delphi</value><value>PDF</value>' +
    '  </field>' +
    '  <field name="Notes">' +
    '    <value>See attachment</value>' +
    '    <value-richtext><body><p>See <b>attachment</b></p></body></value-richtext>' +
    '  </field>' +
    '</fields></xfdf>';
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('intake.pdf', '');
    // Applicant.FullName kootaan uudelleen; Skills täyttää /V- ja /I-taulukot;
    // Notes saa V-arvon <value>-elementistä ja RV-arvon <value-richtext>-elementistä
    Lib.ImportFormDataFromXFDFString(XFDF);
    Lib.SaveToFile('intake-filled.pdf');
  finally
    Lib.Free;
  end;
end;

Miten huomautuskommentit siirretään edestakaisin FDF- tai XFDF-muodossa?

Kommentit siirtyvät huomautustuoteperheen kautta, ja tuettu osajoukko on tarkoituksella pieni. ExportAnnotationsToXFDF ja ImportAnnotationsFromXFDF sekä niiden FDF-vastineet kuljettavat tekstityylisiä merkintöjä kentillä, jotka siirtyvät puhtaasti edestakaisin: huomautuksen alityyppi (subtype), sen suorakulmio (rectangle), sen 0-perusteinen sivun indeksi, kirjoittaja kentässä T, aihe kentässä Subj, pelkkä sisältö Contents ja väri, joka mäppää PDF:n /C DeviceRGB-kolmikon XFDF-attribuutiksi #RRGGBB ja päinvastoin. Tuonti hyväksyy vain tunnetut elementtien nimet ja ohittaa kaikki merkinnät, joiden sivu on alueen ulkopuolella tai joiden suorakulmio puuttuu, jotta käsin muokattu tai vieras XFDF ei voi pudottaa virheellistä huomautusta asiakirjaan. Se, mitä osajoukko ei vielä kuljeta, on syytä sanoa suoraan: rikkaan tekstin sisältö (RC), ponnahdusikkunat (popups), quadpoint-pisteet, musteviivat (ink lists), kärkipisteet (vertices) ja valmiiksi piirretyt ulkoasuvirrat (appearance streams) ovat toistaiseksi soveltamisalan ulkopuolella, joten korostusgeometria ja mukautetut leimat eivät säily tällä polulla. Vahvistaaksesi sen, mikä todella päätyi perille, käy läpi sivun huomautukset tai laajempi elementtipuu, jota käsitellään artikkelissa tekstihaku ja sivuelementtien luettelointi

var
  Src, Dest: TPDFlib;
  Xfdf: WideString;
begin
  Src := TPDFlib.Create;
  Dest := TPDFlib.Create;
  try
    Src.LoadFromFile('reviewed.pdf', '');
    Xfdf := Src.ExportAnnotationsToXFDFString;   // Vain <annots>-osajoukko
 
    Dest.LoadFromFile('clean-copy.pdf', '');     // sivujen on täsmättävä indeksin mukaan
    Dest.ImportAnnotationsFromXFDFString(Xfdf);
    Dest.SaveToFile('clean-copy-commented.pdf');
  finally
    Dest.Free;
    Src.Free;
  end;
end;

Kokonaisen XFA XDP -paketin kirjoittaminen SetXFAFromString-funktiolla

XFA on poikkeustapaus, ja SetXFAFromString on tapa, jolla kirjoitat koko paketin kerralla. Menetelmä ottaa vastaan täydellisen XML Data Package -paketin eli <xdp:xdp>-asiakirjan sen malli- ja tietojoukkopaketteineen, ja tallentaa sen virraksi, johon viitataan polusta /AcroForm/XFA. losLab PDF Library luo AcroForm-säiliön tarvittaessa, kun asiakirjalla ei ole sellaista, joten sinun ei tarvitse lisätä tarpeetonta kenttää vain antaaksesi XFA:lle kiinnityskohdan, ja se hävittää kaiken tallentamansa jäsennellyn XFA-tilan, jotta myöhempi luku heijastaa juuri kirjoittamaasi pakettia vanhentuneen välimuistin sijaan. Koska XFA-paketti on XML:ää eikä välttämättä UTF-8-muodossa, kirjasto tunnistaa UTF-16-tavujärjestysmerkin (BOM) ja purkaa paketin oikein ennen jäsentämistä, mikä on tärkeää työkaluilla tuotetuille paketeille, jotka käyttävät oletuksena UTF-16-koodausta

Kun paketti on paikallaan, GetXFAFormFieldValue ja SetXFAFormFieldValue osoittavat yksittäisiä kenttiä niiden Scripting Object Model (SOM) -polun kautta. losLab PDF Library hyväksyy standardit SOM-juuret sekä paljaat suhteelliset polut, joten form1.FullName, $data.form1.FullName ja xfa.datasets.data.form1.FullName viittaavat kaikki samaan tietosolmuun, ja mallipuoli hyväksyy $template- ja xfa.template-polut samalla tavalla. Tämä joustavuus on tärkeää silloin, kun toinen järjestelmä luo SOM-polut ja tuottaa aina täysin määritellyn muodon

var
  Lib: TPDFlib;
const
  Xdp =
    '<?xml version="1.0" encoding="UTF-8"?>' +
    '<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">' +
    '  <template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">' +
    '    <subform name="form1">' +
    '      <field name="FullName"><ui><textEdit/></ui></field>' +
    '    </subform>' +
    '  </template>' +
    '  <xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">' +
    '    <xfa:data><form1><FullName>Alice Example</FullName></form1></xfa:data>' +
    '  </xfa:datasets>' +
    '</xdp:xdp>';
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetXFAFromString(Xdp, 0);      // luo AcroForm-säiliön, jos sellaista ei ole olemassa
    // lue takaisin data-DOM:in läpi SOM-polun avulla
    if Lib.GetXFAFormFieldValue('form1.FullName') = 'Alice Example' then
      Lib.SetXFAFormFieldValue('form1.FullName', 'Bob Example');
    Lib.SaveToFile('xfa-packet.pdf');
  finally
    Lib.Free;
  end;
end;

Rehellinen yhteenveto on, että kenttien arvot siirtyvät täysin edestakaisin FDF:n ja XFDF:n kautta, mukaan lukien hierarkia, monivalinnat ja rikas teksti; huomautuskommentit siirtyvät edestakaisin rajattuna tekstimerkintöjen osajoukkona selvin puuttein; ja XFA kirjoitetaan ja luetaan kokonaisena pakettina, jonka päällä on kenttäkohtainen SOM-pääsy. Sovita muoto vastaanottimeen, määritä UTF-8 kaikelle itse rakentamallesi ja muista, että tuontimenetelmät koskevat vain sellaisia kenttiä ja huomautuksia, jotka jo sopivat asiakirjaan. Tässä kuvatut lomaketieto-, huomautus- ja XFA-menetelmät ovat osa Delphille ja C++Builderille suunnattua losLab PDF Library -kirjastoa, jonka viiteopas sisältää täydellisen parametriluettelon jokaiselle vienti- ja tuontipisteelle