Die Demosammlung umfasst 32 Themen: 32 Delphi-, 31 C++Builder- und 32 Lazarus-Projekte, insgesamt 95 native Projekte; jede Plattform behält 22 vollständige Workflowdemos im Hauptverzeichnis; zehn fokussierte Themen unter Miscellaneous umfassen 29 native Projekte über die drei Plattformen; XfaLab ist für Delphi und Lazarus verfügbar
Öffnen Sie zuerst einen Workflow aus dem Hauptverzeichnis: PDFViewer für eine Feature-Tour, CreatePDF für Live-Autoring, ContentExtractionLab für ein sofortiges Inventar, FormFill für editierbare Formulare, StandardsLab für Validierung und Konvertierung oder IntakeWorkbenchCli für automatisiertes Intake. Diese sechs Einstiegspunkte liefern sofort brauchbare Ergebnisse; die fokussierten Samples weiter unten sind nützlich, wenn Sie eine einzelne API im Detail untersuchen wollen
| Demo | Einsatzzweck | Abgrenzung zu verwandten Demos |
|---|---|---|
PDFViewer | Allgemeines Ansehen und Funktionsauswertung | Der Haupt-Viewer kombiniert Navigation, Suche, Miniaturansichten, Anzeigemodi, Drucken und ein Showcase der Funktionen |
MultiPageViewer | Fortlaufende Seiten, Spreads und Deckblatt-Layouts | Ein fokussiertes Layout-Beispiel mit den integrierten Auswahl-APIs des Viewers; für das breitere Viewer-Erlebnis PDFViewer verwenden |
SplitView | Seiten oder Dokumente in getrennten Panels vergleichen | Zeigt unabhängige Ansichtspositionen und das Weiterleiten von Befehlen an das aktive Panel statt eines Zwei-Seiten-Layouts in einer Ansicht |
SearchAndSelect | Seitenübergreifende Suchergebnisse und Auswahl | Eine Ergebnisliste und Suchoptionen legen den Such-Workflow direkter offen als eine Viewer-Symbolleiste |
PrintPDF | Vorschau und normale Druckaufträge | Seitenbereiche, Skalierung, Fortschritt und Abbruch decken den zentralen Druck-Workflow ab |
ContentExtractionLab | Mehrere Inhaltskategorien gemeinsam betrachten | TXT/JSON-Inventare beschreiben Objekte und zeigen Text als Vorschau; sie ersetzen weder den vollständigen Text-Export noch den Export von Bilddateien |
ExtractImages | Eingebettete Bildobjekte exportieren | Extrahiert Bild-Bitmaps statt Screenshots ganzer Seiten; die eingebaute Testdatei enthält auf zwei Seiten jeweils zwei echte Bildobjekte |
TableExtractionLab | Tabellen erkennen und als CSV/JSON exportieren | Linien- und Whitespace-Tabellen, verbundene Zellen, Fortsetzungsgruppen und Konfidenz-Regelung sind unabhängig von der reinen Textextraktion |
Attachment | Eingebettete Anhänge lesen, hinzufügen und exportieren | Operationen auf Anhang-Payloads gehen über das Auflisten von Namen und Größen in ContentExtractionLab hinaus |
BatchExport | Seitenbilder aus mehreren Dokumenten exportieren | Streaming-Ausgabe, Status pro Datei und isolierte Fehler demonstrieren Batch-Verarbeitung statt einer einzelnen Konvertierung |
CreatePDF | Seiteninhalte mit Live-Vorschau erstellen | Alle drei nativen Varianten erzeugen durchsuchbaren Text, Vektorformen, ein echtes Rasterbild, Tabellen und eine Review-Annotation; Optionen bauen die Vorschau neu auf, bevor explizit gespeichert wird |
ImageToPDF | Seiten aus Eingabebilddateien aufbauen | Bild-Layout, Einpassen, Reihenfolge und JPEG-Behandlung ergeben einen Dokumentkonvertierungs-Workflow |
MergePDF | Seiten aus mehreren PDFs zusammenfügen | Demonstriert Dokument-Import und geordnetes Zusammenfügen; Splitten deckt den umgekehrten Workflow ab |
SplitPDF | Ausgewählte Seitengruppen in separate PDFs schreiben | Ausgabenamen und Validierung der Seitengruppen unterscheiden sich von Merging und Seitenbild-Rendering |
WatermarkStamp | Wiederholte Overlays auf bestehenden Seiten anwenden | Platzierung und Seiten-Stempeln im Batch ergänzen die grundlegenden Autoring-APIs |
FormFill | Interaktive AcroForm-Eingabe und Flattening | Feldeingaben und gespeicherte Appearances unterscheiden sich von XFDF-Interchange und XFA-spezifischem Verhalten |
AccessibilityReader | Interaktives Lesen und Sprachausgabe | Der GUI-Reader demonstriert Navigation und Hervorheben beim Lesen statt eines Compliance-Audits |
AccessibilityWorkbenchCli | Barrierefreiheit im Batch prüfen und Berichte erzeugen | Befunde, Zusammenfassungen und Review-Gates dienen der Automatisierung; Befunde ersetzen keine manuelle Barrierefreiheits-Prüfung |
StandardsLab | Standard-spezifische Validierung und Konvertierung | Das interaktive Lab legt Operationen und Optionen offen, statt nur einen Aggregatbericht zu erzeugen |
PreflightReport | Interaktive Standardberichte | Die GUI unterstützt manuelle Inspektion und Bericht-Export |
PreflightReportCli | Automatisierte Standardberichte | Batch-Eingaben, Berichtsformate und Exitcode-Gates bilden das automatisierte Gegenstück zu PreflightReport |
IntakeWorkbenchCli | Intake, Sichtung und Routing im Batch | Aggregatberichte, Fehler-Gates und Routing-Entscheidungen machen daraus einen Verarbeitungs-Workflow über die SecurityAudit-GUI hinaus |
Öffnen Sie diese Projekte unter Demo\Delphi\Miscellaneous, Demo\CBuilder\Miscellaneous oder Demo\Lazarus\Miscellaneous; sie enthalten nützliche API-Rezepte und schmale Exportaufgaben, während das Hauptverzeichnis vollständige Workflows zeigt; grundlegende Unicode-Prüfungen und der feste Annotationsschreib-/Auflistungsdurchlauf sind durch Erstellung, Extraktion, XFDF-Austausch und API-Tests abgedeckt und haben keine separaten Demoprojekte mehr
| Demo | Einsatzzweck | Abgrenzung zu verwandten Demos |
|---|---|---|
SecurityAudit | Die Sicherheitslage interaktiv untersuchen | Die GUI betont Berechtigungen, Skripte, Signaturen und Sicherheitsdetails statt Datei-Routing |
XfdfLab | XFDF-Daten importieren und exportieren | Interchange und ein explizites Speichern des importierten PDFs demonstrieren Persistenz; der XFDF-Export allein speichert das veränderte PDF nicht |
AccessibilityLab | Die Reader-APIs in einer Konsole durchgehen | Ein kompakter ausführbarer Walkthrough legt Leseeinheiten, Wortboxen, Struktur und Sprach-Skript-Ausgabe offen |
ExtractText | Vollständigen Text ausgewählter Seiten exportieren | Textlayer-Ausgabe, Seitentrennzeichen und Abbruch unterscheiden sich von Inventar-Vorschauen und Tabellenerkennung; OCR wird nicht durchgeführt |
FontProperties | Schrift- und Zeichenmetriken untersuchen | Startet mit echten Schrift- und Zeichenmetriken für das erste Textobjekt; wählen Sie eine andere Zeile aus, um Schriften zu vergleichen, ohne Dokument-Payloads zu exportieren |
NavigationLab | Lesezeichen, Ziele und Navigationsaktionen | Der kompakte API-Walkthrough ergänzt die sichtbaren Navigationssteuerelemente in PDFViewer |
PDFtoJpg | Ein Dokument in Seitenbilder rendern | Ein kompaktes Rendering-Beispiel für ein einzelnes Dokument; Delphi legt diskrete Seitenbereiche offen, während BatchExport auf mehrere Eingabedokumente zielt |
PrintPreferences | N-up-Blätter und Druckvoreinstellungen des PDF-Autors | Kopien, Gerade-/Ungerade-Filterung und vom Autor gesetzte Voreinstellungen stehen im Vordergrund; dies ist eine Ergänzung zu PrintPDF |
ViewerInteractionLab | Diagnose von Maus-, Tastatur- und Koordinaten-Interaktionen | Das Interaktionslog und die Umrechnung zwischen Seiten- und Gerätekoordinaten machen es zu einem API-Diagnosewerkzeug |
XfaLab | Statische XFA-Pakete und Runtime-Fähigkeiten | Öffnet Template- und Datasets-Pakete mit der gewöhnlichen Runtime; interaktives XFA-Rendering braucht eine XFA-fähige Runtime, mit Delphi- und Lazarus-Varianten |
PDFViewer öffnet sein mitgeliefertes PDFiumDemoShowcase.pdf, ein sechsseitiges Dokument mit durchsuchbarem Text, Vektor-Diagrammen, zwei eingebetteten Bildern, einer Tabelle, vier AcroForm-Feldern, Kapitel-Lesezeichen, Links, einer Review-Notiz und zwei Dateianhängen. Die Experience-Leiste bietet Restart showcase, Find live text, 2-page spread, Night reading, Inspect this PDF und Explore forms; jede Aktion wirkt auf das aktuell geöffnete Dokument
CreatePDF öffnet eine zweiseitige Live-Vorschau eines Berichts. Bearbeiten Sie Titel und Autor oder schalten Sie Formen, Tabelle und Wasserzeichen um, um echte Seiteninhalte neu aufzubauen; die Unicode-Option fügt eine dritte Seite hinzu. Vor dem Speichern wird nichts geschrieben, und das gespeicherte PDF bleibt durchsuchbar
MultiPageViewer, SearchAndSelect, ContentExtractionLab und FormFill öffnen dasselbe Dokument für ihre jeweiligen Workflows, mit Suchtreffern, einem Extraktions-Inventar und editierbaren Feldern, die sofort bereitstehen. Miscellaneous\NavigationLab verwendet seine eigene lokale Kopie für einen Lesezeichen- und Ziel-API-Walkthrough
Attachment startet mit zwei echten eingebetteten Dateien. PrintPreferences startet mit vier Seiten und Autoren-Vorgaben für zwei Kopien, gewählte Seitenbereiche, Skalierung und Duplex; der aktuelle Druck-Workflow setzt Kopien und Seitenbereiche um, während Skalierung und Duplex geprüft und berichtet werden. XfaLab startet mit echten Template- und Datasets-Paketen für die statische XFA-Inspektion; interaktives XFA-Rendering braucht eine passende Runtime und ist eine eigenständige Fähigkeit
Jedes Ansichts-, Formular-, Extraktions- und Navigationsprojekt enthält sein eigenes PDFiumDemoShowcase.pdf. Kopieren Sie das Projektverzeichnis und lassen Sie diese mitgelieferte Eingabe neben der weitergegebenen ausführbaren Datei liegen; Lazarus-Builds legen sie automatisch daneben ab. Die Tour funktioniert offline, und allgemeine Viewer- und Extraktions-Samples stellen außerdem generierte Fallback-Inhalte bereit, wenn das lokale PDF nicht verfügbar ist
Jedes native Demo enthält seine eigene Sample-Implementierung. Die Delphi- und Lazarus-Einstiegspunkte für AccessibilityLab, AccessibilityWorkbenchCli, IntakeWorkbenchCli und PreflightReportCli enthalten den vollständigen Workflow im Projektverzeichnis, einschließlich der compilerspezifischen Initialisierung und des JPEG-Writers. Die Beispiele für Anhänge, Druckvoreinstellungen und XFA erzeugen ihre funktionsbezogenen Eingaben ebenfalls lokal. Außerhalb des kopierten Projekts werden nur die Bibliothek, die passende native Runtime und die üblichen Compiler-Framework-Abhängigkeiten benötigt
Wählen Sie die Bildobjekt-Extraktion, wenn Sie eingebettete Bitmaps brauchen, das Seiten-Rendering, wenn Sie die sichtbare Seite brauchen, und die Textextraktion, wenn das PDF bereits eine Textebene hat. Ein Bericht, der diese Kategorien auflistet, exportiert nicht ihre vollständigen Payloads. Diese Grenzen halten fokussierte Beispiele nützlich, ohne jedes Demo zu einem weiteren Allzweck-Viewer oder einer weiteren Workbench aufzublähen
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;
Die Projekte Demo\Delphi\Miscellaneous\FontProperties, Demo\CBuilder\Miscellaneous\FontProperties und Demo\Lazarus\Miscellaneous\FontProperties zeigen dieselben Metadaten sowohl über TPdf als auch über TPdfView
Die Projekte Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab und Demo\Lazarus\ContentExtractionLab machen aus der Inhaltsextraktion einen einzelnen Workflow. Sie öffnen oder erstellen ein PDF, lassen Benutzer Metadaten, Seitentext, Seitenobjekte, Bilder, Anhänge, Links, Annotationen, Lesezeichen sowie Schrift-/Zeichenmetriken auswählen und generieren dann eine Zusammenfassung sowie einen detaillierten TXT- oder JSON-Bericht. Das Lab enthält außerdem Alle-/Keine-Kategorieauswahl, Bericht-in-Zwischenablage-Kopieren und Aktionen zum Speichern der Quell-PDF, sodass Anwender das generierte Sample-PDF neben dem zugehörigen Extraktionsbericht aufbewahren können. ExtractImages und Attachment bieten Payload-Export-Workflows, während Miscellaneous\ExtractText und Miscellaneous\FontProperties fokussierte API-Beispiele bleiben; ExtractText unterstützt Seitenbereich- und Seitentrennzeichen-Optionen zur Erzeugung seitenweise getrennter Textausgabe in Delphi, C++Builder und Lazarus, während ContentExtractionLab das übergreifende Bestandsbeispiel ist
Die Projekte Demo\Delphi\TableExtractionLab, Demo\CBuilder\TableExtractionLab und Demo\Lazarus\TableExtractionLab machen aus der Tabellenerkennung einen einzigen Workflow. Das Labor erstellt ein Beispieldokument mit einer Tabelle mit Linien und verbundenen Kopfzellen, einer Tabelle ohne Linien, die über Zwischenräume ausgerichtet ist, und einer langen Tabelle, die über eine Seitengrenze hinweg umbrochen wird, führt dann ExtractTables für die aktuelle Seite oder ExtractDocumentTables für das gesamte Dokument aus und berichtet Zeilen, Spalten, RowSpan- und ColumnSpan-Werte, Kopfzeilen-Flags, Erkennungsmodus, Konfidenz und Fortsetzungsgruppen. Die Optionen DetectRuledTables, DetectWhitespaceTables, DetectContinuations, DetectFilledRulings und MinConfidence sind in der Benutzeroberfläche bearbeitbar, und die ausgewählte Tabelle wird über TPdfTable.ToCsv und TPdfTable.ToJson exportiert. Das Labor verarbeitet auch einen ganzen Ordner im Batch: Batch folder führt ExtractDocumentTables über jede PDF-Datei aus, berichtet Tabellenanzahlen pro Dokument, markiert Scans ohne Textebene und wiederholt leere Dokumente mit einer Voreinstellung für kompakte Spalten; Export all CSV schreibt jede erkannte Tabelle neben die Quelldateien. Jedes Dokument öffnet in seiner eigenen TPdf-Instanz, damit eine unlesbare Datei den Rest des Batches nicht beeinträchtigen kann
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;
// Ohne Flattening schreibt FormField[i] := value nur den /V-Eintrag —
// das sichtbare Erscheinungsbild stammt aus dem /AP-Stream und wird
// möglicherweise nicht aktualisiert. GenerateFormAppearances regeneriert
// /AP für jedes Widget; anschließend backt FlattenAllPages die Annotationen
// und Widgets jeder Seite dauerhaft als nicht editierbaren Inhalt ein.
Pdf1.FileName := 'C:\Application.pdf';
Pdf1.Active := True;
Pdf1.FormField[0] := 'John Smith';
Pdf1.FormField[1] := '[email protected]';
Pdf1.GenerateFormAppearances;
if Pdf1.FlattenAllPages then
Pdf1.SaveAs('C:\Application.flat.pdf');
Pdf1.Active := False;
var
I: Integer;
Bitmap: TBitmap;
begin
for I := 0 to Pdf1.BitmapCount - 1 do
begin
Bitmap := Pdf1.Bitmap[I];
try
Bitmap.SaveToFile('C:\ExtractedImage' + IntToStr(I) + '.bmp');
finally
Bitmap.Free;
end;
end;
end;
var
JpegStream: TFileStream;
begin
JpegStream := TFileStream.Create('C:\MyImage.jpg', fmOpenRead or fmShareDenyWrite);
try
Pdf1.AddJpegImage(JpegStream, 100, 500, 200, 150);
finally
JpegStream.Free;
end;
Pdf1.AddPicture(Image1.Picture, 100, 300, 150, 100);
Pdf1.UpdatePage;
end;
var
I: Integer;
Annotation: TPdfAnnotation;
begin
for I := 0 to Pdf1.AnnotationCount - 1 do
begin
Annotation := Pdf1.Annotation[I];
ShowMessage('Annotation ' + IntToStr(I) + ': ' + Annotation.ContentsText);
end;
end;
Pdf1.CreatePath(100, 100, 200, 150, fmAlternate, clYellow, $FF, True, clBlack, $FF, 2.0);
Pdf1.AddPath;
Pdf1.CreatePath(100, 100, fmNone, clBlack, $FF, True, clRed, $FF, 3.0);
Pdf1.LineTo(200, 100);
Pdf1.LineTo(150, 200);
Pdf1.ClosePath;
Pdf1.AddPath;
Pdf1.UpdatePage;
var
I: Integer;
Bookmarks: TBookmarks;
begin
Bookmarks := Pdf1.Bookmarks;
for I := 0 to Length(Bookmarks) - 1 do
ShowMessage(Bookmarks[I].Title + ' -> Page ' + IntToStr(Bookmarks[I].PageNumber));
end;
var
I: Integer;
Destination: TDestination;
begin
for I := 0 to Pdf1.DestinationCount - 1 do
begin
Destination := Pdf1.Destination[I];
if Destination.Name = 'Chapter1' then
begin
Pdf1.PageNumber := Destination.PageNumber;
Break;
end;
end;
end;
var
I: Integer;
AttachmentData: TBytes;
AttachmentName: WString;
FileStream: TFileStream;
begin
for I := 0 to Pdf1.AttachmentCount - 1 do
begin
AttachmentName := Pdf1.AttachmentName[I];
AttachmentData := Pdf1.Attachment[I];
FileStream := TFileStream.Create('C:\Extracted_' + string(AttachmentName), fmCreate);
try
if Length(AttachmentData) > 0 then
FileStream.WriteBuffer(AttachmentData[0], Length(AttachmentData));
finally
FileStream.Free;
end;
end;
if Pdf1.CreateAttachment('MyFile.txt') then
Pdf1.Attachment[Pdf1.AttachmentCount - 1] := TFile.ReadAllBytes('C:\MyFile.txt');
end;
Pdf1.AddPage(Pdf1.PageCount + 1, 612, 792);
Pdf1.AddPage(Pdf1.PageCount + 1, 595, 842);
Pdf1.AddPage(Pdf1.PageCount + 1, 400, 600);
var
SourcePdf: TPdf;
begin
SourcePdf := TPdf.Create(nil);
try
SourcePdf.FileName := 'C:\SourceDocument.pdf';
SourcePdf.Active := True;
Pdf1.ImportPages(SourcePdf, '1', Pdf1.PageCount + 1);
finally
SourcePdf.Free;
end;
end;
if Pdf1.PageCount > 1 then
Pdf1.DeletePage(Pdf1.PageCount);
// Komprimiertes PDF-Dokument erstellen
Pdf1.CreateDocument;
Pdf1.Compressed := True; // sicherstellen, dass alle Streams FlateDecode-komprimiert sind (Standard)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // A4-Größe
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;
// Mit Optionen speichern
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // inkrementelles Speichern
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // Sicherheit entfernen
// Auf eine bestimmte PDF-Zielversion speichern. Die PDF-Versionsprüfung lehnt
// jede Funktion ab, die nach dem gewählten Ziel eingeführt wurde (JBIG2Decode,
// MarkInfo, AES-Verschlüsselung, Polygon-/Caret-/Watermark-/Redact-Annotationen, ...).
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
ShowMessage('Save rejected: document contains features newer than PDF 1.5');
// SaveAsPdfA ergänzt den Basis-Speichervorgang durch ein inkrementelles Update,
// das einen XMP-Metadaten-Stream, ein sRGB-ICC-OutputIntent und einen
// aktualisierten Dokument-Catalog einfügt, sodass das Ergebnis PDF/A-1b-konform
// ist. Das mitgelieferte sRGB-IEC61966-2.1-Profil wird automatisch verwendet,
// wenn kein benutzerdefiniertes Profil bereitgestellt wird.
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 mit einem benutzerdefinierten ICC-Profil (z. B. CMYK-Druckpipelines)
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;
// In-Memory-PDF/A-Ausgabe
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;
// Günstige Header-Stil-Prüfung — liest die Konformität, ohne den vollen
// Validator zu durchlaufen. Ergebnis: pacNone, pac1a, pac1b, pac2b, pac3b
// oder 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;
// Vollständiger Validator mit detaillierten Befunden pro Issue
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;
Die Projekte Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab und Demo\Lazarus\StandardsLab laden oder erstellen ein PDF, führen ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR und ValidatePdfVT aus und zeigen dann jede erkannte Konformitätsstufe und die Issue-Anzahl in einer Tabelle an. Ihre Speichern-Schaltflächen rufen SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR und SaveAsPdfVT auf, sodass der Marker-Output-Workflow sichtbar wird, ohne ein eigenes Test-Harness schreiben zu müssen
Die Unit FPdfPreflightReport fasst ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR und ValidatePdfVT zu einem einzigen abfragbaren Berichtsobjekt mit Info- / Warning- / Error-Schweregraden, Status- und Issue-Aggregation, Lookup, Suche, Filterung und stabilen SHA-256-Fingerprints zusammen. Berichte können als Text, HTML, Markdown, JSON, CSV, SARIF 2.1.0, JUnit XML, NDJSON oder natives XML ausgegeben werden. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport und Demo\CBuilder\PreflightReport bieten interaktive Auswahl- und Preview-Workflows, während Demo\Delphi\PreflightReportCli skriptfähige Input-/Output-Argumente, Standardfilterung, optionale Berichtsanhang-Einbettung, CI-Gating über Exitcodes, Batch-Listen- und Verzeichnisverarbeitung, kollisionssicheres Routing, Aggregat-Zusammenfassungen und CSV-Manifeste ergänzt. Der erzeugte Bericht weist darauf hin, dass die integrierten Validatoren Marker-Prüfungen und ausgewählte Prüfungen auf Dateiebene abdecken, sodass vollständige Preflight-Prüfungen auf Inhaltsebene weiterhin an eine dedizierte Validierungs-Engine delegiert werden können
// LoadCustomDocument lässt PDFium Blöcke von einem beliebigen seekbaren
// TStream bedarfsweise abrufen. Der Stream wird NICHT in den Speicher
// kopiert, sodass mehrere Gigabyte große PDFs, Remote-HTTP-Body-Streams
// und datenbankgestützte Quellen alle ohne vorab erstellten Puffer
// nutzbar sind.
var
FileStream: TFileStream;
begin
// Streaming aus Datei lesen (keine Speicherkopie)
FileStream := TFileStream.Create('C:\Huge.pdf',
fmOpenRead or fmShareDenyWrite);
Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
// ^^ Eigentum übertragen — Pdf1 gibt FileStream bei UnloadDocument frei.
Pdf1.Active := True;
ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;
// Extern besitzter Stream (Aufrufer gibt ihn nach UnloadDocument frei)
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 stellt IPdfCancellationToken /
// IPdfCancellationTokenSource für Cancel-from-UI-Semantik bereit, die
// über die langlaufenden Export- und Render-Pfade hinweg funktioniert.
uses FPdfAsync;
var
CancelSource: IPdfCancellationTokenSource;
CancelToken: IPdfCancellationToken;
begin
CancelSource := PdfCancellationTokenSource;
CancelToken := CancelSource.Token;
// ... Benutzer klickt auf Abbrechen ...
CancelSource.Cancel;
// Der Worker prüft regelmäßig CancelToken.IsCancelled, oder der Render-
// Pfad fragt es über PDFiums IFSDK_PAUSE-Callback ab.
if CancelToken.IsCancelled then
Memo1.Lines.Add('Cancelled by user');
end;
// RenderPageProgressive ermöglicht es einem langen High-DPI-Rendering,
// mitten in der Seite abzubrechen, wenn der Benutzer Abbrechen klickt,
// anstatt den Aufrufer bis zum Abschluss zu blockieren.
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> startet einen Worker auf einem Hintergrund-Thread und
// sendet einen TPdfFutureResult<T>-Envelope zurück an den Haupt-Thread.
//
// Wichtig: TPdf serialisiert Render-Aufrufe pro Instanz, doch der Worker
// besitzt weiterhin den Dispatch und die Dokumentlebensdauer. Mutieren Sie
// keinen TPdf während eines laufenden Renderings; Batch-Exporte halten
// üblicherweise eine TPdf pro Datei.
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 umschließt PDFiums FS_MATRIX, sodass Aufrufer eine
// Transformation deklarativ aufbauen und an FPDFPageObj_SetMatrix
// übergeben können.
uses FPdfMatrix, FPdfView;
var
M: TPdfMatrix;
RawMatrix: FS_MATRIX;
PageObj: FPDF_PAGEOBJECT;
begin
M := TPdfMatrix.Create;
try
M.Translate(72, 200); // 1 Zoll nach rechts, ~2,8 Zoll nach oben verschieben
M.Scale(0.5, 0.5); // halb großer Stempel
M.Rotate(15); // 15-Grad-Drehung gegen Uhrzeigersinn
// M.HorizontalFlip;
// M.VerticalFlip;
// M.CentralFlip;
// M.Skew(10, 5);
// M.Multiply(Other); // mit einer anderen Matrix post-multiplizieren
// Auf ein beliebiges PDFium-Seitenobjekt anwenden (Text / Pfad / Bild / Form XObject)
RawMatrix := M.Handle;
FPDFPageObj_SetMatrix(PageObj, RawMatrix);
finally
M.Free;
end;
end;
// ImportPagesByIndex kopiert ein explizites null-basiertes Index-Array
// aus einem Quell-PDF in dieses Dokument. InsertAt = 0 fügt vor Seite 1
// ein; PageCount hängt an.
var
Source: TPdf;
begin
Source := TPdf.Create(nil);
try
Source.FileName := 'C:\Report.pdf';
Source.Active := True;
// Seiten 0, 2, 4 (eins-basiert: Seiten 1, 3, 5) aus Source importieren
// und am Ende von Pdf1 anhängen.
Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);
// Leeres Array importiert jede Quellseite
// Pdf1.ImportPagesByIndex(Source, [], 0);
finally
Source.Free;
end;
end;
// ImportNPagesToOne liefert ein brandneues TPdf, dessen Seiten NumX*NumY-
// Kompositionen DIESES Dokuments sind. OutputWidth/Height sind PDF-
// Benutzereinheiten (1 Einheit = 1/72 Zoll).
var
Composite: TPdf;
begin
Pdf1.FileName := 'C:\Slides.pdf';
Pdf1.Active := True;
// 4-up A4 Querformat: 842 x 595 pt, 2 Spalten x 2 Zeilen
Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
try
if Composite <> nil then
Composite.SaveAs('C:\Slides_4up.pdf');
finally
Composite.Free;
end;
end;
// MovePages nimmt die null-basierten Indizes der zu verschiebenden Seiten
// sowie den Zielindex für die erste verschobene Seite nach Abschluss der
// Verschiebung entgegen. Andere Seiten verschieben sich um den
// verschobenen Block herum, sodass die Dokumentlänge gleich bleibt.
//
// Beispiel: In einem 5-Seiten-Dokument mit Seiten [0,1,2,3,4]
// ordnet MovePages([2, 3], 0) zu [2, 3, 0, 1, 4] um.
if not Pdf1.MovePages([2, 3], 0) then
ShowMessage('Invalid indices passed to MovePages');
// CreateXObjectFromPage erzeugt ein wiederverwendbares Form-XObject-Handle
// aus einer beliebigen Seite eines beliebigen anderen TPdf. Stempeln Sie
// dieses Handle auf so viele Seiten DIESES Dokuments wie Sie möchten —
// jeder Stempel ist ein einzelnes PDFium-Seitenobjekt, das über die
// Matrix-API positioniert, skaliert und gedreht werden kann.
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;
// Seite 0 von WatermarkSource als wiederverwendbares XObject einhüllen
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;
// Wasserzeichen auf der Seite zentrieren
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; // schließt das FPDF_XOBJECT-Handle
end;
Pdf1.SaveAs('C:\Watermarked.pdf');
finally
WatermarkSource.Free;
end;
end;
Die Projekte Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp und Demo\CBuilder\WatermarkStamp verwandeln dieses Muster in einen lauffähigen Workflow: Sie erstellen ein Ziel-PDF und ein wiederverwendbares Stempel-PDF, hüllen die Stempel-Seite mit CreateXObjectFromPage ein, fügen dasselbe Form XObject auf jeder Zielseite ein, wenden TPdfMatrix-Transformationen für zentrierte Wasserzeichen und Stempel oben rechts an und fügen vor dem Speichern des Ergebnisses optionale Seitenzahl-Beschriftungen hinzu
// AddImage(FileName) akzeptiert jedes Format, das mit den VCL- oder LCL-
// Grafikunits registriert ist (BMP, PNG über PngImage, JPG, ...).
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);
// AddImage(TBitmap) überspringt den TPicture-Zwischenschritt, ideal wenn
// das Bitmap bereits aus Rendering- oder Batch-Generierungsarbeiten stammt.
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;
Die Projekte Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF und Demo\Lazarus\ImageToPDF machen daraus einen vollständigen Batch-Workflow: mehrere Bilddateien auswählen, das ausgewählte Element vorschauen, eine PDF-Seite pro Bild erstellen, jedes Bild in eine A4-Seite im Hoch- oder Querformat skalieren, das Ergebnis speichern und das erzeugte PDF öffnen
var
MatchCount: Integer;
begin
// Eine clYellow-Maske über jeden Treffer auf der aktuellen Ansichtsseite zeichnen.
// Beim Wechsel zu einer anderen Seite wird die Hervorhebung automatisch gelöscht.
PdfView1.HighlightColor := clYellow;
MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
Status.Caption := Format('%d match(es) on this page', [MatchCount]);
// Das Overlay programmgesteuert vor einem Seitenwechsel entfernen
PdfView1.ClearHighlight;
end;
// FitMode hält lange Dokumente bei Resize oder Seitenwechseln gerahmt.
// Das direkte Setzen von Zoom hebt FitMode auf (er wechselt zurück zu pfmNone).
PdfView1.FitMode := pfmFitPage; // gesamte Seite im Ansichtsbereich
// PdfView1.FitMode := pfmFitWidth; // Seitenbreite entspricht Ansichtsbereichsbreite
// PdfView1.FitMode := pfmActualSize; // 100 %
// PageColor trennt die gerenderte PDF-Seite von der Color des Host-Steuerelements
// — typische Dark-Mode-Viewer-Einrichtung hält den Bildlaufbereich dunkel,
// während die PDF-Seite papierweiß bleibt.
PdfView1.Color := clBlack; // Bildlaufbereich
PdfView1.PageColor := clWhite; // PDF-Seitenhintergrund
// Optionaler Schlagschatten und Seitenrahmen, um die Seite von einem dunklen
// Hintergrund abzuheben
PdfView1.PageShadowSize := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;
Die Projekte Demo\Delphi\Miscellaneous\ViewerInteractionLab, Demo\CBuilder\Miscellaneous\ViewerInteractionLab und Demo\Lazarus\Miscellaneous\ViewerInteractionLab halten die moderne Interaktionsoberfläche von TPdfView in einem kompakten Formular zusammen: ein Sample-PDF öffnen oder erstellen, DisplayMode umschalten, FitMode anwenden, PageColor ändern, Seiten drehen, Suchtreffer der aktuellen Seite mit HighlightSearchText hervorheben, Benutzer-Textauswahl aktivieren oder deaktivieren, SelectedText untersuchen, SelectAll, CopySelectionToClipboard und ClearSelection aufrufen und beobachten, wie sich die DeviceToPage-Koordinaten in der Statusleiste aktualisieren, während sich die Maus über die Seite bewegt
Die Projekte Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect und Demo\Lazarus\SearchAndSelect machen aus dem Such- und Auswahl-Viewer-Workflow eigenständige Beispiele. Sie öffnen oder erstellen ein PDF, listen seitenübergreifende Suchergebnisse mit Seitennummer, Zeichenindex und Vorschautext auf, unterstützen Vorheriger-/Nächster-Navigation und Doppelklick-Sprung zum Treffer, heben Treffer der aktuellen Seite mit HighlightSearchText hervor, stellen Match-Case- und Whole-Word-Optionen bereit, schalten AllowUserTextSelection um, zeigen eine Vorschau von SelectedText, steuern SelectAll, CopySelectionToClipboard und ClearSelection über Schaltflächen und ermöglichen Benutzern den Wechsel zwischen Einzel- und Spread-Anzeigemodi
Die Projekte Demo\Delphi\Miscellaneous\PrintPreferences, Demo\CBuilder\Miscellaneous\PrintPreferences und Demo\Lazarus\Miscellaneous\PrintPreferences lesen die Druckvoreinstellungen des Autors vor dem Drucken. Sie zeigen PrintCopies, PrintPageRanges, PrintScaling und PrintPaperHandling an, können die vom Autor bereitgestellten Kopien und Bereiche in die Druckeingaben übernehmen und veranschaulichen, wie SetPdfPrintPaperHandlingDevMode die PDF-Duplex-Intent auf ein Windows-DEVMODE abbildet
Die Projekte Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF und Demo\Lazarus\PrintPDF zeigen den Standard-Druck-Workflow: ein PDF öffnen, Seiten vorschauen, Seitenbereiche und Kopien im plattform-eigenen Druckdialog wählen, Collate-Modus respektieren, jede Seite über TPdf.RenderPage rendern und Druckfortschritt mit Abbruch bereitstellen. Das Delphi-Beispiel akzeptiert außerdem per Drag-and-Drop fallengelassene PDF-Dateien zum schnellen Laden
Die Projekte Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer und Demo\Lazarus\MultiPageViewer üben fortlaufendes, Spread- und deckblattbewusstes Betrachten mit regulären Seitennavigations- und Zoom-Workflows. Sie eignen sich auch zum Testen der Open-to-First-Page-Geschwindigkeit und der Rendering-Reaktionsfähigkeit bei sehr großen PDFs: In Tests mit großen Dateien können 2.x GB große Dokumente, bei denen Acrobat oder Foxit 20-30 Sekunden bis zur ersten Ansicht brauchen, über den PDFium Delphi Component-Viewer-Pfad nahezu sofort nutzbar werden
Die Projekte Demo\Delphi\SplitView, Demo\CBuilder\SplitView und Demo\Lazarus\SplitView zeigen Nebeneinander-PDF-Prüfung. Die C++Builder- und Lazarus-Beispiele konzentrieren sich auf Zwei- oder Drei-Panel-Vergleich, Auswahl der aktiven Ansicht, optionale synchronisierte Navigationsbefehle, gemeinsame Zoom-Voreinstellungen und Drehung für die ausgewählte Ansicht oder alle geladenen Ansichten. Verwenden Sie diese Beispiele, um große Dokumente zu vergleichen und dabei die erste nutzbare Ansicht sowie das nachfolgende Seitenrendering reaktionsfähig zu halten; dieselben 2.x GB großen PDF-Dateien, für die Acrobat oder Foxit 20-30 Sekunden zum Öffnen brauchen, können im PDFium Delphi Component-Viewer-Pfad nahezu sofort nutzbar werden
Die Projekte Demo\Delphi\Miscellaneous\SecurityAudit, Demo\CBuilder\Miscellaneous\SecurityAudit und Demo\Lazarus\Miscellaneous\SecurityAudit öffnen PDF schreibgeschützt und listen Berechtigungen, eingebettete Anhänge, Dokument-JavaScript, URI-/Launch-Linkannotationen, Weblinks, Signaturübersichten, XFA-Status, V8-Helperverfügbarkeit und Unsupported-Feature-Callbacks; dieselben Übersichts-/Fundzeilen lassen sich als TXT-Bericht speichern oder kopieren oder als strukturiertes JSON speichern; dies sind Risikopanels für Hostanwendungen, keine Malwareerkennung
// LinkOptions steuert die automatische Behandlung der vier PDF-Link-Aktionstypen.
// Standardmäßig sind nur goto und URI aktiviert; launch (ein Programm starten) und
// embedded GotoR (zu einer anderen Datei springen) bleiben AUS, sodass ein
// versehentlicher Klick keinen beliebigen Code ausführen kann.
PdfView1.LinkOptions :=
[loAutoGoto, loAutoOpenURI]; // sichere Standardwerte
// OnAnnotationLinkClick wird für In-Dokument-Ziele ausgelöst (Seitensprünge,
// benannte Zielorte, Aktionsketten). Setzen Sie Handled = True, um zu verhindern,
// dass die Ansicht den Link automatisch verfolgt.
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; // automatische Behandlung unterdrücken
end;
end;
// OnWebLinkClick wird für URI-Strings ausgelöst, die vom Web-Link-Scanner von
// PDFium entdeckt wurden (automatisch erkannte URLs im Textstream, auch ohne
// /A <</S/URI...>>-Annotation).
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
WebLinkIndex: Integer; const Url: WString;
var Handled: Boolean);
begin
Log.Add('Web link clicked: ' + string(Url));
end;
// Auf dem fokussierten AcroForm-Widget steuern sechs neue Methoden die
// Formular-Bearbeitungshistorie von PDFium. Alle sechs verhalten sich sicher
// als No-op, wenn kein Widget fokussiert ist oder das Dokument kein AcroForm
// enthält.
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;
// Alle drei stehen standardmäßig auf True, sodass bestehende Apps ohne
// Verhaltensänderung aktualisieren. Setzen Sie sie auf False für Kioske,
// Vorschaupanels oder schreibgeschützte Einbettungen ohne Subclassing von
// TPdfView.
PdfView1.AllowUserPageChange := False; // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // Mausrad hoch/runter springt Seiten
PdfView1.AllowUserTextSelection := False; // Maus/Tastatur-Textauswahl
// Ctrl+Mausrad ist für Zoom reserviert und wird vom Navigations-Handler
// unabhängig von ChangePageOnMouseScrolling übersprungen.
// TPdfThumbnailView (Unit FPdfThumbnail) rendert eine Seiten-Miniaturansicht
// pro Zeile in einem scrollbaren Seitenpanel.
uses FPdfThumbnail;
procedure TForm1.FormCreate(Sender: TObject);
begin
ThumbView.Pdf := Pdf1;
ThumbView.ThumbnailWidth := 120;
ThumbView.ThumbnailHeight := 160;
ThumbView.SelectionColor := $00CFA85F; // warme Hervorhebung
ThumbView.OnPageClick := ThumbViewPageClick;
end;
procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
PdfView1.PageNumber := PageIndex + 1; // PageIndex ist null-basiert
end;
procedure TForm1.PdfView1PageNumberChanged(Sender: TObject);
begin
ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;
// Muster, verwendet von Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport
// und Demo\Lazarus\BatchExport. Eine TPdf pro Datei, IPdfCancellationToken
// für Cancel-from-UI, TPdfStreamAdapter für gepuffertes Dateiladen.
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 ist eine günstige Catalog-Prüfung — True, wenn /StructTreeRoot
// existiert. StructureElements materialisiert den Baum in ein flaches Array
// von TPdfStructureElement-Records mit Typ, Titel, Alternativtext, Actual
// Text, Expansionstext, Sprache, Ebene, Eltern-Index und Zählungen für
// Children / Marked Content / Attribute.
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 ist eine globale Funktion, die auf PDFiums
// FPDF_SetPrintMode abbildet. Verwenden Sie den passenden TPdfPrintMode-Wert
// für den eingesetzten Druckertreiber.
SetPdfPrintMode(pmEmf); // GDI EMF (Standard)
// SetPdfPrintMode(pmTextOnly); // nur-Text-Rendering
// SetPdfPrintMode(pmPostScript2); // PostScript Level 2
// SetPdfPrintMode(pmPostScript3); // PostScript Level 3
// SetPdfPrintMode(pmPostScript2PassThrough);
// SetPdfPrintMode(pmPostScript3PassThrough);
// SetPdfPrintMode(pmEmfImageMasks); // EMF + Image Masks
// SetPdfPrintMode(pmPostScript3Type42); // PostScript 3 + Type-42-Schriftarten
// PrintCopies, PrintPageRanges, PrintScaling und PrintPaperHandling sind
// die Einträge des Viewer-Preference-Dictionary — sie kodieren, was der
// Dokumentautor als Standard für PDF-Reader gewünscht hat.
var
Ranges: TPrintPageRanges;
Range: TPrintPageRange;
begin
ShowMessage('Suggested copies: ' + IntToStr(Pdf1.PrintCopies));
ShowMessage('Print scaling: ' + GetEnumName(TypeInfo(TPrintScaling),
Ord(Pdf1.PrintScaling)));
Ranges := Pdf1.PrintPageRanges; // eins-basierte Bereiche
for Range in Ranges do
Log.Add(Format('Pages %d..%d', [Range.First, Range.Last]));
if SetPdfPrintPaperHandlingDevMode(PrinterDevMode, Pdf1.PrintPaperHandling) then
ApplyPrinterDevMode(PrinterDevMode);
end;
// JavaScript auf Dokumentebene wird im /Names /JavaScript-Namensbaum
// gespeichert. JavaScriptAction[i] liefert einen Eintrag;
// JavaScriptActions liefert das vollständige Array.
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;
// Zero-Copy-Render-Pfad (Standard seit v1.21.0): RenderPage schreibt direkt
// in den DIB-Puffer des Ziel-TBitmap, wenn Aufrufer ein vorab allokiertes
// Bitmap übergeben, und vermeidet so eine Kopie von Breite * Höhe * 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;