Biblioteka losLab PDF Library umożliwia dwukierunkowy transfer danych formularzy z i do plików PDF na trzy różne sposoby: FDF i XFDF dla wartości pól AcroForm, te same dwa formaty dla komentarzy w adnotacjach oraz cały pakiet XFA XDP wpisywany bezpośrednio do formularza. Metody ExportFormDataToXFDF, ImportFormDataFromXFDF, ImportAnnotationsFromFDF, ExportAnnotationsToXFDF oraz SetXFAFromString stanowią punkty wejścia, a każda z nich posiada wariant plikowy oraz wariant tekstowy
Powodem istnienia tak wielu metod jest to, że dane formularza PDF nie są jednolite. Wypełniony formularz AcroForm zawiera wartości pól, może również przenosić komentarze recenzentów, z kolei historyczny format XFA osadza w sobie cały opis aplikacji XML, który nie ma nic wspólnego z pozostałymi dwoma. Biblioteka losLab PDF Library celowo rozdziela te trzy kwestie na osobne interfejsy API, ponieważ ich połączenie wymusiłoby nieprawidłowy model danych dla co najmniej dwóch z nich. Decyzja o wyborze odpowiedniej rodziny metod to pierwszy krok projektowy, który zazwyczaj rozstrzyga się w momencie, gdy dowiesz się, jakiego formatu wymaga system odbiorczy po drugiej stronie
Jaka jest różnica między danymi formularza FDF, XFDF i XFA?
FDF i XFDF przenoszą te same informacje w dwóch różnych składniach, podczas gdy XFA to zupełnie osobny świat. FDF to miniaturowy dokument o składni PDF (ISO 32000-2 §12.7.8): tablica /Fields słowników << /T (nazwa) /V (wartość) >>, z sekwencjami ucieczki w ciągach znaków dokładnie takimi samymi, jak te stosowane wewnątrz plików PDF. XFDF to wersja XML tych samych danych (ISO 19444), czyli drzewo <fields>, które Acrobat i większość systemów zaplecza formularzy odczytują i zapisują natywnie. XFA nie jest żadnym z nich: to szablon architektury XML Forms Architecture wraz z danymi, przechowywany jako pakiet XML Data Package (XDP), do którego plik PDF odwołuje się ze ścieżki /AcroForm/XFA. Wybierz FDF lub XFDF do współpracy na poziomie wartości pól, a po XFA sięgaj tylko przy utrzymywaniu dokumentu, który został pierwotnie utworzony jako formularz XFA
W ramach FDF i XFDF biblioteka losLab PDF Library wprowadza kolejny podział: wartości pól kontra komentarze w adnotacjach. Rodzina metod obsługi danych formularzy (ExportFormDataToFDF, ImportFormDataFromFDF, ExportFormDataToXFDF, ImportFormDataFromXFDF) odczytuje i zapisuje poddrzewo /Fields i nie modyfikuje niczego innego. Rodzina metod obsługi adnotacji (ExportAnnotationsToFDF, ImportAnnotationsFromFDF, ExportAnnotationsToXFDF, ImportAnnotationsFromXFDF) przetwarza natomiast poddrzewo /Annots, co w programie Acrobat odpowiada funkcji eksportu komentarzy. Obie te rodziny nigdy się nakładają, więc eksportowanie wartości pól nie spowoduje pobrania przypadkowych notatek recenzenckich, a importowanie komentarzy nie naruszy wartości wpisanych już przez użytkownika. To, co dzieje się z kontrolką (widget) po jej kliknięciu lub przeliczeniu, to jeszcze inna kwestia, którą opisano w powiązanej notatce o interaktywnych akcjach formularzy i języku JavaScript
Jak uzupełnić formularz PDF z poziomu FDF lub XFDF w Delphi?
Wczytaj dokument, wywołaj jedną z metod importu i zapisz. Metody ImportFormDataFromXFDF oraz ImportFormDataFromFDF parsują przychodzące dane formularza, dopasowują każdy wpis do pola AcroForm na podstawie jego pełnej nazwy kwalifikowanej, ustawiają wartość i zwracają liczbę faktycznie zaktualizowanych pól. Obie te metody modyfikują wyłącznie pola, które już istnieją w docelowym pliku PDF; żadna z nich nie utworzy nowego pola dla nazwy, której formularz nie definiuje, co zapobiega niezamierzonemu rozrostowi formularza pod wpływem przypadkowego lub niebezpiecznego pliku danych
var
Lib: TPDFlib;
FieldsSet: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('application-blank.pdf', '');
// XFDF produced by a case-management system, already UTF-8 on disk
FieldsSet := Lib.ImportFormDataFromXFDF('applicant-1042.xfdf');
if FieldsSet > 0 then
Lib.SaveToFile('application-filled.pdf');
finally
Lib.Free;
end;
end;
Dwa detale decydują o tym, czy nietrywialne dane przetrwają operację. Pierwszym z nich jest stosowanie sekwencji ucieczki (escaping): FDF przechowuje wartości jako ciągi znaków o składni PDF, więc wartości zawierające nawiasy, ukośniki wsteczne (backslash) lub bajty niedrukowalne trafiają do pliku w postaci zakodowanej ósemkowo zgodnie z ISO 32000-2 §7.3.4.2. Biblioteka losLab PDF Library odwraca to kodowanie przy imporcie, dzięki czemu nawiasy i ukośniki powracają do swojej pierwotnej postaci tekstowej wpisanej przez użytkownika. Drugim szczegółem jest kodowanie znaków: metody operujące na plikach zapisują i odczytują formaty XFDF oraz FDF w kodowaniu UTF-8, co jest zgodne z deklaracją XFDF i niezbędne do poprawnego przesłania wszelkich wartości zawierających znaki spoza zestawu ASCII (np. litery diakrytyczne, symbole walut czy znaki języków azjatyckich). Jeśli budujesz plik XFDF samodzielnie, zadeklaruj UTF-8 i zapisz go w UTF-8, a mechanizm importu przetworzy go bezbłędnie
Pola hierarchiczne, wybory wielowartościowe i sformatowany tekst
Nazwy pól hierarchicznych to pierwszy element, na którym zawodzi prosty eksporter. AcroForm odwołuje się do zagnieżdżonego pola za pomocą pełnej nazwy z kropkami, np. Applicant.FullName, natomiast XFDF nie umieszcza tej nazwy z kropkami w jednym atrybucie nazwy; standard ISO 19444 zagnieżdża ją w strukturze jako <field name="Applicant"><field name="FullName">. Biblioteka losLab PDF Library przy eksporcie dzieli nazwy z kropkami na zagnieżdżone elementy <field>, a przy imporcie składa je z powrotem w pełną nazwę kwalifikowaną, dzięki czemu oba kierunki operacji pozostają symetryczne. Podczas eksportu pomija również nienadrzędne pola pośrednie (rodziców), ponieważ węzeł nadrzędny w AcroForm reprezentuje tylko poziom nazewnictwa i sam nie posiada żadnej wartości; wygenerowanie go skutkowałoby pustym znacznikiem <value></value>, który nie odpowiada rzeczywistemu formularzowi. Pola wyboru wielokrotnego (multi-select) to druga pułapka: pole wyboru może przechowywać kilka wybranych wartości jednocześnie, co w formacie XFDF reprezentowane jest przez powtarzające się elementy <value>, a w FDF jako tablica /V. Biblioteka losLab PDF Library dzieli pole na wiele wartości tylko wtedy, gdy jest to rzeczywiście pole wyboru wielokrotnego, dzięki czemu zwykłe wielolinijkowe pole tekstowe zachowuje swoje znaki końca linii zamiast rozbijać się na niepoprawne pojedyncze wartości
Sformatowany tekst (rich text) to trzeci aspekt i zarazem ten, w którym najczęściej dochodzi do pomyłek. Pole z formatowaniem przechowuje swoje znaczniki w sekcji RV jako poddrzewo XHTML, a format XFDF przenosi je w elemencie <value-richtext>. Biblioteka losLab PDF Library zapisuje to poddrzewo jako aktywny fragment XML, a nie jako tekst z ucieczkami znaków, dzięki czemu narzędzia zewnętrzne odczytują prawdziwy tekst sformatowany zamiast ciągu widocznych tagów tekstowych; podczas importu zachowuje ono surowe poddrzewo RV i nadal stosuje zwykłą wartość <value> do pola V. Gdy pole oferuje jedno i drugie, zwykła wartość ma priorytet dla pola V, a tekst sformatowany towarzyszy jej w RV, co jest regułą interoperacyjności zapobiegającą sytuacji, w której import tekstu sformatowanego po cichu nadpisałby wartość ustawioną już przez inne narzędzie. Jeśli tekst sformatowany służy do przekazywania struktury dokumentu technologiom wspomagającym, należy traktować go w ten sam sposób, jak kolejność czytania omówioną w notatce na temat struktury znaczników PDF (tagged PDF) i ułatwień dostępu
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 reassembles; Skills fills the /V + /I arrays;
// Notes gets V from <value> and RV from <value-richtext>
Lib.ImportFormDataFromXFDFString(XFDF);
Lib.SaveToFile('intake-filled.pdf');
finally
Lib.Free;
end;
end;
Jak przesyłać w obie strony komentarze w adnotacjach za pomocą FDF lub XFDF?
Komentarze są przesyłane za pomocą rodziny metod adnotacji, przy czym obsługiwany podzbiór jest celowo niewielki. Metody ExportAnnotationsToXFDF i ImportAnnotationsFromXFDF, wraz z ich odpowiednikami dla formatu FDF, przenoszą znaczniki tekstowe wraz z polami, które faktycznie dają się czysto zapisać i odczytać: podtyp adnotacji, jej prostokąt ograniczający (rectangle), indeks strony liczony od 0, autora w polu T, temat w polu Subj, zwykłą treść Contents oraz kolor, który mapuje potrójną wartość /C DeviceRGB z pliku PDF na atrybut XFDF #RRGGBB i odwrotnie. Funkcja importu akceptuje wyłącznie znane nazwy elementów i pomija każdy wpis, którego strona wykracza poza zakres lub w którym brakuje współrzędnych prostokąta, dzięki czemu ręcznie edytowany lub zewnętrzny plik XFDF nie wprowadzi do dokumentu uszkodzonej adnotacji. Warto jasno sprecyzować, czego ten podzbiór jeszcze nie obsługuje: zawartości tekstowej z formatowaniem (RC), okienek popup, punktów opisujących geometrię zaznaczenia (quadpoints), rysunków odręcznych (ink lists), wierzchołków i gotowych strumieni wyglądu (appearance streams) — te elementy wykraczają obecnie poza zakres obsługi, więc geometria wyróżnień i niestandardowe wyglądy stempli nie zostaną zachowane na tej ścieżce. Aby zweryfikować, co faktycznie zostało zaimportowane, można przejść przez adnotacje strony lub szersze drzewo elementów opisane w artykule o wyszukiwaniu tekstu i enumeracji elementów strony
var
Src, Dest: TPDFlib;
Xfdf: WideString;
begin
Src := TPDFlib.Create;
Dest := TPDFlib.Create;
try
Src.LoadFromFile('reviewed.pdf', '');
Xfdf := Src.ExportAnnotationsToXFDFString; // <annots> subset only
Dest.LoadFromFile('clean-copy.pdf', ''); // pages must line up by index
Dest.ImportAnnotationsFromXFDFString(Xfdf);
Dest.SaveToFile('clean-copy-commented.pdf');
finally
Dest.Free;
Src.Free;
end;
end;
Zapisywanie pełnego pakietu XFA XDP za pomocą SetXFAFromString
Format XFA to przypadek szczególny, a metoda SetXFAFromString pozwala na zapisanie całej tej struktury jednocześnie. Metoda ta przyjmuje kompletny pakiet XML Data Package, czyli dokument <xdp:xdp> wraz z jego pakietami szablonów i zestawów danych, i przechowuje go jako strumień wskazywany przez ścieżkę /AcroForm/XFA. Biblioteka losLab PDF Library tworzy kontener AcroForm na żądanie, jeśli dokument go nie posiada, dzięki czemu nie trzeba dodawać tymczasowego pola tylko po to, by podpiąć strukturę XFA. Ponadto biblioteka odrzuca wszelkie buforowane stany przetworzonego obiektu XFA, więc kolejny odczyt odzwierciedla nowo zapisany pakiet, a notowań nieaktualna pamięć podręczna. Ponieważ pakiet XFA jest kodowany w XML i nie musi być zakodowany w UTF-8, biblioteka wykrywa znacznik kolejności bajtów (BOM) UTF-16 i poprawnie dekoduje pakiet przed przetworzeniem, co ma kluczowe znaczenie dla danych generowanych przez narzędzia domyślnie korzystające z UTF-16
Po osadzeniu pakietu metody GetXFAFormFieldValue oraz SetXFAFormFieldValue odwołują się do poszczególnych pól za pomocą ich ścieżki SOM (Scripting Object Model). Biblioteka losLab PDF Library akceptuje standardowe korzenie SOM oraz zwykłe ścieżki względne, więc zapisy form1.FullName, $data.form1.FullName i xfa.datasets.data.form1.FullName wskazują na ten sam węzeł danych, a po stronie szablonu odwołania $template i xfa.template są interpretowane tożsamo. Ta elastyczność ma znaczenie w sytuacjach, gdy ścieżki SOM są generowane przez system zewnętrzny, który zawsze tworzy pełne nazwy kwalifikowane
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); // creates the AcroForm container if none exists
// read back through the data DOM with an SOM path
if Lib.GetXFAFormFieldValue('form1.FullName') = 'Alice Example' then
Lib.SetXFAFormFieldValue('form1.FullName', 'Bob Example');
Lib.SaveToFile('xfa-packet.pdf');
finally
Lib.Free;
end;
end;
Podsumowując, wartości pól są w pełni przesyłane w obie strony za pomocą formatów FDF i XFDF, z zachowaniem hierarchii, wyboru wielokrotnego i tekstu sformatowanego; komentarze w adnotacjach są przesyłane jako zdefiniowany podzbiór znaczników tekstowych z wyraźnymi ograniczeniami; natomiast format XFA jest zapisywany i odczytywany jako cały pakiet z dodatkowym dostępem na poziomie poszczególnych pól poprzez ścieżki SOM. Dopasuj format do odbiorcy, zadeklaruj UTF-8 dla wszystkiego, co tworzysz ręcznie, i pamiętaj, że metody importu modyfikują wyłącznie te pola i adnotacje, które już pasują do struktury dokumentu. Opisane tutaj metody obsługi danych formularzy, adnotacji oraz XFA są częścią biblioteki losLab PDF Library dla Delphi i C++Builder, a dokumentacja referencyjna zawiera pełną listę parametrów dla każdej z metod eksportu i importu