Технічна стаття

Імпорт та експорт анотацій PDF як XFDF в Delphi

HotPDF експортує та імпортує анотації PDF як XFDF за допомогою двох функцій, що працюють із поточним завантаженим документом: ExportLoadedAnnotationsToXFDF та ImportLoadedAnnotationsFromXFDF. XFDF — це XML-формат обміну анотаціями, стандартизований як ISO 19444-1. Ця пара функцій дозволяє програмі на Delphi або C++Builder передавати коментарі в Acrobat або сторонній інструмент рецензування та отримувати результати розмітки назад, без потреби переписувати вміст сторінок, на яких розташовані анотації.

Уявіть два напрямки використання цієї технології. Рецензент відкриває створений вами звіт в Acrobat, малює червону стрілку на зміщеному малюнку, обводить неправильний підсумок та пише замітку на полях, а потім експортує коментарі в невеликий файл XFDF. Або навпаки: ваша програма сама генерує розмітку, і вам потрібно надіслати її користувачу, який не використовує HotPDF. В обох випадках анотації передаються як XML, зрозумілий обом сторонам, а сторінки PDF залишаються незмінними на рівні байтів.

Яка різниця між FDF та XFDF?

FDF та XFDF містять однакові дані у двох різних синтаксисах, і ця різниця є важливою при виборі формату для передачі в інший інструмент. FDF — це старіший формат Forms Data Format, визначений у самій специфікації PDF: він використовує синтаксис об'єктів PDF, тому файл FDF виглядає як спрощений PDF і потребує спеціального парсера для читання. XFDF — це представлення тих самих даних у форматі XML, стандартизоване незалежно як ISO 19444-1, що дозволяє відкривати, порівнювати чи генерувати його за допомогою будь-якої бібліотеки XML на будь-якій платформі. Обидва формати можуть містити значення полів форм у дереві <fields> (згідно з розділом 6.3 стандарту ISO 19444-1) та анотації в дереві <annots>. HotPDF розділяє ці завдання, спрямовуючи дані форм через ExportLoadedFormToXFDF, а ExportLoadedAnnotationsToXFDF залишаючи для <annots>. Якщо ви обмінюєтеся коментарями з веб-сервісом, сервером рецензування на Java або скриптом, XFDF позбавляє іншу сторону необхідності вбудовувати парсер PDF.

Як експортувати анотації PDF як XFDF в Delphi?

HotPDF експортує анотації, обходячи кожну сторінку завантаженого документа, створюючи по одному елементу XFDF для кожної підтримуваної анотації та повертаючи кількість записаних анотацій. Спочатку завантажте PDF, а потім викличте ExportLoadedAnnotationsToXFDF із цільовим шляхом. Повернуте ціле число — це кількість серіалізованих анотацій. Результат менше або рівний нулю означає, що нічого не було експоновано і файл не створювався, що вказує на відсутність у документі анотацій підтримуваних підтипів.

var
  Pdf: THotPDF;
  Written: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('report-reviewed.pdf', '') > 0 then
    begin
      // Write one XFDF element per supported annotation on every page
      Written := Pdf.ExportLoadedAnnotationsToXFDF('comments.xfdf');
      if Written <= 0 then
        ShowMessage('No supported annotations were found');
    end;
  finally
    Pdf.Free;
  end;
end;

Згенерований XFDF — це простий для читання XML. HotPDF записує кореневий елемент <xfdf> у просторі імен ISO 19444-1, контейнер <annots> та один дочірній елемент для кожної анотації з її індексом сторінки (з 0), кольором та геометрією як атрибутами або дочірніми елементами. Лінія з жовтою заливкою та відкритою стрілкою поруч із заповненим багатокутником серіалізується так:

<?xml version="1.0" encoding="UTF-8"?>
<xfdf xmlns="http://ns.adobe.com/xfdf/">
  <annots>
    <line page="0" start="72,700" end="220,700"
          color="#FF0000" interior-color="#FFFF00"
          head="OpenArrow" tail="None">
      <contents-richtext>Baseline looks off</contents-richtext>
    </line>
    <polygon page="0" color="#0000FF" interior-color="#CCE5FF">
      <vertices>72,120;180,120;180,200;72,200</vertices>
    </polygon>
  </annots>
</xfdf>

Як підтипи анотацій відображаються в елементи XFDF

Кожен підтип анотації відображається у відповідний елемент ISO 19444-1 зі своїми геометричними правилами, і HotPDF слідує цим структурам, а не створює власні. Лінійні анотації містять атрибути start та end, які зберігають координати двох кінцевих точок із масиву L анотації, а стилі закінчення ліній LE стають атрибутами head та tail. Анотації багатокутників (polygon) та ламаних ліній (polyline) переносять свій список точок у дочірній елемент <vertices> як пари x,y, розділені крапкою з комою, а не в атрибут, оскільки переглядач, який очікує дочірній елемент, просто пропустить точки, записані в інших місцях. Малюнки від руки (ink annotations), які можуть містити кілька окремих ліній, містять елемент <inklist> з дочірніми елементами <gesture> для кожного штриха, завдяки чому підпис із кількох штрихів зберігається як окремі лінії, а не як одна суцільна пляма.

Стилі розширеного тексту, кольору та меж зберігаються разом із геометрією. Форматований текст нотатки записується як дочірній елемент <contents-richtext>; внутрішня заливка, яку PDF зберігає в масиві IC (колір всередині кола, квадрата, багатокутника або стрілки лінії, а також заливка області редагування), передається як атрибут interior-color у форматі #RRGGBB; а ширина межі, шаблон штрихів та ефект «хмари» відображаються в атрибути width, dashes, style та intensity. Завдяки цьому виділена область із ефектом хмари відображатиметься так само і на іншій стороні. HotPDF також зберігає спливаючі вікна, прикріплені до анотацій розмітки, імпортуючи їхню геометрію та стан (відкрито/закрито) у словник Popup анотації, а також переносить стани перегляду та перевірки текстових анотацій, завдяки чому рецензований документ зберігає не лише графіку, а й метадані робочого процесу.

Імпорт XFDF назад у завантажений документ

HotPDF імпортує XFDF, розбираючи XML, створюючи нову анотацію для кожного елемента за допомогою NewLoadedAnnotation, прикріплюючи її до вказаної сторінки та повертаючи кількість доданих анотацій. Робочий процес симетричний експорту: завантажте базовий PDF, викличте ImportLoadedAnnotationsFromXFDF із файлом рецензента, а потім збережіть завантажений документ для збереження нової розмітки. Якщо файл відсутній або XML не вдалося розібрати, функція повертає нуль, а завантажений документ залишається без змін.

var
  Pdf: THotPDF;
  Added: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('report.pdf', '') > 0 then
    begin
      Added := Pdf.ImportLoadedAnnotationsFromXFDF('comments.xfdf');
      if Added > 0 then
        Pdf.SaveLoadedDocument('report-annotated.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Оскільки кожен елемент XFDF вказує свій власний індекс сторінки, анотації потрапляють на потрібні сторінки, навіть якщо ви імпортуєте кілька файлів послідовно. Це дозволяє об'єднувати коментарі від різних рецензентів в один документ перед його збереженням. Наступний приклад демонструє об'єднання коментарів від двох рецензентів в один майстер-файл. Для створення та редагування об'єктів анотацій у коді див. статтю про те, як створювати та редагувати об'єкти анотацій PDF безпосередньо з Delphi.

var
  Pdf: THotPDF;
  Total, I: Integer;
  Files: array[0..1] of string;
begin
  Files[0] := 'alice-comments.xfdf';
  Files[1] := 'bob-comments.xfdf';
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('master.pdf', '') > 0 then
    begin
      Total := 0;
      for I := Low(Files) to High(Files) do
        Inc(Total, Pdf.ImportLoadedAnnotationsFromXFDF(Files[I]));
      if Total > 0 then
        Pdf.SaveLoadedDocument('master-merged.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Що переноситься без проблем, а що ні

HotPDF підтримує передачу підтипів анотацій, передбачених стандартом ISO 19444-1, і навмисно пропускає інші типи, щоб уникнути некоректного інтерпретування переглядачами. Підтримуваний набір охоплює основні типи розмітки, що використовуються при рецензуванні: текстові нотатки, вільний текст, лінії, квадрати, кола, багатокутники, ламані лінії, чотири типи виділення тексту (підсвічування, підкреслення, закреслення та хвиляста лінія), штампи, малюнки від руки та знаки вставки (caret), а також вкладення файлів, звуки, області редагування та посилання — всього вісімнадцять підтипів. Анотація, підтип якої не входить до цього списку, пропускається при експорті і не враховується у лічильнику повернутого значення.

Особливістю є форматований текст (rich text). HotPDF зберігає елемент <contents-richtext>, тому стилізований текст і звичайний вміст передаються, але XFDF містить лише розмітку тексту та стилів коментаря, а не готовий потік вигляду. Тому цільова програма сама перемальовує спливаюче вікно зі своїми шрифтами та макетом, а не копіює точний вигляд до пікселя. Сприймайте цей двосторонній обмін як передачу вмісту та наміру рецензента, а не точного екранного відображення. Якщо ваш стилізований вміст знаходиться в даних форми XFA, а не в потоках анотацій, діють інші правила, описані в статті про те, як HotPDF обробляє XFA exData, розширений текст та гіперпосилання.

Обробка на рівні символів є суворішою, ніж здається, і це важливо для надійності. HotPDF застосовує правила екранування стандарту ISO 19444-1 розділу 5.8.2 при записі тексту, кодуючи спеціальні символи XML та керівні байти, тому коментар, який містить амперсанд, кутові дужки або розрив рядка, створює коректний XML, сумісний з будь-ким парсером. Ці ж правила дзеркально застосовуються при імпорті. Завдяки цьому примітка, скопійована з електронної таблиці разом із розділовими знаками, повертається неушкодженою і не порушує структуру файлу.

Обмін анотаціями — це лише одна з функцій API для роботи із завантаженими документами, і вона легко поєднується з іншими можливостями: імпортуйте XFDF рецензента, коригуйте сторінки або редагуйте метадані документа, спрощуйте структуру чи міняйте права доступу, а потім експортуйте оновлений XFDF для наступного етапу. Усе це постачається у складі компонента HotPDF Component для Delphi та C++Builder разом із повним описом підтипів анотацій та функцій обміну даними форм XFDF.