Tekninen artikkeli

PDFium XFA -tallennus: rivinvaihdot, emojit ja restoreState

PDFium Component tallentaa muokatut XFA-lomakkeiden arvot täsmälleen, tallennuksen ja uudelleenavauksen yli, kun se ajaa Windowsin V8-ajonaikaisen pdfium.v8.dllin, joka toimitettiin versiossa v3.125.2 tai uudempana. Vanhemmat ajonaikaiset lisäsivät rivinvaihtoja kenttien arvoihin, katkoivat emojit toisiinsa liittymättömäksi BMP-merkiksi, ohittivat hiljaa yksistreamiset XFA-tallennukset ja saattoivat niellä epäonnistuneen lopullisen kirjoituksen. Yksi uudelleenavauksen oire ei ole lainkaan kirjaston vika: dynaaminen lomake, jonka juurialilomakkeelta puuttuu restoreState="auto", rakentaa layoutinsa mallipohjasta

Tämän bugiraportit näyttivät kaikki samalta. Asiakas täyttää XFA-korvauslomakkeen Delphi-katselimessa, tallentaa, avaa uudelleen, ja jotain on hieman pielessä. Tyhjä kommenttiruutu pitää nyt tyhjää riviä, ja toisen tallennuksen jälkeen se pitää kahta. Emojilla kirjoitettu nimi palaa yksityiskäytön glyyfin kanssa. Kukaan ei saa virhettä, mikä tekee näistä vioista kalliita: ajautuminen näkyy viikkoja myöhemmin jonkun muun viennissä

Mitä menee pieleen, kun XFA-lomake tallennetaan ja avataan uudelleen?

Neljä erillistä vikaa natiivissa XFA-tallennuspolussa aiheuttivat arvojen ajautumisen, ja jokainen piiloutui onnistuneen näyttävän tallennuksen taakse. Kaksi tuli sarjallistuksesta, yksi yksistreamisestä tallennusasettelusta ja yksi PDF-kirjoittajasta itsestään. Taulukko kartoittaa jokaisen oireen syyhynsä ja julkaisuun, jossa PDFium Component korjasi sen

Oire uudelleenavauksen jälkeenSyyKorjattu versiossa
Tyhjä kenttä pitää rivinvaihtoa; arvot kasvavat rivinvaihdon tallennusta kohdenMolemmat XFA-kirjoittajat lisäsivät layout-rivinvaihtoja aloitustägien jälkeenv3.125.2, pdfium.v8.dll
U+1F642 palaa muodossa U+F642, tai emoji katoaa lomakepaketista16-bittinen wchar_t-typistys dekoodauksessa; surrogate-suodatus lomakesarjallistajassav3.125.2, pdfium.v8.dll
Muokkaukset yksistreamisessä XFA-asiakirjassa ovat vain poissaNatiivi tallennus torjui streamin asettelun, mutta paluuarvoa ei huomioituv3.125.2; kommentit ja käsittelyohjeet säilytetty versiosta v3.126.0 alkaen
Typistetty tiedosto, vaikka tallennus ilmoitti onnistuneenLopullinen puskuroitu kirjoitus epäonnistui sen jälkeen, kun kirjoittaja oli jo palauttanut onnistumisenv3.125.2 V8-ajonaikainen; v3.125.3 tavallinen pdfium.dll
Kolmisivuinen dynaaminen lomake avautuu uudelleen kahdella sivullaJuurialilomake ei pyydä restoreState="auto"aLomakkeen laadinta, ei kirjaston vika

Aiemmat kirjoitukset päättelivät, ettei XFA-kenttien muokkauksia voinut säilyttää PDFiumilla lainkaan, mikä oli tarkkaa silloisten ajonaikaisten osalta. Uudempi V8-ajonaikainen tallentaa XFA-arvot natiivisti, joten elävässä lomakkeessa tehty muokkaus saavuttaa tallennetun datasets-paketin ilman pakettileikkausta puolestasi

Mikä PDFium-ajonaikainen tallentaa XFA-arvot?

XFA-tallennuksen uskollisuus riippuu natiivista DLL:stä, ei Delphi-kääreestä, joten ensimmäinen tarkistus on se, minkä ajonaikaisen prosessisi oikeasti latasi. PDFium Component toimittaa kaksi Windows-koontiversiota arkkitehtuuria kohden: tavallisen pdfium.dllin, joka on rakennettu ilman V8:aa ja XFA:aa, ja pdfium.v8.dllin, joka kantaa JavaScript-moottorin ja XFA-lomakeajonaikaisen. Vain pdfium.v8.dll voi ajaa XFA-lomaketta, joten jokainen tässä kuvattu XFA-korjaus asuu siellä, alkaen v3.125.2:n uudelleenrakennetuista Win32- ja Win64-V8-kirjastoista

Lopullisen kirjoituksen korjaus on geneeristä PDF-kirjoittajakoodia, joten sillä on merkitystä myös tavallisille asiakirjoille. v3.125.3 rakensi tavalliset pdfium.dll-kirjastot uudelleen kantamaan saman korjauksen. Jaettu lähdekoodi ei ole todiste jaetusta käytöksestä: kunnes binääri on rakennettu uudelleen, vanha DLL pitää vanhan vikan

Toinen ansa istui lataajassa. Ennen versiota v3.125.2 kohteen EnableV8Engine asettaminen arvoon True sai sidonnan poimimaan oletusarvoisen pdfium.v8.dll-nimen ja ohittamaan täyden polun kohteessa LibraryName. Sovellus, joka osoitti tuoreesti käyttöönotettuun ajonaikaiseen, saattoi yhä ladata vanhemman kopion toisesta kansiosta. Versiosta v3.125.2 alkaen hakemiston sisältävä LibraryName valitsee täsmälleen kyseisen tiedoston kummassakin moottoritilassa, ja puuttuva polku epäonnistuu toiseen mukana toimitettuun kirjastoon palaamisen sijaan

uses
  System.SysUtils, PDFium;

procedure SelectXfaRuntime;
begin
  // Hakemisto kohteessa LibraryName kiinnittää täsmälleen tämän tiedoston (v3.125.2 ja uudemmat);
  // jos tiedosto puuttuu, lataus nostaa poikkeuksen toiseen palaamisen sijaan
{$IFDEF WIN64}
  PDFium.LibraryName := ExtractFilePath(ParamStr(0)) + 'DLLs\Win64\pdfium.v8.dll';
{$ELSE}
  PDFium.LibraryName := ExtractFilePath(ParamStr(0)) + 'DLLs\Win32\pdfium.v8.dll';
{$ENDIF}
  PDFium.EnableV8Engine := True;
  PDFium.LoadLibrary;  // epäonnistu käynnistyksessä, ei ensimmäisessä tallennuksessa
end;

Asiakirjan avaamisen jälkeen TPdf.XFA kertoo, että tiedosto sisältää XFA:n, ja TPdf.XfaRuntimeAvailable kertoo, että ladattu DLL voi oikeasti suorittaa sen. Jos sinun on myös erotettava staattiset ja dynaamiset lomakkeet, TPdf.FormType palauttaa arvon ftXfaFull tai ftXfaForeground; artikkeli XFA-lomakkeiden tunnistaminen ja XFA-pakettien poiminta Delphissä kattaa kyseisen tutkinnan yksityiskohtaisesti

Miksi tallennetut XFA-kentät saavat ylimääräisiä rivinvaihtoja?

Tallennetut XFA-kentät saivat rivinvaihtoja, koska molemmat natiivit XFA-kirjoittajat, geneerinen XML-elementtikirjoittaja ja lomakepaketin sarjallistaja, painoivat tuotoksensa siististi rivinvaihdolla aloitustägien jälkeen. Useimmissa XML:ssä kyseinen tyhjä merkki on kosmeettinen. XFA-datassa se ei ole: kun datasets-paketti jäsennetään uudelleen, teksti kohteen <Comments> ja </Comments> välillä on kentän arvo, rivinvaihto mukaan lukien. Tyhjä kenttä avautui siis uudelleen kantamassa yhtä LF:tä, ja jokainen myöhempi tallennus- ja uudelleenavauskierto saattoi lisätä toisen

PDFium Componentin XFA-tallennuskierron kaavio, jossa kirjoittaja lisää rivinvaihdon aloitustägien jälkeen, uudelleenavattu jäsentäjä lukee LF:n Comments-tägien välillä kentän arvona, ja jokainen myöhempi tallennus liittää toisen rivinvaihdon, kunnes v3.125.2 poistaa vain sarjallistajan syntetisoiman tyhjän merkin
Yksi tallennus-uudelleenavauskierto istuttaa ensimmäisen rivinvaihdon, ja jokainen myöhempi kierros lisää toisen, minkä vuoksi ajautuminen näytti koko muotonsa vasta toisella sukupolvella

Ilmeinen korjaus, arvojen karsiminen latauksen yhteydessä, olisi väärin. Käyttäjät kirjoittavat alussa ja lopussa olevia välilyöntejä sekä tahallista monirivistä tekstiä XFA-kenttiin, ja osoitelohkon tai kiinteän levyisen koodin on selvittävä tavu tavulta. Korjaus versiossa v3.125.2 poistaa siksi vain sen tyhjän merkin, jonka sarjallistaja itse syntetisoi tägien ympärille. Käyttäjäarvot, olemassa olevat tekstitietosolmut ja CDATA-osiot kulkevat läpi koskemattomina, joten arvo " indented" pysyy sisennettynä ja tahallisesti tyhjä kenttä pysyy tyhjänä

Miksi emoji palaa eri merkkinä?

Emoji palasi vääränä, koska Windowsin wchar_t on 16 bittiä leveä, ja kaksi dekoodauspolkua tallensi täyden Unicode-skaalaariarvon yhteen wchar_teen. UTF-8-streamin dekooderi ja numeeristen merkkiviittausten, kuten &#x1F642;, jäsentäjä tekivät molemmat näin. U+1F642, hieman hymyilevä kasvo, ei mahdu 16 bittiin, joten ylemmät bitit putosivat pois ja U+F642 ilmestyi tilalle: koodipiste Private Use Area -alueella, jonka useimmat fontit renderöivät laatikkona tai eivät mitään

Lomakesarjallistajalla oli vastakkainen ongelma. Se suodatti merkit yksi wchar_t kerrallaan, näki kaksi surrogate-koodiyksikköä, jotka ovat kelvottomia eristettyinä, ja pudotti molemmat, joten emoji katosi lomakepaketista kokonaan. Versiossa v3.125.2 dekooderi kuluttaa jokaisen skaalaariarvon kokonaan ja emittoi oikean surrogate-parin. Kun jäljellä on vain yksi tulopaikka, se pitää matalan surrogaten odottavana eikä ilmoita streamin loppua, kun kyseinen yksikkö on yhä puskuroituna. Lukulohkojen yli jaettu UTF-8-jakso viedään seuraavaan lukuun hylkäämisen sijaan. Lomakkeenviejä pitää nykyään kelvolliset surrogate-parit yhdessä, ja numeeriset merkkiviittaukset tuottavat oikeat parit myös

PDFium Componentin surrogate-käsittelyn kaavio, jossa U+1F642 saapuu UTF-16-parina D83D DE42 ja kaksi vikapolkua turmelee sen: 16-bittiset wchar_t-dekooderit typistävät skaalaarin arvoon U+F642 yksityiskäytön alueella, kun taas lomakesarjallistaja suodattaa yksittäiset surrogatet ja pudottaa emojin kokonaan
Windowsin wchar_t on 16 bittiä leveä, joten skaalaari, joka tarvitsee surrogate-parin, joko menetti ylemmän puoliskonsa tai katosi paketista, kunnes molemmat polut oppivat pitämään parit yhdessä

Latin-1-testidata ei koskaan näytä mitään tästä, joten jokaisen XFA-kierrostestin tarvitsee vähintään yhden täydentävän tason merkin

Yksistreaminen XFA ja tallennusviat, joita kukaan ei nähnyt

Yksistreaminen XFA-asiakirja menetti muokkauksensa, koska natiivi tallennusavustaja torjui kyseisen tallennusasettelun ja sen kutsuja ohitti epäonnistumisen. ISO 32000-1 §12.7.8 sallii interaktiivisen lomakesanakirjan /XFA-tietueen olla joko pakettien nimien ja streamien taulukko tai yksi stream, joka kantaa koko XDP-asiakirjan. Pakettitaulukot ovat tavallinen tapaus, mutta yksittäiset streamit ovat täysin laillisia, ja PDF-tallennus päättyi ikään kuin mitään ei olisi tapahtunut, kun lomakedata pysyi vanhoissa arvoissaan

Versiosta v3.125.2 alkaen V8-ajonaikainen käsittelee tuetun yksistreamisen osajoukon. Se vie ensin molemmat elävät paketit, datasets ja form, lavastusalueelle ja validoi ne, ja vasta sen jälkeen korvaa täsmäävät paketit alkuperäisessä XDP:ssä. Muut paketit ja juuren nimiavaruusjulistukset säilytetään. Jos lavastus epäonnistuu, pysyvää XFA-streamia ei koskaan kosketeta ja asiakirja pitää muokkausmerkkinsä

XML-kommentit ja käsittelyohjeet vaativat ylimääräistä huolenpitoa, koska sisäinen XML-DOM pudottaa ne. Versiossa v3.125.2 niiden läsnäolo sai tallennuksen epäonnistumaan suoraan sisällön hiljaisen menettämisen sijaan. v3.126.0 säilyttää ne: ennen jäsentämistä jokainen kommentti tai käsittelyohje vaihdetaan merkkiin, joka on rakennettu etuliitteestä, joka ei esiinny missään alkuperäisessä tekstissä. Kun elävät paketit on korvattu, jokaisen merkin on ilmestyttävä täsmälleen kerran ennen kuin alkuperäinen tokeni palautetaan ja stream kirjoitetaan. Korvattujen pakettien ulkopuoliset tokenit säilyttävät siksi tekstinsä ja järjestyksensä, mukaan lukien prologin, mallipohjan ja muiden pakettien tokenit

Jotkin syötteet torjutaan yhä tahallaan, ja jokainen torjunta on eksplisiittinen tallennusvika:

  • Kommentit tai käsittelyohjeet elävien datasets- tai form-pakettien sisällä, koska niiden alkuperäisiä paikkoja ei voi kuvata tuoreesti viettyyn sisältöön
  • DTD-julistukset ja XMLDSig-allekirjoitukset, koska XDP:n uudelleenkirjoittaminen ei voi pitää XML-allekirjoitusta kelvollisena
  • Virheellinen UTF-8- tai UTF-16-koodaus, keskeneräiset tägit, virheelliset merkkiviittaukset, tuntemattomat entiteetit ja epämuodostuneet käsittelyohjeet, jotka torjutaan hiljaisen korjaamisen sijaan
PDFium Componentin yksistreamisen XFA-tallennuksen putki, jossa elävät datasets- ja form-paketit viedään lavastukseen, validoidaan ja korvataan sitten alkuperäisen XDP:n sisällä kommentit merkkien kautta säilyttäen, kun taas lavastusviat ja syötteet kuten DTD:t tai XMLDSig torjuvat tallennuksen eksplisiittisesti
Lavastettu vienti validoidaan ennen kuin mitään korvataan, joten epäonnistunut tallennus jättää pysyvän XFA-streamin koskemattomaksi ja asiakirja pitää muokkausmerkkinsä

Yksistreaminen tuotos on UTF-8:a ja säilyttää XML-sisältömallin, ei alkuperäistä tavuasettelua tai koodausjulistusta

Viimeinen vika istui XFA:n alapuolella. Natiivi tiedostokirjoittaja puskuroi tuotoksen 32 Kt:n lohkoihin ja huuhteli viimeisen osittaisen lohkon vain tuhoajassaan, sen jälkeen kun asiakirjakirjoittaja oli jo ilmoittanut onnistumisesta. Levy täynnä - tai I/O-vika kyseisessä viimeisessä lohkossa oli näkymätön kutsujalle. Versiosta v3.125.2 alkaen V8-ajonaisessa ja v3.125.3 alkaen tavallisessa ajonaikaisessa kyseinen lopullinen huuhtelu on osa tallennustulosta, ja XFA-muokkausmerkki tyhjennetään vasta aidon onnistumisen jälkeen. Delphi-puolella TPdf.SaveAs(const FileName: string; Option: TSaveOption = saNone; PdfVersion: TPdfVersion = pvUnknown): Boolean kirjoittaa väliaikaiseen tiedostoon kohteen viereen ja siirtää sen paikalleen vain, kun tallennus palauttaa Truen, joten epäonnistunut tallennus jättää edellisen tiedoston ehjäksi

Miksi dynaaminen XFA-lomake avautuu uudelleen vähemmällä sivulla?

Dynaaminen XFA-lomake avautuu uudelleen vähemmällä sivulla, kun sen juurialilomake ei julista restoreState="auto"a, ja kyseessä on lomakkeen laadintapäätös eikä PDFium Componentin vika. XFA 3.3:ssa kohteen restoreState oletus juurialilomakkeella on manual. Kohteen manual alla XFA-prosessori palauttaa vain rajatun tilan tallennetusta lomakepaketista ja jättää loput laatijan skripteille. Tallennetut kenttien arvot ja toistuvien alilomakkeiden instanssimäärät palaavat yhä, mutta ajonaikaisesti asetetut geometriset ominaisuudet eivät

Tapaus, joka paljasti tämän, oli kolmisivuinen lomake, jonka skripti kasvatti alilomakkeen arvoon h="450pt". Tallennettu lomakepaketti kantoi uutta korkeutta, arvot ja instanssimäärät. Uudelleenavauksessa layout rakennettiin kuitenkin mallipohjan korkeuksista, ja lomake virtasi uudelleen kahdelle sivulle. Ajonaikainen oli oikeassa: mallipohja ei ollut koskaan pyytänyt automaattista palautusta. Sen julistaminen juurialilomakkeelle korjaa uudelleenavauksen:

<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
  <subform name="form1" layout="tb" restoreState="auto">
    <pageSet>
      <pageArea name="Page1">
        <contentArea x="0.25in" y="0.25in" w="8in" h="10.5in"/>
        <medium stock="letter"/>
      </pageArea>
    </pageSet>
    <subform name="Details" layout="tb" w="7.5in">
      <!-- kentät; skriptit voivat muuttaa h:n tai lisätä instansseja ajonaikaisesti -->
    </subform>
  </subform>
</template>

Jos et omista mallipohjaa, älä paikkaa sen ympärillä katselimessa: lomake, joka nojaa manual-tilaan, odottaa omien skriptiensä rakentavan tilan. Elävä uudelleensivutus käyttäjän kirjoittaessa on erillinen aihe, käsitelty artikkelissa miten PDFium Component seuraa dynaamisen XFA:n sivumääriä ja siirtyneitä kenttiä

Miten varmistat XFA-tallennuksen Delphissä?

Ainoa luotettava XFA-tallennuksen tarkistus on avata tallennettu tiedosto uudessa TPdf-instanssissa ja lukea tallennettu data takaisin. TPdf.GetXfaDatasets palauttaa datasets-paketin sellaisena kuin se on tallennettu asiakirjaan, ei elävää XFA-datamallia, joten sen kutsuminen ennen tallennusta näyttää vanhat arvot. Uudelleenavauksen jälkeen se näyttää täsmälleen sen, mitä kirjoitettiin. Yksistreamisella asiakirjalla ei ole erikseen nimettyjä paketteja: PDFium ilmoittaa koko XDP:n yhtenä pakettina tyhjällä nimellä, joten GetXfaPacketByName('datasets') ja GetXfaDatasets eivät palauta mitään, ja varapolku lukee koko streamin funktion GetXfaFormPackets kautta

uses
  System.SysUtils, PDFium, FPdfXfa;

function ReadSavedXfaData(const FileName: string): string;
var
  Pdf: TPdf;
  Packets: TXfaPacketList;
  Bytes: TBytes;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := FileName;
    Pdf.Active := True;
    Bytes := Pdf.GetXfaDatasets;          // pakettitaulukon asettelu
    if Length(Bytes) = 0 then
    begin
      Packets := Pdf.GetXfaFormPackets;   // yksi stream: yksi nimetön paketti
      if Length(Packets) = 1 then
      begin
        SetLength(Bytes, Length(Packets[0].Content));
        if Length(Bytes) > 0 then
          Move(Packets[0].Content[0], Bytes[0], Length(Bytes));
      end;
    end;
    Result := TEncoding.UTF8.GetString(Bytes);  // tallennettu XDP-tuotos on UTF-8:a
  finally
    Pdf.Free;
  end;
end;

Tallennusrutiini sitoo sitten odottavan muokkauksen, tarkistaa kohteen SaveAs tuloksen ja vertaa uudelleenavattua arvoa. TPdf.ClearFormFieldFocus tappaa lomakekohdistuksen, mikä on se hetki, jolloin PDFium sitoo kohdistetun kentän muokkauspuskurin. TPdf.SetFocusedFormFieldText(const Value: WString): Boolean täyttää kohdistetun kentän ohjelmallisesti, mutta se nojaa kohdistukseen, jonka kääre seuraa funktion FocusFormField kautta, joka kulkee widget-annotaatioiden läpi. Dynaamisella XFA-sivulla ei ole normaalisti yhtään, joten siellä teksti saapuu yleensä näppäimistösyötteenä kohteessa TPdfView, ja funktio palauttaa Falsen, kun yhdelläkään seuratulla kentällä ei ole kohdistusta

function XmlText(const S: string): string;
begin
  Result := StringReplace(S, '&', '&amp;', [rfReplaceAll]);
  Result := StringReplace(Result, '<', '&lt;', [rfReplaceAll]);
end;

procedure SaveXfaAndVerify(Pdf: TPdf; const FileName, FieldTag,
  Expected: string);
var
  Saved: string;
begin
  // Valinnainen skriptattu täyttö; False tarkoittaa, ettei yhdelläkään seuratulla kentällä ole kohdistusta
  if (Pdf.FocusedFormFieldIndex >= 0) and
     not Pdf.SetFocusedFormFieldText(Expected) then
    raise EPdfError.Create('Could not write the focused field');

  Pdf.ClearFormFieldFocus;              // sito muokkauspuskurin
  if not Pdf.SaveAs(FileName) then      // sisältää lopullisen huuhtelun (v3.125.2+)
    raise EPdfError.CreateFmt('Saving %s failed', [FileName]);

  Saved := ReadSavedXfaData(FileName);
  if Pos('<' + FieldTag + '>' + XmlText(Expected) + '</' + FieldTag + '>',
    Saved) = 0 then
    raise EPdfError.CreateFmt('%s did not survive the round trip', [FieldTag]);
end;

Kohtele alimerkkijonotestiä savutestinä. Tyhjä elementti voidaan sarjallistaa muodossa <Tag/>, attribuutit voivat ilmestyä data-elementeille, ja koodauksen karkaaminen muotojen & ja < ohitse on sarjallistajan valinta. Tuotantotarkistuksiin lataa uudelleenavattu XML aidolla XML-jäsentäjällä ja vertaa sidotun data-elementin tekstitietosolmua. Aja tarkistus kahdesti peräkkäin myös, koska rivinvaihtovika näytti koko muotonsa vasta toisella sukupolvella

Pikaopas: XFA-tallennuksen uskollisuuden tarkistuslista

  • Ota käyttöön pdfium.v8.dll versiosta v3.125.2 tai uudempi XFA-lomakkeille ja v3.125.3 tai uudempi tavalliselle pdfium.dllille, jotta lopullisen kirjoituksen korjaus on molemmissa
  • Osoita LibraryName täyteen polkuun ja aseta EnableV8Engine arvoon True; puuttuva polku epäonnistuu toisen kopion lataamisen sijaan
  • Vahvista TPdf.XFA ja TPdf.XfaRuntimeAvailable asiakirjan avaamisen jälkeen
  • Kutsu funktiota ClearFormFieldFocus ennen SaveAsia, jotta kohdistettu kenttä sitoutuu
  • Älä koskaan ohita kohteen SaveAs totuusarvoista tulosta; False-tulos jättää edellisen tiedoston paikalleen
  • Varmista avaamalla uudelleen uudessa TPdfissä ja lukemalla GetXfaDatasets, palaten funktioon GetXfaFormPackets yksistreamiselle XFA:lle
  • Testaa tyhjillä arvoilla, alussa olevilla välilyönneillä, monirivistä tekstiä, merkillä & ja täydentävän tason merkillä, kahden tallennussukupolven yli
  • Odota eksplisiittisiä tallennusvikoja DTD:ille, XMLDSigille ja kommenteille elävien pakettien sisällä yksistreamisessä XFA:ssa
  • Jos dynaaminen lomake menettää ajonaikaisen geometriansa uudelleenavauksessa, tarkista juurialilomakkeelta restoreState="auto" ennen kuin epäilet kirjastoa

Callback-rakenteesta, jonka XFA-ajonaikainen odottaa isäntäsovellukselta, kerrotaan artikkelissa FPDF_FORMFILLINFO versio 2 ja XFA-ABI Delphissä. V8-ajonaikainen, Delphi- ja C++Builder-kääre ja katselinohjain ovat kaikki osa pakettia PDFium Component for Delphi and C++Builder, joka sisältää molemmat Windows-ajonaikaiset Win32:lle ja Win64:lle