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;
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;
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;
Os projetos Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties e Demo\Lazarus\FontProperties mostram os mesmos metadados tanto via TPdf quanto via TPdfView.
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.
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;
// 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;
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);
// 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');
// 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;
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;
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.
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.
// 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;
// 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;
// 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;
// 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;
// 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;
// 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;
// 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;
// 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');
// 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.
// 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.
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;
// 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;
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.
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.
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.
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.
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.
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.
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.
// 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;
// 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;
// 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.
// 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;
// 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;
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;
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;
// 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;
// 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
// 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;
// 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;
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;