PDFium Component Docs

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

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

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

Pdf1.FileName := 'C:\MyDocument.pdf';
Pdf1.Active := True;
ShowMessage('דפים: ' + IntToStr(Pdf1.PageCount));
ShowMessage('כותרת: ' + Pdf1.Title);
ShowMessage('מחבר: ' + Pdf1.Author);

var
  Stream: TMemoryStream;
begin
  Stream := TMemoryStream.Create;
  try
    Stream.LoadFromFile('C:\MyDocument.pdf');
    Pdf1.LoadDocument(Stream, True);
    Pdf1.Active := True;
    ShowMessage('ה-PDF נטען מזרם');
  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('טקסט במלבן: ' + RectText);
end;

חיפוש טקסט

var
  Position: Integer;
begin
  Position := Pdf1.FindFirst('Hello', [seCaseSensitive]);
  while Position >= 0 do
  begin
    ShowMessage('נמצא בתו: ' + IntToStr(Position));
    Position := Pdf1.FindNext;
  end;
end;

הוספת טקסט עם עיצוב

Pdf1.PageNumber := 1;
Pdf1.AddText('כותרת המסמך', 'Times New Roman', 16, 100, 750, clBlue);
Pdf1.AddText('זהו תוכן המסמך', '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('אובייקט ' + IntToStr(I));
      Memo1.Lines.Add('שם בסיס: ' + Pdf1.FontBaseName[I]);
      Memo1.Lines.Add('משפחה: ' + Pdf1.FontFamilyName[I]);
      Memo1.Lines.Add('מוטמע: ' + BoolToStr(Pdf1.FontIsEmbedded[I], True));
      Memo1.Lines.Add('משקל: ' + IntToStr(Pdf1.FontWeight[I]));
      Memo1.Lines.Add('זווית נטויה: ' + IntToStr(Pdf1.FontItalicAngle[I]));
      Memo1.Lines.Add('עלייה: ' + FloatToStr(Pdf1.FontAscent[I, MetricSize]));
      Memo1.Lines.Add('ירידה: ' + FloatToStr(Pdf1.FontDescent[I, MetricSize]));
      Memo1.Lines.Add('בתים של נתוני גופן: ' + IntToStr(Length(FontBytes)));
    end;

  if Pdf1.CharacterCount > 0 then
  begin
    Memo1.Lines.Add('גודל התו הראשון: ' + FloatToStr(Pdf1.CharacterFontSize[0]));
    Memo1.Lines.Add('משקל התו הראשון: ' + IntToStr(Pdf1.CharacterFontWeight[0]));
    Memo1.Lines.Add('זווית התו הראשון: ' + 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, מאפשרים למשתמשים לבחור metadata, טקסט עמוד, אובייקטי עמוד, images, attachments, links, annotations, bookmarks ומדדי גופן / תו, ואז מייצרים סיכום יחד עם דוח TXT או JSON מפורט. המעבדה כוללת גם בחירה של all / none categories, העתקת הדוח ללוח, ופעולות שמירת PDF המקור כדי שהמשתמשים יוכלו לשמור את ה-PDF לדוגמה שנוצר לצד דוח החילוץ שלו. הדגמות ExtractText, ExtractImages, Attachment ו-FontProperties הישנות נשארות דוגמאות ממוקדות; ExtractText תומכת באפשרויות טווח-עמודים ומפריד-עמודים כדי להפיק פלט טקסט לכל עמוד ב-Delphi, C++Builder ו-Lazarus, בעוד ContentExtractionLab היא דגימת המלאי החוצה-חתכים.

פעולות טפסים

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

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 each widget, then
// FlattenAllPages bakes all annotations and widgets on each page into
// fixed content that cannot be edited
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('הערה ' + 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);

שמירה ודחיסה

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

// יצירת מסמך PDF דחוס
Pdf1.CreateDocument;
Pdf1.Compressed := True; // לוודא שכל הזרמים דחוסים ב-FlateDecode (ברירת המחדל)
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;

// שמירה עם אפשרויות
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // שמירה מצטברת
Pdf1.SaveAs('C:\Noאבטחה.pdf', saRemoveSecurity); // הסרת אבטחה

// שמירה ליעד גרסת PDF מסוים. מאמת גרסת ה-PDF דוחה
// כל תכונה שהתווספה אחרי היעד שנבחר (JBIG2Decode, MarkInfo,
// AES encryption, Polygon / Caret / Watermark / Redact annotations, ...)
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
  ShowMessage('השמירה נדחתה: המסמך מכיל תכונות חדשות יותר מ-PDF 1.5');

יצירת פלט ארכיוני PDF/A-1b

// SaveAsPdfA מבצע עיבוד אחרי השמירה הבסיסית עם עדכון מצטבר שמחדיר
// זרם מטא-נתונים XMP, ‏ICC OutputIntent מסוג sRGB, וקטלוג מסמך מעודכן
// כך שהתוצאה תואמת ל-PDF/A-1b. פרופיל sRGB IEC61966-2.1 המצורף
// משמש אוטומטית כאשר לא מסופק פרופיל מותאם אישית

Pdf1.FileName := 'C:\Report.pdf';
Pdf1.Active := True;
if Pdf1.SaveAsPdfA('C:\Report.pdfa.pdf', pac1b) then
  ShowMessage('נשמר ארכיון PDF/A-1b');

// PDF/A עם פרופיל ICC מותאם אישית (לדוגמה צינורות הדפסה 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;

// פלט PDF/A בזיכרון
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;

  // בדיקת כותרת זולה בסגנון probe — קוראת את התאימות בלי להריץ
  // את המאמת המלא. תוצאה: pacNone, pac1a, pac1b, pac2b, pac3b, או
  // 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 := 'תאימות לא ידועה';
  end;

  // מאמת מלא עם אבחון לכל בעיה
  Validation := Pdf1.ValidatePdfA;
  Memo1.Lines.Add('תאימות: ' + GetEnumName(TypeInfo(TPdfAConformance),
    Ord(Validation.Conformance)));
  for Issue in Validation.Issues do
    Memo1.Lines.Add('- ' + Issue.Description);
end;

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

The Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab, and Demo\Lazarus\StandardsLab projects load or create a PDF, run ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR, and ValidatePdfVT, then show each detected conformance level and issue count in one grid. Their save buttons call SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR, and SaveAsPdfVT so the marker-output workflow is visible without writing a custom harness.

יצירת דוחות Preflight

יחידת FPdfPreflightReport עוטפת את ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR ו-ValidatePdfVT לאובייקט דוח אחד. הפרויקטים Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport ו-Demo\CBuilder\PreflightReport מאפשרים למשתמשים לבחור תקנים, ליצור סיכום, לבדוק מצב, עדיפות, קטגוריות בעיות, ספירות קטגוריות, פעולות הבאות, קודי בעיות ופעולות מומלצות, להחליף את התצוגה המקדימה בין text, Markdown, JSON ו-CSV, ולייצא דוחות TXT, HTML, Markdown, JSON או CSV. דוגמת ה-GUI של Delphi מוסיפה גם תצוגת Action Plan שמרכזת תקנים שנכשלו, עדיפויות, קודי בעיות והמלצות לרשימת תיקון. Demo\Delphi\PreflightReportCli חושפת את אותו נתיב דוח כזרימת קונסול עם ארגומנטים של קלט / פלט, סיסמה אופציונלית, בחירת תבנית פלט כולל plan / actionplan, סינון תקנים, הטמעת דוח בקובץ יחיד אופציונלי attach=output.pdf, קודי יציאה של CI דרך failon=, עיבוד batch=list.txt, סריקת תיקיות batchdir=folder עם רקורסיה אופציונלית ועיבוד יציב לפי נתיב, ניתוב דוחות באמצעות outdir=reports, שמות דוח בטוחים להתנגשות עבור שמות קלט כפולים, סיכומי TXT / JSON / HTML עם סיכומי סטטוס מצטברים ולפי תקן, manifests של שורות CSV עם הגדרות ריצה החוזרות לכל פריט, ומצב דוגמה ללא ארגומנטים עבור סקריפטים ומשימות CI. הדוח שנוצר מציין שהמאמתים המובנים מכסים בדיקות ברמת marker וברמת קובץ נבחר, כך שבדיקת preflight מלאה ברמת תוכן עדיין יכולה להיות מנותבת למנוע אימות ייעודי

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

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

// LoadCustomDocument lets PDFium pull blocks from any seekable TStream on
// לפי דרישה. הזרם אינו מועתק לזיכרון, כך ש-PDFs בני כמה GB, זרמי
// body של HTTP ומקורות מבוססי מסד נתונים כולם אפשריים בלי
// חיץ מקדים
var
  FileStream: TFileStream;
begin
  // סטרימינג מקובץ ללא עותק בזיכרון
  FileStream := TFileStream.Create('C:\Huge.pdf',
    fmOpenRead or fmShareDenyWrite);
  Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
  // ^^ הבעלות הועברה — Pdf1 משחרר את FileStream ב-UnloadDocument
  Pdf1.Active := True;
  ShowMessage('דפים: ' + 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;

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

אסימוני ביטול שיתופיים

// 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
  // קורא אותו דרך הקריאה החוזרת IFSDK_PAUSE של PDFium
  if CancelToken.IsCancelled then
    Memo1.Lines.Add('בוטל על ידי המשתמש');
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('הרינדור בוטל — תוצאה חלקית זמינה');
      prsFailed:    Memo1.Lines.Add('PDFium reported FPDF_RENDER_FAILED');
    end;
  finally
    Bitmap.Free;
  end;
end;

הרצת רינדורים בשרשור רקע עם 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('בוטל');
        pfsFailed:    Log.Add('העובד נכשל: ' + 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
// PDF המקור לתוך מסמך זה. InsertAt = 0 מוסיף לפני דף 1;
// PageCount מצרף בסוף
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
// אינדקס היעד של הדף שהוזז ראשון לאחר השלמת ההזזה
// דפים אחרים זזים סביב הבלוק שהוזז כך שאורך המסמך נשאר
// the same.
//
// דוגמה: 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
// כל דף של TPdf אחר. הטביעו את הידית הזו על כמה שיותר דפים של
// מסמך זה שתרצו — כל חותמת היא אובייקט דף יחיד של PDFium
// שאפשר למקם, להגדיל ולהקטין ולסובב דרך ה-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;

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

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

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

// AddImage(FileName) accepts any format registered with the VCL or LCL
// יחידות גרפיקה (BMP, PNG דרך 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;

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

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

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

var
  MatchCount: Integer;
begin
  // צבעו מסכת clYellow מעל כל התאמה בדף הנוכחי של הצופה
  // מעבר לדף אחר מנקה את ההדגשה באופן אוטומטי
  PdfView1.HighlightColor := clYellow;
  MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
  Status.Caption := Format('%d התאמות בדף הזה', [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

The Demo\Delphi\ViewerInteractionLab, Demo\CBuilder\ViewerInteractionLab, and Demo\Lazarus\ViewerInteractionLab projects keep the modern TPdfView interaction surface in one compact form: open or create a sample PDF, switch DisplayMode, apply FitMode, change PageColor, rotate pages, highlight current-page search hits with HighlightSearchText, enable or disable user text selection, inspect SelectedText, call SelectAll, CopySelectionToClipboard, and ClearSelection, and watch DeviceToPage קואורדינטות מתעדכנות בשורת המצב בזמן שהעכבר נע מעל הדף

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

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

קריאת העדפות הדפסה של 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

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

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

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

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

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

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

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

// LinkOptions שולט בטיפול האוטומטי בארבעת סוגי פעולות הקישור של PDF
// הסוגים. ברירות המחדל מפעילות goto ו-URI בלבד; launch (הרצת תוכנית) ו-
// GotoR מוטמע (קפיצה לקובץ אחר) נשארים כבויים כך שלחיצה מקרית לא תוכל
// להפעיל קוד שרירותי
PdfView1.LinkOptions :=
  [loAutoGoto, loAutoOpenURI];  // safe defaults

// OnAnnotationLinkClick מופעל עבור יעדים בתוך המסמך (קפיצות דף,
// יעדים בעלי שם, שרשראות פעולות). סמנו Handled = True כדי למנוע מה
// מציג לעקוב אחר הקישור אוטומטית
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 מופעל עבור מחרוזות URI שזוהו על ידי סורק ה-web-link של PDFium
// סורק (כתובות URL שזוהו אוטומטית בזרם הטקסט, גם בלי
// /A <</S/URI...>> annotation).
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
  WebLinkIndex: Integer; const Url: WString;
  var Handled: Boolean);
begin
  Log.Add('קישור אינטרנט נלחץ: ' + string(Url));
end;

עריכת ווידג'טי טופס במציג

בחירה וביטול / ביצוע מחדש

// On the focused AcroForm widget, six new methods drive PDFium's form
// היסטוריית העריכה. כל ששתם יוצאים מוקדם בבטחה אם אין widget ממוקד או
// שלמסמך אין AcroForm
procedure TForm1.btnSelectAllClick(Sender: TObject);
begin
  PdfView1.SelectAllFormText;
  EditMenuCopy.Caption := 'העתק: ' + 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;

נעילת המציע עבור קיוסקים

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

// All three default to True so existing apps upgrade with no behaviour
// שינוי. הפכו אותם ל-False עבור קיוסקים, חלוניות תצוגה מקדימה או
// הטמעות לקריאה בלבד בלי ליצור תת-מחלקה של TPdfView
PdfView1.AllowUserPageChange       := False;  // PgUp/PgDn/Ctrl+דף הבית/End
PdfView1.ChangePageOnMouseScrolling := False; // wheel up/down jumps
PdfView1.AllowUserTextSelection    := False;  // mouse/keyboard text selection

// Ctrl+wheel שמור לזום ונעקף על ידי
// מטפל הניווט ללא תלות ב-ChangePageOnMouseScrolling.

חלונית תמונות ממוזערות

קישור TPdfThumbnailView אל TPdfView

// TPdfThumbnailView (unit FPdfThumbnail) renders one page thumbnail per
// שורה בחלונית צד נגללת
uses FPdfThumbnail;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ThumbView.Pdf              := Pdf1;
  ThumbView.ThumbnailWidth   := 120;
  ThumbView.ThumbnailHeight  := 160;
  ThumbView.SelectionColor   := $00CFA85F;  // הדגשה חמימה
  ThumbView.OnPageClick      := ThumbViewPageClick;
end;

procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
  PdfView1.PageNumber := PageIndex + 1; // PageIndex הוא מבוסס אפס
end;

procedure TForm1.PdfView1PageNumberשונה(Sender: TObject);
begin
  ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;

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

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

// Pattern used by Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport,
// ו-Demo\Lazarus\BatchExport. ‏TPdf אחד לכל קובץ, ‏IPdfCancellationToken
// לביטול מה-UI, ‏TPdfStreamAdapter לטעינת קובץ עם חיץ
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('דף %d, X %.2f, Y %.2f, תו %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 מממש את העץ למערך שטוח של
// רשומות TPdfStructureElement עם type, title, alternate text, actual
// text, expansion text, language, level, parent index, ו-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. השתמשו בערך TPdfPrintMode המתאים עבור
// מנהל ההתקן של המדפסת שבשימוש
SetPdfPrintMode(pmEmf);                   // GDI EMF (default)
// SetPdfPrintMode(pmTextOnly);           // רינדור טקסט בלבד
// 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 ו-PrintPaperHandling הם
// רשומות viewer-preference dictionary — הן מקודדות מה
// שהמחבר רצה שקוראי PDF יבחרו כברירת מחדל
var
  Ranges: TPrintPageRanges;
  Range: TPrintPageRange;
begin
  ShowMessage('Suggested copies: ' + IntToStr(Pdf1.PrintCopies));
  ShowMessage('Print scaling: ' + GetEnumName(TypeInfo(TPrintScaling),
    Ord(Pdf1.PrintScaling)));

  Ranges := Pdf1.PrintPageRanges; // טווחים מבוססי אחת
  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 של Name-Tree

// Document-level JavaScript is stored in the /Names /JavaScript name
// tree. ‏JavaScriptAction[i] returns one record; JavaScriptActions returns
// את כל המערך
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;

// Copy-free render path (default since v1.21.0): RenderPage writes
// directly into the target TBitmap DIB buffer when callers pass
// a preallocated bitmap, avoiding one copy of width * height * 4 bytes
procedure RenderIntoCallerBitmap(Bitmap: TBitmap);
begin
  Bitmap.PixelFormat := pf32bit;
  Bitmap.SetSize(2480, 3508); // A4 @ 300 DPI
  Pdf1.RenderPage(Bitmap, 0, 0, Bitmap.Width, Bitmap.Height);
end;