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;
De projecten Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties en Demo\Lazarus\FontProperties tonen dezelfde metadata via zowel TPdf als TPdfView.
De projecten Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab en Demo\Lazarus\ContentExtractionLab maken van inhoudsextractie één workflow. Ze openen of maken een PDF, laten gebruikers metadata, paginatekst, pagina-objecten, afbeeldingen, bijlagen, links, annotaties, bladwijzers en font-/karaktermetrics kiezen, en genereren vervolgens een samenvatting plus een gedetailleerd TXT- of JSON-rapport. De lab bevat ook alle / geen categorie-selectie, rapport-kopiëren-naar-klembord en bron-PDF-opslag-acties, zodat gebruikers de gegenereerde voorbeeld-PDF naast zijn extractierapport kunnen bewaren. De oudere demo's ExtractText, ExtractImages, Attachment en FontProperties blijven gerichte voorbeelden; ExtractText ondersteunt opties voor paginabereik en paginascheidingstekens om tekstuitvoer per pagina te produceren in Delphi, C++Builder en Lazarus, terwijl ContentExtractionLab het overkoepelende inventarisvoorbeeld is.
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;
// Zonder afvlakken schrijft FormField[i] := value alleen de /V-entry —
// de zichtbare appearance komt uit de /AP-stream en kan mogelijk niet
// vernieuwen. GenerateFormAppearances regenereert /AP voor elk widget,
// daarna bakt FlattenAllPages alle annotaties en widgets van elke pagina
// in als permanente, niet-bewerkbare inhoud.
Pdf1.FileName := 'C:\Application.pdf';
Pdf1.Active := True;
Pdf1.FormField[0] := 'John Smith';
Pdf1.FormField[1] := 'john@example.com';
Pdf1.GenerateFormAppearances;
if Pdf1.FlattenAllPages then
Pdf1.SaveAs('C:\Application.flat.pdf');
Pdf1.Active := False;
var
I: Integer;
Bitmap: TBitmap;
begin
for I := 0 to Pdf1.BitmapCount - 1 do
begin
Bitmap := Pdf1.Bitmap[I];
try
Bitmap.SaveToFile('C:\ExtractedImage' + IntToStr(I) + '.bmp');
finally
Bitmap.Free;
end;
end;
end;
var
JpegStream: TFileStream;
begin
JpegStream := TFileStream.Create('C:\MyImage.jpg', fmOpenRead or fmShareDenyWrite);
try
Pdf1.AddJpegImage(JpegStream, 100, 500, 200, 150);
finally
JpegStream.Free;
end;
Pdf1.AddPicture(Image1.Picture, 100, 300, 150, 100);
Pdf1.UpdatePage;
end;
var
I: Integer;
Annotation: TPdfAnnotation;
begin
for I := 0 to Pdf1.AnnotationCount - 1 do
begin
Annotation := Pdf1.Annotation[I];
ShowMessage('Annotation ' + IntToStr(I) + ': ' + Annotation.ContentsText);
end;
end;
Pdf1.CreatePath(100, 100, 200, 150, fmAlternate, clYellow, $FF, True, clBlack, $FF, 2.0);
Pdf1.AddPath;
Pdf1.CreatePath(100, 100, fmNone, clBlack, $FF, True, clRed, $FF, 3.0);
Pdf1.LineTo(200, 100);
Pdf1.LineTo(150, 200);
Pdf1.ClosePath;
Pdf1.AddPath;
Pdf1.UpdatePage;
var
I: Integer;
Bookmarks: TBookmarks;
begin
Bookmarks := Pdf1.Bookmarks;
for I := 0 to Length(Bookmarks) - 1 do
ShowMessage(Bookmarks[I].Title + ' -> Page ' + IntToStr(Bookmarks[I].PageNumber));
end;
var
I: Integer;
Destination: TDestination;
begin
for I := 0 to Pdf1.DestinationCount - 1 do
begin
Destination := Pdf1.Destination[I];
if Destination.Name = 'Chapter1' then
begin
Pdf1.PageNumber := Destination.PageNumber;
Break;
end;
end;
end;
var
I: Integer;
AttachmentData: TBytes;
AttachmentName: WString;
FileStream: TFileStream;
begin
for I := 0 to Pdf1.AttachmentCount - 1 do
begin
AttachmentName := Pdf1.AttachmentName[I];
AttachmentData := Pdf1.Attachment[I];
FileStream := TFileStream.Create('C:\Extracted_' + string(AttachmentName), fmCreate);
try
if Length(AttachmentData) > 0 then
FileStream.WriteBuffer(AttachmentData[0], Length(AttachmentData));
finally
FileStream.Free;
end;
end;
if Pdf1.CreateAttachment('MyFile.txt') then
Pdf1.Attachment[Pdf1.AttachmentCount - 1] := TFile.ReadAllBytes('C:\MyFile.txt');
end;
Pdf1.AddPage(Pdf1.PageCount + 1, 612, 792);
Pdf1.AddPage(Pdf1.PageCount + 1, 595, 842);
Pdf1.AddPage(Pdf1.PageCount + 1, 400, 600);
var
SourcePdf: TPdf;
begin
SourcePdf := TPdf.Create(nil);
try
SourcePdf.FileName := 'C:\SourceDocument.pdf';
SourcePdf.Active := True;
Pdf1.ImportPages(SourcePdf, '1', Pdf1.PageCount + 1);
finally
SourcePdf.Free;
end;
end;
if Pdf1.PageCount > 1 then
Pdf1.DeletePage(Pdf1.PageCount);
// Maak een gecomprimeerd PDF-document
Pdf1.CreateDocument;
Pdf1.Compressed := True; // zorg dat alle streams FlateDecode-gecomprimeerd zijn (standaard)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // A4-formaat
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;
// Opslaan met opties
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // incrementeel opslaan
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // beveiliging verwijderen
// Opslaan met specifieke PDF-versie als doel. De PDF-versie-validator
// weigert elke functie die na de gekozen doelversie is geïntroduceerd
// (JBIG2Decode, MarkInfo, AES-versleuteling, Polygon / Caret / Watermark /
// Redact-annotaties, ...).
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
ShowMessage('Save rejected: document contains features newer than PDF 1.5');
// SaveAsPdfA nabewerkt de basisopslag met een incrementele update die een
// XMP-metadatastream, een sRGB ICC OutputIntent en een bijgewerkte
// documentcatalogus injecteert zodat het resultaat PDF/A-1b-conform is.
// Het meegeleverde sRGB IEC61966-2.1-profiel wordt automatisch gebruikt
// wanneer geen aangepast profiel wordt geleverd.
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 met een aangepast ICC-profiel (bijvoorbeeld voor CMYK-print-pijplijnen)
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;
// PDF/A-uitvoer in het geheugen
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;
// Goedkope header-stijl probe — leest de conformiteit zonder de
// volledige validator te itereren. Resultaat: pacNone, pac1a, pac1b,
// pac2b, pac3b of 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;
// Volledige validator met diagnostiek per probleem
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;
De projecten Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab en Demo\Lazarus\StandardsLab laden of maken een PDF, voeren ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR en ValidatePdfVT uit en tonen vervolgens elk gedetecteerd conformiteitsniveau en aantal problemen in één raster. Hun save-knoppen roepen SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR en SaveAsPdfVT aan, zodat de marker-output-workflow zichtbaar is zonder een aangepast harnas te schrijven.
De unit FPdfPreflightReport wikkelt ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR en ValidatePdfVT in één rapportobject. Met Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport en Demo\CBuilder\PreflightReport kunnen gebruikers standaarden kiezen, een samenvatting genereren, status, prioriteit, probleem-categorieën, categorie-tellingen, volgende acties, probleem-codes en aanbevolen acties inspecteren, de voorvertoning wisselen tussen tekst, Markdown, JSON en CSV en TXT-, HTML-, Markdown-, JSON- of CSV-rapporten exporteren. De Delphi-GUI-voorbeeldapplicatie bevat ook een Action Plan-voorbeeldweergave die niet-voltooide standaarden, prioriteiten, probleemcodes en aanbevelingen samenvoegt tot een reparatielijst. Demo\Delphi\PreflightReportCli stelt hetzelfde rapport-pad beschikbaar als console-workflow met argumenten voor invoer/uitvoer, optioneel wachtwoord, selectie van uitvoerformaat met plan / actionplan, filtering op standaard, optionele rapport-inbedding van één bestand via attach=output.pdf, CI-gating-exitcodes via failon=, verwerking via batch=list.txt, mapscanning via batchdir=folder met optionele recursie en stabiele verwerking op pad-volgorde, rapportroutering via outdir=reports, collision-veilige rapportnamen voor dubbele invoerbestandsnamen, TXT-/JSON-/HTML-samenvattingen met aggregaat- en standaard-status-totalen, CSV-rij-manifesten met run-instellingen die per item worden herhaald, en een sample-modus zonder argumenten voor scripts en CI-jobs. Het gegenereerde rapport vermeldt dat de ingebouwde validators marker-level- en geselecteerde file-level-checks dekken, zodat full content-level preflight nog steeds kan worden gedelegeerd aan een gespecialiseerde validatie-engine.
// LoadCustomDocument laat PDFium blokken ophalen uit elke seekable TStream
// op aanvraag. De stream wordt NIET naar het geheugen gekopieerd, zodat
// PDF's van meerdere GB, remote HTTP body streams en databasegebaseerde
// bronnen allemaal haalbaar zijn zonder een vooraf gemaakte buffer.
var
FileStream: TFileStream;
begin
// Streamen vanuit bestand (geen kopie in geheugen)
FileStream := TFileStream.Create('C:\Huge.pdf',
fmOpenRead or fmShareDenyWrite);
Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
// ^^ eigendom overgedragen — Pdf1 vrijgeeft FileStream bij UnloadDocument.
Pdf1.Active := True;
ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;
// Stream in extern beheer (aanroeper geeft hem vrij na UnloadDocument)
var
Stream: TFileStream;
begin
Stream := TFileStream.Create('C:\Live.pdf',
fmOpenRead or fmShareDenyNone);
try
Pdf1.LoadCustomDocument(Stream, False);
Pdf1.Active := True;
DoWork;
Pdf1.Active := False;
finally
Stream.Free;
end;
end;
// FPdfAsync biedt IPdfCancellationToken / IPdfCancellationTokenSource voor
// cancel-from-UI-semantiek die werkt over de langlopende export- en
// renderpaden.
uses FPdfAsync;
var
CancelSource: IPdfCancellationTokenSource;
CancelToken: IPdfCancellationToken;
begin
CancelSource := PdfCancellationTokenSource;
CancelToken := CancelSource.Token;
// ... gebruiker klikt op Annuleren ...
CancelSource.Cancel;
// De worker controleert periodiek CancelToken.IsCancelled, of het
// renderpad peilt het via PDFium's IFSDK_PAUSE-callback.
if CancelToken.IsCancelled then
Memo1.Lines.Add('Cancelled by user');
end;
// RenderPageProgressive laat een lange high-DPI-render halverwege de
// pagina afbreken wanneer de gebruiker op Annuleren klikt, in plaats van
// de aanroeper te blokkeren tot het klaar is.
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> start een worker op een achtergrondthread en stuurt een
// TPdfFutureResult<T>-envelop terug naar de hoofdthread.
//
// Belangrijk: TPdf serialiseert render-aanroepen per instance, maar de
// worker beheert nog steeds dispatch en levensduur van het document. Muteer
// geen TPdf terwijl een render onderweg is; batch-export houdt meestal één
// TPdf per bestand aan.
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 wikkelt PDFium's FS_MATRIX zodat aanroepers een transform
// declaratief kunnen bouwen en aan FPDFPageObj_SetMatrix kunnen overdragen.
uses FPdfMatrix, FPdfView;
var
M: TPdfMatrix;
RawMatrix: FS_MATRIX;
PageObj: FPDF_PAGEOBJECT;
begin
M := TPdfMatrix.Create;
try
M.Translate(72, 200); // verplaats 1in naar rechts, ~2.8in omhoog
M.Scale(0.5, 0.5); // half-formaat stempel
M.Rotate(15); // 15 graden CCW-rotatie
// M.HorizontalFlip;
// M.VerticalFlip;
// M.CentralFlip;
// M.Skew(10, 5);
// M.Multiply(Other); // post-multipliceer met een andere matrix
// Toepassen op elk PDFium-pagina-object (text / path / image / form XObject)
RawMatrix := M.Handle;
FPDFPageObj_SetMatrix(PageObj, RawMatrix);
finally
M.Free;
end;
end;
// ImportPagesByIndex kopieert een expliciete zero-based index-array van
// een bron-PDF naar dit document. InsertAt = 0 voegt in vóór pagina 1;
// PageCount voegt toe aan het einde.
var
Source: TPdf;
begin
Source := TPdf.Create(nil);
try
Source.FileName := 'C:\Report.pdf';
Source.Active := True;
// Importeer pagina's 0, 2, 4 (one-based: pagina's 1, 3, 5) uit Source
// en voeg ze toe aan het einde van Pdf1.
Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);
// Een lege array importeert elke bronpagina
// Pdf1.ImportPagesByIndex(Source, [], 0);
finally
Source.Free;
end;
end;
// ImportNPagesToOne retourneert een gloednieuwe TPdf waarvan de pagina's
// NumX*NumY composieten zijn van DIT document. OutputWidth/Height zijn
// PDF-user-units (1 unit = 1/72 in).
var
Composite: TPdf;
begin
Pdf1.FileName := 'C:\Slides.pdf';
Pdf1.Active := True;
// 4-up A4 landschap: 842 x 595 pt, 2 kolommen x 2 rijen
Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
try
if Composite <> nil then
Composite.SaveAs('C:\Slides_4up.pdf');
finally
Composite.Free;
end;
end;
// MovePages neemt de zero-based indices van de te verplaatsen pagina's
// plus de doelindex voor de eerste verplaatste pagina nadat de verplaatsing
// is voltooid. Andere pagina's schuiven rond het verplaatste blok zodat de
// documentlengte hetzelfde blijft.
//
// Voorbeeld: in een document van 5 pagina's met pagina's [0,1,2,3,4],
// herordent MovePages([2, 3], 0) naar [2, 3, 0, 1, 4].
if not Pdf1.MovePages([2, 3], 0) then
ShowMessage('Invalid indices passed to MovePages');
// CreateXObjectFromPage maakt een herbruikbare Form XObject-handle van
// elke pagina van elke andere TPdf. Stempel die handle op zoveel pagina's
// van DIT document als u wilt — elke stempel is één PDFium-pagina-object
// dat kan worden gepositioneerd, geschaald en geroteerd via de matrix-API.
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;
// Wikkel pagina 0 van WatermarkSource in als herbruikbaar XObject
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;
// Centreer het watermerk op de pagina
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; // sluit de FPDF_XOBJECT-handle
end;
Pdf1.SaveAs('C:\Watermarked.pdf');
finally
WatermarkSource.Free;
end;
end;
De projecten Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp en Demo\CBuilder\WatermarkStamp maken van dit patroon een uitvoerbare workflow: ze maken een doel-PDF en een herbruikbare stempel-PDF, wikkelen de stempelpagina in met CreateXObjectFromPage, voegen hetzelfde Form XObject in op elke doelpagina, passen TPdfMatrix-transformaties toe voor gecentreerde watermerken en stempels rechtsboven en voegen optionele paginanummerlabels toe vóór het opslaan van het resultaat.
// AddImage(FileName) accepteert elk formaat dat is geregistreerd bij de
// grafische units van VCL of LCL (BMP, PNG via PngImage, JPG, ...).
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);
// AddImage(TBitmap) slaat de tussenstap via TPicture over, ideaal wanneer
// de bitmap al uit rendering- of batch-generatiewerk komt.
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;
De projecten Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF en Demo\Lazarus\ImageToPDF maken hiervan een volledige batchworkflow: selecteer meerdere afbeeldingsbestanden, bekijk het geselecteerde item, maak één PDF-pagina per afbeelding, schaal elke afbeelding in een A4-staand- of liggende pagina, sla het resultaat op en open de gegenereerde PDF.
var
MatchCount: Integer;
begin
// Schilder een clYellow-masker over elke match op de huidige pagina van
// de viewer. Wisselen naar een andere pagina wist de markering automatisch.
PdfView1.HighlightColor := clYellow;
MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
Status.Caption := Format('%d match(es) on this page', [MatchCount]);
// Verwijder de overlay programmatisch voordat de pagina wisselt
PdfView1.ClearHighlight;
end;
// FitMode houdt lange documenten ingekaderd tijdens Resize of paginawisselingen.
// Het rechtstreeks instellen van Zoom annuleert FitMode (wordt teruggezet naar pfmNone).
PdfView1.FitMode := pfmFitPage; // hele pagina in viewport
// PdfView1.FitMode := pfmFitWidth; // paginabreedte komt overeen met viewport-breedte
// PdfView1.FitMode := pfmActualSize; // 100%
// PageColor ontkoppelt de gerenderde PDF-pagina van de Color van de host-control
// — een typische donker-thema viewer-opzet houdt het scrollgebied donker terwijl
// de PDF-pagina papierwit blijft.
PdfView1.Color := clBlack; // scrollgebied
PdfView1.PageColor := clWhite; // PDF-pagina-achtergrond
// Optionele slagschaduw en paginarand om de pagina van een donkere achtergrond te tillen
PdfView1.PageShadowSize := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;
De projecten Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab en Demo\Lazarus\ViewerInteractionLab houden de moderne TPdfView-interactie-oppervlakte in één compact formulier: open of maak een voorbeeld-PDF, wissel DisplayMode, pas FitMode toe, wijzig PageColor, roteer pagina's, markeer huidige-pagina-zoekresultaten met HighlightSearchText, schakel gebruikerstekstselectie in of uit, inspecteer SelectedText, roep SelectAll, CopySelectionToClipboard en ClearSelection aan, en bekijk hoe DeviceToPage-coördinaten in de statusbalk bijwerken terwijl de muis over de pagina beweegt.
De projecten Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect en Demo\Lazarus\SearchAndSelect maken van de viewer-workflow voor zoeken en selecteren standalone voorbeelden. Ze openen of maken een PDF, vermelden zoekresultaten over alle pagina's met paginanummer, karakterindex en voorvertoningstekst, ondersteunen Vorige/Volgende-navigatie en dubbelklik-resultaat-springen, markeren huidige-pagina-matches met HighlightSearchText, stellen hoofdletter-/heel-woord-opties beschikbaar, schakelen AllowUserTextSelection aan/uit, bekijken SelectedText, sturen vanuit knoppen SelectAll, CopySelectionToClipboard en ClearSelection aan, en laten gebruikers wisselen tussen enkele en spread-weergavemodi.
De projecten Demo\Delphi\PrintPreferences, Demo\CBuilder\PrintPreferences en Demo\Lazarus\PrintPreferences lezen auteur-printvoorkeuren vóór het afdrukken. Ze tonen PrintCopies, PrintPageRanges, PrintScaling en PrintPaperHandling, kunnen door de auteur opgegeven kopieën en bereiken kopiëren naar de print-invoeren, en laten zien hoe SetPdfPrintPaperHandlingDevMode de PDF-duplex-intentie vertaalt naar een Windows-DEVMODE.
De projecten Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF en Demo\Lazarus\PrintPDF tonen de standaard print-workflow: open een PDF, bekijk pagina's vooraf, kies paginabereiken en kopieën in het platform-printdialoog, respecteer collate-modus, render elke pagina via TPdf.RenderPage en stel printvoortgang beschikbaar met annulering. Het Delphi-voorbeeld accepteert ook gesleepte PDF-bestanden voor snel laden.
De projecten Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer en Demo\Lazarus\MultiPageViewer oefenen continue, spread- en omslag-bewuste weergave met reguliere pagina-navigatie- en zoom-workflows. Ze zijn ook nuttig voor het testen van open-to-first-page-snelheid en render-responsiviteit op zeer grote PDF's: in tests met grote bestanden kunnen documenten van 2,x GB die Acrobat of Foxit 20-30 seconden kunnen kosten vóór de eerste weergave, bijna onmiddellijk bruikbaar worden via het PDFiumPas-viewer-pad.
De projecten Demo\Delphi\SplitView, Demo\CBuilder\SplitView en Demo\Lazarus\SplitView tonen PDF-beoordeling naast elkaar. De C++Builder- en Lazarus-voorbeelden richten zich op vergelijking in twee of drie panelen, selectie van actieve weergave, optionele gesynchroniseerde navigatie-commando's, gedeelde zoom-presets en rotatie voor de geselecteerde weergave of alle geladen weergaven. Gebruik deze voorbeelden om grote documenten te vergelijken terwijl de eerste bruikbare weergave en daaropvolgende pagina-rendering responsief blijven; dezelfde 2,x GB PDF-bestanden die Acrobat of Foxit 20-30 seconden nodig hebben om te openen, kunnen bijna onmiddellijk bruikbaar worden in het PDFiumPas-viewer-pad.
De projecten Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit en Demo\Lazarus\SecurityAudit openen een PDF alleen-lezen en vermelden de status van rechten, ingebedde bijlagen, document-JavaScript-acties, URI- en Launch-linkannotaties, weblinks, samenvattingen van handtekeningen, XFA-status, beschikbaarheid van V8-helpers en niet-ondersteunde-functie-callbacks. Dezelfde samenvattings- en bevindingsrijen kunnen worden opgeslagen of gekopieerd als TXT-auditrapport of als gestructureerd JSON-rapport worden opgeslagen. Ze zijn bedoeld als risico-panelen voor de host-toepassing, niet als malware-detectie.
// LinkOptions bestuurt de automatische afhandeling van de vier PDF-link-
// actietypen. Standaardwaarden schakelen alleen goto en URI in; launch
// (een programma uitvoeren) en embedded GotoR (naar een ander bestand
// springen) blijven UIT, zodat een per ongeluk klik geen willekeurige code
// kan uitvoeren.
PdfView1.LinkOptions :=
[loAutoGoto, loAutoOpenURI]; // veilige standaardwaarden
// OnAnnotationLinkClick vuurt voor bestemmingen binnen het document
// (paginasprongen, benoemde bestemmingen, actie-ketens). Markeer Handled = True
// om te voorkomen dat de viewer de link automatisch volgt.
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; // onderdruk automatische afhandeling
end;
end;
// OnWebLinkClick vuurt voor URI-strings ontdekt door PDFium's weblink-
// scanner (automatisch gedetecteerde URL's in de tekststream, zelfs zonder
// /A <</S/URI...>>-annotatie).
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
WebLinkIndex: Integer; const Url: WString;
var Handled: Boolean);
begin
Log.Add('Web link clicked: ' + string(Url));
end;
// Op het gefocuste AcroForm-widget sturen zes nieuwe methoden de
// formulier-edit-geschiedenis van PDFium aan. Alle zes vallen veilig
// terug wanneer geen widget gefocust is of het document geen AcroForm
// bevat.
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 drie staan standaard op True, zodat bestaande apps zonder
// gedragswijziging upgraden. Zet ze op False voor kiosken, preview-panes
// of alleen-lezen embeds zonder TPdfView te subclassen.
PdfView1.AllowUserPageChange := False; // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // wiel-omhoog/omlaag-sprongen
PdfView1.AllowUserTextSelection := False; // muis-/toetsenbord-tekstselectie
// Ctrl+wheel is gereserveerd voor zoom en wordt overgeslagen door de
// navigatie-handler ongeacht ChangePageOnMouseScrolling.
// TPdfThumbnailView (unit FPdfThumbnail) rendert één paginaminiatuur per
// rij in een scrollbaar zijpaneel.
uses FPdfThumbnail;
procedure TForm1.FormCreate(Sender: TObject);
begin
ThumbView.Pdf := Pdf1;
ThumbView.ThumbnailWidth := 120;
ThumbView.ThumbnailHeight := 160;
ThumbView.SelectionColor := $00CFA85F; // warme accentkleur
ThumbView.OnPageClick := ThumbViewPageClick;
end;
procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
PdfView1.PageNumber := PageIndex + 1; // PageIndex is zero-based
end;
procedure TForm1.PdfView1PageNumberGewijzigd(Sender: TObject);
begin
ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;
// Patroon gebruikt door Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport
// en Demo\Lazarus\BatchExport. Één TPdf per bestand, IPdfCancellationToken
// voor cancel-from-UI, TPdfStreamAdapter voor gebufferde file load.
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 is een goedkope catalog-probe — True wanneer /StructTreeRoot
// bestaat. StructureElements materialiseert de boom in een flat array van
// TPdfStructureElement-records met type, title, alternate text, actual
// text, expansion text, language, level, parent index en child / marked
// content / attribute counts.
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 is een globale functie die overeenkomt met PDFium's
// FPDF_SetPrintMode. Gebruik de bijpassende TPdfPrintMode-waarde voor het
// gebruikte printer-stuurprogramma.
SetPdfPrintMode(pmEmf); // GDI EMF (standaard)
// SetPdfPrintMode(pmTextOnly); // alleen-tekst-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-fonts
// PrintCopies, PrintPageRanges, PrintScaling en PrintPaperHandling zijn
// de viewer-preference-dictionary-entries — ze coderen wat de
// documentauteur wilde dat PDF-readers standaard zouden gebruiken.
var
Ranges: TPrintPageRanges;
Range: TPrintPageRange;
begin
ShowMessage('Suggested copies: ' + IntToStr(Pdf1.PrintCopies));
ShowMessage('Print scaling: ' + GetEnumName(TypeInfo(TPrintScaling),
Ord(Pdf1.PrintScaling)));
Ranges := Pdf1.PrintPageRanges; // one-based bereiken
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 op documentniveau wordt opgeslagen in de /Names /JavaScript
// name-tree. JavaScriptAction[i] retourneert één entry; JavaScriptActions
// retourneert de volledige 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 renderpad (standaard sinds v1.21.0): RenderPage schrijft
// rechtstreeks in de DIB-buffer van de bestemmings-TBitmap wanneer
// aanroepers een vooraf gealloceerde bitmap doorgeven, waarmee één kopie
// van width * height * 4 bytes wordt vermeden.
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;