Documentação do PDFiumPas

Exemplos de Programação do PDFium VCL

Operações Básicas com Documentos

Carregando e Exibindo um Documento 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;

Criando um Novo Documento 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;

Operações de Texto

Extraindo Texto do 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;

Pesquisando Texto

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;

Adicionando Texto com Formatação

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;

Inspecionando Metadados de Caractere e Fonte

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;

Os projetos Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties e Demo\Lazarus\FontProperties mostram os mesmos metadados tanto via TPdf quanto via TPdfView.

Construindo um Relatório de Extração de Conteúdo Multicategoria

Os projetos Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab e Demo\Lazarus\ContentExtractionLab transformam a extração de conteúdo em um único fluxo de trabalho. Eles abrem ou criam um PDF, permitem que os usuários escolham metadados, texto de página, objetos de página, imagens, anexos, links, anotações, favoritos e métricas de fonte / caractere, e em seguida geram um resumo mais um relatório TXT ou JSON detalhado. O lab também inclui seleção de categoria todas / nenhuma, cópia de relatório para a área de transferência e ações de salvamento de PDF de origem, para que os usuários possam manter o PDF de exemplo gerado ao lado de seu relatório de extração. As demos mais antigas ExtractText, ExtractImages, Attachment e FontProperties permanecem como exemplos focados; ExtractText suporta opções de intervalo de páginas e separador de páginas para produzir saída de texto por página em Delphi, C++Builder e Lazarus, enquanto ContentExtractionLab é o exemplo de inventário transversal.

Operações de Formulário

Trabalhando com Campos de Formulário

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;

Achatando Valores de Formulário Preenchidos na Página

// Sem achatamento, FormField[i] := value escreve apenas a entrada /V —
// a aparência visível vem do stream /AP e pode não ser atualizada.
// GenerateFormAppearances regenera /AP para cada widget, então
// FlattenAllPages grava as anotações e widgets de cada página
// em conteúdo permanente e não editável.
Pdf1.FileName := 'C:\Application.pdf';
Pdf1.Active := True;
Pdf1.FormField[0] := 'John Smith';
Pdf1.FormField[1] := 'john@example.com';
Pdf1.GenerateFormAppearances;
if Pdf1.FlattenAllPages then
  Pdf1.SaveAs('C:\Application.flat.pdf');
Pdf1.Active := False;

Operações de Imagem

Extraindo Imagens

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;

Adicionando Imagens

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;

Operações de Anotação

Trabalhando com Anotações

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;

Operações de Desenho

Desenhando Formas

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;

Navegação e Favoritos

Trabalhando com Favoritos

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;

Trabalhando com Destinos Nomeados

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;

Operações de Anexo

Trabalhando com Anexos

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;

Gerenciamento de Páginas

Operações de Página

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);

Salvamento e Compactação

Salvando com Compactação

// Cria um documento PDF compactado
Pdf1.CreateDocument;
Pdf1.Compressed := True; // garante que todos os streams sejam compactados com FlateDecode (padrão)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // tamanho A4
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;

// Salvar com opções
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // salvamento incremental
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // remove a segurança

// Salvar em uma versão de PDF de destino específica. O validador de versão PDF rejeita
// qualquer recurso introduzido após a versão de destino escolhida (JBIG2Decode, MarkInfo,
// criptografia AES, anotações Polygon / Caret / Watermark / Redact, ...).
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
  ShowMessage('Salvamento rejeitado: o documento contém recursos mais novos que PDF 1.5');

Criando Saída de Arquivamento PDF/A-1b

// SaveAsPdfA pós-processa o salvamento base com uma atualização incremental que
// injeta um stream de metadados XMP, um OutputIntent ICC sRGB e um Catalog
// de documento atualizado, de modo que o resultado seja compatível com PDF/A-1b.
// O perfil sRGB IEC61966-2.1 incluso é usado automaticamente quando nenhum
// perfil personalizado é fornecido.
Pdf1.FileName := 'C:\Report.pdf';
Pdf1.Active := True;
if Pdf1.SaveAsPdfA('C:\Report.pdfa.pdf', pac1b) then
  ShowMessage('Arquivo PDF/A-1b salvo');

// PDF/A com perfil ICC personalizado (pipelines de impressão CMYK, por exemplo)
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;

// Saída PDF/A em memória
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;

Validando a Conformidade PDF/A

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

  // Sondagem barata estilo cabeçalho — lê a conformidade sem iterar
  // o validador completo. Resultado: pacNone, pac1a, pac1b, pac2b, pac3b ou
  // pacUnknown.
  case Pdf1.PdfAConformance of
    pac1b: Status.Caption := 'PDF/A-1b';
    pac2b: Status.Caption := 'PDF/A-2b';
    pacNone: Status.Caption := 'Não é PDF/A';
  else
    Status.Caption := 'Conformidade desconhecida';
  end;

  // Validador completo com diagnósticos por problema
  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;

Inspecionando Múltiplos Padrões PDF no StandardsLab

Os projetos Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab e Demo\Lazarus\StandardsLab carregam ou criam um PDF, executam ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR e ValidatePdfVT, e em seguida mostram cada nível de conformidade detectado e contagem de problemas em uma única grade. Seus botões de salvar chamam SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR e SaveAsPdfVT, de modo que o fluxo de trabalho de saída com marcadores fique visível sem escrever uma harness personalizada.

Gerando Relatórios de Preflight

A unit FPdfPreflightReport encapsula ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR e ValidatePdfVT em um único objeto de relatório. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport e Demo\CBuilder\PreflightReport permitem que os usuários escolham padrões, gerem um resumo, inspecionem status, prioridade, categorias de problema, contagens por categoria, próximas ações, códigos de problema e ações recomendadas, alternem a pré-visualização entre texto, Markdown, JSON e CSV, e exportem relatórios TXT, HTML, Markdown, JSON ou CSV. A amostra GUI Delphi também adiciona uma visualização Action Plan que condensa padrões não aprovados, prioridades, códigos de problema e recomendações em uma lista de remediação. Demo\Delphi\PreflightReportCli expõe o mesmo caminho de relatório como um fluxo de trabalho de console com argumentos de entrada / saída, senha opcional, seleção de formato de saída incluindo plan / actionplan, filtragem de padrão, incorporação opcional de relatório em arquivo único attach=output.pdf, códigos de saída de gating de CI failon=, processamento batch=list.txt, varredura de diretório batchdir=folder com recursão opcional e processamento ordenado por caminho estável, roteamento de relatório outdir=reports, nomes de relatório seguros contra colisão para nomes de arquivo de entrada duplicados, resumos TXT / JSON / HTML com totais de status agregados e por padrão, manifestos de linhas CSV com configurações de execução repetidas por item, e um modo de exemplo sem argumentos para scripts e jobs de CI. O relatório gerado informa que os validadores integrados cobrem verificações em nível de marcador e seleções em nível de arquivo, de modo que o preflight completo em nível de conteúdo ainda possa ser delegado a um motor de validação dedicado.

Carregamento via Streaming e Documentos Grandes

Carregando um PDF Sob Demanda com LoadCustomDocument

// LoadCustomDocument permite que o PDFium puxe blocos de qualquer TStream
// pesquisável sob demanda. O stream NÃO é copiado para a memória, de modo
// que PDFs de vários GB, streams de corpo HTTP remoto e fontes baseadas
// em banco de dados são viáveis sem um buffer antecipado.
var
  FileStream: TFileStream;
begin
  // Streaming de leitura de arquivo (sem cópia em memória)
  FileStream := TFileStream.Create('C:\Huge.pdf',
    fmOpenRead or fmShareDenyWrite);
  Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
  // ^^ propriedade transferida — Pdf1 libera FileStream em UnloadDocument.
  Pdf1.Active := True;
  ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;

// Stream de propriedade externa (chamador libera após 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;

Cancelamento Assíncrono e Renderização Progressiva

Tokens de Cancelamento Cooperativos

// FPdfAsync expõe IPdfCancellationToken /
// IPdfCancellationTokenSource para semântica de cancelar a partir da UI
// que funciona nos caminhos de exportação e renderização de longa duração.
uses FPdfAsync;

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

  // ... usuário clica em Cancelar ...
  CancelSource.Cancel;

  // O worker verifica periodicamente CancelToken.IsCancelled, ou o caminho
  // de renderização o consulta através do callback IFSDK_PAUSE do PDFium.
  if CancelToken.IsCancelled then
    Memo1.Lines.Add('Cancelled by user');
end;

Renderização Progressiva Cancelável de Página

// RenderPageProgressive permite que uma renderização longa em alto DPI seja
// abortada no meio da página quando o usuário clica em Cancelar, em vez de
// bloquear o chamador até a conclusão.
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('Renderização cancelada — resultado parcial disponível');
      prsFailed:    Memo1.Lines.Add('PDFium relatou FPDF_RENDER_FAILED');
    end;
  finally
    Bitmap.Free;
  end;
end;

Executando Renderizações em Thread de Trabalho com TPdfFuture

// TPdfFuture<T> inicia um worker em uma thread de trabalho em segundo plano
// e envia um envelope TPdfFutureResult<T> de volta para a thread principal.
//
// Importante: TPdf serializa chamadas de renderização por instância, mas o worker
// ainda é proprietário do despacho e do ciclo de vida do documento. Não mute um TPdf
// enquanto uma renderização está em andamento; exportação em lote geralmente mantém
// um TPdf por arquivo.
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;

Matrizes de Transformação

Compondo Translate / Scale / Rotate / Skew

// FPdfMatrix encapsula a FS_MATRIX do PDFium para que os chamadores possam construir
// uma transformação declarativamente e entregá-la a FPDFPageObj_SetMatrix.
uses FPdfMatrix, FPdfView;

var
  M: TPdfMatrix;
  RawMatrix: FS_MATRIX;
  PageObj: FPDF_PAGEOBJECT;
begin
  M := TPdfMatrix.Create;
  try
    M.Translate(72, 200);       // mover 1 pol à direita, ~2,8 pol acima
    M.Scale(0.5, 0.5);          // carimbo com metade do tamanho
    M.Rotate(15);               // rotação de 15 graus no sentido anti-horário
    // M.HorizontalFlip;
    // M.VerticalFlip;
    // M.CentralFlip;
    // M.Skew(10, 5);
    // M.Multiply(Other);       // pós-multiplica com outra matriz

    // Aplicar a qualquer objeto de página PDFium (texto / caminho / imagem / Form XObject)
    RawMatrix := M.Handle;
    FPDFPageObj_SetMatrix(PageObj, RawMatrix);
  finally
    M.Free;
  end;
end;

Composição de Páginas

Importando um Subconjunto de Páginas por Índice

// ImportPagesByIndex copia um array explícito de índices baseado em zero de
// um PDF de origem para este documento. InsertAt = 0 insere antes da página 1;
// PageCount anexa.
var
  Source: TPdf;
begin
  Source := TPdf.Create(nil);
  try
    Source.FileName := 'C:\Report.pdf';
    Source.Active   := True;

    // Importa as páginas 0, 2, 4 (baseado em um: páginas 1, 3, 5) de Source
    // e as anexa ao final de Pdf1.
    Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);

    // Array vazio importa todas as páginas de origem
    // Pdf1.ImportPagesByIndex(Source, [], 0);
  finally
    Source.Free;
  end;
end;

Saída Composta N-up

// ImportNPagesToOne retorna um novo TPdf cujas páginas são compostos NumX*NumY
// DESTE documento. OutputWidth/Height são unidades de usuário PDF
// (1 unidade = 1/72 pol).
var
  Composite: TPdf;
begin
  Pdf1.FileName := 'C:\Slides.pdf';
  Pdf1.Active   := True;
  // A4 paisagem 4-up: 842 x 595 pt, 2 colunas x 2 linhas
  Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
  try
    if Composite <> nil then
      Composite.SaveAs('C:\Slides_4up.pdf');
  finally
    Composite.Free;
  end;
end;

Reordenando Páginas no Local

// MovePages recebe os índices baseados em zero das páginas a serem movidas mais
// o índice de destino para a primeira página movida após a conclusão do movimento.
// Outras páginas se deslocam em torno do bloco movido, de modo que o comprimento
// do documento permaneça o mesmo.
//
// Exemplo: em um documento de 5 páginas com páginas numeradas [0,1,2,3,4],
// MovePages([2, 3], 0) reordena para [2, 3, 0, 1, 4].
if not Pdf1.MovePages([2, 3], 0) then
  ShowMessage('Índices inválidos passados para MovePages');

Marca d'Água via Reutilização de Página com Form XObject

// CreateXObjectFromPage fabrica um handle de Form XObject reutilizável a partir
// de qualquer página de qualquer outro TPdf. Estampe esse handle em quantas
// páginas DESTE documento você quiser — cada carimbo é um único objeto de
// página PDFium que pode ser posicionado, escalonado e rotacionado através
// da API de matriz.
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;

    // Encapsula a página 0 de WatermarkSource como um XObject reutilizável
    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;

        // Centraliza a marca d'água na página
        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; // fecha o handle FPDF_XOBJECT
    end;

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

Os projetos Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp e Demo\CBuilder\WatermarkStamp transformam esse padrão em um fluxo de trabalho executável: criam um PDF de destino e um PDF de carimbo reutilizável, encapsulam a página de carimbo com CreateXObjectFromPage, inserem o mesmo Form XObject em cada página de destino, aplicam transformações TPdfMatrix para marcas d'água centralizadas e carimbos no canto superior direito, e adicionam rótulos opcionais de número de página antes de salvar o resultado.

Inserção de Imagem (Overloads Diretos de AddImage)

Adicionando um Arquivo de Imagem Registrado ou um TBitmap

// AddImage(FileName) aceita qualquer formato registrado com as unidades de
// gráficos VCL ou LCL (BMP, PNG via PngImage, JPG, ...).
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);

// AddImage(TBitmap) pula o intermediário TPicture, ideal quando o
// bitmap já vem de trabalho de renderização ou geração em lote.
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;

Os projetos Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF e Demo\Lazarus\ImageToPDF transformam isso em um fluxo de trabalho de lote completo: selecione vários arquivos de imagem, pré-visualize o item selecionado, crie uma página PDF por imagem, escale cada imagem em uma página A4 retrato ou paisagem, salve o resultado e abra o PDF gerado.

Destaque de Pesquisa do Visualizador

Destacar Cada Correspondência na Página Atual

var
  MatchCount: Integer;
begin
  // Pinta uma máscara clYellow sobre cada correspondência na página atual da visualização.
  // Mudar para uma página diferente limpa o destaque automaticamente.
  PdfView1.HighlightColor := clYellow;
  MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
  Status.Caption := Format('%d correspondência(s) nesta página', [MatchCount]);

  // Remover programaticamente a sobreposição antes da mudança de página
  PdfView1.ClearHighlight;
end;

Layout e Cor da Página do Visualizador

Modo de Ajuste e Fundo da Página

// FitMode mantém documentos longos enquadrados durante Resize ou mudanças de página.
// Definir Zoom diretamente cancela FitMode (reverte para pfmNone).
PdfView1.FitMode := pfmFitPage;       // página inteira na viewport
// PdfView1.FitMode := pfmFitWidth;   // largura da página corresponde à largura da viewport
// PdfView1.FitMode := pfmActualSize; // 100%

// PageColor desacopla a página PDF renderizada da Color do controle hospedeiro
// — configuração típica de visualizador em modo escuro mantém a área de rolagem
// escura enquanto a página PDF permanece branco-papel.
PdfView1.Color     := clBlack;        // área de rolagem
PdfView1.PageColor := clWhite;        // fundo da página PDF

// Sombra projetada e borda de página opcionais para destacar a página de um fundo escuro
PdfView1.PageShadowSize  := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;

Explorando a Interação do Visualizador no ViewerInteractionLab

Os projetos Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab e Demo\Lazarus\ViewerInteractionLab mantêm a superfície moderna de interação do TPdfView em um formulário compacto: abrir ou criar um PDF de exemplo, alternar DisplayMode, aplicar FitMode, alterar PageColor, rotacionar páginas, destacar correspondências de pesquisa da página atual com HighlightSearchText, habilitar ou desabilitar a seleção de texto pelo usuário, inspecionar SelectedText, chamar SelectAll, CopySelectionToClipboard e ClearSelection, e observar as coordenadas DeviceToPage atualizarem na barra de status enquanto o mouse se move sobre a página.

Pesquisando e Selecionando Texto no SearchAndSelect

Os projetos Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect e Demo\Lazarus\SearchAndSelect transformam o fluxo de trabalho de pesquisa e seleção do visualizador em amostras autônomas. Eles abrem ou criam um PDF, listam resultados de pesquisa em todas as páginas com número de página, índice de caractere e texto de pré-visualização, suportam navegação Anterior / Próximo e salto de resultado por clique duplo, destacam correspondências da página atual com HighlightSearchText, expõem opções de correspondência de maiúsculas e palavra inteira, alternam AllowUserTextSelection, pré-visualizam SelectedText, controlam SelectAll, CopySelectionToClipboard e ClearSelection a partir de botões, e permitem que os usuários alternem entre modos de exibição único e layout.

Lendo Preferências de Impressão PDF no PrintPreferences

Os projetos Demo\Delphi\PrintPreferences, Demo\CBuilder\PrintPreferences e Demo\Lazarus\PrintPreferences leem as preferências de impressão do autor antes de imprimir. Eles exibem PrintCopies, PrintPageRanges, PrintScaling e PrintPaperHandling, podem copiar cópias e intervalos fornecidos pelo autor para as entradas de impressão, e mostram como SetPdfPrintPaperHandlingDevMode mapeia a intenção de duplex do PDF em um DEVMODE do Windows.

Impressão Padrão de PDF no PrintPDF

Os projetos Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF e Demo\Lazarus\PrintPDF mostram o fluxo de trabalho padrão de impressão: abrir um PDF, pré-visualizar páginas, escolher intervalos de páginas e cópias no diálogo de impressão da plataforma, honrar o modo de agrupamento, renderizar cada página através de TPdf.RenderPage e expor o progresso de impressão com cancelamento. A amostra Delphi também aceita arquivos PDF arrastados para carregamento rápido.

Visualização Rápida de Múltiplas Páginas no MultiPageViewer

Os projetos Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer e Demo\Lazarus\MultiPageViewer exercitam visualização contínua, em layout e com capa, com fluxos de trabalho regulares de navegação entre páginas e zoom. Também são úteis para testar a velocidade de abertura na primeira página e a responsividade de renderização em PDFs muito grandes: em testes de arquivos grandes, documentos de 2,x GB que podem fazer o Acrobat ou o Foxit demorar 20-30 segundos antes da primeira visualização podem se tornar utilizáveis quase imediatamente através do caminho do visualizador PDFiumPas.

Revisão Lado a Lado no SplitView

Os projetos Demo\Delphi\SplitView, Demo\CBuilder\SplitView e Demo\Lazarus\SplitView mostram revisão de PDF lado a lado. As amostras C++Builder e Lazarus focam em comparação de dois ou três painéis, seleção de visualização ativa, comandos opcionais de navegação sincronizada, presets de zoom compartilhados e rotação para a visualização selecionada ou todas as visualizações carregadas. Use essas amostras para comparar documentos grandes mantendo a primeira visualização utilizável e a renderização de páginas subsequentes responsivas; os mesmos arquivos PDF de 2,x GB que podem levar de 20 a 30 segundos para abrir no Acrobat ou Foxit podem se tornar utilizáveis quase imediatamente no caminho do visualizador PDFiumPas.

Auditando a Superfície de Risco do PDF no SecurityAudit

Os projetos Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit e Demo\Lazarus\SecurityAudit abrem um PDF somente leitura e listam estado de permissão, anexos incorporados, ações JavaScript do documento, anotações de link URI e Launch, links da web, resumos de assinatura, estado XFA, disponibilidade de auxiliar V8 e callbacks de recurso não suportado. As mesmas linhas de resumo e descoberta podem ser salvas ou copiadas como um relatório de auditoria TXT ou salvas como JSON estruturado. Eles são destinados como painéis de risco da aplicação hospedeira, não como detecção de malware.

Manipulação de Links do Visualizador

Cliques Seguros para Seguir com Listas de Permissão por Ação

// LinkOptions controla a manipulação automática dos quatro tipos de ação
// de link PDF. Os padrões habilitam apenas goto e URI; launch (executar
// um programa) e GotoR incorporado (saltar para outro arquivo) permanecem
// DESLIGADOS, de modo que um clique perdido não possa executar código
// arbitrário.
PdfView1.LinkOptions :=
  [loAutoGoto, loAutoOpenURI];  // padrões seguros

// OnAnnotationLinkClick dispara para destinos dentro do documento (saltos
// de página, destinos nomeados, cadeias de ação). Marque Handled = True
// para impedir que a visualização siga automaticamente o link.
procedure TForm1.PdfView1AnnotationLinkClick(Sender: TObject;
  LinkIndex: Integer; const Action: TPdfAction;
  var Handled: Boolean);
begin
  if Action.Kind = paUri then
  begin
    if MessageDlg('Abrir ' + Action.Uri + '?', mtConfirmation, [mbYes, mbNo], 0)
      <> mrYes then
      Handled := True;  // suprime tratamento automático
  end;
end;

// OnWebLinkClick dispara para strings URI descobertas pelo scanner de
// links da web do PDFium (URLs autodetectadas no stream de texto, mesmo
// sem uma anotação /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;

Edição de Widget de Formulário no Visualizador

Seleção e Desfazer / Refazer

// No widget AcroForm em foco, seis novos métodos controlam o histórico
// de edição de formulário do PDFium. Todos os seis fazem curto-circuito
// de forma segura se nenhum widget estiver em foco ou o documento não
// possuir 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;

Bloqueando o Visualizador para Quiosques

Desabilitar Gestos de Navegação de Página

// Os três têm o padrão True, de modo que aplicações existentes atualizem
// sem alteração de comportamento. Defina como False para quiosques,
// painéis de visualização ou incorporações somente leitura sem fazer
// subclasse de TPdfView.
PdfView1.AllowUserPageChange       := False;  // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // saltos de roda para cima/baixo
PdfView1.AllowUserTextSelection    := False;  // seleção de texto via mouse/teclado

// Ctrl+roda é reservado para zoom e é ignorado pelo manipulador de
// navegação independentemente de ChangePageOnMouseScrolling.

Painel Lateral de Miniaturas

Vinculando um TPdfThumbnailView a um TPdfView

// TPdfThumbnailView (unit FPdfThumbnail) renderiza uma miniatura de página
// por linha em um painel lateral rolável.
uses FPdfThumbnail;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ThumbView.Pdf              := Pdf1;
  ThumbView.ThumbnailWidth   := 120;
  ThumbView.ThumbnailHeight  := 160;
  ThumbView.SelectionColor   := $00CFA85F;  // destaque quente
  ThumbView.OnPageClick      := ThumbViewPageClick;
end;

procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
  PdfView1.PageNumber := PageIndex + 1; // PageIndex é baseado em zero
end;

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

Exportação em Lote Cancelável Cooperativamente

PDF para JPG Multiarquivo com Progresso por Arquivo e por Página

// Padrão usado por Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport
// e Demo\Lazarus\BatchExport. Um TPdf por arquivo, IPdfCancellationToken
// para cancelar a partir da UI, TPdfStreamAdapter para carregamento de
// arquivo com buffer.
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;

Coordenadas e Teste de Acerto do Visualizador

Ajustar a Página Atual e Inspecionar Texto sob o Mouse

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;

Tratamento de Erros

Operações de PDF Robustas

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 e Acessibilidade

Detectar Tagged PDF e Percorrer a Árvore de Estrutura

// IsTagged é uma sondagem barata no Catalog — True quando /StructTreeRoot existe.
// StructureElements materializa a árvore em um array plano de
// records TPdfStructureElement com tipo, título, texto alternativo, texto real,
// texto de expansão, idioma, nível, índice do pai e contagens de filhos /
// marked content / atributos.
var
  Elements: TPdfStructureElements;
  I: Integer;
begin
  if not Pdf1.IsTagged then
  begin
    Log.Add('Documento NÃO está tagged — ferramentas de acessibilidade podem ter dificuldades.');
    Exit;
  end;
  if Pdf1.Language = '' then
    Log.Add('Tagged PDF sem /Lang — falha em 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;

Seleção de Modo de Impressão

Definindo Modo de Impressão EMF / PostScript / Máscara de Imagem

// SetPdfPrintMode é uma função global que mapeia para o
// FPDF_SetPrintMode do PDFium. Use o valor TPdfPrintMode correspondente
// para o driver de impressora em uso.
SetPdfPrintMode(pmEmf);                   // GDI EMF (padrão)
// SetPdfPrintMode(pmTextOnly);           // renderização somente texto
// SetPdfPrintMode(pmPostScript2);        // PostScript Nível 2
// SetPdfPrintMode(pmPostScript3);        // PostScript Nível 3
// SetPdfPrintMode(pmPostScript2PassThrough);
// SetPdfPrintMode(pmPostScript3PassThrough);
// SetPdfPrintMode(pmEmfImageMasks);      // EMF + máscaras de imagem
// SetPdfPrintMode(pmPostScript3Type42);  // PostScript 3 + fontes Type 42

Lendo Configurações de Impressão Pretendidas pelo Autor

// PrintCopies, PrintPageRanges, PrintScaling e PrintPaperHandling são
// as entradas do dicionário de preferências do visualizador — elas
// codificam o que o autor do documento queria que os leitores de PDF
// usassem como padrão.
var
  Ranges: TPrintPageRanges;
  Range: TPrintPageRange;
begin
  ShowMessage('Cópias sugeridas: ' + IntToStr(Pdf1.PrintCopies));
  ShowMessage('Escala de impressão: ' + GetEnumName(TypeInfo(TPrintScaling),
    Ord(Pdf1.PrintScaling)));

  Ranges := Pdf1.PrintPageRanges; // intervalos baseados em um
  for Range in Ranges do
    Log.Add(Format('Páginas %d..%d', [Range.First, Range.Last]));

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

Ações JavaScript do Documento

Enumerando JavaScript da Árvore de Nomes

// JavaScript no nível do documento é armazenado na árvore de nomes
// /Names /JavaScript. JavaScriptAction[i] retorna uma entrada;
// JavaScriptActions retorna o array completo.
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;

Dicas de Desempenho

Otimizando Operações de 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;

// Caminho de renderização zero-copy (padrão desde v1.21.0): RenderPage grava
// diretamente no buffer DIB do TBitmap de destino quando os chamadores passam um
// bitmap pré-alocado, evitando uma cópia de largura * altura * 4 bytes.
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;