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

Аннотации 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 определяет набор стандартных названий штампов, но не иллюстрации, стоящие за ними, поэтому каждая программа просмотра поставляет собственную визуализацию «УТВЕРЖДЕНО» или «КОНФИДЕНЦИАЛЬНО», а некоторые вообще ничего не отображают для названий, которые они не распознают. Прямоугольник управляет полем, в которое масштабируется иллюстрация, а цвет — это подсказка, которую программа просмотра может учесть, а может и нет. Если штамп должен выглядеть одинаково везде, надежный путь — это вообще не стандартный штамп: нарисуйте отметку самостоятельно с помощью 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