Techninis straipsnis

XFDF eksportas ir importas Delphi aplinkoje su PDFium komponentu

„PDFium Component“, skirtas Delphi ir Lazarus, keičiasi formų duomenis bei komentarais (annotations) per XFDF — XML formatą, apibrėžtą standarto ISO 19444-1. Metodas „TPdf.ExportXFDF“ nuoseklina (serialize) kiekvieno lauko reikšmę bei komentarą į XFDF failą arba „TStream“ srautą; „TPdf.ImportXFDF“ nuskaito XFDF dokumentą atgal, atkurdamas komentarus puslapiuose ir pritaikydamas laukų reikšmes. Šis pilnas ciklas (round-trip) apima dvi darbo eigas, kurių prireikia dokumentų programoms: recenzentas pažymi PDF failą programoje „Acrobat“ ir atsiunčia jums komentarus sujungimui, arba atitikties taisyklė reikalauja, kad formos duomenys būtų saugomi atskirame, lengvai palyginamame bei audituojamame faile, o ne pačiame PDF

Ar PDFium palaiko XFDF importą ir eksportą?

Pati „PDFium“ biblioteka to nepalaiko. Grynasis C API apskritai neturi XFDF ar FDF funkcijų — nieko nėra viešosiose antraštėse, kas leistų skaityti ar rašyti šiuos formatus, todėl joks kodo apgaubimas (wrapping) čia nepadės. Todėl „PDFium Component“ realizuoja visą XFDF variklį „Pascal“ kalba atskirame modulyje, kuris kuria bei analizuoja XML tiesiogiai ant esamo „TPdf“ komentarų ir formos laukų duomenų modelio. Rašymo funkcija sukuria XML rankiniu būdu, o skaitytuvas yra ranka parašytas būsenų mašinos analizatorius (state-machine parser), todėl ši funkcija neprideda jokių Delphi „XMLDoc“ ar FPC DOM modulių priklausomybių ir veikia identiškai Delphi bei Lazarus aplinkose

Šis dizaino sprendimas yra svarbus, kai vertinate alternatyvas. Jei iš „Pascal“ kalbos kvietėte grynus „FPDF_*“ metodus, XFDF bus siena: tokios galimybės DLL faile tiesiog nėra. Komponentas kerta šią sieną, traktuodamas XFDF kaip gryną duomenų nuoseklinimo užduotį — surenka laukų reikšmes bei komentarų įrašus, kuriuos apgaubas (wrapper) jau moka skaityti, įrašo juos kaip standartinį XML failą ir atlieka atvirkštinį procesą importo metu

Ką turi XFDF failas?

ISO 19444-1 apibrėžia XFDF kaip XML formato FDF atitikmenį, ir jo turinys dalijasi į du pagrindinius blokus. Elementas „<fields>“ perneša hierarchinius formos laukų pavadinimus bei jų reikšmes — viską, ką naudotojas įvedė, pasirinko ar pažymėjo „AcroForm“ formoje. Elementas „<annots>“ perneša komentarus: „TPdf.ExportXFDF“ sukuria 18 potipių — tekstą, paryškinimą, pabraukimą, perbraukimą, banguotą liniją, liniją, apskritimą, kvadratą, žymeklį, daugiakampį, laužtinę liniją, spaudą, rašalą, laisvą tekstą, failo priedą, garsą, nuorodą ir redagavimą. Valdymo elementų komentarų (widgets) elemente „<annots>“ nėra, nes jų duomenys keliauja per „<fields>“, o vidiniai peržiūros programų potipiai (pvz., Popup) nėra XFDF žodyno dalis

Kad suderinamumas (round-trip) veiktų patikimai, „TPdfAnnotation“ įrašas buvo papildytas metaduomenimis, kurių tikisi XFDF: „Name“ (NM unikalus identifikatorius), „Subject“, „ModificationDate“, „CreationDate“, „Icon“, „Opacity“, linijos galiniai taškai, daugiakampio bei laužtinės linijos viršūnės ir rašalo judesio keliai. Kiekvienas naujas laukas suporuotas su loginiu požymiu „Has*“, todėl kodas, kuris kūrė komentarus ankstesnėse versijose, toliau kompiliuojasi ir kuria tuos pačius žodynus — nenustatytas laukas tiesiog neįrašomas. Jei jau kuriate žymėjimą programiškai, tas pats įrašas, kurį naudojate teksto žymėjimo komentarams su kampiniais taškais (quad points), dabar turi viską, ko reikia XFDF

Kaip eksportuoti formos duomenis ir komentarus į XFDF?

Vienas iškvietimas sutvarko visą dokumentą. Įkelkite PDF su įjungtu formų pildymu, iškvieskite „ExportXFDF“, ir komponentas pereis per kiekvieną puslapį, surinks laukų reikšmes bei komentarus ir įrašys XML. Grąžinama reikšmė yra įrašytų UTF-8 baitų skaičius, kas yra puikus patikrinimas sistemos žurnaluose

var
  Pdf: TPdf;
  Bytes: Integer;
begin
  Pdf := TPdf.Create(Self);
  Pdf.FormFill := True;            // needed so field values are live
  Pdf.FileName := 'expense-report.pdf';
  Pdf.Active := True;
  Bytes := Pdf.ExportXFDF('expense-report.xfdf');
  ShowMessage(Format('%d bytes of XFDF written', [Bytes]));
end;

Abi kryptys turi „TStream“ perkrovimą (overload), todėl niekas nevers kurti laikinų failų diske. Eksportavimas į „TMemoryStream“ yra natūralus pasirinkimas, kai XFDF siunčiamas per HTTP atsakymą, duomenų bazės BLOB lauką ar pasirašytą archyvo įrašą

var
  Buffer: TMemoryStream;
begin
  Buffer := TMemoryStream.Create;
  try
    Pdf.ExportXFDF(Buffer);        // same XML, no file involved
    Buffer.Position := 0;
    // hand the stream to a web response, blob column, or zip entry
  finally
    Buffer.Free;
  end;
end;

Kaip ImportXFDF sulieja komentarus atgal į dokumentą?

Importo pusė užbaigia recenzavimo darbo eigą. Kolega pažymi sutartį programoje „Acrobat“, eksportuoja komentarus kaip XFDF ir atsiunčia jums kelis kilobaitus XML failo, užuot siuntęs antrą PDF kopiją. „TPdf.ImportXFDF“ išanalizuoja šį failą, sukuria kiekvieną komentarą puslapyje, kurį nurodo jo puslapio atributas, ir įrašo laukų reikšmes į atitinkamus valdymo elementų komentarus. Funkcija grąžina bendrą pritaikytų laukų ir komentarų skaičių, todėl vartotojo sąsaja gali tiksliai patvirtinti, kiek informacijos buvo įkelta

var
  Applied: Integer;
begin
  Applied := Pdf.ImportXFDF('review-comments.xfdf');
  StatusBar.SimpleText :=
    Format('%d fields and annotations merged', [Applied]);
end;

Analizatorius yra ranka parašytas žymių skaitytuvas, sukurtas labiau tolerancijai nei griežtumui: nežinomi elementai praleidžiami, sisteminės instrukcijos, DOCTYPE deklaracijos ir komentarai ignoruojami, neatpažinti požymių pavadinimai atmetami, o atributų reikšmės iškoduojamos per pilną XML esybių bei skaitinių simbolių rinkinį, įskaitant UTF-16 surogatines poras. XFDF failas, sukurtas „Acrobat“, internetinės peržiūros programos ar kito įrankio, yra suliejamas be jokių problemų. Kai komentarai patenka į dokumentą, peržiūros ciklas tęsiasi su atsakymų bei būsenų mechanizmu, aprašytu komentarų peržiūros darbo eigos kūrime su PDFium komponentu, o importuotos laukų reikšmės dalyvauja įprastoje judėjimo medzi laukų logikoje, aprašytoje formos laukų navigacijoje

Kodėl XFDF failams svarbūs dešimtainių skaičių skirtukai?

Kiekvienas XFDF komentaras yra pozicionuojamas pagal koordinačių eilutes — pavyzdžiui, „rect="70.5,540,200,560"“ — ir standartas ISO 19444-1 reikalauja naudoti tašką kaip dešimtainį skirtuką. Numatytasis Delphi skaičių formatavimas seka „Windows“ regioninius nustatymus (locale), todėl, pavyzdžiui, lietuviškoje ar prancūziškoje sistemoje paprastas „FloatToStr“ paverčia 70.5 į 70,5, kas sugadina kableliais atskirtą koordinačių sąrašą į šiukšles, kurias kiti įrankiai atmeta arba perskaito neteisingai. „PDFium Component“ normalizuoja kiekvieną skaičių, kurį rašo: regioninis dešimtainis skirtukas pakeičiamos tašku, o paskutiniai nuliai nukerpami, todėl eksportuotas failas išlieka identiškas baitų lygiu, nesvarbu, ar jis buvo sukurtas en-US, ar lietuviškoje aplinkoje. Jei kada nors taisėte PDF įrankį, kuris veikė biure, bet neveikė pas Europos klientą, tikriausiai susidūrėte būtent su šia klaidų klase — verta žinoti, kad komponentas ją sutvarko už jus

Kokios yra suderinamumo (round-trip) ribos?

Verta atvirai įvardyti dvi ribas. Pirma, XFDF perneša komentaro duomenis, o ne jo vizualų atvaizdavimą — atvaizdavimo srautai (appearance streams) nėra formato dalis, todėl gaunanti peržiūros programa sukuria kiekvieno komentaro išvaizdą iš jo savybių. Paryškinimas ar kvadratas visur bus atvaizduojami teisingai; spaudas su nestandartine išvaizda bus atvaizduojamas pagal tai, ką atitinkama programa piešia tam spaudui. Antra, žemesnio lygio PDFium API leidžia nustatyti tekstines reikšmes, bet neturi funkcijų komentarų skaičiams ar kelių geometrijai nustatyti, todėl nepermatomumas („Opacity“), linijos galai, viršūnės ir rašalo judesiai yra tik eksportuojami: komponentas juos teisingai išsaugo rašydamas XFDF, bet negali įrašyti atgal į PDF importo metu. Laukų reikšmės, turinys, spalvos, stačiakampiai (rects), kampiniai taškai, datos ir tapatybės metaduomenys pilnai suderinami abiem kryptimis

Praktinei pradžiai pavyzdys „XfdfLab“ pateikiamas Delphi, C++Builder ir Lazarus demonstraciniuose rinkiniuose ir sujungia abu iškvietimus su mygtukais virš bet kurio atidaryto PDF failo. XFDF palaikymas yra įtrauktas į dabartinį „PDFium Component“ leidimą kartu su komentarų bei formų API, kuriais jis remiasi