Technical Article

Преобразуване на XFA в AcroForm в Delphi с HotPDF

Два формуляра могат да съдържат едни и същи полета и да се държат по коренно различен начин. AcroForm съхранява своите полета като обикновени PDF обекти, разположени върху реалното съдържание на страницата, така че всеки съвместим четец може да го визуализира. Динамичният XFA формуляр не съхранява почти нищо като PDF: полетата, оформлението и дори геометрията на страниците живеят в XML пакет, а видимите страници се генерират в момента на отваряне от графичен двигател (layout engine), който само Adobe е разпространявала масово. Изпратете такъв файл към уеб четец, архивиращо приложение или инструмент за извличане на текст и няма да получите формуляра. Ще получите една сива страница със съобщението „МолÑ? изчакайте... Ако това съобщение не бъде заменено от правилното съдържание на документа, вашият PDF четец може да не е в състояние да покаже този тип документâ€? Всеки, който е обработвал държавни или застрахователни документи, познава тази страница веднага щом я види.

Това временно съдържание (placeholder) не означава повреден файл. То е точно това, което форматът предвижда да се случи, когато няма наличен XFA процесор. Към момента това описва почти всеки четец извън настолния Acrobat. Ето защо практичният ход е динамичният формуляр да се конвертира в обикновен AcroForm, преди да достигне до следващите етапи по веригата. HotPDF, PDF библиотеката на losLab за Delphi и C++Builder, извършва това преобразуване програмно, като възстановява XML формуляра като вградени полета върху стандартни страници.

Защо двата модела не могат да съществуват едновременно

AcroForm е дефиниран в стандарта ISO 32000-1 §12.7. Всяко поле е PDF обект с анотация (widget annotation) и поток за визуализация (appearance stream), страницата е реално PDF съдържание, а данните се разполагат върху него. XFA обръща този модел: формулярът е XML документ, XDP пакет, съхранен в записа /XFA на речника на AcroForm. Страниците на динамичния формуляр съдържат само съобщението „МолÑ? изчакайтеâ€?и нищо друго, тъй като реалното съдържание никога не е било сериализирано като PDF. Даден четец обработва файла по единия или по другия модел. Игнорирайте записа /XFA и ще видите празната обвивка; поддържайте го без XFA двигател и ще видите предупреждението. Стандартът ISO 32000-2 сложи край на спора, като премахна XFA от спецификацията PDF 2.0, което е основната причина преобразуването да се превърне от частен случай в стандартна практика.

Преди да конвертирате каквото и да е, го класифицирайте, тъй като не всеки XFA файл показва споменатото предупреждение. Статичните XFA формуляри съдържат предварително визуализирани PDF страници до XML кода, така че те се показват навсякъде и показват проблеми едва при попълване. Динамичните формуляри съдържат само предупреждението и са неизползваеми до конвертирането им. Доверявайте се на самия документ, а не на разширението или изпращача. Файл, който показва реално съдържание в четец, различен от Adobe, но все пак съдържа запис /XFA, е статичен или хибриден; файл, който показва предупредителната страница, е динамичен. Записвайте типа на всеки входящ файл. Двата вида се държат по различен начин по-късно, а проблем с празен архивиран формуляр се разрешава бързо, когато в лога вече пише: „динамичеÐ?XFA, преобразуван, 47 съпоставени полета, 2 предупрежденияâ€?

Преобразуване на зареден XFA документ във вградени полета

Преобразуването се извършва върху документ, който вече е в паметта. FlattenLoadedXFA анализира шаблона XFA и неговите пакети с данни, оформя структурата и я изгражда наново като полета на AcroForm върху реални PDF страници:

var
  Pdf: THotPDF;
  MappedCount, I: Integer;
  Warnings: TStrings;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.LoadFromFile('dynamic_xfa.pdf');
    MappedCount := Pdf.FlattenLoadedXFA(True);   // True = fields stay editable
    Warnings := Pdf.XFAFlattenWarnings;
    for I := 0 to Warnings.Count - 1 do
      Log('XFA flatten warning: ' + Warnings[I]); // unmapped elements
    Pdf.SaveLoadedDocument('native_acroform.pdf');
    Log(Format('Mapped %d fields', [MappedCount]));
  finally
    Pdf.Free;
  end;
end;

Върнатата стойност и списъкът с предупреждения са важни изходни данни, а не шум при отстраняване на грешки, така че ги запазвайте. Преобразуването по своята същност води до загуба на информация: XFA скриптовете, изчисляемите полета и динамичните подформи (subforms) нямат еквивалент в AcroForm, а списъкът XFAFlattenWarnings описва всеки елемент от шаблона, който не е бил съпоставен. Ако архивирате преобразувания файл без списъка с предупреждения, някой ден може да видите празно поле за крайна сума в архивното копие, без да имате обяснение защо. Флагът Editable определя дали новите полета остават достъпни за попълване. Предайте стойност True, когато потребителите ще продължат да работят с формуляра след това, и заключете стойностите, когато целта е създаване на постоянен архив.

Проверката на преобразуването е отчасти визуална и отчасти структурна â€?имате нужда и от двете части. Структурната част е лесна: уверете се, че броят на полетата съответства на MappedCount. Визуалната част е тази, която улавя сериозните несъответствия. Отворете оригиналния формуляр в настолния Acrobat (все още единственият четец, изпълняващ XFA процеса) до преобразувания файл в стандартен четец и сравнете стойностите и оформлението на поне една попълнена мостра за шаблон. Дата, която XFA е визуализирал като 2026-06-11, може да се окаже в копието на AcroForm като необработена, неформатирана стойност и само визуалната проверка би открила това.

Когато входящите данни са XDP пакет

Не всяка задача започва от готов PDF файл. Понякога получавате XDP пакета самостоятелно, експортиран от инструмент за проектиране на форми или прехвърлен от партньорска система. Методът ApplyXFAAsAcroForm пропуска стъпката за зареждане на файл и прилага пакета директно към текущия документ:

XDPBytes := TFile.ReadAllBytes('benefit-claim.xdp');
MappedCount := Pdf.ApplyXFAAsAcroForm(XDPBytes, True);

Същата група извиквания работи и в обратна посока, за по-редките случаи, когато трябва да генерирате XFA, а не да го обработвате. AddXFAPacket прикачва отделни наименувани пакети (като 'xdp' или 'config'). SetXFADocument добавя цялото съдържание на документа в едно извикване. ClearXFAPackets изтрива регистрацията, за да започнете отначало, а AddXFASignaturePacket вгражда XAdES данни за процеси, които подписват директно XML данните на формуляра. Генерирането на XFA в наши дни е специфична нужда, почти винаги наложена от наследена система, която не приема нищо друго, но когато се изисква по договор, тези извиквания улесняват задачата.

Другото значение на термина „flattenâ€?/h2>

Думата „flattenâ€?често предизвиква объркване, тъй като назовава съвсем друга операция: вграждане на визуалния облик на полетата на AcroForm в потока от съдържание на страницата, докато не останат интерактивни обекти. HotPDF не предлага такъв приложен програмен интерфейс (API) в момента и е по-добре да знаете това сега, отколкото по средата на проекта. Това, което библиотеката предоставя вместо това, е заключване на ниво поле при неговото създаване, подкрепено от правата за достъп до документа (document permissions):

// Lock the value at field creation: read-only text field
Pdf.CurrentPage.AddTextField('CaseNumber', 'BC-2026-0117',
  Rect(50, 700, 220, 720), 0, [ffReadOnly]);

// Belt and suspenders: restrict form filling document-wide
Pdf.ActivateProtection := True;
Pdf.CryptKeyLength := aes256;
Pdf.OwnerPassword := 'records-owner';
Pdf.ProtectOptions := [prPrint, prInformationCopy, prExtractContent];
// fill permission withheld: prFillAnnotations is absent from the set

Бъдете наясно какво точно ви дава това и какво не. Полето, достъпно само за четене (read-only field), все още е обект на формуляр. То се показва в съответния панел на четеца, неговата стойност е четима чрез приложния интерфейс на формата, а инструмент, който пренаписва файла, може да изключи флага за достъп само за четене. Флаговете за права повишават защитата, но зависят от това дали четецът ще реши да ги спазва â€?ограничение, което стандартът ISO 32000-1 описва съвсем ясно. Когато регулатор изисква архивният запис да не съдържа никакви обекти на формуляри, правилното решение с HotPDF е документът да се изгради наново: прочетете стойностите и след това ги изчертайте като стандартно съдържание с TextOut върху нова страница, вместо да използвате флаговете за достъп само за четене. Нещо, което трябва да помните при правата за достъп, е, че CryptKeyLength трябва да бъде зададено преди BeginDoc; останалото е описано в статията за AES-256 шифриране и нива на достъп.

Какво означава XFA за съответствието при архивиране

Стандартите PDF/A и PDF/X отхвърлят напълно XFA структурата. Процесът, захранващ архив по ISO 19005, трябва първо да извърши преобразуване, като редът на операциите е задължителен: зареждане, FlattenLoadedXFA, записване и след това стартиране на генерирането за архивиране или валидиране върху резултата от AcroForm. До не третирате преобразуването като доказателство за съответствие. То коригира модела на формуляра, но оставя шрифтовете, цветовете и метаданните без промяна, така че валидирайте резултата с veraPDF, преди да го одобрите. След като формулярът бъде преобразуван в AcroForm, неговото поведение има свои собствени средства за контрол. JavaScript тригерите, действията за изпращане и скриптовете за валидиране са разгледани в статията за полета на AcroForm и действия в HotPDF.

Приложните интерфейси (API) за регистрация на XFA, преобразуване и формуляри се доставят с компонента HotPDF за Delphi и C++Builder, чиято документация проследява развитието на характеристиките на XFA в последните версии.