PDFium Delphi Component Docs

Примеры программирования PDFium VCL

Выбор демо для запуска

Коллекция демо насчитывает 33 темы: 33 проекта Delphi, 32 проекта C++Builder и 33 проекта Lazarus — всего 98 нативных проектов. В основном каталоге каждой платформы собрано 24 полноценных демо рабочих процессов. Девять сфокусированных тем живут внутри Miscellaneous — это 26 нативных проектов на три платформы; XfaLab доступен для Delphi и Lazarus

Начните с рабочего процесса из основного каталога: PDFViewer для тура по функциям, CreatePDF для живого создания документов, ContentExtractionLab для мгновенной инвентаризации, FormFill для редактируемых форм, StandardsLab для валидации и конвертации или IntakeWorkbenchCli для автоматизированного приёма документов. Эти шесть точек входа сразу дают рабочий результат; сфокусированные образцы ниже пригодятся, когда нужно детально разобрать отдельный API

Основные демо рабочих процессов

ДемоДля чегоЧем отличается от родственных демо
PDFViewerОбщий просмотр и оценка возможностейОсновной просмотрщик объединяет навигацию, поиск, миниатюры, режимы отображения, печать и витрину функций
MultiPageViewerНепрерывные страницы, развороты и макеты с обложкойСфокусированный пример макетов на встроенных API выделения просмотрщика; для более широкого просмотра берите PDFViewer
SplitViewСравнение страниц или документов в отдельных панеляхДемонстрирует независимые позиции представлений и адресацию команд активной панели, а не двухстраничный макет в одном представлении
SearchAndSelectРезультаты поиска и выделение по страницамСписок результатов и параметры поиска раскрывают процесс поиска непосредственнее, чем панель просмотрщика
PrintPDFПредпросмотр и обычные задания печатиДиапазоны страниц, масштабирование, прогресс и отмена дают основной процесс печати
ContentExtractionLabСовместная инспекция нескольких категорий содержимогоИнвентаризации TXT/JSON описывают объекты и предпросматривают текст; они не заменяют полный экспорт текста или файлов изображений
ExtractImagesЭкспорт встроенных объектов изображенийИзвлекает битмапы изображений, а не снимки целых страниц; встроенная фикстура содержит по два настоящих объекта изображения на каждой из двух страниц
TableExtractionLabОбнаружение таблиц и экспорт CSV/JSONТаблицы с линейками и по пробелам, объединённые ячейки, группы продолжения и управление уверенностью не зависят от извлечения простого текста
AttachmentЧтение, добавление и экспорт встроенных вложенийОперации с полезной нагрузкой вложений выходят за рамки перечисления имён и размеров в ContentExtractionLab
BatchExportЭкспорт изображений страниц из нескольких документовПотоковый вывод, статус по файлам и изолированные сбои демонстрируют пакетную обработку, а не одиночную конвертацию
CreatePDFСоздание содержимого страниц с живым предпросмотромВсе три нативных варианта создают текст, доступный для поиска, векторные фигуры, настоящее растровое изображение, таблицы и аннотацию-заметку; опции перестраивают предпросмотр до явного сохранения
ImageToPDFПостроение страниц из входных файлов изображенийКомпоновка, подгонка, порядок и обработка JPEG дают процесс конвертации документов
MergePDFСборка страниц из нескольких PDFДемонстрирует импорт документов и упорядоченную композицию; разбиение решает обратную задачу
SplitPDFЗапись выбранных групп страниц в отдельные PDFИменование вывода и валидация групп страниц отличаются от слияния или рендеринга изображений страниц
WatermarkStampНаложение повторяющихся слоёв на существующие страницыРазмещение и пакетная штамповка страниц дополняют базовые API создания
FormFillИнтерактивный ввод AcroForm и уплощение формВвод полей и сохранённые внешние виды отличаются от обмена XFDF и специфического поведения XFA
AccessibilityReaderИнтерактивное чтение и озвучиваниеGUI-читалка демонстрирует навигацию по чтению и подсветку, а не аудит соответствия
AccessibilityWorkbenchCliПакетный обзор доступности и генерация отчётовНаходки, сводки и пороги обзора служат автоматизации; находки не заменяют ручной обзор доступности
StandardsLabВалидация и конвертация по стандартамИнтерактивная лаборатория раскрывает операции и параметры, а не только генерирует агрегированный отчёт
PreflightReportИнтерактивные отчёты по стандартамGUI поддерживает ручную инспекцию и экспорт отчёта
PreflightReportCliАвтоматизированные отчёты по стандартамПакетные входы, форматы отчётов и пороги кода выхода дают автоматизационную пару PreflightReport
IntakeWorkbenchCliПакетный приём, сортировка и маршрутизацияАгрегированные отчёты, пороги сбоев и решения маршрутизации делают это процессом обработки, выходящим за рамки GUI SecurityAudit

Сфокусированные образцы в Miscellaneous

Открывайте эти проекты из Demo\Delphi\Miscellaneous, Demo\CBuilder\Miscellaneous или Demo\Lazarus\Miscellaneous. Они сохраняют полезные рецепты API и узкие задачи экспорта, пока основной каталог сосредоточен на полных сценариях. Базовые проверки записи и повторного чтения Unicode покрываются рабочими процессами создания и извлечения; отдельных демо-проектов для них больше нет

ДемоДля чегоЧем отличается от родственных демо
SecurityAuditИнтерактивная инспекция состояния безопасностиGUI делает акцент на разрешениях, скриптах, подписях и деталях безопасности, а не на маршрутизации файлов
XfdfLabИмпорт и экспорт данных XFDFОбмен и явное сохранение импортированного PDF демонстрируют сохраняемость; одного экспорта XFDF недостаточно для сохранения изменённого PDF
AccessibilityLabОбход API чтения в консолиКомпактный исполняемый обход раскрывает единицы чтения, рамки слов, структуру и вывод speech-скриптов
ExtractTextЭкспорт полного текста с выбранных страницВывод текстового слоя, разделители страниц и отмена отличаются от предпросмотров инвентаризации и обнаружения таблиц; OCR не выполняется
FontPropertiesИнспекция метрик шрифтов и символовСразу открывается на реальных метриках шрифта и символов первого текстового объекта; выберите другую строку, чтобы сравнить шрифты, не выгружая содержимое документа
NavigationLabЗакладки, назначения и действия навигацииКомпактный обход API дополняет видимые элементы навигации в PDFViewer
PDFtoJpgРендеринг одного документа в изображения страницКомпактный пример рендеринга одного документа; Delphi раскрывает отдельные диапазоны страниц, тогда как BatchExport сосредоточен на нескольких входных документах
PrintPreferencesЛисты N-up и настройки печати автора PDFКопии, фильтрация чёт/нечёт и настройки от автора — учебный фокус; это спутник PrintPDF
ViewerInteractionLabДиагностика взаимодействия мыши, клавиатуры и координатЛог взаимодействий и преобразование координат страница/устройство делают его диагностическим инструментом API
XfaLabСтатические пакеты XFA и возможности runtimeОткрывает пакеты template и datasets обычным runtime; интерактивный рендеринг XFA требует runtime с поддержкой XFA, есть варианты для Delphi и Lazarus

Начните с готового тура по функциям

PDFViewer открывает встроенный в комплект PDFiumDemoShowcase.pdf — шестистраничный документ с текстом, доступным для поиска, векторными диаграммами, двумя встроенными изображениями, таблицей, четырьмя полями AcroForm, закладками глав, ссылками, заметкой-рецензией и двумя файловыми вложениями. Панель функций предлагает Restart showcase, Find live text, 2-page spread, Night reading, Inspect this PDF и Explore forms; каждое действие работает с текущим документом

CreatePDF открывает живой двухстраничный предпросмотр отчёта. Отредактируйте заголовок и автора либо переключите фигуры, таблицу и водяной знак, чтобы перестроить реальное содержимое страниц; опция Unicode добавляет третью страницу. Пока вы не сохраните, ничего не записывается, а сохранённый PDF остаётся доступным для поиска

MultiPageViewer, SearchAndSelect, ContentExtractionLab и FormFill открывают тот же документ для своих рабочих процессов, причём результаты поиска, инвентаризация извлечения и редактируемые поля готовы сразу. Miscellaneous\NavigationLab использует собственную локальную копию для обхода API закладок и назначений

Attachment стартует с двумя настоящими встроенными файлами. PrintPreferences открывается на четырёх страницах с настройками автора: две копии, выбранные диапазоны страниц, масштабирование и дуплекс; текущий процесс печати применяет копии и диапазоны страниц, а масштабирование и дуплекс инспектируются и попадают в отчёт. XfaLab стартует с реальных пакетов template и datasets для инспекции статического XFA; интерактивный рендеринг XFA требует подходящего runtime и является отдельной возможностью

Каждый проект просмотра, форм, извлечения и навигации содержит собственный PDFiumDemoShowcase.pdf. Скопируйте каталог проекта и держите этот встроенный входной файл рядом с распространяемым исполняемым файлом; сборки Lazarus подкладывают его автоматически. Тур работает офлайн, а общие просмотрщики и образцы извлечения вдобавок дают сгенерированный резервный контент, когда локальный PDF недоступен

Копирование и поддержка нативных образцов

Каждое нативное демо содержит собственную реализацию образца. Точки входа Delphi и Lazarus для AccessibilityLab, AccessibilityWorkbenchCli, IntakeWorkbenchCli и PreflightReportCli несут полный рабочий процесс прямо в каталоге проекта, включая специфичную для компилятора инициализацию и запись JPEG. Примеры вложений, настроек печати и XFA тоже локально строят собственные входные данные под свою функцию. За пределами скопированного проекта нужны только библиотека, соответствующий нативный runtime и обычные зависимости фреймворка компилятора

Выбирайте извлечение объектов изображений, когда нужны встроенные битмапы, рендеринг страниц, когда нужна видимая страница, и извлечение текста, когда у PDF уже есть текстовый слой. Отчёт, перечисляющий эти категории, не выгружает их полные данные. Эти границы удерживают сфокусированные примеры полезными, не раздувая каждое демо до очередного универсального просмотрщика или workbench

Базовые операции с документами

Загрузка и отображение PDF-документа

Pdf1.FileName := 'C:\MyDocument.pdf';
Pdf1.Active := True;
ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
ShowMessage('Title: ' + Pdf1.Title);
ShowMessage('Author: ' + Pdf1.Author);

var
  Stream: TMemoryStream;
begin
  Stream := TMemoryStream.Create;
  try
    Stream.LoadFromFile('C:\MyDocument.pdf');
    Pdf1.LoadDocument(Stream, True);
    Pdf1.Active := True;
    ShowMessage('PDF loaded from stream');
  finally
    Stream.Free;
  end;
end;

Создание нового PDF-документа

Pdf1.CreateDocument;
Pdf1.Active := True;
Pdf1.AddPage(0, 612, 792);
Pdf1.PageNumber := 1;
Pdf1.AddText('Hello World!', 'Arial', 12, 100, 700, clBlack, $FF, 0.0);
Pdf1.UpdatePage;
Pdf1.SaveAs('C:\NewDocument.pdf');
Pdf1.Active := False;

Операции с текстом

Извлечение текста из PDF

var
  PageText: WString;
  RectText: WString;
begin
  Pdf1.PageNumber := 1;
  PageText := Pdf1.Text;
  Memo1.Text := PageText;
  RectText := Pdf1.TextInRectangle(100, 100, 500, 200);
  ShowMessage('Text in rectangle: ' + RectText);
end;

Поиск текста

var
  Position: Integer;
begin
  Position := Pdf1.FindFirst('Hello', [seCaseSensitive]);
  while Position >= 0 do
  begin
    ShowMessage('Found at character: ' + IntToStr(Position));
    Position := Pdf1.FindNext;
  end;
end;

Добавление текста с форматированием

Pdf1.PageNumber := 1;
Pdf1.AddText('Document Title', 'Times New Roman', 16, 100, 750, clBlue);
Pdf1.AddText('This is the document content.', 'Arial', 12, 100, 700, clBlack);
Pdf1.UpdatePage;

Изучение метаданных символов и шрифтов

var
  I: Integer;
  MetricSize: Single;
  FontBytes: TBytes;
begin
  Pdf1.PageNumber := 1;
  for I := 0 to Pdf1.ObjectCount - 1 do
    if Pdf1.ObjectType[I] = otText then
    begin
      MetricSize := 12.0;
      FontBytes := Pdf1.FontData[I];
      Memo1.Lines.Add('Object ' + IntToStr(I));
      Memo1.Lines.Add('Base name: ' + Pdf1.FontBaseName[I]);
      Memo1.Lines.Add('Family: ' + Pdf1.FontFamilyName[I]);
      Memo1.Lines.Add('Embedded: ' + BoolToStr(Pdf1.FontIsEmbedded[I], True));
      Memo1.Lines.Add('Weight: ' + IntToStr(Pdf1.FontWeight[I]));
      Memo1.Lines.Add('Italic angle: ' + IntToStr(Pdf1.FontItalicAngle[I]));
      Memo1.Lines.Add('Ascent: ' + FloatToStr(Pdf1.FontAscent[I, MetricSize]));
      Memo1.Lines.Add('Descent: ' + FloatToStr(Pdf1.FontDescent[I, MetricSize]));
      Memo1.Lines.Add('Font data bytes: ' + IntToStr(Length(FontBytes)));
    end;

  if Pdf1.CharacterCount > 0 then
  begin
    Memo1.Lines.Add('First character size: ' + FloatToStr(Pdf1.CharacterFontSize[0]));
    Memo1.Lines.Add('First character weight: ' + IntToStr(Pdf1.CharacterFontWeight[0]));
    Memo1.Lines.Add('First character angle: ' + FloatToStr(Pdf1.CharacterAngle[0]));
  end;
end;

Проекты Demo\Delphi\Miscellaneous\FontProperties, Demo\CBuilder\Miscellaneous\FontProperties и Demo\Lazarus\Miscellaneous\FontProperties показывают те же метаданные через TPdf и TPdfView

Построение многокатегорийного отчёта об извлечении содержимого

Проекты Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab и Demo\Lazarus\ContentExtractionLab превращают извлечение содержимого в единый рабочий процесс. Они открывают или создают PDF, позволяют пользователям выбрать метаданные, текст страниц, объекты страниц, изображения, вложения, ссылки, аннотации, закладки и метрики шрифтов / символов, затем генерируют сводку плюс подробный отчёт TXT или JSON. Лаборатория также включает выбор всех / ни одной категории, копирование отчёта в буфер обмена и действия сохранения исходного PDF, чтобы пользователи могли держать сгенерированный образец PDF рядом с его отчётом извлечения. ExtractImages и Attachment дают рабочие процессы экспорта полезной нагрузки, а Miscellaneous\ExtractText и Miscellaneous\FontProperties остаются сфокусированными примерами API; ExtractText поддерживает параметры диапазона страниц и разделителя страниц для формирования постраничного текстового вывода в Delphi, C++Builder и Lazarus, тогда как ContentExtractionLab представляет собой образец сквозной инвентаризации

Обнаружение и извлечение таблиц

Проекты Demo\Delphi\TableExtractionLab, Demo\CBuilder\TableExtractionLab и Demo\Lazarus\TableExtractionLab превращают обнаружение таблиц в единый сценарий. Лаборатория строит образцовый документ с таблицей с линиями разметки и объединёнными ячейками заголовка, таблицей, выровненной по пробелам без линий, и длинной таблицей, разрывающейся на границе страницы, затем запускает ExtractTables для текущей страницы или ExtractDocumentTables для всего документа и сообщает строки, столбцы, значения RowSpan и ColumnSpan, флаги заголовков, режим обнаружения, достоверность и группы продолжений. Опции DetectRuledTables, DetectWhitespaceTables, DetectContinuations, DetectFilledRulings и MinConfidence редактируются в интерфейсе, а выбранная таблица экспортируется через TPdfTable.ToCsv и TPdfTable.ToJson. Лаборатория также обрабатывает пакетом целую папку: Batch folder запускает ExtractDocumentTables для каждого PDF, сообщает количество таблиц по каждому документу, помечает сканы без текстового слоя и повторяет пустые документы с профилем компактных столбцов; Export all CSV записывает каждую найденную таблицу рядом с исходными файлами. Каждый документ открывается в собственном экземпляре TPdf, так что один нечитаемый файл не может задеть остальную часть пакета

Операции с формами

Работа с полями форм

var
  I: Integer;
  FieldInfo: TPdfFormFieldInfo;
begin
  Pdf1.FormField[0] := 'John';
  for I := 0 to Pdf1.FormFieldCount - 1 do
  begin
    FieldInfo := Pdf1.FormFieldInfo[I];
    ShowMessage(FieldInfo.Name + ': ' + Pdf1.FormField[I]);
  end;
end;

Сплющивание заполненных значений формы в страницу

// Без сплющивания FormField[i] := value пишет только запись /V —
// видимый внешний вид берётся из потока /AP и может не обновиться.
// GenerateFormAppearances регенерирует /AP для каждого виджета, затем
// FlattenAllPages запекает аннотации и виджеты каждой страницы
// в постоянное, нередактируемое содержимое.
Pdf1.FileName := 'C:\Application.pdf';
Pdf1.Active := True;
Pdf1.FormField[0] := 'John Smith';
Pdf1.FormField[1] := '[email protected]';
Pdf1.GenerateFormAppearances;
if Pdf1.FlattenAllPages then
  Pdf1.SaveAs('C:\Application.flat.pdf');
Pdf1.Active := False;

Операции с изображениями

Извлечение изображений

var
  I: Integer;
  Bitmap: TBitmap;
begin
  for I := 0 to Pdf1.BitmapCount - 1 do
  begin
    Bitmap := Pdf1.Bitmap[I];
    try
      Bitmap.SaveToFile('C:\ExtractedImage' + IntToStr(I) + '.bmp');
    finally
      Bitmap.Free;
    end;
  end;
end;

Добавление изображений

var
  JpegStream: TFileStream;
begin
  JpegStream := TFileStream.Create('C:\MyImage.jpg', fmOpenRead or fmShareDenyWrite);
  try
    Pdf1.AddJpegImage(JpegStream, 100, 500, 200, 150);
  finally
    JpegStream.Free;
  end;
  Pdf1.AddPicture(Image1.Picture, 100, 300, 150, 100);
  Pdf1.UpdatePage;
end;

Операции с аннотациями

Работа с аннотациями

var
  I: Integer;
  Annotation: TPdfAnnotation;
begin
  for I := 0 to Pdf1.AnnotationCount - 1 do
  begin
    Annotation := Pdf1.Annotation[I];
    ShowMessage('Annotation ' + IntToStr(I) + ': ' + Annotation.ContentsText);
  end;
end;

Операции рисования

Рисование фигур

Pdf1.CreatePath(100, 100, 200, 150, fmAlternate, clYellow, $FF, True, clBlack, $FF, 2.0);
Pdf1.AddPath;

Pdf1.CreatePath(100, 100, fmNone, clBlack, $FF, True, clRed, $FF, 3.0);
Pdf1.LineTo(200, 100);
Pdf1.LineTo(150, 200);
Pdf1.ClosePath;
Pdf1.AddPath;
Pdf1.UpdatePage;

Навигация и закладки

Работа с закладками

var
  I: Integer;
  Bookmarks: TBookmarks;
begin
  Bookmarks := Pdf1.Bookmarks;
  for I := 0 to Length(Bookmarks) - 1 do
    ShowMessage(Bookmarks[I].Title + ' -> Page ' + IntToStr(Bookmarks[I].PageNumber));
end;

Работа с именованными адресами назначения

var
  I: Integer;
  Destination: TDestination;
begin
  for I := 0 to Pdf1.DestinationCount - 1 do
  begin
    Destination := Pdf1.Destination[I];
    if Destination.Name = 'Chapter1' then
    begin
      Pdf1.PageNumber := Destination.PageNumber;
      Break;
    end;
  end;
end;

Операции с вложениями

Работа с вложениями

var
  I: Integer;
  AttachmentData: TBytes;
  AttachmentName: WString;
  FileStream: TFileStream;
begin
  for I := 0 to Pdf1.AttachmentCount - 1 do
  begin
    AttachmentName := Pdf1.AttachmentName[I];
    AttachmentData := Pdf1.Attachment[I];
    FileStream := TFileStream.Create('C:\Extracted_' + string(AttachmentName), fmCreate);
    try
      if Length(AttachmentData) > 0 then
        FileStream.WriteBuffer(AttachmentData[0], Length(AttachmentData));
    finally
      FileStream.Free;
    end;
  end;
  if Pdf1.CreateAttachment('MyFile.txt') then
    Pdf1.Attachment[Pdf1.AttachmentCount - 1] := TFile.ReadAllBytes('C:\MyFile.txt');
end;

Управление страницами

Операции со страницами

Pdf1.AddPage(Pdf1.PageCount + 1, 612, 792);
Pdf1.AddPage(Pdf1.PageCount + 1, 595, 842);
Pdf1.AddPage(Pdf1.PageCount + 1, 400, 600);

var
  SourcePdf: TPdf;
begin
  SourcePdf := TPdf.Create(nil);
  try
    SourcePdf.FileName := 'C:\SourceDocument.pdf';
    SourcePdf.Active := True;
    Pdf1.ImportPages(SourcePdf, '1', Pdf1.PageCount + 1);
  finally
    SourcePdf.Free;
  end;
end;

if Pdf1.PageCount > 1 then
  Pdf1.DeletePage(Pdf1.PageCount);

Сохранение и сжатие

Сохранение со сжатием

// Создать сжатый PDF-документ
Pdf1.CreateDocument;
Pdf1.Compressed := True; // гарантировать сжатие всех потоков через FlateDecode (по умолчанию)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // размер A4
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;

// Сохранить с опциями
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // инкрементальное сохранение
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // удалить защиту

// Сохранить с целевой версией PDF. Валидатор версии PDF отклоняет
// любую функцию, появившуюся после выбранной цели (JBIG2Decode, MarkInfo,
// шифрование AES, аннотации Polygon / Caret / Watermark / Redact, ...).
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
  ShowMessage('Save rejected: document contains features newer than PDF 1.5');

Создание архивного вывода PDF/A-1b

// SaveAsPdfA постобрабатывает базовое сохранение инкрементальным
// обновлением, которое внедряет поток метаданных XMP, ICC OutputIntent
// sRGB и обновлённый catalog документа, благодаря чему результат
// соответствует PDF/A-1b. Встроенный профиль sRGB IEC61966-2.1 используется
// автоматически, если пользовательский профиль не передан.
Pdf1.FileName := 'C:\Report.pdf';
Pdf1.Active := True;
if Pdf1.SaveAsPdfA('C:\Report.pdfa.pdf', pac1b) then
  ShowMessage('Saved PDF/A-1b archive');

// PDF/A с пользовательским ICC-профилем (например, конвейеры печати CMYK)
var
  Opts: TPdfASaveOptions;
begin
  Opts := Default(TPdfASaveOptions);
  Opts.Conformance    := pac1b;
  Opts.IccProfileData := TFile.ReadAllBytes('C:\Profiles\Coated_GRACoL_2006.icc');
  Pdf1.SaveAsPdfA('C:\Report.cmyk.pdfa.pdf', Opts);
end;

// Вывод PDF/A в память
var
  Stream: TMemoryStream;
begin
  Stream := TMemoryStream.Create;
  try
    Opts := Default(TPdfASaveOptions);
    Opts.Conformance := pac1b;
    if Pdf1.SaveAsPdfAToStream(Stream, Opts) then
      UploadToArchive(Stream);
  finally
    Stream.Free;
  end;
end;

Валидация соответствия PDF/A

var
  Validation: TPdfAValidationResult;
  Issue: TPdfAValidationIssue;
begin
  Pdf1.FileName := 'C:\Incoming\report.pdf';
  Pdf1.Active   := True;

  // Дешёвая проверка стиля «заголовок» — читает соответствие без
  // итерации полного валидатора. Результат: pacNone, pac1a, pac1b,
  // pac2b, pac3b или pacUnknown.
  case Pdf1.PdfAConformance of
    pac1b: Status.Caption := 'PDF/A-1b';
    pac2b: Status.Caption := 'PDF/A-2b';
    pacNone: Status.Caption := 'Not PDF/A';
  else
    Status.Caption := 'Unknown conformance';
  end;

  // Полный валидатор с диагностикой по каждой проблеме
  Validation := Pdf1.ValidatePdfA;
  Memo1.Lines.Add('Conformance: ' + GetEnumName(TypeInfo(TPdfAConformance),
    Ord(Validation.Conformance)));
  for Issue in Validation.Issues do
    Memo1.Lines.Add('- ' + Issue.Description);
end;

Изучение нескольких стандартов PDF в StandardsLab

Проекты Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab и Demo\Lazarus\StandardsLab загружают или создают PDF, запускают ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR и ValidatePdfVT, затем показывают каждый обнаруженный уровень соответствия и количество проблем в одной сетке. Кнопки сохранения вызывают SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR и SaveAsPdfVT, благодаря чему рабочий процесс маркерного вывода виден без написания собственной обвязки

Генерация отчётов препроверки

Модуль FPdfPreflightReport объединяет ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR и ValidatePdfVT в один объект отчёта, доступный для запросов, со степенями серьёзности Info / Warning / Error, агрегированием статусов и проблем, поиском, фильтрацией и стабильными отпечатками SHA-256. Отчёты можно выгружать в виде текста, HTML, Markdown, JSON, CSV, SARIF 2.1.0, JUnit XML, NDJSON или собственного XML. Проекты Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport и Demo\CBuilder\PreflightReport предоставляют интерактивные сценарии выбора и предпросмотра, а Demo\Delphi\PreflightReportCli добавляет программируемые аргументы входа / выхода, фильтрацию по стандартам, опциональное встраивание вложения отчёта, управление кодом выхода для CI, пакетную обработку списков и каталогов, устойчивую к коллизиям маршрутизацию, агрегированные сводки и манифесты CSV. Сгенерированный отчёт указывает, что встроенные валидаторы покрывают проверки маркерного уровня и выбранные проверки уровня файла, поэтому полную препроверку уровня содержимого по-прежнему можно делегировать специализированному движку валидации

Потоковая загрузка и большие документы

Загрузка PDF по требованию с LoadCustomDocument

// LoadCustomDocument позволяет PDFium забирать блоки из любого
// позиционируемого TStream по требованию. Поток НЕ копируется в память,
// поэтому многогигабайтные PDF, удалённые потоки тел HTTP и БД-источники
// все жизнеспособны без предварительного буфера.
var
  FileStream: TFileStream;
begin
  // Потоковое чтение из файла (без копии в памяти)
  FileStream := TFileStream.Create('C:\Huge.pdf',
    fmOpenRead or fmShareDenyWrite);
  Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
  // ^^ передача владения — Pdf1 освобождает FileStream при UnloadDocument.
  Pdf1.Active := True;
  ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;

// Внешне-управляемый поток (вызывающий освобождает его после UnloadDocument)
var
  Stream: TFileStream;
begin
  Stream := TFileStream.Create('C:\Live.pdf',
    fmOpenRead or fmShareDenyNone);
  try
    Pdf1.LoadCustomDocument(Stream, False);
    Pdf1.Active := True;
    DoWork;
    Pdf1.Active := False;
  finally
    Stream.Free;
  end;
end;

Асинхронная отмена и прогрессивный рендеринг

Кооперативные токены отмены

// FPdfAsync предоставляет IPdfCancellationToken /
// IPdfCancellationTokenSource для семантики «отмена из интерфейса»,
// которая работает в длительных путях экспорта и рендеринга.
uses FPdfAsync;

var
  CancelSource: IPdfCancellationTokenSource;
  CancelToken:  IPdfCancellationToken;
begin
  CancelSource := PdfCancellationTokenSource;
  CancelToken  := CancelSource.Token;

  // ... пользователь нажимает «Отмена» ...
  CancelSource.Cancel;

  // Worker периодически проверяет CancelToken.IsCancelled, либо путь
  // рендеринга опрашивает его через обратный вызов IFSDK_PAUSE PDFium.
  if CancelToken.IsCancelled then
    Memo1.Lines.Add('Cancelled by user');
end;

Отменяемый прогрессивный рендеринг страницы

// RenderPageProgressive позволяет длительному рендерингу с высоким DPI
// прерваться в середине страницы, когда пользователь нажимает «Отмена»,
// вместо блокировки вызывающего до завершения.
var
  Status: TPdfProgressiveStatus;
  Bitmap: TBitmap;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit;
    Bitmap.SetSize(2480, 3508); // A4 @ 300 DPI

    Status := Pdf1.RenderPageProgressive(Bitmap, 0, 0, Bitmap.Width,
      Bitmap.Height, CancelToken, ro0, [reAnnotations], clWhite);

    case Status of
      prsDone:      SaveBitmap(Bitmap);
      prsCancelled: Memo1.Lines.Add('Render cancelled — partial result available');
      prsFailed:    Memo1.Lines.Add('PDFium reported FPDF_RENDER_FAILED');
    end;
  finally
    Bitmap.Free;
  end;
end;

Запуск рендеринга в фоновом потоке с TPdfFuture

// TPdfFuture<T> запускает worker в фоновом потоке и отправляет конверт
// TPdfFutureResult<T> обратно в главный поток.
//
// Важно: TPdf сериализует вызовы рендеринга для каждого экземпляра, но
// worker всё ещё владеет диспетчеризацией и временем жизни документа.
// Не мутируйте TPdf во время выполняющегося рендеринга; пакетный экспорт
// обычно держит по одному TPdf на файл.
type
  TRenderResult = record
    Bitmap : TBitmap;
    PageNo : Integer;
  end;

var
  Future: TPdfFuture<TRenderResult>;
begin
  Future := TPdfFuture<TRenderResult>.Create(
    function (const ACancelToken: IPdfCancellationToken): TRenderResult
    var
      LocalPdf: TPdf;
    begin
      LocalPdf := TPdf.Create(nil);
      try
        LocalPdf.FileName := 'C:\Huge.pdf';
        LocalPdf.Active   := True;
        LocalPdf.PageNumber := 1;
        Result.Bitmap := LocalPdf.RenderPage(0, 0, 2480, 3508);
        Result.PageNo := 1;
      finally
        LocalPdf.Free;
      end;
    end,
    procedure (const AResult: TPdfFutureResult<TRenderResult>)
    begin
      case AResult.State of
        pfsSuccess:   PaintBitmapToView(AResult.Value.Bitmap);
        pfsCancelled: Log.Add('Cancelled');
        pfsFailed:    Log.Add('Worker failed: ' + AResult.ErrorMessage);
      end;
    end);
end;

Матрицы преобразования

Композиция Translate / Scale / Rotate / Skew

// FPdfMatrix оборачивает FS_MATRIX из PDFium, чтобы вызывающие могли
// декларативно построить преобразование и передать его в
// FPDFPageObj_SetMatrix.
uses FPdfMatrix, FPdfView;

var
  M: TPdfMatrix;
  RawMatrix: FS_MATRIX;
  PageObj: FPDF_PAGEOBJECT;
begin
  M := TPdfMatrix.Create;
  try
    M.Translate(72, 200);       // сдвинуть на 1 дюйм вправо, ~2.8 дюйма вверх
    M.Scale(0.5, 0.5);          // штамп половинного размера
    M.Rotate(15);               // поворот на 15° против часовой
    // M.HorizontalFlip;
    // M.VerticalFlip;
    // M.CentralFlip;
    // M.Skew(10, 5);
    // M.Multiply(Other);       // постумножение на другую матрицу

    // Применить к любому объекту страницы PDFium (текст / путь / изображение / Form XObject)
    RawMatrix := M.Handle;
    FPDFPageObj_SetMatrix(PageObj, RawMatrix);
  finally
    M.Free;
  end;
end;

Композиция страниц

Импорт подмножества страниц по индексу

// ImportPagesByIndex копирует явный массив нулевых индексов из исходного
// PDF в этот документ. InsertAt = 0 вставляет перед страницей 1;
// PageCount добавляет в конец.
var
  Source: TPdf;
begin
  Source := TPdf.Create(nil);
  try
    Source.FileName := 'C:\Report.pdf';
    Source.Active   := True;

    // Импортировать страницы 0, 2, 4 (от единицы: страницы 1, 3, 5) из Source
    // и добавить их в конец Pdf1.
    Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);

    // Пустой массив импортирует все страницы источника
    // Pdf1.ImportPagesByIndex(Source, [], 0);
  finally
    Source.Free;
  end;
end;

Композитный вывод N-up

// ImportNPagesToOne возвращает совершенно новый TPdf, страницы которого
// являются композициями NumX*NumY ЭТОГО документа. OutputWidth/Height —
// это пользовательские единицы PDF (1 единица = 1/72 дюйма).
var
  Composite: TPdf;
begin
  Pdf1.FileName := 'C:\Slides.pdf';
  Pdf1.Active   := True;
  // 4-up A4 альбомная: 842 x 595 pt, 2 столбца x 2 строки
  Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
  try
    if Composite <> nil then
      Composite.SaveAs('C:\Slides_4up.pdf');
  finally
    Composite.Free;
  end;
end;

Изменение порядка страниц на месте

// MovePages принимает нулевые индексы перемещаемых страниц плюс индекс
// назначения для первой перемещённой страницы после завершения перемещения.
// Другие страницы сдвигаются вокруг перемещаемого блока, благодаря чему
// длина документа остаётся прежней.
//
// Пример: в 5-страничном документе со страницами [0,1,2,3,4],
// MovePages([2, 3], 0) переупорядочивает в [2, 3, 0, 1, 4].
if not Pdf1.MovePages([2, 3], 0) then
  ShowMessage('Invalid indices passed to MovePages');

Водяные знаки через повторное использование страниц Form XObject

// CreateXObjectFromPage создаёт переиспользуемый дескриптор Form XObject
// из любой страницы любого другого TPdf. Штампуйте этот дескриптор на
// столько страниц ЭТОГО документа, сколько нужно — каждый штамп
// представляет собой один объект страницы PDFium, который можно
// позиционировать, масштабировать и поворачивать через матричный API.
uses FPdfMatrix, FPdfView;

var
  WatermarkSource: TPdf;
  XObj:            TPdfXObject;
  PageObj:         FPDF_PAGEOBJECT;
  M:               TPdfMatrix;
  RawMatrix:       FS_MATRIX;
  I:               Integer;
begin
  WatermarkSource := TPdf.Create(nil);
  try
    WatermarkSource.FileName := 'C:\Watermarks\Confidential.pdf';
    WatermarkSource.Active   := True;

    // Обернуть страницу 0 из WatermarkSource как переиспользуемый XObject
    XObj := Pdf1.CreateXObjectFromPage(WatermarkSource, 0);
    try
      for I := 1 to Pdf1.PageCount do
      begin
        Pdf1.PageNumber := I;
        PageObj := Pdf1.InsertFormObjectFromXObject(XObj);
        if PageObj = nil then Continue;

        // Центрировать водяной знак на странице
        M := TPdfMatrix.Create;
        try
          M.Scale(0.5, 0.5);
          M.Translate(0.25 * Pdf1.PageWidth, 0.5 * Pdf1.PageHeight);
          RawMatrix := M.Handle;
          FPDFPageObj_SetMatrix(PageObj, RawMatrix);
        finally
          M.Free;
        end;

        Pdf1.UpdatePage;
      end;
    finally
      XObj.Free; // закрывает дескриптор FPDF_XOBJECT
    end;

    Pdf1.SaveAs('C:\Watermarked.pdf');
  finally
    WatermarkSource.Free;
  end;
end;

Проекты Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp и Demo\CBuilder\WatermarkStamp превращают этот шаблон в работающий процесс: они создают целевой PDF и переиспользуемый PDF штампа, оборачивают страницу штампа через CreateXObjectFromPage, вставляют один и тот же Form XObject на каждую целевую страницу, применяют преобразования TPdfMatrix для центрированных водяных знаков и штампов в правом верхнем углу и добавляют опциональные метки номеров страниц перед сохранением результата

Вставка изображений (прямые перегрузки AddImage)

Добавление зарегистрированного файла изображения или TBitmap

// AddImage(FileName) принимает любой формат, зарегистрированный в
// графических модулях VCL или LCL (BMP, PNG через PngImage, JPG, ...).
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);

// AddImage(TBitmap) пропускает промежуточный TPicture, что идеально,
// когда растровое изображение уже получено из рендеринга или пакетной
// генерации.
var
  Bitmap: TBitmap;
begin
  Bitmap := Pdf1.RenderPage(0, 0, 595, 842);
  try
    Pdf1.PageNumber := 2;
    Pdf1.AddImage(Bitmap, 50, 50, 250, 350);
    Pdf1.UpdatePage;
  finally
    Bitmap.Free;
  end;
end;

Проекты Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF и Demo\Lazarus\ImageToPDF превращают это в полный пакетный рабочий процесс: выбрать несколько файлов изображений, предпросмотреть выбранный элемент, создать одну страницу PDF на каждое изображение, масштабировать каждое изображение в страницу A4 в портретной или альбомной ориентации, сохранить результат и открыть сгенерированный PDF

Подсветка поиска в просмотрщике

Подсветить каждое совпадение на текущей странице

var
  MatchCount: Integer;
begin
  // Нарисовать маску clYellow поверх каждого совпадения на текущей странице просмотра.
  // Переход на другую страницу автоматически очищает подсветку.
  PdfView1.HighlightColor := clYellow;
  MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
  Status.Caption := Format('%d match(es) on this page', [MatchCount]);

  // Программно убрать наложение перед сменой страницы
  PdfView1.ClearHighlight;
end;

Раскладка просмотрщика и цвет страницы

Режим вписывания и фон страницы

// FitMode удерживает длинные документы в кадре при изменении размера
// или смене страницы. Прямая установка Zoom отменяет FitMode (он
// возвращается к pfmNone).
PdfView1.FitMode := pfmFitPage;       // вся страница в области просмотра
// PdfView1.FitMode := pfmFitWidth;   // ширина страницы совпадает с шириной области просмотра
// PdfView1.FitMode := pfmActualSize; // 100%

// PageColor отделяет отрисованную страницу PDF от Color управляющего хоста
// — типичная настройка просмотрщика тёмной темы оставляет область прокрутки
// тёмной, а сама страница PDF остаётся белой как бумага.
PdfView1.Color     := clBlack;        // область прокрутки
PdfView1.PageColor := clWhite;        // фон страницы PDF

// Опциональная отбрасываемая тень и рамка страницы, чтобы приподнять
// страницу над тёмным фоном
PdfView1.PageShadowSize  := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;

Изучение взаимодействия с просмотрщиком в ViewerInteractionLab

Проекты Demo\Delphi\Miscellaneous\ViewerInteractionLab, Demo\CBuilder\Miscellaneous\ViewerInteractionLab и Demo\Lazarus\Miscellaneous\ViewerInteractionLab держат современную поверхность взаимодействия TPdfView в одной компактной форме: открыть или создать образец PDF, переключить DisplayMode, применить FitMode, изменить PageColor, повернуть страницы, подсветить совпадения поиска на текущей странице через HighlightSearchText, включить или выключить пользовательское выделение текста, изучить SelectedText, вызвать SelectAll, CopySelectionToClipboard и ClearSelection и наблюдать обновление координат DeviceToPage в строке состояния при движении мыши над страницей

Поиск и выделение текста в SearchAndSelect

Проекты Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect и Demo\Lazarus\SearchAndSelect превращают рабочий процесс поиска и выделения в просмотрщике в самостоятельные образцы. Они открывают или создают PDF, перечисляют результаты поиска по всем страницам с номером страницы, индексом символа и предпросмотром текста, поддерживают навигацию «Предыдущее / Следующее» и переход к результату по двойному клику, подсвечивают совпадения текущей страницы через HighlightSearchText, предоставляют опции учёта регистра и целого слова, переключают AllowUserTextSelection, показывают предпросмотр SelectedText, управляют SelectAll, CopySelectionToClipboard и ClearSelection с кнопок и позволяют пользователям переключаться между одиночными и развёрнутыми режимами отображения

Чтение настроек печати PDF в PrintPreferences

Проекты Demo\Delphi\Miscellaneous\PrintPreferences, Demo\CBuilder\Miscellaneous\PrintPreferences и Demo\Lazarus\Miscellaneous\PrintPreferences читают настройки печати автора перед печатью. Они отображают PrintCopies, PrintPageRanges, PrintScaling и PrintPaperHandling, могут копировать предоставленные автором копии и диапазоны во входные параметры печати и показывают, как SetPdfPrintPaperHandlingDevMode отображает намерение дуплекса PDF в Windows DEVMODE

Стандартная печать PDF в PrintPDF

Проекты Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF и Demo\Lazarus\PrintPDF показывают стандартный процесс печати: открыть PDF, предпросмотреть страницы, выбрать диапазоны страниц и копии в платформенном диалоге печати, учитывать режим разборки копий, рендерить каждую страницу через TPdf.RenderPage и предоставлять прогресс печати с отменой. Образец Delphi также принимает PDF-файлы, перетащенные мышью, для быстрой загрузки

Быстрый многостраничный просмотр в MultiPageViewer

Проекты Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer и Demo\Lazarus\MultiPageViewer используют непрерывный, развёрнутый и учитывающий обложку просмотр со штатными рабочими процессами навигации по страницам и масштабирования. Они также полезны для тестирования скорости открытия первой страницы и отзывчивости рендеринга на очень больших PDF: в тестах с большими файлами документы размером 2.x ГБ, на которые Acrobat или Foxit могут тратить 20–30 секунд до первого просмотра, могут стать пригодными к использованию практически мгновенно через путь просмотрщика PDFium Delphi Component

Параллельный обзор в SplitView

Проекты Demo\Delphi\SplitView, Demo\CBuilder\SplitView и Demo\Lazarus\SplitView показывают параллельный обзор PDF. Образцы C++Builder и Lazarus посвящены сравнению двух или трёх панелей, выбору активного представления, опциональным синхронизированным командам навигации, общим пресетам масштабирования и повороту для выбранного представления или всех загруженных представлений. Используйте эти образцы для сравнения больших документов, сохраняя при этом отзывчивость первого пригодного просмотра и последующего рендеринга страниц; те же PDF-файлы размером 2.x ГБ, на открытие которых Acrobat или Foxit могут тратить 20–30 секунд, могут стать пригодными к использованию практически мгновенно через путь просмотрщика PDFium Delphi Component

Аудит поверхности рисков PDF в SecurityAudit

Проекты Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit и Demo\Lazarus\SecurityAudit открывают PDF только для чтения и перечисляют состояние разрешений, встроенные вложения, действия JavaScript документа, аннотации-ссылки URI и Launch, веб-ссылки, сводки подписей, состояние XFA, доступность помощников V8 и обратные вызовы неподдерживаемых функций. Те же строки сводки и находок можно сохранить или скопировать как аудиторский отчёт TXT или сохранить как структурированный JSON. Они предназначены как панели рисков приложения-хоста, а не как обнаружение вредоносных программ

Обработка ссылок в просмотрщике

Безопасный переход по клику со списками разрешённых действий

// LinkOptions управляет автоматической обработкой четырёх типов
// действий PDF-ссылок. По умолчанию включены только goto и URI; запуск
// программы (loAutoLaunch) и встроенный GotoR (переход к другому файлу)
// остаются ВЫКЛ, чтобы случайный клик не мог запустить произвольный код.
PdfView1.LinkOptions :=
  [loAutoGoto, loAutoOpenURI];  // безопасные значения по умолчанию

// OnAnnotationLinkClick срабатывает для адресов назначения внутри
// документа (переходы по страницам, именованные адреса, цепочки действий).
// Установите Handled := True, чтобы запретить автоследование по ссылке.
procedure TForm1.PdfView1AnnotationLinkClick(Sender: TObject;
  LinkIndex: Integer; const Action: TPdfAction;
  var Handled: Boolean);
begin
  if Action.Kind = paUri then
  begin
    if MessageDlg('Open ' + Action.Uri + '?', mtConfirmation, [mbYes, mbNo], 0)
      <> mrYes then
      Handled := True;  // подавить автообработку
  end;
end;

// OnWebLinkClick срабатывает для строк URI, обнаруженных сканером
// веб-ссылок PDFium (автообнаруженные URL в текстовом потоке, даже без
// аннотации /A <</S/URI...>>).
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
  WebLinkIndex: Integer; const Url: WString;
  var Handled: Boolean);
begin
  Log.Add('Web link clicked: ' + string(Url));
end;

Редактирование виджетов формы в просмотрщике

Выделение и Undo / Redo

// На сфокусированном виджете AcroForm шесть новых методов управляют
// историей редактирования формы PDFium. Все шесть безопасно завершают
// работу, если ни один виджет не сфокусирован или документ не несёт
// AcroForm.
procedure TForm1.btnSelectAllClick(Sender: TObject);
begin
  PdfView1.SelectAllFormText;
  EditMenuCopy.Caption := 'Copy: ' + string(PdfView1.GetSelectedFormText);
end;

procedure TForm1.btnUndoClick(Sender: TObject);
begin
  btnUndo.Enabled := PdfView1.FormCanUndo;
  btnRedo.Enabled := PdfView1.FormCanRedo;
  if PdfView1.FormCanUndo then
    PdfView1.FormUndo;
end;

procedure TForm1.btnRedoClick(Sender: TObject);
begin
  if PdfView1.FormCanRedo then
    PdfView1.FormRedo;
end;

Блокировка просмотрщика для киосков

Отключение жестов навигации по страницам

// Все три по умолчанию равны True, поэтому существующие приложения
// обновляются без изменения поведения. Установите их в False для
// киосков, панелей предпросмотра или встраиваний только для чтения
// без наследования TPdfView.
PdfView1.AllowUserPageChange       := False;  // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // переходы колесом вверх/вниз
PdfView1.AllowUserTextSelection    := False;  // выделение текста мышью/клавиатурой

// Ctrl+колесо зарезервировано для масштабирования и пропускается
// обработчиком навигации независимо от ChangePageOnMouseScrolling.

Боковая панель миниатюр

Связывание TPdfThumbnailView с TPdfView

// TPdfThumbnailView (модуль FPdfThumbnail) рендерит одну миниатюру
// страницы в строку в прокручиваемой боковой панели.
uses FPdfThumbnail;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ThumbView.Pdf              := Pdf1;
  ThumbView.ThumbnailWidth   := 120;
  ThumbView.ThumbnailHeight  := 160;
  ThumbView.SelectionColor   := $00CFA85F;  // тёплая подсветка
  ThumbView.OnPageClick      := ThumbViewPageClick;
end;

procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
  PdfView1.PageNumber := PageIndex + 1; // PageIndex отсчитывается от нуля
end;

procedure TForm1.PdfView1PageNumberChanged(Sender: TObject);
begin
  ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;

Кооперативно отменяемый пакетный экспорт

Многофайловое преобразование PDF в JPG с прогрессом по файлу и по странице

// Шаблон, используемый Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport
// и Demo\Lazarus\BatchExport. Один TPdf на файл, IPdfCancellationToken
// для «отмены из интерфейса», TPdfStreamAdapter для буферизованной
// загрузки файла.
uses FPdfAsync;

procedure ExportBatch(const Files: TArray<string>;
  const OutDir: string; DPI: Integer; Quality: Integer;
  const ACancelToken: IPdfCancellationToken);
var
  I, J: Integer;
  Job:  TPdf;
  Bmp:  TBitmap;
  Jpeg: TJPEGImage;
begin
  for I := 0 to High(Files) do
  begin
    if ACancelToken.IsCancelled then Break;
    OverallProgress.Position := I;

    Job := TPdf.Create(nil);
    try
      Job.FileName := Files[I];
      Job.Active   := True;
      PageProgress.Max := Job.PageCount;

      for J := 1 to Job.PageCount do
      begin
        if ACancelToken.IsCancelled then Break;
        PageProgress.Position := J;

        Job.PageNumber := J;
        Bmp := Job.RenderPage(
          0, 0,
          Round(Job.PageWidth  * DPI / 72),
          Round(Job.PageHeight * DPI / 72));
        try
          Jpeg := TJPEGImage.Create;
          try
            Jpeg.CompressionQuality := Quality;
            Jpeg.Assign(Bmp);
            Jpeg.SaveToFile(Format('%s\%s.p%.3d.jpg',
              [OutDir, ExtractFileName(Files[I]), J]));
          finally
            Jpeg.Free;
          end;
        finally
          Bmp.Free;
        end;
      end;
    finally
      Job.Free;
    end;
  end;
end;

Координаты просмотрщика и хит-тестинг

Вписывание текущей страницы и проверка текста под мышью

procedure FitCurrentPage;
begin
  PdfView1.Zoom := PdfView1.PageZoom[PdfView1.PageNumber];
end;

procedure InspectViewerPoint(X, Y: Integer);
var
  PageNo: Integer;
  PageX, PageY: Double;
  CharIndex: Integer;
  Ch: WString;
begin
  PageNo := PdfView1.PageNumber;
  if not PdfView1.DeviceToPage(X, Y, PageNo, PageX, PageY) then
    Exit;

  CharIndex := PdfView1.CharacterIndexAtPos(X, Y, 8, 8);
  if CharIndex >= 0 then
  begin
    Ch := PdfView1.Text(CharIndex, 1);
    ShowMessage(Format('Page %d, X %.2f, Y %.2f, character %s',
      [PageNo, PageX, PageY, string(Ch)]));
  end;
end;

Обработка ошибок

Надёжные операции с PDF

function LoadPdfSafely(const FileName: string): Boolean;
begin
  Result := False;
  if not FileExists(FileName) then
    Exit;
  try
    Pdf1.FileName := FileName;
    Pdf1.Active := True;
    Result := Pdf1.Active;
  except
    Result := False;
  end;
end;

function ExtractTextSafely(PageNo: Integer): WString;
begin
  Result := '';
  if not Pdf1.Active then
    Exit;
  if (PageNo < 1) or (PageNo > Pdf1.PageCount) then
    Exit;
  Pdf1.PageNumber := PageNo;
  Result := Pdf1.Text;
end;

Tagged PDF и доступность

Определение Tagged PDF и обход дерева структуры

// IsTagged — дешёвая проверка каталога — True, когда существует
// /StructTreeRoot. StructureElements материализует дерево в плоский
// массив записей TPdfStructureElement с типом, заголовком,
// альтернативным текстом, фактическим текстом, расширительным текстом,
// языком, уровнем, индексом родителя и счётчиками потомков /
// маркированного содержимого / атрибутов.
var
  Elements: TPdfStructureElements;
  I: Integer;
begin
  if not Pdf1.IsTagged then
  begin
    Log.Add('Document is NOT tagged — accessibility tools may struggle.');
    Exit;
  end;
  if Pdf1.Language = '' then
    Log.Add('Tagged PDF without /Lang — fails PDF/UA.');

  Elements := Pdf1.StructureElements;
  for I := 0 to High(Elements) do
    Log.Add(StringOfChar(' ', Elements[I].Level * 2)
      + Elements[I].StructType + ' "' + Elements[I].Title + '"');
end;

Выбор режима печати

Установка режима печати EMF / PostScript / маска изображения

// SetPdfPrintMode — глобальная функция, которая отображается в
// FPDF_SetPrintMode PDFium. Используйте подходящее значение TPdfPrintMode
// для используемого драйвера принтера.
SetPdfPrintMode(pmEmf);                   // GDI EMF (по умолчанию)
// SetPdfPrintMode(pmTextOnly);           // рендеринг только текста
// SetPdfPrintMode(pmPostScript2);        // PostScript Level 2
// SetPdfPrintMode(pmPostScript3);        // PostScript Level 3
// SetPdfPrintMode(pmPostScript2PassThrough);
// SetPdfPrintMode(pmPostScript3PassThrough);
// SetPdfPrintMode(pmEmfImageMasks);      // EMF + маски изображений
// SetPdfPrintMode(pmPostScript3Type42);  // PostScript 3 + шрифты Type 42

Чтение настроек печати, заданных автором

// PrintCopies, PrintPageRanges, PrintScaling и PrintPaperHandling —
// это записи словаря настроек просмотрщика. Они кодируют то, что автор
// документа хотел использовать как значения по умолчанию для читателей PDF.
var
  Ranges: TPrintPageRanges;
  Range: TPrintPageRange;
begin
  ShowMessage('Suggested copies: ' + IntToStr(Pdf1.PrintCopies));
  ShowMessage('Print scaling: ' + GetEnumName(TypeInfo(TPrintScaling),
    Ord(Pdf1.PrintScaling)));

  Ranges := Pdf1.PrintPageRanges; // диапазоны от единицы
  for Range in Ranges do
    Log.Add(Format('Pages %d..%d', [Range.First, Range.Last]));

  if SetPdfPrintPaperHandlingDevMode(PrinterDevMode, Pdf1.PrintPaperHandling) then
    ApplyPrinterDevMode(PrinterDevMode);
end;

Действия JavaScript уровня документа

Перечисление JavaScript из дерева имён

// JavaScript уровня документа хранится в дереве имён /Names /JavaScript.
// JavaScriptAction[i] возвращает одну запись; JavaScriptActions
// возвращает полный массив.
var
  I: Integer;
  Action: TPdfJavaScriptAction;
begin
  for I := 0 to Pdf1.JavaScriptActionCount - 1 do
  begin
    Action := Pdf1.JavaScriptAction[I];
    Memo1.Lines.Add('Name:   ' + string(Action.Name));
    Memo1.Lines.Add('Script: ' + string(Action.Script));
    Memo1.Lines.Add('---');
  end;
end;

Советы по производительности

Оптимизация операций с PDF

procedure ProcessMultiplePages;
var
  I: Integer;
  PageText: WString;
begin
  for I := 1 to Pdf1.PageCount do
  begin
    Pdf1.PageNumber := I;
    PageText := Pdf1.Text;
    ProcessPageText(PageText);
  end;
end;

procedure SetOptimalRenderOptions;
begin
  PdfView1.Options := [reAnnotations, reLcd];
end;

// Путь рендеринга без копирования (по умолчанию с v1.21.0): RenderPage
// пишет напрямую в буфер DIB назначения TBitmap, когда вызывающие
// передают предварительно выделенное растровое изображение, избегая
// одной копии размером ширина * высота * 4 байта.
procedure RenderIntoCallerBitmap(Bitmap: TBitmap);
begin
  Bitmap.PixelFormat := pf32bit;
  Bitmap.SetSize(2480, 3508); // A4 @ 300 DPI
  Pdf1.RenderPage(Bitmap, 0, 0, Bitmap.Width, Bitmap.Height);
end;