Техническа статия

Обмен на данни от формуляри във FDF, XFDF и XFA формати в Delphi

Библиотеката losLab PDF Library прехвърля данни от формуляри в и извън PDF по три различни начина: чрез FDF и XFDF формати за стойности на полетата в AcroForm, чрез същите тези два формата за коментари към анотации, както и чрез записване на цял пакет XFA XDP директно във формуляра. Методите ExportFormDataToXFDF, ImportFormDataFromXFDF, ImportAnnotationsFromFDF, ExportAnnotationsToXFDF и SetXFAFromString са входните точки, като всеки от тях има вариант за работа с файлове и вариант за работа с низове

Причината да съществува толкова много методи е, че данните в PDF формулярите не са еднородни. Един попълнен AcroForm съдържа стойности на полета, може също така да носи коментари за преглед, а остарял XFA формуляр вгражда цяло XML описание на приложението, което няма нищо общо с предходните две. losLab PDF Library умишлено разделя тези три области в различни API интерфейси, тъй като обединяването им би наложило неподходящ модел на данни за поне две от тях. Изборът от коя фамилия функции се нуждаете е първото дизайнерско решение и обикновено се определя в момента, в който разберете какво действително консумира получаващата система от другата страна

Каква е разликата между FDF, XFDF и XFA данни от формуляри?

Форматите FDF и XFDF пренасят една и съща информация в два различни синтаксиса, а XFA е напълно отделен свят. FDF е документ с миниатюрен PDF синтаксис (ISO 32000-2 §12.7.8): масив /Fields от речници тип << /T (name) /V (value) >>, в които низовете са екранирани точно по начина, по който се екранират буквени низове в PDF. XFDF е XML формата на същите данни (ISO 19444) – дърво <fields>, което Acrobat и повечето системи за обработка на формуляри четат и пишат нативно. XFA не е нито едно от двете: това е шаблон от архитектурата за XML формуляри (XML Forms Architecture) заедно с неговите данни, съхранявани като XML пакет от данни (XDP), който PDF файлът реферира от /AcroForm/XFA. Изберете FDF или XFDF, когато обменяте стойности на полета, и се насочете към XFA само когато поддържате документ, който първоначално е бил създаден като XFA формуляр

В рамките на FDF и XFDF библиотеката losLab PDF Library поставя втора граница: стойности на полета спрямо коментари под формата на анотации. Фамилията за данни от формуляри (ExportFormDataToFDF, ImportFormDataFromFDF, ExportFormDataToXFDF, ImportFormDataFromXFDF) чете и пише само в поддървото /Fields и не докосва нищо друго. Фамилията за анотации (ExportAnnotationsToFDF, ImportAnnotationsFromFDF, ExportAnnotationsToXFDF, ImportAnnotationsFromXFDF) вместо това чете и пише в поддървото /Annots, което в Acrobat съответства на „Експортиране на коментари“. Двата процеса никога не се застъпват, така че експортирането на стойности на полета няма да събере случайни бележки от прегледи, а импортирането на коментари няма да промени стойностите, които потребителят вече е въвел. Това, което даден елемент (widget) прави, когато върху него се щракне или се преизчисли, е трети вид логика, разгледана в придружаващата статия за интерактивни действия с формуляри и JavaScript

Как се попълва PDF формуляр от FDF или XFDF в Delphi?

Зареждате документа, извиквате един метод за импортиране и го записвате. Всеки от методите ImportFormDataFromXFDF and ImportFormDataFromFDF анализира входящите данни от формуляра, съпоставя всеки запис с поле в AcroForm по неговото пълно име (fully qualified name), задава стойността и връща броя на действително актуализираните полета. И двата метода променят само полета, които вече съществуват в целевия PDF файл; нито един от тях не създава ново поле за име, което формулярът не дефинира, което предотвратява безшумното разширяване на формуляра ви от повредени или външни файлове с данни

Две подробности определят дали по-сложните данни ще оцелеят при прехвърлянето. Първата е екранирането (escaping): FDF съхранява стойностите като буквени низове в PDF, така че стойност, съдържаща скоба, наклонена черта или непечатаем байт, пристига обвина в осмично екраниране съгласно ISO 32000-2 §7.3.4.2, а losLab PDF Library премахва това екраниране при импортиране, така че скобите и наклонените черти се връщат като обикновените знаци, които потребителят е въвел. Втората е кодирането: файловите методи пишат и четат XFDF и FDF във формат UTF-8, което се декларира в самия XFDF и е необходимо за всяка не-ASCII стойност на поле (име с диакритични знаци, валутен символ, CJK адрес), за да се прехвърли без повреда. Ако сами изграждате XFDF, декларирайте и пишете в UTF-8 и импортирането ще се извърши безпроблемно

Йерархични полета, избор на множество стойности и форматиран текст (Rich Text)

Йерархичните имена на полета са първото място, на което по-прост експортьор се срива. AcroForm адресира вложено поле с пълно име с точки, като например Applicant.FullName, но XFDF не поставя този низ в един атрибут за име; стандартът ISO 19444 го влага по следния начин: <field name="Applicant"><field name="FullName">. losLab PDF Library разделя името с точки на вложени елементи <field> при експортиране и ги сглобява обратно в пълното име при импортиране, запазвайки симетрията в двете посоки. При експортиране тя също така пропуска родителските полета без крайни стойности, тъй като родителският възел в AcroForm носи само йерархичното име и няма собствена стойност; извеждането му би генерирало празен елемент <value></value>, който не съответства на истинския формуляр. Списъчните кутии с множество избори са вторият капан: едно поле за избор може да съдържа няколко избрани стойности едновременно, което XFDF изразява чрез повтарящи се елементи <value>, а FDF – като масив /V, а losLab PDF Library разделя полето на множество стойности само когато то наистина поддържа избор на няколко елемента, така че обикновено многоредово текстово поле запазва своите нови редове, вместо да бъде разделено на некоректни стойности

Форматираният текст (rich text) е третата особеност и тази, която се бърка най-често. Поле с форматиране съхранява своите маркери в записа RV като XHTML поддърво, а XFDF го пренася в <value-richtext>. losLab PDF Library записва това поддърво като действащ XML фрагмент, вместо да го екранира, така че следващите инструменти четат истински форматиран текст вместо низ от видими тагове; при импортиране тя запазва суровото поддърво RV и въпреки това прилага чистата стойност <value> към V. Когато дадено поле предлага и двете, обикновената стойност е с приоритет за V, а форматираният текст се записва успоредно в RV – това е правилото за съвместимост, което не позволява на импортирането на форматиран текст безшумно да пренапише стойност, която друг инструмент вече е задал. Когато вашият форматиран текст служи за предаване на структурата на документа към асистивни технологии, се отнасяйте към него по същия начин, по който бихте се отнесли към подредбата на четене, обсъдена в статията за маркиран PDF и структура за достъпност

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;

Как се прехвърлят двупосочно коментари от анотации като FDF или XFDF?

Коментарите се пренасят чрез фамилията от методи за анотации и поддържаното подмножество е съзнателно малко. Методите ExportAnnotationsToXFDF и ImportAnnotationsFromXFDF, заедно с техните FDF съответствия, прехвърлят текстови маркери за полета, които действително се прехвърлят чисто: подтипа на анотацията, нейния правоъгълник, нейния 0-базиран индекс на страницата, автора в T, темата в Subj, чистото съдържание Contents и цвета, който картографира PDF тройката /C DeviceRGB към и от XFDF атрибута #RRGGBB. Импортирането приема само познати имена на елементи и пропуска всеки запис, чиято страница е извън диапазона или чийто правоъгълник липсва, така че ръчно редактиран или външен XFDF не може да вмъкне повредена анотация в документа. Това, което подмножеството все още не поддържа, си струва да се каже ясно: богати текстови съдържания (RC), изскачащи прозорци (popups), quadpoints, списъци с мастило (ink lists), върхове (vertices) и генерирани потоци за визуализация са извън обхвата за момента, така че геометрията на маркиране и персонализираните изгледи на печати няма да оцелеят при този процес. За да потвърдите какво действително е импортирано, обходете анотациите на страницата или по-широкото дърво от елементи, разгледано в статията за търсене на текст и изброяване на елементи на страницата

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;

Записване на пълен XFA XDP пакет със SetXFAFromString

XFA е изключението, а SetXFAFromString е начинът, по който записвате цялата структура наведнъж. Методът приема пълен XML Data Package – документа <xdp:xdp> с неговите пакети за шаблони и масиви от данни – и го съхранява като поток, рефериран от /AcroForm/XFA. losLab PDF Library създава контейнера AcroForm при поискване, когато документът няма такъв, така че не се налага да добавяте временно поле само за да прикачите XFA, и изчиства всяко съхранявано състояние на XFA, така че последващо четене да отразява пакета, който току-що сте записали, а не остарял кеш. Тка че XFA пакетът е XML и не е задължително да бъде в UTF-8, библиотеката разпознава UTF-16 маркера за подредба на байтовете (BOM) и декодира пакета правилно преди парсване, което е важно за пакети, създадени от инструменти, работещи по подразбиране в UTF-16

След като пакетът е разположен, методите GetXFAFormFieldValue и SetXFAFormFieldValue се обръщат към отделни полета чрез техния път в Scripting Object Model (SOM). losLab PDF Library приема стандартните SOM корени, както и чисти относителни пътища, така че form1.FullName, $data.form1.FullName и xfa.datasets.data.form1.FullName се разрешават до един и същ възел от данни, а страната на шаблона приема $template и xfa.template по същия начин. Тази гъвкавост е важна, когато SOM пътищата се генерират от друга система, която винаги излъчва пълния квалифициран формат

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;

Честното обобщение е, че стойностите на полетата се прехвърлят изцяло през FDF и XFDF, включая йерархия, множествен избор и форматиран текст; коментарите на анотациите се пренасят като дефинирано подмножество за текстово форматиране с ясни ограничения; а XFA се записва и чете като цял пакет с индивидуален достъп до SOM полета отгоре. Съобразете формата с получателя, декларирайте UTF-8 за всичко, което изграждате ръчно, и помнете, че методите за импортиране взаимодействат само с полета и анотации, които вече съответстват на документа. Описаните тук методи за данни от формуляри, анотации и XFA са част от losLab PDF Library за Delphi и C++Builder, чиято справочна документация съдържа пълния списък с параметри за всяка входна точка за експортиране и импортиране