PDFium Delphi Component Docs

דוגמאות תכנות PDFium VCL

פעולות מסמך בסיסיות

טעינה והצגה של מסמך PDF

Pdf1.FileName := 'C:\MyDocument.pdf';
Pdf1.Active := True;
ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
ShowMessage('Title: ' + Pdf1.Title);
ShowMessage('Author: ' + Pdf1.Author);

var
  Stream: TMemoryStream;
begin
  Stream := TMemoryStream.Create;
  try
    Stream.LoadFromFile('C:\MyDocument.pdf');
    Pdf1.LoadDocument(Stream, True);
    Pdf1.Active := True;
    ShowMessage('PDF loaded from stream');
  finally
    Stream.Free;
  end;
end;

יצירת מסמך PDF חדש

Pdf1.CreateDocument;
Pdf1.Active := True;
Pdf1.AddPage(0, 612, 792);
Pdf1.PageNumber := 1;
Pdf1.AddText('Hello World!', 'Arial', 12, 100, 700, clBlack, $FF, 0.0);
Pdf1.UpdatePage;
Pdf1.SaveAs('C:\NewDocument.pdf');
Pdf1.Active := False;

פעולות טקסט

חילוץ טקסט מ-PDF

var
  PageText: WString;
  RectText: WString;
begin
  Pdf1.PageNumber := 1;
  PageText := Pdf1.Text;
  Memo1.Text := PageText;
  RectText := Pdf1.TextInRectangle(100, 100, 500, 200);
  ShowMessage('Text in rectangle: ' + RectText);
end;

חיפוש טקסט

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;

הפרויקטים Demo\Delphi\FontProperties, Demo\CBuilder\FontProperties ו-Demo\Lazarus\FontProperties מציגים את אותם מטא-נתונים דרך TPdf וגם דרך TPdfView

בניית דוח חילוץ תוכן רב-קטגורי

הפרויקטים Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab ו-Demo\Lazarus\ContentExtractionLab הופכים את חילוץ התוכן לזרימת עבודה אחת. הם פותחים או יוצרים PDF, מאפשרים למשתמשים לבחור מטא-נתונים, טקסט עמוד, אובייקטי עמוד, תמונות, קבצים מצורפים, קישורים, הערות, סימניות ומדדי גופן / תו, ואז מייצרים סיכום ודוח מפורט ב-TXT או ב-JSON. המעבדה כוללת גם בחירת קטגוריות הכול / כלום, העתקת דוח ללוח העתקה ופעולות שמירה של ה-PDF המקורי, כך שהמשתמשים יכולים להשאיר את ה-PDF לדוגמה שנוצר לצד דוח החילוץ שלו. הדמויות הישנות יותר ExtractText, ExtractImages, Attachment ו-FontProperties נשארות דוגמאות ממוקדות; ExtractText תומך באפשרויות טווח עמודים ומפריד עמודים כדי להפיק פלט טקסט לכל עמוד ב-Delphi, C++Builder ו-Lazarus, ואילו ContentExtractionLab הוא דוגמת המיפוי החוצת-התחומים

זיהוי וחילוץ של טבלאות

הפרויקטים Demo\Delphi\TableExtractionLab, Demo\CBuilder\TableExtractionLab ו-Demo\Lazarus\TableExtractionLab הופכים זיהוי טבלאות לזרימת עבודה אחת. המעבדה בונה מסמך לדוגמה המכיל טבלה עם קווים ותאי כותרת ממוזגים, טבלה המיושרת ברווחים ללא קווים, וטבלה ארוכה שנחתכת בגבול עמוד, ואז מריצה את ExtractTables עבור העמוד הנוכחי או את ExtractDocumentTables עבור כל המסמך ומדווחת שורות, עמודות, ערכי RowSpan ו-ColumnSpan, דגלי כותרת, מצב זיהוי, מדד ביטחון וקבוצות המשך. האפשרויות DetectRuledTables, DetectWhitespaceTables, DetectContinuations, DetectFilledRulings ו-MinConfidence ניתנות לעריכה בממשק המשתמש, והטבלה הנבחרת מיוצאת דרך TPdfTable.ToCsv ו-TPdfTable.ToJson. המעבדה מריצה גם אצווה על תיקה שלמה: Batch folder מריץ ExtractDocumentTables על כל PDF, מדווח מספרי טבלאות למסמך, מסמן סריקות ללא שכבת טקסט ומנסה שוב מסמכים ריקים עם תבנית עמודות צפופה; Export all CSV כותב כל טבלה שזוהתה לצד קובצי המקור. כל מסמך נפתח במופע TPdf משלו, כך שקובץ אחד שאינו קריא אינו יכול להשפיע על שאר האצווה

פעולות טפסים

עבודה עם שדות טופס

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;

שיטוח ערכי טופס שמולאו אל העמוד

// Without flattening, FormField[i] := value writes only the /V entry —
// the visible appearance comes from the /AP stream and may not refresh
// GenerateFormAppearances regenerates /AP for every widget, then
// FlattenAllPages bakes every page's annotations and widgets into
// permanent, non-editable content
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);

שמירה ודחיסה

שמירה עם דחיסה

// Create a compressed PDF document
Pdf1.CreateDocument;
Pdf1.Compressed := True; // ensure all streams are FlateDecode compressed (default)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // A4 size
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;

// Save with options
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // incremental save
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // remove security

// Save to a specific PDF version target. The PDF version validator rejects
// any feature introduced after the chosen target (JBIG2Decode, MarkInfo,
// AES encryption, Polygon / Caret / Watermark / Redact annotations, ...)
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
  ShowMessage('Save rejected: document contains features newer than PDF 1.5');

יצירת פלט ארכיון 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
// is supplied
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 with a custom ICC profile (CMYK printing pipelines for example)
var
  Opts: TPdfASaveOptions;
begin
  Opts := Default(TPdfASaveOptions);
  Opts.Conformance    := pac1b;
  Opts.IccProfileData := TFile.ReadAllBytes('C:\Profiles\Coated_GRACoL_2006.icc');
  Pdf1.SaveAsPdfA('C:\Report.cmyk.pdfa.pdf', Opts);
end;

// In-memory PDF/A output
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;

אימות תאימות PDF/A

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

  // Cheap header-style probe — reads the conformance without iterating
  // the full validator. Result: pacNone, pac1a, pac1b, pac2b, pac3b, or
  // 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;

  // Full validator with per-issue diagnostics
  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;

בחינת תקני PDF מרובים ב-StandardsLab

הפרויקטים Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab ו-Demo\Lazarus\StandardsLab טוענים או יוצרים PDF, מריצים את ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR ו-ValidatePdfVT, ומציגים אז את כל רמת התאימות שזוהתה ואת מספר הבעיות בטבלה אחת. כפתורי השמירה שלהם קוראים ל-SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR ו-SaveAsPdfVT, כך שזרימת העבודה של פלט סמנים נראית לעין בלי לכתוב מתקן מותאם

יצירת דוחות preflight

יחידת FPdfPreflightReport עוטפת את ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR ו-ValidatePdfVT לאובייקט דוח אחד שניתן לתשאול, עם חומרה של Info / Warning / Error, צבירה של מצב ובעיות, חיפוש, סינון וטביעות אצבע יציבות של SHA-256. דוחות יכולים להיפלט כטקסט, HTML, Markdown, JSON, CSV, SARIF 2.1.0, JUnit XML, NDJSON או XML מקורי. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport ו-Demo\CBuilder\PreflightReport מספקים זרימות עבודה אינטראקטיביות של בחירה ותצוגה מקדימה, ואילו Demo\Delphi\PreflightReportCli מוסיף ארגומנטי קלט / פלט לסקריפטים, סינון סטנדרטי, הטמעת קובץ מצורף של דוח אופציונלית, שערי קוד יציאה של CI, עיבוד רשימות אצווה ותיקיות, ניתוב בטוח מפני התנגשויות, סיכומים מצטברים ומניפסטים של CSV. הדוח שנוצר מצהיר שהמאמתים המובנים מכסים בדיקות ברמת סמנים ובדיקות נבחרות ברמת קובץ, כך ש-preflight מלא ברמת התוכן עדיין יכול להינתן למנוע אימות ייעודי

טעינה מזרימה ומסמכים גדולים

טעינת PDF לפי דרישה עם LoadCustomDocument

// LoadCustomDocument lets PDFium pull blocks from any seekable TStream on
// demand. The stream is NOT copied into memory, so multi-GB PDFs, remote
// HTTP body streams, and database-backed sources are all viable without
// an up-front buffer
var
  FileStream: TFileStream;
begin
  // Read-from-file streaming (no in-memory copy)
  FileStream := TFileStream.Create('C:\Huge.pdf',
    fmOpenRead or fmShareDenyWrite);
  Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
  // ^^ ownership transferred — Pdf1 frees FileStream on UnloadDocument
  Pdf1.Active := True;
  ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;

// Externally-owned stream (caller frees it after 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;

ביטול אסינכרוני ורינדור מתקדם

Tokens ביטול שיתופיים

// FPdfAsync exposes IPdfCancellationToken /
// IPdfCancellationTokenSource for cancel-from-UI semantics that work
// across the long-running export and render paths
uses FPdfAsync;

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

  // user clicks Cancel
  CancelSource.Cancel;

  // The worker periodically checks CancelToken.IsCancelled, or the render
  // path polls it through PDFium's IFSDK_PAUSE callback
  if CancelToken.IsCancelled then
    Memo1.Lines.Add('Cancelled by user');
end;

רינדור עמוד מתקדם ניתן לביטול

// RenderPageProgressive lets a long high-DPI render abort mid-page when
// the user clicks Cancel, instead of blocking the caller until done
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;

הרצת רינדורים ב-thread רקע עם TPdfFuture

// TPdfFuture<T> spins up a worker on a background thread and posts a
// TPdfFutureResult<T> envelope back to the main thread
//
// Important: TPdf serializes render calls per instance, but the worker still
// owns dispatch and document lifetime. Do not mutate a TPdf while a render is
// in flight; batch export usually keeps one TPdf per file
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;

מטריצות טרנספורמציה

הרכבת Translate / Scale / Rotate / Skew

// FPdfMatrix wraps PDFium's FS_MATRIX so callers can build a transform
// declaratively and hand it to FPDFPageObj_SetMatrix
uses FPdfMatrix, FPdfView;

var
  M: TPdfMatrix;
  RawMatrix: FS_MATRIX;
  PageObj: FPDF_PAGEOBJECT;
begin
  M := TPdfMatrix.Create;
  try
    M.Translate(72, 200);       // move 1in right, ~2.8in up
    M.Scale(0.5, 0.5);          // half-size stamp
    M.Rotate(15);               // 15-degree CCW rotation
    // M.HorizontalFlip;
    // M.VerticalFlip;
    // M.CentralFlip;
    // M.Skew(10, 5);
    // M.Multiply(Other);       // post-multiply with another matrix

    // Apply to any PDFium page object (text / path / image / form XObject)
    RawMatrix := M.Handle;
    FPDFPageObj_SetMatrix(PageObj, RawMatrix);
  finally
    M.Free;
  end;
end;

הרכבת עמודים

ייבוא תת-קבוצת עמודים לפי אינדקס

// ImportPagesByIndex copies an explicit zero-based index array from a
// source PDF into this document. InsertAt = 0 inserts before page 1;
// PageCount appends
var
  Source: TPdf;
begin
  Source := TPdf.Create(nil);
  try
    Source.FileName := 'C:\Report.pdf';
    Source.Active   := True;

    // Import pages 0, 2, 4 (one-based: pages 1, 3, 5) from Source
    // and append them at the end of Pdf1
    Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);

    // Empty array imports every source page
    // Pdf1.ImportPagesByIndex(Source, [], 0);
  finally
    Source.Free;
  end;
end;

פלט מורכב N-up

// ImportNPagesToOne returns a brand-new TPdf whose pages are NumX*NumY
// composites of THIS document. OutputWidth/Height are PDF user units
// (1 unit = 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;

סידור עמודים מחדש במקום

// MovePages takes the zero-based indices of the pages to move plus the
// destination index for the first moved page after the move completes
// Other pages shift around the moved block so the document length stays
// the same
//
// Example: in a 5-page document with pages numbered [0,1,2,3,4],
// MovePages([2, 3], 0) reorders to [2, 3, 0, 1, 4]
if not Pdf1.MovePages([2, 3], 0) then
  ShowMessage('Invalid indices passed to MovePages');

סימני מים באמצעות שימוש חוזר בעמודים כ-Form XObject

// CreateXObjectFromPage manufactures a reusable Form XObject handle from
// any page of any other TPdf. Stamp that handle onto as many pages of
// THIS document as you want — each stamp is a single PDFium page object
// that can be positioned, scaled, and rotated through the 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;

    // Wrap page 0 of WatermarkSource as a reusable 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;

        // Centre the watermark on the page
        M := TPdfMatrix.Create;
        try
          M.Scale(0.5, 0.5);
          M.Translate(0.25 * Pdf1.PageWidth, 0.5 * Pdf1.PageHeight);
          RawMatrix := M.Handle;
          FPDFPageObj_SetMatrix(PageObj, RawMatrix);
        finally
          M.Free;
        end;

        Pdf1.UpdatePage;
      end;
    finally
      XObj.Free; // closes the FPDF_XOBJECT handle
    end;

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

הפרויקטים Demo\Delphi\WatermarkStamp, Demo\Lazarus\WatermarkStamp ו-Demo\CBuilder\WatermarkStamp הופכים את התבנית הזו לזרימת עבודה שניתן להריץ: הם יוצרים PDF יעד ו-PDF חותמת לשימוש חוזר, עוטפים את עמוד החותמת בעזרת CreateXObjectFromPage, מכניסים את אותו Form XObject לכל עמוד יעד, מחילים טרנספורמציות של TPdfMatrix עבור סימני מים ממורכזים וחותמות בפינה העליונה-ימנית, ומוסיפים תוויות מספר עמוד אופציונליות לפני שמירת התוצאה

הוספת תמונות (overloads ישירים של AddImage)

הוספת קובץ תמונה רשום או TBitmap

// AddImage(FileName) accepts any format registered with the VCL or LCL
// graphics units (BMP, PNG via PngImage, JPG, ...)
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);

// AddImage(TBitmap) skips the TPicture intermediary, ideal when the
// bitmap already comes from rendering or batch generation work
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;

הפרויקטים Demo\Delphi\ImageToPDF, Demo\CBuilder\ImageToPDF ו-Demo\Lazarus\ImageToPDF הופכים זאת לזרימת עבודה מלאה של אצווה: בחירת מספר קובצי תמונה, תצוגה מקדימה של הפריט הנבחר, יצירת עמוד PDF אחד לכל תמונה, התאמת כל תמונה לעמוד A4 במאונך או במאוזן, שמירת התוצאה ופתיחת ה-PDF שנוצר

הדגשת תוצאות חיפוש במציג

הדגשת כל התאמה בעמוד הנוכחי

var
  MatchCount: Integer;
begin
  // Paint a clYellow mask over every match on the view's current page
  // Switching to a different page clears the highlight automatically
  PdfView1.HighlightColor := clYellow;
  MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
  Status.Caption := Format('%d match(es) on this page', [MatchCount]);

  // Programmatically remove the overlay before the page changes
  PdfView1.ClearHighlight;
end;

פריסת המציג וצבע עמוד

מצב התאמה ורקע עמוד

// FitMode keeps long documents framed during Resize or page changes
// Setting Zoom directly cancels FitMode (it reverts to pfmNone)
PdfView1.FitMode := pfmFitPage;       // whole page in viewport
// PdfView1.FitMode := pfmFitWidth;   // page width matches viewport width
// PdfView1.FitMode := pfmActualSize; // 100%

// PageColor decouples the rendered PDF page from the host control's Color
// — typical dark-mode viewer setup keeps the scroll area dark while the
// PDF page stays paper-white
PdfView1.Color     := clBlack;        // scroll area
PdfView1.PageColor := clWhite;        // PDF page background

// Optional drop shadow and page border to lift the page off a dark bg
PdfView1.PageShadowSize  := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;

חקר אינטראקציית המציג ב-ViewerInteractionLab

הפרויקטים Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab ו-Demo\Lazarus\ViewerInteractionLab מרכזים את משטח האינטראקציה המודרני של TPdfView בטופס קומפקטי אחד: פתיחה או יצירה של PDF לדוגמה, החלפת DisplayMode, החלת FitMode, שינוי PageColor, סיבוב עמודים, הדגשת תוצאות חיפוש בעמוד הנוכחי עם HighlightSearchText, הפעלה או כיבוי של בחירת טקסט על ידי המשתמש, בחינת SelectedText, קריאה ל-SelectAll, CopySelectionToClipboard ו-ClearSelection, ומעקב אחר עדכון הקואורדינטות של DeviceToPage בסרגל המצב כאשר העכבר נע מעל העמוד

חיפוש ובחירת טקסט ב-SearchAndSelect

הפרויקטים Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect ו-Demo\Lazarus\SearchAndSelect הופכים את זרימת העבודה של חיפוש ובחירה במציג לדוגמאות עומדות בפני עצמן. הם פותחים או יוצרים PDF, מפרטים את כל תוצאות החיפוש עם מספר עמוד, אינדקס תו וטקסט תצוגה מקדימה, תומכים בניווט קודם / הבא ובקפיצה לתוצאה בלחיצה כפולה, מדגישים התאמות בעמוד הנוכחי עם HighlightSearchText, חושפים אפשרויות תלוי-רישיות ומילה שלמה, מחליפים את AllowUserTextSelection, מציגים תצוגה מקדימה של SelectedText, מניעים את SelectAll, CopySelectionToClipboard ו-ClearSelection מכפתורים ומאפשרים למשתמשים לעבור בין מצבי תצוגה בודד ובפריסה

קריאת העדפות הדפסה של PDF ב-PrintPreferences

הפרויקטים Demo\Delphi\PrintPreferences, Demo\CBuilder\PrintPreferences ו-Demo\Lazarus\PrintPreferences קוראים את העדפות ההדפסה של המחבר לפני ההדפסה. הם מציגים את PrintCopies, PrintPageRanges, PrintScaling ו-PrintPaperHandling, יכולים להעתיק עותקים וטווחים שסיפק המחבר אל קלטי ההדפסה, ומציגים כיצד SetPdfPrintPaperHandlingDevMode ממפה כוונת duplex של PDF אל DEVMODE של Windows

הדפסת PDF סטנדרטית ב-PrintPDF

הפרויקטים Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF ו-Demo\Lazarus\PrintPDF מציגים את זרימת העבודה הסטנדרטית להדפסה: פתיחת PDF, תצוגה מקדימה של עמודים, בחירת טווחי עמודים ועותקים בדיאלוג ההדפסה של הפלטפורמה, כיבוד מצב collate, רינדור כל עמוד דרך TPdf.RenderPage וחשיפת התקדמות הדפסה עם אפשרות ביטול. הדוגמה של Delphi מקבלת גם קובצי PDF שנגררו אליה לטעינה מהירה

תצוגה מהירה מרובת עמודים ב-MultiPageViewer

הפרויקטים Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer ו-Demo\Lazarus\MultiPageViewer מתרגלים תצוגה רציפה, בפריסה ומודעת-שער עם זרימות ניווט וזום רגילות בעמודים. הם שימושיים גם לבדיקת מהירות הפתיחה לעמוד הראשון ושל זריזות הרינדור ב-PDF עצומים: בבדיקות קבצים גדולים, מסמכים של 2.x GB שגורמים ל-Acrobat או ל-Foxit להשקיע 20-30 שניות לפני התצוגה הראשונה יכולים להפוך לשמישים כמעט מיד דרך נתיב המציג של PDFium Delphi Component

סקירה זה לצד זה ב-SplitView

הפרויקטים Demo\Delphi\SplitView, Demo\CBuilder\SplitView ו-Demo\Lazarus\SplitView מציגים סקירת PDF זה לצד זה. הדוגמאות של C++Builder ו-Lazarus מתמקדות בהשוואה בשתי או שלוש חלוניות, בחירת תצוגה פעילה, פקודות ניווט מסונכרנות אופציונליות, הגדרות זום משותפות וסיבוב עבור התצוגה הנבחרת או לכל התצוגות הטעונות. השתמשו בדוגמאות אלה כדי להשוות מסמכים גדולים תוך שמירה על תצוגה ראשונה שמישה ועל רינדור עמודים זריז אחריה; אותם קובצי PDF של 2.x GB שעשויים לקחת ל-Acrobat או ל-Foxit 20-30 שניות לפתיחה יכולים להפוך לשמישים כמעט מיד בנתיב המציג של PDFium Delphi Component

ביקורת משטח סיכון PDF ב-SecurityAudit

הפרויקטים Demo\Delphi\SecurityAudit, Demo\CBuilder\SecurityAudit ו-Demo\Lazarus\SecurityAudit פותחים PDF לקריאה בלבד ומפרטים מצב הרשאות, קבצים מצורפים מוטמעים, פעולות JavaScript של המסמך, הערות קישור URI ו-Launch, קישורי אינטרנט, סיכומי חתימות, מצב XFA, זמינות עזרי V8 ו-callbacks של תכונות שאינן נתמכות. את אותן שורות סיכום וממצאים אפשר לשמור או להעתיק כדוח ביקורת TXT או לשמור כ-JSON מובנה. הן מיועדות כלוחות סיכונים ליישום מארח, ולא לזיהוי תוכנות זדוניות

טיפול בקישורים במציג

מעקב בטוח אחר לחיצות עם רשימות היתר לכל פעולה

// LinkOptions controls automatic handling of the four PDF link action
// types. Defaults enable goto and URI only; launch (run a program) and
// embedded GotoR (jump to another file) stay OFF so a stray click cannot
// run arbitrary code
PdfView1.LinkOptions :=
  [loAutoGoto, loAutoOpenURI];  // safe defaults

// OnAnnotationLinkClick fires for in-document destinations (page jumps,
// named destinations, action chains). Mark Handled = True to prevent the
// view from auto-following the link
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;  // suppress auto-handling
  end;
end;

// OnWebLinkClick fires for URI strings discovered by PDFium's web-link
// scanner (auto-detected URLs in the text stream, even without an
// /A <</S/URI...>> annotation)
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
  WebLinkIndex: Integer; const Url: WString;
  var Handled: Boolean);
begin
  Log.Add('Web link clicked: ' + string(Url));
end;

עריכת widgets של טפסים במציג

בחירה ו-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
// the document carries no 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;

נעילת המציג עבור kiosks

השבתת מחוות ניווט בעמודים

// All three default to True so existing apps upgrade with no behaviour
// change. Flip them False for kiosks, preview panes, or read-only
// embeddings without subclassing 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.

חלונית צד של תמונות ממוזערות

קישור TPdfThumbnailView ל-TPdfView

// TPdfThumbnailView (unit FPdfThumbnail) renders one page thumbnail per
// row in a scrollable side panel
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.PdfView1PageNumberChanged(Sender: TObject);
begin
  ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;

ייצוא אצווה ניתן לביטול שיתופי

המרת קבצי PDF מרובים ל-JPG עם התקדמות לכל קובץ ולכל עמוד

// Pattern used by Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport,
// and Demo\Lazarus\BatchExport. One TPdf per file, IPdfCancellationToken
// for cancel-from-UI, TPdfStreamAdapter for buffered 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;

טיפול בשגיאות

פעולות PDF עמידות

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 ונגישות

זיהוי Tagged PDF ומעבר על עץ המבנה

// IsTagged is a cheap catalog probe — True when /StructTreeRoot exists
// 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
// 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;

בחירת מצב הדפסה

קביעת מצב הדפסה של EMF / PostScript / Image-Mask

// SetPdfPrintMode is a global function that maps to PDFium's
// FPDF_SetPrintMode. Use the matching TPdfPrintMode value for the
// printer driver in use
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

קריאת הגדרות הדפסה שהמחבר התכוון אליהן

// PrintCopies, PrintPageRanges, PrintScaling, and PrintPaperHandling are
// the viewer-preference dictionary entries — they encode what the
// document author wanted PDF readers to default to
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;

פעולות JavaScript של המסמך

מניית JavaScript של עץ השמות

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

טיפים לביצועים

אופטימיזציה של פעולות PDF

procedure ProcessMultiplePages;
var
  I: Integer;
  PageText: WString;
begin
  for I := 1 to Pdf1.PageCount do
  begin
    Pdf1.PageNumber := I;
    PageText := Pdf1.Text;
    ProcessPageText(PageText);
  end;
end;

procedure SetOptimalRenderOptions;
begin
  PdfView1.Options := [reAnnotations, reLcd];
end;

// Zero-copy render path (default since v1.21.0): RenderPage writes
// directly into the destination TBitmap's DIB buffer when callers pass a
// pre-allocated bitmap, avoiding one width * height * 4 bytes copy
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;