PDFium Delphi Component Docs

Exemples de programmation PDFium VCL

Choisir une démo exécutable

La collection de démos couvre 32 sujets : 32 projets Delphi, 31 projets C++Builder et 32 projets Lazarus, soit 95 projets natifs au total ; chaque plateforme conserve 22 démos de traitements complets dans son répertoire principal ; dix sujets ciblés se trouvent sous Miscellaneous, avec 29 projets natifs sur les trois plateformes ; XfaLab est disponible pour Delphi et Lazarus

Ouvrez d'abord un workflow du répertoire principal : PDFViewer pour un tour d'horizon des fonctionnalités, CreatePDF pour la création en direct, ContentExtractionLab pour un inventaire instantané, FormFill pour des formulaires modifiables, StandardsLab pour la validation et la conversion, ou IntakeWorkbenchCli pour l'intake automatisé. Ces six points d'entrée produisent immédiatement un résultat concret ; les exemples ciblés ci-dessous servent quand vous devez inspecter une API en détail

Démos des workflows principaux

DémoÀ quoi elle sertEn quoi elle diffère des démos associées
PDFViewerVisualisation générale et évaluation des fonctionnalitésLa visionneuse principale combine navigation, recherche, vignettes, modes d'affichage, impression et tour d'horizon des fonctionnalités
MultiPageViewerPages continues, doubles pages et mises en page avec couvertureUn exemple ciblé de mise en page appuyé sur les API de sélection intégrées de la visionneuse ; utilisez PDFViewer pour l'expérience de visionneuse la plus large
SplitViewComparer des pages ou des documents dans des panneaux séparésDémontre des positions de vue indépendantes et le routage des commandes vers le panneau actif, plutôt qu'une disposition deux pages dans une seule vue
SearchAndSelectRésultats de recherche multi-pages et sélectionUne liste de résultats et des options de recherche exposent le workflow de recherche plus directement qu'une barre d'outils de visionneuse
PrintPDFPrévisualisation et travaux d'impression ordinairesPlages de pages, mise à l'échelle, progression et annulation fournissent le workflow d'impression principal
ContentExtractionLabInspecter plusieurs catégories de contenu ensembleLes inventaires TXT/JSON décrivent les objets et prévisualisent le texte ; ils ne remplacent pas l'export complet du texte ou des fichiers image
ExtractImagesExporter les objets image intégrésExtrait des bitmaps d'images plutôt que des captures de pages entières ; le document d'exemple intégré contient deux vrais objets image sur chacune de ses deux pages
TableExtractionLabDétecter les tableaux et exporter en CSV/JSONTableaux à filets et alignés par espaces, cellules fusionnées, groupes de continuation et contrôles de confiance sont indépendants de l'extraction en texte brut
AttachmentLire, ajouter et exporter les pièces jointes intégréesLes opérations sur les charges utiles des pièces jointes vont au-delà du listage de noms et de tailles dans ContentExtractionLab
BatchExportExporter des images de pages depuis plusieurs documentsSortie en flux, statut par fichier et échecs isolés démontrent le traitement par lots plutôt qu'une conversion unique
CreatePDFCréer du contenu de page avec un aperçu en directLes trois variantes natives créent du texte recherchable, des formes vectorielles, une vraie image raster, des tableaux et une annotation de revue ; les options reconstruisent l'aperçu avant un enregistrement explicite
ImageToPDFConstruire des pages à partir de fichiers image en entréeDisposition, ajustement, ordre des images et gestion JPEG fournissent un workflow de conversion de documents
MergePDFAssembler des pages de plusieurs PDFDémontre l'import de documents et la composition ordonnée ; le découpage résout le workflow inverse
SplitPDFÉcrire des groupes de pages sélectionnées dans des PDF séparésLe nommage des sorties et la validation des groupes de pages diffèrent de la fusion ou du rendu en images de pages
WatermarkStampAppliquer des surimpressions répétées à des pages existantesLe placement et le tamponnage de pages par lots complètent les API de création de base
FormFillSaisie AcroForm interactive et aplatissementLa saisie de champs et les apparences enregistrées se distinguent de l'interchange XFDF et du comportement propre à XFA
AccessibilityReaderLecture interactive et synthèse vocaleLe lecteur graphique démontre la navigation en lecture et le surlignage plutôt qu'un audit de conformité
AccessibilityWorkbenchCliRevue d'accessibilité par lots et génération de rapportsAnomalies, synthèses et critères de blocage de revue servent l'automatisation ; les anomalies ne remplacent pas une revue d'accessibilité manuelle
StandardsLabValidation et conversion propres aux normesLe lab interactif expose les opérations et options au lieu de se contenter de générer un rapport agrégé
PreflightReportRapports de normes interactifsL'interface graphique prend en charge l'inspection manuelle et l'export de rapports
PreflightReportCliRapports de normes automatisésEntrées par lots, formats de rapports et blocages par code de sortie fournissent la contrepartie automatisée de PreflightReport
IntakeWorkbenchCliIntake par lots, triage et routageRapports agrégés, blocages sur échec et décisions de routage en font un workflow de traitement au-delà de l'interface graphique SecurityAudit

Exemples ciblés dans Miscellaneous

Ouvrez ces projets depuis Demo\Delphi\Miscellaneous, Demo\CBuilder\Miscellaneous ou Demo\Lazarus\Miscellaneous ; ils conservent des recettes API utiles et des exports ciblés, le répertoire principal se concentrant sur les traitements complets ; les vérifications Unicode de base et le parcours fixe d’écriture puis d’énumération d’annotations sont couverts par la création, l’extraction, les échanges XFDF et les tests API ; ils n’ont plus de projets de démo distincts

DémoÀ quoi elle sertEn quoi elle diffère des démos associées
SecurityAuditInspecter la posture de sécurité de façon interactiveL'interface graphique met l'accent sur les permissions, les scripts, les signatures et les détails de sécurité plutôt que sur le routage de fichiers
XfdfLabImporter et exporter des données XFDFL'interchange et l'enregistrement explicite du PDF importé démontrent la persistance ; exporter XFDF seul n'enregistre pas le PDF modifié
AccessibilityLabParcourir les API de lecture dans une consoleUne visite guidée exécutable compacte expose les unités de lecture, les boîtes de mots, la structure et la sortie du script de synthèse vocale
ExtractTextExporter le texte complet de pages sélectionnéesSortie de la couche de texte, séparateurs de pages et annulation se distinguent des prévisualisations d'inventaire et de la détection de tableaux ; aucun OCR n'est effectué
FontPropertiesInspecter les métriques de police et de caractèreS'ouvre avec les métriques réelles de police et de caractère du premier objet texte ; sélectionnez une autre ligne pour comparer les polices sans exporter les charges utiles du document
NavigationLabSignets, destinations et actions de navigationLa visite guidée compacte des API complète les contrôles de navigation visibles dans PDFViewer
PDFtoJpgRendre un document en images de pagesUn exemple compact de rendu mono-document ; Delphi expose des plages de pages discrètes, tandis que BatchExport se concentre sur plusieurs documents en entrée
PrintPreferencesFeuilles N-up et préférences d'impression de l'auteur du PDFCopies, filtrage pages impaires/paires et préférences fournies par l'auteur sont au cœur de la démonstration ; c'est un compagnon de PrintPDF
ViewerInteractionLabDiagnostiquer les interactions souris, clavier et coordonnéesLe journal d'interactions et la conversion de coordonnées page/périphérique en font un outil de diagnostic d'API
XfaLabPaquets XFA statiques et capacités du runtimeOuvre les paquets template et datasets avec le runtime ordinaire ; le rendu XFA interactif requiert un runtime avec XFA activé, avec des variantes Delphi et Lazarus

Commencer par un tour d'horizon des fonctionnalités qui fonctionne

PDFViewer ouvre son PDFiumDemoShowcase.pdf embarqué, un document de six pages avec du texte recherchable, des graphiques vectoriels, deux images intégrées, un tableau, quatre champs AcroForm, des signets de chapitres, des liens, une note de revue et deux pièces jointes. La barre d'expérience propose Restart showcase, Find live text, 2-page spread, Night reading, Inspect this PDF et Explore forms ; chaque action agit sur le document courant

CreatePDF ouvre un aperçu de rapport en direct sur deux pages. Modifiez son titre et son auteur, ou basculez les formes, le tableau et le filigrane pour reconstruire le contenu réel des pages ; l'option Unicode ajoute une troisième page. Rien n'est écrit tant que vous n'enregistrez pas, et le PDF enregistré reste recherchable

MultiPageViewer, SearchAndSelect, ContentExtractionLab et FormFill ouvrent le même document pour leurs propres workflows, avec des résultats de recherche, un inventaire d'extraction et des champs modifiables prêts immédiatement. Miscellaneous\NavigationLab utilise sa propre copie locale pour une visite guidée des API de signets et de destinations

Attachment démarre avec deux vrais fichiers intégrés. PrintPreferences démarre avec quatre pages et des préférences d'auteur pour deux copies, des plages de pages sélectionnées, la mise à l'échelle et le recto verso ; le workflow d'impression actuel applique les copies et les plages de pages, tandis que la mise à l'échelle et le recto verso sont inspectés et rapportés. XfaLab démarre avec de vrais paquets template et datasets pour l'inspection XFA statique ; le rendu XFA interactif requiert un runtime approprié et constitue une capacité distincte

Chaque projet de visualisation, de formulaire, d'extraction et de navigation contient son propre PDFiumDemoShowcase.pdf. Copiez le répertoire du projet et conservez ce fichier embarqué à côté de l'exécutable distribué ; les builds Lazarus le placent automatiquement. Le tour d'horizon fonctionne hors ligne, et les visionneuses généralistes et les exemples d'extraction fournissent aussi du contenu de repli généré quand le PDF local n'est pas disponible

Copier et maintenir les exemples natifs

Chaque démo native contient sa propre implémentation d'exemple. Les points d'entrée Delphi et Lazarus de AccessibilityLab, AccessibilityWorkbenchCli, IntakeWorkbenchCli et PreflightReportCli incluent le workflow complet dans le répertoire du projet, avec l'initialisation propre au compilateur et l'écriture JPEG. Les exemples Attachment, de préférences d'impression et XFA construisent aussi localement leurs propres données d'entrée spécifiques à chaque fonctionnalité. Seuls la bibliothèque, le runtime natif correspondant et les dépendances de framework habituelles du compilateur sont nécessaires en dehors du projet copié

Choisissez l'extraction d'objets image quand vous avez besoin des bitmaps intégrés, le rendu de page quand vous avez besoin de la page visible, et l'extraction de texte quand le PDF a déjà une couche de texte. Un rapport qui liste ces catégories n'exporte pas leurs charges utiles complètes. Ces frontières gardent les exemples ciblés utiles sans transformer chaque démo en une visionneuse ou un workbench généraliste de plus

Opérations de base sur les documents

Charger et afficher un document 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;

Créer un nouveau document 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;

Opérations sur le texte

Extraire du texte d'un 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;

Rechercher du texte

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;

Ajouter du texte avec formatage

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;

Inspecter les métadonnées de caractère et de police

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\Miscellaneous\FontProperties, Demo\CBuilder\Miscellaneous\FontProperties et Demo\Lazarus\Miscellaneous\FontProperties montrent les mêmes métadonnées via TPdf et TPdfView

Construire un rapport d'extraction de contenu multi-catégorie

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. ExtractImages et Attachment fournissent des workflows d'export de charges utiles, tandis que Miscellaneous\ExtractText et Miscellaneous\FontProperties conservent des exemples d'API 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

Détecter et extraire des tableaux

Les projets Demo\Delphi\TableExtractionLab, Demo\CBuilder\TableExtractionLab et Demo\Lazarus\TableExtractionLab transforment la détection de tableaux en un flux de travail unique. Le laboratoire construit un document d'exemple contenant un tableau à filets avec cellules d'en-tête fusionnées, un tableau aligné par espaces sans filets et un long tableau qui se coupe à la frontière d'une page, puis exécute ExtractTables pour la page courante ou ExtractDocumentTables pour tout le document et rapporte les lignes, les colonnes, les valeurs RowSpan et ColumnSpan, les indicateurs d'en-tête, le mode de détection, la confiance et les groupes de continuation. Les options DetectRuledTables, DetectWhitespaceTables, DetectContinuations, DetectFilledRulings et MinConfidence sont modifiables dans l'interface, et le tableau sélectionné s'exporte via TPdfTable.ToCsv et TPdfTable.ToJson. Le laboratoire traite aussi un dossier entier par lots : Batch folder exécute ExtractDocumentTables sur chaque PDF, rapporte le nombre de tableaux par document, signale les scans dépourvus de couche de texte et retente les documents vides avec un préréglage de colonnes compactes ; Export all CSV écrit chaque tableau détecté à côté des fichiers sources. Chaque document s'ouvre dans sa propre instance de TPdf, afin qu'un fichier illisible ne puisse pas affecter le reste du lot

Opérations sur les formulaires

Travailler avec les champs de formulaire

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;

Aplatir les valeurs de formulaire remplies dans la page

// 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] := '[email protected]';
Pdf1.GenerateFormAppearances;
if Pdf1.FlattenAllPages then
  Pdf1.SaveAs('C:\Application.flat.pdf');
Pdf1.Active := False;

Opérations sur les images

Extraire des images

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;

Ajouter des images

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;

Opérations sur les annotations

Travailler avec les annotations

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;

Opérations de dessin

Dessiner des formes

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;

Navigation et signets

Travailler avec les signets

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;

Travailler avec les destinations nommées

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;

Opérations sur les pièces jointes

Travailler avec les pièces jointes

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;

Gestion des pages

Opérations sur les pages

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

Enregistrement et compression

Enregistrer avec compression

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

Créer une sortie d'archivage PDF/A-1b

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

Valider la conformité PDF/A

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;

Inspecter plusieurs normes PDF dans StandardsLab

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é

Générer des rapports de contrôle en amont

L'unité FPdfPreflightReport encapsule ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR et ValidatePdfVT dans un seul objet de rapport interrogeable avec sévérité Info / Warning / Error, agrégation des statuts et des problèmes, recherche ponctuelle, recherche, filtrage et empreintes SHA-256 stables. Les rapports peuvent être émis en texte, HTML, Markdown, JSON, CSV, SARIF 2.1.0, JUnit XML, NDJSON ou XML natif. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport et Demo\CBuilder\PreflightReport fournissent des workflows interactifs de sélection et de prévisualisation, tandis que Demo\Delphi\PreflightReportCli ajoute des arguments d'entrée / sortie scriptables, le filtrage par norme, l'intégration facultative d'une pièce jointe de rapport, le blocage par code de sortie CI, le traitement par lots de listes et de répertoires, le routage résistant aux collisions, les synthèses agrégées et les manifestes CSV. 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é

Chargement en flux et documents volumineux

Charger un PDF à la demande avec LoadCustomDocument

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

Annulation asynchrone et rendu progressif

Jetons d'annulation coopératifs

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

Rendu de page progressif annulable

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

Exécuter des rendus sur un thread d'arrière-plan avec TPdfFuture

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

Matrices de transformation

Composer Translate / Scale / Rotate / Skew

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

Composition de pages

Importer un sous-ensemble de pages par index

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

Sortie composite N-up

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

Réordonner les pages sur place

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

Filigrane via réutilisation de page Form XObject

// 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

Insertion d'image (surcharges AddImage directes)

Ajouter un fichier image enregistré ou un TBitmap

// 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é

Surbrillance de recherche dans la visionneuse

Surligner chaque correspondance sur la page courante

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;

Mise en page de la visionneuse et couleur de page

Mode d'ajustement et arrière-plan de page

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

Explorer l'interaction de visionneuse dans ViewerInteractionLab

Les projets Demo\Delphi\Miscellaneous\ViewerInteractionLab, Demo\CBuilder\Miscellaneous\ViewerInteractionLab et Demo\Lazarus\Miscellaneous\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

Rechercher et sélectionner du texte dans SearchAndSelect

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

Lire les préférences d'impression PDF dans PrintPreferences

Les projets Demo\Delphi\Miscellaneous\PrintPreferences, Demo\CBuilder\Miscellaneous\PrintPreferences et Demo\Lazarus\Miscellaneous\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

Impression PDF standard dans PrintPDF

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

Visualisation rapide multi-pages dans MultiPageViewer

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 PDFium Delphi Component

Revue côte à côte dans SplitView

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 PDFium Delphi Component

Auditer la surface de risque PDF dans SecurityAudit

Les projets Demo\Delphi\Miscellaneous\SecurityAudit, Demo\CBuilder\Miscellaneous\SecurityAudit et Demo\Lazarus\Miscellaneous\SecurityAudit ouvrent un PDF en lecture seule et recensent permissions, pièces jointes incorporées, actions JavaScript du document, annotations de lien URI et Launch, liens web, résumés de signatures, état XFA, disponibilité des helpers V8 et callbacks de fonctionnalités non prises en charge ; les mêmes lignes de synthèse et résultats peuvent être enregistrées ou copiées en rapport TXT, ou enregistrées en JSON structuré ; ces projets servent de panneaux de risque pour l’application hôte, pas de détecteurs de logiciels malveillants

Gestion des liens dans la visionneuse

Clic-pour-suivre sécurisé avec listes blanches par action

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

Édition de widget de formulaire dans la visionneuse

Sélection et Annuler / Rétablir

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

Verrouiller la visionneuse pour les bornes interactives

Désactiver les gestes de navigation de page

// 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.

Panneau latéral de miniatures

Lier un TPdfThumbnailView à un TPdfView

// 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.PdfView1PageNumberChanged(Sender: TObject);
begin
  ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;

Export par lot annulable de manière coopérative

PDF multi-fichiers vers JPG avec progression par fichier et par page

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

Coordonnées de la visionneuse et hit-test

Ajuster la page courante et inspecter le texte sous la souris

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;

Gestion des erreurs

Opérations PDF robustes

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 et accessibilité

Détecter Tagged PDF et parcourir l'arbre de structure

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

Sélection du mode d'impression

Définir le mode d'impression EMF / PostScript / masque d'image

// 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

Lire les paramètres d'impression voulus par l'auteur

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

Actions JavaScript du document

Énumérer le JavaScript de l'arbre de noms

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

Conseils de performance

Optimiser les opérations 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;

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