PDFium Component Docs

Programovací příklady PDFium VCL

Základní operace s dokumenty

Načtení a zobrazení PDF dokumentu

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 načtený ze streamu');
  finally
    Stream.Free;
  end;
end;

Vytvoření nového PDF dokumentu

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;

Textové operace

Extrakce textu z PDF

var
  PageText: WString;
  RectText: WString;
begin
  Pdf1.PageNumber := 1;
  PageText := Pdf1.Text;
  Memo1.Text := PageText;
  RectText := Pdf1.TextInRectangle(100, 100, 500, 200);
  ShowMessage('Text v obdélníku: ' + RectText);
end;

Vyhledávání textu

var
  Position: Integer;
begin
  Position := Pdf1.FindFirst('Hello', [seCaseSensitive]);
  while Position >= 0 do
  begin
    ShowMessage('Nalezeno na znaku: ' + IntToStr(Position));
    Position := Pdf1.FindNext;
  end;
end;

Přidání textu s formátováním

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;

Kontrola metadat znaků a fontů

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('Objekt ' + IntToStr(I));
      Memo1.Lines.Add('Základní název: ' + Pdf1.FontBaseName[I]);
      Memo1.Lines.Add('Rodina: ' + Pdf1.FontFamilyName[I]);
      Memo1.Lines.Add('Vložený: ' + BoolToStr(Pdf1.FontIsEmbedded[I], True));
      Memo1.Lines.Add('Tloušťka: ' + IntToStr(Pdf1.FontWeight[I]));
      Memo1.Lines.Add('Kurzíva: ' + 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('Počet bajtů fontu: ' + IntToStr(Length(FontBytes)));
    end;

  if Pdf1.CharacterCount > 0 then
  begin
    Memo1.Lines.Add('Velikost prvního znaku: ' + FloatToStr(Pdf1.CharacterFontSize[0]));
    Memo1.Lines.Add('Tloušťka prvního znaku: ' + IntToStr(Pdf1.CharacterFontWeight[0]));
    Memo1.Lines.Add('Úhel prvního znaku: ' + FloatToStr(Pdf1.CharacterAngle[0]));
  end;
end;

Projekty Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties a Demo\Lazarus\FontProperties zobrazují stejná metadata přes TPdf i TPdfView

Tvorba reportu pro vícekategoriální extrakci obsahu

Projekty Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab a Demo\Lazarus\ContentExtractionLab převádějí extrakci obsahu do jednoho pracovního toku. Otevírají nebo vytvářejí PDF, umožňují vybrat metadata, text stránky, objekty stránky, obrázky, přílohy, odkazy, anotace, záložky a metriky fontů či znaků a pak vygenerují souhrn i podrobný report TXT nebo JSON. Laboratoř obsahuje také volbu kategorií vše nebo nic, kopírování reportu do schránky a akce pro uložení zdrojového PDF, aby uživatelé mohli mít vygenerovaný ukázkový PDF vedle reportu extrakce. Starší ukázky ExtractText, ExtractImages, Attachment a FontProperties zůstávají cílenými příklady; ExtractText podporuje volby rozsahu stránek a oddělovače stránek pro vytvoření textového výstupu po jednotlivých stránkách napříč Delphi, C++Builderem a Lazarusem, zatímco ContentExtractionLab je průřezová ukázka inventáře

Operace s formuláři

Práce s poli formuláře

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;

Flatten Filled Form Values into the Page

// Without flattening, FormField[i] := value writes only the /V entry —
// viditelný vzhled pochází z /AP streamu a nemusí se hned obnovit
// GenerateFormAppearances regenerates /AP for every widget, then
// FlattenAllPages bakes every page's annotations and widgets into
// trvalý, needitovatelný obsah
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;

Operace s obrázky

Extrakce obrázků

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;

Přidávání obrázků

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;

Operace s anotacemi

Práce s anotacemi

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;

Kreslicí operace

Kreslení tvarů

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;

Navigace a záložky

Práce se záložkami

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;

Práce s pojmenovanými cíli

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;

Operace s přílohami

Práce s přílohami

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;

Správa stránek

Operace se stránkami

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

Saving and Compression

Ukládání s kompresí

// Vytvořit komprimovaný dokument PDF
Pdf1.CreateDocument;
Pdf1.Compressed := True; // zajistí kompresi všech streamů FlateDecode (výchozí)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // formát A4
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;

// Uložit s volbami
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // přírůstkové uložení
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // odstranit zabezpečení

// Uložit do konkrétní cílové verze PDF. Kontrola verze PDF odmítne
// jakoukoli funkci zavedenou po zvoleném cíli (JBIG2Decode, MarkInfo,
// šifrování AES, anotace Polygon / Caret / Watermark / Redact, ...)
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
  ShowMessage('Uložení odmítnuto: dokument obsahuje funkce novější než PDF 1.5');

Vytvoření archivního výstupu PDF/A-1b

// SaveAsPdfA post-processes the base save with an incremental update that
// injects an XMP metadata stream, an sRGB ICC OutputIntent, and an updated
// document catalog so the result is PDF/A-1b conformant. The bundled
// sRGB IEC61966-2.1 profile is used automatically when no custom profile
// je k dispozici
Pdf1.FileName := 'C:\Report.pdf';
Pdf1.Active := True;
if Pdf1.SaveAsPdfA('C:\Report.pdfa.pdf', pac1b) then
  ShowMessage('Archiv PDF/A-1b uložen');

// PDF/A s vlastním ICC profilem (například pro CMYK tiskové workflow)
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;

// Výstup PDF/A v paměti
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;

Validace shody PDF/A

var
  Validation: TPdfAValidationResult;
  Issue: TPdfAValidationIssue;
begin
  Pdf1.FileName := 'C:\Incoming\report.pdf';
  Pdf1.Active   := True;

  // Rychlá kontrola hlavičky — načte shodu bez spuštění
  // plného validátoru. Výsledek: pacNone, pac1a, pac1b, pac2b, pac3b nebo
  // 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 := 'Neznámá shoda';
  end;

  // Plný validátor s diagnostikou jednotlivých problémů
  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;

Kontrola více standardů PDF ve StandardsLab

Projekty Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab a Demo\Lazarus\StandardsLab načtou nebo vytvoří PDF, spustí ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR a ValidatePdfVT, a pak v jedné tabulce zobrazí každou zjištěnou úroveň shody i počet problémů. Tlačítka pro uložení volají SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR a SaveAsPdfVT, takže je pracovní tok markerového výstupu vidět bez psaní vlastního harnessu

Generating Preflight Reports

Jednotka FPdfPreflightReport sdružuje ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR a ValidatePdfVT do jednoho objektu reportu. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport a Demo\CBuilder\PreflightReport umožňují uživatelům vybrat standardy, vygenerovat souhrn, prohlédnout stav, prioritu, kategorie problémů, počty kategorií, další akce, kódy problémů a doporučené akce, přepínat náhled mezi textem, Markdownem, JSON a CSV a exportovat reporty TXT, HTML, Markdown, JSON nebo CSV. Delphi GUI ukázka navíc přidává náhled Action Plan, který shrnuje nevyhovující standardy, priority, kódy problémů a doporučení do seznamu kroků k nápravě. Demo\Delphi\PreflightReportCli zpřístupňuje stejný tok reportu jako konzolový pracovní postup s vstupními a výstupními argumenty, volitelným heslem, volbou výstupního formátu včetně plan / actionplan, filtrováním standardů, volitelným vložením reportu do jediného souboru přes attach=output.pdf, chybovými kódy pro CI přes failon=, zpracováním batch=list.txt, prohledáváním adresáře přes batchdir=folder s volitelnou rekurzí a stabilním řazením podle cesty, směrováním reportů přes outdir=reports, názvy reportů odolnými proti kolizím při duplicitních názvech vstupních souborů, souhrny TXT / JSON / HTML s celkovými součty a součty podle standardů, manifesty CSV s opakovaným nastavením pro každou položku a ukázkový režim bez argumentů pro skripty a CI joby. Vygenerovaný report uvádí, že vestavěné validátory pokrývají kontroly na úrovni markerů a vybraných souborových kontrol, takže plný content-level preflight lze stále předat specializovanému validačnímu enginu.

Streamované načítání a velké dokumenty

Načítání PDF na vyžádání pomocí LoadCustomDocument

// LoadCustomDocument umožňuje PDFium načítat bloky z libovolného sekvenčně
// přístupného TStream na vyžádání. Stream se NEkopíruje do paměti, takže
// vícegigabajtové PDF, vzdálené HTTP body streamy i zdroje v databázi jsou
// použitelné bez předběžného bufferu
var
  FileStream: TFileStream;
begin
  // Streamování ze souboru (bez kopie do paměti)
  FileStream := TFileStream.Create('C:\Huge.pdf',
    fmOpenRead or fmShareDenyWrite);
  Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
  // ^^ vlastnictví je předáno — Pdf1 uvolní FileStream při UnloadDocument
  Pdf1.Active := True;
  ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;

// Externě vlastněný stream (volající jej uvolní 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;

Asynchronní zrušení a progresivní vykreslování

Kooperativní tokeny zrušení

// FPdfAsync zpřístupňuje IPdfCancellationToken /
// IPdfCancellationTokenSource pro zrušení z UI, které funguje
// napříč dlouhotrvajícími exportními a vykreslovacími cestami
uses FPdfAsync;

var
  CancelSource: IPdfCancellationTokenSource;
  CancelToken:  IPdfCancellationToken;
begin
  CancelSource := PdfCancellationTokenSource;
  CancelToken  := CancelSource.Token;

  // ... uživatel klikne na Zrušit ...
  CancelSource.Cancel;

  // Pracovník průběžně kontroluje CancelToken.IsCancelled nebo jej
  // vykreslovací cesta dotazuje přes PDFium callback IFSDK_PAUSE
  if CancelToken.IsCancelled then
    Memo1.Lines.Add('Zrušeno uživatelem');
end;

Progresivní vykreslení stránky s možností zrušení

// RenderPageProgressive umožňuje dlouhé vykreslování ve vysokém DPI
// přerušit uprostřed stránky, když uživatel klikne na Cancel, místo aby
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('Vykreslení zrušeno — k dispozici je částečný výsledek');
      prsFailed:    Memo1.Lines.Add('PDFium nahlásilo FPDF_RENDER_FAILED');
    end;
  finally
    Bitmap.Free;
  end;
end;

Spouštění vykreslování na pozadí pomocí TPdfFuture

// TPdfFuture<T> spustí pracovník na pozadí a pošle obálku
// TPdfFutureResult<T> zpět na hlavní vlákno
//
// Důležité: TPdf serializuje volání vykreslení pro každou instanci, ale
// pracovník stále řídí dispatch i životnost dokumentu. Neměňte TPdf, když
// právě běží render; dávkový export obvykle drží jednu instanci TPdf na soubor
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('Zrušeno');
        pfsFailed:    Log.Add('Pracovník selhal: ' + AResult.ErrorMessage);
      end;
    end);
end;

Transformační matice

Skládání Translate / Scale / Rotate / Skew

// FPdfMatrix zapouzdřuje FS_MATRIX z PDFium, takže volající mohou
// sestavovat transformaci deklarativně a předat ji do FPDFPageObj_SetMatrix
uses FPdfMatrix, FPdfView;

var
  M: TPdfMatrix;
  RawMatrix: FS_MATRIX;
  PageObj: FPDF_PAGEOBJECT;
begin
  M := TPdfMatrix.Create;
  try
    M.Translate(72, 200);       // posun o 1 in doprava, ~2,8 in nahoru
    M.Scale(0.5, 0.5);          // razítko na polovinu velikosti
    M.Rotate(15);               // otočení proti směru hodin o 15 stupňů
    // M.HorizontalFlip;
    // M.VerticalFlip;
    // M.CentralFlip;
    // M.Skew(10, 5);
    // M.Multiply(Other);       // post-multiply s další maticí

    // Aplikovat na libovolný PDFium page object (text / path / image / form XObject)
    RawMatrix := M.Handle;
    FPDFPageObj_SetMatrix(PageObj, RawMatrix);
  finally
    M.Free;
  end;
end;

Skládání stránek

Importování podmnožiny stránek podle indexu

// ImportPagesByIndex kopíruje explicitní pole indexů od nuly ze zdrojového
// PDF do tohoto dokumentu. InsertAt = 0 vloží před stránku 1; PageCount
// přidá na konec
var
  Source: TPdf;
begin
  Source := TPdf.Create(nil);
  try
    Source.FileName := 'C:\Report.pdf';
    Source.Active   := True;

    // Importovat stránky 0, 2, 4 (jedna-based: stránky 1, 3, 5) ze Source
    // a připojit je na konec Pdf1
    Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);

    // Prázdné pole importuje každou zdrojovou stránku
    // Pdf1.ImportPagesByIndex(Source, [], 0);
  finally
    Source.Free;
  end;
end;

Kompozitní výstup N-up

// ImportNPagesToOne vrací zcela nový TPdf, jehož stránky jsou kompozity
// NumX*NumY tohoto dokumentu. OutputWidth/Height jsou uživatelské jednotky PDF
// (1 jednotka = 1/72 in)
var
  Composite: TPdf;
begin
  Pdf1.FileName := 'C:\Slides.pdf';
  Pdf1.Active   := True;
  // 4-up A4 landscape: 842 x 595 pt, 2 columns x 2 rows
  Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
  try
    if Composite <> nil then
      Composite.SaveAs('C:\Slides_4up.pdf');
  finally
    Composite.Free;
  end;
end;

Přeuspořádání stránek na místě

// MovePages bere indexy stránek od nuly, které se mají přesunout, a také
// cílový index první přesunuté stránky po dokončení přesunu
// Ostatní stránky se přesunou kolem posunutého bloku, takže délka dokumentu
// zůstane stejná
//
// Příklad: v 5stránkovém dokumentu s čísly stránek [0,1,2,3,4]
// MovePages([2, 3], 0) přeuspořádá na [2, 3, 0, 1, 4]
if not Pdf1.MovePages([2, 3], 0) then
  ShowMessage('Invalid indices passed to MovePages');

Vodoznaky pomocí opakovaného použití stránky jako Form XObject

// CreateXObjectFromPage vytvoří z libovolné stránky libovolného jiného TPdf
// znovupoužitelný handle Form XObject. Tento handle pak můžete otisknout na
// libovolný počet stránek TOHOTO dokumentu — každý otisk je jediný PDFium
// page object, který lze umístit, škálovat a otočit přes 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;

    // Zabalit stránku 0 z WatermarkSource jako znovupoužitelný 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;

        // Vycentrovat vodoznak na stránce
        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; // zavře FPDF_XOBJECT handle
    end;

    Pdf1.SaveAs('C:\Watermarked.pdf');
  finally
    WatermarkSource.Free;
  end;
end;

Projekty Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp a Demo\CBuilder\WatermarkStamp převádějí tento vzor do spustitelného workflow: vytvoří cílové PDF a znovupoužitelný razítkový PDF, obalí stránku razítka pomocí CreateXObjectFromPage, vloží stejné Form XObject na každou cílovou stránku, použijí transformace TPdfMatrix pro centrované watermarky a razítka vpravo nahoře a před uložením výsledku přidají volitelné štítky čísel stránek

Vkládání obrázků (přímé přetížení AddImage)

Adding a Registered Image File or a TBitmap

// AddImage(FileName) přijímá libovolný formát registrovaný ve VCL nebo LCL
// grafických jednotkách (BMP, PNG přes PngImage, JPG, ...)
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);

// AddImage(TBitmap) přeskočí mezičlánek TPicture, ideální když bitmapa už
// pochází z vykreslování nebo dávkové generace
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 a Demo\Lazarus\ImageToPDF převádějí toto do plného dávkového pracovního toku: vybrat více souborů s obrázky, zobrazit náhled vybrané položky, vytvořit jednu PDF stránku na obrázek, přizpůsobit každý obrázek na stránku A4 na výšku nebo na šířku, uložit výsledek a otevřít vygenerované PDF

Zvýraznění hledání v prohlížeči

Zvýraznění každé shody na aktuální stránce

var
  MatchCount: Integer;
begin
  // Vykreslit masku clYellow přes každou shodu na aktuální stránce zobrazení
  // Přepnutí na jinou stránku zvýraznění automaticky vymaže
  PdfView1.HighlightColor := clYellow;
  MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
  Status.Caption := Format('%d shod na této stránce', [MatchCount]);

  // Programově odstranit překrytí před změnou stránky
  PdfView1.ClearHighlight;
end;

Rozvržení prohlížeče a barva stránky

Režim přizpůsobení a pozadí stránky

// FitMode udržuje dlouhé dokumenty ve výřezu při změně velikosti nebo stránky
// Přímé nastavení Zoom FitMode zruší (vrátí se na pfmNone)
PdfView1.FitMode := pfmFitPage;       // celá stránka ve výřezu
// PdfView1.FitMode := pfmFitWidth;   // šířka stránky odpovídá šířce výřezu
// PdfView1.FitMode := pfmActualSize; // 100 %

// PageColor odděluje vykreslenou PDF stránku od Color hostitelského ovládacího prvku
// typické nastavení pro tmavý režim ponechá posuvnou oblast tmavou, zatímco
// PDF stránka zůstává papírově bílá
PdfView1.Color     := clBlack;        // oblast posuvu
PdfView1.PageColor := clWhite;        // pozadí PDF stránky

// Volitelný stín a okraj stránky, aby stránka vystoupila z tmavého pozadí
PdfView1.PageShadowSize  := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;

Práce s interakcí prohlížeče ve ViewerInteractionLab

Projekty Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab a Demo\Lazarus\ViewerInteractionLab udržují moderní interakční povrch TPdfView v jednom kompaktním formuláři: otevřít nebo vytvořit ukázkové PDF, přepnout DisplayMode, použít FitMode, změnit PageColor, otáčet stránky, zvýraznit shody na aktuální stránce pomocí HighlightSearchText, zapnout nebo vypnout uživatelský výběr textu, zkontrolovat SelectedText, volat SelectAll, CopySelectionToClipboard a ClearSelection, a sledovat, jak se souřadnice DeviceToPage mění ve stavovém řádku při pohybu myši nad stránkou

Vyhledávání a výběr textu v SearchAndSelect

Projekty Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect a Demo\Lazarus\SearchAndSelect převádějí pracovní tok prohlížeče pro vyhledávání a výběr do samostatných ukázek. Otevírají nebo vytvářejí PDF, vypisují výsledky hledání na všech stránkách s číslem stránky, indexem znaku a náhledem textu, podporují navigaci Previous / Next a přechod na výsledek dvojklikem, zvýrazňují shody na aktuální stránce pomocí HighlightSearchText, zpřístupňují volby match-case a whole-word, přepínají AllowUserTextSelection, zobrazují náhled SelectedText, ovládají SelectAll, CopySelectionToClipboard a ClearSelection z tlačítek a umožňují přepínat mezi single a spread režimem zobrazení

Reading PDF Print Preferences in PrintPreferences

Projekty Demo\Delphi\PrintPreferences, Demo\CBuilder\PrintPreferences a Demo\Lazarus\PrintPreferences čtou tiskové předvolby autora před tiskem. Zobrazují PrintCopies, PrintPageRanges, PrintScaling a PrintPaperHandling, mohou zkopírovat autorem dodané počty kopií a rozsahy do vstupů tisku a ukazují, jak SetPdfPrintPaperHandlingDevMode mapuje záměr oboustranného tisku PDF do Windows DEVMODE

Standard PDF Printing in PrintPDF

Projekty Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF a Demo\Lazarus\PrintPDF ukazují standardní pracovní tok tisku: otevřít PDF, zobrazit náhled stránek, zvolit rozsahy stránek a počet kopií v tiskovém dialogu platformy, respektovat režim řazení kopií, vykreslit každou stránku přes TPdf.RenderPage a zobrazit průběh tisku s možností zrušení. Delphi ukázka také přijímá přetažené PDF soubory pro rychlé načtení

Fast Multi-Page Viewing in MultiPageViewer

Projekty Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer a Demo\Lazarus\MultiPageViewer zkoušejí plynulé, rozložené a obálkové zobrazení s běžnou navigací po stránkách a zoom workflow. Hodí se také pro testování rychlosti otevření první stránky a odezvy vykreslování u velmi velkých PDF: v testech velkých souborů mohou dokumenty velikosti 2.x GB, které v Acrobat nebo Foxit potřebují před prvním zobrazením 20-30 sekund, být přes cestu prohlížeče PDFiumPas použitelné téměř okamžitě

Side-by-Side Review in SplitView

Projekty Demo\Delphi\SplitView, Demo\CBuilder\SplitView a Demo\Lazarus\SplitView ukazují porovnávání PDF vedle sebe. Ukázky v C++Builderu a Lazarusu se zaměřují na porovnání ve dvou nebo třech panelech, výběr aktivního pohledu, volitelné synchronizované navigační příkazy, sdílené zoom předvolby a rotaci pro vybraný pohled nebo všechny načtené pohledy. Tyto ukázky použijte pro porovnávání velkých dokumentů při zachování odezvy první použitelné části pohledu i následného vykreslování stránek; stejné PDF soubory velikosti 2.x GB, které mohou Acrobat nebo Foxit otevírat 20-30 sekund, mohou být v cestě prohlížeče PDFiumPas použitelné téměř okamžitě

Auditing PDF Risk Surface in SecurityAudit

Projekty Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit a Demo\Lazarus\SecurityAudit otevírají PDF jen pro čtení a vypisují stav oprávnění, vložené přílohy, akce JavaScriptu v dokumentu, anotace odkazů URI a Launch, webové odkazy, souhrny podpisů, stav XFA, dostupnost pomocných funkcí V8 a callbacky nepodporovaných funkcí. Stejné řádky souhrnu a nálezů lze uložit nebo zkopírovat jako TXT audit report nebo jako strukturovaný JSON. Jsou určené jako panely rizik hostitelské aplikace, ne jako detekce malwaru

Zpracování odkazů v prohlížeči

Bezpečné následování kliknutím s povolenými seznamy podle akce

// LinkOptions řídí automatické zpracování čtyř typů akcí PDF odkazu
// Výchozí hodnoty zapínají pouze goto a URI; launch (spustit program) a
// embedded GotoR (skok do jiného souboru) zůstávají VYPNUTÉ, aby náhodné kliknutí
// nemohlo spustit libovolný kód
PdfView1.LinkOptions :=
  [loAutoGoto, loAutoOpenURI];  // safe defaults

// OnAnnotationLinkClick se vyvolá pro cíle v dokumentu (skoky na stránku,
// pojmenované cíle, řetězce akcí). Nastavte Handled = True, aby prohlížeč
// odkaz automaticky nenásledoval
procedure TForm1.PdfView1AnnotationLinkClick(Sender: TObject;
  LinkIndex: Integer; const Action: TPdfAction;
  var Handled: Boolean);
begin
  if Action.Kind = paUri then
  begin
    if MessageDlg('Otevřít ' + Action.Uri + '?', mtConfirmation, [mbYes, mbNo], 0)
      <> mrYes then
      Handled := True;  // potlačí automatické zpracování
  end;
end;

// OnWebLinkClick se vyvolá pro řetězce URI zjištěné web-link skenerem PDFium
// (automaticky nalezené URL v textovém proudu, i bez anotace /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;

Viewer Form Widget Editing

Selection and Undo / Redo

// On the focused AcroForm widget, six new methods drive PDFium's form
// edit history. All six short-circuit safely if no widget is focused or
// dokument neobsahuje 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;

Locking Down the Viewer for Kiosks

Disable Page Navigation Gestures

// All three default to True so existing apps upgrade with no behaviour
// change. Flip them False for kiosks, preview panes, or read-only
// vložení bez nutnosti dědit z TPdfView
PdfView1.AllowUserPageChange       := False;  // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // wheel up/down jumps
PdfView1.AllowUserTextSelection    := False;  // mouse/keyboard text selection

// Ctrl+wheel is reserved for zoom and is skipped by the navigation
// handler regardless of ChangePageOnMouseScrolling.

Thumbnail Side Panel

Linking a TPdfThumbnailView to a TPdfView

// TPdfThumbnailView (unit FPdfThumbnail) renders one page thumbnail per
// řádek v posuvném bočním panelu
uses FPdfThumbnail;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ThumbView.Pdf              := Pdf1;
  ThumbView.ThumbnailWidth   := 120;
  ThumbView.ThumbnailHeight  := 160;
  ThumbView.SelectionColor   := $00CFA85F;  // warm highlight
  ThumbView.OnPageClick      := ThumbViewPageClick;
end;

procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
  PdfView1.PageNumber := PageIndex + 1; // PageIndex is zero-based
end;

procedure TForm1.PdfView1PageNumberZměněno (Changed)(Sender: TObject);
begin
  ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;

Cooperatively-Cancellable Batch Export

Multi-File PDF to JPG with Per-File and Per-Page Progress

// Pattern used by Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport,
// and Demo\Lazarus\BatchExport. One TPdf per file, IPdfCancellationToken
// pro zrušení z UI, TPdfStreamAdapter pro bufferované načtení souboru
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;

Viewer Coordinates and Hit Testing

Fit the Current Page and Inspect Text Under the Mouse

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;

Error Handling

Robust PDF Operations

function LoadPdfSafely(const FileName: string): Boolean;
begin
  Result := False;
  if not FileExists(FileName) then
    Exit;
  try
    Pdf1.FileName := FileName;
    Pdf1.Active := True;
    Result := Pdf1.Active;
  except
    Result := False;
  end;
end;

function ExtractTextSafely(PageNo: Integer): WString;
begin
  Result := '';
  if not Pdf1.Active then
    Exit;
  if (PageNo < 1) or (PageNo > Pdf1.PageCount) then
    Exit;
  Pdf1.PageNumber := PageNo;
  Result := Pdf1.Text;
end;

Tagged PDF and Accessibility

Detect Tagged PDF and Walk the Structure Tree

// IsTagged je levná kontrola katalogu - True, když existuje /StructTreeRoot
// StructureElements materialises the tree into a flat array of
// TPdfStructureElement records with type, title, alternate text, actual
// text, expansion text, language, level, parent index, and child / marked
// počty obsahu / atributů
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;

Print Mode Selection

Setting EMF / PostScript / Image-Mask Print Mode

// SetPdfPrintMode is a global function that maps to PDFium's
// FPDF_SetPrintMode. Use the matching TPdfPrintMode value for the
// používaný ovladač tiskárny
SetPdfPrintMode(pmEmf);                   // GDI EMF (default)
// SetPdfPrintMode(pmTextOnly);           // text-only 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

Reading Author-Intended Print Settings

// PrintCopies, PrintPageRanges, PrintScaling, and PrintPaperHandling are
// the viewer-preference dictionary entries — they encode what the
// ke kterému chtěl autor dokumentu, aby se PDF čtečky chovaly jako výchozí
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 ranges
  for Range in Ranges do
    Log.Add(Format('Pages %d..%d', [Range.First, Range.Last]));

  if SetPdfPrintPaperHandlingDevMode(PrinterDevMode, Pdf1.PrintPaperHandling) then
    ApplyPrinterDevMode(PrinterDevMode);
end;

Document JavaScript Actions

Enumerating Name-Tree JavaScript

// Document-level JavaScript is stored in the /Names /JavaScript name
// tree. JavaScriptAction[i] returns one entry; JavaScriptActions returns
// celé pole
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;

Performance Tips

Optimizing PDF Operations

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;

// Bezkopírová renderovací cesta (výchozí od v1.21.0): RenderPage zapisuje
// directly into the destination TBitmap's DIB buffer when callers pass a
// předem alokovanou bitmapu, čímž se vyhne jedné kopii o velikosti width * height * 4 bajtů
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;