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

Анотації PDF у Delphi з HotPDF: типи та прямокутники

Анотація не є вмістом сторінки. Коли ви викликаєте TextOut або малюєте прямокутник, ці позначки стають частиною потоку вмісту сторінки, вшитими у байти, які малює рендерер. Анотація ж є окремим словником, що кріпиться до сторінки через її масив /Annots, зі своїм власним прямокутником, власним зовнішнім виглядом та власним життєвим циклом. Читач може відкрити її, перемістити, сховати або видалити, не зачіпаючи жодного гліфа базової сторінки. Таке розділення є головною причиною існування анотацій, і це також джерело двох речей, які найперше дивують людей: де саме опиняється анотація, та як вона виглядає, коли за неї береться певний переглядач

HotPDF відкриває підтипи анотацій ISO 32000 через родину викликів AddXxxAnnotation для об'єкта сторінки. Всі вони мають однакову форму: прямокутник, який фіксує анотацію на сторінці в користувацькому просторі PDF, деяке корисне навантаження (текст, назва штампа, пара точок) та колір. Якщо правильно задати прямокутник, більшу частину роботи вже зроблено. Залишається лише знати, які підтипи несуть свій власний зовнішній вигляд, а які покладаються на переглядача для їх відтворення

Сторінка PDF, створена за допомогою HotPDF, на якій показані значки текстових нотаток, блоки вільного тексту, квадратні та лінійні позначки, а також штампи затвердження, розміщені на сторінці
Одна сторінка, що містить відразу кілька підтипів анотацій: текстові нотатки, вільний текст, геометричні позначки та штампи

Прямокутник є анотацією, а не текстом

Кожен виклик анотації приймає TRect, і цей прямокутник означає дещо інше, ніж координати, які ви передаєте в TextOut. Для текстової нотатки це активна зона для кліку, невелика ділянка, де розташований значок нотатки і де клік відкриває коментар. Для квадрата або блоку вільного тексту це видима межа позначки. Для штампа це рамка, в яку масштабується зображення штампа. Ці числа є точками користувацького простору PDF, які вимірюються від нижнього лівого кута сторінки зі збільшенням координати Y вгору, як і у всьому HotPDF

Текстова нотатка є найлегшим підтипом. Ви задаєте їй основний текст, прямокутник для значка, прапорець, чи відкривається вона за замовчуванням, назву значка та колір

Pdf.CurrentPage.AddTextAnnotation(
  'Reviewer: confirm the totals on this line before sign-off.',
  Rect(120, 700, 140, 720),   // icon hotspot, ~20pt square
  False,                      // closed until the reader clicks it
  taComment,                  // bubble icon
  clBlue);

Прямокутник тут навмисно невеликий, близько двадцяти точок зі сторони, оскільки текстова нотатка залишається лише значком, доки хтось на неї не клікне. Якщо зробити прямокутник великим, ви не отримаєте велику нотатку; ви отримаєте занадто велику зону для кліку зі значком, прикріпленим до одного кута. Прапорець Open контролює, чи показуватиметься спливаюче вікно під час завантаження документа. Якщо встановити True для кількох нотаток, вони накладатимуться одна на одну та на вміст сторінки, тому залишайте цей параметр лише для тієї нотатки, яку читач дійсно має побачити негайно

Назва значка походить від THPDFTextAnnotationType, що відповідає стандартним значкам нотаток: taComment, taKey, taNote, taHelp, taParagraph, taNewParagraph та taInsert. Значок є єдиною річчю, яку змінює цей тип. Він не змінює поведінку, і варто знати, що не всі переглядачі відмальовують усі сім; найбезпечнішими варіантами для старих і нових читалок є taComment, taNote та taHelp

Вільний текст пишеться на сторінці, але залишається анотацією

Анотація з вільним текстом виглядає як вміст, оскільки текст видно без кліку, і він розташований у своєму прямокутнику як підпис. Але це все ще анотація з усіма можливостями відділення, що саме те, що вам потрібно для штампа рецензування або мітки чернетки, яку хтось має мати змогу згодом видалити. У сигнатурі виклику значок та прапорець відкриття замінено на значення вирівнювання

Pdf.CurrentPage.AddFreeTextAnnotation(
  'DRAFT - not for distribution',
  Rect(200, 210, 400, 235),   // the box the text is laid into
  ftCenter,                   // ftLeftJust / ftCenter / ftRightJust
  clRed);

Тут прямокутник має більше значення, ніж для текстової нотатки, оскільки текст переноситься та вирівнюється всередині нього. Якщо розмір блоку буде занадто малим по висоті, текст обріжеться по нижньому краю; якщо він буде занадто вузьким, перенесення відбудеться в непередбачених місцях. Вирівнювання береться з THPDFFreeTextAnnotationJust і має лише три значення. Оскільки вільний текст є анотацією-розміткою, читач, який відкриває файл у редакторі, може виділити його, перемістити або видалити як єдине ціле. Саме ця відмінність визначає, чи варто використовувати вільний текст, чи просто намалювати слова за допомогою TextOut. Якщо мітка має бути постійною, намалюйте її. Якщо вона є редакторською та призначена для зняття, зробіть її анотацією

Геометричні та лінійні позначки для вказування на об'єкти

Квадрати, кола та лінії є розміткою, яку ви використовуєте, щоб вказати на область, а не описувати її словами. AddCircleSquareAnnotation охоплює дві форми блоків через THPDFCSAnnotationType зі значенням csCircle або csSquare, при цьому прямокутник задає межі фігури

// A box drawn around a figure that needs attention
Pdf.CurrentPage.AddCircleSquareAnnotation(
  'Check this region against the source data',
  Rect(50, 300, 120, 360),
  csSquare,
  clGreen);

// A line, given two points rather than a rectangle
var
  StartPt, EndPt: THPDFCurrPoint;
begin
  StartPt.X := 130; StartPt.Y := 360;
  EndPt.X   := 250; EndPt.Y   := 320;
  Pdf.CurrentPage.AddLineAnnotation(
    'Points from the note to the figure',
    StartPt, EndPt,
    clBlue);
end;

Зверніть увагу, що лінійна анотація порушує шаблон з прямокутником: вона приймає два записи THPDFCurrPoint, початок і кінець, оскільки лінія визначається її кінцевими точками, а не обмежувальною рамкою. Колір задає колір лінії. Якщо вам потрібні наконечники стрілок, HotPDF має перевантажені версії AddLineAnnotation, які приймають стилі закінчення ліній, але звичайна форма з трьома аргументами малює просту лінію, що зазвичай і потрібно для вказівника

Підтипи текстової розмітки працюють з областю, яку ви вже розмістили. AddHighlightAnnotation приймає прямокутник, необов'язковий вміст та колір, який за замовчуванням є жовтим, і забарвлює область так само, як це робить маркер. Ця анотація призначена для розміщення над справжнім текстом, тому прямокутник має відповідати межам слів, які ви намалювали. Це означає, що зазвичай ви обчислюєте його з тих самих координат, які передали в TextOut, а не вгадуєте

Штампи залежать від переглядача для їх відтворення

Анотація штампа найчастіше виглядає по-різному у різних читачів, і варто розуміти причину цього. AddStampAnnotation задає ім'я стандартного штампа через THPDFStampAnnotationType, зі значеннями на кшталт satApproved, satConfidential, satFinal, satDraft та satForComment

Pdf.CurrentPage.AddStampAnnotation(
  'Approved for release on review',
  Rect(50, 400, 200, 440),
  satApproved,
  clGreen);

Назва штампа є запитом. Формат PDF визначає набір стандартних назв штампів, але не графіку, що стоїть за ними. Тому кожен переглядач постачає власне зображення для "APPROVED" або "CONFIDENTIAL", а деякі взагалі нічого не відмальовують для назв, які вони не розпізнають. Прямокутник контролює рамку, в яку масштабується графіка, а колір є підказкою, яку переглядач може врахувати або проігнорувати. Якщо штамп має виглядати ідентично всюди, надійним шляхом є відмова від стандартного штампа: намалюйте позначку самостійно за допомогою TextOut та викликів малювання, або розмістіть її як анотацію вільного тексту, зовнішнім виглядом якої ви керуєте. Використовуйте стандартний штамп тоді, коли ви хочете отримати звичний для переглядача вигляд і готові миритися з варіаціями

Вкладення файлів мають таку саму структуру з прямокутника та корисного навантаження. AddFileAttachmentAnnotation приймає опис, шлях до файлу для вбудовування, прямокутник для значка скріпки та колір. Файл міститься всередині PDF, а значок є ручкою, яку читач використовує для його вилучення

Чим анотації відрізняються від полів AcroForm

Плутанина, яка забирає найбільше часу, полягає у ставленні до анотації як до поля форми. Обидві сутності кріпляться до сторінки через /Annots, і поле форми фактично є спеціальним підтипом анотації (віджетом). Ось чому вони виглядають схожими. Але вони не є взаємозамінними. Поле форми містить значення, має назву, бере участь у порядку переходу за клавішею Tab, і його можна надіслати, скинути або використати у скриптах; ви створюєте їх за допомогою викликів AddTextField, AddCheckBox та AddPushButton, а не викликів анотацій на поточній сторінці. Анотація-розмітка містить коментар або фігуру, не має значення для надсилання, і стає неправильним інструментом у той момент, коли вам потрібно зібрати введені дані

Практичний тест є простим. Якщо користувач має друкувати, вибирати або натискати, і документ повинен це запам'ятати, вам потрібне поле AcroForm. Якщо ви залишаєте нотатку, позначаєте область або ставите штамп про статус, який подорожує разом з файлом, але не є даними, вам потрібна анотація. Їх змішування створює документи, які виглядають правильно, але поводяться неправильно: "поле", яке ніхто не може заповнити, або коментар, що зникає при скиданні форми. Інтерактивна частина з типами полів, перевіркою та діями надсилання є окремою темою, яка розглядається в інструкції з полів та дій AcroForm

Збирання сторінки до купи

Частини збираються так само, як і решта HotPDF. Налаштуйте властивості документа, викличте BeginDoc, намалюйте будь-який потрібний вміст сторінки за допомогою викликів тексту та графіки, додайте анотації зверху і закрийте за допомогою EndDoc. Анотації кріпляться до CurrentPage, тому після AddPage вони потрапляють на нову сторінку, і нотатка, яку ви призначали для першої сторінки, тихо з'явиться на другій, якщо ви додасте її після розриву

Pdf := THotPDF.Create(nil);
try
  Pdf.FileName := 'annotated.pdf';
  Pdf.Compression := cmFlateDecode;
  Pdf.FontEmbedding := True;
  Pdf.BeginDoc;

  Pdf.CurrentPage.SetFont('Arial', [], 11);
  Pdf.CurrentPage.TextOut(50, 740, 0, 'Quarterly figures, draft for review');

  Pdf.CurrentPage.AddTextAnnotation(
    'Confirm the totals before sign-off.',
    Rect(50, 720, 70, 740), False, taComment, clBlue);
  Pdf.CurrentPage.AddFreeTextAnnotation(
    'DRAFT', Rect(450, 720, 540, 745), ftCenter, clRed);
  Pdf.CurrentPage.AddStampAnnotation(
    'For comment', Rect(50, 660, 180, 695), satForComment, clGreen);

  Pdf.EndDoc;
finally
  Pdf.Free;
end;

Ще одна корисна звичка, коли результат виглядає неправильно: відкрийте файл у кількох переглядачах, перш ніж вирішити, що код не працює. Штампи та більш рідкісні значки нотаток зазвичай є головними винуватцями, і оскільки анотація є запитом до читача, а не намальованими пікселями, різниця між Acrobat та легковаговим переглядачем часто є не помилкою у вашому виклику, а особливістю реалізації специфікації

Показані тут виклики анотацій є частиною компонента HotPDF для Delphi та C++Builder