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

Експорт и импорт на XFDF в Delphi с PDFium Component

Компонентът PDFium Component за Delphi и Lazarus обменя данни от формуляри и анотации чрез XFDF – XML форматът за обмен на данни, дефиниран от ISO 19444-1. Методът TPdf.ExportXFDF сериализира всяка стойност на поле от формуляр и всяка поддържана анотация в заредения документ в XFDF файл или TStream; методът TPdf.ImportXFDF прочита XFDF документа обратно, пресъздавайки анотациите на съответните страници и прилагайки стойностите на полетата. Този двупосочен процес покрива два работни потока, от които всяко документно приложение в крайна сметка се нуждае: рецензент маркира PDF файл в Acrobat и ви изпраща коментарите за сглобяване, или правило за съвместимост изисква данните от формуляри да съществуват в отделен, подлежащ на одит файл, вместо да бъдат вградени в самия PDF

Поддържа ли PDFium импорт и експорт на XFDF?

Самата библиотека PDFium не поддържа това. Нативният C API изобщо няма XFDF или FDF функции – нищо в неговите публични хедъри не чете или записва тези формати, така че никакво обвиване (wrapping) на кода няма да ви помогне. Поради тази причина PDFium Component имплементира цялото XFDF ядро в Pascal, в отделен модул, който изгражда и анализира XML кода директно върху съществуващия модел за данни на анотации и формуляри в TPdf. Записващият метод генерира XML ръчно, а четецът е ръчно написан синтактичен анализатор, така че функцията не добавя зависимости към Delphi XMLDoc или DOM модулите на FPC и работи идентично под Delphi и Lazarus

Това дизайнерско решение е важно при оценка на алтернативите. Ако сте извиквали чисти FPDF_* функции от Pascal, XFDF е пречка: тази функционалност просто не съществува в DLL файла. Компонентът преодолява това ограничение, като разглежда XFDF като чист проблем на сериализация – събиране на стойностите на полетата и записите на анотации, които обвиващият клас вече знае как да чете, записването им като стандартен XML и извършване на обратния процес при импортиране

Какво съдържа един XFDF файл?

Стандартът ISO 19444-1 дефинира XFDF като XML репрезентация на FDF, и неговото съдържание се разделя на два основни блока. Елементът <fields> пренася йерархичните имена на полета от формуляри и техните стойности – всичко, което потребителят е написал, избрал или отбелязал в AcroForm. Елементът <annots> съдържа анотациите: методът TPdf.ExportXFDF поддържа 18 подтипа – text, highlight, underline, strikeout, squiggly, line, circle, square, caret, polygon, polyline, stamp, ink, freetext, fileattachment, sound, link и redact. Анотациите тип Widget нарочно липсват в <annots>, тъй като техните данни се пренасят в <fields>, а вътрешните за четците подтипове като Popup не са част от дефинициите на XFDF

За осигуряване на съвместимост, записът TPdfAnnotation бе разширен с метаданните, които XFDF очаква: Name (уникалният идентификатор NM), Subject, ModificationDate, CreationDate, Icon, Opacity, крайни точки на линии, върхове на полигони и полилинии, както и пътища на жестове с мастило. Всяко ново поле е придружено от булево свойство Has*, така че код, класически писан за по-ранни версии, продължава да се компилира и да генерира същите данни – незададено поле никога не се записва. Ако вече създавате маркировки програмно, същият запис, който използвате за текстови анотации с QuadPoints, сега съдържа всичко необходимо за XFDF

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

Едно-единствено извикване обработва целия документ. Заредете PDF файла с активирано попълване на формуляри, извикайте ExportXFDF, и компонентът ще обходи всяка страница, ще събере стойностите на полетата и анотациите и ще ги запише в XML. Върнатата стойност е броят на записаните UTF-8 байтове, което служи за бърза проверка в системните дневници

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;

И двата процеса поддържат претоварване с TStream, така че няма нужда от използване на временни файлове на диска. Експортирането в TMemoryStream е най-подходящият метод, когато XFDF е насочен към HTTP отговор, база данни или компресиран архив

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;

Как ImportXFDF обединява коментарите обратно в документа?

Импортирането е етапът, в който процесът на рецензиране приключва. Колега анотира договор в Acrobat, експортира коментарите като XFDF и ви изпраща файл от няколко килобайта вместо пълно копие на PDF документа. Методът TPdf.ImportXFDF анализира файла, създава всяка анотация на страницата, указана в атрибута page, и записва стойностите в съответните widget анотации. Функцията връща общия брой на приложените полета и анотации, така че интерфейсът може да потвърди какво точно е заредено

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

Синтактичният анализатор е ръчно написан скенер за тагове, разработен за висока толерантност: непознати елементи се пропускат, инструкции за обработка, декларации DOCTYPE и коментари се игнорират, непрепоръчителни флагове се премахват, а стойностите на атрибутите се декодират през пълния набор от XML препратки, включително UTF-16 сурогатни двойки. XFDF файлове, създадени от Acrobat, уеб четци или други инструменти, се обединяват директно. След като анотациите са в документа, цикълът на рецензиране продължава с функциите за отговори и статуси, описани в статията за изграждане на процес за рецензиране на анотации с PDFium Component, а импортираните стойности на полета участват в логиката за навигация, разгледана в навигация на полета на формуляри

Защо десетичните разделители са важни за XFDF?

Всяка анотация в XFDF се позиционира чрез координатни низове – rect="70.5,540,200,560" и подобни – и стандартът ISO 19444-1 изисква използването на точка за десетичен разделител. Подразбиращото се форматиране на числа с плаваща запетая в Delphi следва регионалните настройки на Windows, така че на немска или френска система функцията FloatToStr превръща 70.5 в 70,5, което поврежда разделения със запетаи списък с координати. Компонентът PDFium Component нормализира всяко число, което записва: десетичният разделител се заменя с точка и излишните нули се премахват, така че експортираният файл е идентичен по байтове, независимо дали е генериран на en-US или de-DE система. Ако някога сте дебъгвали PDF инструмент, който работи в офиса ви, но дава грешки при клиенти в Европа, сте се сблъсквали точно с този клас грешки – добре е да знаете, че компонентът се справя с него автоматично

Какви са ограниченията при двупосочния процес?

Две основни ограничения трябва да се посочат ясно. Първо, XFDF пренася данните за анотациите, а не техния визуален облик (appearance streams не са част от формата), така че приемащият четец регенерира изгледа от свойствата. Маркиране (highlight) или квадрат се изобразяват коректно навсякъде, но печат с персонализиран изглед ще се върне към дизайна, който целевият четец рисува за съответното име. Второ, вграденото PDFium API поддържа задаване на стойности за низове, но не и за числови параметри или геометрия на пътища на анотации, така че Opacity, крайни точки на линии, върхове и жестове с мастило се поддържат само при експорт: компонентът ги запазва коректно при запис на XFDF, но не може да ги запише обратно в PDF при импортиране. Стойности на полета, съдържание, цветове, размери (rects), QuadPoints, дати и метаданни за идентичност се поддържат напълно двупосочно

За бърз старт демонстрационният проект XfdfLab се доставя с примерите за Delphi, C++Builder и Lazarus и свързва двете извиквания с бутони над всеки отворен PDF файл. Поддръжката на XFDF е включена в текущата версия на PDFium Component, заедно с програмните интерфейси за анотации и формуляри, на които тя се базира