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;
Projekty Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties i Demo\Lazarus\FontProperties pokazują te same metadane poprzez TPdf i TPdfView.
Projekty Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab i Demo\Lazarus\ContentExtractionLab przekształcają ekstrakcję treści w jeden przepływ pracy. Otwierają lub tworzą PDF, umożliwiają użytkownikom wybór metadanych, tekstu stron, obiektów stron, obrazów, załączników, linków, adnotacji, zakładek oraz metryk czcionek / znaków, a następnie generują podsumowanie wraz ze szczegółowym raportem TXT lub JSON. Laboratorium obejmuje również wybór wszystkich / żadnych kategorii, kopiowanie raportu do schowka oraz akcje zapisu źródłowego PDF, dzięki czemu użytkownicy mogą zachować wygenerowany przykładowy PDF obok jego raportu ekstrakcji. Starsze dema ExtractText, ExtractImages, Attachment i FontProperties pozostają skoncentrowanymi przykładami; ExtractText obsługuje opcje zakresu stron i separatora stron do generowania wyjścia tekstowego na stronę w Delphi, C++Builder i Lazarus, podczas gdy ContentExtractionLab jest przekrojowym przykładem inwentaryzacji.
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;
// Bez spłaszczania FormField[i] := value zapisuje tylko wpis /V —
// widoczny wygląd pochodzi ze strumienia /AP i może się nie odświeżyć.
// GenerateFormAppearances regeneruje /AP dla każdego widżetu, następnie
// FlattenAllPages wypala adnotacje i widżety każdej strony w
// trwałą, nieedytowalną zawartość.
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);
// Utwórz skompresowany dokument PDF
Pdf1.CreateDocument;
Pdf1.Compressed := True; // upewnij się, że wszystkie strumienie są skompresowane FlateDecode (domyślnie)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // rozmiar A4
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;
// Zapisz z opcjami
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // zapis przyrostowy
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // usuń zabezpieczenia
// Zapisz z określonym celem wersji PDF. Walidator wersji PDF odrzuca
// każdą funkcję wprowadzoną po wybranym celu (JBIG2Decode, MarkInfo,
// szyfrowanie AES, adnotacje Polygon / Caret / Watermark / Redact, ...).
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
ShowMessage('Save rejected: document contains features newer than PDF 1.5');
// SaveAsPdfA przetwarza wynik zapisu bazowego poprzez aktualizację przyrostową,
// która wstrzykuje strumień metadanych XMP, OutputIntent sRGB ICC oraz
// zaktualizowany Catalog dokumentu, dzięki czemu wynik jest zgodny z PDF/A-1b.
// Dołączony profil sRGB IEC61966-2.1 jest używany automatycznie, gdy nie podano
// niestandardowego profilu.
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 z niestandardowym profilem ICC (np. potoki druku CMYK)
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;
// Wyjście PDF/A w pamięci
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;
// Tania sonda w stylu nagłówka — odczytuje zgodność bez iteracji
// pełnego walidatora. Wynik: pacNone, pac1a, pac1b, pac2b, pac3b lub
// 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;
// Pełny walidator z diagnostyką per problem
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;
Projekty Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab i Demo\Lazarus\StandardsLab ładują lub tworzą PDF, uruchamiają ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR i ValidatePdfVT, a następnie pokazują każdy wykryty poziom zgodności i liczbę problemów w jednej siatce. Ich przyciski zapisu wywołują SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR i SaveAsPdfVT, dzięki czemu przepływ pracy wyjścia z markerami jest widoczny bez pisania niestandardowego harnessu.
Jednostka FPdfPreflightReport opakowuje ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR i ValidatePdfVT w jeden obiekt raportu. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport i Demo\CBuilder\PreflightReport umożliwiają użytkownikom wybór standardów, generowanie podsumowania, sprawdzanie statusu, priorytetu, kategorii problemów, liczników kategorii, kolejnych akcji, kodów problemów i rekomendowanych działań, przełączanie podglądu między tekstem, Markdown, JSON i CSV oraz eksport raportów TXT, HTML, Markdown, JSON lub CSV. Przykład GUI Delphi również dodaje podgląd Action Plan, który kondensuje niezaliczone standardy, priorytety, kody problemów i rekomendacje w listę kroków naprawczych. Demo\Delphi\PreflightReportCli udostępnia tę samą ścieżkę raportu jako przepływ pracy konsoli z argumentami wejścia / wyjścia, opcjonalnym hasłem, wyborem formatu wyjścia z plan / actionplan, filtrowaniem standardów, opcjonalnym osadzeniem raportu pojedynczego pliku attach=output.pdf, kodami wyjścia bramkującymi CI failon=, przetwarzaniem batch=list.txt, skanowaniem katalogów batchdir=folder z opcjonalną rekursją i stabilnym przetwarzaniem posortowanym po ścieżce, routingiem raportów outdir=reports, bezpiecznymi dla kolizji nazwami raportów dla zduplikowanych nazw plików wejściowych, podsumowaniami TXT / JSON / HTML ze zbiorczymi i standardowymi sumami statusu, manifestami wierszy CSV z ustawieniami uruchomienia powtarzanymi per element oraz trybem przykładowym bez argumentów dla skryptów i zadań CI. Wygenerowany raport stwierdza, że wbudowane walidatory pokrywają kontrole na poziomie markerów i wybrane na poziomie pliku, dzięki czemu pełne sprawdzenie preflight na poziomie treści może zostać delegowane do dedykowanego silnika walidacji.
// LoadCustomDocument pozwala PDFium pobierać bloki z dowolnego strumienia
// TStream z możliwością przeszukiwania na żądanie. Strumień NIE jest
// kopiowany do pamięci, więc wielogigabajtowe pliki PDF, zdalne strumienie
// treści HTTP i źródła bazodanowe są wszystkie wykonalne bez wstępnego
// buforowania.
var
FileStream: TFileStream;
begin
// Strumieniowe odczytywanie z pliku (bez kopii w pamięci)
FileStream := TFileStream.Create('C:\Huge.pdf',
fmOpenRead or fmShareDenyWrite);
Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
// ^^ własność przeniesiona — Pdf1 zwalnia FileStream przy UnloadDocument.
Pdf1.Active := True;
ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;
// Strumień zarządzany zewnętrznie (wywołujący zwalnia go po 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 udostępnia IPdfCancellationToken /
// IPdfCancellationTokenSource dla semantyki anulowania z UI, która
// działa na długo wykonywanych ścieżkach eksportu i renderowania.
uses FPdfAsync;
var
CancelSource: IPdfCancellationTokenSource;
CancelToken: IPdfCancellationToken;
begin
CancelSource := PdfCancellationTokenSource;
CancelToken := CancelSource.Token;
// ... użytkownik klika Anuluj ...
CancelSource.Cancel;
// Worker okresowo sprawdza CancelToken.IsCancelled, lub ścieżka
// renderowania sprawdza go poprzez callback IFSDK_PAUSE PDFium.
if CancelToken.IsCancelled then
Memo1.Lines.Add('Cancelled by user');
end;
// RenderPageProgressive pozwala długiemu renderowaniu w wysokim DPI przerwać
// w połowie strony, gdy użytkownik kliknie Anuluj, zamiast blokować wywołującego
// do zakończenia.
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> uruchamia workera w wątku tła i wysyła kopertę
// TPdfFutureResult<T> z powrotem do głównego wątku.
//
// Ważne: TPdf serializuje wywołania renderowania per instancję, ale worker nadal
// posiada dyspozytora i czas życia dokumentu. Nie modyfikuj TPdf, gdy renderowanie
// jest w toku; eksport wsadowy zwykle utrzymuje jeden TPdf na plik.
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 opakowuje FS_MATRIX PDFium, dzięki czemu wywołujący mogą zbudować
// transformację deklaratywnie i przekazać ją do FPDFPageObj_SetMatrix.
uses FPdfMatrix, FPdfView;
var
M: TPdfMatrix;
RawMatrix: FS_MATRIX;
PageObj: FPDF_PAGEOBJECT;
begin
M := TPdfMatrix.Create;
try
M.Translate(72, 200); // przesuń o 1 cal w prawo, ~2.8 cala w górę
M.Scale(0.5, 0.5); // stempel o połowie rozmiaru
M.Rotate(15); // obrót o 15 stopni przeciwnie do ruchu wskazówek zegara
// M.HorizontalFlip;
// M.VerticalFlip;
// M.CentralFlip;
// M.Skew(10, 5);
// M.Multiply(Other); // post-multiplikacja z inną macierzą
// Zastosuj do dowolnego obiektu strony PDFium (tekst / ścieżka / obraz / Form XObject)
RawMatrix := M.Handle;
FPDFPageObj_SetMatrix(PageObj, RawMatrix);
finally
M.Free;
end;
end;
// ImportPagesByIndex kopiuje jawną tablicę indeksów liczonych od zera z
// źródłowego PDF do tego dokumentu. InsertAt = 0 wstawia przed stroną 1;
// PageCount dołącza na końcu.
var
Source: TPdf;
begin
Source := TPdf.Create(nil);
try
Source.FileName := 'C:\Report.pdf';
Source.Active := True;
// Importuj strony 0, 2, 4 (liczone od 1: strony 1, 3, 5) ze Source
// i dołącz je na końcu Pdf1.
Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);
// Pusta tablica importuje każdą stronę źródłową
// Pdf1.ImportPagesByIndex(Source, [], 0);
finally
Source.Free;
end;
end;
// ImportNPagesToOne zwraca zupełnie nowy TPdf, którego strony są kompozytami
// NumX*NumY TEGO dokumentu. OutputWidth/Height to jednostki użytkownika PDF
// (1 jednostka = 1/72 cala).
var
Composite: TPdf;
begin
Pdf1.FileName := 'C:\Slides.pdf';
Pdf1.Active := True;
// 4-up A4 poziomo: 842 x 595 pt, 2 kolumny x 2 wiersze
Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
try
if Composite <> nil then
Composite.SaveAs('C:\Slides_4up.pdf');
finally
Composite.Free;
end;
end;
// MovePages przyjmuje indeksy stron liczone od zera, które mają zostać
// przeniesione, plus indeks docelowy dla pierwszej przeniesionej strony
// po zakończeniu przenoszenia. Inne strony przesuwają się wokół przeniesionego
// bloku, dzięki czemu długość dokumentu pozostaje taka sama.
//
// Przykład: w 5-stronicowym dokumencie ze stronami ponumerowanymi [0,1,2,3,4],
// MovePages([2, 3], 0) zmienia kolejność na [2, 3, 0, 1, 4].
if not Pdf1.MovePages([2, 3], 0) then
ShowMessage('Invalid indices passed to MovePages');
// CreateXObjectFromPage tworzy wielokrotnie używalny uchwyt Form XObject z
// dowolnej strony dowolnego innego TPdf. Stempluj ten uchwyt na dowolnej liczbie
// stron TEGO dokumentu — każdy stempel jest pojedynczym obiektem strony PDFium,
// który może być pozycjonowany, skalowany i obracany poprzez API macierzy.
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;
// Opakuj stronę 0 WatermarkSource jako wielokrotnie używalny 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;
// Wycentruj znak wodny na stronie
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; // zamyka uchwyt FPDF_XOBJECT
end;
Pdf1.SaveAs('C:\Watermarked.pdf');
finally
WatermarkSource.Free;
end;
end;
Projekty Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp i Demo\CBuilder\WatermarkStamp przekształcają ten wzorzec w uruchamialny przepływ pracy: tworzą docelowy PDF i wielokrotnie używalny PDF stempla, opakowują stronę stempla za pomocą CreateXObjectFromPage, wstawiają ten sam Form XObject na każdą docelową stronę, stosują transformacje TPdfMatrix dla wycentrowanych znaków wodnych i stempli w prawym górnym rogu oraz dodają opcjonalne etykiety numerów stron przed zapisaniem wyniku.
// AddImage(FileName) akceptuje każdy format zarejestrowany w jednostkach
// graficznych VCL lub LCL (BMP, PNG przez PngImage, JPG, ...).
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);
// AddImage(TBitmap) pomija pośrednika TPicture, idealne gdy bitmapa
// pochodzi już z renderowania lub generowania wsadowego.
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;
Projekty Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF i Demo\Lazarus\ImageToPDF przekształcają to w pełny przepływ pracy wsadowy: wybierz wiele plików obrazów, podgląd wybranego elementu, utwórz jedną stronę PDF na obraz, przeskaluj każdy obraz do strony A4 pionowej lub poziomej, zapisz wynik i otwórz wygenerowany PDF.
var
MatchCount: Integer;
begin
// Nałóż maskę clYellow nad każdym dopasowaniem na bieżącej stronie widoku.
// Przejście na inną stronę usuwa podświetlenie automatycznie.
PdfView1.HighlightColor := clYellow;
MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
Status.Caption := Format('%d match(es) on this page', [MatchCount]);
// Programowo usuń nakładkę przed zmianą strony
PdfView1.ClearHighlight;
end;
// FitMode utrzymuje długie dokumenty w ramach podczas zmiany rozmiaru lub zmiany strony.
// Ustawienie Zoom bezpośrednio anuluje FitMode (powraca do pfmNone).
PdfView1.FitMode := pfmFitPage; // cała strona w obszarze wyświetlania
// PdfView1.FitMode := pfmFitWidth; // szerokość strony pasuje do szerokości obszaru wyświetlania
// PdfView1.FitMode := pfmActualSize; // 100%
// PageColor oddziela renderowaną stronę PDF od koloru kontrolki hosta
// — typowa konfiguracja przeglądarki w trybie ciemnym utrzymuje obszar
// przewijania ciemny, podczas gdy strona PDF pozostaje papierowo biała.
PdfView1.Color := clBlack; // obszar przewijania
PdfView1.PageColor := clWhite; // tło strony PDF
// Opcjonalny cień i obramowanie strony, aby unieść stronę nad ciemnym tłem
PdfView1.PageShadowSize := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;
Projekty Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab i Demo\Lazarus\ViewerInteractionLab utrzymują nowoczesną powierzchnię interakcji TPdfView w jednym kompaktowym formularzu: otwórz lub utwórz przykładowy PDF, przełącz DisplayMode, zastosuj FitMode, zmień PageColor, obróć strony, podświetl dopasowania wyszukiwania bieżącej strony za pomocą HighlightSearchText, włącz lub wyłącz zaznaczanie tekstu przez użytkownika, sprawdź SelectedText, wywołaj SelectAll, CopySelectionToClipboard i ClearSelection oraz obserwuj współrzędne DeviceToPage aktualizujące się w pasku statusu, gdy mysz porusza się nad stroną.
Projekty Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect i Demo\Lazarus\SearchAndSelect przekształcają przepływ pracy wyszukiwania i zaznaczania w samodzielne próbki. Otwierają lub tworzą PDF, wymieniają wyniki wyszukiwania ze wszystkich stron z numerem strony, indeksem znaku i tekstem podglądu, obsługują nawigację Poprzedni / Następny i podwójne kliknięcie wyniku, podświetlają dopasowania bieżącej strony za pomocą HighlightSearchText, udostępniają opcje dopasowania wielkości liter i całego słowa, przełączają AllowUserTextSelection, podglądają SelectedText, sterują SelectAll, CopySelectionToClipboard i ClearSelection z przycisków oraz pozwalają użytkownikom przełączać się między pojedynczymi a rozkładówkowymi trybami wyświetlania.
Projekty Demo\Delphi\PrintPreferences, Demo\CBuilder\PrintPreferences i Demo\Lazarus\PrintPreferences odczytują preferencje druku autora przed drukowaniem. Wyświetlają PrintCopies, PrintPageRanges, PrintScaling i PrintPaperHandling, mogą kopiować podane przez autora kopie i zakresy do wejść druku oraz pokazują, jak SetPdfPrintPaperHandlingDevMode mapuje intencję duplex PDF do Windows DEVMODE.
Projekty Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF i Demo\Lazarus\PrintPDF pokazują standardowy przepływ pracy druku: otwórz PDF, podgląd stron, wybierz zakresy stron i kopie w platformowym oknie dialogowym druku, uwzględnij tryb sortowania, renderuj każdą stronę poprzez TPdf.RenderPage i udostępnij postęp druku z anulowaniem. Przykład Delphi akceptuje również upuszczone pliki PDF dla szybkiego ładowania.
Projekty Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer i Demo\Lazarus\MultiPageViewer ćwiczą przeglądanie ciągłe, rozkładówkowe i z okładką wraz z regularnymi przepływami pracy nawigacji po stronach i powiększenia. Są również użyteczne do testowania szybkości otwierania pierwszej strony i responsywności renderowania na bardzo dużych plikach PDF: w testach dużych plików dokumenty o rozmiarze 2.x GB, które mogą sprawić, że Acrobat lub Foxit spędzą 20-30 sekund przed pierwszym widokiem, mogą stać się użyteczne niemal natychmiast poprzez ścieżkę przeglądarki PDFiumPas.
Projekty Demo\Delphi\SplitView, Demo\CBuilder\SplitView i Demo\Lazarus\SplitView pokazują przegląd PDF obok siebie. Próbki C++Builder i Lazarus koncentrują się na porównaniu dwóch lub trzech paneli, wyborze aktywnego widoku, opcjonalnie synchronizowanych poleceniach nawigacji, współdzielonych presetach powiększenia i obrocie dla wybranego widoku lub wszystkich załadowanych widoków. Użyj tych próbek do porównywania dużych dokumentów przy zachowaniu pierwszego użytecznego widoku i responsywnego renderowania kolejnych stron; te same pliki PDF o rozmiarze 2.x GB, których otwarcie może zająć Acrobatowi lub Foxitowi 20-30 sekund, mogą stać się użyteczne niemal natychmiast na ścieżce przeglądarki PDFiumPas.
Projekty Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit i Demo\Lazarus\SecurityAudit otwierają PDF tylko do odczytu i wymieniają stan uprawnień, osadzone załączniki, akcje JavaScript dokumentu, adnotacje linków URI i Launch, linki webowe, podsumowania podpisów, stan XFA, dostępność pomocników V8 oraz callbacki nieobsługiwanych funkcji. To samo podsumowanie i wiersze wyników można zapisać lub skopiować jako raport audytu TXT lub zapisać jako ustrukturyzowany JSON. Są przeznaczone jako panele ryzyka aplikacji hosta, nie jako wykrywanie złośliwego oprogramowania.
// LinkOptions kontroluje automatyczną obsługę czterech typów akcji linków PDF.
// Domyślnie włącza tylko goto i URI; launch (uruchom program) i embedded GotoR
// (przejdź do innego pliku) pozostają WYŁĄCZONE, aby przypadkowe kliknięcie
// nie mogło uruchomić dowolnego kodu.
PdfView1.LinkOptions :=
[loAutoGoto, loAutoOpenURI]; // bezpieczne ustawienia domyślne
// OnAnnotationLinkClick uruchamia się dla miejsc docelowych w dokumencie
// (przejścia do stron, nazwane miejsca docelowe, łańcuchy akcji). Ustaw
// Handled = True, aby zapobiec automatycznemu podążaniu za linkiem.
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; // wyciszanie automatycznej obsługi
end;
end;
// OnWebLinkClick uruchamia się dla ciągów URI odkrytych przez skaner
// linków webowych PDFium (automatycznie wykryte URL w strumieniu tekstu,
// nawet bez adnotacji /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;
// Na sfokusowanym widżecie AcroForm sześć nowych metod steruje historią
// edycji formularzy PDFium. Wszystkie sześć bezpiecznie zwraca, jeśli żaden
// widżet nie jest sfokusowany lub dokument nie zawiera 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;
// Wszystkie trzy domyślnie ustawione na True, dzięki czemu istniejące aplikacje
// aktualizują się bez zmiany zachowania. Przełącz je na False dla kiosków,
// paneli podglądu lub osadzeń tylko do odczytu bez tworzenia podklasy TPdfView.
PdfView1.AllowUserPageChange := False; // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // skoki kółkiem w górę/dół
PdfView1.AllowUserTextSelection := False; // zaznaczanie tekstu myszą/klawiaturą
// Ctrl+kółko jest zarezerwowane dla powiększenia i jest pomijane przez
// moduł nawigacji niezależnie od ChangePageOnMouseScrolling.
// TPdfThumbnailView (jednostka FPdfThumbnail) renderuje jedną miniaturę strony
// w każdym wierszu w przewijalnym panelu bocznym.
uses FPdfThumbnail;
procedure TForm1.FormCreate(Sender: TObject);
begin
ThumbView.Pdf := Pdf1;
ThumbView.ThumbnailWidth := 120;
ThumbView.ThumbnailHeight := 160;
ThumbView.SelectionColor := $00CFA85F; // ciepłe podświetlenie
ThumbView.OnPageClick := ThumbViewPageClick;
end;
procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
PdfView1.PageNumber := PageIndex + 1; // PageIndex jest liczony od zera
end;
procedure TForm1.PdfView1PageNumberZmieniono(Sender: TObject);
begin
ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;
// Wzorzec używany przez Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport
// i Demo\Lazarus\BatchExport. Jeden TPdf na plik, IPdfCancellationToken
// dla anulowania z UI, TPdfStreamAdapter dla buforowanego ładowania pliku.
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 jest tanim sondowaniem Catalog — True, gdy istnieje /StructTreeRoot.
// StructureElements materializuje drzewo w płaską tablicę rekordów
// TPdfStructureElement z typem, tytułem, tekstem alternatywnym, tekstem
// rzeczywistym, tekstem rozwinięcia, językiem, poziomem, indeksem rodzica
// oraz liczbą dzieci / marked content / atrybutów.
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 jest funkcją globalną, która mapuje do
// FPDF_SetPrintMode PDFium. Użyj pasującej wartości TPdfPrintMode dla
// używanego sterownika drukarki.
SetPdfPrintMode(pmEmf); // GDI EMF (domyślnie)
// SetPdfPrintMode(pmTextOnly); // renderowanie tylko tekstu
// SetPdfPrintMode(pmPostScript2); // PostScript Level 2
// SetPdfPrintMode(pmPostScript3); // PostScript Level 3
// SetPdfPrintMode(pmPostScript2PassThrough);
// SetPdfPrintMode(pmPostScript3PassThrough);
// SetPdfPrintMode(pmEmfImageMasks); // EMF + maski obrazów
// SetPdfPrintMode(pmPostScript3Type42); // PostScript 3 + czcionki Type 42
// PrintCopies, PrintPageRanges, PrintScaling i PrintPaperHandling to
// wpisy słownika viewer-preference — kodują to, czego autor dokumentu
// chciał, aby czytniki PDF używały domyślnie.
var
Ranges: TPrintPageRanges;
Range: TPrintPageRange;
begin
ShowMessage('Suggested copies: ' + IntToStr(Pdf1.PrintCopies));
ShowMessage('Print scaling: ' + GetEnumName(TypeInfo(TPrintScaling),
Ord(Pdf1.PrintScaling)));
Ranges := Pdf1.PrintPageRanges; // zakresy liczone od 1
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 na poziomie dokumentu jest przechowywany w drzewie nazw
// /Names /JavaScript. JavaScriptAction[i] zwraca jeden wpis;
// JavaScriptActions zwraca pełną tablicę.
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;
// Ścieżka renderowania bez kopiowania (domyślnie od v1.21.0): RenderPage zapisuje
// bezpośrednio do bufora DIB docelowej TBitmap, gdy wywołujący przekazują
// wstępnie zaalokowaną bitmapę, unikając jednej kopii o rozmiarze szerokość * wysokość * 4 bajty.
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;