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;
Les projets Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties et Demo\Lazarus\FontProperties montrent les mêmes métadonnées via TPdf et TPdfView.
Les projets Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab et Demo\Lazarus\ContentExtractionLab transforment l'extraction de contenu en un workflow unique. Ils ouvrent ou créent un PDF, permettent aux utilisateurs de choisir les métadonnées, le texte de page, les objets de page, les images, les pièces jointes, les liens, les annotations, les signets et les métriques de police / caractère, puis génèrent une synthèse plus un rapport TXT ou JSON détaillé. Le lab inclut également la sélection toutes / aucune catégorie, la copie de rapport vers le presse-papiers et les actions d'enregistrement du PDF source afin que les utilisateurs puissent conserver le PDF d'exemple généré à côté de son rapport d'extraction. Les démos plus anciennes ExtractText, ExtractImages, Attachment et FontProperties demeurent des exemples ciblés ; ExtractText prend en charge les options de plage de pages et de séparateur de pages pour produire une sortie texte par page sous Delphi, C++Builder et Lazarus, tandis que ContentExtractionLab est l'exemple d'inventaire 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;
// Sans aplatissement, FormField[i] := value écrit uniquement l'entrée /V —
// l'apparence visible provient du flux /AP et peut ne pas se rafraîchir.
// GenerateFormAppearances régénère /AP pour chaque widget, puis
// FlattenAllPages intègre les annotations et widgets de chaque page dans
// du contenu permanent et non modifiable.
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);
// Créer un document PDF compressé
Pdf1.CreateDocument;
Pdf1.Compressed := True; // garantir que tous les flux sont compressés FlateDecode (défaut)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // format A4
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;
// Enregistrer avec options
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // enregistrement incrémentiel
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // suppression de la sécurité
// Enregistrer vers une cible de version PDF spécifique. Le validateur de
// version PDF rejette toute fonctionnalité introduite après la cible
// choisie (JBIG2Decode, MarkInfo, chiffrement AES, annotations 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');
// SaveAsPdfA post-traite l'enregistrement de base avec une mise à jour
// incrémentielle qui injecte un flux de métadonnées XMP, un OutputIntent
// ICC sRGB et un catalogue de document mis à jour, de sorte que le résultat
// soit conforme PDF/A-1b. Le profil sRGB IEC61966-2.1 intégré est utilisé
// automatiquement lorsqu'aucun profil personnalisé n'est fourni.
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 avec un profil ICC personnalisé (pipelines d'impression CMYK par exemple)
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;
// Sortie PDF/A en mémoire
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;
// Sonde de style header bon marché — lit la conformité sans itérer
// le validateur complet. Résultat : 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 := 'Not PDF/A';
else
Status.Caption := 'Unknown conformance';
end;
// Validateur complet avec diagnostics par problème
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;
Les projets Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab et Demo\Lazarus\StandardsLab chargent ou créent un PDF, exécutent ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR et ValidatePdfVT, puis affichent chaque niveau de conformité détecté et le nombre de problèmes dans une seule grille. Leurs boutons d'enregistrement appellent SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR et SaveAsPdfVT afin que le workflow de sortie avec marqueurs soit visible sans écrire de harnais personnalisé.
L'unité FPdfPreflightReport encapsule ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR et ValidatePdfVT dans un seul objet de rapport. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport et Demo\CBuilder\PreflightReport permettent aux utilisateurs de choisir les normes, générer une synthèse, inspecter statut, priorité, catégories de problèmes, comptes par catégorie, prochaines actions, codes de problème et actions recommandées, basculer la prévisualisation entre texte, Markdown, JSON et CSV, et exporter des rapports TXT, HTML, Markdown, JSON ou CSV. L'échantillon Delphi GUI ajoute également un aperçu Action Plan qui regroupe les standards non conformes, priorités, codes de problème et recommandations en une liste de corrections. Demo\Delphi\PreflightReportCli expose le même chemin de rapport en tant que workflow console avec arguments d'entrée / sortie, mot de passe facultatif, sélection du format de sortie incluant plan / actionplan, filtrage par norme, intégration facultative d'un rapport mono-fichier attach=output.pdf, codes de sortie de blocage CI failon=, traitement batch=list.txt, analyse de répertoires batchdir=folder avec récursion facultative et traitement stable trié par chemin, routage des rapports outdir=reports, noms de rapport résistant aux collisions pour les fichiers d'entrée à nom dupliqué, synthèses TXT / JSON / HTML avec totaux agrégés et de statut par norme, manifestes de lignes CSV avec paramètres d'exécution répétés par élément, et un mode d'exemple sans argument pour les scripts et les tâches CI. Le rapport généré indique que les validateurs intégrés couvrent les contrôles au niveau marqueur et certains contrôles au niveau fichier, de sorte que le contrôle complet au niveau contenu peut toujours être délégué à un moteur de validation dédié.
// LoadCustomDocument permet à PDFium de récupérer les blocs depuis tout
// TStream cherchable à la demande. Le flux n'est PAS copié en mémoire, de
// sorte que les PDF de plusieurs Go, les flux de corps HTTP distants et les
// sources adossées à une base de données sont tous viables sans tampon
// préalable.
var
FileStream: TFileStream;
begin
// Streaming en lecture depuis fichier (pas de copie en mémoire)
FileStream := TFileStream.Create('C:\Huge.pdf',
fmOpenRead or fmShareDenyWrite);
Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
// ^^ propriété transférée — Pdf1 libère FileStream sur UnloadDocument.
Pdf1.Active := True;
ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;
// Flux détenu en externe (l'appelant le libère aprè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 expose IPdfCancellationToken /
// IPdfCancellationTokenSource pour une sémantique d'annulation depuis
// l'UI qui fonctionne à travers les chemins longs d'export et de rendu.
uses FPdfAsync;
var
CancelSource: IPdfCancellationTokenSource;
CancelToken: IPdfCancellationToken;
begin
CancelSource := PdfCancellationTokenSource;
CancelToken := CancelSource.Token;
// ... l'utilisateur clique sur Annuler ...
CancelSource.Cancel;
// Le worker vérifie périodiquement CancelToken.IsCancelled, ou le chemin
// de rendu l'interroge via le callback IFSDK_PAUSE de PDFium.
if CancelToken.IsCancelled then
Memo1.Lines.Add('Cancelled by user');
end;
// RenderPageProgressive permet à un long rendu haute résolution d'être
// interrompu en milieu de page lorsque l'utilisateur clique sur Annuler,
// au lieu de bloquer l'appelant jusqu'à la fin.
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<T> lance un worker sur un thread d'arrière-plan et renvoie
// une enveloppe TPdfFutureResult<T> vers le thread principal.
//
// Important : TPdf sérialise les appels de rendu par instance, mais le
// worker reste responsable de la répartition et de la durée de vie du
// document. Ne mutez pas un TPdf pendant qu'un rendu est en cours ;
// l'export par lot conserve généralement un TPdf par fichier.
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 encapsule FS_MATRIX de PDFium afin que les appelants puissent
// construire une transformation de manière déclarative et la passer à
// FPDFPageObj_SetMatrix.
uses FPdfMatrix, FPdfView;
var
M: TPdfMatrix;
RawMatrix: FS_MATRIX;
PageObj: FPDF_PAGEOBJECT;
begin
M := TPdfMatrix.Create;
try
M.Translate(72, 200); // déplacer 1in à droite, ~2,8in vers le haut
M.Scale(0.5, 0.5); // tampon à mi-taille
M.Rotate(15); // rotation de 15 degrés CCW
// M.HorizontalFlip;
// M.VerticalFlip;
// M.CentralFlip;
// M.Skew(10, 5);
// M.Multiply(Other); // post-multiplier avec une autre matrice
// Appliquer à tout objet de page PDFium (text / path / image / form XObject)
RawMatrix := M.Handle;
FPDFPageObj_SetMatrix(PageObj, RawMatrix);
finally
M.Free;
end;
end;
// ImportPagesByIndex copie un tableau d'index explicite base zéro depuis
// un PDF source dans ce document. InsertAt = 0 insère avant la page 1 ;
// PageCount ajoute à la fin.
var
Source: TPdf;
begin
Source := TPdf.Create(nil);
try
Source.FileName := 'C:\Report.pdf';
Source.Active := True;
// Importer les pages 0, 2, 4 (base un : pages 1, 3, 5) depuis Source
// et les ajouter à la fin de Pdf1.
Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);
// Un tableau vide importe toutes les pages source
// Pdf1.ImportPagesByIndex(Source, [], 0);
finally
Source.Free;
end;
end;
// ImportNPagesToOne renvoie un nouveau TPdf dont les pages sont des
// composites NumX*NumY de CE document. OutputWidth/Height sont des unités
// utilisateur PDF (1 unité = 1/72 in).
var
Composite: TPdf;
begin
Pdf1.FileName := 'C:\Slides.pdf';
Pdf1.Active := True;
// A4 paysage 4-up : 842 x 595 pt, 2 colonnes x 2 lignes
Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
try
if Composite <> nil then
Composite.SaveAs('C:\Slides_4up.pdf');
finally
Composite.Free;
end;
end;
// MovePages prend les indices base zéro des pages à déplacer plus
// l'index de destination pour la première page déplacée après l'achèvement
// du déplacement. Les autres pages se décalent autour du bloc déplacé, de
// sorte que la longueur du document reste la même.
//
// Exemple : dans un document de 5 pages avec les pages numérotées [0,1,2,3,4],
// MovePages([2, 3], 0) réorganise en [2, 3, 0, 1, 4].
if not Pdf1.MovePages([2, 3], 0) then
ShowMessage('Invalid indices passed to MovePages');
// CreateXObjectFromPage fabrique un handle Form XObject réutilisable à
// partir de n'importe quelle page d'un autre TPdf. Tamponnez ce handle sur
// autant de pages de CE document que vous voulez — chaque tampon est un
// seul objet de page PDFium qui peut être positionné, mis à l'échelle et
// tourné via l'API matrice.
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;
// Encapsuler la page 0 de WatermarkSource en tant que XObject réutilisable
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;
// Centrer le filigrane sur la page
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; // ferme le handle FPDF_XOBJECT
end;
Pdf1.SaveAs('C:\Watermarked.pdf');
finally
WatermarkSource.Free;
end;
end;
Les projets Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp et Demo\CBuilder\WatermarkStamp transforment ce modèle en un workflow exécutable : ils créent un PDF cible et un PDF de tampon réutilisable, encapsulent la page de tampon avec CreateXObjectFromPage, insèrent le même Form XObject sur chaque page cible, appliquent des transformations TPdfMatrix pour des filigranes centrés et des tampons en haut à droite, et ajoutent des étiquettes de numéro de page facultatives avant d'enregistrer le résultat.
// AddImage(FileName) accepte tout format enregistré avec les unités
// graphiques VCL ou LCL (BMP, PNG via PngImage, JPG, ...).
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);
// AddImage(TBitmap) évite l'intermédiaire TPicture, idéal lorsque le
// bitmap provient déjà du rendu ou de la génération par lot.
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;
Les projets Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF et Demo\Lazarus\ImageToPDF transforment cela en un workflow par lots complet : sélectionner plusieurs fichiers image, prévisualiser l'élément sélectionné, créer une page PDF par image, mettre à l'échelle chaque image dans une page A4 portrait ou paysage, enregistrer le résultat et ouvrir le PDF généré.
var
MatchCount: Integer;
begin
// Peindre un masque clYellow sur chaque correspondance de la page courante
// de la vue. Passer à une autre page efface automatiquement la surbrillance.
PdfView1.HighlightColor := clYellow;
MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
Status.Caption := Format('%d match(es) on this page', [MatchCount]);
// Supprimer la superposition par programmation avant le changement de page
PdfView1.ClearHighlight;
end;
// FitMode conserve les longs documents cadrés lors d'un Resize ou d'un
// changement de page. Définir Zoom directement annule FitMode (il revient
// à pfmNone).
PdfView1.FitMode := pfmFitPage; // page entière dans la fenêtre
// PdfView1.FitMode := pfmFitWidth; // la largeur de la page correspond à la fenêtre
// PdfView1.FitMode := pfmActualSize; // 100 %
// PageColor dissocie la page PDF rendue du Color du contrôle hôte
// — configuration typique de visionneuse en mode sombre qui garde la
// zone de défilement sombre tandis que la page PDF reste blanc papier.
PdfView1.Color := clBlack; // zone de défilement
PdfView1.PageColor := clWhite; // arrière-plan de la page PDF
// Ombre portée et bordure de page facultatives pour détacher la page d'un fond sombre
PdfView1.PageShadowSize := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;
Les projets Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab et Demo\Lazarus\ViewerInteractionLab conservent la surface d'interaction TPdfView moderne dans un formulaire compact : ouvrir ou créer un PDF d'exemple, changer de DisplayMode, appliquer FitMode, changer PageColor, faire pivoter les pages, surligner les correspondances de recherche de la page courante avec HighlightSearchText, activer ou désactiver la sélection de texte par l'utilisateur, inspecter SelectedText, appeler SelectAll, CopySelectionToClipboard et ClearSelection, et voir les coordonnées DeviceToPage se mettre à jour dans la barre d'état pendant que la souris se déplace sur la page.
Les projets Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect et Demo\Lazarus\SearchAndSelect transforment le workflow de visionneuse de recherche et sélection en exemples autonomes. Ils ouvrent ou créent un PDF, listent les résultats de recherche multi-pages avec numéro de page, index de caractère et texte de prévisualisation, prennent en charge la navigation Précédent / Suivant et le saut aux résultats par double-clic, surlignent les correspondances de la page courante avec HighlightSearchText, exposent les options sensible à la casse et mot entier, basculent AllowUserTextSelection, prévisualisent SelectedText, pilotent SelectAll, CopySelectionToClipboard et ClearSelection depuis des boutons, et laissent les utilisateurs basculer entre les modes d'affichage simple et double page.
Les projets Demo\Delphi\PrintPreferences, Demo\CBuilder\PrintPreferences et Demo\Lazarus\PrintPreferences lisent les préférences d'impression de l'auteur avant l'impression. Ils affichent PrintCopies, PrintPageRanges, PrintScaling et PrintPaperHandling, peuvent copier les copies et plages fournies par l'auteur dans les entrées d'impression, et montrent comment SetPdfPrintPaperHandlingDevMode mappe l'intention recto-verso PDF vers un DEVMODE Windows.
Les projets Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF et Demo\Lazarus\PrintPDF montrent le workflow d'impression standard : ouvrir un PDF, prévisualiser les pages, choisir les plages de pages et les copies dans la boîte de dialogue d'impression de la plateforme, respecter le mode d'assemblage, rendre chaque page via TPdf.RenderPage, et exposer la progression de l'impression avec annulation. L'exemple Delphi accepte également les fichiers PDF déposés pour un chargement rapide.
Les projets Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer et Demo\Lazarus\MultiPageViewer exercent la visualisation continue, double page et tenant compte de la couverture avec des workflows réguliers de navigation et de zoom. Ils sont également utiles pour tester la vitesse d'ouverture à la première page et la réactivité du rendu sur de très grands PDF : dans les tests sur fichiers volumineux, des documents de 2,x Go qui peuvent faire passer Acrobat ou Foxit 20-30 secondes avant le premier rendu peuvent devenir utilisables presque immédiatement via le chemin de visionneuse PDFiumPas.
Les projets Demo\Delphi\SplitView, Demo\CBuilder\SplitView et Demo\Lazarus\SplitView montrent une revue PDF côte à côte. Les exemples C++Builder et Lazarus se concentrent sur la comparaison à deux ou trois panneaux, la sélection de la vue active, des commandes de navigation synchronisées facultatives, des préréglages de zoom partagés et la rotation pour la vue sélectionnée ou toutes les vues chargées. Utilisez ces exemples pour comparer des documents volumineux tout en gardant le premier rendu utilisable et le rendu de page subséquent réactifs ; les mêmes fichiers PDF de 2,x Go qui peuvent prendre 20-30 secondes à Acrobat ou Foxit pour s'ouvrir peuvent devenir utilisables presque immédiatement dans le chemin de visionneuse PDFiumPas.
Les projets Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit et Demo\Lazarus\SecurityAudit ouvrent un PDF en lecture seule et listent l'état des permissions, les pièces jointes intégrées, les actions JavaScript du document, les annotations de lien URI et Launch, les liens Web, les synthèses de signature, l'état XFA, la disponibilité des helpers V8 et les callbacks de fonctionnalités non prises en charge. Les mêmes lignes de synthèse et de résultats peuvent être enregistrées ou copiées sous forme de rapport d'audit TXT ou enregistrées sous forme de JSON structuré. Ils sont conçus comme des panneaux de risque d'application hôte, et non comme une détection de logiciel malveillant.
// LinkOptions contrôle la gestion automatique des quatre types d'actions
// de lien PDF. Les valeurs par défaut activent uniquement goto et URI ;
// launch (exécuter un programme) et embedded GotoR (sauter vers un autre
// fichier) restent DÉSACTIVÉS afin qu'un clic intempestif ne puisse pas
// exécuter de code arbitraire.
PdfView1.LinkOptions :=
[loAutoGoto, loAutoOpenURI]; // valeurs par défaut sécurisées
// OnAnnotationLinkClick se déclenche pour les destinations dans le
// document (sauts de page, destinations nommées, chaînes d'actions).
// Marquez Handled = True pour empêcher la vue de suivre automatiquement le lien.
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; // supprimer la gestion automatique
end;
end;
// OnWebLinkClick se déclenche pour les chaînes URI découvertes par le
// scanner de liens Web de PDFium (URL auto-détectées dans le flux de texte,
// même sans annotation /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;
// Sur le widget AcroForm actif, six nouvelles méthodes pilotent
// l'historique d'édition de formulaire de PDFium. Les six court-circuitent
// en toute sécurité si aucun widget n'a le focus ou si le document ne porte
// pas d'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;
// Les trois ont pour valeur par défaut True, de sorte que les applications
// existantes se mettent à niveau sans changement de comportement. Passez-
// les à False pour les bornes interactives, panneaux de prévisualisation
// ou intégrations en lecture seule sans sous-classer TPdfView.
PdfView1.AllowUserPageChange := False; // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // sauts molette haut/bas
PdfView1.AllowUserTextSelection := False; // sélection de texte souris/clavier
// Ctrl+molette est réservé au zoom et ignoré par le gestionnaire de
// navigation, quel que soit ChangePageOnMouseScrolling.
// TPdfThumbnailView (unité FPdfThumbnail) rend une miniature de page par
// ligne dans un panneau latéral défilant.
uses FPdfThumbnail;
procedure TForm1.FormCreate(Sender: TObject);
begin
ThumbView.Pdf := Pdf1;
ThumbView.ThumbnailWidth := 120;
ThumbView.ThumbnailHeight := 160;
ThumbView.SelectionColor := $00CFA85F; // surbrillance chaude
ThumbView.OnPageClick := ThumbViewPageClick;
end;
procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
PdfView1.PageNumber := PageIndex + 1; // PageIndex est en base zéro
end;
procedure TForm1.PdfView1PageNumberModifié (Changed)(Sender: TObject);
begin
ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;
// Modèle utilisé par Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport
// et Demo\Lazarus\BatchExport. Un TPdf par fichier, IPdfCancellationToken
// pour annuler depuis l'UI, TPdfStreamAdapter pour le chargement de fichier mis en tampon.
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 est une sonde de catalogue bon marché — True lorsque
// /StructTreeRoot existe. StructureElements matérialise l'arbre en un
// tableau plat de records TPdfStructureElement avec type, titre, texte
// alternatif, texte réel, texte d'expansion, langue, niveau, index parent
// et comptes d'enfants / contenu balisé / attributs.
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;
// SetPdfPrintMode est une fonction globale qui mappe vers FPDF_SetPrintMode
// de PDFium. Utilisez la valeur TPdfPrintMode correspondant au pilote
// d'imprimante utilisé.
SetPdfPrintMode(pmEmf); // GDI EMF (défaut)
// SetPdfPrintMode(pmTextOnly); // rendu texte seul
// SetPdfPrintMode(pmPostScript2); // PostScript Level 2
// SetPdfPrintMode(pmPostScript3); // PostScript Level 3
// SetPdfPrintMode(pmPostScript2PassThrough);
// SetPdfPrintMode(pmPostScript3PassThrough);
// SetPdfPrintMode(pmEmfImageMasks); // EMF + masques d'image
// SetPdfPrintMode(pmPostScript3Type42); // PostScript 3 + polices Type 42
// PrintCopies, PrintPageRanges, PrintScaling et PrintPaperHandling sont
// les entrées du dictionnaire des préférences de visionneuse — elles
// encodent ce que l'auteur du document voulait voir comme valeurs par
// défaut des lecteurs 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; // plages base un
for Range in Ranges do
Log.Add(Format('Pages %d..%d', [Range.First, Range.Last]));
if SetPdfPrintPaperHandlingDevMode(PrinterDevMode, Pdf1.PrintPaperHandling) then
ApplyPrinterDevMode(PrinterDevMode);
end;
// Le JavaScript de niveau document est stocké dans l'arbre de noms
// /Names /JavaScript. JavaScriptAction[i] renvoie une entrée ;
// JavaScriptActions renvoie le tableau complet.
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;
// Chemin de rendu sans copie (par défaut depuis v1.21.0) : RenderPage
// écrit directement dans le tampon DIB du TBitmap de destination lorsque
// les appelants passent un bitmap pré-alloué, évitant une copie de
// largeur * hauteur * 4 octets.
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;