Pdf1.FileName := 'C:\MyDocument.pdf';
Pdf1.Active := True;
ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
ShowMessage('Title: ' + Pdf1.Title);
ShowMessage('Author: ' + Pdf1.Author);
var
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
Stream.LoadFromFile('C:\MyDocument.pdf');
Pdf1.LoadDocument(Stream, True);
Pdf1.Active := True;
ShowMessage('PDF loaded from stream');
finally
Stream.Free;
end;
end;
Pdf1.CreateDocument;
Pdf1.Active := True;
Pdf1.AddPage(0, 612, 792);
Pdf1.PageNumber := 1;
Pdf1.AddText('Hello World!', 'Arial', 12, 100, 700, clBlack, $FF, 0.0);
Pdf1.UpdatePage;
Pdf1.SaveAs('C:\NewDocument.pdf');
Pdf1.Active := False;
var
PageText: WString;
RectText: WString;
begin
Pdf1.PageNumber := 1;
PageText := Pdf1.Text;
Memo1.Text := PageText;
RectText := Pdf1.TextInRectangle(100, 100, 500, 200);
ShowMessage('Text in rectangle: ' + RectText);
end;
var
Position: Integer;
begin
Position := Pdf1.FindFirst('Hello', [seCaseSensitive]);
while Position >= 0 do
begin
ShowMessage('Found at character: ' + IntToStr(Position));
Position := Pdf1.FindNext;
end;
end;
Pdf1.PageNumber := 1;
Pdf1.AddText('Document Title', 'Times New Roman', 16, 100, 750, clBlue);
Pdf1.AddText('This is the document content.', 'Arial', 12, 100, 700, clBlack);
Pdf1.UpdatePage;
var
I: Integer;
MetricSize: Single;
FontBytes: TBytes;
begin
Pdf1.PageNumber := 1;
for I := 0 to Pdf1.ObjectCount - 1 do
if Pdf1.ObjectType[I] = otText then
begin
MetricSize := 12.0;
FontBytes := Pdf1.FontData[I];
Memo1.Lines.Add('Object ' + IntToStr(I));
Memo1.Lines.Add('Base name: ' + Pdf1.FontBaseName[I]);
Memo1.Lines.Add('Family: ' + Pdf1.FontFamilyName[I]);
Memo1.Lines.Add('Embedded: ' + BoolToStr(Pdf1.FontIsEmbedded[I], True));
Memo1.Lines.Add('Weight: ' + IntToStr(Pdf1.FontWeight[I]));
Memo1.Lines.Add('Italic angle: ' + IntToStr(Pdf1.FontItalicAngle[I]));
Memo1.Lines.Add('Ascent: ' + FloatToStr(Pdf1.FontAscent[I, MetricSize]));
Memo1.Lines.Add('Descent: ' + FloatToStr(Pdf1.FontDescent[I, MetricSize]));
Memo1.Lines.Add('Font data bytes: ' + IntToStr(Length(FontBytes)));
end;
if Pdf1.CharacterCount > 0 then
begin
Memo1.Lines.Add('First character size: ' + FloatToStr(Pdf1.CharacterFontSize[0]));
Memo1.Lines.Add('First character weight: ' + IntToStr(Pdf1.CharacterFontWeight[0]));
Memo1.Lines.Add('First character angle: ' + FloatToStr(Pdf1.CharacterAngle[0]));
end;
end;
تعرض مشاريع 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;
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');
// 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;
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;
تقوم مشاريع Demo\Delphi\StandardsLab وDemo\CBuilder\StandardsLab وDemo\Lazarus\StandardsLab بتحميل أو إنشاء PDF، وتشغيل ValidatePdfA وValidatePdfUa وValidatePdfE وValidatePdfX وValidatePdfR وValidatePdfVT، ثم تعرض كل مستوى توافق مُكتشَف وعدد المشكلات في شبكة واحدة. تستدعي أزرار الحفظ SaveAsPdfA وSaveAsPdfUa وSaveAsPdfE وSaveAsPdfX وSaveAsPdfR وSaveAsPdfVT بحيث يكون سير عمل إخراج العلامات مرئيًا دون كتابة harness مخصص.
تلف الوحدة 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. يذكر التقرير المُولَّد أن المدققات المضمنة تغطي الفحوصات على مستوى العلامات وفحوصات على مستوى الملف المختارة، بحيث يمكن للفحص الكامل للمحتوى أن يُفوَّض إلى محرك تحقق مخصص.
// 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<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;
// 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;
// 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');
// 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(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;
تبقي مشاريع Demo\Delphi\ViewerInteractionLab وDemo\CBuilder\ViewerInteractionLab وDemo\Lazarus\ViewerInteractionLab سطح تفاعل TPdfView الحديث في نموذج مدمج واحد: افتح أو أنشئ عينة PDF، بدّل DisplayMode، طبّق FitMode، غيّر PageColor، دوّر الصفحات، ميّز نتائج البحث في الصفحة الحالية بـ HighlightSearchText، فعّل أو عطّل تحديد نص المستخدم، افحص SelectedText، استدعِ SelectAll وCopySelectionToClipboard وClearSelection، وراقب إحداثيات DeviceToPage تتحدث في شريط الحالة بينما يتحرك الماوس فوق الصفحة.
تحوّل مشاريع Demo\Delphi\SearchAndSelect وDemo\CBuilder\SearchAndSelect وDemo\Lazarus\SearchAndSelect سير عمل البحث والتحديد في العارض إلى عينات مستقلة. تفتح أو تنشئ PDF، تسرد نتائج البحث في كل الصفحات مع رقم الصفحة وفهرس الحرف ونص المعاينة، تدعم تنقل السابق / التالي والقفز إلى النتيجة بالنقر المزدوج، تميز تطابقات الصفحة الحالية بـ HighlightSearchText، تعرض خيارات مراعاة الحالة والكلمة الكاملة، تبدّل AllowUserTextSelection، تعاين SelectedText، تُشغّل SelectAll وCopySelectionToClipboard وClearSelection من الأزرار، وتسمح للمستخدمين بالتبديل بين أوضاع العرض الفردية والمنتشرة.
تقرأ مشاريع Demo\Delphi\PrintPreferences وDemo\CBuilder\PrintPreferences وDemo\Lazarus\PrintPreferences تفضيلات طباعة المؤلف قبل الطباعة. تعرض PrintCopies وPrintPageRanges وPrintScaling وPrintPaperHandling، يمكنها نسخ النسخ والنطاقات المُقدَّمة من المؤلف إلى مدخلات الطباعة، وتظهر كيف يُعيِّن SetPdfPrintPaperHandlingDevMode نية Duplex لـ PDF إلى DEVMODE الخاص بـ Windows.
تعرض مشاريع Demo\Delphi\PrintPDF وDemo\CBuilder\PrintPDF وDemo\Lazarus\PrintPDF سير عمل الطباعة القياسية: افتح PDF، عاين الصفحات، اختر نطاقات الصفحات والنسخ في مربع حوار الطباعة الخاص بالمنصة، احترم وضع الترتيب، اعرض كل صفحة عبر TPdf.RenderPage، واعرض تقدم الطباعة مع الإلغاء. تقبل عينة Delphi أيضًا ملفات PDF المُسحبة للتحميل السريع.
تختبر مشاريع Demo\Delphi\MultiPageViewer وDemo\CBuilder\MultiPageViewer وDemo\Lazarus\MultiPageViewer العرض المستمر والمنتشر والمدرك للغلاف مع سير عمل التنقل والتكبير العادية بين الصفحات. مفيدة أيضًا لاختبار سرعة الفتح إلى الصفحة الأولى وسرعة استجابة العرض على ملفات PDF كبيرة جدًا: في اختبارات الملفات الكبيرة، يمكن للمستندات بحجم 2.x جيجابايت التي قد تجعل Acrobat أو Foxit يقضون 20-30 ثانية قبل العرض الأول أن تصبح قابلة للاستخدام تقريبًا فورًا من خلال مسار عارض PDFiumPas.
تعرض مشاريع Demo\Delphi\SplitView وDemo\CBuilder\SplitView وDemo\Lazarus\SplitView مراجعة PDF جنبًا إلى جنب. تركز عينات C++Builder و Lazarus على مقارنة لوحتين أو ثلاث لوحات، واختيار العرض النشط، وأوامر التنقل المتزامنة الاختيارية، وإعدادات تكبير مشتركة، وتدوير للعرض المحدد أو جميع العروض المُحمَّلة. استخدم هذه العينات لمقارنة المستندات الكبيرة مع الحفاظ على عرض أول قابل للاستخدام وعرض الصفحات اللاحق سريع الاستجابة؛ نفس ملفات PDF بحجم 2.x جيجابايت التي قد تستغرق Acrobat أو Foxit 20-30 ثانية لفتحها يمكن أن تصبح قابلة للاستخدام تقريبًا فورًا في مسار عارض PDFiumPas.
تفتح مشاريع 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 (وحدة 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;
// النمط المستخدم في 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;
function LoadPdfSafely(const FileName: string): Boolean;
begin
Result := False;
if not FileExists(FileName) then
Exit;
try
Pdf1.FileName := FileName;
Pdf1.Active := True;
Result := Pdf1.Active;
except
Result := False;
end;
end;
function ExtractTextSafely(PageNo: Integer): WString;
begin
Result := '';
if not Pdf1.Active then
Exit;
if (PageNo < 1) or (PageNo > Pdf1.PageCount) then
Exit;
Pdf1.PageNumber := PageNo;
Result := Pdf1.Text;
end;
// IsTagged هو فحص 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;
// 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 على مستوى المستند مخزن في شجرة أسماء
// /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;
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;