Внедрение панели чтения PDF в бизнес-приложение Delphi не требует написания цикла рендеринга, движка прокрутки или модели масштабирования. losLab PDF Library поставляется с TPDFlibViewer, элементом управления VCL, который открывает документ одним вызовом и предоставляет выделение текста, панорамирование, масштабирование рамкой, снимки экрана, подсветку областей, подсветку результатов полнотекстового поиска, взаимодействие с аннотациями и печать в качестве встроенного поведения. В этой статье рассматриваются пять шагов, которые превратят пустую форму в работающий просмотрщик: размещение элемента управления, привязка готовых действий, добавление подсветки поиска, обработка кликов по аннотациям и печать
Зачем использовать элемент управления просмотрщиком вместо написания цикла рендеринга?
Аргумент разделения на уровни идет первым, так как он объясняет, что именно вы приобретаете. Отрисовка страницы в растровое изображение — это простые 20 процентов; остальные 80 процентов — это все, что ее окружает: кэш растровых изображений страниц с лимитом вытеснения, макет прокрутки страниц разного размера, режимы масштабирования, которые пересчитываются при изменении размера, хит-тестирование, сопоставляющее пиксель мыши с координатами страницы при любом масштабе и повороте, а также планирование перерисовки без мерцания. TPDFlibViewer берет все это на себя и делегирует растровую работу тому же движку, который описан в статье о многодвижковом рендеринге, поэтому уровень управления и уровень рендеринга остаются независимо заменяемыми. Код вашей формы взаимодействует с прокручиваемым элементом управления Windows, а не с рендерером
Как встроить PDF-просмотрщик в приложение Delphi?
Метод TPDFlibViewer.LoadFromFile открывает документ через собственный внутренний экземпляр TPDFlib элемента управления и возвращает 1 в случае успеха, поэтому минимальный просмотрщик состоит из вызова конструктора, назначения Parent и одной загрузки. Если у вас уже есть экземпляр TPDFlib в другом месте приложения, AttachLibrary вместо этого отобразит выбранный в нем документ, при этом владение останется на вашей стороне
uses PDFlibViewer;
procedure TMainForm.FormCreate(Sender: TObject);
begin
FViewer := TPDFlibViewer.Create(Self);
FViewer.Parent := Self;
FViewer.Align := alClient;
if FViewer.LoadFromFile('quarterly-report.pdf', '') = 1 then
FViewer.ViewerMode := vmHand; // pan by dragging; a plain click still follows links
end;
Свойство ViewerMode определяет действие мыши из пяти возможных значений: vmSelect — это инструмент выделения текста по умолчанию, vmHand выполняет панорамирование путем перетаскивания с курсором-рукой, в то время как обычный клик по-прежнему переходит по ссылкам, vmZoom — это масштабирование рамкой, которое увеличивает выделенную область во весь экран, vmSnapshot копирует выделенную область в буфер обмена (с событием OnSnapshot и переключателем SnapshotToClipboard), а vmHighlight рисует постоянную полупрозрачную подсветку областей. Инструмент подсветки заслуживает отдельного упоминания координат: области хранятся в пользовательском пространстве страницы, а не в клиентских пикселях, поэтому метки AddHighlightRegion сохраняются при масштабировании, повороте вида и переходе по страницам без каких-либо дополнительных усилий с вашей стороны. В повернутом виде направление перетаскивания может даже идти против осей страницы, что дает отрицательную ширину или высоту после обратного преобразования поворота — элемент управления нормализует прямоугольник за вас
Как подключить панель инструментов PDF в Delphi без связующего кода?
Модуль PDFlibViewerActions полностью устраняет необходимость в связующем коде для панели инструментов. Он предоставляет двадцать готовых наследников TAction, охватывающих навигацию по страницам (TPDFlibViewerFirstPage, TPDFlibViewerPriorPage, TPDFlibViewerNextPage, TPDFlibViewerLastPage), масштабирование (TPDFlibViewerZoomIn, TPDFlibViewerZoomOut, TPDFlibViewerFitWidth, TPDFlibViewerFitPage, TPDFlibViewerActualSize), поворот вида (TPDFlibViewerRotateClockwise, TPDFlibViewerRotateAntiClockwise), печать, диалоги открытия и сохранения файлов, копирование выделенного текста и по одному переключателю для каждого инструмента мыши (TPDFlibViewerSelectMode, TPDFlibViewerHandMode, TPDFlibViewerMarqueeZoomMode, TPDFlibViewerSnapshotMode, TPDFlibViewerHighlightMode). Каждое действие указывает на элемент управления через опубликованное свойство Viewer и поддерживает собственное состояние: каждое действие отключается, когда документ не открыт, действия навигации отслеживают положение страницы, копирование отслеживает наличие выделенного фрагмента, а действия режима и соответствия синхронизируют свое отмеченное состояние с активным инструментом и режимом масштабирования. В режиме разработки все они регистрируются в категории losLab PDF редактора Action List; в рантайме те же классы занимают по три строки каждый
uses System.Actions, Vcl.ActnList, PDFlibViewer, PDFlibViewerActions;
procedure TMainForm.BuildToolbar;
var
NextPage: TPDFlibViewerNextPage;
FitWidth: TPDFlibViewerFitWidth;
HandMode: TPDFlibViewerHandMode;
begin
NextPage := TPDFlibViewerNextPage.Create(Self);
NextPage.ActionList := ActionList1;
NextPage.Viewer := FViewer;
btnNext.Action := NextPage;
FitWidth := TPDFlibViewerFitWidth.Create(Self);
FitWidth.ActionList := ActionList1;
FitWidth.Viewer := FViewer;
btnFitWidth.Action := FitWidth;
HandMode := TPDFlibViewerHandMode.Create(Self);
HandMode.ActionList := ActionList1;
HandMode.Viewer := FViewer;
btnHand.Action := HandMode;
end;
При создании собственной регистрации действий поверх этого шаблона следует знать об одной ловушке на уровне области видимости модуля. Функция RegisterActions объявлена в System.Actions, а не в Vcl.ActnList, и ошибка компилятора, которую вы получите, если в секции uses указан только Vcl.ActnList, вводит в заблуждение: литерал массива классов [TAction1, TAction2, ...] начинает парситься как множество (set), порождая цепочку ошибок E2010 Incompatible types: 'Integer' and 'class of ...', которые выглядят как несоответствие типов в ваших классах действий. Решением является добавление одного модуля в uses, но диагностическое сообщение указывает куда угодно, кроме него. Для панелей масштабирования свойство TPDFlibViewer.EffectiveZoomPercent сообщает о реально действующем масштабе — включая вычисленный коэффициент в режимах заполнения по ширине и по странице — поэтому шаг увеличения может основываться на реальном текущем увеличении, а не на последнем присвоенном вами значении
Подсветка результатов поиска и переворот координат, стоящий за ней
Метод HighlightSearchHits ищет запрос по всему документу и рисует полупрозрачную полосу над каждым совпадением, возвращая количество совпадений; методы ClearSearchHighlights, SearchHighlightCount и свойство SearchHighlightColor дополняют этот функционал. Поскольку прямоугольники совпадений хранятся в пространстве страницы и проецируются заново при каждой отрисовке, полосы остаются привязанными к тексту, когда пользователь прокручивает, масштабирует или поворачивает вид. Если вы используете сопутствующий компонент TPDFlibSearchPanel, его метод RunSearch автоматически рисует те же полосы в прикрепленном просмотрщике благодаря свойству HighlightInView, которое включено по умолчанию
procedure TMainForm.btnSearchClick(Sender: TObject);
var
Hits: Integer;
begin
FViewer.SearchHighlightColor := clYellow;
Hits := FViewer.HighlightSearchHits(edtQuery.Text, False); // case-insensitive
StatusBar1.SimpleText := Format('%d matches', [Hits]);
end;
Тонкость и причина, по которой стоит предпочесть встроенный путь самостоятельному расчету на основе необработанных результатов поиска, заключается в преобразовании систем координат. Текстовый движок возвращает прямоугольники совпадений в координатах, зависящих от настройки Origin библиотеки: при Origin 0 или 3 ось Y направлена снизу вверх, как в собственном пространстве пользователя PDF, а при Origin 2 или 3 ось X отсчитывается от правого края. Наложение просмотрщика работает в пространстве страницы сверху вниз и слева направо, поэтому каждый прямоугольник совпадения должен быть перевернут (Y = PageHeight - Bottom, а где применимо — X = PageWidth - Right) перед сохранением. Использование возвращенного верхнего края "как есть" при начале координат по умолчанию нарисует подсветку в зеркально отраженном по вертикали положении на странице. TPDFlibViewer выполняет это преобразование в соответствии с активным началом координат при записи каждого совпадения, а затем применяет прямое отображение поворота при отрисовке, благодаря чему полосы располагаются правильно под любым углом. Сама полупрозрачность использует Windows API AlphaBlend с постоянным значением source alpha, поскольку холст VCL не имеет собственной поддержки альфа-смешивания. Базовые API текстового поиска и геометрии подробно описаны в статье о поиске текста и перечислении элементов страницы
Клики по аннотациям, всплывающие подсказки и экспорт вложений
TPDFlibViewer рассматривает аннотации как интерактивные объекты первого класса. Клик по аннотации, не являющейся ссылкой, вызывает событие OnAnnotClick с номером страницы, индексом аннотации (начиная с 1) и строкой подтипа; метод AnnotAtPagePoint программно выполняет аналогичный хит-тест и пропускает сопутствующие всплывающие аннотации Popup, благодаря чему вы всегда получаете именно ту аннотацию, которую видит пользователь. Наведение курсора на аннотацию показывает ее содержимое (Contents) в качестве подсказки элемента управления с помощью переключателя AnnotHints, который включен по умолчанию и работает совместно со стандартным свойством VCL ShowHint. Для аннотаций типа FileAttachment метод SaveAnnotAttachmentToFile записывает встроенный файл на диск и возвращает 1 в случае успеха, что позволяет реализовать рабочий процесс сохранения по клику буквально в несколько строк
procedure TMainForm.ViewerAnnotClick(Sender: TObject; APage, AnnotIndex: Integer;
const Subtype: WideString);
begin
if Subtype = 'FileAttachment' then
if FViewer.SaveAnnotAttachmentToFile(APage, AnnotIndex,
'C:\Temp\attached-invoice.xml') = 1 then
StatusBar1.SimpleText := 'Attachment saved';
end;
Печать и ограничения, которые следует учитывать
Печать не требует отдельной интеграции. Метод PrintDoc показывает стандартное диалоговое окно печати Windows (выбор принтера, диапазон страниц, копии, сопоставление) и передает задачу движку печати библиотеки, в то время как PrintDoc(False) отправляет весь документ на принтер по умолчанию вообще без пользовательского интерфейса. Метод PrintDocRange работает полностью программно с явным указанием имени принтера, начальной и конечной страниц, количества копий и флага сопоставления, что является подходящей точкой входа для пакетных или запускаемых сервером заданий печати, управляемых из того же экземпляра просмотрщика
Заслуживают честного упоминания два ограничения. Во-первых, TPDFlibViewer — это элемент управления Windows VCL; слой просмотрщика исключен из сборок NOVCL, поэтому консольные службы и не-VCL целевые платформы должны использовать базовый API рендеринга TPDFlib напрямую. Во-вторых, безголовое использование намеренно поддерживается, но имеет ограничения: метод CaptureRegion отрисовывает любую клиентскую область — страницы плюс подсветку — в растровое изображение, принадлежащее вызывающей стороне, и работает на элементе управления без родительского элемента и без дескриптора окна, так как путь отрисовки избегает вызовов VCL, которые принудительно создали бы дескриптор. Это делает практичной попиксельную верификацию в автоматических тестах, но все, что действительно требует цикла сообщений (например, интерактивные инструменты мыши), все же должно выполняться в реальной сессии пользовательского интерфейса. Что касается производительности, элемент управления хранит кэш страниц в памяти с настраиваемым лимитом с помощью SetCacheLimit и может сохранять отрисованные страницы между сессиями; статья о дисковом кэше страниц просмотрщика подробно описывает этот слой и его поведение при различных DPI мониторов
Итак, пять шагов: разместить элемент управления и вызвать LoadFromFile, подключить готовые действия из PDFlibViewerActions, вызвать HighlightSearchHits для поиска на странице, обработать OnAnnotClick для рабочих процессов с аннотациями и предоставить доступ к PrintDoc. Каждая описанная здесь часть поставляется в стандартном пакете losLab PDF Library для Delphi, где элемент управления просмотрщиком, классы действий и движок рендеринга устанавливаются вместе