PDFium 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 هو نموذج الجرد الشامل متعدد الفئات.

عمليات النماذج

العمل مع حقول النماذج

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;

تسطيح قيم النماذج المعبأة إلى الصفحة

// بدون التسطيح، FormField[i] := value يكتب فقط إدخال /V —
// المظهر المرئي يأتي من stream /AP وقد لا يُحدَّث.
// GenerateFormAppearances يعيد توليد /AP لكل widget، ثم
// FlattenAllPages يثبت التعليقات التوضيحية والـ widgets على كل صفحة كمحتوى
// دائم غير قابل للتحرير.
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;

التنقل والعلامات المرجعية

العمل مع العلامات المرجعية (Bookmarks)

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; // التأكد من أن جميع streams مضغوطة بـ FlateDecode (افتراضي)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // حجم A4
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:\NoSecurity.pdf', saRemoveSecurity); // إزالة الأمان

// الحفظ بهدف إصدار PDF محدد. مدقق إصدار PDF يرفض
// أي ميزة تم تقديمها بعد الإصدار الهدف المختار (JBIG2Decode، MarkInfo،
// تشفير AES، تعليقات Polygon / Caret / Watermark / Redact، ...).
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
  ShowMessage('Save rejected: document contains features newer than PDF 1.5');

إنشاء إخراج أرشيفي PDF/A-1b

// SaveAsPdfA يعالج الحفظ الأساسي بعد ذلك بتحديث تدريجي
// يحقن مجرى بيانات XMP وصفية وOutputIntent ICC من نوع sRGB وCatalog
// مستند مُحدَّث بحيث تكون النتيجة متوافقة مع 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('Saved PDF/A-1b archive');

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

  // فحص خفيف بأسلوب الترويسة — يقرأ التوافق دون تكرار
  // المدقق الكامل. النتيجة: 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 := 'Unknown conformance';
  end;

  // المدقق الكامل مع تشخيص لكل مشكلة
  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 بحيث يكون سير عمل إخراج العلامات مرئيًا دون كتابة harness مخصص.

توليد تقارير Preflight

تلف الوحدة FPdfPreflightReport ValidatePdfA وValidatePdfUa وValidatePdfE وValidatePdfX وValidatePdfR وValidatePdfVT في كائن تقرير واحد. تتيح Demo\Delphi\PreflightReport وDemo\Lazarus\PreflightReport وDemo\CBuilder\PreflightReport للمستخدمين اختيار المعايير، وتوليد ملخص، وفحص الحالة والأولوية وفئات المشكلات وعدد الفئات والإجراءات التالية ورموز المشكلات والإجراءات الموصى بها، والتبديل بين معاينة النص و Markdown و JSON و CSV، وتصدير تقارير TXT أو HTML أو Markdown أو JSON أو CSV. ويضيف مثال واجهة Delphi GUI أيضًا عرض Action Plan الذي يلخّص المعايير غير المجتازة والأولويات وأكواد المشكلات والتوصيات في قائمة مرجعية للإصلاح تعرّض Demo\Delphi\PreflightReportCli مسار التقرير نفسه كسير عمل وحدة تحكم مع وسائط الإدخال / الإخراج، وكلمة مرور اختيارية، واختيار تنسيق الإخراج، بما في ذلك plan / actionplan، وتصفية المعايير، وتضمين تقرير لملف واحد اختياري attach=output.pdf، ورموز خروج بوابة CI failon=، ومعالجة batch=list.txt، ومسح دليل batchdir=folder مع تكرار اختياري ومعالجة مرتبة بمسار ثابت، وتوجيه تقرير outdir=reports، وأسماء تقارير آمنة من التصادم لأسماء ملفات الإدخال المكررة، وملخصات TXT / JSON / HTML مع إجماليات حالة كلية ومعيارية، وبيانات صفوف CSV مع إعدادات التشغيل متكررة لكل عنصر، ووضع عينة بدون وسائط للسكريبتات ووظائف CI. يذكر التقرير المُولَّد أن المدققات المضمنة تغطي الفحوصات على مستوى العلامات وفحوصات على مستوى الملف المختارة، بحيث يمكن للفحص الكامل للمحتوى أن يُفوَّض إلى محرك تحقق مخصص.

التحميل المتدفق والمستندات الكبيرة

تحميل PDF عند الطلب باستخدام LoadCustomDocument

// LoadCustomDocument يسمح لـ PDFium بسحب الكتل من أي TStream قابل
// للبحث عند الطلب. الـ stream لا يُنسخ إلى الذاكرة، بحيث تكون ملفات
// PDF متعددة الجيجابايت و streams استجابة 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('Pages: ' + IntToStr(Pdf1.PageCount));
end;

// Stream مملوك خارجيًا (المستدعي يحرره بعد 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 يعرض IPdfCancellationToken /
// IPdfCancellationTokenSource لدلالات الإلغاء من واجهة المستخدم التي
// تعمل عبر مسارات التصدير والعرض الطويلة.
uses FPdfAsync;

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

  // ... ينقر المستخدم على Cancel ...
  CancelSource.Cancel;

  // العامل يفحص CancelToken.IsCancelled بشكل دوري، أو مسار العرض
  // يستطلعه عبر callback IFSDK_PAUSE الخاص بـ PDFium.
  if CancelToken.IsCancelled then
    Memo1.Lines.Add('Cancelled by user');
end;

عرض صفحة تدريجي قابل للإلغاء

// RenderPageProgressive يسمح لعرض طويل عالي الدقة DPI بالإجهاض في منتصف
// الصفحة عندما ينقر المستخدم على Cancel، بدلاً من حجب المستدعي حتى الانتهاء.
var
  Status: TPdfProgressiveStatus;
  Bitmap: TBitmap;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit;
    Bitmap.SetSize(2480, 3508); // A4 @ 300 DPI

    Status := Pdf1.RenderPageProgressive(Bitmap, 0, 0, Bitmap.Width,
      Bitmap.Height, CancelToken, ro0, [reAnnotations], clWhite);

    case Status of
      prsDone:      SaveBitmap(Bitmap);
      prsCancelled: Memo1.Lines.Add('Render cancelled — partial result available');
      prsFailed:    Memo1.Lines.Add('PDFium reported FPDF_RENDER_FAILED');
    end;
  finally
    Bitmap.Free;
  end;
end;

تشغيل العروض على خيط في الخلفية باستخدام TPdfFuture

// TPdfFuture<T> يُطلق عاملاً على خيط في الخلفية ويُرسل
// مظروف TPdfFutureResult<T> عائدًا إلى الخيط الرئيسي.
//
// مهم: TPdf يُسلسل استدعاءات العرض لكل instance، لكن العامل لا يزال يمتلك
// الإرسال وعمر المستند. لا تغير TPdf أثناء جريان عرض ما؛ التصدير الدفعي
// عادةً يحتفظ بـ TPdf واحد لكل ملف.
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 يلف FS_MATRIX الخاص بـ PDFium بحيث يمكن للمستدعين بناء تحويل
// بشكل تصريحي وتمريره إلى FPDFPageObj_SetMatrix.
uses FPdfMatrix, FPdfView;

var
  M: TPdfMatrix;
  RawMatrix: FS_MATRIX;
  PageObj: FPDF_PAGEOBJECT;
begin
  M := TPdfMatrix.Create;
  try
    M.Translate(72, 200);       // التحريك بمقدار بوصة لليمين، ~2.8 بوصة للأعلى
    M.Scale(0.5, 0.5);          // ختم بنصف الحجم
    M.Rotate(15);               // تدوير عكس عقارب الساعة بزاوية 15 درجة
    // M.HorizontalFlip;
    // M.VerticalFlip;
    // M.CentralFlip;
    // M.Skew(10, 5);
    // M.Multiply(Other);       // الضرب اللاحق بمصفوفة أخرى

    // التطبيق على أي كائن صفحة PDFium (نص / مسار / صورة / form XObject)
    RawMatrix := M.Handle;
    FPDFPageObj_SetMatrix(PageObj, RawMatrix);
  finally
    M.Free;
  end;
end;

تركيب الصفحات

استيراد مجموعة فرعية من الصفحات حسب الفهرس

// ImportPagesByIndex ينسخ مصفوفة فهارس صريحة بدءًا من الصفر من
// PDF مصدر إلى هذا المستند. InsertAt = 0 يدرج قبل الصفحة 1؛
// PageCount يلحق.
var
  Source: TPdf;
begin
  Source := TPdf.Create(nil);
  try
    Source.FileName := 'C:\Report.pdf';
    Source.Active   := True;

    // استيراد الصفحات 0، 2، 4 (بدءًا من واحد: الصفحات 1، 3، 5) من Source
    // وإلحاقها في نهاية Pdf1.
    Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);

    // مصفوفة فارغة تستورد كل صفحات المصدر
    // Pdf1.ImportPagesByIndex(Source, [], 0);
  finally
    Source.Free;
  end;
end;

إخراج تركيب N-up

// ImportNPagesToOne يُرجع TPdf جديدًا تمامًا، صفحاته مركبات
// NumX*NumY من هذا المستند. OutputWidth/Height بوحدات مستخدم PDF
// (1 وحدة = 1/72 بوصة).
var
  Composite: TPdf;
begin
  Pdf1.FileName := 'C:\Slides.pdf';
  Pdf1.Active   := True;
  // 4-up A4 أفقي: 842 x 595 pt، 2 أعمدة x 2 صفوف
  Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
  try
    if Composite <> nil then
      Composite.SaveAs('C:\Slides_4up.pdf');
  finally
    Composite.Free;
  end;
end;

إعادة ترتيب الصفحات في مكانها

// MovePages يأخذ الفهارس بدءًا من الصفر للصفحات المراد نقلها بالإضافة إلى
// فهرس الوجهة للصفحة الأولى المنقولة بعد اكتمال النقل.
// الصفحات الأخرى تتنقل حول الكتلة المنقولة بحيث يبقى طول المستند
// كما هو.
//
// مثال: في مستند مكون من 5 صفحات مرقمة [0,1,2,3,4]،
// MovePages([2, 3], 0) يعيد الترتيب إلى [2, 3, 0, 1, 4].
if not Pdf1.MovePages([2, 3], 0) then
  ShowMessage('Invalid indices passed to MovePages');

العلامة المائية عبر إعادة استخدام صفحة Form XObject

// CreateXObjectFromPage يصنع مقبض Form XObject قابل لإعادة الاستخدام من
// أي صفحة في أي TPdf آخر. اختم هذا المقبض على أي عدد من صفحات
// هذا المستند تريد — كل ختم هو كائن صفحة PDFium واحد
// يمكن تحديد موضعه وتحجيمه وتدويره عبر واجهة 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;

    // لف الصفحة 0 من WatermarkSource كـ 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;

        // توسيط العلامة المائية على الصفحة
        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; // يغلق مقبض FPDF_XOBJECT
    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 للعلامات المائية الموسطة والأختام في الزاوية اليمنى العلوية، وتضيف تسميات أرقام صفحات اختيارية قبل حفظ النتيجة.

إدراج الصور (تحميلات AddImage المباشرة الزائدة)

إضافة ملف صورة مسجل أو TBitmap

// AddImage(FileName) يقبل أي تنسيق مسجل في وحدات الرسوميات VCL أو LCL
// (BMP، PNG عبر PngImage، JPG، ...).
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);

// AddImage(TBitmap) يتخطى وسيط TPicture، مثالي عندما يكون
// bitmap قد جاء بالفعل من أعمال العرض أو التوليد الدفعي.
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
  // رسم قناع clYellow فوق كل تطابق على الصفحة الحالية للعرض.
  // الانتقال إلى صفحة أخرى يمسح التمييز تلقائيًا.
  PdfView1.HighlightColor := clYellow;
  MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
  Status.Caption := Format('%d match(es) on this page', [MatchCount]);

  // إزالة الطبقة برمجيًا قبل تغيير الصفحة
  PdfView1.ClearHighlight;
end;

تخطيط العارض ولون الصفحة

وضع الملاءمة وخلفية الصفحة

// FitMode يبقي المستندات الطويلة مؤطرة أثناء Resize أو تغييرات الصفحة.
// تعيين Zoom مباشرة يلغي FitMode (يعود إلى pfmNone).
PdfView1.FitMode := pfmFitPage;       // الصفحة بكاملها في منطقة العرض
// PdfView1.FitMode := pfmFitWidth;   // عرض الصفحة يطابق عرض منطقة العرض
// PdfView1.FitMode := pfmActualSize; // 100%

// PageColor يفصل صفحة PDF المعروضة عن Color الخاص بعنصر التحكم المضيف
// — إعداد عارض الوضع الداكن النموذجي يبقي منطقة التمرير داكنة بينما تبقى
// صفحة PDF بيضاء ورقية.
PdfView1.Color     := clBlack;        // منطقة التمرير
PdfView1.PageColor := clWhite;        // خلفية صفحة PDF

// ظل اختياري وحد للصفحة لرفع الصفحة عن خلفية داكنة
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، عاين الصفحات، اختر نطاقات الصفحات والنسخ في مربع حوار الطباعة الخاص بالمنصة، احترم وضع الترتيب، اعرض كل صفحة عبر TPdf.RenderPage، واعرض تقدم الطباعة مع الإلغاء. تقبل عينة Delphi أيضًا ملفات PDF المُسحبة للتحميل السريع.

عرض متعدد الصفحات سريع في MultiPageViewer

تختبر مشاريع Demo\Delphi\MultiPageViewer وDemo\CBuilder\MultiPageViewer وDemo\Lazarus\MultiPageViewer العرض المستمر والمنتشر والمدرك للغلاف مع سير عمل التنقل والتكبير العادية بين الصفحات. مفيدة أيضًا لاختبار سرعة الفتح إلى الصفحة الأولى وسرعة استجابة العرض على ملفات PDF كبيرة جدًا: في اختبارات الملفات الكبيرة، يمكن للمستندات بحجم 2.x جيجابايت التي قد تجعل Acrobat أو Foxit يقضون 20-30 ثانية قبل العرض الأول أن تصبح قابلة للاستخدام تقريبًا فورًا من خلال مسار عارض PDFiumPas.

مراجعة جنب-إلى-جنب في SplitView

تعرض مشاريع Demo\Delphi\SplitView وDemo\CBuilder\SplitView وDemo\Lazarus\SplitView مراجعة PDF جنبًا إلى جنب. تركز عينات C++Builder و Lazarus على مقارنة لوحتين أو ثلاث لوحات، واختيار العرض النشط، وأوامر التنقل المتزامنة الاختيارية، وإعدادات تكبير مشتركة، وتدوير للعرض المحدد أو جميع العروض المُحمَّلة. استخدم هذه العينات لمقارنة المستندات الكبيرة مع الحفاظ على عرض أول قابل للاستخدام وعرض الصفحات اللاحق سريع الاستجابة؛ نفس ملفات PDF بحجم 2.x جيجابايت التي قد تستغرق Acrobat أو Foxit 20-30 ثانية لفتحها يمكن أن تصبح قابلة للاستخدام تقريبًا فورًا في مسار عارض PDFiumPas.

تدقيق سطح خطر PDF في SecurityAudit

تفتح مشاريع Demo\Delphi\SecurityAudit وDemo\CBuilder\SecurityAudit وDemo\Lazarus\SecurityAudit ملف PDF للقراءة فقط وتسرد حالة الصلاحيات والمرفقات المضمنة وإجراءات JavaScript المستند وتعليقات روابط URI و Launch وروابط الويب وملخصات التوقيعات وحالة XFA وتوفر مساعد V8 وcallbacks الميزات غير المدعومة. يمكن حفظ صفوف الملخصات والنتائج نفسها أو نسخها كتقرير تدقيق TXT أو حفظها كـ JSON منظم. يُقصد بها كلوحات خطر للتطبيق المضيف، وليس كاكتشاف برامج خبيثة.

معالجة روابط العارض

اتباع آمن للنقرات مع قوائم سماح لكل إجراء

// LinkOptions يتحكم في المعالجة التلقائية لأنواع إجراءات روابط PDF الأربعة.
// الافتراضيات تُمكّن goto و URI فقط؛ launch (تشغيل برنامج) و
// embedded GotoR (الانتقال إلى ملف آخر) يبقيان معطلين بحيث لا
// تستطيع نقرة شاردة تشغيل كود اعتباطي.
PdfView1.LinkOptions :=
  [loAutoGoto, loAutoOpenURI];  // افتراضيات آمنة

// 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;  // قمع المعالجة التلقائية
  end;
end;

// OnWebLinkClick يُطلق لسلاسل URI التي اكتشفها ماسح روابط الويب الخاص بـ
// PDFium (URLs المُكتشفة تلقائيًا في stream النص، حتى بدون
// تعليق /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;

تحرير عناصر النموذج في العارض

التحديد والتراجع / الإعادة

// على عنصر AcroForm widget المركّز، ست طرق جديدة تشغّل سجل تحرير نموذج
// PDFium. جميع الستة تنتهي بأمان إذا لم يكن هناك widget مركّز أو
// المستند لا يحمل 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;

قفل العارض للأكشاك

تعطيل إيماءات التنقل بين الصفحات

// كلها الثلاثة بقيمة افتراضية True بحيث ترقى التطبيقات الموجودة دون تغيير
// في السلوك. اقلبها إلى False للأكشاك أو ألواح المعاينة أو التضمينات
// للقراءة فقط دون subclassing لـ TPdfView.
PdfView1.AllowUserPageChange       := False;  // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // قفزات العجلة للأعلى/للأسفل
PdfView1.AllowUserTextSelection    := False;  // تحديد النص بالماوس/لوحة المفاتيح

// Ctrl+wheel محجوز للتكبير ويتم تخطيه بواسطة معالج التنقل
// بغض النظر عن ChangePageOnMouseScrolling.

اللوحة الجانبية للصور المصغرة

ربط TPdfThumbnailView بـ TPdfView

// TPdfThumbnailView (وحدة FPdfThumbnail) يعرض صورة مصغرة لصفحة واحدة لكل
// صف في لوحة جانبية قابلة للتمرير.
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تغيير (Changed)(Sender: TObject);
begin
  ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;

تصدير دفعي قابل للإلغاء تعاونيًا

تحويل متعدد الملفات من PDF إلى JPG مع تقدم لكل ملف وكل صفحة

// النمط المستخدم في Demo\Delphi\BatchExport و Demo\CBuilder\BatchExport
// و Demo\Lazarus\BatchExport. TPdf واحد لكل ملف، IPdfCancellationToken
// للإلغاء من واجهة المستخدم، 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('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 هو فحص Catalog خفيف — True عندما يوجد /StructTreeRoot.
// StructureElements يجسّد الشجرة كمصفوفة مسطحة من سجلات
// TPdfStructureElement مع النوع والعنوان والنص البديل والنص
// الفعلي ونص التوسع واللغة والمستوى وفهرس الأصل وعدد الأبناء /
// المحتوى الموسوم / السمات.
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 هي دالة عامة تعيّن إلى FPDF_SetPrintMode
// الخاص بـ PDFium. استخدم قيمة TPdfPrintMode المطابقة لمشغل الطابعة
// المستخدم.
SetPdfPrintMode(pmEmf);                   // GDI EMF (افتراضي)
// SetPdfPrintMode(pmTextOnly);           // عرض نص فقط
// SetPdfPrintMode(pmPostScript2);        // PostScript Level 2
// SetPdfPrintMode(pmPostScript3);        // PostScript Level 3
// SetPdfPrintMode(pmPostScript2PassThrough);
// SetPdfPrintMode(pmPostScript3PassThrough);
// SetPdfPrintMode(pmEmfImageMasks);      // EMF + أقنعة صور
// SetPdfPrintMode(pmPostScript3Type42);  // PostScript 3 + خطوط Type 42

قراءة إعدادات الطباعة المقصودة من المؤلف

// PrintCopies و PrintPageRanges و PrintScaling و PrintPaperHandling هي
// إدخالات قاموس تفضيلات العارض — تكوّن ما أراد مؤلف المستند
// أن تكون عليه القيم الافتراضية لقارئات 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 في شجرة الأسماء

// JavaScript على مستوى المستند مخزن في شجرة أسماء
// /Names /JavaScript. JavaScriptAction[i] يُرجع إدخالاً واحدًا؛
// JavaScriptActions يُرجع المصفوفة الكاملة.
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;

// مسار العرض بدون نسخ (افتراضي منذ v1.21.0): RenderPage يكتب
// مباشرة في مخزن DIB الخاص بـ TBitmap الوجهة عندما يمرر المستدعون
// bitmap مُخصصًا مسبقًا، متجنبًا نسخة width * height * 4 بايتات واحدة.
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;